Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Max min values #1396

Open
wants to merge 26 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
92afc55
create unit changes
Nov 26, 2024
8953030
edit unit changes
Nov 26, 2024
4edc46d
Merge pull request #32 from oss-slu/create_unit_1307
Rakesh-Ranga-Buram Nov 27, 2024
c29b70c
Merge pull request #33 from oss-slu/edit_unit_1307
Rakesh-Ranga-Buram Nov 27, 2024
a9380c9
adding coulumns to the units table
Rakesh-Ranga-Buram Nov 27, 2024
e72f18e
Disable changes
Rakesh-Ranga-Buram Dec 5, 2024
fb0b593
Merge pull request #34 from oss-slu/Rakesh_disable_changes
Rakesh-Ranga-Buram Dec 7, 2024
99f5de5
Requested Changes
Rakesh-Ranga-Buram Dec 27, 2024
55a44d8
Merge pull request #35 from oss-slu/MinMax_RequestedChanges
Rakesh-Ranga-Buram Dec 27, 2024
500c00e
preference table changes
Rakesh-Ranga-Buram Dec 28, 2024
d162834
Merge pull request #36 from oss-slu/MinMax_Preference
Rakesh-Ranga-Buram Dec 28, 2024
1b3a182
Merge branch 'development' into MaxMinValues
Rakesh-Ranga-Buram Dec 29, 2024
b4f9431
Update PreferencesComponent.tsx
Rakesh-Ranga-Buram Dec 29, 2024
ca4f316
Update data.ts
Rakesh-Ranga-Buram Dec 29, 2024
788a59e
Adding DisableChecksType enum tests
Rakesh-Ranga-Buram Dec 29, 2024
057b53f
Merge pull request #37 from oss-slu/MinMax_EnumTests
Rakesh-Ranga-Buram Dec 29, 2024
00870ec
Pipeline changes
Rakesh-Ranga-Buram Jan 10, 2025
1851023
Merge pull request #38 from oss-slu/MinMaxValues_pipeline
Rakesh-Ranga-Buram Jan 10, 2025
8523554
Merge branch 'development' into MaxMinValues
Rakesh-Ranga-Buram Jan 13, 2025
996bb4d
resolving merge conflicts
Rakesh-Ranga-Buram Jan 13, 2025
587ab18
Merge pull request #42 from oss-slu/MinMaxValues_conflicts
Rakesh-Ranga-Buram Jan 13, 2025
5ae6979
unrelated translation fix
huss Jan 30, 2025
66781a4
resolving comments
Rakesh-Ranga-Buram Feb 13, 2025
3d31689
Merge pull request #43 from oss-slu/MinMaxValues_comment
Rakesh-Ranga-Buram Feb 13, 2025
9ffcc01
Removing min/max from site settings
Rakesh-Ranga-Buram Feb 20, 2025
81d3655
Merge pull request #44 from oss-slu/MinMaxValues_SiteSettings
Rakesh-Ranga-Buram Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/client/app/components/admin/PreferencesComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { preferencesApi } from '../../redux/api/preferencesApi';
import {
MIN_DATE, MIN_DATE_MOMENT, MAX_DATE, MAX_DATE_MOMENT, MAX_VAL, MIN_VAL, MAX_ERRORS
} from '../../redux/selectors/adminSelectors';
import { PreferenceRequestItem, TrueFalseType } from '../../types/items';
import { PreferenceRequestItem } from '../../types/items';
import { ChartTypes } from '../../types/redux/graph';
import { LanguageTypes } from '../../types/redux/i18n';
import { AreaUnitType } from '../../utils/getAreaUnitConversion';
import { showErrorNotification, showSuccessNotification } from '../../utils/notifications';
import { useTranslate } from '../../redux/componentHooks';
import TimeZoneSelect from '../TimeZoneSelect';
import { defaultAdminState } from '../../redux/slices/adminSlice';
import { DisableChecksType } from '../../types/redux/units';


