diff --git a/app/components/home/index.tsx b/app/components/home/index.tsx
index 69dd8c1..1e4a23f 100644
--- a/app/components/home/index.tsx
+++ b/app/components/home/index.tsx
@@ -27,6 +27,7 @@ import TabContent from "~/components/home/tab-content";
import { MainLayout } from "~/layout";
import { forceReload } from "~/lib/utils";
import { SyncButton } from "../sync-status";
+import WeekNavigation from "./week-navigation";
export default function Home({ user }: { user: User }) {
// Handle query parameters
@@ -54,16 +55,6 @@ export default function Home({ user }: { user: User }) {
const setStartDate = (date: DateTime) =>
updateQuery("start", date.startOf("week").toISODate()!);
- const handlePreviousWeek = () => {
- const previousWeek = startDate.minus({ weeks: 1 });
- setStartDate(previousWeek);
- };
-
- const handleNextWeek = () => {
- const nextWeek = startDate.plus({ weeks: 1 });
- setStartDate(nextWeek);
- };
-
// Fetch colles from API
const {
studentColles,
@@ -188,22 +179,7 @@ export default function Home({ user }: { user: User }) {
{/* Week Navigation */}
-
diff --git a/app/components/home/week-navigation.tsx b/app/components/home/week-navigation.tsx
new file mode 100644
index 0000000..a3477c9
--- /dev/null
+++ b/app/components/home/week-navigation.tsx
@@ -0,0 +1,41 @@
+import { ChevronLeft, ChevronRight } from "lucide-react";
+import { Button } from "../ui/button";
+import DatePickerWithRange from "./date-picker";
+import type { DateTime } from "luxon";
+
+export default function WeekNavigation({
+ startDate,
+ setStartDate,
+}: {
+ startDate: DateTime;
+ setStartDate: (date: DateTime) => void;
+}) {
+ const handlePreviousWeek = () => {
+ const previousWeek = startDate.minus({ weeks: 1 });
+ setStartDate(previousWeek);
+ };
+
+ const handleNextWeek = () => {
+ const nextWeek = startDate.plus({ weeks: 1 });
+ setStartDate(nextWeek);
+ };
+
+ return (
+
+ );
+}
diff --git a/app/components/repas/date-picker.tsx b/app/components/repas/date-picker.tsx
new file mode 100644
index 0000000..3954e46
--- /dev/null
+++ b/app/components/repas/date-picker.tsx
@@ -0,0 +1,67 @@
+import { DateTime } from "luxon";
+import { CalendarIcon } from "lucide-react";
+import { fr } from "date-fns/locale";
+import { Calendar } from "~/components/ui/calendar";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "~/components/ui/popover";
+import { cn } from "~/lib/utils";
+
+export default function DatePickerWithRange({
+ className,
+ startDate,
+ setStartDate,
+}: {
+ className?: string;
+ startDate: DateTime;
+ setStartDate: (date: DateTime) => void;
+}) {
+ function handleDateSelect(selectedDate?: Date) {
+ if (selectedDate) {
+ setStartDate(DateTime.fromJSDate(selectedDate));
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const formatDate = (date: DateTime, includeYear = false) => {
+ const localDate = date.setLocale("fr");
+ return includeYear
+ ? localDate.toFormat("dd MMM yyyy")
+ : localDate.toFormat("dd MMM");
+};
diff --git a/app/components/repas/day-navigation.tsx b/app/components/repas/day-navigation.tsx
new file mode 100644
index 0000000..5e83909
--- /dev/null
+++ b/app/components/repas/day-navigation.tsx
@@ -0,0 +1,41 @@
+import { ChevronLeft, ChevronRight } from "lucide-react";
+import { Button } from "../ui/button";
+import type { DateTime } from "luxon";
+import DatePickerWithRange from "./date-picker";
+
+export default function DayNavigation({
+ startDate,
+ setStartDate,
+}: {
+ startDate: DateTime;
+ setStartDate: (date: DateTime) => void;
+}) {
+ const handlePreviousDay = () => {
+ const previousDay = startDate.minus({ days: 1 });
+ setStartDate(previousDay);
+ };
+
+ const handleNextDay = () => {
+ const nextDay = startDate.plus({ days: 1 });
+ setStartDate(nextDay);
+ };
+
+ return (
+
+ );
+}
diff --git a/app/components/repas/index.tsx b/app/components/repas/index.tsx
new file mode 100644
index 0000000..a559c35
--- /dev/null
+++ b/app/components/repas/index.tsx
@@ -0,0 +1,67 @@
+import { useState } from "react";
+import { Tabs, TabsList, tabsStyle, TabsTrigger } from "~/components/ui/tabs";
+import BottomNavigation from "~/components/bottom-nav";
+import { CalendarCheck, ChefHat } from "lucide-react";
+import WIP from "./wip";
+import Menus from "./menus";
+
+const tabs = [
+ {
+ value: "menu",
+ label: "Menus",
+ icon:
,
+ content:
,
+ },
+ {
+ value: "bjrepas",
+ label: "BJ Repas",
+ icon:
,
+ content:
,
+ },
+];
+
+export default function RepasPage() {
+ // user / notifications / preferences tabs
+ const [activeTab, setActiveTab] = useState(tabs[0].value);
+
+ return (
+
+ {/* Tabs */}
+
+
+ {tabs.map((tab) => (
+
+ {tab.icon}
+ {tab.label}
+
+ ))}
+
+
+
+ {/* Tab Content */}
+
+ {tabs.map((tab) => (
+
+ {tab.content}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/app/components/repas/menu-card.tsx b/app/components/repas/menu-card.tsx
new file mode 100644
index 0000000..ab96bac
--- /dev/null
+++ b/app/components/repas/menu-card.tsx
@@ -0,0 +1,81 @@
+import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
+import { Utensils, Salad, Cake, CookingPot, Icon, Carrot } from "lucide-react";
+import { cheese } from "@lucide/lab";
+
+interface Course {
+ name: string;
+ description: string;
+}
+
+interface Meal {
+ name: string;
+ courses: Course[];
+}
+
+const getCourseIcon = (courseName: string) => {
+ const name = courseName.toLowerCase();
+ if (name.includes("hors")) return
;
+ if (name.includes("plat")) return
;
+ if (name.includes("garniture")) return
;
+ if (name.includes("fromage"))
+ return
;
+ if (name.includes("dessert")) return
;
+ return
;
+};
+
+export function DailyMenu({ meals }: { meals: Meal[] }) {
+ if (meals.length === 0) {
+ return (
+
+
+ Aucun menu disponible pour ce jour.
+
+
+ );
+ }
+
+ return (
+
+
+ {meals.length > 1 ? "Menus" : "Menu"} du jour
+
+
+ {meals.map((meal, mealIndex) => (
+
+
+
+
+
+
+
+ {meal.name}
+
+
+
+
+
+
+
+
+ {meal.courses.map((course, courseIndex) => (
+
+
+ {getCourseIcon(course.name)}
+
+
+
+ {course.name}
+
+
+ {course.description}
+
+
+
+ ))}
+
+
+
+ ))}
+
+ );
+}
diff --git a/app/components/repas/menus.tsx b/app/components/repas/menus.tsx
new file mode 100644
index 0000000..03330c1
--- /dev/null
+++ b/app/components/repas/menus.tsx
@@ -0,0 +1,31 @@
+import { useMenus } from "~/lib/api";
+import { DailyMenu } from "./menu-card";
+import { DateTime } from "luxon";
+import { useState } from "react";
+import DayNavigation from "./day-navigation";
+
+export default function Menus() {
+ const { isLoading, error, menus } = useMenus();
+ const [date, setDate] = useState(DateTime.now().startOf("day"));
+
+ const findMenuForDate = (date: DateTime) => {
+ return menus?.find((menu: any) => {
+ const menuDate = DateTime.fromISO(menu.date).startOf("day");
+ return menuDate.equals(date);
+ });
+ };
+
+ return (
+
+ {isLoading &&
Chargement des menus...
}
+ {error && (
+
Erreur lors du chargement des menus.
+ )}
+ {menus && menus.length === 0 &&
Aucun menu disponible.
}
+
+ {/* Menus */}
+
+
+
+ );
+}
diff --git a/app/components/repas/wip.tsx b/app/components/repas/wip.tsx
new file mode 100644
index 0000000..57ee55f
--- /dev/null
+++ b/app/components/repas/wip.tsx
@@ -0,0 +1,23 @@
+import { Github } from "lucide-react";
+import { Button } from "../ui/button";
+
+export default function WIP() {
+ return (
+
+
+ ⚠️ Cette fonctionnalité n’est pas encore implémentée.
+
+
+ Vous pouvez contribuer au développement du projet ici :
+
+
+
+ );
+}
diff --git a/app/lib/api.ts b/app/lib/api.ts
index 5c15d64..8b87684 100644
--- a/app/lib/api.ts
+++ b/app/lib/api.ts
@@ -457,3 +457,26 @@ export const useAverages = (period: string) => {
...props,
};
};
+
+/**
+ * === REPAS API ===
+ */
+const fetchMenus = async () => {
+ return makeRequest(`/menus`, "Échec de la récupération des menus");
+};
+export const useMenus = () => {
+ const { data, ...props } = useQuery({
+ queryKey: ["menus"],
+ queryFn: fetchMenus,
+ staleTime: Duration.fromObject({
+ hours: 6, // 6 hours
+ }).toMillis(),
+ gcTime: Duration.fromObject({
+ days: 1, // 1 day
+ }).toMillis(),
+ });
+ return {
+ menus: data || [],
+ ...props,
+ };
+};
diff --git a/app/routes/repas.tsx b/app/routes/repas.tsx
index db140d4..48e0be3 100644
--- a/app/routes/repas.tsx
+++ b/app/routes/repas.tsx
@@ -4,9 +4,7 @@ import Loader from "~/components/loader";
import { MainLayout } from "~/layout";
import { AUTH_ERROR, useUser } from "~/lib/api";
import { forceReload } from "~/lib/utils";
-import { Button } from "~/components/ui/button";
-import { Github } from "lucide-react";
-import BottomNavigation from "~/components/bottom-nav";
+import RepasPage from "~/components/repas";
export default function Repas() {
const { user, isLoading, error } = useUser();
@@ -29,25 +27,7 @@ export default function Repas() {
}
>
- {/* Under construction message */}
-
-
- ⚠️ Cette fonctionnalité n’est pas encore implémentée.
-
-
- Vous pouvez contribuer au développement du projet ici :
-
-
-
-
-
+
);
}
diff --git a/package.json b/package.json
index 1dbc142..1a5c65f 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"deploy": "netlify deploy --prod --dir=./build/client --message=\"Deploy to Netlify from Forgejo\" --site=$NETLIFY_SITE_ID --auth=$NETLIFY_AUTH_TOKEN --no-build"
},
"dependencies": {
+ "@lucide/lab": "^0.1.2",
"@marsidev/react-turnstile": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-avatar": "^1.1.10",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8fdac74..99a9daf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@lucide/lab':
+ specifier: ^0.1.2
+ version: 0.1.2
'@marsidev/react-turnstile':
specifier: ^1.1.0
version: 1.1.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -1255,6 +1258,9 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+ '@lucide/lab@0.1.2':
+ resolution: {integrity: sha512-VprF2BJa7ZuTGOhUd5cf8tHJXyL63wdxcGieAiVVoR9hO0YmPsnZO0AGqDiX2/br+/MC6n8BoJcmPilltOXIJA==}
+
'@lukeed/ms@2.0.2':
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
engines: {node: '>=8'}
@@ -7804,6 +7810,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.4
+ '@lucide/lab@0.1.2': {}
+
'@lukeed/ms@2.0.2': {}
'@mapbox/node-pre-gyp@2.0.0(supports-color@10.0.0)':