Skip to content

Commit

Permalink
TASK-308 Evaluate working time issues on frontend (#233)
Browse files Browse the repository at this point in the history
* Adds worker info on new month

* Moves errors from backend to frontend

Co-authored-by: Paweł Renc <[email protected]>
  • Loading branch information
Qwebeck and prenc authored Feb 26, 2021
1 parent a342c5d commit 63cbca9
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 23 deletions.
14 changes: 12 additions & 2 deletions src/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import axios, { AxiosInstance } from "axios";
import { v4 as uuidv4 } from "uuid";
import { ScheduleDataModel } from "../common-models/schedule-data.model";
import { ScheduleError } from "../common-models/schedule-error.model";
import { AlgorithmErrorCode, ScheduleError } from "../common-models/schedule-error.model";
import { SHIFTS } from "../common-models/shift-info.model";
import { v4 as uuidv4 } from "uuid";

interface BackendErrorObject extends Omit<ScheduleError, "kind"> {
code: string;
Expand All @@ -21,6 +21,13 @@ function escapeJuliaIndexes(error: ScheduleError): ScheduleError {
return error;
}

function isUnderTimeAndOvertimeErrors(error: ScheduleError): boolean {
return (
error.kind === AlgorithmErrorCode.WorkerOvertime ||
error.kind === AlgorithmErrorCode.WorkerUnderTime
);
}

type NameUuidMapper = {
[name: string]: string;
};
Expand Down Expand Up @@ -75,6 +82,9 @@ class Backend {
.then((resp) => resp.data.map((el: BackendErrorObject) => ({ ...el, kind: el.code })))
.then((errors) => errors.map(escapeJuliaIndexes))
.then((errors) => errors.map(this.remapUsernames))
.then((errors: ScheduleError[]) =>
errors.filter((err) => !isUnderTimeAndOvertimeErrors(err))
)
);
}

Expand Down
1 change: 1 addition & 0 deletions src/common-models/schedule-data.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function extendMonthDMToScheduleDM(
shifts[key] = extendSchedule("shifts", key, ShiftCode.W);
});

