Compare commits

...

3 commits

Author SHA1 Message Date
Nathan Lamy
2208b868c6 feat: add vite pwa plugin
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m36s
2025-07-30 18:57:06 +02:00
Nathan Lamy
a80b64dc4a ui: fix sonner theme 2025-07-30 18:45:17 +02:00
Nathan Lamy
4eadd2f195 chore: remove scroll restoration 2025-07-30 18:33:18 +02:00
11 changed files with 2556 additions and 84 deletions

View file

@ -127,3 +127,11 @@ html,
body { body {
@apply w-full h-full; @apply w-full h-full;
} }
[data-sonner-toast][data-styled='true'] [data-description] {
color: var(--primary) !important;
}
[data-sonner-toaster][data-sonner-theme='dark'] [data-description] {
color: var(--primary) !important;
}

View file

@ -1,7 +1,7 @@
import type React from "react"; import type React from "react";
import type { Colle } from "~/lib/api"; import type { Colle } from "~/lib/api";
import { Link, useNavigate } from "react-router"; import { Link } from "react-router";
import { import {
Card, Card,
CardContent, CardContent,
@ -26,26 +26,14 @@ const getSubjectEmoji = (_: string) => {
type ColleCardProps = { type ColleCardProps = {
colle: Colle; colle: Colle;
onToggleFavorite: (id: number, favorite: boolean) => void; onToggleFavorite: (id: number, favorite: boolean) => void;
beforeClick: () => void;
isFavorite: boolean; isFavorite: boolean;
}; };
export default function ColleCard({ export default function ColleCard({
colle, colle,
onToggleFavorite, onToggleFavorite,
beforeClick,
isFavorite, isFavorite,
}: ColleCardProps) { }: ColleCardProps) {
const navigate = useNavigate();
// TODO: Remove this if scroll restoration is not needed (test first)
const handleCardClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
beforeClick();
setTimeout(() => navigate(`/colles/${colle.id}`), 100);
};
// TODO: Favorites // TODO: Favorites
const handleToggleFavorite = (e: React.MouseEvent) => { const handleToggleFavorite = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent card click e.stopPropagation(); // Prevent card click
@ -58,7 +46,7 @@ export default function ColleCard({
const subjectEmoji = getSubjectEmoji(colle.subject.name); const subjectEmoji = getSubjectEmoji(colle.subject.name);
return ( return (
<Link to={`/colles/${colle.id}`} onClick={handleCardClick}> <Link to={`/colles/${colle.id}`}>
<Card <Card
id={`colle-${colle.id}`} id={`colle-${colle.id}`}
className="h-full cursor-pointer hover:shadow-md transition-shadow border-primary" className="h-full cursor-pointer hover:shadow-md transition-shadow border-primary"
@ -94,7 +82,7 @@ export default function ColleCard({
)} )}
</CardHeader> </CardHeader>
<CardContent className="pb-0 pt-0 px-4" onClick={handleCardClick}> <CardContent className="pb-0 pt-0 px-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="space-y-1"> <div className="space-y-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View file

@ -117,27 +117,6 @@ export default function Home() {
// sessionStorage.setItem('student', studentFilter) // sessionStorage.setItem('student', studentFilter)
}, [subjectFilter, examinerFilter]); }, [subjectFilter, examinerFilter]);
// Restore scroll position
// TODO: Test and check if needed!!
// // SCROLL
// const setScrollPosition = (colleId: number) => {
// sessionStorage.setItem("colles_position", colleId.toString());
// };
// const restoreScrollPosition = () => {
// const position = sessionStorage.getItem("colles_position");
// if (position) {
// const element = document.getElementById(`colle-${position}`);
// if (element) {
// element.scrollIntoView({ behavior: "smooth", block: "center" });
// sessionStorage.removeItem("colles_position");
// }
// }
// };
// const location = useLocation();
// useEffect(() => {
// setTimeout(restoreScrollPosition, 500);
// }, [location]);
return ( return (
<div className="space-y-6 pb-20 md:pb-0"> <div className="space-y-6 pb-20 md:pb-0">
{/* Week Navigation */} {/* Week Navigation */}

View file

@ -84,11 +84,8 @@ export default function TabContent({
<ColleCard <ColleCard
key={colle.id} key={colle.id}
colle={colle} colle={colle}
beforeClick={() => {}}
onToggleFavorite={() => {}} onToggleFavorite={() => {}}
isFavorite={false} isFavorite={false}
// TODO: Implement scroll position handling
// beforeClick={() => setScrollPosition(colle.id)}
// TODO: Implement favorite toggle // TODO: Implement favorite toggle
// onToggleFavorite={handleToggleFavorite} // onToggleFavorite={handleToggleFavorite}
// isFavorite={isFavorite(colle)} // isFavorite={isFavorite(colle)}

View file

@ -84,14 +84,16 @@ export const useTheme = () => {
return context; return context;
}; };
const isWindowAvailable = () => {
return typeof window !== "undefined" && window !== null;
}
export const useDisplayedTheme = () => { export const useDisplayedTheme = () => {
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
if (theme === "system") { if (theme === "system") {
return { return {
theme: window.matchMedia("(prefers-color-scheme: dark)").matches theme: isWindowAvailable() && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light",
? "dark"
: "light",
setTheme, setTheme,
}; };
} }

View file

@ -1,12 +1,8 @@
import { Toaster as Sonner, type ToasterProps } from "sonner"; import { Toaster as Sonner, type ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => { const Toaster = ({ ...props }: ToasterProps) => {
// TODO: Use theme hook
const { theme = "system" } = {};
return ( return (
<Sonner <Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group" className="toaster group"
style={ style={
{ {

View file

@ -25,8 +25,6 @@ import { Separator } from "~/components/ui/separator";
import UserDropdown from "~/components/user-dropdown"; import UserDropdown from "~/components/user-dropdown";
import ColleDetailsSkeleton from "~/components/details/skeleton-details"; import ColleDetailsSkeleton from "~/components/details/skeleton-details";
import AttachmentItem from "~/components/details/attachment"; import AttachmentItem from "~/components/details/attachment";
// TODO: Scroll restoration
// import { ScrollToTopOnMount } from "~/components/noscroll";
import Error from "~/components/error"; import Error from "~/components/error";
import { Badge } from "~/components/ui/badge"; import { Badge } from "~/components/ui/badge";
import { AUTH_ERROR, useColle, useUser } from "~/lib/api"; import { AUTH_ERROR, useColle, useUser } from "~/lib/api";
@ -131,8 +129,6 @@ export default function ColleDetailPage() {
return ( return (
<div className="container mx-auto py-6 px-4 pb-20 md:pb-6 md:py-8"> <div className="container mx-auto py-6 px-4 pb-20 md:pb-6 md:py-8">
{/* <ScrollToTopOnMount /> */}
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-4">
<Button variant="outline" onClick={() => goBack(navigate)}> <Button variant="outline" onClick={() => goBack(navigate)}>
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />

View file

@ -49,14 +49,15 @@
}, },
"devDependencies": { "devDependencies": {
"@react-router/dev": "^7.5.3", "@react-router/dev": "^7.5.3",
"@tailwindcss/vite": "^4.1.4", "@tailwindcss/vite": "^4.1.11",
"@types/luxon": "^3.7.0", "@types/luxon": "^3.7.0",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19.1.2", "@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2", "@types/react-dom": "^19.1.2",
"tailwindcss": "^4.1.4", "tailwindcss": "^4.1.4",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "^6.3.3", "vite": "^7.0.6",
"vite-plugin-pwa": "^1.0.2",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
} }
} }

2529
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

35
pwa.config.ts Normal file
View file

@ -0,0 +1,35 @@
export default {
workbox: {
importScripts: ["/sw/push.js"],
},
includeAssets: [
"favicon.ico",
"favicon-96x96.png",
"web-app-manifest-192x192.png",
"web-app-manifest-512x512.png",
"apple-touch-icon.png",
],
registerType: "autoUpdate",
manifest: {
lang: "fr",
name: "Khollisé ⚔️",
short_name: "Khollisé",
description: "BJColle but faster, better and prettier ⚡️",
theme_color: "#0d0d0d",
background_color: "#f4efe3",
orientation: "portrait",
display: "standalone",
icons: [
{
src: "/web-app-manifest-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "/web-app-manifest-512x512.png",
sizes: "512x512",
type: "image/png",
},
],
},
};

View file

@ -1,8 +1,11 @@
import { reactRouter } from "@react-router/dev/vite"; import { reactRouter } from "@react-router/dev/vite";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import pwaConfig from "./pwa.config";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
import { VitePWA } from "vite-plugin-pwa";
export default defineConfig({ export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], // @ts-expect-error TODO: Fix type error with VitePWA
plugins: [tailwindcss(), reactRouter(), tsconfigPaths(), VitePWA(pwaConfig)],
}); });