From 70c7db810f20b1f488e7cae3794cb4bc5df7331b Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Thu, 13 Nov 2025 21:19:09 +0100 Subject: [PATCH 01/13] Grades subject - Added a badge on the grade cards to display the subject --- src/lib/utils/grades.ts | 40 +++++++++++++++++++++++++++++++++ src/pages/grades/grade-card.tsx | 24 ++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/lib/utils/grades.ts b/src/lib/utils/grades.ts index 40601d5..197b419 100644 --- a/src/lib/utils/grades.ts +++ b/src/lib/utils/grades.ts @@ -34,3 +34,43 @@ export function getGrades({ return true; }); } + +export type GradeBadgeInfo = { + label: string; + rawCode: string; + normalizedCode: string; +}; + +const gradeBadgeKeywordMap: Array<{ keyword: string; label: string }> = [ + { keyword: "maths", label: "Maths" }, + { keyword: "physique", label: "Physique" }, + { keyword: "info", label: "Informatique" }, + { keyword: "anglais", label: "Anglais" }, + { keyword: "sii", label: "SII" }, + { keyword: "fhs", label: "FHS" }, +]; + +export function getGradeBadgeInfoFromCode(code?: string | null): GradeBadgeInfo | null { + const rawCode = (code ?? "").trim(); + if (!rawCode) return null; + + const normalizedBase = rawCode + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/\s+/g, " ") + .trim(); + + const normalizedCode = normalizedBase.toUpperCase(); + const searchableCode = normalizedBase.toLowerCase(); + + const keywordMatch = gradeBadgeKeywordMap.find(({ keyword }) => + searchableCode.includes(keyword) + ); + const label = keywordMatch?.label ?? ""; + + return { + label, + rawCode, + normalizedCode, + }; +} diff --git a/src/pages/grades/grade-card.tsx b/src/pages/grades/grade-card.tsx index 014ff2f..ed7dd09 100644 --- a/src/pages/grades/grade-card.tsx +++ b/src/pages/grades/grade-card.tsx @@ -1,10 +1,12 @@ import { Card } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; import { Grade } from "@/types/aurion"; import { format } from "date-fns"; import { fr } from "date-fns/locale"; import { motion } from "framer-motion"; import { SquareArrowOutDownRightIcon } from "lucide-react"; import { useTranslation } from "react-i18next"; +import { getGradeBadgeInfoFromCode } from "@/lib/utils/grades"; const MotionCard = motion(Card); @@ -39,9 +41,10 @@ export function GradeCardAnimate({ initial="hidden" animate="show" exit="exit" - className="border-none bg-white shadow-md transition-shadow dark:bg-mauria-card p-4 h-full" + className="relative border-none bg-white shadow-md transition-shadow dark:bg-mauria-card p-4 h-full overflow-visible" onClick={onGradeClick.bind(null, grade)} > +
@@ -91,9 +94,10 @@ export function GradeCard({ return ( +
@@ -131,3 +135,19 @@ export function GradeCard({ ); } + +const GradeTypeBadge = ({ code }: { code?: string | null }) => { + if (!code?.trim()) return null; + + const badgeInfo = getGradeBadgeInfoFromCode(code); + if (!badgeInfo?.label) return null; + + return ( + + {badgeInfo.label} + + ); +}; From 3479e80977303b3444bd444be47df97f6d59f5a3 Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Thu, 13 Nov 2025 21:58:48 +0100 Subject: [PATCH 02/13] More subjects - Added more subjects --- src/lib/utils/grades.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/utils/grades.ts b/src/lib/utils/grades.ts index 197b419..e9b7abc 100644 --- a/src/lib/utils/grades.ts +++ b/src/lib/utils/grades.ts @@ -42,9 +42,12 @@ export type GradeBadgeInfo = { }; const gradeBadgeKeywordMap: Array<{ keyword: string; label: string }> = [ - { keyword: "maths", label: "Maths" }, - { keyword: "physique", label: "Physique" }, + { keyword: "math", label: "Maths" }, + { keyword: "phys", label: "Physique" }, + { keyword: "optique", label: "Physique" }, { keyword: "info", label: "Informatique" }, + { keyword: "prog", label: "Informatique" }, + { keyword: "web", label: "Informatique" }, { keyword: "anglais", label: "Anglais" }, { keyword: "sii", label: "SII" }, { keyword: "fhs", label: "FHS" }, From 7776c9289d229aecca9fd57eddaec9378e4dd300 Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Mon, 26 Jan 2026 10:56:38 +0100 Subject: [PATCH 03/13] Data sync race fix - Fix initial data sync race causing missing session on startup --- src/contexts/initialGetter.tsx | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/contexts/initialGetter.tsx b/src/contexts/initialGetter.tsx index d6e9a3f..eeae307 100644 --- a/src/contexts/initialGetter.tsx +++ b/src/contexts/initialGetter.tsx @@ -11,6 +11,7 @@ export const InitialGetter = ({ children }: Props) => { const [isLoading, setIsLoading] = useState(true); const nbTryRef = useRef(0); const hasTimedOut = useRef(false); + const responseReceivedRef = useRef(false); const { t } = useTranslation(); useEffect(() => { @@ -18,8 +19,27 @@ export const InitialGetter = ({ children }: Props) => { const TIMEOUT_MS = 1500; // global timeout 1.5s const requestData = () => { - console.log("Requesting all data from parent..."); + if (hasTimedOut.current) return; + nbTryRef.current += 1; + responseReceivedRef.current = false; + console.log( + `Requesting all data from parent (attempt ${nbTryRef.current}/${MAX_RETRIES})...` + ); window.parent.postMessage({ type: "REQUEST_ALL_DATA" }, "*"); + + const delay = 200 * nbTryRef.current; + setTimeout(() => { + if (hasTimedOut.current || responseReceivedRef.current) return; + if (nbTryRef.current < MAX_RETRIES) { + console.warn( + `No response (attempt ${nbTryRef.current}/${MAX_RETRIES}), retrying...` + ); + requestData(); + } else { + console.warn("Giving up after 3 failed attempts."); + setIsLoading(false); + } + }, delay); }; const handleMessage = async (event: MessageEvent) => { @@ -32,13 +52,13 @@ export const InitialGetter = ({ children }: Props) => { } if (type === "ALL_DATA_RESPONSE") { + responseReceivedRef.current = true; console.log("All data received:", payload); if (payload["email"]) { overrideStorage(payload as Record); setIsLoading(false); } else { - nbTryRef.current += 1; console.warn( `No email found (attempt ${nbTryRef.current}/${MAX_RETRIES})` ); @@ -48,7 +68,7 @@ export const InitialGetter = ({ children }: Props) => { !hasTimedOut.current ) { const delay = 200 * nbTryRef.current; // 200ms, 400ms, 600ms - setTimeout(requestData, delay); + setTimeout(requestData, delay); } else { console.warn("Giving up after 3 failed attempts."); setIsLoading(false); @@ -57,6 +77,8 @@ export const InitialGetter = ({ children }: Props) => { } }; + window.addEventListener("message", handleMessage); + // Initial request requestData(); @@ -69,7 +91,6 @@ export const InitialGetter = ({ children }: Props) => { } }, TIMEOUT_MS); - window.addEventListener("message", handleMessage); return () => { window.removeEventListener("message", handleMessage); clearTimeout(timeout); From 6b3a7a652c333fe68d0047b214df3f72f120e893 Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Mon, 26 Jan 2026 11:05:17 +0100 Subject: [PATCH 04/13] Cache persistence - Set React Query persist maxAge to 30 days --- src/contexts/reactQueryContext.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/contexts/reactQueryContext.tsx b/src/contexts/reactQueryContext.tsx index c0eead7..602543e 100644 --- a/src/contexts/reactQueryContext.tsx +++ b/src/contexts/reactQueryContext.tsx @@ -17,6 +17,8 @@ const queryClient = new QueryClient({ }, }); +const PERSIST_MAX_AGE = 1000 * 60 * 60 * 24 * 30; // 30 days + const localStorageAsyncPersister = createAsyncStoragePersister({ storage: { getItem: (key) => Promise.resolve(getFromStorage(key)), @@ -33,7 +35,10 @@ export const ReactQueryProvider = ({ children }: ReactQueryProviderProps) => { return ( { console.log( "Cache React Query restauré avec persistance asynchrone" From d3edb505cedd9670f0adc17be5ede097ea86acfd Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Mon, 26 Jan 2026 11:37:12 +0100 Subject: [PATCH 05/13] Offline fallback - Allow webapp to run under /webapp by setting base path and router basename --- src/App.tsx | 6 +++++- src/pages/secondary/login.tsx | 6 +++++- vite.config.ts | 17 +++++++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 872bfe5..e9b6374 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -92,6 +92,10 @@ function AppRoutes() { } function App() { + const baseUrl = import.meta.env.BASE_URL ?? "/"; + const routerBaseName = + baseUrl === "/" ? undefined : baseUrl.replace(/\/$/, ""); + return ( @@ -99,7 +103,7 @@ function App() { - + diff --git a/src/pages/secondary/login.tsx b/src/pages/secondary/login.tsx index 55610ed..2c7eaf6 100644 --- a/src/pages/secondary/login.tsx +++ b/src/pages/secondary/login.tsx @@ -23,6 +23,7 @@ import { readInitialLocale, type LocaleOption, } from "@/lib/utils/translations"; +import { useNavigate } from "react-router"; const FIRST_LAUNCH_KEY = "firstLaunch"; @@ -33,6 +34,7 @@ export function LoginPage() { const [showPassword, setShowPassword] = useState(false); const [locale, setLocale] = useState(readInitialLocale); const { t } = useTranslation(); + const navigate = useNavigate(); useEffect(() => { applyLocale(locale); @@ -59,7 +61,9 @@ export function LoginPage() { ); } - window.location.href = shouldShowWelcome ? "/welcome" : "/"; + navigate(shouldShowWelcome ? "/welcome" : "/", { + replace: true, + }); } else { toast.error("Identifiants invalides", { description: diff --git a/vite.config.ts b/vite.config.ts index 77db572..aa2248f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,11 +3,16 @@ import react from "@vitejs/plugin-react"; import { resolve } from "path"; // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - "@": resolve(__dirname, "./src"), +export default defineConfig(() => { + const basePath = process.env.VITE_BASE_PATH ?? "/"; + + return { + base: basePath, + plugins: [react()], + resolve: { + alias: { + "@": resolve(__dirname, "./src"), + }, }, - }, + }; }); From 47b4512fc24960d16658fc76913e437cff0402b9 Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Mon, 26 Jan 2026 14:15:33 +0100 Subject: [PATCH 06/13] Cherry theme - Added a cherry theme --- src/components/sidebar.tsx | 6 ++ src/components/theme-provider.tsx | 4 +- src/components/theme-toggle.tsx | 7 +- src/locales/en-US.json | 3 +- src/locales/es-ES.json | 3 +- src/locales/fr-FR.json | 3 +- src/styles/globals.css | 123 ++++++++++++++++++++++++++++++ 7 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 842fbcb..109ee23 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -3,6 +3,7 @@ import { useEffect, useState, type CSSProperties } from "react"; import { + Cherry, Moon, Sun, MoonStar, @@ -83,6 +84,7 @@ export default function Sidebar() { light: { Icon: Sun, label: t("sidebar.themeParameter.light") }, dark: { Icon: Moon, label: t("sidebar.themeParameter.dark") }, oled: { Icon: MoonStar, label: t("sidebar.themeParameter.oled") }, + cherry: { Icon: Cherry, label: t("sidebar.themeParameter.cherry") }, } as const; const { Icon: ThemeIcon, label: themeLabel } = @@ -120,6 +122,10 @@ export default function Sidebar() { value: "oled", label: t("sidebar.themeParameter.oled"), }, + { + value: "cherry", + label: t("sidebar.themeParameter.cherry"), + }, ], }, { diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx index 8ed65f2..424aaa5 100644 --- a/src/components/theme-provider.tsx +++ b/src/components/theme-provider.tsx @@ -5,7 +5,7 @@ import type React from "react"; import { createContext, useContext, useEffect, useState } from "react"; -type Theme = "dark" | "light" | "oled"; +type Theme = "dark" | "light" | "oled" | "cherry"; type ThemeProviderProps = { children: React.ReactNode; @@ -30,7 +30,7 @@ export function ThemeProvider({ useEffect(() => { const root = window.document.documentElement; - root.classList.remove("light", "dark", "oled"); + root.classList.remove("light", "dark", "oled", "cherry"); root.classList.add(theme); saveToStorage("theme", theme); }, [theme]); diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx index f52108d..47416f6 100644 --- a/src/components/theme-toggle.tsx +++ b/src/components/theme-toggle.tsx @@ -1,13 +1,13 @@ "use client"; -import { Moon, MoonStar, Sun } from "lucide-react"; +import { Cherry, Moon, MoonStar, Sun } from "lucide-react"; import { useTheme } from "@/components/theme-provider"; import { Button } from "@/components/ui/button"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); - const themeCycle: typeof theme[] = ["light", "dark", "oled"]; + const themeCycle: typeof theme[] = ["light", "cherry", "dark", "oled"]; const nextTheme = () => { const currentIndex = themeCycle.indexOf(theme); const nextIndex = (currentIndex + 1) % themeCycle.length; @@ -31,6 +31,9 @@ export function ThemeToggle() { {theme === "oled" && ( )} + {theme === "cherry" && ( + + )} ); } diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 1f729a6..ef8953f 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -37,7 +37,8 @@ "title": "Theme", "dark": "Dark", "light": "Light", - "oled": "OLED" + "oled": "OLED", + "cherry": "Cherry" }, "backgroundParameter": { "title": "Background", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index 1488c91..16792c5 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -37,7 +37,8 @@ "title": "Tema", "dark": "Oscuro", "light": "Claro", - "oled": "OLED" + "oled": "OLED", + "cherry": "Cherry" }, "backgroundParameter": { "title": "Fondo", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 71d9b82..c8e44c1 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -37,7 +37,8 @@ "title": "Thème", "dark": "Sombre", "light": "Clair", - "oled": "OLED" + "oled": "OLED", + "cherry": "Cherry" }, "backgroundParameter": { "title": "Fond", diff --git a/src/styles/globals.css b/src/styles/globals.css index 4bce13c..ee7a1d6 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -225,6 +225,129 @@ hsl(var(--mauria-green)) ); } + .cherry { + color-scheme: light; + + --background: 338 100% 94%; + --foreground: 340 16% 20%; + + --card: 344 100% 92%; + --card-foreground: 340 16% 20%; + + --popover: 344 100% 92%; + --popover-foreground: 340 16% 20%; + + --primary: 351 100% 86%; + --primary-foreground: 340 16% 20%; + + --secondary: 344 100% 92%; + --secondary-foreground: 340 18% 26%; + + --muted: 338 100% 94%; + --muted-foreground: 340 12% 48%; + + --accent: 342 50% 54%; + --accent-foreground: 0 0% 0%; + + --destructive: 0 84% 60%; + --destructive-foreground: 210 40% 98%; + + --border: 347 100% 90%; + --input: 347 100% 90%; + --ring: 349 100% 88%; + + --mauria-bg: 338 100% 94%; + --mauria-card: 344 100% 92%; + --mauria-accent: 342 50% 54%; + --mauria-alert: 340 100% 96%; + --mauria-border: 347 100% 90%; + --mauria-purple: 342 50% 54%; + --mauria-text: 340 16% 20%; + + --mauria-white: 0 0% 100%; + --mauria-black: 0 0% 0%; + --mauria-accent-dark: 342 55% 48%; + --mauria-purple-soft: 342 40% 66%; + --mauria-purple-bright: 342 55% 48%; + --mauria-purple-light: 342 35% 78%; + --mauria-green: 148 34% 52%; + --mauria-green-dark: 148 34% 40%; + --mauria-green-soft: 148 34% 64%; + --mauria-planning-slot-border: 347 100% 90% / 0.25; + --mauria-planning-today: 338 100% 94% / 0.35; + --mauria-particles: 349 100% 88%; + --mauria-meteor-glow: 338 100% 94% / 0.25; + --mauria-chart-line: 344 100% 92%; + + --planning-button-text: hsl(var(--mauria-purple)); + --planning-button-background: hsl(var(--mauria-card)); + --planning-button-shadow: 0 12px 32px hsl(var(--mauria-purple) / 0.18); + --planning-button-active-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-accent)), + hsl(var(--mauria-accent-dark)) + ); + --planning-button-active-shadow: 0 12px 24px hsl( + var(--mauria-accent) / 0.22 + ); + --planning-button-active-text: hsl(var(--mauria-white)); + + --planning-grid-background: hsl(var(--mauria-card)); + --planning-slot-border-color: hsl(var(--mauria-planning-slot-border)); + --planning-today-background: hsl(var(--mauria-planning-today)); + + --planning-event-default-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-purple)), + hsl(var(--mauria-purple-soft)) + ); + --planning-event-default-shadow: 0 16px 30px hsl( + var(--mauria-purple) / 0.18 + ); + --planning-event-default-text: hsl(var(--mauria-white)); + --planning-event-default-solid: hsl(var(--mauria-purple-soft)); + + --planning-event-epreuve-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-accent)), + hsl(var(--mauria-accent-dark)) + ); + --planning-event-epreuve-shadow: 0 16px 30px hsl( + var(--mauria-accent) / 0.2 + ); + --planning-event-epreuve-text: hsl(var(--mauria-white)); + + --planning-event-personal-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-green)), + hsl(var(--mauria-green-soft)) + ); + --planning-event-personal-shadow: 0 16px 30px hsl( + var(--mauria-green) / 0.2 + ); + --planning-event-personal-text: hsl(var(--mauria-white)); + + --planning-event-autogere-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-green)), + hsl(var(--mauria-green-soft)) + ); + --planning-event-autogere-shadow: 0 16px 30px hsl( + var(--mauria-green) / 0.2 + ); + --planning-event-autogere-text: hsl(var(--mauria-white)); + + --particles-color: hsl(var(--mauria-particles)); + --meteors-trail-glow: hsl(var(--mauria-meteor-glow)); + --chart-grid-stroke: hsl(var(--mauria-chart-line)); + --chart-dot-stroke: transparent; + --bubble-color-first: 255, 225, 236; + --bubble-color-second: 255, 215, 226; + --bubble-color-third: 255, 205, 216; + --bubble-color-fourth: 255, 195, 206; + --bubble-color-fifth: 255, 185, 196; + --bubble-color-sixth: 255, 205, 216; + } .oled { color-scheme: dark; From c19d6a221e7ee919773c251c0043c7e11dc7429e Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Thu, 29 Jan 2026 14:09:11 +0100 Subject: [PATCH 07/13] Pride theme --- src/components/drawer-event-task.tsx | 2 +- src/components/sidebar.tsx | 20 ++- src/components/theme-provider.tsx | 4 +- src/components/theme-toggle.tsx | 13 +- src/components/ui/sonner.tsx | 6 +- src/locales/en-US.json | 3 +- src/locales/es-ES.json | 3 +- src/locales/fr-FR.json | 3 +- src/pages/grades/grade-card.tsx | 6 +- src/pages/grades/page.tsx | 2 +- src/pages/home/sections.tsx | 8 +- src/styles/globals.css | 188 +++++++++++++++++++++++++-- 12 files changed, 219 insertions(+), 39 deletions(-) diff --git a/src/components/drawer-event-task.tsx b/src/components/drawer-event-task.tsx index 7b55f86..d5e088d 100644 --- a/src/components/drawer-event-task.tsx +++ b/src/components/drawer-event-task.tsx @@ -259,7 +259,7 @@ export function DrawerEventTask({ )} - + {drawerTitle} diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 109ee23..7acf8c0 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -3,6 +3,7 @@ import { useEffect, useState, type CSSProperties } from "react"; import { + Rainbow, Cherry, Moon, Sun, @@ -85,6 +86,7 @@ export default function Sidebar() { dark: { Icon: Moon, label: t("sidebar.themeParameter.dark") }, oled: { Icon: MoonStar, label: t("sidebar.themeParameter.oled") }, cherry: { Icon: Cherry, label: t("sidebar.themeParameter.cherry") }, + pride: { Icon: Rainbow, label: t("sidebar.themeParameter.pride") }, } as const; const { Icon: ThemeIcon, label: themeLabel } = @@ -126,6 +128,10 @@ export default function Sidebar() { value: "cherry", label: t("sidebar.themeParameter.cherry"), }, + { + value: "pride", + label: t("sidebar.themeParameter.pride"), + }, ], }, { @@ -216,7 +222,7 @@ export default function Sidebar() { ); } diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index 0c27d79..1164011 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -35,13 +35,15 @@ const Toaster = ({ ...props }: ToasterProps) => { const { theme = "system" } = useTheme(); + const sonnerTheme: ToasterProps["theme"] = + theme === "dark" || theme === "oled" ? "dark" : "light"; const mergedToastOptions: ToasterProps["toastOptions"] = { ...toastOptions, classNames: { ...toastOptions?.classNames, toast: mergeClassName( - "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg", + "group toast bg-background text-foreground border border-border shadow-lg", toastOptions?.classNames?.toast ), description: mergeClassName( @@ -61,7 +63,7 @@ const Toaster = ({ return (
-
+
{grade.name}
@@ -108,7 +108,7 @@ export function GradeCard({
-
+
{grade.name}
@@ -145,7 +145,7 @@ const GradeTypeBadge = ({ code }: { code?: string | null }) => { return ( {badgeInfo.label} diff --git a/src/pages/grades/page.tsx b/src/pages/grades/page.tsx index 334cda8..0ea594c 100644 --- a/src/pages/grades/page.tsx +++ b/src/pages/grades/page.tsx @@ -147,7 +147,7 @@ export function GradesPage() { {selectedGrade && (
-

+

{selectedGrade.name}

diff --git a/src/pages/home/sections.tsx b/src/pages/home/sections.tsx index 42cb9f5..8cc61d0 100644 --- a/src/pages/home/sections.tsx +++ b/src/pages/home/sections.tsx @@ -64,9 +64,7 @@ export const LessonCard = ({

{lesson.courseTitle}

- + {formatLessonType(lesson.type)}
@@ -167,10 +165,10 @@ export const ImportantMessage = ({ message }: { message?: MessageEntry }) => ( className="rounded-lg bg-white dark:bg-mauria-alert oled:bg-black" > - + {message?.title ?? "Aucun message important"} - + {message?.message ?? "Bonne journée !"} diff --git a/src/styles/globals.css b/src/styles/globals.css index ee7a1d6..4b02422 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -30,10 +30,10 @@ --mauria-scale: 1; --background: 40 33% 98%; - --foreground: 222 47% 11%; + --foreground: 0 0% 0%; --card: 0 0% 100%; - --card-foreground: 222 47% 11%; + --card-foreground: 0 0% 0%; --popover: 0 0% 100%; --popover-foreground: 222 47% 11%; @@ -101,23 +101,21 @@ --planning-event-default-gradient: linear-gradient( 45deg, - hsl(var(--mauria-purple)), - hsl(var(--mauria-purple-soft)) - ); - --planning-event-default-shadow: 0 16px 32px hsl( - var(--mauria-purple) / 0.24 + hsl(352 90% 65%), + hsl(50 95% 58%), + hsl(200 85% 60%) ); + --planning-event-default-shadow: 0 16px 32px hsl(200 85% 60% / 0.28); --planning-event-default-text: hsl(var(--mauria-white)); - --planning-event-default-solid: hsl(var(--mauria-purple-soft)); + --planning-event-default-solid: hsl(352 90% 65%); --planning-event-epreuve-gradient: linear-gradient( 45deg, - hsl(var(--mauria-accent)), - hsl(var(--mauria-accent-dark)) - ); - --planning-event-epreuve-shadow: 0 16px 32px hsl( - var(--mauria-accent) / 0.24 + hsl(25 95% 60%), + hsl(50 95% 58%), + hsl(135 65% 50%) ); + --planning-event-epreuve-shadow: 0 16px 32px hsl(50 95% 58% / 0.28); --planning-event-epreuve-text: hsl(var(--mauria-white)); --planning-event-personal-gradient: linear-gradient( @@ -348,6 +346,139 @@ --bubble-color-fifth: 255, 185, 196; --bubble-color-sixth: 255, 205, 216; } + .pride { + color-scheme: light; + + --background: 0 0% 98%; + --foreground: 222 47% 11%; + + --card: 0 0% 100%; + --card-foreground: 222 47% 11%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 0%; + + --primary: 200 85% 55%; + --primary-foreground: 0 0% 100%; + + --secondary: 210 40% 96%; + --secondary-foreground: 0 0% 0%; + + --muted: 210 40% 96%; + --muted-foreground: 0 0% 25%; + + --accent: 352 90% 60%; + --accent-foreground: 0 0% 0%; + + --destructive: 0 84% 60%; + --destructive-foreground: 210 40% 98%; + + --border: 214 32% 91%; + --input: 214 32% 91%; + --ring: 200 85% 70%; + + --mauria-bg: 0 0% 98%; + --mauria-card: 0 0% 100%; + --mauria-accent: 25 95% 60%; + --mauria-alert: 0 100% 95%; + --mauria-border: 214 32% 91%; + --mauria-purple: 0 0% 0%; + --mauria-text: 0 0% 0%; + + --mauria-white: 0 0% 100%; + --mauria-black: 0 0% 0%; + --mauria-accent-dark: 50 95% 58%; + --mauria-purple-soft: 0 0% 0%; + --mauria-purple-bright: 0 0% 0%; + --mauria-purple-light: 0 0% 0%; + --mauria-green: 135 65% 45%; + --mauria-green-dark: 135 65% 35%; + --mauria-green-soft: 135 65% 55%; + --mauria-planning-slot-border: 214 32% 91% / 0.4; + --mauria-planning-today: 50 95% 80% / 0.25; + --mauria-particles: 200 85% 60%; + --mauria-meteor-glow: 352 90% 70% / 0.25; + --mauria-chart-line: 270 50% 70%; + + --planning-button-text: hsl(var(--mauria-purple)); + --planning-button-background: hsl(var(--mauria-card)); + --planning-button-shadow: 0 12px 32px hsl(var(--mauria-purple) / 0.2); + --planning-button-active-gradient: linear-gradient( + 45deg, + hsl(var(--mauria-accent)), + hsl(var(--mauria-accent-dark)) + ); + --planning-button-active-shadow: 0 12px 24px hsl( + var(--mauria-accent) / 0.24 + ); + --planning-button-active-text: hsl(var(--mauria-white)); + + --planning-grid-background: hsl(var(--mauria-card)); + --planning-slot-border-color: hsl(var(--mauria-planning-slot-border)); + --planning-today-background: hsl(var(--mauria-planning-today)); + + --planning-event-default-gradient: linear-gradient( + 45deg, + hsl(25 95% 60%), + hsl(50 95% 58%) + ); + --planning-event-default-shadow: 0 16px 32px hsl(25 95% 60% / 0.28); + --planning-event-default-text: hsl(var(--mauria-black)); + --planning-event-default-solid: hsl(352 90% 65%); + + --planning-event-epreuve-gradient: linear-gradient( + 45deg, + hsl(330 85% 60%), + hsl(352 90% 65%) + ); + --planning-event-epreuve-shadow: 0 16px 32px hsl(330 85% 60% / 0.28); + --planning-event-epreuve-text: hsl(var(--mauria-black)); + + --planning-event-personal-gradient: linear-gradient( + 45deg, + hsl(135 65% 50%), + hsl(170 65% 45%) + ); + --planning-event-personal-shadow: 0 16px 32px hsl(135 65% 50% / 0.28); + --planning-event-personal-text: hsl(var(--mauria-black)); + + --planning-event-autogere-gradient: linear-gradient( + 45deg, + hsl(200 85% 60%), + hsl(240 70% 60%) + ); + --planning-event-autogere-shadow: 0 16px 32px hsl(200 85% 60% / 0.28); + --planning-event-autogere-text: hsl(var(--mauria-black)); + + --particles-color: hsl(var(--mauria-particles)); + --meteors-trail-glow: hsl(var(--mauria-meteor-glow)); + --chart-grid-stroke: hsl(var(--mauria-chart-line)); + --chart-dot-stroke: transparent; + --bubble-color-first: 255, 99, 132; + --bubble-color-second: 255, 159, 64; + --bubble-color-third: 255, 205, 86; + --bubble-color-fourth: 75, 192, 192; + --bubble-color-fifth: 54, 162, 235; + --bubble-color-sixth: 153, 102, 255; + --mauria-pride-gradient: linear-gradient( + 135deg, + hsl(352 90% 65%), + hsl(25 95% 60%), + hsl(50 95% 58%), + hsl(135 65% 50%), + hsl(200 85% 60%), + hsl(270 80% 65%) + ); + --mauria-pride-gradient-soft: linear-gradient( + 135deg, + hsl(352 90% 65% / 0.25), + hsl(25 95% 60% / 0.25), + hsl(50 95% 58% / 0.25), + hsl(135 65% 50% / 0.25), + hsl(200 85% 60% / 0.25), + hsl(270 80% 65% / 0.25) + ); + } .oled { color-scheme: dark; @@ -481,6 +612,37 @@ .oled .bg-mauria-accent\/20 { background-color: hsl(0 0% 16% / 0.65) !important; } + .pride header .text-white { + color: hsl(var(--mauria-black)); + } + .pride header .text-white:hover { + color: hsl(var(--mauria-black)); + } + .pride .bg-mauria-purple { + background-image: var(--mauria-pride-gradient); + background-color: transparent; + } + .pride .bg-mauria-purple\/10 { + background-image: var(--mauria-pride-gradient-soft); + background-color: transparent; + } + .pride .bg-mauria-accent { + background-image: var(--mauria-pride-gradient); + background-color: transparent; + } + .pride .bg-mauria-accent\/20 { + background-image: var(--mauria-pride-gradient-soft); + background-color: transparent; + } + .pride .bg-accent { + background-image: var(--mauria-pride-gradient-soft); + background-color: transparent; + } + .toaster [data-sonner-toast] { + background-color: hsl(var(--background)); + color: hsl(var(--foreground)); + border: 1px solid hsl(var(--border)); + } } /* From 73c40fd5148fd18ea234087a2c739554fe78d6c0 Mon Sep 17 00:00:00 2001 From: Dark-Louis Date: Fri, 30 Jan 2026 08:49:14 +0100 Subject: [PATCH 08/13] Ocean and forest themes - Added ocean and forest themes - Fixed dark and oled hover color --- src/components/bottom-navigation.tsx | 22 +- src/components/sidebar.tsx | 12 + src/components/theme-provider.tsx | 12 +- src/components/theme-toggle.tsx | 10 +- .../ui/shadcn-io/grid-pattern/index.tsx | 5 +- src/locales/en-US.json | 2 + src/locales/es-ES.json | 2 + src/locales/fr-FR.json | 2 + src/styles/globals.css | 244 ++++++++++++++++++ 9 files changed, 304 insertions(+), 7 deletions(-) diff --git a/src/components/bottom-navigation.tsx b/src/components/bottom-navigation.tsx index 4ddc7e6..9d6423a 100644 --- a/src/components/bottom-navigation.tsx +++ b/src/components/bottom-navigation.tsx @@ -11,12 +11,18 @@ import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils/cn"; import { useNavigate, useLocation } from "react-router"; import { useTranslation } from "react-i18next"; +import { useTheme } from "@/components/theme-provider"; export default function BottomNavigation() { const navigate = useNavigate(); const { t } = useTranslation(); const location = useLocation(); const pathname = location.pathname; + const { theme } = useTheme(); + const hoverWhite = theme === "dark" || theme === "oled"; + const hoverTextClass = hoverWhite + ? "group-hover:text-white" + : "group-hover:text-mauria-purple"; const items = [ { @@ -70,7 +76,7 @@ export default function BottomNavigation() { "group flex h-auto flex-1 flex-col items-center gap-1 rounded-full px-4 pb-2 pt-2 text-[0.7rem] font-medium transition-colors hover:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 [&_svg]:size-7 min-w-0", isActive ? "text-mauria-purple dark:text-white oled:text-gray-100" - : "text-gray-400 dark:text-gray-500 oled:text-gray-500 group-hover:text-mauria-purple dark:group-hover:text-white oled:group-hover:text-gray-200" + : "text-gray-400 dark:text-gray-500 oled:text-gray-500" )} onClick={() => navigate(item.path)} aria-current={isActive ? "page" : undefined} @@ -84,9 +90,19 @@ export default function BottomNavigation() { : "bg-transparent group-hover:bg-mauria-purple/10 dark:group-hover:bg-white/10 oled:group-hover:bg-white/10" )} /> - + - + {item.label} diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 7acf8c0..43494c4 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -8,6 +8,8 @@ import { Moon, Sun, MoonStar, + TreePine, + Waves, Menu, HeartHandshake, BadgeX, @@ -86,6 +88,8 @@ export default function Sidebar() { dark: { Icon: Moon, label: t("sidebar.themeParameter.dark") }, oled: { Icon: MoonStar, label: t("sidebar.themeParameter.oled") }, cherry: { Icon: Cherry, label: t("sidebar.themeParameter.cherry") }, + ocean: { Icon: Waves, label: t("sidebar.themeParameter.ocean") }, + forest: { Icon: TreePine, label: t("sidebar.themeParameter.forest") }, pride: { Icon: Rainbow, label: t("sidebar.themeParameter.pride") }, } as const; @@ -128,6 +132,14 @@ export default function Sidebar() { value: "cherry", label: t("sidebar.themeParameter.cherry"), }, + { + value: "ocean", + label: t("sidebar.themeParameter.ocean"), + }, + { + value: "forest", + label: t("sidebar.themeParameter.forest"), + }, { value: "pride", label: t("sidebar.themeParameter.pride"), diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx index 3868fb4..a0696af 100644 --- a/src/components/theme-provider.tsx +++ b/src/components/theme-provider.tsx @@ -5,7 +5,7 @@ import type React from "react"; import { createContext, useContext, useEffect, useState } from "react"; -type Theme = "dark" | "light" | "oled" | "cherry" | "pride"; +type Theme = "dark" | "light" | "oled" | "cherry" | "pride" | "ocean" | "forest"; type ThemeProviderProps = { children: React.ReactNode; @@ -30,7 +30,15 @@ export function ThemeProvider({ useEffect(() => { const root = window.document.documentElement; - root.classList.remove("light", "dark", "oled", "cherry", "pride"); + root.classList.remove( + "light", + "dark", + "oled", + "cherry", + "pride", + "ocean", + "forest" + ); root.classList.add(theme); saveToStorage("theme", theme); }, [theme]); diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx index ff040a9..b86bbb6 100644 --- a/src/components/theme-toggle.tsx +++ b/src/components/theme-toggle.tsx @@ -1,6 +1,6 @@ "use client"; -import { Cherry, Moon, MoonStar, Rainbow, Sun } from "lucide-react"; +import { Cherry, Moon, MoonStar, Rainbow, Sun, TreePine, Waves } from "lucide-react"; import { useTheme } from "@/components/theme-provider"; import { Button } from "@/components/ui/button"; @@ -10,6 +10,8 @@ export function ThemeToggle() { const themeCycle: typeof theme[] = [ "light", "cherry", + "ocean", + "forest", "pride", "dark", "oled", @@ -40,6 +42,12 @@ export function ThemeToggle() { {theme === "cherry" && ( )} + {theme === "ocean" && ( + + )} + {theme === "forest" && ( + + )} {theme === "pride" && ( )} diff --git a/src/components/ui/shadcn-io/grid-pattern/index.tsx b/src/components/ui/shadcn-io/grid-pattern/index.tsx index 94c61e2..ed0a664 100644 --- a/src/components/ui/shadcn-io/grid-pattern/index.tsx +++ b/src/components/ui/shadcn-io/grid-pattern/index.tsx @@ -11,6 +11,7 @@ interface GridPatternProps { squares?: Array<[number, number]>; strokeDasharray?: string; className?: string; + style?: React.CSSProperties; [key: string]: unknown; } @@ -22,6 +23,7 @@ export function GridPattern({ strokeDasharray = "0", squares, className, + style, ...props }: GridPatternProps) { const id = React.useId(); @@ -30,9 +32,10 @@ export function GridPattern({ - mode Beta + beta + + + +
diff --git a/src/contexts/initialGetter.tsx b/src/contexts/initialGetter.tsx index eeae307..aa83fae 100644 --- a/src/contexts/initialGetter.tsx +++ b/src/contexts/initialGetter.tsx @@ -12,14 +12,20 @@ export const InitialGetter = ({ children }: Props) => { const nbTryRef = useRef(0); const hasTimedOut = useRef(false); const responseReceivedRef = useRef(false); + const parentReadyRef = useRef(false); + const initialRequestSentRef = useRef(false); const { t } = useTranslation(); useEffect(() => { - const MAX_RETRIES = 3; - const TIMEOUT_MS = 1500; // global timeout 1.5s + const MAX_RETRIES = 6; + const TIMEOUT_MS = 5000; // global timeout 5s + const READY_WAIT_MS = 600; const requestData = () => { if (hasTimedOut.current) return; + if (!initialRequestSentRef.current) { + initialRequestSentRef.current = true; + } nbTryRef.current += 1; responseReceivedRef.current = false; console.log( @@ -45,6 +51,14 @@ export const InitialGetter = ({ children }: Props) => { const handleMessage = async (event: MessageEvent) => { const { type, key, payload } = event.data ?? {}; + if (type === "PARENT_READY") { + parentReadyRef.current = true; + if (isLoading && !initialRequestSentRef.current) { + requestData(); + } + return; + } + if (type === "DATA_RESPONSE" && key) { console.log("Data received for %s:", key, payload); saveFromApp(key, payload); @@ -79,8 +93,17 @@ export const InitialGetter = ({ children }: Props) => { window.addEventListener("message", handleMessage); - // Initial request - requestData(); + const isEmbedded = window.parent !== window; + let readyTimeout: number | undefined; + if (!isEmbedded) { + requestData(); + } else { + readyTimeout = window.setTimeout(() => { + if (!parentReadyRef.current) { + requestData(); + } + }, READY_WAIT_MS); + } // Global timeout in case parent never answers const timeout = setTimeout(() => { @@ -94,6 +117,9 @@ export const InitialGetter = ({ children }: Props) => { return () => { window.removeEventListener("message", handleMessage); clearTimeout(timeout); + if (readyTimeout) { + clearTimeout(readyTimeout); + } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/lib/utils/storage.ts b/src/lib/utils/storage.ts index d0fc02b..10d7813 100644 --- a/src/lib/utils/storage.ts +++ b/src/lib/utils/storage.ts @@ -1,3 +1,66 @@ +type StorageLogEntry = { + ts: string; + action: "set" | "override" | "remove" | "clear" | "launch"; + key?: string; + size?: number; + details?: string; +}; + +const STORAGE_LOGS_KEY = "__mauria_storage_logs__"; +const STORAGE_LOGS_LIMIT = 200; + +function readStorageLogs(): StorageLogEntry[] { + try { + const raw = localStorage.getItem(STORAGE_LOGS_KEY); + if (!raw) return []; + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function writeStorageLogs(logs: StorageLogEntry[]) { + try { + localStorage.setItem(STORAGE_LOGS_KEY, JSON.stringify(logs)); + } catch { + // Ignore logging failures to avoid breaking storage usage + } +} + +function appendStorageLog(entry: StorageLogEntry) { + try { + const logs = readStorageLogs(); + logs.push(entry); + if (logs.length > STORAGE_LOGS_LIMIT) { + logs.splice(0, logs.length - STORAGE_LOGS_LIMIT); + } + writeStorageLogs(logs); + } catch { + // Ignore logging failures + } +} + +export function getStorageLogs(): StorageLogEntry[] { + return readStorageLogs(); +} + +export function clearStorageLogs() { + try { + localStorage.removeItem(STORAGE_LOGS_KEY); + } catch { + // Ignore logging failures + } +} + +export function logAppLaunch() { + appendStorageLog({ + ts: new Date().toISOString(), + action: "launch", + details: "----- LANCEMENT DE L'APP -----", + }); +} + export function saveToStorage(key: string, value: string) { try { localStorage.setItem(key, value); @@ -5,6 +68,14 @@ export function saveToStorage(key: string, value: string) { { type: "SAVE_DATA", key, payload: value }, "*" ); + if (key !== STORAGE_LOGS_KEY) { + appendStorageLog({ + ts: new Date().toISOString(), + action: "set", + key, + size: value.length, + }); + } } catch (e) { // Handle storage errors (e.g., quota exceeded) console.error("Error saving to localStorage", e); @@ -14,6 +85,15 @@ export function saveToStorage(key: string, value: string) { export function saveFromApp(key: string, value: string) { try { localStorage.setItem(key, value); + if (key !== STORAGE_LOGS_KEY) { + appendStorageLog({ + ts: new Date().toISOString(), + action: "set", + key, + size: value.length, + details: "from-app", + }); + } } catch (e) { // Handle storage errors (e.g., quota exceeded) console.error("Error saving to localStorage", e); @@ -39,6 +119,11 @@ export function overrideStorage(data: Record) { for (const [key, value] of Object.entries(data)) { localStorage.setItem(key, value); } + appendStorageLog({ + ts: new Date().toISOString(), + action: "override", + details: `keys:${Object.keys(data).length}`, + }); } catch (e) { // Handle storage errors console.error("Error overriding localStorage", e); @@ -48,6 +133,13 @@ export function overrideStorage(data: Record) { export function removeFromStorage(key: string) { try { localStorage.removeItem(key); + if (key !== STORAGE_LOGS_KEY) { + appendStorageLog({ + ts: new Date().toISOString(), + action: "remove", + key, + }); + } } catch (e) { // Handle storage errors console.error("Error removing from localStorage", e); @@ -57,6 +149,10 @@ export function removeFromStorage(key: string) { export function clearStorage() { try { localStorage.clear(); + appendStorageLog({ + ts: new Date().toISOString(), + action: "clear", + }); } catch (e) { // Handle storage errors console.error("Error clearing localStorage", e); diff --git a/src/main.tsx b/src/main.tsx index ee40c07..ecb90cc 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,6 +4,9 @@ import App from "./App.tsx"; import "@/i18n"; import "@/styles/globals.css"; import { ErrorBoundary } from "./components/error-boundary.tsx"; +import { logAppLaunch } from "./lib/utils/storage"; + +logAppLaunch(); createRoot(document.getElementById("root")!).render( diff --git a/src/pages/secondary/logs.tsx b/src/pages/secondary/logs.tsx new file mode 100644 index 0000000..4f782ae --- /dev/null +++ b/src/pages/secondary/logs.tsx @@ -0,0 +1,97 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { clearStorageLogs, getStorageLogs, logAppLaunch } from "@/lib/utils/storage"; + +type StorageLogEntry = { + ts: string; + action: "set" | "override" | "remove" | "clear" | "launch"; + key?: string; + size?: number; + details?: string; +}; + +function formatLogLine(entry: StorageLogEntry) { + if (entry.action === "launch") { + return `[${formatTimestamp(entry.ts)}] ${ + entry.details ?? "----- LANCEMENT DE L'APP -----" + }`; + } + const parts = [`[${formatTimestamp(entry.ts)}]`, entry.action.toUpperCase()]; + if (entry.key) parts.push(entry.key); + if (entry.size !== undefined) parts.push(`size=${entry.size}`); + if (entry.details) parts.push(entry.details); + return parts.join(" "); +} + +function formatTimestamp(ts: string) { + const date = new Date(ts); + if (Number.isNaN(date.getTime())) return ts; + const pad = (value: number) => String(value).padStart(2, "0"); + const year = date.getFullYear(); + const month = pad(date.getMonth() + 1); + const day = pad(date.getDate()); + const hours = pad(date.getHours()); + const minutes = pad(date.getMinutes()); + const seconds = pad(date.getSeconds()); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} + +export function LogsPage() { + const [logs, setLogs] = useState([]); + + useEffect(() => { + const refresh = () => { + const nextLogs = getStorageLogs(); + setLogs(nextLogs); + }; + refresh(); + const interval = window.setInterval(refresh, 1000); + return () => { + window.clearInterval(interval); + }; + }, []); + + return ( +
+
+ + +
+ + Logs + + +
+
+ + + {logs.length === 0 ? ( +
+ Aucun log localStorage pour le moment. +
+ ) : ( +
+                                    {logs.map(formatLogLine).join("\n")}
+                                
+ )} +
+
+
+
+
+ ); +} From 7862a51e44509b920b806f0912210e33a74b9f4b Mon Sep 17 00:00:00 2001 From: Mylow Date: Tue, 3 Feb 2026 21:44:25 +0100 Subject: [PATCH 10/13] =?UTF-8?q?Change=20'Fiert=C3=A9'=20to=20'Rainbow'?= =?UTF-8?q?=20in=20en-US.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 8c1991a..5cd8d8f 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -41,7 +41,7 @@ "cherry": "Cherry", "ocean": "Ocean", "forest": "Forest", - "pride": "Fierté" + "pride": "Rainbow" }, "backgroundParameter": { "title": "Background", From 45a0978f02ebf101121ccae097b820b74463d743 Mon Sep 17 00:00:00 2001 From: Mylow Date: Tue, 3 Feb 2026 21:45:00 +0100 Subject: [PATCH 11/13] Update 'pride' translation to 'Arc-en-ciel' --- src/locales/es-ES.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index da3f82b..dd23781 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -41,7 +41,7 @@ "cherry": "Cherry", "ocean": "Océano", "forest": "Bosque", - "pride": "Fierté" + "pride": "Arc-en-ciel" }, "backgroundParameter": { "title": "Fondo", From c7e8946f9596c42e36e15d4e5db6a9f23682b016 Mon Sep 17 00:00:00 2001 From: Mylow Date: Tue, 3 Feb 2026 21:45:37 +0100 Subject: [PATCH 12/13] Update translation for 'pride' in Spanish locale --- src/locales/es-ES.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index dd23781..07810ee 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -41,7 +41,7 @@ "cherry": "Cherry", "ocean": "Océano", "forest": "Bosque", - "pride": "Arc-en-ciel" + "pride": "Arcoíris" }, "backgroundParameter": { "title": "Fondo", From a7353d53a86f93246e38effe42fbf1ec1823b456 Mon Sep 17 00:00:00 2001 From: Mylow Date: Tue, 3 Feb 2026 21:45:54 +0100 Subject: [PATCH 13/13] Update French translation for 'pride' key --- src/locales/fr-FR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index 958e76b..453f79a 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -41,7 +41,7 @@ "cherry": "Cherry", "ocean": "Océan", "forest": "Forêt", - "pride": "Fierté" + "pride": "Arc-en-ciel" }, "backgroundParameter": { "title": "Fond",