Skip to content

Commit

Permalink
feat(procedures): implemented gdpr form
Browse files Browse the repository at this point in the history
ref:MANAGER-15317

Signed-off-by: Omar ALKABOUSS MOUSSANA <[email protected]>
  • Loading branch information
Omar ALKABOUSS MOUSSANA committed Oct 11, 2024
1 parent d2bff99 commit d8bbf5a
Show file tree
Hide file tree
Showing 15 changed files with 732 additions and 1 deletion.
3 changes: 3 additions & 0 deletions packages/manager/apps/container/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ module.exports = {
'./src/**/*.{js,jsx,ts,tsx}',
'../../../manager-react-components/src/**/*.{js,jsx,ts,tsx}',
],
corePlugins: {
preflight: false,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
ODS_INPUT_TYPE,
OdsInputValueChangeEventDetail,
OdsSelectValueChangeEventDetail,
OsdsInputCustomEvent,
OsdsSelectCustomEvent,
} from '@ovhcloud/ods-components';
import {
ODS_THEME_COLOR_INTENT,
ODS_THEME_TYPOGRAPHY_LEVEL,
} from '@ovhcloud/ods-common-theming';
import {
OsdsFormField,
OsdsSelect,
OsdsSelectOption,
OsdsText,
} from '@ovhcloud/ods-components/react';
import React, { FunctionComponent } from 'react';

export type SelectOption = {
label: string;
value: string;
};

type Props = {
label: string;
value?: string;
onChange?: (
event: OsdsSelectCustomEvent<OdsSelectValueChangeEventDetail>,
) => void;
onBlur?: React.FocusEventHandler<HTMLOsdsSelectElement>;
name: string;
id: string;
error?: string;
required?: boolean;
placeholder?: string;
options: SelectOption[];
};

