Skip to content

Commit

Permalink
feat(procedures): gdpr implementation of document upload (#13581)
Browse files Browse the repository at this point in the history
ref: MANAGER-15339

Signed-off-by: Omar ALKABOUSS MOUSSANA <[email protected]>
  • Loading branch information
oalkabouss authored and Omar ALKABOUSS MOUSSANA committed Oct 28, 2024
1 parent 2f6cc35 commit 899e79d
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -26,9 +32,14 @@ export const FileInputList: FunctionComponent<Props> = ({ className }) => {
return value?.length ? (
<div className={className}>
{multiple && (
<label className="text-sky-900 text-xs font-semibold">
<OsdsText
className="font-semibold"
size={ODS_THEME_TYPOGRAPHY_SIZE._100}
level={ODS_THEME_TYPOGRAPHY_LEVEL.caption}
color={ODS_THEME_COLOR_INTENT.text}
>
{t('account-disable-2fa-file-input-attachments')}
</label>
</OsdsText>
)}
{value.map((file, index) => (
<FileInputListItem
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FunctionComponent, MouseEvent } from 'react';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { ODS_ICON_NAME, ODS_ICON_SIZE } from '@ovhcloud/ods-components';
import { OsdsIcon } from '@ovhcloud/ods-components/react';
import { OsdsIcon, OsdsText } from '@ovhcloud/ods-components/react';
import { FileWithError } from './FileInputContainer';

type FileInputListItemProps = {
Expand Down Expand Up @@ -44,14 +44,21 @@ export const FileInputListItem: FunctionComponent<FileInputListItemProps> = ({
}
size={ODS_ICON_SIZE.sm}
/>
<div className="text-sm">
<OsdsText
className="text-sm"
color={
hasError
? ODS_THEME_COLOR_INTENT.error
: ODS_THEME_COLOR_INTENT.primary
}
>
{file.name} {`(${bytesToSize(file.size)})`}
{file.errors.map((error, index) => (
<span className="block" key={index}>
{error}
</span>
))}
</div>
</OsdsText>

<OsdsIcon
name={ODS_ICON_NAME.CLOSE}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { FunctionComponent } from 'react';
import { Control, Controller, Validate, ValidationRule } from 'react-hook-form';
import { OsdsText } from '@ovhcloud/ods-components/react';
import {
ODS_THEME_COLOR_INTENT,
ODS_THEME_TYPOGRAPHY_LEVEL,
} from '@ovhcloud/ods-common-theming';
import { ODS_TEXT_SIZE } from '@ovhcloud/ods-components';
import {
FileInputContainer,
FileWithError,
} from '@/components/FileInput/FileInputContainer';
import { GDPRFormValues } from '@/types/gdpr.type';

type Props = {
name: keyof GDPRFormValues;
label: string;
control: Control<GDPRFormValues>;
required?: string;
helper?: string;
accept?: string;
maxFiles: number;
maxSize: number;
pattern?: ValidationRule<RegExp>;
tooltips?: string[];
multiple?: boolean;
validate?:
| Validate<string, GDPRFormValues>
| Record<string, Validate<string, GDPRFormValues>>;
};

export const FileField: FunctionComponent<Props> = ({
control,
name,
label,
required,
accept,
maxFiles,
maxSize,
multiple,
helper,
}) => {
return (
<div className="mb-8">
<Controller
name={name}
control={control}
rules={{
required,
validate: {
custom: (files: FileWithError[]) => {
if (!files) return true;
return files.flatMap((itemFile) => itemFile.errors).length < 1;
},
},
}}
render={({ field: { onChange, value, name: _name } }) => (
<FileInputContainer
id={`field_id_${_name}`}
accept={accept}
maxFiles={maxFiles}
value={value as FileWithError[]}
onChange={(e) => onChange(e.files)}
maxSize={maxSize}
multiple={multiple}
>
<label className="block mb-2">
<OsdsText
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
color={ODS_THEME_COLOR_INTENT.primary}
>
{label}:{required && <sup>*</sup>}
</OsdsText>
</label>
<FileInputContainer.FileInput />

{helper && <OsdsText className="block">{helper}</OsdsText>}

<FileInputContainer.FileList className="w-5/5" />
</FileInputContainer>
)}
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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');
Expand Down Expand Up @@ -133,7 +137,82 @@ export const RGDPForm: FunctionComponent = () => {
control={control}
/>
</div>
<div>
<div className="mb-6 flex flex-col gap-4">
<OsdsText
color={ODS_THEME_COLOR_INTENT.info}
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
className="block mb-6"
size={ODS_TEXT_SIZE._500}
>
{t('rgdp_form_upload_documents_title')}
</OsdsText>

<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
size={ODS_TEXT_SIZE._500}
level={ODS_THEME_TYPOGRAPHY_LEVEL.body}
>
{t('rgdp_form_upload_documents_notice')}
</OsdsText>

<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
size={ODS_TEXT_SIZE._500}
level={ODS_THEME_TYPOGRAPHY_LEVEL.body}
>
{t('rgdp_form_upload_documents_notice_readable_doc')}
</OsdsText>

<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
size={ODS_TEXT_SIZE._500}
level={ODS_THEME_TYPOGRAPHY_LEVEL.body}
>
{t('rgdp_form_upload_documents_notice_no_valid_doc')}
</OsdsText>

<OsdsText
color={ODS_THEME_COLOR_INTENT.text}
size={ODS_TEXT_SIZE._500}
level={ODS_THEME_TYPOGRAPHY_LEVEL.body}
className="font-bold"
>
{t('rgdp_form_upload_documents_notice_file_format')}
</OsdsText>
</div>

<FileField
label={t('rgdp_form_field_label_id_front')}
name="idDocumentFront"
control={control}
accept={RGDPAcceptFile}
maxFiles={1}
maxSize={MaxFileSize}
helper={t('rgdp_form_field_helper_id')}
/>

<FileField
label={t('rgdp_form_field_label_id_back')}
name="idDocumentBack"
control={control}
accept={RGDPAcceptFile}
maxFiles={1}
maxSize={MaxFileSize}
helper={t('rgdp_form_field_helper_id')}
/>

<FileField
label={t('rgdp_form_field_label_other_documents')}
name="otherDocuments"
control={control}
accept={RGDPAcceptFile}
maxFiles={OtherDocumentsMaxFiles}
multiple
maxSize={MaxFileSize}
helper={t('rgdp_form_field_helper_other_documents', { maxFiles: 8 })}
/>
</div>
<OsdsButton
type={ODS_BUTTON_TYPE.submit}
color={ODS_THEME_COLOR_INTENT.primary}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ export const GDPRSubjectValues = [

export const EmailRegex = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9]{2}(?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
export const TextInputRegex = /^[^<>]*$/;

export const OtherDocumentsMaxFiles = 8;
export const RGDPAcceptFile = 'image/jpeg,image/jpg,image/png,application/pdf';
export const MaxFileSize = 10 * 1024 * 1024;
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const SelectField: FunctionComponent<Props> = ({
<OsdsSelect
onOdsValueChange={onChange}
onBlur={onBlur}
value={value}
value={value as string}
name={name}
required={Boolean(required)}
id={id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const TextAreaField: FunctionComponent<Props> = ({
onOdsBlur={onBlur}
error={Boolean(error) || undefined}
id={id}
value={value}
value={value as string}
color={ODS_THEME_COLOR_INTENT.primary}
/>
</OsdsFormField>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const TextField: FunctionComponent<Props> = ({
name={name}
onOdsInputBlur={onBlur}
id={id}
value={value}
value={value as string}
type={ODS_INPUT_TYPE.text}
color={ODS_THEME_COLOR_INTENT.primary}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a style='color:#2563eb; font-weight: 700;' href='{{legalPolicyLink}}' target='_blank'>Politique d'utilisation de données à caractère personnel OVHcloud.</a>"
"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 <a style='color:#2563eb; font-weight: 700;' href='{{legalPolicyLink}}' target='_blank'>Politique d'utilisation de données à caractère personnel OVHcloud.</a>",
"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}}."
}
5 changes: 5 additions & 0 deletions packages/manager/apps/procedures/src/types/gdpr.type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FileWithError } from '@/components/FileInput/FileInputContainer';

export type GDPRFormValues = {
firstName: string;
surname: string;
Expand All @@ -7,4 +9,7 @@ export type GDPRFormValues = {
nicHandle?: string;
messageSubject: string;
requestDescription: string;
idDocumentFront: FileWithError[];
idDocumentBack: FileWithError[];
otherDocuments: FileWithError[];
};

0 comments on commit 899e79d

Please sign in to comment.