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..6ac04091 100644 --- a/src/app/__root.tsx +++ b/src/app/__root.tsx @@ -5,6 +5,7 @@ import { IconContext } from "react-icons" import DevSettings from "@/components/DevSettings" import Layout from "@/components/Layout" import { Toaster } from "@/components/ui/sonner" +import { TooltipProvider } from "@/components/ui/tooltip" import ContextProvider from "@/contexts/ContextProvider" export const queryClient = new QueryClient() @@ -19,14 +20,16 @@ function RootComponent() { - - - + + + + - {import.meta.env.DEV && } - - - + {import.meta.env.DEV && } + + + + 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/app/_home/$school/$year/index.tsx b/src/app/_home/$school/$year/index.tsx index c81d3bef..6a858916 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 +

+

+ 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..f59a7a9b 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,62 @@ 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 ? ( -
- -
- ) : ( + return ( -

Scegli un anno di immatricolazione

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

+ Anno di immatricolazione +

+

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

+
+ +
+ {years.map((year, index) => { + const isFirst = index === 0 + + return ( setClicked(true)} + className="group" > - + + {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/app/_home/index.tsx b/src/app/_home/index.tsx index 33248f50..c2a11fac 100644 --- a/src/app/_home/index.tsx +++ b/src/app/_home/index.tsx @@ -1,11 +1,10 @@ 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 { ArchiveTip } from "@/components/Homepage/ArchiveTip" +import { SchoolCard } from "@/components/Homepage/SchoolCard" import { useQueries } from "@/hooks/use-queries" export const Route = createFileRoute("/_home/")({ @@ -22,40 +21,36 @@ function RouteComponent() { return ( -
-

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

-

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

+
+
+

+ Storico Graduatorie PoliMi +

+

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

+
+ {schools && ( - +
{schools.map((school) => ( - - - + ))} - +
+ )} + + {isPending && ( +
+ +
)} - {isPending && } {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..9744190e --- /dev/null +++ b/src/components/Homepage/ArchiveTip.tsx @@ -0,0 +1,79 @@ +import { useMemo } from "react" +import { AiOutlineBulb } from "react-icons/ai" +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 -- 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 new file mode 100644 index 00000000..1f4ce422 --- /dev/null +++ b/src/components/Homepage/RankingSelector.tsx @@ -0,0 +1,267 @@ +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" +import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip" + +type Props = { + phases: PhaseLink[] + school: School + year: number +} + +type Language = "IT" | "EN" + +type LanguageConfig = { + lang: Language + title: string + flag: string + headerStyles: 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", + 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", + 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]) + + return ( +
+ {/* Columns grid */} +
+ {columnsData.map(({ config, groups }) => ( + + ))} +
+ + {/* Summary */} +
+

+ {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. +

+
+
+
+
+ ) +} + +function LanguageColumn({ + config, + groups, + school, + year, +}: { + config: LanguageConfig + groups: PhaseGroup[] + school: School + year: number +}) { + 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, + school, + year, +}: { + group: PhaseGroup + 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-UE 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 "Generica" + } + return numberToRoman(phase.secondary) +} diff --git a/src/components/Homepage/SchoolCard.tsx b/src/components/Homepage/SchoolCard.tsx new file mode 100644 index 00000000..8ede03bf --- /dev/null +++ b/src/components/Homepage/SchoolCard.tsx @@ -0,0 +1,78 @@ +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: "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", + }, + 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 +} + +export function SchoolCard({ school }: Props) { + const config = SCHOOL_CONFIG[school] + + return ( + +
+
+ + {config.icon} + +
+
+

+ {school} +

+

+ {config.description} +

+
+
+
+ + ) +} diff --git a/src/components/PathBreadcrumb.tsx b/src/components/PathBreadcrumb.tsx index 03fbfe28..5bd9a4a2 100644 --- a/src/components/PathBreadcrumb.tsx +++ b/src/components/PathBreadcrumb.tsx @@ -1,12 +1,19 @@ +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 { useQueries } from "@/hooks/use-queries" +import { numberToRoman } from "@/utils/strings/numbers" +import { cn } from "@/utils/ui" +import PhaseFlag from "./custom-ui/PhaseFlag" +import { Badge } from "./ui/badge" 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 } }, }) @@ -30,6 +37,59 @@ export default function PathBreadcrumb() { )} + {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-UE + + )} + + + ) +} 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: { 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/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 ( ) diff --git a/src/index.css b/src/index.css index ba8249e2..66a78f1d 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 underline-offset-2 hover:underline; } button { 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" +} 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()