Skip to content
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
41 changes: 41 additions & 0 deletions backend/src/controllers/permanence.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,44 @@ export const sendDailyNotificationToUsers: AppRequestHandler = async (_req, res)

Ok(res, { msg: 'Notifications quotidiennes envoyées avec succès' });
};

export const sendConcurrentPermanenceNotifications: AppRequestHandler = async (_req, res) => {
try {
const notifications = await permanence_service.getConcurrentPermanenceNotifications();
await permanence_service.sendConcurrentPermanenceNotifications(notifications);
Ok(res, { msg: `${notifications.length} notification(s) de conflit envoyée(s).` });
} catch (err) {
console.error(err);
Error(res, { msg: 'Erreur lors de l’envoi des notifications de permanences concurrentes' });
}
};

export const purgeConcurrentPermanences: AppRequestHandler = async (_req, res) => {
try {
const result = await permanence_service.purgeConcurrentPermanences();
Ok(res, {
data: result,
msg: `${result.removedRegistrations} inscription(s) supprimée(s) pour ${result.affectedUsers} utilisateur(s).`,
});
} catch (err) {
console.error(err);
Error(res, { msg: 'Erreur lors de la purge des permanences concurrentes' });
}
};

export const getConcurrentPermanences: AppRequestHandler = async (req, res) => {
const userId = req.user?.userId;

if (!userId) {
Error(res, { msg: 'Utilisateur non identifié' });
return;
}

try {
const status = await permanence_service.getConcurrentPermanencesStatus(Number(userId));
Ok(res, { data: status });
} catch (err) {
console.error(err);
Error(res, { msg: 'Erreur lors de la vérification des permanences concurrentes' });
}
};
12 changes: 11 additions & 1 deletion backend/src/email/email.registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { PermanenceEmailData, TemplateRenderer, TeamAssignmentEmailData } from '../types/email';
import type {
ConcurrentPermanencesEmailData,
PermanenceEmailData,
TemplateRenderer,
TeamAssignmentEmailData,
} from '../types/email';

export const templateResetPassword = 'reset-password.html';
const templateNotebook = 'notebook.html';
Expand All @@ -7,6 +12,7 @@ const templateWelcome = 'welcome.html';
const templateNotifyNews = 'notify-news.html';
const templateNotifyTentConfirmation = 'notify-tent-confirmation.html';
const templateNotifyPermanenceReminder = 'notify-permanence-reminder.html';
const templateNotifyConcurrentPermanences = 'notify-concurrent-permanences.html';
const templateNotifyTeamAssignment = 'notify-team-assignment.html';
const templateMentorReminder = 'mentor-reminder.html';

