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

Issue#1163 resolved, Issue#1292 resolved, and Issue#1293 partially resolved #1325

Merged
merged 29 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ce1cc1c
added const arrow functions to MapControlsComponent
danielshid Jul 13, 2024
0f05b00
added drop-down menu for map
danielshid Jul 13, 2024
6963f7f
copied and stripped BarControlsComponent of bar specific components t…
danielshid Jul 19, 2024
bf45a42
functional map interval dropdown
danielshid Jul 23, 2024
6b882df
delete original MapControlsComponent code with buttons
danielshid Jul 23, 2024
5110a82
implemented compare interval dropodwn menu
danielshid Jul 29, 2024
90cac84
delete original compare button code
danielshid Jul 29, 2024
97f384b
touchups to CompareControlsComponent and added comments
danielshid Jul 30, 2024
3b8281a
implemented bar, map, and compare controls into one file
danielshid Aug 1, 2024
e583f3f
delete original code fragments and generalize variable/function names
danielshid Aug 2, 2024
9d80e12
Merge remote-tracking branch 'origin/development' into issue#1292
danielshid Aug 4, 2024
c5742bc
fixed import to meet length requirement
danielshid Aug 4, 2024
3c15d96
delete trailing space
danielshid Aug 4, 2024
1394d1f
used input instead of dropdown for compare controls
danielshid Aug 9, 2024
90f74c2
Reimplemented three separate components
danielshid Aug 9, 2024
c4204ac
corrected function comments and format docs
danielshid Aug 9, 2024
2415d7a
format doc
danielshid Aug 9, 2024
161a7b5
removed conditionals for comparecontrols
danielshid Aug 9, 2024
c56d653
Merge remote-tracking branch 'origin/development' into pr/danielshid/…
huss Aug 10, 2024
80a11d6
covert indentation to tabs
danielshid Aug 10, 2024
ed08f9d
Merge branch 'issue#1292' of https://github.com/danielshid/OED into i…
danielshid Aug 10, 2024
e427b88
added missing semicolons
danielshid Aug 10, 2024
6032a96
"made interval component cleaner"
danielshid Aug 12, 2024
396f48c
consistency changes to IntervalControlsComponent.tsx
danielshid Sep 17, 2024
265887a
code does not work needs debugging
danielshid Oct 4, 2024
f26e35c
Merge branch 'OpenEnergyDashboard:development' into issue#1292
danielshid Oct 4, 2024
5e56635
changed compare enums
danielshid Oct 4, 2024
c7e5d17
fully integrate looping over the enum
danielshid Oct 8, 2024
c368728
Merge branch 'OpenEnergyDashboard:development' into issue#1292
danielshid Oct 8, 2024
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
121 changes: 6 additions & 115 deletions src/client/app/components/BarControlsComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,147 +2,38 @@
* 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 moment from 'moment';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { FormFeedback, FormGroup, Input, Label } from 'reactstrap';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { graphSlice, selectBarStacking, selectBarWidthDays } from '../redux/slices/graphSlice';
import { graphSlice, selectBarStacking } from '../redux/slices/graphSlice';
import translate from '../utils/translate';
import TooltipMarkerComponent from './TooltipMarkerComponent';
import IntervalControlsComponent from './IntervalControlsComponent';