/**
Expand Down Expand Up @@ -302,8 +303,8 @@ export default function PreferencesComponent() {
type='select'
value={localAdminPref.defaultMeterDisableChecks?.toString()}
onChange={e => makeLocalChanges('defaultMeterDisableChecks', e.target.value)}>
{Object.keys(TrueFalseType).map(key => {
return (<option value={key} key={key}>{translate(`TrueFalseType.${key}`)}</option>);
{Object.keys(DisableChecksType).map(key => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the design doc called for removing min, max and this since it is now controlled by the unit for the default. Note this will change the DB and other files. I did a quick search and they probably are not used anywhere so hopefully removing does not cause issues.

return (<option value={key} key={key}>{translate(`DisableChecksType.${key}`)}</option>);
})}
</Input>
</div>
Expand Down
48 changes: 31 additions & 17 deletions src/client/app/components/meters/CreateMeterModalComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { useTranslate } from '../../redux/componentHooks';
import TimeZoneSelect from '../TimeZoneSelect';
import TooltipHelpComponent from '../TooltipHelpComponent';
import TooltipMarkerComponent from '../TooltipMarkerComponent';
import { selectUnitDataById } from '../../redux/api/unitsApi';
import { DisableChecksType } from '../../types/redux/units';

interface CreateMeterModalProps {
onCreateMeter?: (meterIdentifier: string) => void; // Define the type of the callback function
Expand All @@ -55,6 +57,7 @@ export default function CreateMeterModalComponent(props: CreateMeterModalProps):
const [meterDetails, setMeterDetails] = useState(defaultValues);
const unitIsSelected = meterDetails.unitId !== -999;
const defaultGaphicUnitIsSelected = meterDetails.defaultGraphicUnit !== -999;
const unitsDataById = useAppSelector(selectUnitDataById);

const { compatibleGraphicUnits, incompatibleGraphicUnits, compatibleUnits } = useAppSelector(state =>
// Type assertion due to conflicting GPS Property
Expand Down Expand Up @@ -92,6 +95,22 @@ export default function CreateMeterModalComponent(props: CreateMeterModalProps):
const handleTimeZoneChange = (timeZone: string) => {
setMeterDetails({ ...meterDetails, ['timeZone']: timeZone });
};

const handleUnitChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedUnitId = Number(e.target.value);
const selectedUnit = unitsDataById[selectedUnitId];

if (selectedUnit) {
setMeterDetails(details => ({
...details,
unitId: selectedUnitId,
minVal: selectedUnit.minVal,
maxVal: selectedUnit.maxVal,
disableChecks: selectedUnit.disableChecks
}));
}
};

// Reset the state to default values
const resetState = () => {
setMeterDetails(defaultValues);
Expand Down Expand Up @@ -242,9 +261,7 @@ export default function CreateMeterModalComponent(props: CreateMeterModalProps):
</Label>
<Input id='unitId' name='unitId' type='select'
value={meterDetails.unitId}
onChange={e => {
handleNumberChange(e);
}}
onChange={handleUnitChange}
invalid={!unitIsSelected}>
<option value={-999} key={-999} hidden disabled>
{translate('select.unit')}
Expand Down Expand Up @@ -541,25 +558,25 @@ export default function CreateMeterModalComponent(props: CreateMeterModalProps):
<Row xs='1' lg='2'>
{/* minVal input */}
<Col><FormGroup>
<Label for='minVal'>{translate('meter.minVal')}</Label>
<Label for='minVal'>{translate('min.value')}</Label>
<Input id='minVal' name='minVal' type='number'
onChange={e => handleNumberChange(e)}
min={MIN_VAL}
max={meterDetails.maxVal}
defaultValue={meterDetails.minVal}
value={meterDetails.minVal}
invalid={meterDetails?.minVal < MIN_VAL || meterDetails?.minVal > meterDetails?.maxVal} />
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: MIN_VAL, max: meterDetails.maxVal }} />
</FormFeedback>
</FormGroup></Col>
{/* maxVal input */}
<Col><FormGroup>
<Label for='maxVal'>{translate('meter.maxVal')}</Label>
<Label for='maxVal'>{translate('max.value')}</Label>
<Input id='maxVal' name='maxVal' type='number'
onChange={e => handleNumberChange(e)}
min={meterDetails.minVal}
max={MAX_VAL}
defaultValue={meterDetails.maxVal}
value={meterDetails.maxVal}
invalid={meterDetails?.maxVal > MAX_VAL || meterDetails?.minVal > meterDetails?.maxVal} />
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: meterDetails.minVal, max: MAX_VAL }} />
Expand Down Expand Up @@ -614,17 +631,14 @@ export default function CreateMeterModalComponent(props: CreateMeterModalProps):
</FormFeedback>
</FormGroup></Col>
<Col><FormGroup>
<Label for='disableChecks'>{translate('meter.disableChecks')}</Label>
<Label for='disableChecks'>{translate('disable.checks')}</Label>
<Input id='disableChecks' name='disableChecks' type='select'
defaultValue={meterDetails.disableChecks?.toString()}
onChange={e => handleBooleanChange(e)}>
{
Object.keys(TrueFalseType).map(key =>
<option value={key} key={key}>
{translate(`TrueFalseType.${key}`)}
</option>
)
}
value={meterDetails.disableChecks}
onChange={e => handleStringChange(e)}>
{Object.keys(DisableChecksType).map(key => {
return (<option value={key} key={key} >
{translate(`DisableChecksType.${key}`)}</option>);
})}
</Input>
</FormGroup></Col>
</Row>
Expand Down
47 changes: 37 additions & 10 deletions src/client/app/components/meters/EditMeterModalComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import '../../styles/modal.css';
import { tooltipBaseStyle } from '../../styles/modalStyle';
import { TrueFalseType } from '../../types/items';
import { MeterData, MeterTimeSortType, MeterType } from '../../types/redux/meters';
import { UnitRepresentType } from '../../types/redux/units';
import { DisableChecksType, UnitRepresentType } from '../../types/redux/units';
import { GPSPoint, isValidGPSInput } from '../../utils/calibration';
import { AreaUnitType } from '../../utils/getAreaUnitConversion';
import { getGPSString, nullToEmptyString } from '../../utils/input';
Expand Down Expand Up @@ -67,6 +67,19 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr

