From 899e79d5c9e3b5bfd47968dae8f4ff9de3448cab Mon Sep 17 00:00:00 2001 From: Omar Date: Fri, 18 Oct 2024 07:26:13 +0200 Subject: [PATCH] feat(procedures): gdpr implementation of document upload (#13581) ref: MANAGER-15339 Signed-off-by: Omar ALKABOUSS MOUSSANA --- .../components/FileInput/FileInputList.tsx | 15 +++- .../FileInput/FileInputListItem.tsx | 13 ++- .../FileField/FileField.component.tsx | 85 +++++++++++++++++++ .../rgdp/rgdpForm/RGDPForm.component.tsx | 81 +++++++++++++++++- .../pages/rgdp/rgdpForm/RGDPForm.constants.ts | 4 + .../src/pages/rgdp/rgdpForm/RGDPForm.test.tsx | 6 ++ .../SelectField/SelectField.component.tsx | 2 +- .../TextAreaField/TextAreaField.component.tsx | 2 +- .../TextField/TextField.component.tsx | 2 +- .../translations/rgdp/Messages_fr_FR.json | 13 ++- .../apps/procedures/src/types/gdpr.type.ts | 5 ++ 11 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/FileField/FileField.component.tsx diff --git a/packages/manager/apps/procedures/src/components/FileInput/FileInputList.tsx b/packages/manager/apps/procedures/src/components/FileInput/FileInputList.tsx index 38d965f6060a..6740500ae2d1 100644 --- a/packages/manager/apps/procedures/src/components/FileInput/FileInputList.tsx +++ b/packages/manager/apps/procedures/src/components/FileInput/FileInputList.tsx @@ -1,5 +1,11 @@ import React, { FunctionComponent, MouseEvent, useContext } from 'react'; import { useTranslation } from 'react-i18next'; +import { OsdsText } from '@ovhcloud/ods-components/react'; +import { + ODS_THEME_COLOR_INTENT, + ODS_THEME_TYPOGRAPHY_LEVEL, + ODS_THEME_TYPOGRAPHY_SIZE, +} from '@ovhcloud/ods-common-theming'; import { FileInputContext } from './FileInputContainer'; import { FileInputListItem } from './FileInputListItem'; @@ -26,9 +32,14 @@ export const FileInputList: FunctionComponent = ({ className }) => { return value?.length ? (
{multiple && ( - + )} {value.map((file, index) => ( = ({ } size={ODS_ICON_SIZE.sm} /> -
+ {file.name} {`(${bytesToSize(file.size)})`} {file.errors.map((error, index) => ( {error} ))} -
+ ; + required?: string; + helper?: string; + accept?: string; + maxFiles: number; + maxSize: number; + pattern?: ValidationRule; + tooltips?: string[]; + multiple?: boolean; + validate?: + | Validate + | Record>; +}; + +export const FileField: FunctionComponent = ({ + control, + name, + label, + required, + accept, + maxFiles, + maxSize, + multiple, + helper, +}) => { + return ( +
+ { + if (!files) return true; + return files.flatMap((itemFile) => itemFile.errors).length < 1; + }, + }, + }} + render={({ field: { onChange, value, name: _name } }) => ( + onChange(e.files)} + maxSize={maxSize} + multiple={multiple} + > + + + + {helper && {helper}} + + + + )} + /> +
+ ); +}; diff --git a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.component.tsx b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.component.tsx index 98943b631c37..639e781e8854 100644 --- a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.component.tsx +++ b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.component.tsx @@ -2,7 +2,7 @@ import { ODS_THEME_COLOR_INTENT, ODS_THEME_TYPOGRAPHY_LEVEL, } from '@ovhcloud/ods-common-theming'; -import { ODS_BUTTON_TYPE } from '@ovhcloud/ods-components'; +import { ODS_BUTTON_TYPE, ODS_TEXT_SIZE } from '@ovhcloud/ods-components'; import { OsdsButton, OsdsText } from '@ovhcloud/ods-components/react'; import React, { FunctionComponent, useEffect } from 'react'; import { useForm } from 'react-hook-form'; @@ -14,9 +14,13 @@ import { TextAreaField } from './TextAreaField/TextAreaField.component'; import { EmailRegex, GDPRSubjectValues, + MaxFileSize, + OtherDocumentsMaxFiles, + RGDPAcceptFile, TextInputRegex, } from './RGDPForm.constants'; import './RGDPForm.style.css'; +import { FileField } from './FileField/FileField.component'; export const RGDPForm: FunctionComponent = () => { const { t } = useTranslation('rgdp'); @@ -133,7 +137,82 @@ export const RGDPForm: FunctionComponent = () => { control={control} />
+
+
+ + {t('rgdp_form_upload_documents_title')} + + + + {t('rgdp_form_upload_documents_notice')} + + + + {t('rgdp_form_upload_documents_notice_readable_doc')} + + + + {t('rgdp_form_upload_documents_notice_no_valid_doc')} + + + + {t('rgdp_form_upload_documents_notice_file_format')} + +
+ + + + + +
]*$/; + +export const OtherDocumentsMaxFiles = 8; +export const RGDPAcceptFile = 'image/jpeg,image/jpg,image/png,application/pdf'; +export const MaxFileSize = 10 * 1024 * 1024; diff --git a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.test.tsx b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.test.tsx index da1ae233c1ca..692f4b03f905 100644 --- a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.test.tsx +++ b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/RGDPForm.test.tsx @@ -52,6 +52,12 @@ describe('RGDPForm', () => { expect( getByText('rgdp_form_field_label_request_description:'), ).toBeInTheDocument(); + + expect(getByText('rgdp_form_field_label_id_front:')).toBeInTheDocument(); + expect(getByText('rgdp_form_field_label_id_back:')).toBeInTheDocument(); + expect( + getByText('rgdp_form_field_label_other_documents:'), + ).toBeInTheDocument(); }); }); diff --git a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/SelectField/SelectField.component.tsx b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/SelectField/SelectField.component.tsx index 116bff9f8c63..d68b443df4a6 100644 --- a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/SelectField/SelectField.component.tsx +++ b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/SelectField/SelectField.component.tsx @@ -71,7 +71,7 @@ export const SelectField: FunctionComponent = ({ = ({ onOdsBlur={onBlur} error={Boolean(error) || undefined} id={id} - value={value} + value={value as string} color={ODS_THEME_COLOR_INTENT.primary} /> diff --git a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/TextField/TextField.component.tsx b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/TextField/TextField.component.tsx index d9b78cff6dc9..c09f907941c1 100644 --- a/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/TextField/TextField.component.tsx +++ b/packages/manager/apps/procedures/src/pages/rgdp/rgdpForm/TextField/TextField.component.tsx @@ -67,7 +67,7 @@ export const TextField: FunctionComponent = ({ name={name} onOdsInputBlur={onBlur} id={id} - value={value} + value={value as string} type={ODS_INPUT_TYPE.text} color={ODS_THEME_COLOR_INTENT.primary} /> diff --git a/packages/manager/apps/procedures/src/public/translations/rgdp/Messages_fr_FR.json b/packages/manager/apps/procedures/src/public/translations/rgdp/Messages_fr_FR.json index 9c5886bdbbff..8710fecd4d1c 100644 --- a/packages/manager/apps/procedures/src/public/translations/rgdp/Messages_fr_FR.json +++ b/packages/manager/apps/procedures/src/public/translations/rgdp/Messages_fr_FR.json @@ -28,5 +28,16 @@ "rgdp_form_subject_payment_method_remove": "Suppression des informations de paiement", "rgdp_form_subject_other_request": "Autre demande", "rgdp_legal_information": "Les données collectées ci-dessus sont nécessaires au traitement de votre demande. Elles seront conservées pour une durée de 5 ans. Dans l'hypothèse où une copie de votre pièce d'identité viendrait à vous être demandé, celle-ci sera conservée pour une durée maximale de 15 jours.", - "rgdp_legal_information_policy": "Pour en savoir plus, sur le traitement de vos données personnelles et connaître vos droits, vous pouvez consulter notre Politique d'utilisation de données à caractère personnel OVHcloud." + "rgdp_legal_information_policy": "Pour en savoir plus, sur le traitement de vos données personnelles et connaître vos droits, vous pouvez consulter notre Politique d'utilisation de données à caractère personnel OVHcloud.", + "rgdp_form_upload_documents_title": "Déposez vos documents", + "rgdp_form_upload_documents_notice": "Pour exercer vos droits sur les données vous concernant, merci de joindre la copie d'un justificatif d'identité (tel que carte nationale d'identité, passeport ou autre), sauf si les éléments communiqués dans le cadre de votre demande permettent de vous identifier de façon certaine. À défaut, la demande ne pourra pas être traitée.", + "rgdp_form_upload_documents_notice_readable_doc": "Attention, veuillez vous assurer que tous vos documents sont corrects et lisibles avant envoi.", + "rgdp_form_upload_documents_notice_no_valid_doc": "En cas de documents non valides, cette procédure sera annulée et vous devrez en effectuer une nouvelle.", + "rgdp_form_upload_documents_notice_file_format": "Format : jpg, jpeg, pdf, png. La taille maximale du fichier pour chaque document est de 10 Mo.", + + "rgdp_form_field_label_id_front": "Pièce d'identité (recto)", + "rgdp_form_field_label_id_back": "Pièce d'identité (verso)", + "rgdp_form_field_label_other_documents": "Autre document nécessaire pour la demande", + "rgdp_form_field_helper_id": "Carte nationale d'identité, passeport, carte de séjour ou permis de conduire.", + "rgdp_form_field_helper_other_documents": "Le nombre maximal de documents supporté est de {{maxFiles}}." } diff --git a/packages/manager/apps/procedures/src/types/gdpr.type.ts b/packages/manager/apps/procedures/src/types/gdpr.type.ts index 7488ff3aaf4f..89c520f89dac 100644 --- a/packages/manager/apps/procedures/src/types/gdpr.type.ts +++ b/packages/manager/apps/procedures/src/types/gdpr.type.ts @@ -1,3 +1,5 @@ +import { FileWithError } from '@/components/FileInput/FileInputContainer'; + export type GDPRFormValues = { firstName: string; surname: string; @@ -7,4 +9,7 @@ export type GDPRFormValues = { nicHandle?: string; messageSubject: string; requestDescription: string; + idDocumentFront: FileWithError[]; + idDocumentBack: FileWithError[]; + otherDocuments: FileWithError[]; };