Skip to content

Commit

Permalink
Merge pull request #105 from mission-apprentissage/suppression-demande
Browse files Browse the repository at this point in the history
Suppression demande
  • Loading branch information
flodlc authored Oct 2, 2023
2 parents 8769413 + 30017c1 commit 420dadb
Show file tree
Hide file tree
Showing 45 changed files with 601 additions and 322 deletions.
15 changes: 3 additions & 12 deletions server/public/mails/activate_account.mjml.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,15 @@
background-color="#0e4194"
color="white"
>
Lien d'activation
Activer votre compte
</mj-button>
<mj-text>
Concernant la saisie des demandes d’ouverture, fermeture… :<br />
- vous devez enregistrer la liste des demandes qui seront soumises
au vote du Conseil Régional et non pas toutes les demandes que vous
avez examinées.<br />
- vous pouvez enregistrer les demandes en mode brouillon ou
soumettre à la validation<br />
- lorsque la saisie de toutes les demandes pour la Région Académique
sera terminée, l’utilisateur “Pilote” (Recteur ou SGA) devra valider
la liste<br />
<br />
Pour toute question, consultez la
<a href="<%= utils.getPublicUrl(`/documentation`) %>">
documentation dans Orion</a
>
ou contactez-nous.<br />
ou contactez-nous à l'adresse suivante:
[email protected]<br />
</mj-text>
<%- include('./common/signature.ejs'); %>
</mj-column>
Expand Down
2 changes: 1 addition & 1 deletion server/public/mails/activate_account_pilote.mjml.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
background-color="#0e4194"
color="white"
>
Lien d'activation
Activer votre compte
</mj-button>
<mj-text>
Pour toute question, consultez la
Expand Down
54 changes: 54 additions & 0 deletions server/public/mails/activate_account_region.mjml.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<mjml>
<%- include('./common/head.ejs'); %>
<mj-body background-color="#f9fafc">
<%- include('./common/header.ejs'); %>
<mj-wrapper padding="10px 20px">
<mj-section background-color="#ffffff" padding-top="0">
<mj-column>
<mj-text>
Bonjour <%= data.recipient.firstname %> <%= data.recipient.lastname
%>,<br />
<br />
Orion vous donne accès à différentes données et indicateurs qui
outillent les réflexions autour de la transformation de la carte des
formations professionnelles en voie initiale.<br />
<br />
À compter d’aujourd’hui, vous pouvez saisir les demandes
d’ouverture/fermeture de formations dans Orion, ce qui permettra
d’initier le dialogue de gestion avec la DGESCO.<br />
<br />
Pour y accéder, activez votre compte en cliquant sur le lien
ci-dessous :<br />
</mj-text>
<mj-button
href="<%= utils.getPublicUrl(`/auth/activer-compte?activationToken=${data.activationToken}`) %>"
background-color="#0e4194"
color="white"
>
Activer votre compte
</mj-button>
<mj-text>
À venir pour compléter ce module de saisie :
<br />
<ul>
<li>
Le 16/10, une console d’analyse des demandes enrichie par les
indicateurs InserJeunes ;
</li>
<li>
Le 23/10, une page de pilotage du taux de transformation de la
carte.
</li>
</ul>
</mj-text>
<mj-text>
Si vous rencontrez des problèmes lors de la connexion ou de la
saisie, vous pouvez contacter notre équipe à l’adresse suivante :
[email protected]
</mj-text>
<%- include('./common/signature.ejs'); %>
</mj-column>
</mj-section>
</mj-wrapper>
</mj-body>
</mjml>
9 changes: 9 additions & 0 deletions server/src/modules/core/services/mailer/mailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export type TemplatePayloads = {
};
activationToken: string;
};
activate_account_region: {
recipient: {
lastname?: string | undefined;
firstname?: string | undefined;
email: string;
role: string;
};
activationToken: string;
};
};

