From 5de009b3783416dfad18139124ee645552daba7e Mon Sep 17 00:00:00 2001 From: mikedivine Date: Thu, 20 Jun 2024 22:07:33 +0000 Subject: [PATCH 01/45] fixed CSV with previous changes --- .../components/FormFileUploaderComponent.tsx | 15 +- .../app/components/HeaderButtonsComponent.tsx | 11 +- src/client/app/components/RouteComponent.tsx | 6 +- .../csv/MetersCSVUploadComponent.tsx | 179 +++--- .../csv/ReadingsCSVUploadComponent.tsx | 533 +++++++++--------- .../meters/CreateMeterModalComponent.tsx | 11 +- src/client/app/translations/data.ts | 2 + 7 files changed, 399 insertions(+), 358 deletions(-) diff --git a/src/client/app/components/FormFileUploaderComponent.tsx b/src/client/app/components/FormFileUploaderComponent.tsx index 813569e43..d74ed78e3 100644 --- a/src/client/app/components/FormFileUploaderComponent.tsx +++ b/src/client/app/components/FormFileUploaderComponent.tsx @@ -11,6 +11,7 @@ interface FileUploader { required: boolean; formText: string; labelStyle?: React.CSSProperties; + onFileChange: (file: File | null) => void; } /** @@ -19,13 +20,25 @@ interface FileUploader { * @returns File uploader element */ export default function FileUploaderComponent(props: FileUploader) { + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0] || null; + props.onFileChange(file); + }; + return ( - + diff --git a/src/client/app/components/HeaderButtonsComponent.tsx b/src/client/app/components/HeaderButtonsComponent.tsx index 5e289288e..fa51d389c 100644 --- a/src/client/app/components/HeaderButtonsComponent.tsx +++ b/src/client/app/components/HeaderButtonsComponent.tsx @@ -174,8 +174,15 @@ export default function HeaderButtonsComponent() { style={state.csvViewableLinkStyle} disabled={state.shouldCSVButtonDisabled} tag={Link} - to="/csv"> - + to="/csvMeters"> + + + + , children: [ - { path: 'csv', element: } + { path: 'csvReadings', element: }, + { path: 'csvMeters', element: } ] }, { path: '*', element: } diff --git a/src/client/app/components/csv/MetersCSVUploadComponent.tsx b/src/client/app/components/csv/MetersCSVUploadComponent.tsx index 29ca0444d..e7e8470a9 100644 --- a/src/client/app/components/csv/MetersCSVUploadComponent.tsx +++ b/src/client/app/components/csv/MetersCSVUploadComponent.tsx @@ -5,106 +5,107 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Button, Form, FormGroup, Input, Label } from 'reactstrap'; -import { MODE } from '../../containers/csv/UploadCSVContainer'; -import { MetersCSVUploadProps } from '../../types/csvUploadForm'; import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; -import { AppDispatch } from '../../store'; import { baseApi } from '../../redux/api/baseApi'; -import { connect } from 'react-redux'; -const mapDispatchToProps = (dispatch: AppDispatch) => { - return { - resetApiCache: () => dispatch(baseApi.util.invalidateTags(['MeterData'])) - }; -}; -type ResetProp = { resetApiCache: () => void } -type MetersCsvUploadPropWithCacheDispatch = MetersCSVUploadProps & ResetProp -class MetersCSVUploadComponent extends React.Component { - private fileInput: React.RefObject; +import { useDispatch } from 'react-redux'; +import { uploadCSVApi } from '../../utils/api'; - constructor(props: MetersCsvUploadPropWithCacheDispatch) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleSetMeterName = this.handleSetMeterName.bind(this); - this.fileInput = React.createRef(); - } +interface MetersCSVUploadComponentProps {} + +const MetersCSVUploadComponent: React.FC = () => { + const dispatch = useDispatch(); + const[ meterData, setMeterData] = React.useState({ + meterName: '', + gzip: false, + headerRow: false, + update: false + }); + const [ file, setFile ] = React.useState(null); + const fileInput = React.useRef(null); + const resetApiCache = () => { + dispatch(baseApi.util.invalidateTags(['MeterData'])); + }; - private async handleSubmit(e: React.MouseEvent) { - try { - e.preventDefault(); - const current = this.fileInput.current as HTMLInputElement; - const { files } = current; - if (files && (files as FileList).length !== 0) { - await this.props.submitCSV(files[0]); - // TODO Using an alert is not the best. At some point this should be integrated - // with react. - showSuccessNotification('

SUCCESS

The meter upload was a success.'); + const handleSubmit = async(e: React.MouseEvent) => { + e.preventDefault(); + if(file) { + try { + await submitMeters(file); + showSuccessNotification('

SUCCESS

The meter was uploaded successfully.'); + } catch (error) { + // A failed axios request should result in an error. + showErrorNotification(error.response.data as string); } - } catch (error) { - // A failed axios request should result in an error. - showErrorNotification(error.response.data as string); } - // Refetch meters details by invalidating its api cache. - this.props.resetApiCache(); + resetApiCache(); + }; - } + const handleFileChange = (file : File | null) => { + setFile(file); + }; - private handleSetMeterName(e: React.ChangeEvent) { - const target = e.target; - this.props.setMeterName(MODE.meters, target.value); - } + const handleChange = (e:React.ChangeEvent) => { + const { name, value } = e.target; + setMeterData(prevState => ({ + ...prevState, + [name]: value + })); + }; - public render() { - const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' - }; + const submitMeters = async (file : File) => { + return await uploadCSVApi.submitMeters(meterData, file); + }; + + const titleStyle: React.CSSProperties = { + fontWeight: 'bold', + paddingBottom: '5px' + }; - const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' - }; + const checkboxStyle: React.CSSProperties = { + paddingBottom: '15px' + }; - const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' - }; + const formStyle: React.CSSProperties = { + display: 'flex', + justifyContent: 'center', + padding: '20px' + }; - return ( -
-
- - - - - - - - - - - - - - - - -
- ); - } + return ( +
+
+ + + + + + + + + + + + + + + + +
+ ); +}; -} -export default connect(null, mapDispatchToProps)(MetersCSVUploadComponent); +export default MetersCSVUploadComponent; \ No newline at end of file diff --git a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx index 0c74636b2..5d5c7db7a 100644 --- a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx +++ b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx @@ -5,12 +5,18 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Button, Col, Form, FormGroup, Input, Label } from 'reactstrap'; -import { MODE } from '../../containers/csv/UploadCSVContainer'; -import { BooleanMeterTypes, ReadingsCSVUploadProps, TimeSortTypes } from '../../types/csvUploadForm'; +import { BooleanMeterTypes, TimeSortTypes, ReadingsCSVUploadPreferencesItem } from '../../types/csvUploadForm'; import { ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; import { showErrorNotification, showInfoNotification } from '../../utils/notifications'; import translate from '../../utils/translate'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; +import { uploadCSVApi } from '../../utils/api'; +import CreateMeterModalComponent from '../meters/CreateMeterModalComponent'; +import { useAppSelector } from '../../redux/reduxHooks'; +import { authApi, authPollInterval } from '../../redux/api/authApi'; +import { selectIsAdmin } from '../../redux/slices/currentUserSlice'; +import { selectVisibleMeterAndGroupData } from '../../redux/selectors/adminSelectors'; +import { MeterData } from '../../types/redux/meters'; /** * Returns a range of values between the specified lower and upper bounds. @@ -26,188 +32,187 @@ function range(lower: number, upper: number): number[] { return arr; } -export default class ReadingsCSVUploadComponent extends React.Component { - private fileInput: React.RefObject; - constructor(props: ReadingsCSVUploadProps) { - super(props); - this.handleSetMeterName = this.handleSetMeterName.bind(this); - this.handleSetTimeSort = this.handleSetTimeSort.bind(this); - this.handleSetDuplications = this.handleSetDuplications.bind(this); - this.handleSetCumulative = this.handleSetCumulative.bind(this); - this.handleSetCumulativeReset = this.handleSetCumulativeReset.bind(this); - this.handleSetCumulativeResetStart = this.handleSetCumulativeResetStart.bind(this); - this.handleSetCumulativeResetEnd = this.handleSetCumulativeResetEnd.bind(this); - this.handleSetLengthGap = this.handleSetLengthGap.bind(this); - this.handleSetLengthVariation = this.handleSetLengthVariation.bind(this); - this.handleSetEndOnly = this.handleSetEndOnly.bind(this); - this.handleSetHonorDst = this.handleSetHonorDst.bind(this); - this.handleSetRelaxedParsing = this.handleSetRelaxedParsing.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.fileInput = React.createRef(); - } - - private async handleSubmit(e: React.MouseEvent) { - try { - e.preventDefault(); - const current = this.fileInput.current as HTMLInputElement; - const { files } = current; - if (files && (files as FileList).length !== 0) { - const msg = await this.props.submitCSV(files[0]); - // If the meter was created then update the meter state so it is available. - // Getting this needed state is a pain with the old React system. Thus, - // this fetches the meter state in all cases. This should be fairly fast, - // esp. compared to the upload time. - // TODO When this is converted to React hooks it is hoped that getting the - // value about whether the meter was created will be easier and can be done then. - // Also, you cannot dispatch with Hooks so this is left until then. For now, - // we reload after the alert. - // dispatch(fetchMetersDetails()); - // TODO Using an alert is not the best. At some point this should be integrated - // with react. - // There should be a message (not void) but that is not the type so overriding. - showInfoNotification(msg as unknown as string); - // TODO remove when the above TODO is done. - window.location.reload(); - } - } catch (error) { - // A failed axios request should result in an error. - showErrorNotification(error.response.data as string); - } - } - - private handleSetMeterName(e: React.ChangeEvent) { - const target = e.target; - this.props.setMeterName(MODE.readings, target.value); - } - - private handleSetTimeSort(e: React.ChangeEvent) { - const target = e.target; - this.props.selectTimeSort(target.value as TimeSortTypes); - } - - private handleSetDuplications(e: React.ChangeEvent) { - const target = e.target; - this.props.selectDuplications(target.value); - } - - private handleSetCumulative(e: React.ChangeEvent) { - const target = e.target; - this.props.selectCumulative(target.value as BooleanMeterTypes); - } - - private handleSetCumulativeReset(e: React.ChangeEvent) { - const target = e.target; - this.props.selectCumulativeReset(target.value as BooleanMeterTypes); - } +/** + * Defines the CSV Readings page card view + * @returns CSV Readings page element + */ +export default function ReadingsCSVUploadComponent() { - private handleSetCumulativeResetStart(e: React.ChangeEvent) { - const target = e.target; - this.props.setCumulativeResetStart(target.value); - } + // Check for admin status + const isAdmin = useAppSelector(selectIsAdmin); + // page may contain admin info so verify admin status while admin is authenticated. + authApi.useTokenPollQuery(undefined, { skip: !isAdmin, pollingInterval: authPollInterval }); + // We only want displayable meters if non-admins because they still have + // non-displayable in state. + const { visibleMeters } = useAppSelector(selectVisibleMeterAndGroupData); + const [readingsData, setReadingsData] = React.useState(ReadingsCSVUploadDefaults); + const fileInputReference = React.useRef(null); + const [selectedFile, setSelectedFile] = React.useState(null); + const [selectedMeter, setSelectedMeter] = React.useState(null); + const [createdMeterIdentifier, setCreatedMeterIdentifier] = React.useState(null); - private handleSetCumulativeResetEnd(e: React.ChangeEvent) { - const target = e.target; - this.props.setCumulativeResetEnd(target.value); - } + // gets the meter identifier and updates the created meter identifier to signal a new meter was created + const handleCreateMeter = async (meterIdentifier: string) => { + // Handle the returned meter data here in the parent component + setCreatedMeterIdentifier(meterIdentifier); + }; - private handleSetLengthGap(e: React.ChangeEvent) { - const target = e.target; - this.props.setLengthGap(target.value); - } + // If a new meter was created then select it as the meter to be used + React.useEffect(() => { + if (createdMeterIdentifier) { + const createdMeter = visibleMeters.find(meter => meter.identifier === createdMeterIdentifier) || null; + if (createdMeter) { + setSelectedMeter(createdMeter); + } + } + }, [createdMeterIdentifier,visibleMeters]); - private handleSetLengthVariation(e: React.ChangeEvent) { - const target = e.target; - this.props.setLengthVariation(target.value); - } + const handleFileChange = (file: File | null) => { + setSelectedFile(file); + }; - private handleSetEndOnly(e: React.ChangeEvent) { - const target = e.target; - this.props.selectEndOnly(target.value as BooleanMeterTypes); - } + const handleSelectedMeterChange = (e: React.ChangeEvent) => { + const selectedMeterId = parseInt(e.target.value, 10); + const foundMeter = visibleMeters.find(meter => meter.id === selectedMeterId) || null; + setSelectedMeter(foundMeter); + }; - private handleSetHonorDst() { - this.props.toggleHonorDst(); - } + const handleChange = (e:React.ChangeEvent) => { + const { name, value } = e.target; + setReadingsData(prevState => ({ + ...prevState, + [name]: value + })); + }; - private handleSetRelaxedParsing() { - this.props.toggleRelaxedParsing(); - } + const handleCheckboxChange = (e: React.ChangeEvent) => { + const { name, checked } = e.target; + setReadingsData(prevState => ({ + ...prevState, + [name]: checked + })); + }; - public render() { - const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' - }; + const getBooleanMeterType = (value: boolean | undefined) => { + switch (value) { + case true: + return BooleanMeterTypes.true; + case false: + return BooleanMeterTypes.false; + default: + return ''; + } + }; - const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' - }; + const submitReadings = async (file: File) => { + return await uploadCSVApi.submitReadings(readingsData, file); + }; - const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' - }; + // dispatch(fetchMetersDetails()); + // TODO Using an alert is not the best. At some point this should be integrated + // with react. + // There should be a message (not void) but that is not the type so overriding. + const handleSubmit = async (e: React.MouseEvent) => { + e.preventDefault(); + if (selectedFile) { + try { + const msg = await submitReadings(selectedFile); + showInfoNotification(msg as unknown as string); + window.location.reload(); + } catch (error) { + // A failed axios request should result in an error. + showErrorNotification(error.response.data as string); + } + } + }; - return ( -
-
- - - - - - - - - - - - - - - - - - - - - {range(1, 10).map(i => ( - - ))} - - - - - - + return ( +
+ + + + + + + {Array.from(visibleMeters).map(meter => { + return (); + })} + + +

+ + + +
+ + + + + + + + + + + + + + + {range(1, 10).map(i => ( + + ))} + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); - } + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); } + +const titleStyle: React.CSSProperties = { + fontWeight: 'bold', + paddingBottom: '5px' +}; + +const checkboxStyle: React.CSSProperties = { + paddingBottom: '15px' +}; + +const formStyle: React.CSSProperties = { + display: 'flex', + justifyContent: 'center', + padding: '20px' +}; \ No newline at end of file diff --git a/src/client/app/components/meters/CreateMeterModalComponent.tsx b/src/client/app/components/meters/CreateMeterModalComponent.tsx index a98b93012..1e7b43a6d 100644 --- a/src/client/app/components/meters/CreateMeterModalComponent.tsx +++ b/src/client/app/components/meters/CreateMeterModalComponent.tsx @@ -28,11 +28,16 @@ import TimeZoneSelect from '../TimeZoneSelect'; import TooltipHelpComponent from '../TooltipHelpComponent'; import TooltipMarkerComponent from '../TooltipMarkerComponent'; +interface CreateMeterModalProps { + onCreateMeter?: (meterIdentifier: string) => void; // Define the type of the callback function +} + /** * Defines the create meter modal form + * @param props for create meter to return the identifier * @returns Meter create element */ -export default function CreateMeterModalComponent() { +export default function CreateMeterModalComponent(props: CreateMeterModalProps): React.JSX.Element { // Tracks whether a unit/ default unit has been selected. // RTKQ Mutation to submit add meter const [submitAddMeter] = metersApi.endpoints.addMeter.useMutation(); @@ -152,6 +157,9 @@ export default function CreateMeterModalComponent() { // if successful, the mutation will invalidate existing cache causing all meter details to be retrieved showSuccessNotification(translate('meter.successfully.create.meter')); resetState(); + if (props.onCreateMeter) { + props.onCreateMeter(meterDetails.identifier); + } }) .catch(err => { showErrorNotification(translate('meter.failed.to.create.meter') + '"' + err.data + '"'); @@ -162,7 +170,6 @@ export default function CreateMeterModalComponent() { } }; - const tooltipStyle = { ...tooltipBaseStyle, // Only an admin can create a meter. diff --git a/src/client/app/translations/data.ts b/src/client/app/translations/data.ts index 9a0c1fc23..baa1831cf 100644 --- a/src/client/app/translations/data.ts +++ b/src/client/app/translations/data.ts @@ -85,6 +85,8 @@ const LocaleTranslationData = { "create.user": "Create a User", "create.unit": "Create a Unit", "csv": "CSV", + "csvMeters": "CSV Meters", + "csvReadings": "CSV Readings", "csv.file": "CSV File", "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Header Row", From 62698fdc8c3d54f8bd8bb35c5dadabc83d523cbe Mon Sep 17 00:00:00 2001 From: mikedivine Date: Mon, 24 Jun 2024 23:29:40 +0000 Subject: [PATCH 02/45] Finished formatting formatting CSV and updating tooltip data, tested with csv uploads manually and looks to be working --- .../components/FormFileUploaderComponent.tsx | 29 +- .../app/components/TooltipHelpComponent.tsx | 2 + .../csv/MetersCSVUploadComponent.tsx | 163 +++-- .../csv/ReadingsCSVUploadComponent.tsx | 580 +++++++++++------- .../app/containers/csv/UploadCSVContainer.tsx | 325 ---------- src/client/app/translations/data.ts | 34 +- src/client/app/types/csvUploadForm.ts | 34 +- src/client/app/utils/api/UploadCSVApi.ts | 1 - 8 files changed, 531 insertions(+), 637 deletions(-) delete mode 100644 src/client/app/containers/csv/UploadCSVContainer.tsx diff --git a/src/client/app/components/FormFileUploaderComponent.tsx b/src/client/app/components/FormFileUploaderComponent.tsx index d74ed78e3..e6c502d0b 100644 --- a/src/client/app/components/FormFileUploaderComponent.tsx +++ b/src/client/app/components/FormFileUploaderComponent.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { Col, Input, FormGroup, FormText, Label } from 'reactstrap'; +import translate from '../utils/translate'; interface FileUploader { reference: React.RefObject; @@ -29,20 +30,22 @@ export default function FileUploaderComponent(props: FileUploader) { return ( - - - - - - ); } diff --git a/src/client/app/components/TooltipHelpComponent.tsx b/src/client/app/components/TooltipHelpComponent.tsx index edfaa8708..ec03e0c53 100644 --- a/src/client/app/components/TooltipHelpComponent.tsx +++ b/src/client/app/components/TooltipHelpComponent.tsx @@ -47,6 +47,8 @@ export default function TooltipHelpComponent(props: TooltipHelpProps) { 'help.admin.unitview': { link: `${helpUrl}/adminUnitViewing.html` }, 'help.admin.user': { link: `${helpUrl}/adminUser.html` }, 'help.csv.header': { link: `${helpUrl}/adminDataAcquisition.html` }, + 'help.csv.meters': { link: `${helpUrl}/adminMetersImport.html` }, + 'help.csv.readings': { link: `${helpUrl}/adminReadingsImport.html` }, 'help.home.area.normalize': { link: `${helpUrl}/areaNormalization.html` }, 'help.home.bar.custom.slider.tip': { link: `${helpUrl}/barGraphic.html#usage` }, 'help.home.bar.interval.tip': { link: `${helpUrl}/barGraphic.html#usage` }, diff --git a/src/client/app/components/csv/MetersCSVUploadComponent.tsx b/src/client/app/components/csv/MetersCSVUploadComponent.tsx index e7e8470a9..9ed09a0fe 100644 --- a/src/client/app/components/csv/MetersCSVUploadComponent.tsx +++ b/src/client/app/components/csv/MetersCSVUploadComponent.tsx @@ -4,12 +4,15 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { Button, Form, FormGroup, Input, Label } from 'reactstrap'; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; +import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; import { baseApi } from '../../redux/api/baseApi'; import { useDispatch } from 'react-redux'; import { uploadCSVApi } from '../../utils/api'; +import translate from '../../utils/translate'; interface MetersCSVUploadComponentProps {} @@ -57,54 +60,134 @@ const MetersCSVUploadComponent: React.FC = () => return await uploadCSVApi.submitMeters(meterData, file); }; - const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' + const handleClear = () => { + setMeterData({ + meterName: '', + gzip: false, + headerRow: false, + update: false}); }; - const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' + const tooltipStyle = { + display: 'inline-block', + fontSize: '50%', + tooltipReadings: 'help.csv.meters' }; - const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' + const checkBox = { + display: 'flex' }; return ( -
+ +
- - - - - - - - - - - - - - - + + +

+ {translate('csv.upload.meters')} +
+ +
+

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+ +
-
+ ); }; diff --git a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx index 5d5c7db7a..9c5d8626a 100644 --- a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx +++ b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx @@ -3,8 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Button, Col, Form, FormGroup, Input, Label } from 'reactstrap'; +import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; import { BooleanMeterTypes, TimeSortTypes, ReadingsCSVUploadPreferencesItem } from '../../types/csvUploadForm'; import { ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; import { showErrorNotification, showInfoNotification } from '../../utils/notifications'; @@ -72,9 +73,13 @@ export default function ReadingsCSVUploadComponent() { }; const handleSelectedMeterChange = (e: React.ChangeEvent) => { - const selectedMeterId = parseInt(e.target.value, 10); - const foundMeter = visibleMeters.find(meter => meter.id === selectedMeterId) || null; + const selectedMeterName = e.target.value; + const foundMeter = visibleMeters.find(meter => meter.name === selectedMeterName) || null; setSelectedMeter(foundMeter); + setReadingsData(prevState => ({ + ...prevState, + meterName: selectedMeterName + })); }; const handleChange = (e:React.ChangeEvent) => { @@ -126,224 +131,379 @@ export default function ReadingsCSVUploadComponent() { } }; + const handleClear = () => { + setSelectedMeter(null); + }; + + const tooltipStyle = { + display: 'inline-block', + fontSize: '50%', + tooltipReadings: 'help.csv.readings' + }; + + const checkBox = { + display: 'flex' + }; + return ( -
+ +
- - - - - - {Array.from(visibleMeters).map(meter => { - return (); - })} - + + +

+ {translate('csv.upload.readings')} +
+ +
+

-

- +
+ + + +
- -
- - - - - - - - - - - - - - - {range(1, 10).map(i => ( - - ))} - - - - - - - - - - + + + + + + + - - - - +
+ + Time Gaps + +
+ + + + + + + + + + + +
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ +
+
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + -
+ ); -} - -const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' -}; - -const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' -}; - -const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/app/containers/csv/UploadCSVContainer.tsx b/src/client/app/containers/csv/UploadCSVContainer.tsx deleted file mode 100644 index 8e387f36f..000000000 --- a/src/client/app/containers/csv/UploadCSVContainer.tsx +++ /dev/null @@ -1,325 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'; -import TooltipMarkerComponent from '../../components/TooltipMarkerComponent'; -import MetersCSVUploadComponent from '../../components/csv/MetersCSVUploadComponent'; -import ReadingsCSVUploadComponent from '../../components/csv/ReadingsCSVUploadComponent'; -import { BooleanMeterTypes, MetersCSVUploadPreferencesItem, ReadingsCSVUploadPreferencesItem, TimeSortTypes } from '../../types/csvUploadForm'; -import { uploadCSVApi } from '../../utils/api'; -import { MetersCSVUploadDefaults, ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; -import TooltipHelpComponent from '../../components/TooltipHelpComponent'; - -export const enum MODE { - meters = 'meters', - readings = 'readings' -} - -interface UploadCSVContainerState { - uploadReadingsPreferences: ReadingsCSVUploadPreferencesItem; - uploadMetersPreferences: MetersCSVUploadPreferencesItem; - activeTab: MODE; -} - -const tooltipStyle = { - display: 'inline-block', - fontSize: '100%' -}; - -export default class UploadCSVContainer extends React.Component<{}, UploadCSVContainerState> { - constructor(props: {}) { - super(props); - this.setMeterName = this.setMeterName.bind(this); - this.selectTimeSort = this.selectTimeSort.bind(this); - this.selectDuplications = this.selectDuplications.bind(this); - this.selectCumulative = this.selectCumulative.bind(this); - this.selectCumulativeReset = this.selectCumulativeReset.bind(this); - this.setCumulativeResetStart = this.setCumulativeResetStart.bind(this); - this.setCumulativeResetEnd = this.setCumulativeResetEnd.bind(this); - this.setLengthGap = this.setLengthGap.bind(this); - this.setLengthVariation = this.setLengthVariation.bind(this); - this.selectEndOnly = this.selectEndOnly.bind(this); - this.submitReadings = this.submitReadings.bind(this); - this.submitMeters = this.submitMeters.bind(this); - this.toggleCreateMeter = this.toggleCreateMeter.bind(this); - this.toggleGzip = this.toggleGzip.bind(this); - this.toggleHeaderRow = this.toggleHeaderRow.bind(this); - this.toggleRefreshHourlyReadings = this.toggleRefreshHourlyReadings.bind(this); - this.toggleRefreshReadings = this.toggleRefreshReadings.bind(this); - this.toggleUpdate = this.toggleUpdate.bind(this); - this.toggleTab = this.toggleTab.bind(this); - this.toggleHonorDst = this.toggleHonorDst.bind(this); - this.toggleRelaxedParsing = this.toggleRelaxedParsing.bind(this); - } - - state = { - activeTab: MODE.readings, - uploadMetersPreferences: { - ...MetersCSVUploadDefaults - }, - uploadReadingsPreferences: { - ...ReadingsCSVUploadDefaults - } - }; - - private setMeterName(mode: MODE, value: string) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - meterName: value - } - })); - } - private selectTimeSort(value: TimeSortTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - timeSort: value - } - })); - } - private selectDuplications(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - duplications: value - } - })); - } - private selectCumulative(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulative: value - } - })); - } - private selectCumulativeReset(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeReset: value - } - })); - } - private setCumulativeResetStart(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeResetStart: value - } - })); - } - private setCumulativeResetEnd(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeResetEnd: value - } - })); - } - private setLengthGap(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - lengthGap: value - } - })); - } - private setLengthVariation(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - lengthVariation: value - } - })); - } - private selectEndOnly(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - endOnly: value - } - })); - } - private toggleCreateMeter() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - createMeter: !previousState.uploadReadingsPreferences.createMeter - } - })); - } - private toggleGzip(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - gzip: !previousState[preference].gzip - } - })); - }; - } - private toggleHeaderRow(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - headerRow: !previousState[preference].headerRow - } - })); - }; - } - - private toggleRefreshHourlyReadings() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - refreshHourlyReadings: !previousState.uploadReadingsPreferences.refreshHourlyReadings - } - })); - } - - private toggleRefreshReadings() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - refreshReadings: !previousState.uploadReadingsPreferences.refreshReadings - } - })); - } - - private toggleHonorDst() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - honorDst: !previousState.uploadReadingsPreferences.honorDst - } - })); - } - - private toggleRelaxedParsing() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - relaxedParsing: !previousState.uploadReadingsPreferences.relaxedParsing - } - })); - } - - private toggleUpdate(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - update: !previousState[preference].update - } - })); - }; - } - - private async submitReadings(file: File) { - const uploadPreferences = this.state.uploadReadingsPreferences; - return await uploadCSVApi.submitReadings(uploadPreferences, file); - } - - private async submitMeters(file: File) { - const uploadPreferences = this.state.uploadMetersPreferences; - await uploadCSVApi.submitMeters(uploadPreferences, file); - } - - private toggleTab(tab: MODE) { - if (this.state.activeTab !== tab) { - this.setState({ - activeTab: tab - }); - } - } - - public render() { - const navStyle: React.CSSProperties = { - cursor: 'pointer' - }; - return ( -
- - - - - - - - - - -
- ); - } -} diff --git a/src/client/app/translations/data.ts b/src/client/app/translations/data.ts index baa1831cf..073f21cdc 100644 --- a/src/client/app/translations/data.ts +++ b/src/client/app/translations/data.ts @@ -87,7 +87,8 @@ const LocaleTranslationData = { "csv": "CSV", "csvMeters": "CSV Meters", "csvReadings": "CSV Readings", - "csv.file": "CSV File", + "csv.file": "CSV File:", + "csv.clear.button": "Clear Form", "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Header Row", "csv.common.param.update": "Update", @@ -95,27 +96,27 @@ const LocaleTranslationData = { "csv.download.size.warning.size": "Total size of all files will be about (usually within 10% for large exports).", "csv.download.size.warning.verify": "Are you sure you want to download", "csv.readings.param.create.meter": "Create Meter", - "csv.readings.param.cumulative": "Cumulative", - "csv.readings.param.cumulative.reset": "Cumulative Reset", - "csv.readings.param.cumulative.reset.end": "Cumulative Reset End", - "csv.readings.param.cumulative.reset.start": "Cumulative Reset Start", - "csv.readings.param.duplications": "Duplications", - "csv.readings.param.endOnly": "End Only times", + "csv.readings.param.cumulative": "Cumulative:", + "csv.readings.param.cumulative.reset": "Cumulative Reset:", + "csv.readings.param.cumulative.reset.end": "Cumulative Reset End:", + "csv.readings.param.cumulative.reset.start": "Cumulative Reset Start:", + "csv.readings.param.duplications": "Duplications:", + "csv.readings.param.endOnly": "End Only times:", "csv.readings.param.honor.dst": "Honor Daylight Savings Time", - "csv.readings.param.lengthGap": "Length Gap", - "csv.readings.param.length.variation": "Length Variation", - "csv.readings.param.meter.name": "Meter name", - "csv.readings.param.refresh.hourlyReadings": "Refresh Hourly Readings", + "csv.readings.param.length.gap": "Length Gap:", + "csv.readings.param.length.variation": "Length Variation:", + "csv.readings.param.meter.name": "Meter name:", + "csv.readings.param.refresh.hourly.readings": "Refresh Hourly Readings", "csv.readings.param.refresh.readings": "Refresh Daily Readings", "csv.readings.param.relaxed.parsing": "Relaxed Parsing", - "csv.readings.param.time.sort": "Time Sort", + "csv.readings.param.time.sort": "Time Sort:", "csv.readings.section.cumulative.data": "Cumulative Data", "csv.readings.section.time.gaps": "Time Gaps", "csv.submit.button": "Submit CSV Data", "csv.tab.meters": "Meters", "csv.tab.readings": "Readings", - "csv.upload.meters": "Upload Meters CSV ", - "csv.upload.readings": "Upload Readings CSV ", + "csv.upload.meters": "Upload Meters", + "csv.upload.readings": "Upload Readings", "date.range": 'Date Range', "day": "Day", "days": "Days", @@ -230,7 +231,8 @@ const LocaleTranslationData = { "help.admin.unitedit": "This page allows admins to edit units. Please visit {link} for further details and information.", "help.admin.unitview": "This page shows information on units. Please visit {link} for further details and information.", "help.admin.user": "This page allows admins to view and edit users. Please visit {link} for further details and information.", - "help.csv.header": "This page allows certain users to upload meters and readings via a CSV file. Please visit {link} for further details and information.", + "help.csv.meters": "This page allows certain users to upload meters via a CSV file. Please visit {link} for further details and information.", + "help.csv.readings": "This page allows certain users to upload readings via a CSV file. Please visit {link} for further details and information.", "help.groups.groupdetails": "This page shows detailed information on a group. Please visit {link} for further details and information.", "help.groups.groupview": "This page shows information on groups. Please visit {link} for further details and information.", "help.groups.area.calculate": "This will sum together the area of all meters in this group with a nonzero area with an area unit. It will ignore any meters which have no area or area unit. If this group has no area unit, it will do nothing.", @@ -580,6 +582,7 @@ const LocaleTranslationData = { "create.user": "Créer un utilisateur", "csv": "CSV", "csv.file": "Fichier CSV", + "csv.clear.button": "Forme claire", "csv.common.param.gzip": "GzipComment", "csv.common.param.header.row": "Ligne d'en-tête", "csv.common.param.update": "Mise à jour", @@ -1071,6 +1074,7 @@ const LocaleTranslationData = { "create.user": "Crear un usuario", "csv": "CSV", "csv.file": "Archivo de CSV", + "csv.clear.button": "Forma clara", "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Fila de cabecera", "csv.common.param.update": "Actualización", diff --git a/src/client/app/types/csvUploadForm.ts b/src/client/app/types/csvUploadForm.ts index dfc37a76b..7c210c434 100644 --- a/src/client/app/types/csvUploadForm.ts +++ b/src/client/app/types/csvUploadForm.ts @@ -2,8 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { MODE } from '../containers/csv/UploadCSVContainer'; - interface CSVUploadPreferences { meterName: string; gzip: boolean; @@ -19,14 +17,6 @@ export interface CSVUploadPreferencesForm { update: BooleanTypes; } -interface CSVUploadProps extends CSVUploadPreferences { - submitCSV: (file: File) => Promise; - setMeterName: (mode: MODE, value: string) => void; - toggleGzip: () => void; - toggleHeaderRow: () => void; - toggleUpdate: () => void; -} - // This relates to MeterTimeSortTypes in src/client/app/types/redux/meters.ts but also has 'meter value or default'. // They should be kept in sync. export const enum TimeSortTypes { @@ -53,7 +43,6 @@ export const enum BooleanMeterTypes { } export interface ReadingsCSVUploadPreferencesItem extends CSVUploadPreferences { - createMeter: boolean; cumulative: BooleanMeterTypes; cumulativeReset: BooleanMeterTypes; cumulativeResetStart: string; @@ -71,7 +60,6 @@ export interface ReadingsCSVUploadPreferencesItem extends CSVUploadPreferences { } export interface ReadingsCSVUploadPreferencesForm extends CSVUploadPreferencesForm { - createMeter: BooleanTypes; cumulative: BooleanMeterTypes; cumulativeReset: BooleanMeterTypes; cumulativeResetStart: string; @@ -88,29 +76,9 @@ export interface ReadingsCSVUploadPreferencesForm extends CSVUploadPreferencesFo relaxedParsing: BooleanTypes; } -export interface ReadingsCSVUploadProps extends ReadingsCSVUploadPreferencesItem, CSVUploadProps{ - // Note: each of these will have to change in consideration of redux; - selectTimeSort: (value: TimeSortTypes) => void; - selectDuplications: (value: string) => void; - selectCumulative: (value: BooleanMeterTypes) => void; - selectCumulativeReset: (value: BooleanMeterTypes) => void; - setCumulativeResetStart: (value: string) => void; - setCumulativeResetEnd: (value: string) => void; - setLengthGap: (value: string) => void; - setLengthVariation: (value: string) => void; - selectEndOnly: (value: string) => void; - toggleCreateMeter: () => void; - toggleRefreshHourlyReadings: () => void; - toggleRefreshReadings: () => void; - toggleHonorDst: () => void; - toggleRelaxedParsing: () => void; -} - // MetersCSVUpload, MetersCSVUploadPreferencesItem, MetersCSVUploadProps should be interfaces. However, at the moment does not add anything new. // Hence, we define a new type rather than a new interface that extends CSVUploadPreferences and CSVUploadProps to pass our linter. export type MetersCSVUpload = CSVUploadPreferences; -export type MetersCSVUploadPreferencesItem = MetersCSVUpload; - -export type MetersCSVUploadProps = CSVUploadProps; +export type MetersCSVUploadPreferencesItem = MetersCSVUpload; \ No newline at end of file diff --git a/src/client/app/utils/api/UploadCSVApi.ts b/src/client/app/utils/api/UploadCSVApi.ts index 52294d9f3..5da9a3979 100644 --- a/src/client/app/utils/api/UploadCSVApi.ts +++ b/src/client/app/utils/api/UploadCSVApi.ts @@ -24,7 +24,6 @@ export default class UploadCSVApi { gzip: uploadPreferences.gzip ? BooleanTypes.true : BooleanTypes.false, headerRow: uploadPreferences.headerRow ? BooleanTypes.true : BooleanTypes.false, update: uploadPreferences.update ? BooleanTypes.true : BooleanTypes.false, - createMeter: uploadPreferences.createMeter ? BooleanTypes.true : BooleanTypes.false, refreshHourlyReadings: uploadPreferences.refreshHourlyReadings ? BooleanTypes.true : BooleanTypes.false, refreshReadings: uploadPreferences.refreshReadings ? BooleanTypes.true : BooleanTypes.false, honorDst: uploadPreferences.honorDst ? BooleanTypes.true : BooleanTypes.false, From fe2117ced019b286a0f7add0086b1133177aad16 Mon Sep 17 00:00:00 2001 From: mikedivine Date: Tue, 25 Jun 2024 00:15:02 +0000 Subject: [PATCH 03/45] fixed csvUploadDefaults to remove create meter option --- src/client/app/utils/csvUploadDefaults.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/app/utils/csvUploadDefaults.ts b/src/client/app/utils/csvUploadDefaults.ts index 114732acc..03811ce38 100644 --- a/src/client/app/utils/csvUploadDefaults.ts +++ b/src/client/app/utils/csvUploadDefaults.ts @@ -17,7 +17,6 @@ export const ReadingsCSVUploadDefaults: ReadingsCSVUploadPreferencesItem = { lengthGap: '', lengthVariation: '', endOnly: BooleanMeterTypes.meter, - createMeter: false, gzip: false, headerRow: false, refreshHourlyReadings: false, From bbcb16404aa76f6d35211cfc5991415d446b0a3c Mon Sep 17 00:00:00 2001 From: rfrost30 Date: Thu, 27 Jun 2024 16:52:07 +0000 Subject: [PATCH 04/45] fixed MetersCSVUpload to align to top of the page --- src/client/app/components/csv/MetersCSVUploadComponent.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/app/components/csv/MetersCSVUploadComponent.tsx b/src/client/app/components/csv/MetersCSVUploadComponent.tsx index 9ed09a0fe..deb1b164e 100644 --- a/src/client/app/components/csv/MetersCSVUploadComponent.tsx +++ b/src/client/app/components/csv/MetersCSVUploadComponent.tsx @@ -75,14 +75,15 @@ const MetersCSVUploadComponent: React.FC = () => }; const checkBox = { - display: 'flex' + display: 'flex', + alignItems: 'center' }; return ( - +
- +

{translate('csv.upload.meters')} From 2ed3395fa991c8582729f51f22097cef67a74339 Mon Sep 17 00:00:00 2001 From: mikedivine Date: Thu, 27 Jun 2024 19:02:04 +0000 Subject: [PATCH 05/45] formatted imports and file, fixed tooltip headings, fixed menu items grayed out if on that page --- .../app/components/HeaderButtonsComponent.tsx | 10 ++++--- .../csv/MetersCSVUploadComponent.tsx | 21 +++++++------ .../csv/ReadingsCSVUploadComponent.tsx | 30 +++++++++---------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/client/app/components/HeaderButtonsComponent.tsx b/src/client/app/components/HeaderButtonsComponent.tsx index fa51d389c..982aef076 100644 --- a/src/client/app/components/HeaderButtonsComponent.tsx +++ b/src/client/app/components/HeaderButtonsComponent.tsx @@ -57,7 +57,8 @@ export default function HeaderButtonsComponent() { shouldGroupsButtonDisabled: true, shouldMetersButtonDisabled: true, shouldMapsButtonDisabled: true, - shouldCSVButtonDisabled: true, + shouldCSVMetersButtonDisabled: true, + shouldCSVReadingsButtonDisabled: true, shouldUnitsButtonDisabled: true, shouldConversionsButtonDisabled: true, // Translated menu title that depend on whether logged in. @@ -91,7 +92,8 @@ export default function HeaderButtonsComponent() { shouldGroupsButtonDisabled: pathname === '/groups', shouldMetersButtonDisabled: pathname === '/meters', shouldMapsButtonDisabled: pathname === '/maps', - shouldCSVButtonDisabled: pathname === '/csv', + shouldCSVMetersButtonDisabled: pathname === '/csvMeters', + shouldCSVReadingsButtonDisabled: pathname === '/csvReadings', shouldUnitsButtonDisabled: pathname === '/units', shouldConversionsButtonDisabled: pathname === '/conversions' })); @@ -172,14 +174,14 @@ export default function HeaderButtonsComponent() { diff --git a/src/client/app/components/csv/MetersCSVUploadComponent.tsx b/src/client/app/components/csv/MetersCSVUploadComponent.tsx index deb1b164e..ead051c56 100644 --- a/src/client/app/components/csv/MetersCSVUploadComponent.tsx +++ b/src/client/app/components/csv/MetersCSVUploadComponent.tsx @@ -4,15 +4,15 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import TooltipHelpComponent from '../TooltipHelpComponent'; -import TooltipMarkerComponent from '../TooltipMarkerComponent'; +import { useDispatch } from 'react-redux'; import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; -import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; -import FormFileUploaderComponent from '../FormFileUploaderComponent'; import { baseApi } from '../../redux/api/baseApi'; -import { useDispatch } from 'react-redux'; import { uploadCSVApi } from '../../utils/api'; +import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; import translate from '../../utils/translate'; +import FormFileUploaderComponent from '../FormFileUploaderComponent'; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; interface MetersCSVUploadComponentProps {} @@ -75,20 +75,19 @@ const MetersCSVUploadComponent: React.FC = () => }; const checkBox = { - display: 'flex', - alignItems: 'center' + display: 'flex' }; return ( - - + + - +

{translate('csv.upload.meters')}
- +

diff --git a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx index 9c5d8626a..b725f77d0 100644 --- a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx +++ b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx @@ -4,20 +4,20 @@ import * as React from 'react'; import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; -import TooltipHelpComponent from '../TooltipHelpComponent'; -import TooltipMarkerComponent from '../TooltipMarkerComponent'; -import { BooleanMeterTypes, TimeSortTypes, ReadingsCSVUploadPreferencesItem } from '../../types/csvUploadForm'; +import { authApi, authPollInterval } from '../../redux/api/authApi'; +import { useAppSelector } from '../../redux/reduxHooks'; +import { selectVisibleMeterAndGroupData } from '../../redux/selectors/adminSelectors'; +import { selectIsAdmin } from '../../redux/slices/currentUserSlice'; +import { BooleanMeterTypes, ReadingsCSVUploadPreferencesItem, TimeSortTypes } from '../../types/csvUploadForm'; +import { MeterData } from '../../types/redux/meters'; +import { uploadCSVApi } from '../../utils/api'; import { ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; import { showErrorNotification, showInfoNotification } from '../../utils/notifications'; import translate from '../../utils/translate'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; -import { uploadCSVApi } from '../../utils/api'; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; import CreateMeterModalComponent from '../meters/CreateMeterModalComponent'; -import { useAppSelector } from '../../redux/reduxHooks'; -import { authApi, authPollInterval } from '../../redux/api/authApi'; -import { selectIsAdmin } from '../../redux/slices/currentUserSlice'; -import { selectVisibleMeterAndGroupData } from '../../redux/selectors/adminSelectors'; -import { MeterData } from '../../types/redux/meters'; /** * Returns a range of values between the specified lower and upper bounds. @@ -34,7 +34,7 @@ function range(lower: number, upper: number): number[] { } /** - * Defines the CSV Readings page card view + * Defines the CSV Readings page * @returns CSV Readings page element */ export default function ReadingsCSVUploadComponent() { @@ -66,7 +66,7 @@ export default function ReadingsCSVUploadComponent() { setSelectedMeter(createdMeter); } } - }, [createdMeterIdentifier,visibleMeters]); + }, [createdMeterIdentifier, visibleMeters]); const handleFileChange = (file: File | null) => { setSelectedFile(file); @@ -82,7 +82,7 @@ export default function ReadingsCSVUploadComponent() { })); }; - const handleChange = (e:React.ChangeEvent) => { + const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setReadingsData(prevState => ({ ...prevState, @@ -147,14 +147,14 @@ export default function ReadingsCSVUploadComponent() { return ( - +

{translate('csv.upload.readings')}
- +

@@ -178,7 +178,7 @@ export default function ReadingsCSVUploadComponent() {
- +


- -

+ {isAdmin && <>

}