debugger;
const monthInfoModel: MonthInfoModel = {
children_number: extendSchedule("month_info", "children_number", 0),
extra_workers: extendSchedule("month_info", "extra_workers", 0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function ErrorTooltipProvider({
placement: "right-start",
}
);
const { mode } = useSelector((state: ApplicationStateModel) => state.actualState);

const [isFixed, setIsFixed] = useState(false);

Expand All @@ -75,13 +76,13 @@ export function ErrorTooltipProvider({
hideErrorTooltip(true);
}
}

// TODO refactor
return (
<>
<Popper
ref={tooltipRef}
className="errorTooltip"
isOpen={isToolTipOpen}
isOpen={isToolTipOpen && mode === "edit"}
{...attributes.popper}
style={{
...(isToolTipOpen && errors.length !== 0 ? styles.popper : {}),
Expand Down Expand Up @@ -110,14 +111,14 @@ export function ErrorTooltipProvider({
onMouseEnter={showErrorTooltip}
onMouseLeave={(): void => hideErrorTooltip(false)}
>
{errors.length !== 0 && triangleStyle === "single" && (
{mode === "edit" && errors.length !== 0 && triangleStyle === "single" && (
<span
ref={errorTriangle}
className={classNames("error-triangle", tooltipClassname)}
onClick={handleTriangleClick}
/>
)}
{errors.length !== 0 && triangleStyle === "right" && (
{mode === "edit" && errors.length !== 0 && triangleStyle === "right" && (
<div>
<span
ref={errorTriangle}
Expand All @@ -131,21 +132,21 @@ export function ErrorTooltipProvider({
/>
</div>
)}
{errors.length > 1 && (
{mode === "edit" && errors.length > 1 && (
<span
ref={errorTriangle}
className={classNames("error-triangle", tooltipClassname)}
onClick={handleTriangleClick}
/>
)}
{errors.length !== 0 && triangleStyle === "middle" && (
{mode === "edit" && errors.length !== 0 && triangleStyle === "middle" && (
<span
ref={errorTriangle}
className={classNames("error-triangle line", tooltipClassname)}
onClick={handleTriangleClick}
/>
)}
{errors.length !== 0 && triangleStyle === "left" && (
{mode === "edit" && errors.length !== 0 && triangleStyle === "left" && (
<div>
<span
ref={errorTriangle}
Expand Down
32 changes: 24 additions & 8 deletions src/components/summarytable/summarytable-section.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import React, { useContext } from "react";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { WorkerType } from "../../common-models/worker-info.model";
import { ShiftHelper } from "../../helpers/shifts.helper";
import { DataRow } from "../../logic/schedule-logic/data-row";
import {
ApplicationStateModel,
ScheduleStateModel,
} from "../../state/models/application-state.model";
import { ScheduleErrorActionCreator } from "../../state/schedule-error.action-creator";
import { ScheduleLogicContext } from "../schedule-page/table/schedule/use-schedule-state";
import { SummaryTableRow } from "./summarytable-row.component";

Expand Down Expand Up @@ -42,6 +43,27 @@ export function SummaryTableSection({
(state: ApplicationStateModel) => state.actualState[scheduleKey].present.month_info
);

const dispatch = useDispatch();

function calculateSummaryTableInfo(workerName: string): number[] {
const hoursInfo = ShiftHelper.caclulateWorkHoursInfoForDates(
shifts[workerName],
time[workerName],
currentMonth,
year,
dates
);
const [, , overtime] = hoursInfo;
if (overtime !== 0) {
const updateWorkerTimeInfo = ScheduleErrorActionCreator.addUndertimeOrOvertimeError(
overtime,
workerName
);
dispatch(updateWorkerTimeInfo);
}
return hoursInfo;
}

return (
<>
<table
Expand All @@ -55,13 +77,7 @@ export function SummaryTableSection({
<SummaryTableRow
key={`${scheduleLogic?.uuid ?? 0}_${dataRow.rowKey}`}
uuid={scheduleLogic?.uuid ?? "0"}
data={ShiftHelper.caclulateWorkHoursInfoForDates(
shifts[dataRow.rowKey],
time[dataRow.rowKey],
currentMonth,
year,
dates
)}
data={calculateSummaryTableInfo(dataRow.rowKey)}
rowIndex={rowIndex}
/>
);
Expand Down
1 change: 0 additions & 1 deletion src/helpers/array.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export class ArrayHelper {
} else {
updateElements = baseArr.slice(baseArr.length - numerOfElement);
}

return ArrayHelper.replace(updatedArr, updateElements, updatePosition);
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/shifts.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class ShiftHelper {
const dates = VerboseDateHelper.generateVerboseDatesForMonth(month, year);
return Math.round(this.calculateRequiredHoursFromVerboseDates(dates));
}

s;
public static calculateRequiredHoursFromVerboseDates(
verboseDates: Pick<VerboseDate, "isPublicHoliday" | "dayOfWeek">[]
): number {
Expand Down
58 changes: 54 additions & 4 deletions src/state/reducers/month-state/schedule-errors.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,77 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import _ from "lodash";
import { GroupedScheduleErrors, ScheduleError } from "../../../common-models/schedule-error.model";
import {
AlgorithmErrorCode,
GroupedScheduleErrors,
ScheduleError,
} from "../../../common-models/schedule-error.model";
import { PERSISTENT_SCHEDULE_NAME } from "../../app.reducer";
import { ActionModel } from "../../models/action.model";
import { ScheduleActionType } from "./schedule-data/schedule.actions";
import {
RemoveWorkerErrorUpdateOvertimOrUndertimePayload,
ScheduleErrorUpdateOvertimOrUndertimePayload,
} from "../../schedule-error.action-creator";
import { createActionName, ScheduleActionType } from "./schedule-data/schedule.actions";
/* eslint-disable @typescript-eslint/no-explicit-any */

export enum ScheduleErrorActionType {
UPDATE = "updateScheduleError",
REMOVE_WORKER_OVERTIME_ERROR = "REMOVE_WORKER_OVERTIME_ERROR",
ADD_OVERTIME_OR_UNDERTIME = "ADD_OVERTIME_OR_UNDERTIME",
}

export function scheduleErrorsReducer(
state: GroupedScheduleErrors = {},
action: ActionModel<ScheduleError[]>
action:
| ActionModel<ScheduleError[]>
| ActionModel<ScheduleErrorUpdateOvertimOrUndertimePayload>
| ActionModel<RemoveWorkerErrorUpdateOvertimOrUndertimePayload>
): GroupedScheduleErrors {
switch (action.type) {
case ScheduleErrorActionType.UPDATE:
if (!action.payload) action.payload = [];
const errors = _.groupBy(action.payload, (item) => item.kind);
const errors = _.groupBy(action.payload as ScheduleError[], (item) => item.kind);
return errors;

case createActionName(PERSISTENT_SCHEDULE_NAME, ScheduleActionType.ADD_NEW):
case ScheduleActionType.CLEAN_ERRORS:
// In case if new schedule is added we should remove errors, that previously existed
return {};

case ScheduleErrorActionType.REMOVE_WORKER_OVERTIME_ERROR:
const { workerName } = action.payload as RemoveWorkerErrorUpdateOvertimOrUndertimePayload;
const removeFromState = (
code: AlgorithmErrorCode.WorkerOvertime | AlgorithmErrorCode.WorkerUnderTime
): void => {
const matchingIndex = state[code]?.findIndex((p) => p.worker === workerName);
if (!_.isNil(matchingIndex) && matchingIndex !== -1) {
delete state[code]?.[matchingIndex];
state[code] = (state[code] as any[]).filter((el) => !!el);
}
};
removeFromState(AlgorithmErrorCode.WorkerOvertime);
removeFromState(AlgorithmErrorCode.WorkerUnderTime);
return _.cloneDeep(state);

case ScheduleErrorActionType.ADD_OVERTIME_OR_UNDERTIME:
const {
kind,
hours,
worker,
} = action.payload as ScheduleErrorUpdateOvertimOrUndertimePayload;
if (state[kind] === undefined) {
state[kind] = [];
}
const error = {
kind,
hours,
worker,
};
state[kind]!.push(error as any);

return _.cloneDeep(state);

default:
return state;
}
Expand Down
53 changes: 53 additions & 0 deletions src/state/schedule-error.action-creator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* 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 https://mozilla.org/MPL/2.0/. */

import { ThunkFunction } from "../api/persistance-store.model";
import {
AlgorithmErrorCode,
WorkerOvertime,
WorkerUnderTime,
} from "../common-models/schedule-error.model";
import { ActionModel } from "./models/action.model";
import { ScheduleErrorActionType } from "./reducers/month-state/schedule-errors.reducer";

export type ScheduleErrorUpdateOvertimOrUndertimePayload = WorkerUnderTime | WorkerOvertime;

export interface RemoveWorkerErrorUpdateOvertimOrUndertimePayload {
workerName: string;
}

export class ScheduleErrorActionCreator {
public static addUndertimeOrOvertimeError(
timeDiff: number,
worker: string
): ThunkFunction<unknown> {
return (dispatch): void => {
const type =
timeDiff > 0 ? AlgorithmErrorCode.WorkerOvertime : AlgorithmErrorCode.WorkerUnderTime;

const removeAction = this.clearWorkerOverTime(worker);
const addAction = {
type: ScheduleErrorActionType.ADD_OVERTIME_OR_UNDERTIME,
payload: {
kind: type,
hours: timeDiff,
worker,
},
};
dispatch(removeAction);
dispatch(addAction);
};
}

public static clearWorkerOverTime(
worker: string
): ActionModel<RemoveWorkerErrorUpdateOvertimOrUndertimePayload> {
return {
type: ScheduleErrorActionType.REMOVE_WORKER_OVERTIME_ERROR,
payload: {
workerName: worker,
},
};
}
}

0 comments on commit 63cbca9

Please sign in to comment.