Skip to content

Commit b0c9262

Browse files
committed
feat(api,admin): added feature sendCertificationToRegistryManager
1 parent 8251ddb commit b0c9262

File tree

9 files changed

+175
-34
lines changed

9 files changed

+175
-34
lines changed

packages/reva-admin-react/src/app/(admin)/certifications-v2/[certificationId]/page.tsx

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Accordion } from "@codegouvfr/react-dsfr/Accordion";
99
import Tag from "@codegouvfr/react-dsfr/Tag";
1010
import { SectionCard } from "@/components/card/section-card/SectionCard";
1111
import Alert from "@codegouvfr/react-dsfr/Alert";
12+
import { graphqlErrorToast, successToast } from "@/components/toast/toast";
1213

1314
type CertificationForPage = Exclude<
1415
ReturnType<typeof useUpdateCertificationPage>["certification"],
@@ -33,8 +34,25 @@ const PageContent = ({
3334
certification: CertificationForPage;
3435
}) => {
3536
const router = useRouter();
37+
3638
const structureSummaryCardComplete =
3739
!!certification.certificationAuthorityStructure;
40+
41+
const { sendCertificationToRegistryManager } = useUpdateCertificationPage({
42+
certificationId: certification.id,
43+
});
44+
45+
const onClickSend = async () => {
46+
try {
47+
await sendCertificationToRegistryManager.mutateAsync();
48+
successToast("La certification a bien été envoyée");
49+
} catch (error) {
50+
graphqlErrorToast(error);
51+
}
52+
};
53+
54+
const isEditable = certification.statusV2 == "BROUILLON";
55+
3856
return (
3957
<div data-test="update-certification-page">
4058
<h1>{certification.label}</h1>
@@ -49,7 +67,7 @@ const PageContent = ({
4967
data-test="certification-description-card"
5068
title="Descriptif de la certification"
5169
status="COMPLETED"
52-
isEditable
70+
isEditable={isEditable}
5371
titleIconClass="fr-icon-award-fill"
5472
>
5573
<div className="flex flex-col gap-4">
@@ -99,15 +117,19 @@ const PageContent = ({
99117
title="Blocs de compétences"
100118
data-test="competence-blocs-summary-card"
101119
titleIconClass="fr-icon-survey-fill"
102-
hasButton
103-
buttonPriority="tertiary no outline"
104-
buttonTitle="Ajouter un bloc de compétences"
105-
buttonIconId="fr-icon-add-line"
106-
buttonOnClick={() =>
107-
router.push(
108-
`/certifications-v2/${certification.id}/bloc-competence/add`,
109-
)
110-
}
120+
{...(() =>
121+
isEditable
122+
? {
123+
hasButton: true,
124+
buttonPriority: "tertiary no outline",
125+
buttonTitle: "Ajouter un bloc de compétences",
126+
buttonIconId: "fr-icon-add-line",
127+
buttonOnClick: () =>
128+
router.push(
129+
`/certifications-v2/${certification.id}/bloc-competence/add`,
130+
),
131+
}
132+
: { hasButton: false })()}
111133
>
112134
<p>
113135
La modification de bloc est possible, mais doit rester
@@ -135,15 +157,17 @@ const PageContent = ({
135157
))}
136158
</ul>
137159
</Accordion>
138-
<Button
139-
data-test="update-competence-bloc-button"
140-
priority="tertiary no outline"
141-
linkProps={{
142-
href: `/certifications-v2/${certification.id}/bloc-competence/${bloc.id}`,
143-
}}
144-
>
145-
Modifier
146-
</Button>
160+
{isEditable && (
161+
<Button
162+
data-test="update-competence-bloc-button"
163+
priority="tertiary no outline"
164+
linkProps={{
165+
href: `/certifications-v2/${certification.id}/bloc-competence/${bloc.id}`,
166+
}}
167+
>
168+
Modifier
169+
</Button>
170+
)}
147171
</li>
148172
))}
149173
</ul>
@@ -154,7 +178,11 @@ const PageContent = ({
154178
titleIconClass="fr-icon-group-fill"
155179
isEditable
156180
status={structureSummaryCardComplete ? "COMPLETED" : "TO_COMPLETE"}
157-
buttonOnClickHref={`/certifications-v2/${certification.id}/structure`}
181+
buttonOnClickHref={
182+
isEditable
183+
? `/certifications-v2/${certification.id}/structure`
184+
: undefined
185+
}
158186
>
159187
{structureSummaryCardComplete && (
160188
<>
@@ -207,15 +235,28 @@ const PageContent = ({
207235
</EnhancedSectionCard>
208236
</div>
209237
<hr className="mt-8" />
210-
<h2>Validation par le responsable des certifications</h2>
211-
<p>
212-
Lorsque la certification est prête, vous devez l’envoyer au responsable
213-
des certifications pour validation. Si aucun responsable de
214-
certifications n’existe pour le moment et qu’aucune validation n’est
215-
possible, elle pourra être visible des AAP mais pas encore des
216-
candidats.
217-
</p>
218-
<hr className="mb-6" />
238+
{isEditable && (
239+
<>
240+
<h2>Validation par le responsable des certifications</h2>
241+
<div className="flex">
242+
<p className="mb-0">
243+
Lorsque la certification est prête, vous devez l’envoyer au
244+
responsable des certifications pour validation. Si aucun
245+
responsable de certifications n’existe pour le moment et qu’aucune
246+
validation n’est possible, elle pourra être visible des AAP mais
247+
pas encore des candidats.
248+
</p>
249+
<Button
250+
disabled={!certification.certificationAuthorityStructure}
251+
className="h-[40px] self-end"
252+
onClick={onClickSend}
253+
>
254+
Envoyer
255+
</Button>
256+
</div>
257+
<hr className="mt-8 mb-6" />
258+
</>
259+
)}
219260
<Button
220261
priority="secondary"
221262
linkProps={{ href: "/certifications-v2", target: "_self" }}

packages/reva-admin-react/src/app/(admin)/certifications-v2/[certificationId]/updateCertification.hook.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useGraphQlClient } from "@/components/graphql/graphql-client/GraphqlClient";
22
import { graphql } from "@/graphql/generated";
3-
import { useQuery } from "@tanstack/react-query";
3+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
44

55
const getCertificationQuery = graphql(`
66
query getCertificationForUpdateCertificationPage($certificationId: ID!) {
@@ -9,6 +9,7 @@ const getCertificationQuery = graphql(`
99
label
1010
codeRncp
1111
status
12+
statusV2
1213
rncpExpiresAt
1314
rncpDeliveryDeadline
1415
availableAt
@@ -51,11 +52,22 @@ const getCertificationQuery = graphql(`
5152
}
5253
`);
5354

55+
const sendCertificationToRegistryManagerMutation = graphql(`
56+
mutation sendCertificationToRegistryManagerForUpdateCertificationStructurePage(
57+
$input: SendCertificationToRegistryManagerInput!
58+
) {
59+
referential_sendCertificationToRegistryManager(input: $input) {
60+
id
61+
}
62+
}
63+
`);
64+
5465
export const useUpdateCertificationPage = ({
5566
certificationId,
5667
}: {
5768
certificationId: string;
5869
}) => {
70+
const queryClient = useQueryClient();
5971
const { graphqlClient } = useGraphQlClient();
6072

6173
const {
@@ -75,5 +87,20 @@ export const useUpdateCertificationPage = ({
7587

7688
const certification = getCertificationQueryResponse?.getCertification;
7789

78-
return { certification, getCertificationQueryStatus };
90+
const sendCertificationToRegistryManager = useMutation({
91+
mutationFn: () =>
92+
graphqlClient.request(sendCertificationToRegistryManagerMutation, {
93+
input: { certificationId },
94+
}),
95+
onSuccess: () =>
96+
queryClient.invalidateQueries({
97+
queryKey: [certificationId],
98+
}),
99+
});
100+
101+
return {
102+
certification,
103+
getCertificationQueryStatus,
104+
sendCertificationToRegistryManager,
105+
};
79106
};

packages/reva-api/modules/certification-authority/features/createCertificationRegistryManager.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export const createCertificationRegistryManager = async ({
4040
});
4141
} catch (error) {
4242
const errorMessage = (error as FunctionalError).message;
43+
console.log(errorMessage);
44+
4345
if (errorMessage) {
4446
throw new Error(errorMessage);
4547
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { prismaClient } from "../../../prisma/client";
2+
import { SendCertificationToRegistryManagerInput } from "../referential.types";
3+
4+
export const sendCertificationToRegistryManager = async ({
5+
certificationId,
6+
}: SendCertificationToRegistryManagerInput) => {
7+
const certification = await prismaClient.certification.findUnique({
8+
where: {
9+
id: certificationId,
10+
},
11+
include: {
12+
competenceBlocs: true,
13+
certificationAuthorityStructure: true,
14+
},
15+
});
16+
17+
if (!certification) {
18+
throw new Error("La certification n'existe pas");
19+
}
20+
21+
if (!certification.certificationAuthorityStructure) {
22+
throw new Error(
23+
"La certification doit être rattachée à une structure certificatrice",
24+
);
25+
}
26+
27+
if (certification.statusV2 != "BROUILLON") {
28+
throw new Error(
29+
"Le statut de la certification doit être à l'état 'Brouillon'",
30+
);
31+
}
32+
33+
const updatedCertification = await prismaClient.certification.update({
34+
where: { id: certification.id },
35+
data: { statusV2: "A_VALIDER_PAR_CERTIFICATEUR" },
36+
});
37+
38+
return updatedCertification;
39+
};

packages/reva-api/modules/referential/features/updateCertificationStructureInput.ts renamed to packages/reva-api/modules/referential/features/updateCertificationStructure.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { prismaClient } from "../../../prisma/client";
22
import { UpdateCertificationStructureInput } from "../referential.types";
33

4-
export const updateCertificationStructureInput = async ({
4+
export const updateCertificationStructure = async ({
55
certificationId,
66
certificationAuthorityStructureId,
77
}: UpdateCertificationStructureInput) =>

packages/reva-api/modules/referential/referential.graphql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Certification {
2222
abilities: String
2323
codeRncp: String!
2424
status: CertificationStatus!
25+
statusV2: CertificationStatusV2!
2526
typeDiplome: String
2627
degree: Degree!
2728
conventionsCollectives: [ConventionCollective!]!
@@ -96,6 +97,13 @@ enum CertificationStatus {
9697
AVAILABLE
9798
}
9899

100+
enum CertificationStatusV2 {
101+
BROUILLON
102+
A_VALIDER_PAR_CERTIFICATEUR
103+
VALIDE_PAR_CERTIFICATEUR
104+
INACTIVE
105+
}
106+
99107
enum FinanceModule {
100108
unireva
101109
unifvae
@@ -315,20 +323,28 @@ input UpdateCompetenceBlocsInput {
315323
certificationId: ID!
316324
blocs: [CompetenceBlocInput!]!
317325
}
326+
318327
input UpdateCompetenceBlocInput {
319328
id: ID
320329
label: String!
321330
competences: [CompetenceInput!]!
322331
}
332+
323333
input CreateCompetenceBlocInput {
324334
certificationId: ID!
325335
label: String!
326336
competences: [CompetenceInput!]!
327337
}
338+
328339
input UpdateCertificationStructureInput {
329340
certificationId: ID!
330341
certificationAuthorityStructureId: String!
331342
}
343+
344+
input SendCertificationToRegistryManagerInput {
345+
certificationId: ID!
346+
}
347+
332348
type Mutation {
333349
referential_updateCertification(
334350
input: UpdateCertificationInput!
@@ -352,4 +368,7 @@ type Mutation {
352368
referential_updateCertificationStructure(
353369
input: UpdateCertificationStructureInput
354370
): Certification!
371+
referential_sendCertificationToRegistryManager(
372+
input: SendCertificationToRegistryManagerInput
373+
): Certification!
355374
}

packages/reva-api/modules/referential/referential.resolvers.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
UpdateCompetenceBlocInput,
2727
UpdateCompetenceBlocsInput,
2828
UpdateCertificationStructureInput,
29+
SendCertificationToRegistryManagerInput,
2930
} from "./referential.types";
3031
import { RNCPCertification, RNCPReferential } from "./rncp";
3132
import {
@@ -47,7 +48,8 @@ import { getCompetenceBlocsByCertificationIdV2 } from "./features/getCompetenceB
4748
import { getDomainsByCertificationId } from "./features/getDomainsByCertificationId";
4849
import { getDomainsByFormacodes } from "./features/getDomainsByFormacodes";
4950
import { createCertificationCompetenceBloc } from "./features/createCertificationCompetenceBloc";
50-
import { updateCertificationStructureInput } from "./features/updateCertificationStructureInput";
51+
import { updateCertificationStructure } from "./features/updateCertificationStructure";
52+
import { sendCertificationToRegistryManager } from "./features/sendCertificationToRegistryManager";
5153

5254
const unsafeReferentialResolvers = {
5355
Certification: {
@@ -208,7 +210,11 @@ const unsafeReferentialResolvers = {
208210
referential_updateCertificationStructure: (
209211
_parent: unknown,
210212
{ input }: { input: UpdateCertificationStructureInput },
211-
) => updateCertificationStructureInput(input),
213+
) => updateCertificationStructure(input),
214+
referential_sendCertificationToRegistryManager: (
215+
_parent: unknown,
216+
{ input }: { input: SendCertificationToRegistryManagerInput },
217+
) => sendCertificationToRegistryManager(input),
212218
},
213219
};
214220

packages/reva-api/modules/referential/referential.security.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export const referentialResolversSecurityMap = {
2222
hasRole(["admin"]),
2323
],
2424
"Mutation.referential_updateCertificationStructure": [hasRole(["admin"])],
25+
"Mutation.referential_sendCertificationToRegistryManager": [
26+
hasRole(["admin"]),
27+
],
2528

2629
"Query.getEtablissementAsAdmin": [hasRole(["admin"])],
2730
"Query.getCertificationCompetenceBloc": [hasRole(["admin"])],

packages/reva-api/modules/referential/referential.types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,7 @@ export interface UpdateCertificationStructureInput {
9898
certificationId: string;
9999
certificationAuthorityStructureId: string;
100100
}
101+
102+
export interface SendCertificationToRegistryManagerInput {
103+
certificationId: string;
104+
}

0 commit comments

Comments
 (0)