Skip to content

Commit

Permalink
Task-286 Save workers persistently (#222)
Browse files Browse the repository at this point in the history
* Fixes worker agreement

* Contract type and worker type are automatically shown when user is edited

* fixes work time calculation for civil contract

* Adds validation to worker drawer

* Adds opacity

* Fixes edit mode and table with workers

* Adds validation for invalid civil time

* TASK-296 Show month label in edit mode (#212)

* Show month label in edit mode, fix disable button opacity

* Disable edit mode button

* Create app config context to store app mode

* Remove link parent component to shrink onClick space

* Put worker logic in action creator

* Refactor schedule action creator

* Save NZ if in current month

Co-authored-by: Bohdan Forostianyi <[email protected]>
Co-authored-by: Paweł Renc <[email protected]>
  • Loading branch information
3 people authored Feb 26, 2021
1 parent cf35214 commit 4fc7ae0
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 153 deletions.
16 changes: 8 additions & 8 deletions cypress/integration/e2e/table/schedule-errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ context("Schedule errors", () => {
cy.enterEditMode();
});

// it("Should show errors returned by server", () => {
// cy.get("[data-cy=save-schedule-button]").click();
// cy.get("[data-cy=check-schedule-button]").click();
// cy.get("[data-cy=open-folding-section]").click({ multiple: true });
// cy.contains("Za mało pracowników w trakcie dnia w dniu 7 w godzinach 6-22, potrzeba 8, jest 3");
// cy.contains("Za mało pracowników w trakcie dnia w dniu 8 w godzinach 6-22, potrzeba 8, jest 4");
// cy.contains("Za mało pracowników w nocy w dniu 9 w godzinach 6-22, potrzeba 5, jest 3");
// });
// it("Should show errors returned by server", () => {
// cy.get("[data-cy=save-schedule-button]").click();
// cy.get("[data-cy=check-schedule-button]").click();
// cy.get("[data-cy=open-folding-section]").click({ multiple: true });
// cy.contains("Za mało pracowników w trakcie dnia w dniu 7 w godzinach 6-22, potrzeba 8, jest 3");
// cy.contains("Za mało pracowników w trakcie dnia w dniu 8 w godzinach 6-22, potrzeba 8, jest 4");
// cy.contains("Za mało pracowników w nocy w dniu 9 w godzinach 6-22, potrzeba 5, jest 3");
// });
});
1 change: 1 addition & 0 deletions cypress/integration/unit/helpers/shift.helper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ describe("ShiftHelper", () => {
[ShiftCode.K]: 0,
[ShiftCode.OP]: 0,
[ShiftCode.OK]: 0,
[ShiftCode.NZ]: 0,
};

Object.values(SHIFTS).forEach((shift) => {
Expand Down
6 changes: 1 addition & 5 deletions src/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@
import axios, { AxiosInstance } from "axios";
import { ScheduleDataModel } from "../common-models/schedule-data.model";
import { ScheduleError } from "../common-models/schedule-error.model";
import { Shift, SHIFTS } from "../common-models/shift-info.model";
import { SHIFTS } from "../common-models/shift-info.model";
import { v4 as uuidv4 } from "uuid";

interface BackendErrorObject extends Omit<ScheduleError, "kind"> {
code: string;
}

interface BackendShiftModel extends Shift {
is_working_shift: boolean;
}

function escapeJuliaIndexes(error: ScheduleError): ScheduleError {
const indexFields = ["day", "week"];
indexFields.forEach((field) => {
Expand Down
9 changes: 9 additions & 0 deletions src/common-models/shift-info.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export enum ShiftCode {
U = "U",
L4 = "L4",
K = "K",
NZ = "NZ",
OP = "OP",
OK = "OK",
}
Expand Down Expand Up @@ -162,6 +163,14 @@ export const SHIFTS: { [code in ShiftCode]: Shift } = {
color: "56f5f5",
isWorkingShift: false,
},
NZ: {
code: "NZ",
name: "Niezatrudniony",
from: 0,
to: 24,
color: "000000",
isWorkingShift: false,
},
};

export const FREE_SHIFTS = Object.values(SHIFTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import React from "react";
import { Button } from "../../button-component/button.component";
import DefaultModal from "../modal.component";
import { WorkerInfoModel } from "../../../../common-models/worker-info.model";
import { ScheduleDataActionCreator } from "../../../../state/reducers/month-state/schedule-data/schedule-data.action-creator";
import { useDispatch } from "react-redux";
import { WorkerActionCreator } from "../../../../state/reducers/worker.action-creator";

interface DeleteWorkerModalOptions {
setOpen: (open: boolean) => void;
Expand Down Expand Up @@ -38,7 +38,7 @@ export default function DeleteWorkerModalComponent(options: DeleteWorkerModalOpt
</Button>
<Button
onClick={(): void => {
dispatcher(ScheduleDataActionCreator.deleteWorker(worker));
dispatcher(WorkerActionCreator.deleteWorker(worker));
handleClose();
}}
size="small"
Expand Down
13 changes: 9 additions & 4 deletions src/components/namestable/worker-edit.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
import { ShiftHelper } from "../../helpers/shifts.helper";
import { StringHelper } from "../../helpers/string.helper";
import { ApplicationStateModel } from "../../state/models/application-state.model";
import { ScheduleDataActionCreator } from "../../state/reducers/month-state/schedule-data/schedule-data.action-creator";
import { Button } from "../common-components";
import { DropdownButtons } from "../common-components/dropdown-buttons/dropdown-buttons.component";
import { TextMaskCustom } from "../common-components/text-mask-custom/text-mask-custom.component";
import { useMonthInfo } from "../schedule-page/validation-drawer/use-verbose-dates";
import { WorkingTimeHelper } from "./working-time.helper";
import { WorkerActionCreator } from "../../state/reducers/worker.action-creator";

const useStyles = makeStyles({
container: {
Expand Down Expand Up @@ -74,7 +74,12 @@ export function WorkerEditComponent(options: WorkerEditComponentOptions): JSX.El

// #region setting up state
useEffect(() => {
setWorkerInfo({ ...initialWorkerModel, workerName: options.name, workerType: options.type });
setWorkerInfo({
...initialWorkerModel,
workerName: options.name,
workerType: options.type,
prevName: options.name,
});
}, [setWorkerInfo, options.name, options.type]);

const { monthNumber, year } = useMonthInfo();
Expand Down Expand Up @@ -202,8 +207,8 @@ export function WorkerEditComponent(options: WorkerEditComponentOptions): JSX.El

function handleClose(): void {
options.mode === WorkerEditComponentMode.ADD
? dispatcher(ScheduleDataActionCreator.addNewWorker(workerInfo))
: dispatcher(ScheduleDataActionCreator.modifyWorker(workerInfo));
? dispatcher(WorkerActionCreator.addNewWorker(workerInfo))
: dispatcher(WorkerActionCreator.modifyWorker(workerInfo));

setWorkerInfo(initialWorkerModel);
options.setOpen(false);
Expand Down
60 changes: 10 additions & 50 deletions src/state/reducers/month-state/employee-info.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,30 @@
import _ from "lodash";
import { ScheduleDataModel } from "../../../common-models/schedule-data.model";
import { ContractType, WorkersInfoModel } from "../../../common-models/worker-info.model";
import { WorkerInfoExtendedInterface } from "../../../components/namestable/worker-edit.component";
import { ShiftHelper } from "../../../helpers/shifts.helper";
import { ActionModel } from "../../models/action.model";
import { scheduleDataInitialState } from "./schedule-data/schedule-data-initial-state";
import {
AddNewWorkerActionPayload,
UpdateNewWorkerActionPayload,
} from "./schedule-data/schedule-data.action-creator";
import {
createActionName,
ScheduleActionModel,
ScheduleActionType,
} from "./schedule-data/schedule.actions";
import { WorkerActionPayload } from "../worker.action-creator";

function fromFractionToHours(fraction: string): number {
const result = fraction.split("/");
const [dividend, divisor] = result.map((string) => Number.parseInt(string));
return dividend / divisor;
}

function getEmployeeWorkTime({
export function getEmployeeWorkTime({
contractType,
employmentTime,
employmentTimeOther,
civilTime,
monthNumber,
year,
workerName,
}: UpdateNewWorkerActionPayload): number {
}): number {
if (monthNumber === undefined || year === undefined) {
throw Error("Month number and year are required");
}
Expand Down Expand Up @@ -62,21 +57,12 @@ function mockWorkerContractType(workerInfo: WorkersInfoModel): WorkersInfoModel
export function employeeInfoReducerF(name: string) {
return (
state: WorkersInfoModel = scheduleDataInitialState.employee_info,
action:
| ScheduleActionModel
| ActionModel<WorkerInfoExtendedInterface>
| ActionModel<UpdateNewWorkerActionPayload>
| ActionModel<AddNewWorkerActionPayload>
action: ScheduleActionModel | ActionModel<WorkerActionPayload>
): WorkersInfoModel => {
let monthEmployeeInfo: WorkersInfoModel;
let workerName, prevName, workerType, contractType;
if ((action.payload as WorkerInfoExtendedInterface) !== undefined) {
({
workerName,
prevName,
workerType,
contractType,
} = action.payload as WorkerInfoExtendedInterface);
let updatedEmployeeInfo;
if ((action.payload as WorkerActionPayload) !== undefined) {
({ updatedEmployeeInfo } = action.payload as WorkerActionPayload);
}
switch (action.type) {
case createActionName(name, ScheduleActionType.ADD_NEW):
Expand All @@ -89,36 +75,10 @@ export function employeeInfoReducerF(name: string) {
if (!monthEmployeeInfo) return state;
monthEmployeeInfo = mockWorkerContractType(monthEmployeeInfo);
return { ...state, ...monthEmployeeInfo };

case ScheduleActionType.DELETE_WORKER:
delete state.time[workerName];
delete state.type[workerName];
delete state.contractType?.[workerName];
return {
time: { ...state.time },
type: { ...state.type },
contractType: { ...state.contractType },
};
case ScheduleActionType.ADD_NEW_WORKER:
return {
time: {
[workerName]: getEmployeeWorkTime(action.payload as AddNewWorkerActionPayload),
...state.time,
},
type: { [workerName]: workerType, ...state.type },
contractType: { [workerName]: contractType, ...state.contractType },
};
case ScheduleActionType.MODIFY_WORKER:
delete state.time[prevName];
delete state.type[prevName];
delete state.contractType?.[prevName];
case ScheduleActionType.UPDATE_WORKER_INFO:
return {
time: {
...state.time,
[workerName]: getEmployeeWorkTime(action.payload as UpdateNewWorkerActionPayload),
},
type: { ...state.type, [workerName]: workerType },
contractType: { ...state.contractType, [workerName]: contractType },
...state,
...updatedEmployeeInfo,
};
default:
return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,11 @@ import {
import { RevisionType, ScheduleKey, ThunkFunction } from "../../../../api/persistance-store.model";
import { PERSISTENT_SCHEDULE_NAME, TEMPORARY_SCHEDULE_NAME } from "../../../app.reducer";
import { createActionName, ScheduleActionModel, ScheduleActionType } from "./schedule.actions";
import { WorkerInfoExtendedInterface } from "../../../../components/namestable/worker-edit.component";
import { LocalStorageProvider } from "../../../../api/local-storage-provider.model";
import _ from "lodash";
import { WorkerInfoModel } from "../../../../common-models/worker-info.model";
import { ActionModel } from "../../../models/action.model";
import { Shift } from "../../../../common-models/shift-info.model";

export interface UpdateNewWorkerActionPayload extends WorkerInfoExtendedInterface {
monthNumber: number;
year: number;
}
export interface AddNewWorkerActionPayload extends UpdateNewWorkerActionPayload {
shiftCountInActualSchedule: number;
}

export class ScheduleDataActionCreator {
static setScheduleFromScheduleDM(
newSchedule: ScheduleDataModel,
Expand Down Expand Up @@ -101,54 +91,6 @@ export class ScheduleDataActionCreator {
};
}

static addNewWorker(
worker: WorkerInfoExtendedInterface
): ThunkFunction<AddNewWorkerActionPayload> {
return async (dispatch, getState): Promise<void> => {
const { dates } = getState().actualState.persistentSchedule.present.month_info;
const {
month_number: monthNumber,
year,
} = getState().actualState.persistentSchedule.present.schedule_info;
const action = {
type: ScheduleActionType.ADD_NEW_WORKER,
payload: {
...worker,
shiftCountInActualSchedule: dates.length,
monthNumber,
year,
} as AddNewWorkerActionPayload,
};
dispatch(action);
};
}

static deleteWorker(worker: WorkerInfoModel | undefined): ThunkFunction<unknown> {
return async (dispatch): Promise<void> => {
const action = {
type: ScheduleActionType.DELETE_WORKER,
payload: { workerName: worker?.name },
};
dispatch(action);
};
}

static modifyWorker(
worker: WorkerInfoExtendedInterface
): ThunkFunction<UpdateNewWorkerActionPayload> {
return async (dispatch, getState): Promise<void> => {
const {
month_number: monthNumber,
year,
} = getState().actualState.persistentSchedule.present.schedule_info;
const action = {
type: ScheduleActionType.MODIFY_WORKER,
payload: { ...worker, monthNumber, year },
};
dispatch(action);
};
}

static addNewShift(shift: Shift): (dispatch) => Promise<void> {
return async (dispatch): Promise<void> => {
const action = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export type ScheduleActionModel = ActionModel<ScheduleDataModel>;
export enum ScheduleActionType {
UPDATE = "UPDATE_SCHEDULE",
ADD_NEW = "ADD_NEW_SCHEDULE",
ADD_NEW_WORKER = "ADD_NEW_WORKER",
MODIFY_WORKER = "MODIFY_WORKER",
DELETE_WORKER = "DELETE_WORKER",
UPDATE_WORKER_INFO = "UPDATE_WORKER_INFO",
CLEAN_ERRORS = "CLEAN_ERRORS",
ADD_NEW_SHIFT = "ADD_NEW_SHIFT",
MODIFY_SHIFT = "MODIFY_SHIFT",
Expand Down
35 changes: 12 additions & 23 deletions src/state/reducers/month-state/shifts-info.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,35 @@
* 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 { ScheduleDataModel } from "../../../common-models/schedule-data.model";
import { ShiftCode, ShiftInfoModel } from "../../../common-models/shift-info.model";
import { ShiftInfoModel } from "../../../common-models/shift-info.model";
import { scheduleDataInitialState } from "./schedule-data/schedule-data-initial-state";
import {
createActionName,
ScheduleActionType,
ScheduleActionModel,
ScheduleActionType,
} from "./schedule-data/schedule.actions";
import { ActionModel } from "../../models/action.model";
import { WorkerInfoExtendedInterface } from "../../../components/namestable/worker-edit.component";
import { AddNewWorkerActionPayload } from "./schedule-data/schedule-data.action-creator";
import { WorkerActionPayload } from "../worker.action-creator";

export function scheduleShiftsInfoReducerF(name: string) {
return (
state: ShiftInfoModel = scheduleDataInitialState.shifts,
action:
| ScheduleActionModel
| ActionModel<WorkerInfoExtendedInterface>
| ActionModel<AddNewWorkerActionPayload>
action: ScheduleActionModel | ActionModel<WorkerActionPayload>
): ShiftInfoModel => {
let workerName, prevName;
if (action.payload as WorkerInfoExtendedInterface) {
({ workerName, prevName } = action.payload as WorkerInfoExtendedInterface);
let updatedShifts;
if (action.payload as WorkerActionPayload) {
({ updatedShifts } = action.payload as WorkerActionPayload);
}
switch (action.type) {
case createActionName(name, ScheduleActionType.ADD_NEW):
case createActionName(name, ScheduleActionType.UPDATE):
const data = (action.payload as ScheduleDataModel)?.shifts;
return { ...data };
case ScheduleActionType.DELETE_WORKER:
delete state[workerName];
return { ...state };
case ScheduleActionType.ADD_NEW_WORKER:
const { shiftCountInActualSchedule } = action.payload as AddNewWorkerActionPayload;
const newShiftsArr = [...Array(shiftCountInActualSchedule).fill(ShiftCode.W)];
return { ...{ [workerName]: newShiftsArr }, ...state };
case ScheduleActionType.MODIFY_WORKER:
const shiftsArr = state[prevName];
delete state[prevName];
return { ...{ [workerName]: shiftsArr }, ...state };

case ScheduleActionType.UPDATE_WORKER_INFO:
return {
...state,
...updatedShifts,
};
default:
return state;
}
Expand Down
Loading

0 comments on commit 4fc7ae0

Please sign in to comment.