From 25835b47ed28ab9c26a1deffcfd6374cd1913615 Mon Sep 17 00:00:00 2001
From: Ehevi <48785655+Ehevi@users.noreply.github.com>
Date: Tue, 9 Mar 2021 22:46:08 +0100
Subject: [PATCH] TASK-345 Improve descriptions of found schedule's issues
(#263)
---
.../schedule-errors.spec.ts | 2 +-
.../e2e/management-tab/edit-worker.spec.ts | 2 +-
src/app.tsx | 2 +-
.../styles/custom/_error-list-item.scss | 7 +-
src/assets/styles/styles/custom/_tables.scss | 5 +-
.../drawer/jira-like-drawer.component.tsx | 14 +--
.../error-tooltip-provider.component.tsx | 2 +-
.../error-list.component.tsx | 2 +-
src/helpers/error-message.helper.ts | 85 +++++++++++--------
src/helpers/shifts.helper.ts | 15 ++++
src/helpers/translations.helper.ts | 7 ++
11 files changed, 92 insertions(+), 51 deletions(-)
diff --git a/cypress/integration/e2e/app-error-handling/schedule-errors.spec.ts b/cypress/integration/e2e/app-error-handling/schedule-errors.spec.ts
index d3730bb6d..952eb1c40 100644
--- a/cypress/integration/e2e/app-error-handling/schedule-errors.spec.ts
+++ b/cypress/integration/e2e/app-error-handling/schedule-errors.spec.ts
@@ -4,7 +4,7 @@
import { WorkerType } from "../../../../src/common-models/worker-info.model";
-const addWorker = (workerName: string, position: WorkerType) => {
+const addWorker = (workerName: string, position: WorkerType): Cypress.Chainable => {
cy.get('[data-cy="btn-management-tab"]').click();
cy.get('[data-cy="management-page-title"]').should("be.visible");
cy.get('[data-cy="btn-add-worker"]').click();
diff --git a/cypress/integration/e2e/management-tab/edit-worker.spec.ts b/cypress/integration/e2e/management-tab/edit-worker.spec.ts
index 324073dc7..009c69bfd 100644
--- a/cypress/integration/e2e/management-tab/edit-worker.spec.ts
+++ b/cypress/integration/e2e/management-tab/edit-worker.spec.ts
@@ -17,7 +17,7 @@ context("Tab management", () => {
it("Should be able to set worker name", () => {
cy.get('[data-cy="name"]').type(newWorker);
- cy.get(`[value=\"${newWorker}\"]`).should("be.visible");
+ cy.get(`[value="${newWorker}"]`).should("be.visible");
});
it("Should be able to set worker position", () => {
diff --git a/src/app.tsx b/src/app.tsx
index d40bd086a..6f3d5a10e 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -127,7 +127,7 @@ function App(): JSX.Element {
-
+
{isElectron() ? <>> : }
diff --git a/src/assets/styles/styles/custom/_error-list-item.scss b/src/assets/styles/styles/custom/_error-list-item.scss
index 8f66db43f..b2463a8b2 100644
--- a/src/assets/styles/styles/custom/_error-list-item.scss
+++ b/src/assets/styles/styles/custom/_error-list-item.scss
@@ -17,7 +17,7 @@
.red-rectangle {
border-radius: 4px;
- width: 0.8%;
+ width: 4.5px;
position: absolute;
height: 100%;
background-color: $error-red;
@@ -29,6 +29,7 @@
flex: 1;
text-align: center;
padding-left: 15px;
+ padding-right: 25px;
position: relative;
.error-title-content {
@@ -47,6 +48,10 @@
margin: 10px 10px;
text-align: justify;
font-family: "Roboto";
+ strong {
+ letter-spacing: 1.5px;
+ font-weight: bolder;
+ }
}
.error-btn {
diff --git a/src/assets/styles/styles/custom/_tables.scss b/src/assets/styles/styles/custom/_tables.scss
index 7d5d21d87..1572b45c2 100644
--- a/src/assets/styles/styles/custom/_tables.scss
+++ b/src/assets/styles/styles/custom/_tables.scss
@@ -322,6 +322,7 @@ p {
font-size: 13px;
border-radius: 4px;
z-index: 3;
+ max-width: 500px;
}
.cell-details-popper {
@@ -331,8 +332,6 @@ p {
padding: 10px;
font-size: 13px;
border-radius: 4px;
- // position: absolute;
- // margin-left: 35px;
z-index: 2;
box-shadow: 0 1.4px 1.1px rgba(0, 0, 0, 0.034), 0 3.3px 2.7px rgba(0, 0, 0, 0.048),
0 6.2px 5px rgba(0, 0, 0, 0.06), 0 11.1px 8.5px rgba(0, 0, 0, 0.072),
@@ -340,7 +339,7 @@ p {
overflow: hidden;
}
-.errorTootlip-item {
+.errorTooltip-item {
margin: 0px !important;
.error-title {
flex: 0.5 !important;
diff --git a/src/components/common-components/drawer/jira-like-drawer.component.tsx b/src/components/common-components/drawer/jira-like-drawer.component.tsx
index 1ab70e8b8..95cae31e8 100644
--- a/src/components/common-components/drawer/jira-like-drawer.component.tsx
+++ b/src/components/common-components/drawer/jira-like-drawer.component.tsx
@@ -4,13 +4,17 @@
import React from "react";
import DrawerHeader from "./drawer-header.component";
import { Box } from "@material-ui/core";
-import { makeStyles } from "@material-ui/core/styles";
+import { makeStyles, Theme } from "@material-ui/core/styles";
import { useJiraLikeDrawer } from "./jira-like-drawer-context";
import ScssVars from "../../../assets/styles/styles/custom/_variables.module.scss";
-const useStyles = makeStyles({
+export interface StyleProps {
+ width: number;
+}
+
+const useStyles = makeStyles({
drawer: {
- minWidth: 690,
+ width: ({ width }): number => width,
height: `calc(100vh - ${
parseInt(ScssVars.headerHeight.slice(0, -2)) +
parseInt(ScssVars.drawerHeaderHeight.slice(0, -2)) +
@@ -20,8 +24,8 @@ const useStyles = makeStyles({
},
});
-export default function JiraLikeDrawer(): JSX.Element {
- const classes = useStyles();
+export default function JiraLikeDrawer(width): JSX.Element {
+ const classes = useStyles(width);
const { title, open, setOpen, childrenComponent } = useJiraLikeDrawer();
return (
diff --git a/src/components/schedule-page/table/schedule/schedule-parts/error-tooltip-provider.component.tsx b/src/components/schedule-page/table/schedule/schedule-parts/error-tooltip-provider.component.tsx
index 236f52769..3e752b700 100644
--- a/src/components/schedule-page/table/schedule/schedule-parts/error-tooltip-provider.component.tsx
+++ b/src/components/schedule-page/table/schedule/schedule-parts/error-tooltip-provider.component.tsx
@@ -94,7 +94,7 @@ export function ErrorTooltipProvider({
key={`${error.kind}_${index}`}
error={ErrorMessageHelper.getErrorMessage(error)}
interactable={false}
- className="errorTootlip-item"
+ className="errorTooltip-item"
showTitle={showErrorTitle}
/>
))}
diff --git a/src/components/schedule-page/validation-drawer/error-list.component.tsx b/src/components/schedule-page/validation-drawer/error-list.component.tsx
index 079eaf39d..27d375f2a 100644
--- a/src/components/schedule-page/validation-drawer/error-list.component.tsx
+++ b/src/components/schedule-page/validation-drawer/error-list.component.tsx
@@ -39,7 +39,7 @@ export default function ErrorList({ errors = [] }: Options): JSX.Element {
{
errorType: ScheduleErrorType.AON,
errors: errors.filter((e) => e.type === ScheduleErrorType.DSS),
- errorDescription: "Niedozwolona sekwencja zmian",
+ errorDescription: "Naruszenie wymaganej przerwy",
},
{
errorType: ScheduleErrorType.AON,
diff --git a/src/helpers/error-message.helper.ts b/src/helpers/error-message.helper.ts
index 229f70843..b255d460b 100644
--- a/src/helpers/error-message.helper.ts
+++ b/src/helpers/error-message.helper.ts
@@ -14,8 +14,11 @@ import {
ParseErrorCode,
ScheduleError,
} from "../common-models/schedule-error.model";
+import { SHIFTS as shifts } from "../common-models/shift-info.model";
import { ColorHelper } from "./colors/color.helper";
import { Color, Colors } from "./colors/color.model";
+import { ShiftHelper } from "./shifts.helper";
+import { TranslationHelper } from "./translations.helper";
type Error = ScheduleErrorLevel;
@@ -32,12 +35,6 @@ export class ErrorMessageHelper {
}
public static getErrorMessage(error: ScheduleError): ScheduleErrorMessageModel {
- const dayTimeTranslations = {
- MORNING: "porannej",
- AFTERNOON: "popołudniowej",
- NIGHT: "nocnej",
- };
-
const kind = error.kind;
let message: string;
let title = "Nie rozpoznano błędu";
@@ -48,18 +45,17 @@ export class ErrorMessageHelper {
switch (error.kind) {
case AlgorithmErrorCode.AlwaysAtLeastOneNurse:
i = 0;
- message = `Brak pielęgniarek w dniu ${error.day + 1} na zmianie ${
- error.day_time ? dayTimeTranslations[error.day_time] : ""
- }`;
- if (error.segments[i][0] !== 1 && error.segments[i][1] !== 24) {
- message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
+ message = `Brak pielęgniarek`;
+ if (error.segments[i][0] !== 1 || error.segments[i][1] !== 24) {
+ message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
}
while (error.segments[i + 1]) {
i++;
- if (error.segments[i][0] !== 1 && error.segments[i][1] !== 24) {
- message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
+ if (error.segments[i][0] !== 1 || error.segments[i][1] !== 24) {
+ message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
}
}
+ message += `.`;
type = ScheduleErrorType.AON;
title = "date";
if (error.day) {
@@ -68,17 +64,17 @@ export class ErrorMessageHelper {
break;
case AlgorithmErrorCode.WorkerNumberDuringDay:
i = 0;
- message = `Za mało pracowników w trakcie dnia w dniu ${error.day + 1}`;
- if (error.segments && error.segments[i][0] !== 1 && error.segments[i][1] !== 24) {
- message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
+ message = `Za mało pracowników w trakcie dnia`;
+ if (error.segments && (error.segments[i][0] !== 6 || error.segments[i][1] !== 22)) {
+ message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
while (error.segments[i + 1]) {
i++;
- if (error.segments[i][0] !== 1 && error.segments[i][1] !== 24) {
- message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
+ if (error.segments[i][0] !== 6 || error.segments[i][1] !== 22) {
+ message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
}
}
}
- message += `, potrzeba ${error.required}, jest ${error.actual}`;
+ message += `: potrzeba ${error.required}, jest ${error.actual}.`;
type = ScheduleErrorType.WND;
title = "date";
if (error.day) {
@@ -87,17 +83,17 @@ export class ErrorMessageHelper {
break;
case AlgorithmErrorCode.WorkerNumberDuringNight:
i = 0;
- message = `Za mało pracowników w nocy w dniu ${error.day + 1}`;
- if (error.segments && error.segments[i][0] !== 22 && error.segments[i][1] !== 6) {
- message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
+ message = `Za mało pracowników w nocy`;
+ if (error.segments && (error.segments[i][0] !== 22 || error.segments[i][1] !== 6)) {
+ message += ` w godzinach ${error.segments[i][0]}-${error.segments[i][1]}`;
while (error.segments[i + 1]) {
i++;
- if (error.segments[i][0] !== 22 && error.segments[i][1] !== 6) {
- message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
+ if (error.segments[i][0] !== 22 || error.segments[i][1] !== 6) {
+ message += `, ${error.segments[i][0]}-${error.segments[i][1]}`;
}
}
}
- message += `, potrzeba ${error.required}, jest ${error.actual}`;
+ message += `: potrzeba ${error.required}, jest ${error.actual}.`;
type = ScheduleErrorType.WNN;
title = "date";
if (error.day) {
@@ -105,11 +101,24 @@ export class ErrorMessageHelper {
}
break;
case AlgorithmErrorCode.DissalowedShiftSequence:
- message = `Niedozwolona sekwencja zmian dla pracownika ${
+ const timeNeeded = ShiftHelper.requiredFreeTimeAfterShift(shifts[error.preceding]);
+ const [earliestPossible, nextDay] = ShiftHelper.nextLegalShiftStart(
+ shifts[error.preceding]
+ );
+ let tooEarly = earliestPossible - shifts[error.succeeding].from;
+ if (tooEarly < 1) tooEarly += 24;
+ message = `Pracownik ${
error.worker
- } w dniu ${error.day + 1}: ${
- error.succeeding
- } po ${error.preceding}`;
+ } potrzebuje ${timeNeeded} godzin przerwy po zmianie ${
+ error.preceding
+ }
+ (${shifts[error.preceding].from}-${shifts[error.preceding].to}).
+ Nie może mieć zmiany wcześniej niż o ${earliestPossible}`;
+ if (nextDay) message += ` następnego dnia`;
+ message += `. Przypisana zmiana ${error.succeeding} (${
+ shifts[error.succeeding].from
+ }-${shifts[error.succeeding].to}) zaczyna się
+ o ${tooEarly} ${TranslationHelper.hourAccusativus(tooEarly)} za wcześnie.`;
type = ScheduleErrorType.DSS;
title = "date";
if (error.day) {
@@ -117,26 +126,28 @@ export class ErrorMessageHelper {
}
break;
case AlgorithmErrorCode.LackingLongBreak:
- message = `Brak wymaganej długiej przerwy dla pracownika ${
- error.worker
- } w tygodniu ${error.week + 1}`;
+ message = `Brak wymaganej długiej przerwy w tygodniu ${error.week + 1}.`;
type = ScheduleErrorType.LLB;
title = `${error.worker}`;
break;
case AlgorithmErrorCode.WorkerUnderTime:
- message = `Pracownik ${error.worker} ma ${error.hours} niedogodzin`;
+ message = `Pracownik ma ${error.hours} niedo${TranslationHelper.hourAccusativus(
+ error.hours
+ )}.`;
type = ScheduleErrorType.WUH;
title = `${error.worker}`;
break;
case AlgorithmErrorCode.WorkerOvertime:
- message = `Pracownik ${error.worker} ma ${error.hours} nadgodzin`;
+ message = `Pracownik ma ${error.hours} nad${TranslationHelper.hourAccusativus(
+ error.hours
+ )}.`;
type = ScheduleErrorType.WOH;
title = `${error.worker}`;
break;
case ParseErrorCode.UNKNOWN_VALUE:
- message = `Nieznana wartość zmiany: "${error.actual}" dla pracownika ${
- error.worker
- } w dniu ${error.day! + 1}. Przyjęto, że zmiana to wolne.`;
+ message = `Nieznana wartość zmiany: "${error.actual}" w dniu ${
+ error.day! + 1
+ }. Przyjęto, że zmiana to wolne.`;
type = ScheduleErrorType.ILLEGAL_SHIFT_VALUE;
title = `${error.worker}`;
break;
diff --git a/src/helpers/shifts.helper.ts b/src/helpers/shifts.helper.ts
index 3b8a5c3d9..4f22ab84a 100644
--- a/src/helpers/shifts.helper.ts
+++ b/src/helpers/shifts.helper.ts
@@ -48,6 +48,21 @@ export class ShiftHelper {
return duration === 0 ? 24 : duration;
}
+ public static requiredFreeTimeAfterShift(shift: Shift): number {
+ if (this.shiftCodeToWorkTime(shift) < 9) return 11;
+ if (this.shiftCodeToWorkTime(shift) > 12) return 24;
+ return 16;
+ }
+
+ public static nextLegalShiftStart(shift: Shift): [number, boolean] {
+ const sum = shift.to + this.requiredFreeTimeAfterShift(shift);
+ if (sum > 24) {
+ if ((shift.to + this.requiredFreeTimeAfterShift(shift)) % 24 === 0) return [24, true];
+ return [(shift.to + this.requiredFreeTimeAfterShift(shift)) % 24, true];
+ }
+ return [sum, false];
+ }
+
public static groupShiftsByWorkerType(
shifts: ShiftInfoModel = {},
workerTypes: { [workerName: string]: WorkerType } = {}
diff --git a/src/helpers/translations.helper.ts b/src/helpers/translations.helper.ts
index 28ac0006a..5e91b9d7d 100644
--- a/src/helpers/translations.helper.ts
+++ b/src/helpers/translations.helper.ts
@@ -66,4 +66,11 @@ export class TranslationHelper {
SU: "nd",
};
}
+
+ public static hourAccusativus(key: number): string {
+ if (key === 1) return "godzinę";
+ if (key === 12 || key === 13 || key === 14) return "godzin";
+ if ((key - 2) % 10 === 0 || (key - 3) % 10 === 0 || (key - 4) % 10 === 0) return "godziny";
+ return "godzin";
+ }
}