const [validMeter, setValidMeter] = useState(isValidMeter(localMeterEdits));

// Ensure `minVal`, `maxVal` and `disableChecks` are set based on the unit on load
useEffect(() => {
const selectedUnit = unitDataById[localMeterEdits.unitId];
if (selectedUnit) {
setLocalMeterEdits(edits => ({
...edits,
minVal: selectedUnit.minVal,
maxVal: selectedUnit.maxVal,
disableChecks: selectedUnit.disableChecks
}));
}
}, [localMeterEdits.unitId]);

useEffect(() => { setValidMeter(isValidMeter(localMeterEdits)); }, [localMeterEdits]);
/* End State */

Expand All @@ -76,6 +89,20 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr
}
}, [localMeterEdits.cumulative]);

const handleUnitChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedUnitId = Number(e.target.value);
const selectedUnit = unitDataById[selectedUnitId];
if (selectedUnit) {
setLocalMeterEdits({
...localMeterEdits,
unitId: selectedUnitId,
minVal: selectedUnit.minVal,
maxVal: selectedUnit.maxVal,
disableChecks: selectedUnit.disableChecks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think min/maxVal nor disableChecks needs to be set here since the useEffect above fires when unitId is updated.

});
}
};

// Save changes
// Currently using the old functionality which is to compare inherited prop values to state values
// If there is a difference between props and state, then a change was made
Expand Down Expand Up @@ -279,7 +306,7 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr
name='unitId'
type='select'
value={localMeterEdits.unitId}
onChange={e => handleNumberChange(e)}>
onChange={handleUnitChange}>
{Array.from(compatibleUnits).map(unit => {
return (<option value={unit.id} key={unit.id}>{unit.identifier}</option>);
})}
Expand Down Expand Up @@ -599,7 +626,7 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr
<Row xs='1' lg='2'>
{/* minVal input */}
<Col><FormGroup>
<Label for='minVal'>{translate('meter.minVal')}</Label>
<Label for='minVal'>{translate('min.value')}</Label>
<Input
id='minVal'
name='minVal'
Expand All @@ -615,7 +642,7 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr
</FormGroup></Col>
{/* maxVal input */}
<Col><FormGroup>
<Label for='maxVal'>{translate('meter.maxVal')}</Label>
<Label for='maxVal'>{translate('max.value')}</Label>
<Input
id='maxVal'
name='maxVal'
Expand Down Expand Up @@ -687,16 +714,16 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr
</FormGroup></Col>
{/* DisableChecks input */}
<Col><FormGroup>
<Label for='disableChecks'>{translate('meter.disableChecks')}</Label>
<Label for='disableChecks'>{translate('disable.checks')}</Label>
<Input
id='disableChecks'
name='disableChecks'
type='select'
value={localMeterEdits?.disableChecks?.toString()}
onChange={e => handleBooleanChange(e)}
invalid={localMeterEdits?.disableChecks && localMeterEdits.unitId === -99}>
{Object.keys(TrueFalseType).map(key => {
return (<option value={key} key={key}>{translate(`TrueFalseType.${key}`)}</option>);
value={localMeterEdits.disableChecks}
onChange={e => handleStringChange(e)}>
{Object.keys(DisableChecksType).map(key => {
return (<option value={key} key={key} >
{translate(`DisableChecksType.${key}`)}</option>);
})}
</Input>
</FormGroup></Col>
Expand Down
70 changes: 60 additions & 10 deletions src/client/app/components/unit/CreateUnitModalComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import '../../styles/modal.css';
import { TrueFalseType } from '../../types/items';
import TooltipMarkerComponent from '../TooltipMarkerComponent';
import TooltipHelpComponent from '../../components/TooltipHelpComponent';
import { UnitRepresentType, DisplayableType, UnitType } from '../../types/redux/units';
import { UnitRepresentType, DisplayableType, UnitType, DisableChecksType } from '../../types/redux/units';
import { tooltipBaseStyle } from '../../styles/modalStyle';
import { unitsApi } from '../../redux/api/unitsApi';
import { useTranslate } from '../../redux/componentHooks';
import { showSuccessNotification, showErrorNotification } from '../../utils/notifications';
import { MAX_VAL, MIN_VAL } from '../../redux/selectors/adminSelectors';
import { LineGraphRates } from '../../types/redux/graph';
import { customRateValid, isCustomRate } from '../../utils/unitInput';

Expand All @@ -41,7 +42,10 @@ export default function CreateUnitModalComponent() {
// The client code makes the id for the selected unit and default graphic unit be -99
// so it can tell it is not yet assigned and do the correct logic for that case.
// The units API expects these values to be undefined on call so that the database can assign their values.
id: -99
id: -99,
minVal: MIN_VAL,
maxVal: MAX_VAL,
disableChecks: DisableChecksType.reject_none
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think per other comments this should be reject all.

};

/* State */
Expand Down Expand Up @@ -72,6 +76,10 @@ export default function CreateUnitModalComponent() {
setState({ ...state, [e.target.name]: JSON.parse(e.target.value) });
};

const handleNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setState({ ...state, [e.target.name]: Number(e.target.value) });
};

/**
* Updates the rate (both custom and regular state) including setting if custom.
* @param newRate The new rate to set.
Expand Down Expand Up @@ -125,13 +133,15 @@ export default function CreateUnitModalComponent() {

// Keeps canSave state up to date. Checks if valid and if edit made.
useEffect(() => {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove blank line.

// This checks:
// - Name cannot be blank
// - If type of unit is suffix there must be a suffix
// - The rate is set so not the custom input value. This happens if select custom value but don't input with enter.
// - The custom rate is a positive integer
const validUnit = state.name !== '' &&
(state.typeOfUnit !== UnitType.suffix || state.suffix !== '') && state.secInRate !== Number(CUSTOM_INPUT)
&& (state?.minVal < MIN_VAL || state?.minVal > state?.maxVal)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, edit unit does not have this logic on min/max but should. Second, I think the logic is off. I made it && state?.minVal >= MIN_VAL && state?.maxVal <= MAX_VAL && state?.minVal <= state?.maxVal to mimic the form feedback and to get it the be valid (the current logic is more for invalid which is not the check).

&& customRateValid(Number(state.secInRate));
setCanSave(validUnit);
}, [state]);
Expand Down Expand Up @@ -423,19 +433,59 @@ export default function CreateUnitModalComponent() {
</FormGroup>
</Col>
</Row>
<Row xs='1' lg='2'>
{/* minVal input */}
<Col><FormGroup>
<Label for='minVal'>{translate('min.val')}</Label>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be min.value.

<Input id='minVal' name='minVal' type='number'
onChange={e => handleNumberChange(e)}
min={MIN_VAL}
max={state.maxVal}
defaultValue={state.minVal}
invalid={state?.minVal < MIN_VAL || state?.minVal > state?.maxVal} />
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: MIN_VAL, max: state.maxVal }} />
</FormFeedback>
</FormGroup></Col>
{/* maxVal input */}
<Col><FormGroup>
<Label for='maxVal'>{translate('max.value')}</Label>
<Input id='maxVal' name='maxVal' type='number'
onChange={e => handleNumberChange(e)}
min={state.minVal}
max={MAX_VAL}
defaultValue={state.maxVal}
invalid={state?.maxVal > MAX_VAL || state?.minVal > state?.maxVal} />
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: state.minVal, max: MAX_VAL }} />
</FormFeedback>
</FormGroup></Col>
</Row>
<Row xs='1' lg='2'>
{/* DisableChecks input */}
<Col><FormGroup>
<Label for='disableChecks'>{translate('disable.checks')}</Label>
<Input id='disableChecks' name='disableChecks' type='select'
onChange={e => handleStringChange(e)}
defaultValue={state.disableChecks}>
{Object.keys(DisableChecksType).map(key => {
return (<option value={key} key={key} >
{translate(`DisableChecksType.${key}`)}</option>);
})}
</Input>
</FormGroup></Col>
</Row>
{/* Note input */}
<FormGroup>
<Label for="note">{translate('note')}</Label>
<Label for='note'>{translate('note')}</Label>
<Input
id="note"
name="note"
type="textarea"
value={state.note}
id='note'
name='note'
type='textarea'
onChange={e => handleStringChange(e)}
/>
value={state.note} />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value seems above onChange in other places so maybe put back above it.

</FormGroup>
</Container>
</ModalBody>
</Container></ModalBody>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModalBody is a tag own its on line when started so probably should put this on a separate line as it was before.

<ModalFooter>
{/* Hides the modal */}
<Button color="secondary" onClick={handleClose}>
Expand Down
Loading
Loading