All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m54s
268 lines
8 KiB
TypeScript
268 lines
8 KiB
TypeScript
import { DateTime } from "luxon";
|
||
import { useState } from "react";
|
||
import { Button } from "~/components/ui/button";
|
||
import {
|
||
ChevronLeft,
|
||
ChevronRight,
|
||
Star,
|
||
Users,
|
||
UserIcon,
|
||
SortAsc,
|
||
SortDesc,
|
||
} from "lucide-react";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from "~/components/ui/select";
|
||
import { Tabs, TabsList, tabsStyle, TabsTrigger } from "~/components/ui/tabs";
|
||
import DatePickerWithRange from "~/components/home/date-picker";
|
||
import BottomNavigation from "~/components/bottom-nav";
|
||
import Error from "~/components/error";
|
||
import { useSearchParams } from "react-router";
|
||
import { useColles, type User } from "~/lib/api";
|
||
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
|
||
const [query, setQuery] = useSearchParams();
|
||
const updateQuery = (key: string, value: string) => {
|
||
if (query.get(key) !== value) {
|
||
setQuery((prev) => {
|
||
const newQuery = new URLSearchParams(prev);
|
||
newQuery.set(key, value);
|
||
return newQuery;
|
||
});
|
||
}
|
||
};
|
||
|
||
// Tabs
|
||
const activeTab = query.get("view") || "you";
|
||
const setActiveTab = (tab: string) => updateQuery("view", tab);
|
||
|
||
// Date
|
||
const rawStartDate = query.get("start");
|
||
const startDate = rawStartDate
|
||
? DateTime.fromISO(rawStartDate, { zone: "local" })
|
||
: DateTime.now().startOf("week");
|
||
|
||
const setStartDate = (date: DateTime) =>
|
||
updateQuery("start", date.startOf("week").toISODate()!);
|
||
|
||
// Fetch colles from API
|
||
const {
|
||
studentColles,
|
||
classColles,
|
||
favoriteColles,
|
||
healthyUntil,
|
||
lastSync,
|
||
error,
|
||
isLoading,
|
||
} = useColles(startDate);
|
||
|
||
// Error handling (after all hooks)
|
||
if (error)
|
||
return (
|
||
<Error
|
||
title="Impossible de charger les colles"
|
||
message={error?.toString()}
|
||
code={500}
|
||
description="Une erreur s'est produite lors du chargement de la liste des colles."
|
||
/>
|
||
);
|
||
|
||
// TODO: FAVORITES
|
||
const useToggleStar = (auth: any) => {};
|
||
|
||
// Filter state
|
||
const rawSubject = query.get("subject");
|
||
const [subjectFilter, setSubjectFilter] = useState<string>(
|
||
rawSubject === "all" ? "" : rawSubject || ""
|
||
);
|
||
const setSubject = (subject: string) => {
|
||
updateQuery("subject", subject);
|
||
setSubjectFilter(subject);
|
||
};
|
||
const rawExaminer = query.get("examiner");
|
||
const [examinerFilter, setExaminerFilter] = useState<string>(
|
||
rawExaminer === "all" ? "" : rawExaminer || ""
|
||
);
|
||
const setExaminer = (examiner: string) => {
|
||
updateQuery("examiner", examiner);
|
||
setExaminerFilter(examiner);
|
||
};
|
||
const [sorted, setSort] = useState<string>(query.get("sort") || "desc");
|
||
const toggleSort = () => {
|
||
const newSort = sorted === "asc" ? "desc" : "asc";
|
||
updateQuery("sort", newSort);
|
||
setSort(newSort);
|
||
};
|
||
|
||
const generateFilter = (arr: string[], value: string) => {
|
||
const unique = [...new Set(arr)];
|
||
if (value && !unique.includes(value)) {
|
||
unique.push(value);
|
||
}
|
||
unique.sort((a, b) => a.localeCompare(b));
|
||
return unique;
|
||
};
|
||
const subjects = generateFilter(
|
||
classColles.map((colle) => colle.subject?.name),
|
||
subjectFilter
|
||
);
|
||
const examiners = generateFilter(
|
||
classColles.map((colle) => colle.examiner?.name),
|
||
examinerFilter
|
||
);
|
||
|
||
const applyFilters = (colles: any[]) => {
|
||
return colles
|
||
.filter((colle) => {
|
||
const subjectMatch =
|
||
subjectFilter === "all" || !subjectFilter
|
||
? true
|
||
: colle.subject?.name === subjectFilter;
|
||
const examinerMatch =
|
||
examinerFilter === "all" || !examinerFilter
|
||
? true
|
||
: colle.examiner?.name === examinerFilter;
|
||
return subjectMatch && examinerMatch;
|
||
})
|
||
.sort((a, b) => {
|
||
if (sorted === "asc") {
|
||
return a.date.localeCompare(b.date);
|
||
} else {
|
||
return b.date.localeCompare(a.date);
|
||
}
|
||
});
|
||
};
|
||
|
||
return (
|
||
<MainLayout
|
||
header={
|
||
<>
|
||
<h1 className="text-2xl font-bold" onClick={forceReload}>
|
||
Khollisé - {user.className} ⚔️
|
||
</h1>
|
||
<SyncButton healthyUntil={healthyUntil} lastSync={lastSync} />
|
||
</>
|
||
}
|
||
>
|
||
<div className="space-y-6 pb-20 md:pb-0">
|
||
{/* Tabs */}
|
||
<Tabs
|
||
defaultValue="all"
|
||
value={activeTab}
|
||
onValueChange={setActiveTab}
|
||
className="max-w-md w-full"
|
||
>
|
||
<TabsList className="w-full p-0 bg-background justify-start border-b rounded-none">
|
||
<TabsTrigger value="you" className={tabsStyle}>
|
||
<UserIcon className="h-4 w-4" />
|
||
Vous
|
||
</TabsTrigger>
|
||
{/* <TabsTrigger value="favorites" className={tabsStyle}>
|
||
<Star className="h-4 w-4" />
|
||
Favoris
|
||
</TabsTrigger> */}
|
||
<TabsTrigger value="class" className={tabsStyle}>
|
||
<Users className="h-4 w-4" />
|
||
Classe
|
||
</TabsTrigger>
|
||
</TabsList>
|
||
</Tabs>
|
||
|
||
{/* Week Navigation */}
|
||
<WeekNavigation startDate={startDate} setStartDate={setStartDate} />
|
||
|
||
{/* Filter component */}
|
||
<div className="flex gap-2 pb-0 pt-2">
|
||
<Select value={subjectFilter} onValueChange={setSubject}>
|
||
<SelectTrigger className="rounded-full data-[placeholder]:text-primary">
|
||
<SelectValue placeholder="Matière" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="all">Toutes</SelectItem>
|
||
{subjects.map((subject) => (
|
||
<SelectItem key={subject} value={subject}>
|
||
{subject}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
|
||
<Select value={examinerFilter} onValueChange={setExaminer}>
|
||
<SelectTrigger className="rounded-full data-[placeholder]:text-primary">
|
||
<SelectValue placeholder="Colleur" />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem value="all">Tous</SelectItem>
|
||
{examiners.map((examiner) => (
|
||
<SelectItem key={examiner} value={examiner}>
|
||
{examiner}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
|
||
<Button
|
||
variant="outline"
|
||
size="sm"
|
||
className="rounded-full dark:bg-input/30 text-primary font-normal"
|
||
onClick={toggleSort}
|
||
>
|
||
{sorted == "asc" ? (
|
||
<SortAsc className="h-5 w-5" />
|
||
) : (
|
||
<SortDesc className="h-5 w-5" />
|
||
)}
|
||
Trier
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Your Colles Tab */}
|
||
{activeTab === "you" && (
|
||
<TabContent
|
||
tabTitle="Vos colles"
|
||
emptyCollesText="Vous n'avez pas encore de colle cette semaine."
|
||
isLoading={isLoading}
|
||
isSorted={sorted === "desc"}
|
||
colles={applyFilters(studentColles)}
|
||
preferences={user.preferences}
|
||
/>
|
||
)}
|
||
|
||
{/* Favorites Tab
|
||
{activeTab === "favorites" && (
|
||
<TabContent
|
||
tabTitle="Vos favoris"
|
||
emptyCollesText="Vous n'avez pas encore de colle favorite, cliquez sur l'étoile pour ajouter une colle à vos favoris."
|
||
isLoading={isLoading}
|
||
colles={favoriteColles}
|
||
/>
|
||
)} */}
|
||
|
||
{/* Class Colles Tab */}
|
||
{activeTab === "class" && (
|
||
<TabContent
|
||
tabTitle="Les colles de la classe"
|
||
emptyCollesText="Aucune colle trouvée."
|
||
isLoading={isLoading}
|
||
isSorted={sorted === "desc"}
|
||
colles={applyFilters(classColles)}
|
||
preferences={user.preferences}
|
||
/>
|
||
)}
|
||
|
||
{/* Bottom Navigation for Mobile */}
|
||
<BottomNavigation activeId="colles" />
|
||
</div>
|
||
</MainLayout>
|
||
);
|
||
}
|