/**
* @returns controls for the Options Ui page.
* @returns controls for bar page.
*/
export default function BarControlsComponent() {
const dispatch = useAppDispatch();

// The min/max days allowed for user selection
const MIN_BAR_DAYS = 1;
const MAX_BAR_DAYS = 366;
// Special value if custom input for standard menu.
const CUSTOM_INPUT = '-99';

// This is the current bar interval for graphic.
const barDuration = useAppSelector(selectBarWidthDays);
const barStacking = useAppSelector(selectBarStacking);
// Holds the value of standard bar duration choices used so decoupled from custom.
const [barDays, setBarDays] = React.useState<string>(barDuration.asDays().toString());
// Holds the value during custom bar duration input so only update graphic when done entering and
// separate from standard choices.
const [barDaysCustom, setBarDaysCustom] = React.useState<number>(barDuration.asDays());
// True if custom bar duration input is active.
const [showCustomBarDuration, setShowCustomBarDuration] = React.useState<boolean>(false);

const handleChangeBarStacking = () => {
dispatch(graphSlice.actions.changeBarStacking());
};

// Keeps react-level state, and redux state in sync.
// Two different layers in state may differ especially when externally updated (chart link, history buttons.)
React.useEffect(() => {
// Assume value is valid since it is coming from state.
// Do not allow bad values in state.
const isCustom = !(['1', '7', '28'].find(days => days == barDuration.asDays().toString()));
setShowCustomBarDuration(isCustom);
setBarDaysCustom(barDuration.asDays());
setBarDays(isCustom ? CUSTOM_INPUT : barDuration.asDays().toString());
}, [barDuration]);

// Returns true if this is a valid bar duration.
const barDaysValid = (barDays: number) => {
return Number.isInteger(barDays) && barDays >= MIN_BAR_DAYS && barDays <= MAX_BAR_DAYS;
};

// Updates values when the standard bar duration menu is used.
const handleBarDaysChange = (value: string) => {
if (value === CUSTOM_INPUT) {
// Set menu value for standard bar to special value to show custom
// and show the custom input area.
setBarDays(CUSTOM_INPUT);
setShowCustomBarDuration(true);
} else {
// Set the standard menu value, hide the custom bar duration input
// and bar duration for graphing.
// Since controlled values know it is a valid integer.
setShowCustomBarDuration(false);
updateBarDurationChange(Number(value));
}
};

// Updates value when the custom bar duration input is used.
const handleCustomBarDaysChange = (value: number) => {
setBarDaysCustom(value);
};

const handleEnter = (key: string) => {
// This detects the enter key and then uses the previously entered custom
// bar duration to set the bar duration for the graphic.
if (key == 'Enter') {
updateBarDurationChange(barDaysCustom);
}
};

const updateBarDurationChange = (value: number) => {
// Update if okay value. May not be okay if this came from user entry in custom form.
if (barDaysValid(value)) {
dispatch(graphSlice.actions.updateBarDuration(moment.duration(value, 'days')));
}
};

return (
<div>
<div className='checkbox'>
<div className='checkbox' style={divTopBottomPadding}>
<input type='checkbox' style={{ marginRight: '10px' }} onChange={handleChangeBarStacking} checked={barStacking} id='barStacking' />
<label htmlFor='barStacking'>{translate('bar.stacking')}</label>
<TooltipMarkerComponent page='home' helpTextId='help.home.bar.stacking.tip' />
</div>
<div style={divTopBottomPadding}>
<p style={labelStyle}>
{translate('bar.interval')}:
<TooltipMarkerComponent page='home' helpTextId='help.home.bar.days.tip' />
</p>
<Input
id='barDurationDays'
name='barDurationDays'
type='select'
value={barDays}
onChange={e => handleBarDaysChange(e.target.value)}
>
<option value='1'>{translate('day')}</option>
<option value='7'>{translate('week')}</option>
<option value='28'>{translate('4.weeks')}</option>
<option value={CUSTOM_INPUT}>{translate('custom.value')}</option>
</Input>
{/* This has a little more spacing at bottom than optimal. */}
{showCustomBarDuration &&
<FormGroup>
<Label for='barDays'>{translate('bar.days.enter')}:</Label>
<Input id='barDays' name='barDays' type='number'
onChange={e => handleCustomBarDaysChange(Number(e.target.value))}
// This grabs each key hit and then finishes input when hit enter.
onKeyDown={e => { handleEnter(e.key); }}
step='1'
min={MIN_BAR_DAYS}
max={MAX_BAR_DAYS}
value={barDaysCustom}
invalid={!barDaysValid(barDaysCustom)} />
<FormFeedback>
<FormattedMessage id="error.bounds" values={{ min: MIN_BAR_DAYS, max: MAX_BAR_DAYS }} />
</FormFeedback>
</FormGroup>
}
</div >
{<IntervalControlsComponent key='interval' />}
</div >
);
}

const divTopBottomPadding: React.CSSProperties = {
paddingTop: '15px',
paddingTop: '0px',
paddingBottom: '15px'
};

