-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feature/OP-1043-users-and-groups-soft-delete
- Loading branch information
Showing
56 changed files
with
954 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
...s/integrations/admin_activities/types_activities/ages_activities/edit_ages_activity.cy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/// <reference types="cypress" /> | ||
|
||
const AGE_TYPE_START_PATH = "/admin/types/ages"; | ||
|
||
describe("Admission types Edit Activity specs", () => { | ||
it("should render the ui", () => { | ||
cy.authenticate(AGE_TYPE_START_PATH); | ||
cy.dataCy("sub-activity-title").contains("Manage age types"); | ||
}); | ||
|
||
it("should show age types edit form", () => { | ||
cy.dataCy("edit-age-types").click(); | ||
cy.dataCy("sub-activity-title").contains("Edit age types"); | ||
}); | ||
|
||
it("should fail to edit the age type", () => { | ||
cy.byId("ageTypes\\[0\\]\\.to").type("1"); | ||
cy.dataCy("submit-form").click(); | ||
cy.dataCy("dialog-info").should("not.exist"); | ||
}); | ||
|
||
it("should successfully save age types changes", () => { | ||
cy.byId("ageTypes\\[0\\]\\.to").clear().type("0"); | ||
cy.byId("ageTypes\\[5\\]\\.to").clear().type("104"); | ||
cy.dataCy("submit-form").click(); | ||
cy.dataCy("dialog-info").contains("have been updated successfully!"); | ||
cy.dataCy("approve-dialog").click(); | ||
}); | ||
|
||
it("should redirect after age types update", () => { | ||
cy.dataCy("sub-activity-title").contains("Manage age types"); | ||
}); | ||
|
||
it("should cancel the cancellation of the age types update", () => { | ||
cy.dataCy("edit-age-types").click(); | ||
cy.dataCy("cancel-form").click(); | ||
cy.dataCy("dialog-info").contains( | ||
"Are you sure to cancel the age types update?" | ||
); | ||
cy.dataCy("close-dialog").click(); | ||
cy.dataCy("dialog-info").should("not.exist"); | ||
}); | ||
|
||
it("should cancel the age types update", () => { | ||
cy.dataCy("cancel-form").click(); | ||
cy.dataCy("approve-dialog").click(); | ||
cy.dataCy("dialog-info").should("not.exist"); | ||
cy.dataCy("sub-activity-title").contains("Manage age types"); | ||
}); | ||
}); |
19 changes: 19 additions & 0 deletions
19
...integrations/admin_activities/types_activities/ages_activities/manage_ages_activity.cy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/// <reference types="cypress" /> | ||
|
||
const AGE_TYPES_START_PATH = "/admin/types/ages"; | ||
|
||
describe("Age types Activity specs", () => { | ||
it("should render the ui", () => { | ||
cy.authenticate(AGE_TYPES_START_PATH); | ||
cy.dataCy("sub-activity-title").contains("Manage age types"); | ||
}); | ||
|
||
it("should present the table with 6 rows", () => { | ||
cy.dataCy("age-types-table") | ||
.find("table") | ||
.then(($table) => { | ||
const rows = $table.find("tbody tr"); | ||
expect(rows.length).equal(6); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
src/components/accessories/admin/types/components/agetypes/AgeTypes.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { useAppDispatch } from "libraries/hooks/redux"; | ||
import React, { useEffect } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { useNavigate } from "react-router"; | ||
import { getAgeTypes } from "state/types/ageTypes"; | ||
import { setTypeMode } from "../../../../../../state/types/config"; | ||
import Button from "../../../../button/Button"; | ||
import AgeTypesTable from "./ageTypesTable"; | ||
import "./styles.scss"; | ||
|
||
const AgeTypes = () => { | ||
const navigate = useNavigate(); | ||
const dispatch = useAppDispatch(); | ||
|
||
useEffect(() => { | ||
dispatch(getAgeTypes()); | ||
dispatch(setTypeMode("manage")); | ||
}, [dispatch]); | ||
|
||
const { t } = useTranslation(); | ||
|
||
return ( | ||
<> | ||
<h3 data-cy="sub-activity-title">{t("ageTypes.title")}</h3> | ||
|
||
<div className="ageTypes" data-cy="age-types-table"> | ||
<AgeTypesTable | ||
headerActions={ | ||
<Button | ||
onClick={() => { | ||
navigate("./edit"); | ||
}} | ||
type="button" | ||
variant="contained" | ||
color="primary" | ||
dataCy="edit-age-types" | ||
> | ||
{t("ageTypes.editAgeTypes")} | ||
</Button> | ||
} | ||
/> | ||
</div> | ||
</> | ||
); | ||
}; | ||
|
||
export default AgeTypes; |
44 changes: 44 additions & 0 deletions
44
src/components/accessories/admin/types/components/agetypes/ageTypesForm/AgeTypeFields.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import TextField from "components/accessories/textField/TextField"; | ||
import React, { FC } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { IAgeTypeFieldsProps } from "./types"; | ||
|
||
const AgeTypeFields: FC<IAgeTypeFieldsProps> = ({ | ||
formik, | ||
getErrorText, | ||
isValid, | ||
index, | ||
}) => { | ||
const { t } = useTranslation(); | ||
|
||
return ( | ||
<tr className="ageTypeFormRow"> | ||
<td>{formik.values.ageTypes[index].code}</td> | ||
<td className="fromField"> | ||
<TextField | ||
field={formik.getFieldProps(`ageTypes[${index}].from`)} | ||
theme="regular" | ||
label={t("ageTypes.from")} | ||
isValid={isValid("from", index)} | ||
errorText={getErrorText("from", index)} | ||
onBlur={formik.handleBlur} | ||
type="number" | ||
/> | ||
</td> | ||
<td className="toField"> | ||
<TextField | ||
field={formik.getFieldProps(`ageTypes[${index}].to`)} | ||
theme="regular" | ||
label={t("ageTypes.to")} | ||
isValid={isValid("to", index)} | ||
errorText={getErrorText("to", index)} | ||
onBlur={formik.handleBlur} | ||
type="number" | ||
/> | ||
</td> | ||
<td>{t(formik.values.ageTypes[index].description)}</td> | ||
</tr> | ||
); | ||
}; | ||
|
||
export default AgeTypeFields; |
192 changes: 192 additions & 0 deletions
192
src/components/accessories/admin/types/components/agetypes/ageTypesForm/AgeTypesForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import { useFormik } from "formik"; | ||
import { AgeTypeDTO } from "generated"; | ||
import { useAppDispatch, useAppSelector } from "libraries/hooks/redux"; | ||
import { get, has } from "lodash"; | ||
import React, { FC, useEffect, useMemo, useRef, useState } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { useNavigate } from "react-router"; | ||
import { updateAgeTypeReset } from "state/types/ageTypes"; | ||
import { array, number, object, ref, string } from "yup"; | ||
import checkIcon from "../../../../../../../assets/check-icon.png"; | ||
import warningIcon from "../../../../../../../assets/warning-icon.png"; | ||
import { PATHS } from "../../../../../../../consts"; | ||
import { | ||
formatAllFieldValues, | ||
getFromFields, | ||
} from "../../../../../../../libraries/formDataHandling/functions"; | ||
import Button from "../../../../../button/Button"; | ||
import ConfirmationDialog from "../../../../../confirmationDialog/ConfirmationDialog"; | ||
import InfoBox from "../../../../../infoBox/InfoBox"; | ||
import AgeTypeFields from "./AgeTypeFields"; | ||
import { validateRange } from "./consts"; | ||
import "./styles.scss"; | ||
import { IAgeTypesFormProps } from "./types"; | ||
|
||
const AgeTypesForm: FC<IAgeTypesFormProps> = ({ | ||
onSubmit, | ||
rows, | ||
submitButtonLabel, | ||
resetButtonLabel, | ||
isLoading, | ||
}) => { | ||
const dispatch = useAppDispatch(); | ||
const { t } = useTranslation(); | ||
const navigate = useNavigate(); | ||
const infoBoxRef = useRef<HTMLDivElement>(null); | ||
const [openResetConfirmation, setOpenResetConfirmation] = useState(false); | ||
const [validationErrors, setValidationErrors] = useState<string[]>([]); | ||
|
||
const updateAgeTypes = useAppSelector((state) => state.types.ageTypes.update); | ||
|
||
const errorMessage = useMemo( | ||
() => updateAgeTypes.error?.message ?? t("common.somethingwrong"), | ||
[t, updateAgeTypes.error?.message] | ||
); | ||
|
||
const initialValues = { | ||
ageTypes: rows.map((fields) => getFromFields(fields, "value")), | ||
}; | ||
|
||
const validationSchema = object({ | ||
ageTypes: array( | ||
object({ | ||
code: string().required(t("common.required")), | ||
description: string().required(t("common.required")), | ||
from: number() | ||
.required(t("common.required")) | ||
.min(0, t("common.greaterthan", { value: 0 })), | ||
to: number() | ||
.required(t("common.required")) | ||
.min(ref("from"), t("ageTypes.shouldbegreaterthanfrom")), | ||
}) | ||
), | ||
}); | ||
|
||
const formik = useFormik({ | ||
initialValues, | ||
validationSchema, | ||
enableReinitialize: true, | ||
onSubmit: (values) => { | ||
const formattedValues = rows.map((fields, index) => | ||
formatAllFieldValues(fields, values.ageTypes[index]) | ||
); | ||
|
||
const errors = validateRange(formattedValues as AgeTypeDTO[], t); | ||
setValidationErrors(errors); | ||
if (errors.length === 0) { | ||
onSubmit(formattedValues as any); | ||
} | ||
}, | ||
}); | ||
|
||
const isValid = (fieldName: string, index: number): boolean => { | ||
return ( | ||
has(formik.touched.ageTypes?.[index], fieldName) && | ||
has(formik.errors.ageTypes?.[index], fieldName) | ||
); | ||
}; | ||
|
||
const getErrorText = (fieldName: string, index: number): string => { | ||
return has(formik.touched.ageTypes?.[index], fieldName) | ||
? (get(formik.errors.ageTypes?.[index], fieldName) as string) | ||
: ""; | ||
}; | ||
|
||
const handleResetConfirmation = () => { | ||
setOpenResetConfirmation(false); | ||
navigate(-1); | ||
}; | ||
|
||
useEffect(() => { | ||
return () => { | ||
dispatch(updateAgeTypeReset()); | ||
}; | ||
}, [dispatch]); | ||
|
||
return ( | ||
<div className="ageTypesForm"> | ||
<form className="ageTypesForm__form" onSubmit={formik.handleSubmit}> | ||
<div className="row"> | ||
<table className="ageTypesFormTable"> | ||
<thead> | ||
<tr> | ||
<th>{t("ageTypes.code")}</th> | ||
<th>{t("ageTypes.from")}</th> | ||
<th>{t("ageTypes.to")}</th> | ||
<th>{t("ageTypes.description")}</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{rows.map((fields, index) => ( | ||
<AgeTypeFields | ||
formik={formik} | ||
getErrorText={getErrorText} | ||
index={index} | ||
isValid={isValid} | ||
key={index} | ||
/> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
|
||
<div className="ageTypesForm__buttonSet"> | ||
<div className="submit_button"> | ||
<Button | ||
type="submit" | ||
dataCy="submit-form" | ||
variant="contained" | ||
disabled={isLoading} | ||
> | ||
{submitButtonLabel} | ||
</Button> | ||
</div> | ||
<div className="reset_button"> | ||
<Button | ||
type="reset" | ||
variant="text" | ||
dataCy="cancel-form" | ||
disabled={isLoading} | ||
onClick={() => setOpenResetConfirmation(true)} | ||
> | ||
{resetButtonLabel} | ||
</Button> | ||
</div> | ||
</div> | ||
<ConfirmationDialog | ||
isOpen={openResetConfirmation} | ||
title={resetButtonLabel.toUpperCase()} | ||
info={t("ageTypes.cancelUpdate")} | ||
icon={warningIcon} | ||
primaryButtonLabel={t("common.ok")} | ||
secondaryButtonLabel={t("common.discard")} | ||
handlePrimaryButtonClick={handleResetConfirmation} | ||
handleSecondaryButtonClick={() => setOpenResetConfirmation(false)} | ||
/> | ||
{updateAgeTypes.status === "FAIL" && ( | ||
<div ref={infoBoxRef} className="info-box-container"> | ||
<InfoBox type="error" message={errorMessage} /> | ||
</div> | ||
)} | ||
{validationErrors.length > 0 && ( | ||
<div ref={infoBoxRef} className="info-box-container"> | ||
<InfoBox type="error" message={validationErrors.join("; ")} /> | ||
</div> | ||
)} | ||
<ConfirmationDialog | ||
isOpen={!!updateAgeTypes.hasSucceeded} | ||
title={t("ageTypes.updated")} | ||
icon={checkIcon} | ||
info={t("ageTypes.updateSuccess")} | ||
primaryButtonLabel="Ok" | ||
handlePrimaryButtonClick={() => { | ||
navigate(PATHS.admin_age_types); | ||
}} | ||
handleSecondaryButtonClick={() => ({})} | ||
/> | ||
</form> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AgeTypesForm; |
Oops, something went wrong.