From 2443c1f30bde92e5251a0658651191bd79e069ad Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 20:50:08 +0100 Subject: [PATCH 01/13] feat: claude made this new UI --- src/app/_home/$school/$year/index.tsx | 77 ++---- src/app/_home/$school/index.tsx | 88 ++++--- src/app/_home/index.tsx | 59 ++--- src/components/Homepage/RankingSelector.tsx | 269 ++++++++++++++++++++ src/components/Homepage/SchoolCard.tsx | 80 ++++++ src/index.css | 2 +- 6 files changed, 456 insertions(+), 119 deletions(-) create mode 100644 src/components/Homepage/RankingSelector.tsx create mode 100644 src/components/Homepage/SchoolCard.tsx diff --git a/src/app/_home/$school/$year/index.tsx b/src/app/_home/$school/$year/index.tsx index c81d3bef..a375edc6 100644 --- a/src/app/_home/$school/$year/index.tsx +++ b/src/app/_home/$school/$year/index.tsx @@ -1,12 +1,9 @@ import { useQuery } from "@tanstack/react-query" -import { createFileRoute, Link, redirect } from "@tanstack/react-router" +import { createFileRoute, redirect } from "@tanstack/react-router" import Page from "@/components/custom-ui/Page" -import PhaseFlag from "@/components/custom-ui/PhaseFlag" -import { ButtonGrid } from "@/components/Homepage/ButtonGrid" +import { RankingSelector } from "@/components/Homepage/RankingSelector" import PathBreadcrumb from "@/components/PathBreadcrumb" -import { Button } from "@/components/ui/button" import { useQueries } from "@/hooks/use-queries" -import { getPhaseGroups, phaseGroupLabel, phaseLinkLabel } from "@/utils/phase" import type { PhaseLink } from "@/utils/types/data/phase" import { isSchool } from "@/utils/types/data/school" @@ -24,63 +21,31 @@ function RouteComponent() { const { school, year } = Route.useParams() const queries = useQueries() const { data, isPending } = useQuery(queries.index) + if (!data || isPending) return null - const yearData = data[school]?.[year] ?? [] - const groups = getPhaseGroups(yearData).entriesArr() + const yearData = data[school]?.[year] ?? [] + const phases: PhaseLink[] = yearData.map((entry) => ({ + ...entry.phase, + id: entry.id, + })) return ( -

Scegli una graduatoria

- {groups.map(([n, phases]) => ( - 1} - /> - ))} -
- ) -} +
+
+

+ Seleziona graduatoria +

+

+ Filtra e scegli la graduatoria da consultare per{" "} + {school} {year} +

