Skip to content

Commit

Permalink
Migrate enrollment reducer to typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre-Eric Garcia committed Aug 11, 2023
1 parent 7f3a02a commit 7423c12
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 71 deletions.
25 changes: 14 additions & 11 deletions frontend/src/components/molecules/StickyActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,20 @@ export const StickyActions: FunctionComponent<StickyActionsProps> = ({
/>
)}
<ButtonGroup className="datapass-sticky-actions-buttons" align="right">
{user && user.roles.length > 1 && authorizedEvents.length > 1 && (
<div className="datapass-sticky-actions-container">
<EventButton
onClick={() => handleActionChange(EnrollmentEvent.instruct)}
label="Instruction"
icon="edit"
quaternary={currentAction !== EnrollmentEvent.instruct}
iconFill
/>
</div>
)}
{user &&
user.roles &&
user.roles.length > 1 &&
authorizedEvents.length > 1 && (
<div className="datapass-sticky-actions-container">
<EventButton
onClick={() => handleActionChange(EnrollmentEvent.instruct)}
label="Instruction"
icon="edit"
quaternary={currentAction !== EnrollmentEvent.instruct}
iconFill
/>
</div>
)}
{authorizedEvents.includes(EnrollmentEvent.notify) && (
<div className="datapass-sticky-actions-container">
<EventButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const InstructorEnrollmentListFilters = ({
id="target_api"
options={
user?.roles
.filter((role) => role.endsWith(':reporter'))
?.filter((role) => role.endsWith(':reporter'))
.map((role) => {
const targetApiKey = role.split(':')[0];

Expand All @@ -75,7 +75,7 @@ const InstructorEnrollmentListFilters = ({
id="status"
options={Object.entries(STATUS_LABELS)
.filter(([key]) =>
user?.roles.includes('administrator') ? key : key !== 'archived'
user?.roles?.includes('administrator') ? key : key !== 'archived'
)
.map(([key, value]) => ({
key,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import ListHeader from '../../molecules/ListHeader';
import { DataProviderCard } from './DataProviderCard';
import Button from '../../atoms/hyperTexts/Button';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const SubmissionPanel: FunctionComponent<Props> = ({
updateEnrollment,
processEvent
);
if (user && user?.roles?.length > 1) {
if (user && user?.roles && user?.roles?.length > 1) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import { EnrollmentEvent } from '../../../config/event-configuration';
import { EnrollmentStatus } from '../../../config/status-parameters';
import { TeamMemberType } from '../InstructorEnrollmentList';
import enrollmentReducerFactory from './enrollmentReducer';

describe('enrollmentReducerFactory', () => {
const previousEnrollment = {
id: 1,
acl: {
update: true,
submit: true,
[EnrollmentEvent.update]: true,
[EnrollmentEvent.submit]: true,
},
status: 'draft',
status: EnrollmentStatus.draft,
target_api: 'api_particulier',
scopes: {
dgfip_declarant1_nom: false,
cnaf_quotient_familial: false,
},
updated_at: 'now',
created_at: 'now',
team_members: [
{
type: 'demandeur',
id: 1,
type: TeamMemberType.demandeur,
tmp_id: 'tmp_31',
email: '[email protected]',
},
{
type: 'responsable_technique',
id: 2,
type: TeamMemberType.responsable_technique,
tmp_id: 'tmp_34',
},
],
};

describe('enrollmentReducer with no demarches', () => {
const enrollmentReducer = enrollmentReducerFactory(null);

Expand Down Expand Up @@ -65,7 +74,8 @@ describe('enrollmentReducerFactory', () => {
};

expect(
enrollmentReducer(previousEnrollment, event).scopes.dgfip_declarant1_nom
enrollmentReducer(previousEnrollment, event)?.scopes
?.dgfip_declarant1_nom
).toEqual(true);
});

Expand Down Expand Up @@ -235,13 +245,15 @@ describe('enrollmentReducerFactory', () => {
).toEqual([
{
type: 'demandeur',
id: 1,
email: '[email protected]',
tmp_id: 'tmp_31',
},
{
type: 'responsable_technique',
email: '[email protected]',
tmp_id: 'tmp_34',
id: 2,
},
]);
});
Expand All @@ -261,13 +273,15 @@ describe('enrollmentReducerFactory', () => {
).toEqual([
{
type: 'demandeur',
id: 1,
email: '[email protected]',
tmp_id: 'tmp_31',
},
{
type: 'responsable_technique',
email: '',
tmp_id: 'tmp_34',
id: 2,
},
]);
});
Expand Down Expand Up @@ -317,11 +331,13 @@ describe('enrollmentReducerFactory', () => {
type: 'demandeur',
email: '[email protected]',
tmp_id: 'tmp_31',
id: 1,
},
{
type: 'responsable_technique',
email: '',
tmp_id: 'tmp_34',
id: 2,
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
import { cloneDeep, get, isEmpty, isObject, merge, omitBy, set } from 'lodash';
import { Enrollment } from '../InstructorEnrollmentList';

export const globalUpdate = ({ previousEnrollment, futureEnrollment }) =>
type Demarche = {
label: string;
about: string;
state: any;
team_members: any;
};

export const globalUpdate = ({
previousEnrollment,
futureEnrollment,
}: {
previousEnrollment: Enrollment;
futureEnrollment: Enrollment;
}) =>
merge(
{},
previousEnrollment,
omitBy(futureEnrollment, (e) => e === null) // do not merge null properties, keep empty string instead to avoid controlled input to switch to uncontrolled input
);

export const eventUpdateFactory =
(demarches = null) =>
(demarches: Demarche[] | null) =>
({
previousEnrollment,
event: {
target: { type = null, checked = null, value: inputValue, name },
},
}: {
previousEnrollment: Enrollment;
event: any;
}) => {
const value = type === 'checkbox' ? checked : inputValue;

let futureEnrollment = cloneDeep(previousEnrollment);
set(futureEnrollment, name, value);

if (demarches && name === 'demarche') {
const defaultDemarche = get(demarches, 'default', {});
const selectedDemarche = get(demarches, value, {});
const defaultDemarche = get(demarches, 'default', {}) as Demarche;
const selectedDemarche = get(demarches, value, {}) as Demarche;

let futureTeamMembers = futureEnrollment.team_members;
if (
!isEmpty(futureEnrollment.team_members) &&
!isEmpty(defaultDemarche.team_members)
) {
futureTeamMembers = futureEnrollment.team_members.map(
futureTeamMembers = futureEnrollment?.team_members?.map(
(futureTeamMember) => {
if (!defaultDemarche.team_members[futureTeamMember.type]) {
if (
!defaultDemarche.team_members[futureTeamMember.type as string]
) {
return futureTeamMember;
}

if (
!selectedDemarche.team_members ||
!selectedDemarche.team_members[futureTeamMember.type]
!selectedDemarche.team_members[futureTeamMember.type as string]
) {
return defaultDemarche.team_members[futureTeamMember.type];
return defaultDemarche.team_members[
futureTeamMember.type as string
];
}

return selectedDemarche.team_members[futureTeamMember.type];
return selectedDemarche.team_members[
futureTeamMember.type as string
];
}
);
}
Expand All @@ -59,16 +82,28 @@ export const eventUpdateFactory =
return futureEnrollment;
};

type Event = {
target: { type: string; checked: boolean; value: any; name: string };
};

// Fonction de garde de type
function isEvent(obj: Enrollment | Event): obj is Event {
return (obj as Event).target !== undefined;
}

export const enrollmentReducerFactory =
(demarches = null) =>
(previousEnrollment, eventOrFutureEnrollment) => {
(
previousEnrollment: Enrollment,
eventOrFutureEnrollment: Enrollment | Event | string
) => {
if (!isObject(eventOrFutureEnrollment)) {
return previousEnrollment;
}

// if no eventOrFutureEnrollment.target, this is a direct state update (network for instance)
// a direct state update DOES NOT trigger a pre-filled demarche update
if (!eventOrFutureEnrollment.target) {
if (!isEvent(eventOrFutureEnrollment)) {
const futureEnrollment = eventOrFutureEnrollment;

return globalUpdate({ previousEnrollment, futureEnrollment });
Expand Down
41 changes: 13 additions & 28 deletions frontend/src/components/templates/InstructorEnrollmentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,15 @@ export enum TeamMemberType {
}

export type TeamMember = {
email: string;
family_name: string;
given_name: string;
email?: string;
family_name?: string;
given_name?: string;
id: number;
job: string;
phone_number: string;
job?: string;
phone_number?: string;
type?: TeamMemberType;
uid?: string;
tmp_id?: string;
};

export type Contact = {
Expand All @@ -110,8 +111,8 @@ export type Enrollment = {
notify_events_from_demandeurs_count?: number;
unprocessed_notify_events_from_demandeurs_count?: number;
id: number;
intitule: string;
siret: string;
intitule?: string;
siret?: string;
consulted_by_instructor?: boolean;
requested_changes_have_been_done?: boolean;
nom_raison_sociale?: string | null;
Expand All @@ -120,29 +121,13 @@ export type Enrollment = {
status: EnrollmentStatus;
linked_franceconnect_enrollment_id?: number | null;
events?: Event[];
acl?: {
archive: boolean;
copy: boolean;
create: boolean;
destroy: boolean;
get_email_templates: boolean;
index: boolean;
mark_event_as_processed: boolean;
notify: boolean;
refuse: boolean;
request_changes: boolean;
revoke: boolean;
show: boolean;
submit: boolean;
update: boolean;
validate: boolean;
};
acl?: Partial<Record<EnrollmentEvent, boolean>>;
additional_content?: Record<string, unknown>;
cgu_approved: boolean;
cgu_approved?: boolean;
copied_from_enrollment_id?: number;
data_recipients: any;
data_retention_comment: any;
data_retention_period: number;
data_recipients?: any;
data_retention_comment?: any;
data_retention_period?: number;
date_mise_en_production?: string;
demarche?: any;
description?: string;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/templates/InstructorHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const InstructorHome: React.FC = () => {

const instructorTargetApis: TargetAPI[] =
user?.roles
.filter((role) => role.endsWith(':reporter'))
?.filter((role) => role.endsWith(':reporter'))
.map((role) => role.split(':')[0] as TargetAPI) || [];

return (
Expand All @@ -73,7 +73,7 @@ const InstructorHome: React.FC = () => {
defaultOverviewLabel="Toutes les habilitations"
options={instructorTargetApis.map((targetApiKey) => ({
key: targetApiKey,
label: dataProviderConfigurations?.[targetApiKey].label,
label: dataProviderConfigurations?.[targetApiKey]?.label,
}))}
values={targetApis.length === 0 ? [] : targetApis}
onChange={(values = []) => setTargetApis(values)}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/templates/Stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const USER_STATUS_COLORS = {
revoked: '#FF4747',
};

type Stats = {
type StatsData = {
enrollment_count: number;
validated_enrollment_count: number;
average_processing_time_in_days: number;
Expand All @@ -91,7 +91,7 @@ type Stats = {
};

export const Stats = () => {
const [stats, setStats] = useState<Stats | null>(null);
const [stats, setStats] = useState<StatsData | null>(null);
const { targetApi } = useParams();
const { dataProviderConfigurations } = useDataProviderConfigurations();

Expand Down
Loading

0 comments on commit 7423c12

Please sign in to comment.