Expand Down Expand Up @@ -80,6 +86,10 @@ export const templateRenderers: Record<string, TemplateRenderer> = {
return typedData;
},
},
templateNotifyConcurrentPermanences: {
fileName: templateNotifyConcurrentPermanences,
buildData: (data) => data as ConcurrentPermanencesEmailData,
},
templateMentorReminder: {
fileName: templateMentorReminder,
buildData: () => {
Expand Down
116 changes: 116 additions & 0 deletions backend/src/email/templates/notify-concurrent-permanences.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Intégration UTT</title>
</head>
<body
style="
font-family: Arial, sans-serif;
font-size: 16px;
margin: 0;
padding: 0;
background-color: #ffffff;
text-align: center;
color: #222;
">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse">
<tr>
<td align="center" style="padding: 24px">
<table
width="600"
cellpadding="0"
cellspacing="0"
border="0"
style="
max-width: 100%;
margin: 0 auto;
background-color: #f8f8f8;
border-radius: 8px;
overflow: hidden;
">
<tr>
<td align="center" style="padding: 10px">
<img
src="https://integration.utt.fr/img/logo_original.png"
alt="Logo Intégration UTT"
style="width: 104px; height: auto" />
</td>
</tr>
<tr>
<td
style="
font-size: 21px;
font-weight: bold;
line-height: 1.4;
padding: 10px 0;
text-align: center;
">
INTEGRATION UTT
</td>
</tr>
<tr>
<td style="padding: 24px; text-align: left; line-height: 1.6">
<h2 style="font-size: 24px; margin: 0 0 16px; text-align: center">
Permanences concurrentes
</h2>
<p>
Attention, certaines de vos permanences se deroulent sur des creneaux qui se
chevauchent :
</p>
{{#each permanences}}
<div
style="
border-left: 4px solid #e6a700;
padding: 8px 12px;
margin: 12px 0;
background-color: #fff8df;
">
<p style="margin: 0 0 6px"><strong>{{name}}</strong></p>
<p style="margin: 0">Du {{startAt}} au {{endAt}}</p>
<p style="margin: 0">Lieu : {{location}}</p>
</div>
{{/each}}
<p>
Rendez-vous sur la page &laquo; Mes permanences &raquo; pour verifier vos
inscriptions.
</p>
<p>
Après un shotgun, vous avez <strong>jusqu'au lendemain midi</strong> pour supprimer
les doublons, sinon des permanences seront
<strong>retirées de manière aléatoire</strong> pour éliminer les conflits, et les
permanences libérées seront à nouveau shotgun le lendemain.
</p>
</td>
</tr>
<tr>
<td style="padding: 0 24px 24px">
<h3 style="text-align: center">
Retrouve des informations utiles sur notre Instagram !
</h3>
<div style="margin: auto; width: fit-content">
<a href="https://www.instagram.com/bdeutt" target="_blank">
<img
src="https://cdn.tools.unlayer.com/social/icons/rounded/instagram.png"
alt="Instagram"
style="width: 30px; height: 30px; margin: 5px" />
</a>
</div>
<p>Cordialement,<br />L'équipe intégration UTT</p>
<p style="font-size: 12px; color: #999999; margin: 0; text-align: center">
Si vous avez des questions, n'hésitez pas à
<a
href="mailto:integration+site-support@utt.fr"
style="color: #e74160; text-decoration: none"
>nous contacter</a
>.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
5 changes: 5 additions & 0 deletions backend/src/routes/automation.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ const automationRoutes = express.Router();
// Permanences routes
automationRoutes.post('/permanence/notification/hourly', permanenceController.sendHourlyNotificationToUsers);
automationRoutes.post('/permanence/notification/daily', permanenceController.sendDailyNotificationToUsers);
automationRoutes.post(
'/permanence/concurrent/notification',
permanenceController.sendConcurrentPermanenceNotifications,
);
automationRoutes.post('/permanence/concurrent/purge', permanenceController.purgeConcurrentPermanences);

export default automationRoutes;
62 changes: 38 additions & 24 deletions backend/src/routes/permanences.routes.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
import express from "express";
import multer from "multer";
import * as permanenceController from "../controllers/permanence.controller";
import { isRespoMiddleware } from "../middlewares/respoperm.middleware";
import { checkRole } from "../middlewares/user.middleware";
import express from 'express';
import multer from 'multer';
import * as permanenceController from '../controllers/permanence.controller';
import { isRespoMiddleware } from '../middlewares/respoperm.middleware';
import { checkRole } from '../middlewares/user.middleware';

const permanenceRouter = express.Router();
const upload = multer({ dest: "uploads/permcsv/" });
const upload = multer({ dest: 'uploads/permcsv/' });

// Admin routes
permanenceRouter.post("/admin/permanence", checkRole("Admin", ["Respo CE"]), permanenceController.createPermanence);
permanenceRouter.delete("/admin/permanence", checkRole("Admin", ["Respo CE"]), permanenceController.deletePermanence);
permanenceRouter.post("/admin/updatepermanence", checkRole("Admin", ["Respo CE"]), permanenceController.updatePermanence);
permanenceRouter.post("/admin/open", checkRole("Admin", ["Respo CE"]), permanenceController.openPermanence);
permanenceRouter.post("/admin/close", checkRole("Admin", ["Respo CE"]), permanenceController.closePermanence);
permanenceRouter.get("/admin/permanences", checkRole("Admin", ["Respo CE"]), permanenceController.getAllPermanences);
permanenceRouter.get("/admin/users", checkRole("Admin", ["Respo CE"]), permanenceController.getUsersInPermanence);
permanenceRouter.post("/admin/add", checkRole("Admin", ["Respo CE"]), permanenceController.addUserToPermanence);
permanenceRouter.post("/admin/remove", checkRole("Admin", ["Respo CE"]), permanenceController.removeUserToPermanence);
permanenceRouter.post("/admin/importpermanences", checkRole("Admin", ["Respo CE"]), upload.single("file"), permanenceController.uploadPermanencesCSV);
permanenceRouter.post("/admin/claimedmember", checkRole("Admin", ["Respo CE"]), permanenceController.claimMember);
permanenceRouter.post('/admin/permanence', checkRole('Admin', ['Respo CE']), permanenceController.createPermanence);
permanenceRouter.delete('/admin/permanence', checkRole('Admin', ['Respo CE']), permanenceController.deletePermanence);
permanenceRouter.post(
'/admin/updatepermanence',
checkRole('Admin', ['Respo CE']),
permanenceController.updatePermanence,
);
permanenceRouter.post('/admin/open', checkRole('Admin', ['Respo CE']), permanenceController.openPermanence);
permanenceRouter.post('/admin/close', checkRole('Admin', ['Respo CE']), permanenceController.closePermanence);
permanenceRouter.get('/admin/permanences', checkRole('Admin', ['Respo CE']), permanenceController.getAllPermanences);
permanenceRouter.get('/admin/users', checkRole('Admin', ['Respo CE']), permanenceController.getUsersInPermanence);
permanenceRouter.post('/admin/add', checkRole('Admin', ['Respo CE']), permanenceController.addUserToPermanence);
permanenceRouter.post('/admin/remove', checkRole('Admin', ['Respo CE']), permanenceController.removeUserToPermanence);
permanenceRouter.post(
'/admin/importpermanences',
checkRole('Admin', ['Respo CE']),
upload.single('file'),
permanenceController.uploadPermanencesCSV,
);
permanenceRouter.post('/admin/claimedmember', checkRole('Admin', ['Respo CE']), permanenceController.claimMember);

//Respo de perm routes
permanenceRouter.get("/respo/respodetails", isRespoMiddleware, permanenceController.getRespoPermanencesWithMembers);
permanenceRouter.post("/respo/claimedmember", isRespoMiddleware, permanenceController.claimMember);
permanenceRouter.get('/respo/respodetails', isRespoMiddleware, permanenceController.getRespoPermanencesWithMembers);
permanenceRouter.post('/respo/claimedmember', isRespoMiddleware, permanenceController.claimMember);

// Student routes
permanenceRouter.get("/user/permanences", checkRole("Student", []), permanenceController.getOpenPermanences);
permanenceRouter.post("/user/apply", checkRole("Student", []), permanenceController.applyToPermanence);
permanenceRouter.post("/user/leave", checkRole("Student", []), permanenceController.leavePermanence);
permanenceRouter.get("/user/me", checkRole("Student", []), permanenceController.getMyPermanences);
permanenceRouter.get("/user/isrespo", permanenceController.isUserRespo);
permanenceRouter.get('/user/permanences', checkRole('Student', []), permanenceController.getOpenPermanences);
permanenceRouter.get(
'/user/concurrent/status',
checkRole('Student', []),
permanenceController.getConcurrentPermanences,
);
permanenceRouter.post('/user/apply', checkRole('Student', []), permanenceController.applyToPermanence);
permanenceRouter.post('/user/leave', checkRole('Student', []), permanenceController.leavePermanence);
permanenceRouter.get('/user/me', checkRole('Student', []), permanenceController.getMyPermanences);
permanenceRouter.get('/user/isrespo', permanenceController.isUserRespo);
export default permanenceRouter;
Loading
Loading