+
-type GroupProps = { - primary: number - phases: PhaseLink[] - showGeneral: boolean -} - -function Group({ primary, phases, showGeneral }: GroupProps) { - const { school, year } = Route.useParams() - return ( - <> - {(showGeneral || primary !== 0) &&

{phaseGroupLabel(primary)}

} - - {phases.map((phase) => ( - - - - ))} - - + +
+ ) } diff --git a/src/app/_home/$school/index.tsx b/src/app/_home/$school/index.tsx index 6fa5df23..e712b6a0 100644 --- a/src/app/_home/$school/index.tsx +++ b/src/app/_home/$school/index.tsx @@ -1,13 +1,11 @@ import { useQuery } from "@tanstack/react-query" import { createFileRoute, Link, redirect } from "@tanstack/react-router" -import { useState } from "react" import Page from "@/components/custom-ui/Page" -import Spinner from "@/components/custom-ui/Spinner" -import { ButtonGrid } from "@/components/Homepage/ButtonGrid" import PathBreadcrumb from "@/components/PathBreadcrumb" -import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" import { useQueries } from "@/hooks/use-queries" import { isSchool } from "@/utils/types/data/school" +import { cn } from "@/utils/ui" export const Route = createFileRoute("/_home/$school/")({ component: RouteComponent, @@ -20,39 +18,71 @@ export const Route = createFileRoute("/_home/$school/")({ }) function RouteComponent() { - const [clicked, setClicked] = useState(false) const { school } = Route.useParams() const queries = useQueries() const { data, isPending } = useQuery(queries.index) if (!data || isPending) return null - const years = Object.keys(data[school] ?? {}).map(Number) + const years = Object.keys(data[school] ?? {}) + .map(Number) + .sort((a, b) => b - a) - return clicked ? ( -
- -
- ) : ( + const currentYear = new Date().getFullYear() + + return ( -

Scegli un anno di immatricolazione

- - {years - .sort((a, b) => b - a) - .map((year) => ( - setClicked(true)} - > - - - ))} - +
+
+

+ Anno di immatricolazione +

+

+ Seleziona l'anno accademico di cui vuoi consultare le graduatorie +

+
+ +
+ {years.map((year, index) => { + const isRecent = year >= currentYear - 1 + const isFirst = index === 0 + + return ( + + + {year} + {isRecent && ( + + )} + + + ) + })} +
+ + {years.length > 0 && ( +

+ {years.length}{" "} + {years.length === 1 ? "anno disponibile" : "anni disponibili"} dal{" "} + {years[years.length - 1]} al {years[0]} +

+ )} +
) } diff --git a/src/app/_home/index.tsx b/src/app/_home/index.tsx index 33248f50..a21fd2ac 100644 --- a/src/app/_home/index.tsx +++ b/src/app/_home/index.tsx @@ -1,11 +1,9 @@ import { useQuery } from "@tanstack/react-query" -import { createFileRoute, Link } from "@tanstack/react-router" +import { createFileRoute } from "@tanstack/react-router" import Alert from "@/components/custom-ui/Alert" import Page from "@/components/custom-ui/Page" import Spinner from "@/components/custom-ui/Spinner" -import { ButtonGrid } from "@/components/Homepage/ButtonGrid" -import { SchoolEmoji } from "@/components/school-emoji" -import { Button } from "@/components/ui/button" +import { SchoolCard } from "@/components/Homepage/SchoolCard" import { useQueries } from "@/hooks/use-queries" export const Route = createFileRoute("/_home/")({ @@ -22,39 +20,34 @@ function RouteComponent() { return ( -
-

- 👋 Ciao!{" "} - Questo sito raccoglie{" "} - lo storico{" "} - delle graduatorie{" "} - del Politecnico di Milano. -

-

- Inizia scegliendo l'area di studi di tuo interesse -

- {schools && ( - +
+
+

+ Storico Graduatorie PoliMi +

+

+ Consulta le graduatorie storiche del Politecnico di Milano. + Seleziona un'area di studi per iniziare. +

+
+ + {schools && data && ( +
{schools.map((school) => ( - - - + school={school} + yearCount={Object.keys(data[school] ?? {}).length} + /> ))} - +
+ )} + + {isPending && ( +
+ +
)} - {isPending && } {error instanceof Error && {error.message}}
diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx new file mode 100644 index 00000000..bb635efa --- /dev/null +++ b/src/components/Homepage/RankingSelector.tsx @@ -0,0 +1,269 @@ +import { Link } from "@tanstack/react-router" +import { useMemo } from "react" +import { phaseGroupLabel } from "@/utils/phase" +import { numberToRoman } from "@/utils/strings/numbers" +import type { PhaseLink } from "@/utils/types/data/phase" +import type { School } from "@/utils/types/data/school" +import { cn } from "@/utils/ui" + +type Props = { + phases: PhaseLink[] + school: School + year: number +} + +type Language = "IT" | "EN" + +type LanguageConfig = { + lang: Language + title: string + flag: string + headerStyles: string + itemStyles: string + separatorStyles: string +} + +const LANGUAGES: LanguageConfig[] = [ + { + lang: "IT", + title: "Italiano", + flag: "🇮🇹", + headerStyles: + "bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-300", + itemStyles: + "bg-white hover:bg-green-50 dark:bg-slate-800 dark:hover:bg-green-900/20", + separatorStyles: + "bg-green-50 text-green-700 dark:bg-green-900/20 dark:text-green-400", + }, + { + lang: "EN", + title: "English", + flag: "🇬🇧", + headerStyles: + "bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300", + itemStyles: + "bg-white hover:bg-blue-50 dark:bg-slate-800 dark:hover:bg-blue-900/20", + separatorStyles: + "bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400", + }, +] + +// Group phases by primary, then by secondary, keeping extraEu/normal pairs together +type PhaseGroup = { + primary: number + secondaries: Array<{ + secondary: number + normal?: PhaseLink + extraEu?: PhaseLink + }> +} + +function groupPhases(phases: PhaseLink[], lang: Language): PhaseGroup[] { + const langPhases = phases.filter((p) => p.language === lang) + + // Group by primary + const byPrimary = new Map() + for (const phase of langPhases) { + const existing = byPrimary.get(phase.primary) ?? [] + existing.push(phase) + byPrimary.set(phase.primary, existing) + } + + // For each primary, group by secondary and pair normal/extraEu + const result: PhaseGroup[] = [] + for (const [primary, phasesInPrimary] of [...byPrimary.entries()].sort( + ([a], [b]) => a - b + )) { + const bySecondary = new Map< + number, + { normal?: PhaseLink; extraEu?: PhaseLink } + >() + + for (const phase of phasesInPrimary) { + const existing = bySecondary.get(phase.secondary) ?? {} + if (phase.isExtraEu) { + existing.extraEu = phase + } else { + existing.normal = phase + } + bySecondary.set(phase.secondary, existing) + } + + result.push({ + primary, + secondaries: [...bySecondary.entries()] + .sort(([a], [b]) => a - b) + .map(([secondary, pair]) => ({ secondary, ...pair })), + }) + } + + return result +} + +export function RankingSelector({ phases, school, year }: Props) { + const columnsData = useMemo(() => { + return LANGUAGES.map((config) => ({ + config, + groups: groupPhases(phases, config.lang), + })).filter((c) => c.groups.length > 0) + }, [phases]) + + const gridCols = columnsData.length + + return ( +
+ {/* Columns grid */} +
+ {columnsData.map(({ config, groups }) => ( + + ))} +
+ + {/* Summary */} +

+ {phases.length} graduatorie disponibili +

+
+ ) +} + +function LanguageColumn({ + config, + groups, + school, + year, +}: { + config: LanguageConfig + groups: PhaseGroup[] + school: School + year: number +}) { + // const hasMultiplePhases = groups.length > 1 + + return ( +
+ {/* Column header */} +
+ {config.title} + {config.flag} +
+ + {/* Content with phase groups */} +
+ {groups.map((group) => ( +
+ {/* Phase separator - only show if multiple phases */} +
+ {phaseGroupLabel(group.primary)} +
+ + {/* Rankings rows - normal first, then extra-EU */} + +
+ ))} +
+
+ ) +} + +function PhaseRankings({ + group, + config, + school, + year, +}: { + group: PhaseGroup + config: LanguageConfig + school: School + year: number +}) { + const normalPhases = group.secondaries + .filter((s) => s.normal) + .map((s) => s.normal as PhaseLink) + const extraEuPhases = group.secondaries + .filter((s) => s.extraEu) + .map((s) => s.extraEu as PhaseLink) + + return ( +
+ {/* Normal rankings row */} + {normalPhases.length > 0 && ( +
+ {normalPhases.map((phase) => ( + + {getRankingLabel(phase)} + + ))} +
+ )} + + {/* Extra-EU rankings row with dashed border and label */} + {extraEuPhases.length > 0 && ( +
+ {/* Label */} + + Extra-UE + + {/* Buttons */} +
+ {extraEuPhases.map((phase) => ( + + {getRankingLabel(phase)} + + ))} +
+
+ )} +
+ ) +} + +function getRankingLabel(phase: PhaseLink) { + if (phase.secondary === 0) { + return phase.stripped || "Graduatoria" + } + return `${numberToRoman(phase.secondary)}` +} diff --git a/src/components/Homepage/SchoolCard.tsx b/src/components/Homepage/SchoolCard.tsx new file mode 100644 index 00000000..bf010a4b --- /dev/null +++ b/src/components/Homepage/SchoolCard.tsx @@ -0,0 +1,80 @@ +import { Link } from "@tanstack/react-router" +import type { School } from "@/utils/types/data/school" +import { cn } from "@/utils/ui" + +type SchoolConfig = { + icon: string + description: string + gradient: string + hoverGradient: string +} + +const SCHOOL_CONFIG: Record = { + Architettura: { + icon: "🏛️", + description: "Progettazione architettonica e spaziale", + gradient: "from-amber-500/10 to-orange-500/10", + hoverGradient: "hover:from-amber-500/20 hover:to-orange-500/20", + }, + Design: { + icon: "🎨", + description: "Design del prodotto e comunicazione", + gradient: "from-pink-500/10 to-purple-500/10", + hoverGradient: "hover:from-pink-500/20 hover:to-purple-500/20", + }, + Ingegneria: { + icon: "⚙️", + description: "Scienze e tecnologie ingegneristiche", + gradient: "from-blue-500/10 to-cyan-500/10", + hoverGradient: "hover:from-blue-500/20 hover:to-cyan-500/20", + }, + Urbanistica: { + icon: "🏙️", + description: "Pianificazione urbana e territoriale", + gradient: "from-green-500/10 to-emerald-500/10", + hoverGradient: "hover:from-green-500/20 hover:to-emerald-500/20", + }, +} + +type Props = { + school: School + yearCount?: number +} + +export function SchoolCard({ school, yearCount }: Props) { + const config = SCHOOL_CONFIG[school] + + return ( + +
+
+ + {config.icon} + + {yearCount !== undefined && ( + + {yearCount} {yearCount === 1 ? "anno" : "anni"} + + )} +
+
+

+ {school} +

+

+ {config.description} +

+
+
+
+ + ) +} diff --git a/src/index.css b/src/index.css index ba8249e2..23a811ed 100644 --- a/src/index.css +++ b/src/index.css @@ -62,7 +62,7 @@ } a { - @apply text-blue-800 underline-offset-2 hover:underline dark:text-blue-300; + @apply text-blue-800 dark:text-blue-300; } button { From 22d7972e1e8209d8ffdec094b3196bc3de93a083 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 21:22:49 +0100 Subject: [PATCH 02/13] style: mobile layout for year select, some ranking picker changes --- src/app/_home/$school/$year/index.tsx | 2 +- src/app/_home/$school/index.tsx | 89 +++++++++------------ src/components/Homepage/RankingSelector.tsx | 2 - src/components/ui/badge.tsx | 1 + 4 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/app/_home/$school/$year/index.tsx b/src/app/_home/$school/$year/index.tsx index a375edc6..6a858916 100644 --- a/src/app/_home/$school/$year/index.tsx +++ b/src/app/_home/$school/$year/index.tsx @@ -39,7 +39,7 @@ function RouteComponent() { Seleziona graduatoria

- Filtra e scegli la graduatoria da consultare per{" "} + Scegli la graduatoria da consultare per{" "} {school} {year}

diff --git a/src/app/_home/$school/index.tsx b/src/app/_home/$school/index.tsx index e712b6a0..00c17be8 100644 --- a/src/app/_home/$school/index.tsx +++ b/src/app/_home/$school/index.tsx @@ -27,62 +27,53 @@ function RouteComponent() { .map(Number) .sort((a, b) => b - a) - const currentYear = new Date().getFullYear() - return ( -
-
-

- Anno di immatricolazione -

-

- Seleziona l'anno accademico di cui vuoi consultare le graduatorie -

-
+
+

+ Anno di immatricolazione +

+

+ Seleziona l'anno accademico di cui vuoi consultare le graduatorie +

+
-
- {years.map((year, index) => { - const isRecent = year >= currentYear - 1 - const isFirst = index === 0 +
+ {years.map((year, index) => { + const isFirst = index === 0 - return ( - + - - {year} - {isRecent && ( - - )} - - - ) - })} -
- - {years.length > 0 && ( -

- {years.length}{" "} - {years.length === 1 ? "anno disponibile" : "anni disponibili"} dal{" "} - {years[years.length - 1]} al {years[0]} -

- )} + {year} + + + ) + })}
+ + {years.length > 0 && ( +

+ {years.length}{" "} + {years.length === 1 ? "anno disponibile" : "anni disponibili"} dal{" "} + {years[years.length - 1]} al {years[0]} +

+ )} ) } diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index bb635efa..7c2ffe5d 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -150,8 +150,6 @@ function LanguageColumn({ school: School year: number }) { - // const hasMultiplePhases = groups.length > 1 - return (
{/* Column header */} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index 6fff3b71..8798f09a 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -22,6 +22,7 @@ const badgeVariants = cva( tiny: "px-2.5 py-0.5 text-xs", small: "px-3 py-1 text-sm", medium: "px-4 py-1.5 text-md", + large: "px-8 py-2 text-lg", }, }, defaultVariants: { From 28b9fc4161b7cdc1429f2ba124a3f828a7f935eb Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 21:58:51 +0100 Subject: [PATCH 03/13] feat: archive tip in homepage --- src/app/_home/index.tsx | 6 ++ src/components/Homepage/ArchiveTip.tsx | 76 +++++++++++++++++++++ src/components/Homepage/RankingSelector.tsx | 2 +- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/components/Homepage/ArchiveTip.tsx diff --git a/src/app/_home/index.tsx b/src/app/_home/index.tsx index a21fd2ac..e1475b97 100644 --- a/src/app/_home/index.tsx +++ b/src/app/_home/index.tsx @@ -3,6 +3,7 @@ import { createFileRoute } from "@tanstack/react-router" import Alert from "@/components/custom-ui/Alert" import Page from "@/components/custom-ui/Page" import Spinner from "@/components/custom-ui/Spinner" +import { ArchiveTip } from "@/components/Homepage/ArchiveTip" import { SchoolCard } from "@/components/Homepage/SchoolCard" import { useQueries } from "@/hooks/use-queries" @@ -49,6 +50,11 @@ function RouteComponent() {
)} {error instanceof Error && {error.message}} + +
+
+ {data && } +
) diff --git a/src/components/Homepage/ArchiveTip.tsx b/src/components/Homepage/ArchiveTip.tsx new file mode 100644 index 00000000..a8ecae5b --- /dev/null +++ b/src/components/Homepage/ArchiveTip.tsx @@ -0,0 +1,76 @@ +import { useMemo } from "react" +import type { BySchoolYearIndex } from "@/utils/types/data/ranking" + +type Props = { + data: BySchoolYearIndex +} + +export function ArchiveTip({ data }: Props) { + const stats = useMemo(() => { + const years = new Set() + let totalRankings = 0 + + for (const schoolData of Object.values(data)) { + for (const [year, rankings] of Object.entries(schoolData)) { + years.add(Number(year)) + totalRankings += rankings.length + } + } + + const sortedYears = [...years].sort((a, b) => a - b) + + return { + yearCount: years.size, + minYear: sortedYears[0], + maxYear: sortedYears[sortedYears.length - 1], + totalRankings, + } + }, [data]) + + return ( +
+ {/* Decorative background elements */} +
+
+ +
+
+ {/* Stats line with pill badges */} +

+ L'archivio contiene{" "} + + {stats.totalRankings} graduatorie + {" "} + distribuite su{" "} + + {stats.yearCount} anni + {" "} + + ({stats.minYear} - {stats.maxYear}) + +

+ + {/* Warning with subtle styling */} +

+ + Alcune graduatorie potrebbero non essere disponibili per determinati + anni o fasi. +

+
+
+
+ ) +} diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index 7c2ffe5d..a77af5d6 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -116,7 +116,7 @@ export function RankingSelector({ phases, school, year }: Props) {
From cc5489bb584faa76523ce85729669fa4e508ce9d Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 21:59:13 +0100 Subject: [PATCH 04/13] chore: biome --- src/app/_home/$school/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/_home/$school/index.tsx b/src/app/_home/$school/index.tsx index 00c17be8..988b0596 100644 --- a/src/app/_home/$school/index.tsx +++ b/src/app/_home/$school/index.tsx @@ -39,7 +39,7 @@ function RouteComponent() {

-
+
{years.map((year, index) => { const isFirst = index === 0 @@ -54,7 +54,7 @@ function RouteComponent() { size="large" variant="secondary" className={cn( - "cursor-pointer border transition-all hover:bg-slate-200 dark:hover:bg-slate-700/80 justify-center w-full", + "w-full cursor-pointer justify-center border transition-all hover:bg-slate-200 dark:hover:bg-slate-700/80", isFirst ? "border-amber-700/30 shadow-[0px_2px_10px_#ffae1082,inset_0px_-4px_20px_#ffb52575] dark:border-amber-200/90 dark:shadow-[0px_3px_8px_rgba(186,130,21,0.32),inset_0px_-4px_20px_rgba(186,130,21,0.43)]" : "" From 48700f84988544bfc73e3e851f57268f858e7a63 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 22:02:41 +0100 Subject: [PATCH 05/13] fix: design subtitle was too wrong --- src/components/Homepage/SchoolCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Homepage/SchoolCard.tsx b/src/components/Homepage/SchoolCard.tsx index bf010a4b..72d0b38e 100644 --- a/src/components/Homepage/SchoolCard.tsx +++ b/src/components/Homepage/SchoolCard.tsx @@ -18,7 +18,7 @@ const SCHOOL_CONFIG: Record = { }, Design: { icon: "🎨", - description: "Design del prodotto e comunicazione", + description: "Interni, prodotto industriale, comunicazione e moda", gradient: "from-pink-500/10 to-purple-500/10", hoverGradient: "hover:from-pink-500/20 hover:to-purple-500/20", }, From da328c09a48aa5d9c189e5b9c9f772674023abea Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 22:25:24 +0100 Subject: [PATCH 06/13] feat: add phase info in breadcrumb --- src/components/PathBreadcrumb.tsx | 34 ++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/PathBreadcrumb.tsx b/src/components/PathBreadcrumb.tsx index 03fbfe28..8c2d2d07 100644 --- a/src/components/PathBreadcrumb.tsx +++ b/src/components/PathBreadcrumb.tsx @@ -1,18 +1,28 @@ +import { useQueries } from "@/hooks/use-queries" +import { useSuspenseQuery } from "@tanstack/react-query" import { Link, useParams } from "@tanstack/react-router" import { LuArrowRight, LuHouse } from "react-icons/lu" +import { Badge } from "./ui/badge" +import { numberToRoman } from "@/utils/strings/numbers" +import PhaseFlag from "./custom-ui/PhaseFlag" +import { cn } from "@/utils/ui" export default function PathBreadcrumb() { const params = useParams({ shouldThrow: false, strict: false, - select({ school, year }) { - return { school, year } + select({ school, year, id }) { + return { school, year, id } }, }) + if (!params || !params.school) return null const { school, year } = params + const q = useQueries() + const ranking = params.id ? useSuspenseQuery(q.ranking(params.id)) : null + return (
@@ -30,6 +40,24 @@ export default function PathBreadcrumb() { )} -
+ {ranking && ranking.data && ( + <> + + + + + {ranking.data.phase.primary === 0 ? "Fase Generale" : `${numberToRoman(ranking.data.phase.primary)} Fase`} + + + {numberToRoman(ranking.data.phase.secondary)} Grad. + + {ranking.data.phase.isExtraEu && Extra-EU} + + + ) + } +
) } From 6bac6a0c3b9ce45f1e855f202447e07d391062ad Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 22:33:12 +0100 Subject: [PATCH 07/13] fix: react rules and I am bad --- src/app/_home/$school/$year/$id/index.tsx | 2 +- src/components/PathBreadcrumb.tsx | 82 ++++++++++++++++------- src/utils/errors.ts | 4 +- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/app/_home/$school/$year/$id/index.tsx b/src/app/_home/$school/$year/$id/index.tsx index a20cf8f5..db42b493 100644 --- a/src/app/_home/$school/$year/$id/index.tsx +++ b/src/app/_home/$school/$year/$id/index.tsx @@ -178,7 +178,7 @@ function RouteComponent() { if (error) return ( - Error: {error.message} + {error.message} ) return diff --git a/src/components/PathBreadcrumb.tsx b/src/components/PathBreadcrumb.tsx index 8c2d2d07..5efc4366 100644 --- a/src/components/PathBreadcrumb.tsx +++ b/src/components/PathBreadcrumb.tsx @@ -1,11 +1,12 @@ -import { useQueries } from "@/hooks/use-queries" import { useSuspenseQuery } from "@tanstack/react-query" import { Link, useParams } from "@tanstack/react-router" +import { Suspense } from "react" import { LuArrowRight, LuHouse } from "react-icons/lu" -import { Badge } from "./ui/badge" +import { useQueries } from "@/hooks/use-queries" import { numberToRoman } from "@/utils/strings/numbers" -import PhaseFlag from "./custom-ui/PhaseFlag" import { cn } from "@/utils/ui" +import PhaseFlag from "./custom-ui/PhaseFlag" +import { Badge } from "./ui/badge" export default function PathBreadcrumb() { const params = useParams({ @@ -16,13 +17,9 @@ export default function PathBreadcrumb() { }, }) - if (!params || !params.school) return null const { school, year } = params - const q = useQueries() - const ranking = params.id ? useSuspenseQuery(q.ranking(params.id)) : null - return (
@@ -40,24 +37,59 @@ export default function PathBreadcrumb() { )} - {ranking && ranking.data && ( - <> - - - - - {ranking.data.phase.primary === 0 ? "Fase Generale" : `${numberToRoman(ranking.data.phase.primary)} Fase`} - - - {numberToRoman(ranking.data.phase.secondary)} Grad. - - {ranking.data.phase.isExtraEu && Extra-EU} + {params.id && ( + + + + )} +
+ ) +} + +function RankingInfo({ id }: { id: string }) { + const q = useQueries() + const ranking = useSuspenseQuery(q.ranking(id)) + + if (ranking.error) return null + + return ( + <> + + + + + {ranking.data.phase.primary === 0 + ? "Fase Generale" + : `${numberToRoman(ranking.data.phase.primary)} Fase`} + + + {numberToRoman(ranking.data.phase.secondary)} Grad. + + {ranking.data.phase.isExtraEu && ( + + Extra-EU - - ) - } -
+ )} + + ) } diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 0e1f90f3..9105f4ce 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1 +1,3 @@ -export class NotFoundError extends Error {} +export class NotFoundError extends Error { + message = "Not Found" +} From a81c3aed9bcd5b07301a72768edf1169de5210f2 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 22:36:32 +0100 Subject: [PATCH 08/13] fix: better label for ranking without secondary order --- src/components/Homepage/RankingSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index a77af5d6..17c6db04 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -261,7 +261,7 @@ function PhaseRankings({ function getRankingLabel(phase: PhaseLink) { if (phase.secondary === 0) { - return phase.stripped || "Graduatoria" + return "Generica" } return `${numberToRoman(phase.secondary)}` } From efb50ccbfbc0a3e4fdf606e756ce2c9780d81b57 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 23:02:32 +0100 Subject: [PATCH 09/13] feat: move warning to select-id phase, style fixes, add tooltip comp --- package.json | 1 + pnpm-lock.yaml | 36 +++++++++++ src/app/__root.tsx | 17 ++--- src/app/_home/index.tsx | 6 +- src/components/Homepage/ArchiveTip.tsx | 70 ++++++++++----------- src/components/Homepage/RankingSelector.tsx | 40 ++++++++---- src/components/Homepage/SchoolCard.tsx | 8 +-- src/components/PathBreadcrumb.tsx | 2 +- src/components/ui/tooltip.tsx | 30 +++++++++ src/utils/phase.ts | 2 +- 10 files changed, 142 insertions(+), 70 deletions(-) create mode 100644 src/components/ui/tooltip.tsx diff --git a/package.json b/package.json index fd5d14d0..34993dd8 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", + "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/vite": "^4.1.17", "@tanstack/react-query": "^5.90.12", "@tanstack/react-router": "^1.139.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b1d2ae2..a29254a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@radix-ui/react-toggle-group': specifier: ^1.1.11 version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-tooltip': + specifier: ^1.2.8 + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) '@tailwindcss/vite': specifier: ^4.1.17 version: 4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) @@ -974,6 +977,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.1': resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: @@ -2742,6 +2758,26 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + optionalDependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.2.1)': dependencies: react: 19.2.1 diff --git a/src/app/__root.tsx b/src/app/__root.tsx index 7ed3b464..94406559 100644 --- a/src/app/__root.tsx +++ b/src/app/__root.tsx @@ -6,6 +6,7 @@ import DevSettings from "@/components/DevSettings" import Layout from "@/components/Layout" import { Toaster } from "@/components/ui/sonner" import ContextProvider from "@/contexts/ContextProvider" +import { TooltipProvider } from "@/components/ui/tooltip" export const queryClient = new QueryClient() export const Route = createRootRoute({ @@ -19,14 +20,16 @@ function RootComponent() { - - - + + + + - {import.meta.env.DEV && } - - - + {import.meta.env.DEV && } + + + + diff --git a/src/app/_home/index.tsx b/src/app/_home/index.tsx index e1475b97..fca893e0 100644 --- a/src/app/_home/index.tsx +++ b/src/app/_home/index.tsx @@ -35,11 +35,7 @@ function RouteComponent() { {schools && data && (
{schools.map((school) => ( - + ))}
)} diff --git a/src/components/Homepage/ArchiveTip.tsx b/src/components/Homepage/ArchiveTip.tsx index a8ecae5b..ab8c6936 100644 --- a/src/components/Homepage/ArchiveTip.tsx +++ b/src/components/Homepage/ArchiveTip.tsx @@ -28,48 +28,46 @@ export function ArchiveTip({ data }: Props) { }, [data]) return ( -
+
{/* Decorative background elements */}
-
- {/* Stats line with pill badges */} -

- L'archivio contiene{" "} - - {stats.totalRankings} graduatorie - {" "} - distribuite su{" "} - - {stats.yearCount} anni - {" "} - - ({stats.minYear} - {stats.maxYear}) - -

+ {/* Stats line with pill badges */} +

+ L'archivio contiene{" "} + + {stats.totalRankings} graduatorie + {" "} + distribuite su{" "} + + {stats.yearCount} anni + {" "} + + ({stats.minYear} - {stats.maxYear}) + +

- {/* Warning with subtle styling */} -

- - Alcune graduatorie potrebbero non essere disponibili per determinati - anni o fasi. -

-
+ {/* Warning with subtle styling -- TODO: hidden for now, maybe readd it later (hidden -> flex) */} +

+ + Alcune graduatorie potrebbero non essere disponibili per determinati + anni o fasi. +

) diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index 17c6db04..fb4b948a 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -5,6 +5,7 @@ import { numberToRoman } from "@/utils/strings/numbers" import type { PhaseLink } from "@/utils/types/data/phase" import type { School } from "@/utils/types/data/school" import { cn } from "@/utils/ui" +import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip" type Props = { phases: PhaseLink[] @@ -108,18 +109,10 @@ export function RankingSelector({ phases, school, year }: Props) { })).filter((c) => c.groups.length > 0) }, [phases]) - const gridCols = columnsData.length - return (
{/* Columns grid */} -
+
{columnsData.map(({ config, groups }) => ( {/* Summary */} -

- {phases.length} graduatorie disponibili -

+
+

+ {phases.length} graduatorie disponibili in totale.{" "} +

+ + + Perché non trovo una graduatoria? + + +

+ Il Politecnico ha cambiato negli anni le modalità di pubblicazione + delle graduatorie, oltre a eliminare molto rapidamente i file + grezzi dai server pubblici, rendendo malfunzionante il nostro + script di scraping e parsing.
Durante i processi (lenti) di + riscrittura/manutenzione dello script, alcune di queste + graduatorie sono andate perse o mai individuate perché già + eliminate. +

+
+
+
) } @@ -230,7 +244,7 @@ function PhaseRankings({
)} - {/* Extra-EU rankings row with dashed border and label */} + {/* Extra-UE rankings row with dashed border and label */} {extraEuPhases.length > 0 && (
{/* Label */} diff --git a/src/components/Homepage/SchoolCard.tsx b/src/components/Homepage/SchoolCard.tsx index 72d0b38e..d366a1bd 100644 --- a/src/components/Homepage/SchoolCard.tsx +++ b/src/components/Homepage/SchoolCard.tsx @@ -38,10 +38,9 @@ const SCHOOL_CONFIG: Record = { type Props = { school: School - yearCount?: number } -export function SchoolCard({ school, yearCount }: Props) { +export function SchoolCard({ school }: Props) { const config = SCHOOL_CONFIG[school] return ( @@ -59,11 +58,6 @@ export function SchoolCard({ school, yearCount }: Props) { {config.icon} - {yearCount !== undefined && ( - - {yearCount} {yearCount === 1 ? "anno" : "anni"} - - )}

diff --git a/src/components/PathBreadcrumb.tsx b/src/components/PathBreadcrumb.tsx index 5efc4366..5bd9a4a2 100644 --- a/src/components/PathBreadcrumb.tsx +++ b/src/components/PathBreadcrumb.tsx @@ -86,7 +86,7 @@ function RankingInfo({ id }: { id: string }) { className="dark:border-amber-500 dark:text-amber-500" variant="outline" > - Extra-EU + Extra-UE )} diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx new file mode 100644 index 00000000..40709ae5 --- /dev/null +++ b/src/components/ui/tooltip.tsx @@ -0,0 +1,30 @@ +import * as React from "react" +import * as TooltipPrimitive from "@radix-ui/react-tooltip" + +import { cn } from "@/utils/ui.ts" + +const TooltipProvider = TooltipPrimitive.Provider + +const Tooltip = TooltipPrimitive.Root + +const TooltipTrigger = TooltipPrimitive.Trigger + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +TooltipContent.displayName = TooltipPrimitive.Content.displayName + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/src/utils/phase.ts b/src/utils/phase.ts index a135965e..86763492 100644 --- a/src/utils/phase.ts +++ b/src/utils/phase.ts @@ -26,4 +26,4 @@ export const phaseGroupLabel = (primary: number) => export const phaseLinkLabel = (p: PhaseLink) => p.secondary === 0 ? p.stripped - : `${numberToRoman(p.secondary)} Graduatoria ${p.isExtraEu ? "(Extra-EU)" : ""}`.trimEnd() + : `${numberToRoman(p.secondary)} Graduatoria ${p.isExtraEu ? "(Extra-UE)" : ""}`.trimEnd() From 91b02a06d34485bce818fd3cba7b8e752af9b74d Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 23:04:47 +0100 Subject: [PATCH 10/13] chore: biome --- src/app/__root.tsx | 2 +- src/components/Homepage/RankingSelector.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/__root.tsx b/src/app/__root.tsx index 94406559..6ac04091 100644 --- a/src/app/__root.tsx +++ b/src/app/__root.tsx @@ -5,8 +5,8 @@ import { IconContext } from "react-icons" import DevSettings from "@/components/DevSettings" import Layout from "@/components/Layout" import { Toaster } from "@/components/ui/sonner" -import ContextProvider from "@/contexts/ContextProvider" import { TooltipProvider } from "@/components/ui/tooltip" +import ContextProvider from "@/contexts/ContextProvider" export const queryClient = new QueryClient() export const Route = createRootRoute({ diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index fb4b948a..0a7eec9a 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -125,7 +125,7 @@ export function RankingSelector({ phases, school, year }: Props) {

{/* Summary */} -
+

{phases.length} graduatorie disponibili in totale.{" "}

From 83cbdcaf4cb4805591ae251263dc68fa158539b6 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 23:26:06 +0100 Subject: [PATCH 11/13] style: make claude generated style a lil better --- src/app/_home/$school/index.tsx | 2 +- src/app/_home/index.tsx | 2 +- src/components/Homepage/RankingSelector.tsx | 28 ++++++--------------- src/components/Homepage/SchoolCard.tsx | 6 ++++- src/index.css | 2 +- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/app/_home/$school/index.tsx b/src/app/_home/$school/index.tsx index 988b0596..f59a7a9b 100644 --- a/src/app/_home/$school/index.tsx +++ b/src/app/_home/$school/index.tsx @@ -54,7 +54,7 @@ function RouteComponent() { size="large" variant="secondary" className={cn( - "w-full cursor-pointer justify-center border transition-all hover:bg-slate-200 dark:hover:bg-slate-700/80", + "w-full cursor-pointer justify-center border transition-all hover:bg-slate-200 hover:no-underline dark:hover:bg-slate-700/80", isFirst ? "border-amber-700/30 shadow-[0px_2px_10px_#ffae1082,inset_0px_-4px_20px_#ffb52575] dark:border-amber-200/90 dark:shadow-[0px_3px_8px_rgba(186,130,21,0.32),inset_0px_-4px_20px_rgba(186,130,21,0.43)]" : "" diff --git a/src/app/_home/index.tsx b/src/app/_home/index.tsx index fca893e0..c2a11fac 100644 --- a/src/app/_home/index.tsx +++ b/src/app/_home/index.tsx @@ -32,7 +32,7 @@ function RouteComponent() {

- {schools && data && ( + {schools && (
{schools.map((school) => ( diff --git a/src/components/Homepage/RankingSelector.tsx b/src/components/Homepage/RankingSelector.tsx index 0a7eec9a..1f4ce422 100644 --- a/src/components/Homepage/RankingSelector.tsx +++ b/src/components/Homepage/RankingSelector.tsx @@ -20,7 +20,6 @@ type LanguageConfig = { title: string flag: string headerStyles: string - itemStyles: string separatorStyles: string } @@ -31,8 +30,6 @@ const LANGUAGES: LanguageConfig[] = [ flag: "🇮🇹", headerStyles: "bg-green-100 text-green-800 dark:bg-green-900/50 dark:text-green-300", - itemStyles: - "bg-white hover:bg-green-50 dark:bg-slate-800 dark:hover:bg-green-900/20", separatorStyles: "bg-green-50 text-green-700 dark:bg-green-900/20 dark:text-green-400", }, @@ -42,8 +39,6 @@ const LANGUAGES: LanguageConfig[] = [ flag: "🇬🇧", headerStyles: "bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300", - itemStyles: - "bg-white hover:bg-blue-50 dark:bg-slate-800 dark:hover:bg-blue-900/20", separatorStyles: "bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-400", }, @@ -165,7 +160,7 @@ function LanguageColumn({ year: number }) { return ( -
+
{/* Column header */}
{/* Rankings rows - normal first, then extra-EU */} - +
))}
@@ -207,12 +197,10 @@ function LanguageColumn({ function PhaseRankings({ group, - config, school, year, }: { group: PhaseGroup - config: LanguageConfig school: School year: number }) { @@ -234,8 +222,7 @@ function PhaseRankings({ to="/$school/$year/$id" params={{ school, year, id: phase.id }} className={cn( - "rounded-lg border border-slate-200 px-3 py-2.5 text-center font-medium text-slate-900 transition-colors dark:border-slate-600 dark:text-slate-100", - config.itemStyles + "rounded-lg border border-slate-300 px-3 py-1.5 text-center font-medium text-slate-700 transition-colors hover:bg-slate-100 hover:no-underline dark:border-transparent dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700/80" )} > {getRankingLabel(phase)} @@ -246,9 +233,9 @@ function PhaseRankings({ {/* Extra-UE rankings row with dashed border and label */} {extraEuPhases.length > 0 && ( -
+
{/* Label */} - + Extra-UE {/* Buttons */} @@ -259,8 +246,7 @@ function PhaseRankings({ to="/$school/$year/$id" params={{ school, year, id: phase.id }} className={cn( - "rounded-lg border border-slate-200 px-3 py-1.5 text-center font-medium text-slate-700 transition-colors dark:border-slate-600 dark:text-slate-300", - config.itemStyles + "rounded-lg border border-slate-300 px-3 py-1.5 text-center font-medium text-slate-700 transition-colors hover:bg-slate-100 hover:no-underline dark:border-transparent dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700/80" )} > {getRankingLabel(phase)} @@ -277,5 +263,5 @@ function getRankingLabel(phase: PhaseLink) { if (phase.secondary === 0) { return "Generica" } - return `${numberToRoman(phase.secondary)}` + return numberToRoman(phase.secondary) } diff --git a/src/components/Homepage/SchoolCard.tsx b/src/components/Homepage/SchoolCard.tsx index d366a1bd..8ede03bf 100644 --- a/src/components/Homepage/SchoolCard.tsx +++ b/src/components/Homepage/SchoolCard.tsx @@ -44,7 +44,11 @@ export function SchoolCard({ school }: Props) { const config = SCHOOL_CONFIG[school] return ( - +
Date: Tue, 23 Dec 2025 23:34:26 +0100 Subject: [PATCH 12/13] feat: add light-theme warning toast --- src/contexts/DarkModeContext.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/contexts/DarkModeContext.tsx b/src/contexts/DarkModeContext.tsx index 6f2bd6b9..dd2712a9 100644 --- a/src/contexts/DarkModeContext.tsx +++ b/src/contexts/DarkModeContext.tsx @@ -1,4 +1,5 @@ -import { createContext, useState } from "react" +import { createContext, useEffect, useState } from "react" +import { toast } from "sonner" import { LOCAL_STORAGE } from "@/utils/constants" export interface IDarkModeContext { @@ -52,11 +53,21 @@ export function DarkModeProvider({ ...p }: Props) { setIsDarkMode((value) => { const newValue = !value localStorage.setItem(LOCAL_STORAGE.darkMode, JSON.stringify(newValue)) + updateDOMWithTheme(newValue) return newValue }) } + useEffect(() => { + if (!isDarkMode) + toast.warning("Light mode is experimental, expect issues", { + id: "light-mode-warn", + duration: 10000, + }) + else toast.dismiss("light-mode-warn") + }, [isDarkMode]) + return ( ) From b6f37e69e1ded8c02fdede9c73f1b91bc23ab936 Mon Sep 17 00:00:00 2001 From: Lorenzo Corallo Date: Tue, 23 Dec 2025 23:44:11 +0100 Subject: [PATCH 13/13] style: add icon to archive tip component --- src/components/Homepage/ArchiveTip.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Homepage/ArchiveTip.tsx b/src/components/Homepage/ArchiveTip.tsx index ab8c6936..9744190e 100644 --- a/src/components/Homepage/ArchiveTip.tsx +++ b/src/components/Homepage/ArchiveTip.tsx @@ -1,4 +1,5 @@ import { useMemo } from "react" +import { AiOutlineBulb } from "react-icons/ai" import type { BySchoolYearIndex } from "@/utils/types/data/ranking" type Props = { @@ -33,8 +34,12 @@ export function ArchiveTip({ data }: Props) {
-
+
{/* Stats line with pill badges */} +

L'archivio contiene{" "}