export const Select: FunctionComponent<Props> = ({
label,
error,
id,
onChange,
name,
value,
onBlur,
required,
placeholder,
options,
}) => {
return (
<OsdsFormField error={error}>
<label htmlFor={id} slot="label">
<OsdsText
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
color={ODS_THEME_COLOR_INTENT.primary}
>
{label}:{required && <sup>*</sup>}
</OsdsText>
</label>

<OsdsSelect
onOdsValueChange={onChange}
onBlur={onBlur}
value={value}
name={name}
required={required}
id={id}
inline
>
{placeholder && <span slot="placeholder">{placeholder}</span>}
{options.map((option, index) => (
<OsdsSelectOption value={option.value} key={index}>
{option.label}
</OsdsSelectOption>
))}
</OsdsSelect>
</OsdsFormField>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
OdsTextAreaValueChangeEvent,
OsdsTextareaCustomEvent,
} from '@ovhcloud/ods-components';
import {
ODS_THEME_COLOR_INTENT,
ODS_THEME_TYPOGRAPHY_LEVEL,
} from '@ovhcloud/ods-common-theming';
import {
OsdsFormField,
OsdsText,
OsdsTextarea,
} from '@ovhcloud/ods-components/react';
import React, { FunctionComponent } from 'react';

type Props = {
label: string;
value?: string;
onChange?: (
event: OsdsTextareaCustomEvent<OdsTextAreaValueChangeEvent>,
) => void;
onBlur?: (event: OsdsTextareaCustomEvent<void>) => void;
name: string;
id: string;
error?: string;
required?: boolean;
};

export const TextArea: FunctionComponent<Props> = ({
label,
error,
id,
onChange,
name,
value,
onBlur,
required,
}) => {
return (
<OsdsFormField error={error}>
<label htmlFor={id} slot="label">
<OsdsText
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
color={ODS_THEME_COLOR_INTENT.primary}
>
{label}:{required && <sup>*</sup>}
</OsdsText>
</label>
<OsdsTextarea
onOdsValueChange={onChange}
required={required}
name={name}
onOdsBlur={onBlur}
id={id}
value={value}
color={ODS_THEME_COLOR_INTENT.primary}
/>
</OsdsFormField>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
ODS_INPUT_TYPE,
OdsInputValueChangeEventDetail,
OsdsInputCustomEvent,
} from '@ovhcloud/ods-components';
import {
ODS_THEME_COLOR_INTENT,
ODS_THEME_TYPOGRAPHY_LEVEL,
} from '@ovhcloud/ods-common-theming';
import {
OsdsFormField,
OsdsInput,
OsdsText,
} from '@ovhcloud/ods-components/react';
import React, { FunctionComponent } from 'react';

type Props = {
label: string;
value?: string;
onChange?: (
event: OsdsInputCustomEvent<OdsInputValueChangeEventDetail>,
) => void;
onBlur?: (event: OsdsInputCustomEvent<void>) => void;
name: string;
id: string;
error?: string;
required?: boolean;
helper?: string;
};

export const TextInput: FunctionComponent<Props> = ({
label,
error,
id,
onChange,
name,
value,
onBlur,
required,
helper,
}) => {
return (
<OsdsFormField error={error}>
<label htmlFor={id} slot="label">
<OsdsText
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
color={ODS_THEME_COLOR_INTENT.primary}
>
{label}:{required && <sup>*</sup>}
</OsdsText>
</label>
<OsdsInput
onOdsValueChange={onChange}
required={required}
name={name}
onOdsInputBlur={onBlur}
id={id}
value={value}
type={ODS_INPUT_TYPE.text}
color={ODS_THEME_COLOR_INTENT.primary}
/>
{helper && <OsdsText slot="helper">{helper}</OsdsText>}
</OsdsFormField>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ vi.mock('./rgdpIntroduction/RGDPIntroduction.component', () => ({
RGDPIntroduction: () => <div>RGDPIntroduction</div>,
}));

vi.mock('./rgdpForm/RGDPForm.component', () => ({
RGDPForm: () => <div>RGDPForm</div>,
}));

describe('RGDP Component', () => {
it('renders the component correctly', () => {
render(<RGDP />);

const introductionElement = screen.getByText('RGDPIntroduction');
const formElement = screen.getByText('RGDPForm');

expect(introductionElement).toBeInTheDocument();
expect(formElement).toBeInTheDocument();
});
});
2 changes: 2 additions & 0 deletions packages/manager/apps/procedures/src/pages/rgdp/RGDP.page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import { PageLayout } from '@/components/PageLayout/PageLayout.component';
import { RGDPIntroduction } from './rgdpIntroduction/RGDPIntroduction.component';
import { RGDPForm } from './rgdpForm/RGDPForm.component';

export default function RGDP() {
return (
<PageLayout>
<RGDPIntroduction />
<RGDPForm />
</PageLayout>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import { ODS_BUTTON_TYPE } from '@ovhcloud/ods-components';
import { OsdsButton } from '@ovhcloud/ods-components/react';
import React, { FunctionComponent, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { GDPRFormValues } from '@/types/gdpr.type';
import { TextField } from './TextField/TextField.component';
import { SelectField } from './SelectField/SelectField.component';
import { TextAreaField } from './TextAreaField/TextAreaField.component';
import {
EmailRegex,
GDPRSubjectValues,
TextInputRegex,
} from './RGDPForm.constants';

export const RGDPForm: FunctionComponent = () => {
const { t } = useTranslation('rgdp');
const {
handleSubmit,
watch,
control,
formState: { isSubmitting, isValid, touchedFields },
trigger,
} = useForm<GDPRFormValues>({ mode: 'onBlur' });

const onSubmit = (data: GDPRFormValues) => {
// TODO: Handle API call & ConfirmModal
console.log(data);
};

const email = watch('email');

useEffect(() => {
if (touchedFields.confirmEmail) {
trigger('confirmEmail');
}
}, [email]);

console.log(isSubmitting);
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<div className="my-10">
<TextField
label={t('rgdp_form_field_label_firstname')}
name="firstName"
required={t('rgdp_form_validation_message_required')}
pattern={{
value: TextInputRegex,
message: t('rgdp_form_validation_message_invalid_format'),
}}
control={control}
/>

<TextField
label={t('rgdp_form_field_label_surname')}
name="surname"
required={t('rgdp_form_validation_message_required')}
pattern={{
value: TextInputRegex,
message: t('rgdp_form_validation_message_invalid_format'),
}}
control={control}
/>

<TextField
label={t('rgdp_form_field_label_email')}
helper={t('rgdp_form_field_helper_email')}
name="email"
required={t('rgdp_form_validation_message_required')}
pattern={{
value: EmailRegex,
message: t('rgdp_form_validation_message_invalid_format'),
}}
control={control}
/>

<TextField
label={t('rgdp_form_field_label_confirm_email')}
name="confirmEmail"
validate={(value) =>
value === email || t('rgdp_form_validation_message_email_match')
}
control={control}
/>

<TextField
label={`${t('rgdp_form_field_label_nic')} (${t(
'rgdp_form_field_optional',
)})`}
name="nicHandle"
pattern={{
value: TextInputRegex,
message: t('rgdp_form_validation_message_invalid_format'),
}}
control={control}
/>

<SelectField
label={t('rgdp_form_field_label_subject')}
name="messageSubject"
required={t('rgdp_form_validation_message_required')}
control={control}
placeholder={t('rgdp_form_field_placeholder_subject')}
options={GDPRSubjectValues.map((value) => ({
value,
label: t(`rgdp_form_subject_${value}`),
}))}
/>

<TextAreaField
label={t('rgdp_form_field_label_request_description')}
name="requestDescription"
required={t('rgdp_form_validation_message_required')}
control={control}
/>
</div>

<OsdsButton
type={ODS_BUTTON_TYPE.submit}
color={ODS_THEME_COLOR_INTENT.primary}
disabled={!isValid || isSubmitting || undefined}
inline={true}
>
{t('rgdp_form_submit')}
</OsdsButton>
</form>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const GDPRSubjectValues = [
'opposition_right',
'rectification_right',
'access_right',
'erasure_right',
'limitation_right',
'portability_right',
'payment_method_remove',
'other_request',
];

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 = /^[^<>]*$/;
Loading

0 comments on commit d8bbf5a

Please sign in to comment.