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
1 change: 1 addition & 0 deletions backend/src/middlewares/multer.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum MIMEType {
const acceptedMIMETypesByItem: Record<string, Record<string, MIMEType[]>> = {
foodmenu: {
menu: [MIMEType.PDF],
allergens: [MIMEType.PDF],
},
news: {},
notebooks: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Admin/adminExportImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const AdminImportFoodMenu = () => {
</CardHeader>
<CardContent>
<AdminFileImport category="foodmenu" item="menu" title="Menus des repas" />
<AdminFileImport category="foodmenu" item="allergens" title="Allergènes" />
</CardContent>
</Card>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Plannings/planningSection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';

import { checkUploadAvailability } from '../../utils/utils';
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';

type Planning = {
Expand Down Expand Up @@ -42,8 +43,7 @@ export const PlanningSection = () => {
const availability: Record<string, boolean> = {};
for (const planning of plannings) {
try {
const response = await fetch(planning.url, { method: 'HEAD' });
availability[planning.name] = response.ok;
availability[planning.name] = await checkUploadAvailability(planning.url);
} catch {
availability[planning.name] = false;
}
Expand Down
90 changes: 47 additions & 43 deletions frontend/src/components/WEI_SDI_Food/foodSection.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { useEffect, useState } from "react";
import { useEffect, useState } from 'react';

import { checkFoodStatus } from "../../services/requests/event.service";
import { getPermission } from "../../services/requests/user.service";
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
import { checkFoodStatus } from '../../services/requests/event.service';
import { getPermission } from '../../services/requests/user.service';
import { checkUploadAvailability } from '../../utils/utils';
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card';

export const FoodSection = () => {
const [isFoodOpen, setIsFoodOpen] = useState(false);
const [isMenuAvailable, setIsMenuAvailable] = useState(false);
const [isAllergensAvailable, setIsAllergensAvailable] = useState(false);

const permission = getPermission();
const menuUrl = `${import.meta.env.VITE_API_URL}/uploads/foodmenu/menu.pdf`;
const allergensUrl = `${import.meta.env.VITE_API_URL}/uploads/foodmenu/allergens.pdf`;

useEffect(() => {
const script = document.createElement("script");
script.src = "https://www.billetweb.fr/js/export.js";
const script = document.createElement('script');
script.src = 'https://www.billetweb.fr/js/export.js';
script.async = true;
document.body.appendChild(script);

Expand All @@ -22,25 +25,14 @@ export const FoodSection = () => {
const status = await checkFoodStatus();
setIsFoodOpen(status);
} catch {
alert("Erreur lors de la récupération du statut de la nourriture.");
}
};

const checkMenuAvailability = async () => {
try {
const response = await fetch(menuUrl, { method: "HEAD" });
if (response.ok) {
setIsMenuAvailable(true);
}
} catch {
// Ne rien faire si le fichier n'est pas disponible
alert('Erreur lors de la récupération du statut de la nourriture.');
}
};

void fetchStatus();
void checkMenuAvailability();
}, [menuUrl]);

checkUploadAvailability(menuUrl, () => setIsMenuAvailable(true));
checkUploadAvailability(allergensUrl, () => setIsAllergensAvailable(true));
}, [menuUrl, allergensUrl]);

return (
<Card className="w-full max-w-6xl mx-auto bg-gradient-to-br from-blue-100 to-purple-200">
Expand All @@ -58,9 +50,7 @@ export const FoodSection = () => {
<p className="text-xl text-red-600 font-semibold">
🚫 Les menus de la semaine ne sont pas encore disponibles.
</p>
<p className="text-gray-600 mt-2">
Reste connecté, ils seront bientôt publiés !
</p>
<p className="text-gray-600 mt-2">Reste connecté, ils seront bientôt publiés !</p>
</div>
) : (
<Card className="w-full max-w-6xl mx-auto">
Expand All @@ -70,19 +60,34 @@ export const FoodSection = () => {
</CardTitle>
</CardHeader>
<CardContent className="space-y-10">
<iframe
src={menuUrl}
title="Menu PDF"
className="w-full h-[600px] border rounded-lg"
/>
<a
href={menuUrl}
download
className="inline-block px-6 py-2 bg-blue-600 text-white font-medium rounded-xl hover:bg-blue-700 transition"
target="blank"
>
Télécharger le menu
</a>
<iframe src={menuUrl} title="Menu PDF" className="w-full h-[600px] border rounded-lg" />
<div className="flex flex-col md:flex-row gap-4 justify-center items-center">
<a
href={menuUrl}
download
className="inline-block px-6 py-2 bg-blue-600 text-white font-medium rounded-xl hover:bg-blue-700 transition"
target="blank">
Télécharger le menu
</a>
{isAllergensAvailable ? (
<a
href={allergensUrl}
download
className="inline-block px-6 py-2 text-dark outline outline-2 font-medium rounded-xl hover:bg-gray-100 transition"
target="blank">
Consulter les allergènes / See the allergens
</a>
) : (
<>
<br />
<p className="text-l text-red-600 font-semibold text-center">
La liste des allergènes sera bientôt disponible.
<br />
The allergens list will be available soon.
</p>
</>
)}
</div>
</CardContent>
</Card>
)}
Expand All @@ -91,9 +96,7 @@ export const FoodSection = () => {
<p className="text-xl text-red-600 font-semibold">
🚫 La billetterie des repas n'est pas encore disponible.
</p>
<p className="text-gray-600 mt-2">
Reste connecté, elle ouvrira bientôt !
</p>
<p className="text-gray-600 mt-2">Reste connecté, elle ouvrira bientôt !</p>
</div>
) : (
<>
Expand All @@ -104,10 +107,11 @@ export const FoodSection = () => {
className="w-full h-[600px] border-none"
/>
</div>
{(permission === "Student" || permission === "Admin") && (
{(permission === 'Student' || permission === 'Admin') && (
<div className="surface-card overflow-hidden">
<span className="items-center rounded-md bg-red-400/10 px-2 py-1 text-s font-medium text-red-400 inset-ring inset-ring-red-400/20 mx-auto mt-4 mb-4 block w-max">
La billetterie du repas test est réservée aux Chefs d'Equipe et Organisateurs de l'Intégration 2025.
La billetterie du repas test est réservée aux Chefs d'Equipe et Organisateurs de
l'Intégration 2025.
</span>
<iframe
title="Billetterie Repas Test"
Expand All @@ -119,6 +123,6 @@ export const FoodSection = () => {
</>
)}
</CardContent>
</Card >
</Card>
);
};
19 changes: 17 additions & 2 deletions frontend/src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { useEffect, useState } from 'react';

import { isTokenValid } from "../services/requests/auth.service";
import { isTokenValid } from '../services/requests/auth.service';

export const useAuth = () => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
Expand All @@ -23,3 +23,18 @@ export const useAuth = () => {
}, []);
return isAuthenticated;
};

export const checkUploadAvailability = async (url: string, callback?: () => void): Promise<boolean> => {
try {
const response = await fetch(url, { method: 'HEAD' });

if (response.ok) {
callback?.();
return true;
}

return false;
} catch {
return false;
}
};
Loading