154 lines
5.4 KiB
TypeScript
154 lines
5.4 KiB
TypeScript
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);
|