Skip to content

Commit

Permalink
Merge pull request #989 from TrongQuocLe/new_UI_setChecks
Browse files Browse the repository at this point in the history
Enable reading validation via admin input
  • Loading branch information
huss authored Aug 26, 2023
2 parents 4388f54 + c90e2c5 commit cc65531
Show file tree
Hide file tree
Showing 35 changed files with 1,068 additions and 103 deletions.
73 changes: 72 additions & 1 deletion src/client/app/actions/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@ export function updateDefaultMeterReadingFrequency(defaultMeterReadingFrequency:
return { type: ActionType.UpdateDefaultMeterReadingFrequency, defaultMeterReadingFrequency };
}

export function updateDefaultMeterMinimumValue(defaultMeterMinimumValue: number): t.UpdateDefaultMeterMinimumValueAction {
return { type: ActionType.UpdateDefaultMeterMinimumValue, defaultMeterMinimumValue };
}

export function updateDefaultMeterMaximumValue(defaultMeterMaximumValue: number): t.UpdateDefaultMeterMaximumValueAction {
return { type: ActionType.UpdateDefaultMeterMaximumValue, defaultMeterMaximumValue };
}

export function updateDefaultMeterMinimumDate(defaultMeterMinimumDate: string): t.UpdateDefaultMeterMinimumDateAction {
return { type: ActionType.UpdateDefaultMeterMinimumDate, defaultMeterMinimumDate };
}

export function updateDefaultMeterMaximumDate(defaultMeterMaximumDate: string): t.UpdateDefaultMeterMaximumDateAction {
return { type: ActionType.UpdateDefaultMeterMaximumDate, defaultMeterMaximumDate };
}

export function updateDefaultMeterReadingGap(defaultMeterReadingGap: number): t.UpdateDefaultMeterReadingGapAction {
return { type: ActionType.UpdateDefaultMeterReadingGap, defaultMeterReadingGap };
}

export function updateDefaultMeterMaximumErrors(defaultMeterMaximumErrors: number): t.UpdateDefaultMeterMaximumErrorsAction {
return { type: ActionType.UpdateDefaultMeterMaximumErrors, defaultMeterMaximumErrors };
}

export function updateDefaultMeterDisableChecks(defaultMeterDisableChecks: boolean): t.UpdateDefaultMeterDisableChecksAction {
return { type: ActionType.UpdateDefaultMeterDisableChecks, defaultMeterDisableChecks };
}

function requestPreferences(): t.RequestPreferencesAction {
return { type: ActionType.RequestPreferences };
}
Expand Down Expand Up @@ -103,14 +131,50 @@ function fetchPreferences(): Thunk {
}
};
}
// TODO: Add warning for invalid data in admin panel src/client/app/components/admin/PreferencesComponent.tsx
/* Validates preferences
Create Preferences Validation:
Mininum Value cannot bigger than Maximum Value
Minimum Value and Maximum Value must be between valid input
Minimum Date and Maximum cannot be blank
Minimum Date cannot be after Maximum Date
Minimum Date and Maximum Value must be between valid input
Maximum No of Error must be between 0 and valid input
*/