function createTransporter(smtp: SMTPTransport & { secure: boolean }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,14 @@ export const [createUser, createUserFactory] = inject(
);

const template =
({ pilote: "activate_account_pilote" } as const)[role] ??
("activate_account" as const);
(
{
pilote: "activate_account_pilote",
pilote_region: "activate_account_region",
gestionnaire_region: "activate_account_region",
expert_region: "activate_account_region",
} as const
)[role] ?? ("activate_account" as const);

deps.shootTemplate({
to: email,
Expand Down
21 changes: 18 additions & 3 deletions server/src/modules/intentions/routes/demande.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { hasPermissionHandler } from "../../core";
import { countDemandes } from "../queries/countDemandes.query";
import { findDemande } from "../queries/findDemande.query";
import { findDemandes } from "../queries/findDemandes.query";
import { deleteDemande } from "../usecases/deleteDemande/deleteDemande.usecase";
import { submitDemande } from "../usecases/submitDemande/submitDemande.usecase";
import { submitDraftDemande } from "../usecases/submitDraftDemande/submitDraftDemande.usecase";

Expand All @@ -21,7 +22,7 @@ export const demandeRoutes = ({ server }: { server: Server }) => {
"/demande/submit",
{
schema: ROUTES_CONFIG.submitDemande,
preHandler: hasPermissionHandler("intentions/envoi"),
preHandler: hasPermissionHandler("intentions/ecriture"),
},
async (request, response) => {
const { demande } = request.body;
Expand All @@ -39,7 +40,7 @@ export const demandeRoutes = ({ server }: { server: Server }) => {
"/demande/draft",
{
schema: ROUTES_CONFIG.submitDraftDemande,
preHandler: hasPermissionHandler("intentions/envoi"),
preHandler: hasPermissionHandler("intentions/ecriture"),
},
async (request, response) => {
const { demande } = request.body;
Expand All @@ -65,7 +66,7 @@ export const demandeRoutes = ({ server }: { server: Server }) => {
const demande = await findDemande({ id: request.params.id, user });
if (!demande) return response.status(404).send();

const scope = getPermissionScope(user.role, "intentions/envoi");
const scope = getPermissionScope(user.role, "intentions/ecriture");
const canEdit = guardScope(scope?.default, {
user: () => user.id === demande.createurId,
region: () => user.codeRegion === demande.codeRegion,
Expand All @@ -76,6 +77,20 @@ export const demandeRoutes = ({ server }: { server: Server }) => {
}
);

server.delete(
"/demande/:id",
{
schema: ROUTES_CONFIG.deleteDemande,
preHandler: hasPermissionHandler("intentions/ecriture"),
},
async (request, response) => {
const user = request.user;
if (!user) throw Boom.forbidden();
await deleteDemande({ id: request.params.id, user });
response.status(200).send();
}
);

server.get(
"/demandes",
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { kdb } from "../../../../db/db";

export const deleteDemandeQuery = async (id: string) => {
await kdb.deleteFrom("demande").where("id", "=", id).execute();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Boom from "@hapi/boom";
import { getPermissionScope, guardScope } from "shared";

import { RequestUser } from "../../../core/model/User";
import { findOneDemande } from "../../repositories/findOneDemande.query";
import { deleteDemandeQuery } from "./deleteDemande.dep";

export const deleteDemandeFactory =
(deps = { findOneDemande, deleteDemandeQuery }) =>
async ({
id,
user,
}: {
id: string;
user: Pick<RequestUser, "id" | "role" | "codeRegion">;
}) => {
const demande = await deps.findOneDemande(id);
if (!demande) throw Boom.notFound();

const scope = getPermissionScope(user.role, "intentions/ecriture");
const isAllowed = guardScope(scope?.default, {
user: () => user.id === demande.createurId,
region: () => user.codeRegion === demande.codeRegion,
national: () => true,
});
if (!isAllowed) throw Boom.forbidden();
await deps.deleteDemandeQuery(demande.id);
};

export const deleteDemande = deleteDemandeFactory();
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const [submitDemande, submitDemandeFactory] = inject(
if (!dataEtablissement) throw Boom.badRequest("Code uai non valide");
if (!dataEtablissement.codeRegion) throw Boom.badData();

const scope = getPermissionScope(user.role, "intentions/envoi");
const scope = getPermissionScope(user.role, "intentions/ecriture");
const isAllowed = guardScope(scope?.default, {
user: () =>
user.codeRegion === dataEtablissement.codeRegion &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const [submitDraftDemande] = inject(
if (!dataEtablissement) throw Boom.badRequest("Code uai non valide");
if (!dataEtablissement.codeRegion) throw Boom.badData();

const scope = getPermissionScope(user.role, "intentions/envoi");
const scope = getPermissionScope(user.role, "intentions/ecriture");
const isAllowed = guardScope(scope?.default, {
user: () =>
user.codeRegion === dataEtablissement.codeRegion &&
Expand Down
5 changes: 5 additions & 0 deletions shared/client/intentions/intentions.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export const createIntentionsClient = (instance: AxiosInstance) => ({
url: "/demande/draft",
instance,
}),
deleteDemande: createClientMethod<typeof ROUTES_CONFIG.deleteDemande>({
method: "DELETE",
url: ({ params: { id } }) => `/demande/${id}`,
instance,
}),
getDemande: createClientMethod<typeof ROUTES_CONFIG.getDemande>({
method: "GET",
url: ({ params: { id } }) => `/demande/${id}`,
Expand Down
6 changes: 6 additions & 0 deletions shared/client/intentions/intentions.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ export const intentionsSchemas = {
}),
},
},
deleteDemande: {
params: Type.Object({ id: Type.String() }),
response: {
200: Type.Void(),
},
},
getDemandes: {
querystring: Type.Intersect([
FiltersSchema,
Expand Down
9 changes: 3 additions & 6 deletions shared/security/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export const PERMISSIONS = {
admin: {
"pilotage_reforme/lecture": { default: "national" },
"intentions/lecture": { default: "national", draft: "national" },
"intentions/suppression": { default: "national" },
"intentions/envoi": { default: "national" },
"intentions/ecriture": { default: "national" },
},
pilote: {
// "intentions/lecture": { default: "national", draft: "national" },
Expand All @@ -23,13 +22,11 @@ export const PERMISSIONS = {
},
expert_region: {
"intentions/lecture": { default: "region", draft: "region" },
"intentions/suppression": { default: "region" },
"intentions/envoi": { default: "region" },
"intentions/ecriture": { default: "region" },
},
gestionnaire_region: {
"intentions/lecture": { draft: "user", default: "region" },
"intentions/suppression": { default: "user" },
"intentions/envoi": { default: "user" },
"intentions/ecriture": { default: "user" },
},
} satisfies {
[R: string]: {
Expand Down
2 changes: 1 addition & 1 deletion ui/app/(wrapped)/intentions/[intentionId]/page.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default ({
<>
{data && (
<IntentionForm
canEdit={data.canEdit}
disabled={!data.canEdit}
formId={intentionId}
defaultValues={data}
formMetadata={data.metadata}
Expand Down
65 changes: 65 additions & 0 deletions ui/app/(wrapped)/intentions/components/ConfirmationDelete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
AlertDialog,
AlertDialogBody,
AlertDialogContent,
AlertDialogFooter,
AlertDialogOverlay,
Button,
Heading,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { FC, MouseEventHandler, useRef } from "react";

import { ArrowIcon } from "@/components/icons/arrowIcon";

export const ConfirmationDelete = ({
Trigger,
onConfirm,
}: {
Trigger: FC<{ onClick: MouseEventHandler<HTMLButtonElement> }>;
onConfirm: () => Promise<void>;
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const cancelRef = useRef(null);

return (
<>
<Trigger onClick={onOpen} />
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={onClose}
size="xl"
isCentered
>
<AlertDialogOverlay>
<AlertDialogContent borderRadius={0}>
<AlertDialogBody>
<Heading fontSize="3xl" mt="6">
<ArrowIcon mr="2" mb="1" />
Confirmation de suppression
</Heading>
<Text mt="4">
Êtes-vous sûr de vouloir supprimer la demande ?
</Text>
<Text mt="4">
Cette action est irréversible, vous perdrez l’ensemble des
données associées à votre demande.
</Text>
</AlertDialogBody>

<AlertDialogFooter>
<Button variant="secondary" ref={cancelRef} onClick={onClose}>
Annuler
</Button>
<Button variant="primary" onClick={onConfirm} ml={3}>
Confirmer la suppression
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
</>
);
};
Loading

0 comments on commit 420dadb

Please sign in to comment.