Skip to content

Commit

Permalink
Merge pull request OpenEnergyDashboard#5 from Deelane/pr-766-test-cha…
Browse files Browse the repository at this point in the history
…nges

merge Deelane 766 -> wootent 766
  • Loading branch information
Deelane committed Jul 14, 2022
2 parents 10ee02f + 7b6c113 commit 42648ab
Show file tree
Hide file tree
Showing 11 changed files with 470 additions and 616 deletions.
86 changes: 72 additions & 14 deletions src/client/app/actions/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { ActionType, Thunk, Dispatch, GetState } from '../types/redux/actions';
import { showErrorNotification } from '../utils/notifications';
import { showSuccessNotification, showErrorNotification } from '../utils/notifications';
import translate from '../utils/translate';
import { State } from '../types/redux/state';
import * as t from '../types/redux/units';
import { unitsApi } from '../utils/api';

Expand All @@ -18,49 +17,78 @@ export function receiveUnitsDetails(data: t.UnitData[]): t.ReceiveUnitsDetailsAc
}

export function fetchUnitsDetails(): Thunk {
return async (dispatch: Dispatch) => {
dispatch(requestUnitsDetails());
const units = await unitsApi.getUnitsDetails();
dispatch(receiveUnitsDetails(units));
return async (dispatch: Dispatch, getState: GetState) => {
// ensure a fetch is not currently happening
if (!getState().units.isFetching)
{
// set isFetching to true
dispatch(requestUnitsDetails());
// attempt to retrieve units details from database
const units = await unitsApi.getUnitsDetails();
// update the state with the units details and set isFetching to false
dispatch(receiveUnitsDetails(units));
// If this is the first fetch, inform the store that the first fetch has been made
if (!getState().units.hasBeenFetchedOnce)
{
dispatch(confirmUnitsFetchedOnce());
}
}
}
}

export function changeDisplayedUnits(units: number[]): t.ChangeDisplayedUnitsAction {
return { type: ActionType.ChangeDisplayedUnits, selectedUnits: units};
return { type: ActionType.ChangeDisplayedUnits, selectedUnits: units };
}

export function editUnitDetails(unit: t.UnitData):
t.EditUnitDetailsAction {
export function editUnitDetails(unit: t.UnitData): t.EditUnitDetailsAction {
return { type: ActionType.EditUnitDetails, unit };
}

export function submitUnitEdits(unit: number): t.SubmitEditedUnitAction {
return { type: ActionType.SubmitEditedUnit, unit };
}

export function confirmUnitEdits(unit: number): t.ConfirmEditedUnitAction {
return { type: ActionType.ConfirmEditedUnit, unit};
export function confirmUnitEdits(unitId: number): t.ConfirmEditedUnitAction {
return { type: ActionType.ConfirmEditedUnit, unitId };
}

export function deleteEditedUnit(unitId: number): t.DeleteEditedUnitAction {
return {type: ActionType.DeleteEditedUnit, unitId }
}

export function deleteSubmittedUnit(unitId: number): t.DeleteSubmittedUnitAction {
return {type: ActionType.DeleteSubmittedUnit, unitId}
}

function shouldFetchUnitsDetails(state: State): boolean {
return !state.units.isFetching;
export function confirmUnitsFetchedOnce(): t.ConfirmUnitsFetchedOnceAction {
return { type: ActionType.ConfirmUnitsFetchedOnce };
}

// Fetch the units details from the database if they have not already been fetched once
export function fetchUnitsDetailsIfNeeded(): Thunk {
return (dispatch: Dispatch, getState: GetState) => {
if (shouldFetchUnitsDetails(getState())) {
// If units have not been fetched once, return the fetchUnitDetails function
if (!getState().units.hasBeenFetchedOnce)
{
return dispatch(fetchUnitsDetails());
}
// If units have already been fetched, return a resolved promise
return Promise.resolve();
};
}

// TODO maybe this can be removed since we're not doing bulk edits
// I imagine this bulk function exists so that you do not need to pass in a unitId when submit an edited unit

export function submitEditedUnits(): Thunk {
return async (dispatch: Dispatch, getState: GetState) => {
// dispatches submitEditedUnit for each unitData in editedUnits (by id) if they are not already submitting
Object.keys(getState().units.editedUnits).forEach(unitIdS => {
// grab the unitId
const unitId = parseInt(unitIdS);
// check if unitData is already submitting (indexOf returns -1 if item does not exist in array)
if (getState().units.submitting.indexOf(unitId) === -1) {
// if unit is not submitting, submit it
dispatch(submitEditedUnit(unitId));
}
});
Expand All @@ -69,13 +97,26 @@ export function submitEditedUnits(): Thunk {

export function submitEditedUnit(unitId: number): Thunk {
return async (dispatch: Dispatch, getState: GetState) => {
// retrieve the unitData by id
const submittingUnit = getState().units.editedUnits[unitId];
// pushes unitId of the unitData to submit onto the submitting state array
dispatch(submitUnitEdits(unitId));
try {
// posts the edited unitData to the units API
await unitsApi.edit(submittingUnit);
// Clear unit Id from submitting state array
dispatch(deleteSubmittedUnit(unitId));
// Retrieve our edits from the editedUnits state and overwrite the units state with them
dispatch(confirmUnitEdits(unitId));
// Clear our edits from the editedUnits state
dispatch(deleteEditedUnit(unitId));
showSuccessNotification(translate('successfully.edited.unit'));
} catch (err) {
showErrorNotification(translate('failed.to.edit.unit'));
// Clear our changes from to the submitting and editedUnits state
// We must do this in case fetch failed to keep the store in sync with the database
dispatch(deleteSubmittedUnit(unitId));
dispatch(deleteEditedUnit(unitId));
}
};
}
Expand All @@ -90,4 +131,21 @@ export function confirmEditedUnits(): Thunk {
dispatch(confirmUnitEdits(unitId));
});
}
}

// Add unit to database
export function addUnit(unit: t.UnitData): Thunk {
return async (dispatch: Dispatch) => {
try {
// Attempt to add unit to database
await unitsApi.addUnit(unit);
// Update the units state from the database on a successful call
// In the future, getting rid of this database fetch and updating the store on a successful API call would make the page faster
// However, since the database currently assigns the id to the UnitData
dispatch(fetchUnitsDetails());
showSuccessNotification(translate('successfully.added.unit'));
} catch (err) {
showErrorNotification(translate('failed.to.add.unit'));
}
}
}
7 changes: 3 additions & 4 deletions src/client/app/components/RouteComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ import MapCalibrationContainer from '../containers/maps/MapCalibrationContainer'
import UploadCSVContainer from '../containers/csv/UploadCSVContainer';
import { UserRole } from '../types/items';
import { hasPermissions } from '../utils/hasPermissions';
import UnitsDetailContainer from '../containers/unit/UnitsDetailContainer';
import CreateUnitContainer from '../containers/unit/CreateUnitContainer';
import UnitsDetailComponent from './unit/UnitsDetailComponent';
import * as queryString from 'query-string';

interface RouteProps {
Expand Down Expand Up @@ -253,8 +252,8 @@ export default class RouteComponent extends React.Component<RouteProps> {
<Route path='/editGroup' render={() => this.requireAuth(<EditGroupsContainer/>)}/>
<Route path='/users/new' render={() => this.requireAuth(<CreateUserContainer/>)}/>
<Route path='/users' render={() => this.requireAuth(<UsersDetailContainer fetchUsers={() => []}/>)}/>
<Route path='/units'render={() => this.requireAuth(<UnitsDetailContainer />)}/>
<Route path="/addUnit" render={() => this.requireAuth(<CreateUnitContainer />)}/>
<Route path='/units'render={() => this.requireAuth(<UnitsDetailComponent />)}/>
{/* <Route path="/addUnit" render={() => this.requireAuth(<CreateUnitContainer />)}/> */}
<Route path='*' component={HomeComponent}/>
</Switch>
</Router>
Expand Down
Loading

0 comments on commit 42648ab

Please sign in to comment.