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 */} -
-
- -
- -
- -
-
+ {/* Filter component */}
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)':