import { type ClassValue, clsx } from "clsx"; import { del } from "idb-keyval"; import { DateTime } from "luxon"; import type { NavigateFunction } from "react-router"; import { twMerge } from "tailwind-merge"; import { CACHE_KEY } from "./client"; import type { User, UserPreferences } from "./api"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } /** * === STRING UTILS === */ export function capitalizeFirstLetter(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1); } export function titleCase(str: string) { return str.replace( /\w\S*/g, (text) => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase() ); } /** * === NAVIGATION UTILS === */ // Force a reload of the page export function forceReload() { window.location.reload(); } export function goBack(navigate: NavigateFunction) { let canGoBack = false; // Check if there is a history stack to go back to try { canGoBack = window.history.length > 1; } catch (e) { canGoBack = false; } if (!canGoBack) { return navigate("/"); } else { return navigate(-1); } } /** * === COLLES UTILS === */ export const formatDate = (date: string) => { const dt = DateTime.fromISO(date).setLocale("fr"); const str = dt.toLocaleString({ weekday: "long", day: "numeric", month: "long", year: "numeric", }); return titleCase(str); }; export const formatTime = (date: string) => { const dt = DateTime.fromISO(date).setLocale("fr"); return ( dt .toLocaleString({ hour: "2-digit", minute: "2-digit", }) ?.replace(":", "h") || "N/A" ); }; export const formatGrade = (grade?: number) => { if (grade === undefined || grade === null || grade < 0 || grade > 20) return "N/A"; const rounded = Math.round(grade * 10) / 10; const str = rounded % 1 === 0 ? rounded.toFixed(0) // no decimals if .0 : rounded.toFixed(1); // one decimal otherwise return str.replace(".", ",").padStart(2, "0"); // pad with zero if needed }; // === COLORS UTILS === export const colors = [ "red", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose", "slate", "zinc", ]; export const getColorClass = (colorCode: string) => `bg-${colorCode}-100 text-${colorCode}-800 dark:bg-${colorCode}-300 dark:text-${colorCode}-900 hover:bg-${colorCode}-100 dark:hover:bg-${colorCode}-300`; // Used for Tailwind CSS to generate the classes export const classNames = [ [ "bg-red-100 text-red-800 dark:bg-red-300 dark:text-red-900 hover:bg-red-100 dark:hover:bg-red-300", "bg-orange-100 text-orange-800 dark:bg-orange-300 dark:text-orange-900 hover:bg-orange-100 dark:hover:bg-orange-300", "bg-amber-100 text-amber-800 dark:bg-amber-300 dark:text-amber-900 hover:bg-amber-100 dark:hover:bg-amber-300", "bg-yellow-100 text-yellow-800 dark:bg-yellow-300 dark:text-yellow-900 hover:bg-yellow-100 dark:hover:bg-yellow-300", "bg-lime-100 text-lime-800 dark:bg-lime-300 dark:text-lime-900 hover:bg-lime-100 dark:hover:bg-lime-300", "bg-green-100 text-green-800 dark:bg-green-300 dark:text-green-900 hover:bg-green-100 dark:hover:bg-green-300", "bg-emerald-100 text-emerald-800 dark:bg-emerald-300 dark:text-emerald-900 hover:bg-emerald-100 dark:hover:bg-emerald-300", "bg-teal-100 text-teal-800 dark:bg-teal-300 dark:text-teal-900 hover:bg-teal-100 dark:hover:bg-teal-300", "bg-cyan-100 text-cyan-800 dark:bg-cyan-300 dark:text-cyan-900 hover:bg-cyan-100 dark:hover:bg-cyan-300", "bg-sky-100 text-sky-800 dark:bg-sky-300 dark:text-sky-900 hover:bg-sky-100 dark:hover:bg-sky-300", "bg-blue-100 text-blue-800 dark:bg-blue-300 dark:text-blue-900 hover:bg-blue-100 dark:hover:bg-blue-300", "bg-indigo-100 text-indigo-800 dark:bg-indigo-300 dark:text-indigo-900 hover:bg-indigo-100 dark:hover:bg-indigo-300", "bg-violet-100 text-violet-800 dark:bg-violet-300 dark:text-violet-900 hover:bg-violet-100 dark:hover:bg-violet-300", "bg-purple-100 text-purple-800 dark:bg-purple-300 dark:text-purple-900 hover:bg-purple-100 dark:hover:bg-purple-300", "bg-fuchsia-100 text-fuchsia-800 dark:bg-fuchsia-300 dark:text-fuchsia-900 hover:bg-fuchsia-100 dark:hover:bg-fuchsia-300", "bg-pink-100 text-pink-800 dark:bg-pink-300 dark:text-pink-900 hover:bg-pink-100 dark:hover:bg-pink-300", "bg-rose-100 text-rose-800 dark:bg-rose-300 dark:text-rose-900 hover:bg-rose-100 dark:hover:bg-rose-300", "bg-slate-100 text-slate-800 dark:bg-slate-300 dark:text-slate-900 hover:bg-slate-100 dark:hover:bg-slate-300", "bg-zinc-100 text-zinc-800 dark:bg-zinc-300 dark:text-zinc-900 hover:bg-zinc-100 dark:hover:bg-zinc-300", "bg-gray-100 text-gray-800 dark:bg-gray-300 dark:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-300", ], ]; export const DEFAULT_COLOR = "blue"; // Default color for subjects export const DEFAULT_EMOJI = "📚"; // Default emoji for subjects export const getSubjectEmoji = (subject: string, pref: UserPreferences) => { const preference = pref.find((p) => p.name === subject); return preference ? preference.emoji : DEFAULT_EMOJI; }; export const getSubjectColor = (subject: string, pref: UserPreferences) => { const preference = pref.find((p) => p.name === subject); return preference ? preference.color : DEFAULT_COLOR; }; // === DEBUG UTILS === export const clearCache = () => del(CACHE_KEY);