const labelStyle: React.CSSProperties = {
fontWeight: 'bold',
margin: 0
};
106 changes: 38 additions & 68 deletions src/client/app/components/CompareControlsComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,59 @@
/* 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/. */
* 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 moment from 'moment';
import * as React from 'react';
import { Button, ButtonGroup, Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
import { graphSlice, selectComparePeriod, selectSortingOrder } from '../redux/slices/graphSlice';
import { Input } from 'reactstrap';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { ComparePeriod, SortingOrder } from '../utils/calculateCompare';
import { graphSlice, selectSortingOrder } from '../redux/slices/graphSlice';
import { SortingOrder } from '../utils/calculateCompare';
import translate from '../utils/translate';
import TooltipMarkerComponent from './TooltipMarkerComponent';
import IntervalControlsComponent from './IntervalControlsComponent';

/**
* @returns controls for the compare page
* @returns controls for compare page.
*/
export default function CompareControlsComponent() {
const dispatch = useAppDispatch();
const comparePeriod = useAppSelector(selectComparePeriod);

// This is the current sorting order for graphic
const compareSortingOrder = useAppSelector(selectSortingOrder);
const [compareSortingDropdownOpen, setCompareSortingDropdownOpen] = React.useState<boolean>(false);
const handleCompareButton = (comparePeriod: ComparePeriod) => {
dispatch(graphSlice.actions.updateComparePeriod({ comparePeriod, currentTime: moment() }));
};
const handleSortingButton = (sortingOrder: SortingOrder) => {

// Updates sorting order when the sort order menu is used.
const handleSortingChange = (value: string) => {
const sortingOrder = value as unknown as SortingOrder;
dispatch(graphSlice.actions.changeCompareSortingOrder(sortingOrder));
};

return (
<div>
<ButtonGroup
style={zIndexFix}
>
<Button
outline={comparePeriod !== ComparePeriod.Day}
active={comparePeriod === ComparePeriod.Day}
onClick={() => handleCompareButton(ComparePeriod.Day)}
{<IntervalControlsComponent key='interval' />}
<div style={divTopBottomPadding}>
<p style={labelStyle}>
{translate('sort')}:
<TooltipMarkerComponent page='home' helpTextId='help.home.compare.sort.tip' />
</p>
<Input
type="select"
value={compareSortingOrder?.toString()}
onChange={e => handleSortingChange(e.target.value)}
>
{translate('day')}
</Button>
<Button
outline={comparePeriod !== ComparePeriod.Week}
active={comparePeriod === ComparePeriod.Week}
onClick={() => handleCompareButton(ComparePeriod.Week)}
>
{translate('week')}
</Button>
<Button
outline={comparePeriod !== ComparePeriod.FourWeeks}
active={comparePeriod === ComparePeriod.FourWeeks}
onClick={() => handleCompareButton(ComparePeriod.FourWeeks)}
>
{translate('4.weeks')}
</Button>
</ButtonGroup>
<TooltipMarkerComponent page='home' helpTextId='help.home.compare.interval.tip' />
<Dropdown isOpen={compareSortingDropdownOpen} toggle={() => setCompareSortingDropdownOpen(current => !current)}>
<DropdownToggle caret>
{translate('sort')}
</DropdownToggle>
<TooltipMarkerComponent page='home' helpTextId='help.home.compare.sort.tip' />
<DropdownMenu>
<DropdownItem
active={compareSortingOrder === SortingOrder.Alphabetical}
onClick={() => handleSortingButton(SortingOrder.Alphabetical)}
>
{translate('alphabetically')}
</DropdownItem>
<DropdownItem
active={compareSortingOrder === SortingOrder.Ascending}
onClick={() => handleSortingButton(SortingOrder.Ascending)}
>
{translate('ascending')}
</DropdownItem>
<DropdownItem
active={compareSortingOrder === SortingOrder.Descending}
onClick={() => handleSortingButton(SortingOrder.Descending)}
>
{translate('descending')}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
<option value={SortingOrder.Alphabetical.toString()}>{translate('alphabetically')}</option>
<option value={SortingOrder.Ascending.toString()}>{translate('ascending')}</option>
<option value={SortingOrder.Descending.toString()}>{translate('descending')}</option>
</Input>
</div>
</div >
);
}

const zIndexFix: React.CSSProperties = {
zIndex: 0
};
const divTopBottomPadding: React.CSSProperties = {
paddingTop: '0px',
paddingBottom: '15px'
};

const labelStyle: React.CSSProperties = {
fontWeight: 'bold',
margin: 0
};
Loading
Loading