function validPreferences(state: State) {
const MIN_VAL = Number.MIN_SAFE_INTEGER;
const MAX_VAL = Number.MAX_SAFE_INTEGER;
const MIN_DATE_MOMENT = moment(0).utc();
const MAX_DATE_MOMENT = moment(0).utc().add(5000, 'years');
const MAX_ERRORS = 75;
if (state.admin.defaultMeterReadingGap >= 0 &&
state.admin.defaultMeterMinimumValue >= MIN_VAL &&
state.admin.defaultMeterMinimumValue <= state.admin.defaultMeterMaximumValue &&
state.admin.defaultMeterMinimumValue <= MAX_VAL &&
state.admin.defaultMeterMinimumDate !== '' &&
state.admin.defaultMeterMaximumDate !== '' &&
moment(state.admin.defaultMeterMinimumDate).isValid() &&
moment(state.admin.defaultMeterMaximumDate).isValid() &&
moment(state.admin.defaultMeterMinimumDate).isSameOrAfter(MIN_DATE_MOMENT) &&
moment(state.admin.defaultMeterMinimumDate).isSameOrBefore(moment(state.admin.defaultMeterMaximumDate)) &&
moment(state.admin.defaultMeterMaximumDate).isSameOrBefore(MAX_DATE_MOMENT) &&
(state.admin.defaultMeterMaximumErrors >= 0 && state.admin.defaultMeterMaximumErrors <= MAX_ERRORS)) {
return true;
} else {
return false;
}
}
/**
* Submits preferences stored in the state to the API to be stored in the database
*/
export function submitPreferences() {
return async (dispatch: Dispatch, getState: GetState) => {
const state = getState();
try {
if (!validPreferences(state)) {
throw new Error('invalid input');
}
const preferences = await preferencesApi.submitPreferences({
displayTitle: state.admin.displayTitle,
defaultChartToRender: state.admin.defaultChartToRender,
Expand All @@ -121,7 +185,14 @@ export function submitPreferences() {
defaultFileSizeLimit: state.admin.defaultFileSizeLimit,
defaultAreaNormalization: state.admin.defaultAreaNormalization,
defaultAreaUnit: state.admin.defaultAreaUnit,
defaultMeterReadingFrequency: state.admin.defaultMeterReadingFrequency
defaultMeterReadingFrequency: state.admin.defaultMeterReadingFrequency,
defaultMeterMinimumValue: state.admin.defaultMeterMinimumValue,
defaultMeterMaximumValue: state.admin.defaultMeterMaximumValue,
defaultMeterMinimumDate: state.admin.defaultMeterMinimumDate,
defaultMeterMaximumDate: state.admin.defaultMeterMaximumDate,
defaultMeterReadingGap: state.admin.defaultMeterReadingGap,
defaultMeterMaximumErrors: state.admin.defaultMeterMaximumErrors,
defaultMeterDisableChecks: state.admin.defaultMeterDisableChecks
});
// Only return the defaultMeterReadingFrequency because the value from the DB
// generally differs from what the user input so update state with DB value.
Expand Down
2 changes: 1 addition & 1 deletion src/client/app/components/GraphicRateMenuComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ export default function GraphicRateMenuComponent() {
}
</div>
);
}
}
149 changes: 148 additions & 1 deletion src/client/app/components/admin/PreferencesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ import {
UpdateDefaultFileSizeLimit,
ToggleDefaultAreaNormalizationAction,
UpdateDefaultAreaUnitAction,
UpdateDefaultMeterReadingFrequencyAction
UpdateDefaultMeterReadingFrequencyAction,
UpdateDefaultMeterMinimumValueAction,
UpdateDefaultMeterMaximumValueAction,
UpdateDefaultMeterMinimumDateAction,
UpdateDefaultMeterMaximumDateAction,
UpdateDefaultMeterReadingGapAction,
UpdateDefaultMeterMaximumErrorsAction,
UpdateDefaultMeterDisableChecksAction

} from '../../types/redux/admin';
import { removeUnsavedChanges, updateUnsavedChanges } from '../../actions/unsavedWarning';
import { defineMessages, FormattedMessage, injectIntl, WrappedComponentProps } from 'react-intl';
Expand All @@ -24,6 +32,8 @@ import TimeZoneSelect from '../TimeZoneSelect';
import store from '../../index';
import { fetchPreferencesIfNeeded, submitPreferences } from '../../actions/admin';
import { AreaUnitType } from '../../utils/getAreaUnitConversion';
import translate from '../../utils/translate';
import { TrueFalseType } from '../../types/items';

interface PreferencesProps {
displayTitle: string;
Expand All @@ -37,6 +47,13 @@ interface PreferencesProps {
defaultFileSizeLimit: number;
defaultAreaUnit: AreaUnitType;
defaultMeterReadingFrequency: string;
defaultMeterMinimumValue: number;
defaultMeterMaximumValue: number;
defaultMeterMinimumDate: string;
defaultMeterMaximumDate: string;
defaultMeterReadingGap: number;
defaultMeterMaximumErrors: number;
defaultMeterDisableChecks: boolean;
updateDisplayTitle(title: string): UpdateDisplayTitleAction;
updateDefaultChartType(defaultChartToRender: ChartTypes): UpdateDefaultChartToRenderAction;
toggleDefaultBarStacking(): ToggleDefaultBarStackingAction;
Expand All @@ -48,10 +65,18 @@ interface PreferencesProps {
updateDefaultFileSizeLimit(defaultFileSizeLimit: number): UpdateDefaultFileSizeLimit;
updateDefaultAreaUnit(defaultAreaUnit: AreaUnitType): UpdateDefaultAreaUnitAction;
updateDefaultMeterReadingFrequency(defaultMeterReadingFrequency: string): UpdateDefaultMeterReadingFrequencyAction;
updateDefaultMeterMinimumValue(defaultMeterMinimumValue : number): UpdateDefaultMeterMinimumValueAction;
updateDefaultMeterMaximumValue(defaultMeterMaximumValue: number): UpdateDefaultMeterMaximumValueAction;
updateDefaultMeterMinimumDate(defaultMeterMinimumDate: string): UpdateDefaultMeterMinimumDateAction;
updateDefaultMeterMaximumDate(defaultMeterMaximumDate: string): UpdateDefaultMeterMaximumDateAction;
updateDefaultMeterReadingGap(defaultMeterReadingGap: number): UpdateDefaultMeterReadingGapAction;
updateDefaultMeterMaximumErrors(defaultMeterMaximumErrors: number): UpdateDefaultMeterMaximumErrorsAction;
updateDefaultMeterDisableChecks(defaultMeterDisableChecks: boolean): UpdateDefaultMeterDisableChecksAction;
}

type PreferencesPropsWithIntl = PreferencesProps & WrappedComponentProps;

// TODO: Add warning for invalid data
class PreferencesComponent extends React.Component<PreferencesPropsWithIntl> {
constructor(props: PreferencesPropsWithIntl) {
super(props);
Expand All @@ -66,6 +91,13 @@ class PreferencesComponent extends React.Component<PreferencesPropsWithIntl> {
this.handleDefaultAreaNormalizationChange = this.handleDefaultAreaNormalizationChange.bind(this);
this.handleDefaultAreaUnitChange = this.handleDefaultAreaUnitChange.bind(this);
this.handleDefaultMeterReadingFrequencyChange = this.handleDefaultMeterReadingFrequencyChange.bind(this);
this.handleDefaultMeterMinimumValueChange = this.handleDefaultMeterMinimumValueChange.bind(this);
this.handleDefaultMeterMaximumValueChange = this.handleDefaultMeterMaximumValueChange.bind(this);
this.handleDefaultMeterMinimumDateChange = this.handleDefaultMeterMinimumDateChange.bind(this);
this.handleDefaultMeterMaximumDateChange = this.handleDefaultMeterMaximumDateChange.bind(this);
this.handleDefaultMeterReadingGapChange = this.handleDefaultMeterReadingGapChange.bind(this);
this.handleDefaultMeterMaximumErrorsChange = this.handleDefaultMeterMaximumErrorsChange.bind(this);
this.handleDefaultMeterDisableChecksChange = this.handleDefaultMeterDisableChecksChange.bind(this);
}

public render() {
Expand Down Expand Up @@ -294,6 +326,85 @@ class PreferencesComponent extends React.Component<PreferencesPropsWithIntl> {
onChange={this.handleDefaultMeterReadingFrequencyChange}
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.minimum.value' />:
</p>
<Input
type='number'
value={this.props.defaultMeterMinimumValue}
onChange={this.handleDefaultMeterMinimumValueChange}
maxLength={50}
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.maximum.value' />:
</p>
<Input
type='number'
value={this.props.defaultMeterMaximumValue}
onChange={this.handleDefaultMeterMaximumValueChange}
maxLength={50}
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.minimum.date' />:
</p>
<Input
type='text'
value={this.props.defaultMeterMinimumDate}
onChange={this.handleDefaultMeterMinimumDateChange}
placeholder='YYYY-MM-DD HH:MM:SS'
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.maximum.date' />:
</p>
<Input
type='text'
value={this.props.defaultMeterMaximumDate}
onChange={this.handleDefaultMeterMaximumDateChange}
placeholder='YYYY-MM-DD HH:MM:SS'
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.reading.gap' />:
</p>
<Input
type='number'
value={this.props.defaultMeterReadingGap}
onChange={this.handleDefaultMeterReadingGapChange}
maxLength={50}
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.maximum.errors' />:
</p>
<Input
type='number'
value={this.props.defaultMeterMaximumErrors}
onChange={this.handleDefaultMeterMaximumErrorsChange}
maxLength={50}
/>
</div>
<div style={bottomPaddingStyle}>
<p style={titleStyle}>
<FormattedMessage id='default.meter.disable.checks' />:
</p>
<Input
type='select'
value={this.props.defaultMeterDisableChecks?.toString()}
onChange={this.handleDefaultMeterDisableChecksChange}>
{Object.keys(TrueFalseType).map(key => {
return (<option value={key} key={key}>{translate(`TrueFalseType.${key}`)}</option>)
})}
</Input>
</div>
<Button
type='submit'
onClick={this.handleSubmitPreferences}
Expand Down Expand Up @@ -379,6 +490,42 @@ class PreferencesComponent extends React.Component<PreferencesPropsWithIntl> {
this.props.updateDefaultMeterReadingFrequency(e.target.value);
this.updateUnsavedChanges();
}

private handleDefaultMeterMinimumValueChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterMinimumValue(parseFloat(e.target.value));
this.updateUnsavedChanges();
}

private handleDefaultMeterMaximumValueChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterMaximumValue(parseFloat(e.target.value));
this.updateUnsavedChanges();
}

private handleDefaultMeterMinimumDateChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterMinimumDate(e.target.value);
this.updateUnsavedChanges();
}

private handleDefaultMeterMaximumDateChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterMaximumDate(e.target.value);
this.updateUnsavedChanges();
}

private handleDefaultMeterReadingGapChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterReadingGap(parseFloat(e.target.value));
this.updateUnsavedChanges();
}

private handleDefaultMeterMaximumErrorsChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterMaximumErrors(parseInt(e.target.value));
this.updateUnsavedChanges();
}

private handleDefaultMeterDisableChecksChange(e: { target: HTMLInputElement; }) {
this.props.updateDefaultMeterDisableChecks(JSON.parse(e.target.value))
this.updateUnsavedChanges();
}

}

export default injectIntl(PreferencesComponent);
Loading

0 comments on commit cc65531

Please sign in to comment.