Checkpoint: Implémentation des fonctionnalités de communication : formulaire de contact avec choix email/canal, canaux de discussion intégrés (page Mes Échanges), demandes de mise en relation avec gestion admin des canaux, navigation mise à jour dans DashboardLayout, bouton Demande de mise en relation sur fiche établissement. Migration DB : tables canaux_discussion, membres_canal, messages_canal, demandes_mise_en_relation.
This commit is contained in:
@@ -43,6 +43,19 @@ import {
|
||||
setReferentForEtablissement,
|
||||
setAdherentsForEtablissement,
|
||||
updateLocalPassword,
|
||||
createCanal,
|
||||
getCanauxForUser,
|
||||
getMessagesCanal,
|
||||
sendMessageCanal,
|
||||
isMemberOfCanal,
|
||||
getCanalById,
|
||||
getMembresCanal,
|
||||
addMembresCanal,
|
||||
createDemandeMiseEnRelation,
|
||||
getAllDemandesMiseEnRelation,
|
||||
getDemandesMiseEnRelationByUser,
|
||||
traiterDemandeMiseEnRelation,
|
||||
getDemandeMiseEnRelationById,
|
||||
updateUser,
|
||||
updateUserCgu,
|
||||
updateUserSonumRole,
|
||||
@@ -76,10 +89,88 @@ const writeProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
return next({ ctx });
|
||||
});
|
||||
|
||||
// ─── Router principal ─────────────────────────────────────────────────────────
|
||||
/// ─── Canaux de Discussion ────────────────────────────────────────────────────
|
||||
const canauxRouter = router({
|
||||
list: protectedProcedure.query(async ({ ctx }) => {
|
||||
const isGestionnaire = ctx.user.sonumRole === "gestionnaire" || ctx.user.role === "admin";
|
||||
return getCanauxForUser(ctx.user.id, isGestionnaire);
|
||||
}),
|
||||
messages: protectedProcedure
|
||||
.input(z.object({ canalId: z.number().int() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const isGestionnaire = ctx.user.sonumRole === "gestionnaire" || ctx.user.role === "admin";
|
||||
if (!isGestionnaire) {
|
||||
const isMember = await isMemberOfCanal(input.canalId, ctx.user.id);
|
||||
if (!isMember) throw new TRPCError({ code: "FORBIDDEN" });
|
||||
}
|
||||
return getMessagesCanal(input.canalId);
|
||||
}),
|
||||
membres: protectedProcedure
|
||||
.input(z.object({ canalId: z.number().int() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const isGestionnaire = ctx.user.sonumRole === "gestionnaire" || ctx.user.role === "admin";
|
||||
if (!isGestionnaire) {
|
||||
const isMember = await isMemberOfCanal(input.canalId, ctx.user.id);
|
||||
if (!isMember) throw new TRPCError({ code: "FORBIDDEN" });
|
||||
}
|
||||
return getMembresCanal(input.canalId);
|
||||
}),
|
||||
sendMessage: writeProcedure
|
||||
.input(z.object({ canalId: z.number().int(), contenu: z.string().min(1).max(5000) }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const isGestionnaire = ctx.user.sonumRole === "gestionnaire" || ctx.user.role === "admin";
|
||||
if (!isGestionnaire) {
|
||||
const isMember = await isMemberOfCanal(input.canalId, ctx.user.id);
|
||||
if (!isMember) throw new TRPCError({ code: "FORBIDDEN" });
|
||||
}
|
||||
await sendMessageCanal({ canalId: input.canalId, auteurId: ctx.user.id, auteurNom: ctx.user.name ?? "Inconnu", contenu: input.contenu });
|
||||
return { success: true };
|
||||
}),
|
||||
create: gestionnaireProcedure
|
||||
.input(z.object({
|
||||
titre: z.string().min(1).max(255),
|
||||
description: z.string().optional(),
|
||||
type: z.enum(["contact_referent", "mise_en_relation_public", "mise_en_relation_prive"]),
|
||||
visibilite: z.enum(["public", "prive"]),
|
||||
etablissementId: z.number().int().optional(),
|
||||
demandeMiseEnRelationId: z.number().int().optional(),
|
||||
membreIds: z.array(z.number().int()),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const canalId = await createCanal({ ...input, creePar: ctx.user.id });
|
||||
if (input.demandeMiseEnRelationId) {
|
||||
await traiterDemandeMiseEnRelation(input.demandeMiseEnRelationId, ctx.user.id, canalId);
|
||||
}
|
||||
return { canalId };
|
||||
}),
|
||||
addMembres: gestionnaireProcedure
|
||||
.input(z.object({ canalId: z.number().int(), userIds: z.array(z.number().int()) }))
|
||||
.mutation(async ({ input }) => { await addMembresCanal(input.canalId, input.userIds); return { success: true }; }),
|
||||
});
|
||||
|
||||
// ─── Demandes de Mise en Relation ─────────────────────────────────────────────
|
||||
const miseEnRelationRouter = router({
|
||||
soumettre: writeProcedure
|
||||
.input(z.object({ sujet: z.string().min(1).max(500), message: z.string().min(1), etablissementDemandeurId: z.number().int().optional() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const id = await createDemandeMiseEnRelation({
|
||||
demandeurId: ctx.user.id, demandeurNom: ctx.user.name ?? "Inconnu",
|
||||
demandeurEmail: ctx.user.email ?? undefined,
|
||||
etablissementDemandeurId: input.etablissementDemandeurId,
|
||||
sujet: input.sujet, message: input.message,
|
||||
});
|
||||
await notifyOwner({ title: `Nouvelle demande de mise en relation — ${input.sujet}`, content: `${ctx.user.name} (${ctx.user.email}) souhaite une mise en relation.\n\nSujet : ${input.sujet}\n\nMessage : ${input.message}` });
|
||||
return { id };
|
||||
}),
|
||||
all: gestionnaireProcedure.query(() => getAllDemandesMiseEnRelation()),
|
||||
mesDemandes: protectedProcedure.query(({ ctx }) => getDemandesMiseEnRelationByUser(ctx.user.id)),
|
||||
});
|
||||
|
||||
// ─── Router principal ─────────────────────────────────────────────────────────
|
||||
export const appRouter = router({
|
||||
system: systemRouter,
|
||||
canaux: canauxRouter,
|
||||
miseEnRelation: miseEnRelationRouter,
|
||||
|
||||
// ─── Auth ──────────────────────────────────────────────────────────────────
|
||||
auth: router({
|
||||
|
||||
Reference in New Issue
Block a user