Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppression demande #105

Merged
merged 7 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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