From 472e5507b811fe522e5b2c84e1b75faf32624c2f Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Sep 2023 09:21:17 +0200 Subject: [PATCH 1/3] port ObjectHistoryData to TS --- package.json | 2 +- ...tHistoryData.jsx => ObjectHistoryData.tsx} | 327 +++++++++--------- 2 files changed, 169 insertions(+), 160 deletions(-) rename src/src/components/Object/{ObjectHistoryData.jsx => ObjectHistoryData.tsx} (83%) diff --git a/package.json b/package.json index 98f9dcfba..11a19ef80 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "dependencies": { "@iobroker/adapter-core": "^3.0.3", - "@iobroker/socket-classes": "^1.4.0", + "@iobroker/socket-classes": "^1.4.1", "@iobroker/webserver": "^0.3.6", "@iobroker/ws-server": "^2.1.1", "archiver": "^6.0.1", diff --git a/src/src/components/Object/ObjectHistoryData.jsx b/src/src/components/Object/ObjectHistoryData.tsx similarity index 83% rename from src/src/components/Object/ObjectHistoryData.jsx rename to src/src/components/Object/ObjectHistoryData.tsx index 52a868418..cbf9a5c32 100644 --- a/src/src/components/Object/ObjectHistoryData.jsx +++ b/src/src/components/Object/ObjectHistoryData.tsx @@ -1,6 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { withStyles } from '@mui/styles'; +import { Styles, withStyles } from '@mui/styles'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { LocalizationProvider, TimePicker, DatePicker } from '@mui/x-date-pickers'; @@ -31,7 +30,9 @@ import { TableContainer, } from '@mui/material'; -import { Utils, withWidth, TableResize } from '@iobroker/adapter-react-v5'; +import { + Utils, withWidth, TableResize, AdminConnection, +} from '@iobroker/adapter-react-v5'; // icons import { FaPlusSquare as InsertIcon, FaDownload as ExportIcon } from 'react-icons/fa'; @@ -41,9 +42,10 @@ import { Close as IconClose, } from '@mui/icons-material'; +import type { SystemConfig } from '@iobroker/socket-client'; import { localeMap } from './utils'; -function padding3(ms) { +function padding3(ms: number) { if (ms < 10) { return `00${ms}`; } if (ms < 100) { @@ -52,14 +54,7 @@ function padding3(ms) { return ms; } -// function padding2(num) { -// if (num < 10) { -// return `0${num}`; -// } -// return num; -// } - -const styles = theme => ({ +const styles = (theme: any) => ({ paper: { height: '100%', maxHeight: '100%', @@ -206,18 +201,69 @@ const styles = theme => ({ timeInput: { width: 100, }, -}); +}) satisfies Styles; + +interface ObjectHistoryDataProps { + t: (...text: string[]) => string; + lang: ioBroker.Languages; + expertMode: boolean; + socket: AdminConnection; + obj: Record; + customsInstances: any[]; + themeName: string; + objects: Record; + isFloatComma: boolean; + classes: Record; +} + +interface ObjectHistoryDataState { + areYouSure?: boolean; + historyInstance: string; + relativeRange: any; + start: number; + end: number; + values: any; + selected: number[]; + lastSelected: any; + lastSelectedColumn: any; + updateOpened: boolean; + insertOpened: boolean; + historyInstances: null | Record[]; + lcVisible: boolean; + ackVisible: boolean; + fromVisible: boolean; + /** 'insert', 'update', 'delete' */ + supportedFeatures: string[]; + dateFormat: string; + edit: Record; + loading?: boolean; + suppressMessage?: number | boolean; +} + +class ObjectHistoryData extends Component { + private readonly adminInstance: number; + + private readonly supportedFeaturesPromises: Record; -class ObjectHistoryData extends Component { - constructor(props) { + private readonly unit: string; + + private timeTimer?: ReturnType; + + private readSupportedFeaturesTimeout?: ReturnType; + + private rangeValues: any; + + private rangeRef?: React.RefObject; + + constructor(props: ObjectHistoryDataProps) { super(props); - let relativeRange = (window._localStorage || window.localStorage).getItem('App.relativeRange') || 'absolute'; - const start = parseInt((window._localStorage || window.localStorage).getItem('App.absoluteStart'), 10) || 0; - const end = parseInt((window._localStorage || window.localStorage).getItem('App.absoluteEnd'), 10) || 0; - let selected = (window._localStorage || window.localStorage).getItem('App.historySelected') || ''; - const lastSelected = parseInt((window._localStorage || window.localStorage).getItem('App.historyLastSelected'), 10) || null; - const lastSelectedColumn = (window._localStorage || window.localStorage).getItem('App.historyLastSelectedColumn') || null; + let relativeRange = (((window as any)._localStorage) || window.localStorage).getItem('App.relativeRange') || 'absolute'; + const start = parseInt((((window as any)._localStorage) || window.localStorage).getItem('App.absoluteStart'), 10) || 0; + const end = parseInt((((window as any)._localStorage) || window.localStorage).getItem('App.absoluteEnd'), 10) || 0; + let selected = (((window as any)._localStorage) || window.localStorage).getItem('App.historySelected') || ''; + const lastSelected = parseInt((((window as any)._localStorage) || window.localStorage).getItem('App.historyLastSelected'), 10) || null; + const lastSelectedColumn = (((window as any)._localStorage) || window.localStorage).getItem('App.historyLastSelectedColumn') || null; if ((!start || !end) && (!relativeRange || relativeRange === 'absolute')) { relativeRange = '30'; @@ -260,7 +306,7 @@ class ObjectHistoryData extends Component { this.unit = this.props.obj.common && this.props.obj.common.unit ? ` ${this.props.obj.common.unit}` : ''; - this.timeTimer = null; + this.timeTimer = undefined; this.prepareData() .then(() => this.readHistoryRange()) @@ -273,7 +319,7 @@ class ObjectHistoryData extends Component { }); } - readSupportedFeatures(historyInstance) { + readSupportedFeatures(historyInstance: string) { historyInstance = historyInstance || this.state.historyInstance; if (!historyInstance) { return Promise.resolve([]); @@ -284,15 +330,15 @@ class ObjectHistoryData extends Component { this.supportedFeaturesPromises[historyInstance] = new Promise(resolve => { this.readSupportedFeaturesTimeout && clearTimeout(this.readSupportedFeaturesTimeout); this.readSupportedFeaturesTimeout = setTimeout(() => { - this.readSupportedFeaturesTimeout = null; + this.readSupportedFeaturesTimeout = undefined; resolve([]); - }, 2000); + }, 2_000); this.props.socket.sendTo(historyInstance, 'features', null) - .then(result => { + .then((result: any) => { if (this.readSupportedFeaturesTimeout) { this.readSupportedFeaturesTimeout && clearTimeout(this.readSupportedFeaturesTimeout); - this.readSupportedFeaturesTimeout = null; + this.readSupportedFeaturesTimeout = undefined; resolve(result ? result.supportedFeatures || [] : []); } else { this.setState({ supportedFeatures: result ? result.supportedFeatures || [] : [] }); @@ -308,12 +354,12 @@ class ObjectHistoryData extends Component { componentWillUnmount() { this.timeTimer && clearTimeout(this.timeTimer); - this.timeTimer = null; + this.timeTimer = undefined; this.props.socket.unsubscribeState(this.props.obj._id, this.onChange); } - onChange = (id, state) => { + onChange = (id: string, state: ioBroker.State) => { if (id === this.props.obj._id && state && this.state.values && @@ -324,19 +370,19 @@ class ObjectHistoryData extends Component { }; prepareData() { - let list; + let list: Record[]; return this.getHistoryInstances() - .then(_list => { + .then((_list: Record[]) => { list = _list; // read default history return this.props.socket.getCompactSystemConfig(); }) - .then(config => { - const defaultHistory = config && config.common && config.common.defaultHistory; + .then((config: SystemConfig) => { + const defaultHistory = config?.common?.defaultHistory; // find current history // first read from localstorage - let historyInstance = (window._localStorage || window.localStorage).getItem('App.historyInstance') || ''; + let historyInstance = ((window as any)._localStorage || window.localStorage).getItem('App.historyInstance') || ''; if (!historyInstance || !list.find(it => it.id === historyInstance && it.alive)) { // try default history historyInstance = defaultHistory; @@ -353,7 +399,7 @@ class ObjectHistoryData extends Component { historyInstance = defaultHistory; } return this.readSupportedFeatures(historyInstance) - .then(supportedFeatures => new Promise(resolve => { + .then((supportedFeatures: string[]) => new Promise(resolve => { // supportedFeatures = ['insert', 'update', 'delete']; this.setState({ @@ -368,8 +414,8 @@ class ObjectHistoryData extends Component { } getHistoryInstances() { - const list = []; - const ids = []; + const list: Record[] = []; + const ids: string[] = []; this.props.customsInstances.forEach(instance => { const instObj = this.props.objects[`system.adapter.${instance}`]; if (instObj && instObj.common && instObj.common.getHistory) { @@ -381,7 +427,7 @@ class ObjectHistoryData extends Component { if (ids.length) { return this.props.socket.getForeignStates(ids) - .then(alives => { + .then((alives: Record) => { Object.keys(alives).forEach(id => { const item = list.find(it => id.endsWith(`${it.id}.alive`)); if (item) { @@ -394,22 +440,7 @@ class ObjectHistoryData extends Component { return Promise.resolve(list); } - readHistory(start, end) { - /* interface GetHistoryOptions { - instance?: string; - start?: number; - end?: number; - step?: number; - count?: number; - from?: boolean; - ack?: boolean; - q?: boolean; - addID?: boolean; - limit?: number; - ignoreNull?: boolean; - sessionId?: any; - aggregate?: 'minmax' | 'min' | 'max' | 'average' | 'total' | 'count' | 'none'; - } */ + readHistory(start?: number, end?: number) { start = start || this.state.start; end = end || this.state.end; @@ -430,7 +461,7 @@ class ObjectHistoryData extends Component { aggregate: 'none', returnNewestEntries: true, }) - .then(values => { + .then((values: ioBroker.GetHistoryResult) => { // merge range and chart const chart = []; const range = this.rangeValues; @@ -529,13 +560,13 @@ class ObjectHistoryData extends Component { addID: false, aggregate: 'none', }) - .then(values => { + .then((values: ioBroker.GetHistoryResult) => { if (values.length) { // remove interpolated first value if (values[0].val === null || values[0].ts === oldest.getTime()) { values.shift(); } - // mark interpolated + // @ts-expect-error mark interpolated values.forEach(it => it.i = true); this.rangeValues = values; this.setState({ @@ -548,7 +579,7 @@ class ObjectHistoryData extends Component { }); } - onToggleSelect(e, ts, column) { + onToggleSelect(e: React.KeyboardEvent | React.MouseEvent, ts: number, column: string) { let selected = [...this.state.selected]; const pos = selected.indexOf(ts); if (e.shiftKey && this.state.lastSelected) { @@ -586,13 +617,13 @@ class ObjectHistoryData extends Component { selected = [ts]; } - (window._localStorage || window.localStorage).setItem('App.historyLastSelected', ts.toString()); - (window._localStorage || window.localStorage).setItem('App.historyLastSelectedColumn', column); - (window._localStorage || window.localStorage).setItem('App.historySelected', JSON.stringify(selected)); + ((window as any)._localStorage || window.localStorage).setItem('App.historyLastSelected', ts.toString()); + ((window as any)._localStorage || window.localStorage).setItem('App.historyLastSelectedColumn', column); + ((window as any)._localStorage || window.localStorage).setItem('App.historySelected', JSON.stringify(selected)); this.setState({ selected, lastSelected: ts, lastSelectedColumn: column }); } - getTableRows(classes) { + getTableRows(classes: Record) { const rows = []; for (let r = this.state.values.length - 1; r >= 0; r--) { const state = this.state.values[r]; @@ -742,36 +773,36 @@ class ObjectHistoryData extends Component { this.setState({ start, end }, () => this.readHistory()); this.timeTimer = setTimeout(() => { - this.timeTimer = null; + this.timeTimer = undefined; this.shiftTime(); - }, delay || 60000); + }, delay || 60_000); } - setRelativeInterval(mins, dontSave) { + setRelativeInterval(mins: string | number, dontSave?: boolean): void { if (!dontSave) { - (window._localStorage || window.localStorage).setItem('App.relativeRange', mins); + ((window as any)._localStorage || window.localStorage).setItem('App.relativeRange', mins); this.setState({ relativeRange: mins }); } if (mins === 'absolute') { this.timeTimer && clearTimeout(this.timeTimer); - this.timeTimer = null; + this.timeTimer = undefined; return; } - (window._localStorage || window.localStorage).removeItem('App.absoluteStart'); - (window._localStorage || window.localStorage).removeItem('App.absolute'); + ((window as any)._localStorage || window.localStorage).removeItem('App.absoluteStart'); + ((window as any)._localStorage || window.localStorage).removeItem('App.absolute'); const now = new Date(); if (!this.timeTimer) { - const delay = 60000 - now.getSeconds() - (1000 - now.getMilliseconds()); + const delay = 60_000 - now.getSeconds() - (1_000 - now.getMilliseconds()); this.timeTimer = setTimeout(() => { - this.timeTimer = null; + this.timeTimer = undefined; this.shiftTime(); - }, delay || 60000); + }, delay || 60_000); } if (now.getMilliseconds()) { - now.setMilliseconds(1000); + now.setMilliseconds(1_000); } if (now.getSeconds()) { now.setSeconds(60); @@ -828,8 +859,8 @@ class ObjectHistoryData extends Component { now.setFullYear(now.getFullYear() - 1); start = now.getTime(); } else { - mins = parseInt(mins, 10); - start = end - mins * 60000; + mins = Number(mins); + start = end - mins * 60_000; } this.setState({ start, end }, () => @@ -856,6 +887,8 @@ class ObjectHistoryData extends Component { } return + {/* + // @ts-expect-error needs further checking */} { - if (state[attr] === undefined) { + + for (const [attr, val] of Object.entries(state)) { + if (val === undefined) { + // @ts-expect-error delete state[attr]; } - }); + } + if (!this.state.lcVisible && state.lc) { delete state.lc; } @@ -979,7 +1015,7 @@ class ObjectHistoryData extends Component { ts.setSeconds(this.state.edit.time.getSeconds()); ts.setMilliseconds(parseInt(this.state.edit.ms, 10)); - const state = { + const state: ioBroker.SettableState = { ts: ts.getTime(), val, ack: this.state.edit.ack, @@ -991,27 +1027,19 @@ class ObjectHistoryData extends Component { delete state.lc; } - Object.keys(state).forEach(attr => { - if (state[attr] === undefined) { + for (const [attr, val] of Object.entries(state)) { + if (val === undefined) { + // @ts-expect-error delete state[attr]; } - }); + } + this.props.socket.sendTo(this.state.historyInstance, 'insert', [{ id: this.props.obj._id, state }]) .then(() => this.readHistory()); } - // formatTime(ms) { - // const time = new Date(ms); - // return `${padding2(time.getHours())}:${padding2(time.getMinutes())}:${padding2(time.getSeconds())}.${padding3(time.getMilliseconds())}`; - // } - - // formatDate(ms) { - // const time = new Date(ms); - // return `${padding2(time.getDate())}.${padding2(time.getMonth() + 1)}.${time.getFullYear()}`; - // } - - updateEdit(name, value) { + updateEdit(name: string, value: any): void { const edit = JSON.parse(JSON.stringify(this.state.edit)); edit.time = new Date(edit.time); edit.date = new Date(edit.date); @@ -1020,7 +1048,7 @@ class ObjectHistoryData extends Component { this.setState({ edit }); } - renderEditDialog() { + renderEditDialog(): React.JSX.Element { return this.setState({ updateOpened: false, insertOpened: false })} @@ -1059,6 +1087,7 @@ class ObjectHistoryData extends Component { this.updateEdit('date', date)} - renderInput={params => } + renderInput={(params: any) => } /> - {/* this.edit.date = e.target.value} - /> */} this.updateEdit('time', time)} - renderInput={params => } + renderInput={(params: any) => } /> - {/* this.edit.time = e.target.value} - /> */} {this.state.updateOpened ? this.props.t('Update') : this.props.t('Add')} + {/* + // @ts-expect-error this color works */} ; } - setStartDate(start) { - start = start.getTime(); + setStartDate(startDate: Date): void { + const start = startDate.getTime(); if (this.timeTimer) { clearTimeout(this.timeTimer); - this.timeTimer = null; + this.timeTimer = undefined; } - (window._localStorage || window.localStorage).setItem('App.relativeRange', 'absolute'); - (window._localStorage || window.localStorage).setItem('App.absoluteStart', start); - (window._localStorage || window.localStorage).setItem('App.absoluteEnd', this.state.end); + ((window as any)._localStorage || window.localStorage).setItem('App.relativeRange', 'absolute'); + ((window as any)._localStorage || window.localStorage).setItem('App.absoluteStart', start); + ((window as any)._localStorage || window.localStorage).setItem('App.absoluteEnd', this.state.end); this.setState({ start, relativeRange: 'absolute' }, () => this.readHistory()); } - setEndDate(end) { - end = end.getTime(); - (window._localStorage || window.localStorage).setItem('App.relativeRange', 'absolute'); - (window._localStorage || window.localStorage).setItem('App.absoluteStart', this.state.start); - (window._localStorage || window.localStorage).setItem('App.absoluteEnd', end); + setEndDate(endDate: Date): void { + const end = endDate.getTime(); + ((window as any)._localStorage || window.localStorage).setItem('App.relativeRange', 'absolute'); + ((window as any)._localStorage || window.localStorage).setItem('App.absoluteStart', this.state.start); + ((window as any)._localStorage || window.localStorage).setItem('App.absoluteEnd', end); if (this.timeTimer) { clearTimeout(this.timeTimer); - this.timeTimer = null; + this.timeTimer = undefined; } this.setState({ end, relativeRange: 'absolute' }, () => this.readHistory()); } renderToolbar() { const classes = this.props.classes; + // @ts-ignore return {this.props.t('History instance')} @@ -1161,14 +1178,14 @@ class ObjectHistoryData extends Component { value={this.state.historyInstance || ''} onChange={e => { const historyInstance = e.target.value; - (window._localStorage || window.localStorage).setItem('App.historyInstance', historyInstance); + ((window as any)._localStorage || window.localStorage).setItem('App.historyInstance', historyInstance); this.readSupportedFeatures(historyInstance) - .then(supportedFeatures => + .then((supportedFeatures: string[]) => this.setState({ historyInstance, supportedFeatures }, () => this.readHistory())); }} > - {this.state.historyInstances.map(it => { it.id })} + {this.state.historyInstances!.map(it => { it.id })} @@ -1201,52 +1218,57 @@ class ObjectHistoryData extends Component { this.setStartDate(date)} - renderInput={params => } + onChange={date => this.setStartDate(date!)} + renderInput={(params: any) => } /> this.setStartDate(date)} - renderInput={params => } + onChange={date => this.setStartDate(date!)} + renderInput={(params: any) => } />
+ this.setEndDate(date)} - renderInput={params => } + onChange={date => this.setEndDate(date!)} + renderInput={(params: any) => } /> this.setEndDate(date)} - renderInput={params => } + onChange={date => this.setEndDate(date!)} + renderInput={(params: any) => } />
@@ -1260,8 +1282,6 @@ class ObjectHistoryData extends Component { size="large" onClick={() => { const time = new Date(); - // const date = `${time.getFullYear()}.${padding2(time.getMonth() + 1)}.${padding2(time.getDate())}`; - // const tm = `${padding2(time.getHours())}:${padding2(time.getMinutes())}:${padding2(time.getSeconds())}.${padding3(time.getMilliseconds())}`; const edit = { ack: this.state.values[this.state.values.length - 1].ack, @@ -1285,10 +1305,10 @@ class ObjectHistoryData extends Component { size="large" disabled={this.state.selected.length !== 1} onClick={() => { - const state = JSON.parse(JSON.stringify(this.state.values.find(it => it.ts === this.state.lastSelected))); + const state = JSON.parse(JSON.stringify(this.state.values.find((it: any) => it.ts === this.state.lastSelected))); const time = new Date(state.ts); - state.date = new Date(time);// time.getFullYear() + '.' + padding2(time.getMonth() + 1) + '.' + padding2(time.getDate()); - state.time = new Date(time);// padding2(time.getHours()) + ':' + padding2(time.getMinutes()) + ':' + padding2(time.getSeconds()) + '.' + padding3(time.getMilliseconds()); + state.date = new Date(time); + state.time = new Date(time); this.setState({ edit: state, @@ -1302,7 +1322,7 @@ class ObjectHistoryData extends Component { size="large" disabled={!this.state.selected.length} onClick={() => { - if (this.state.suppressMessage && Date.now() - this.state.suppressMessage < 300000) { + if (typeof this.state.suppressMessage === 'number' && Date.now() - this.state.suppressMessage < 300_000) { this.onDelete(); } else { this.setState({ areYouSure: true }); @@ -1325,7 +1345,7 @@ class ObjectHistoryData extends Component { const lines = ['timestamp;value;acknowledged;from;']; - this.state.values.forEach(state => !state.i && !state.e && + this.state.values.forEach((state: any) => !state.i && !state.e && lines.push([ new Date(state.ts).toISOString(), state.val === null || state.val === undefined ? 'null' : state.val.toString(), @@ -1334,6 +1354,7 @@ class ObjectHistoryData extends Component { ].join(';'))); element.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(lines.join('\n'))}`); + // @ts-expect-error check it element.setAttribute('download', `${Utils.getObjectName({ [this.props.obj._id]: this.props.obj }, this.props.obj._id, { language: this.props.lang })}.csv`); element.click(); @@ -1358,16 +1379,4 @@ class ObjectHistoryData extends Component { } } -ObjectHistoryData.propTypes = { - t: PropTypes.func, - lang: PropTypes.string, - expertMode: PropTypes.bool, - socket: PropTypes.object, - obj: PropTypes.object, - customsInstances: PropTypes.array, - themeName: PropTypes.string, - objects: PropTypes.object, - isFloatComma: PropTypes.bool, -}; - export default withWidth()(withStyles(styles)(ObjectHistoryData)); From 2131430daf8256cea4f5ee43215f65c6afbb4837 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Sep 2023 16:33:45 +0200 Subject: [PATCH 2/3] harmonized timestamp of data shown by admin and exported data - closes #2100 --- .../components/Object/ObjectHistoryData.tsx | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/src/components/Object/ObjectHistoryData.tsx b/src/src/components/Object/ObjectHistoryData.tsx index cbf9a5c32..624dd236b 100644 --- a/src/src/components/Object/ObjectHistoryData.tsx +++ b/src/src/components/Object/ObjectHistoryData.tsx @@ -42,6 +42,7 @@ import { Close as IconClose, } from '@mui/icons-material'; +// @ts-expect-error this is weird import type { SystemConfig } from '@iobroker/socket-client'; import { localeMap } from './utils'; @@ -678,7 +679,7 @@ class ObjectHistoryData extends Component !interpolated && this.onToggleSelect(e, ts, 'ts')}> - {`${new Date(state.ts).toLocaleDateString()} ${new Date(state.ts).toLocaleTimeString()}.${padding3(state.ts % 1000)}`} + {`${this.formatTimestamp(state.ts)}`} {selected && this.state.lastSelectedColumn === 'ts' ?
: ''} !interpolated && this.onToggleSelect(e, ts, 'val')}> @@ -983,9 +984,9 @@ class ObjectHistoryData extends Component {this.props.t('History instance')} @@ -1185,7 +1185,7 @@ class ObjectHistoryData extends Component - {this.state.historyInstances!.map(it => { it.id })} + {this.state.historyInstances?.map(it => { it.id })} @@ -1226,7 +1226,7 @@ class ObjectHistoryData extends Component this.setStartDate(date!)} + onChange={date => this.setStartDate(date as Date)} renderInput={(params: any) => } /> this.setStartDate(date!)} + onChange={date => this.setStartDate(date as Date)} renderInput={(params: any) => } />
@@ -1255,7 +1255,7 @@ class ObjectHistoryData extends Component this.setEndDate(date!)} + onChange={date => this.setEndDate(date as Date)} renderInput={(params: any) => } /> this.setEndDate(date!)} + onChange={date => this.setEndDate(date as Date)} renderInput={(params: any) => } /> @@ -1347,7 +1347,7 @@ class ObjectHistoryData extends Component !state.i && !state.e && lines.push([ - new Date(state.ts).toISOString(), + this.formatTimestamp(state.ts), state.val === null || state.val === undefined ? 'null' : state.val.toString(), state.ack ? 'true' : 'false', state.from || '', @@ -1377,6 +1377,15 @@ class ObjectHistoryData extends Component; } + + /** + * Convert timestamp to human-readable date string + * + * @param ts the timestamp + */ + formatTimestamp(ts: number): string { + return `${new Date(ts).toLocaleDateString()} ${new Date(ts).toLocaleTimeString()}.${padding3(ts % 1_000)}`; + } } export default withWidth()(withStyles(styles)(ObjectHistoryData)); From 47edc9a6ef88898c86dcc24d5baa6b1f074de0d5 Mon Sep 17 00:00:00 2001 From: foxriver76 Date: Tue, 12 Sep 2023 16:35:42 +0200 Subject: [PATCH 3/3] updated changelog --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 67faeebc2..694099aee 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ The icons may not be reused in other projects without the proper flaticon licens ### **WORK IN PROGRESS** --> +### **WORK IN PROGRESS** +* (foxriver76) harmonized data on csv export with actual data shown by admin + ## Changelog ### 6.10.1 (2023-09-11) * (foxriver76) fixed `between` function for showing news