fix: use semantic colors across admin pages
Local Build and Deploy / deploy (push) Successful in 49s

This commit is contained in:
Simo committed 2026-07-12 18:50:45 +02:00
1 parent 0fe482009c
commit 84e4123f09
83 files changed
+466 -455

No files matched your search

+5 -5
View File
@@ -48,12 +48,12 @@ export default async function AdminAchievements() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Award size={20} className="text-[var(--color-primary)]" /> <Award size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t("subtitle", { rows: achievements.length, categories: groups.size })} {t("subtitle", { rows: achievements.length, categories: groups.size })}
</p> </p>
</div> </div>
@@ -72,7 +72,7 @@ export default async function AdminAchievements() {
<section key={category} className="mt-6"> <section key={category} className="mt-6">
<h2 className="admin-section-title"> <h2 className="admin-section-title">
{category}{" "} {category}{" "}
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{rows.length} {rows.length}
</span> </span>
</h2> </h2>
+4 -4
View File
@@ -19,12 +19,12 @@ export default async function EditAd({ params }: { params: Promise<{ id: string
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Megaphone size={20} className="text-[var(--color-primary)]" /> <Megaphone size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">Edit advertisement</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">Edit advertisement</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/ads">← Advertisements</Link> <Link href="/admin/ads">← Advertisements</Link>
</p> </p>
</div> </div>
+4 -4
View File
@@ -6,12 +6,12 @@ export default function NewAd() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Megaphone size={20} className="text-[var(--color-primary)]" /> <Megaphone size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">New advertisement</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">New advertisement</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/ads">← Advertisements</Link> <Link href="/admin/ads">← Advertisements</Link>
</p> </p>
</div> </div>
+4 -4
View File
@@ -23,12 +23,12 @@ export default async function AdminAds() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Megaphone size={20} className="text-[var(--color-primary)]" /> <Megaphone size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
<Link href="/admin/ads/new" className="btn btn-primary"> <Link href="/admin/ads/new" className="btn btn-primary">
+ {t("newAd")} + {t("newAd")}
+7 -7
View File
@@ -21,14 +21,14 @@ function severityColor(severity: string): string {
return "var(--color-danger)"; return "var(--color-danger)";
case "warning": case "warning":
case "warn": case "warn":
return "var(--color-primary)"; return "var(--admin-accent)";
case "success": case "success":
return "var(--color-secondary)"; return "var(--color-secondary)";
case "info": case "info":
case "notice": case "notice":
return "var(--color-accent)"; return "var(--color-accent)";
default: default:
return "var(--color-text-muted)"; return "var(--admin-text-muted)";
} }
} }
@@ -73,12 +73,12 @@ export default async function AdminAlerts() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Bell size={20} className="text-[var(--color-primary)]" /> <Bell size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -141,7 +141,7 @@ export default async function AdminAlerts() {
<Badge label={a.severity || "—"} color={severityColor(a.severity)} /> <Badge label={a.severity || "—"} color={severityColor(a.severity)} />
</td> </td>
<td> <td>
<Badge label={a.type || "—"} color="var(--color-text-muted)" /> <Badge label={a.type || "—"} color="var(--admin-text-muted)" />
</td> </td>
<td>{a.message}</td> <td>{a.message}</td>
</tr> </tr>
+7 -7
View File
@@ -49,12 +49,12 @@ export default async function AdminApplications() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<ClipboardList size={20} className="text-[var(--color-primary)]" /> <ClipboardList size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -78,21 +78,21 @@ export default async function AdminApplications() {
return ( return (
<article key={String(a.id)} className="admin-card p-3"> <article key={String(a.id)} className="admin-card p-3">
<div className="flex items-baseline justify-between gap-2 mb-1"> <div className="flex items-baseline justify-between gap-2 mb-1">
<h3 className="m-0 text-sm font-semibold text-[var(--color-text)]"> <h3 className="m-0 text-sm font-semibold text-[var(--admin-text)]">
{username ? ( {username ? (
<Link href={`/admin/users/${a.userId}`}>{username}</Link> <Link href={`/admin/users/${a.userId}`}>{username}</Link>
) : ( ) : (
<>{t("userNumber", { id: a.userId })}</> <>{t("userNumber", { id: a.userId })}</>
)} )}
</h3> </h3>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{t("rankLabel", { rank: a.rankId })} {t("rankLabel", { rank: a.rankId })}
</span> </span>
</div> </div>
<p className="text-xs theme-text-muted dark:theme-text-muted m-0 mb-2"> <p className="text-xs theme-text-muted dark:theme-text-muted m-0 mb-2">
{formatDate(a.createdAt)} {formatDate(a.createdAt)}
</p> </p>
<p className="text-sm text-[var(--color-text)] whitespace-pre-wrap m-0 mb-3">{a.content}</p> <p className="text-sm text-[var(--admin-text)] whitespace-pre-wrap m-0 mb-3">{a.content}</p>
<form action={dismissApplication}> <form action={dismissApplication}>
<input type="hidden" name="id" value={String(a.id)} /> <input type="hidden" name="id" value={String(a.id)} />
<button type="submit" className="btn btn-danger"> <button type="submit" className="btn btn-danger">
+5 -5
View File
@@ -29,17 +29,17 @@ export default async function EditArticle({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Newspaper size={20} className="text-[var(--color-primary)]" /> <Newspaper size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("form.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("form.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/articles">← {t("title")}</Link> <Link href="/admin/articles">← {t("title")}</Link>
</p> </p>
</div> </div>
</div> </div>
{error ? <p className="text-xs text-[var(--color-error-text-readable)] dark:text-[var(--color-error-text-readable)] mb-4">Error: {error}</p> : null} {error ? <p className="text-xs text-[var(--admin-error)] dark:text-[var(--admin-error)] mb-4">Error: {error}</p> : null}
<ArticleForm <ArticleForm
action={async (fd) => { action={async (fd) => {
"use server"; "use server";
+5 -5
View File
@@ -11,17 +11,17 @@ export default async function NewArticle({ searchParams }: { searchParams: Promi
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Newspaper size={20} className="text-[var(--color-primary)]" /> <Newspaper size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("form.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("form.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/articles">← {t("title")}</Link> <Link href="/admin/articles">← {t("title")}</Link>
</p> </p>
</div> </div>
</div> </div>
{error ? <p className="text-xs text-[var(--color-error-text-readable)] dark:text-[var(--color-error-text-readable)] mb-4">Error: {error}</p> : null} {error ? <p className="text-xs text-[var(--admin-error)] dark:text-[var(--admin-error)] mb-4">Error: {error}</p> : null}
<ArticleForm action={createArticle} /> <ArticleForm action={createArticle} />
</main> </main>
); );
+5 -5
View File
@@ -23,19 +23,19 @@ export default async function AdminArticles({ searchParams }: { searchParams: Pr
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Newspaper size={20} className="text-[var(--color-primary)]" /> <Newspaper size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
<Link href="/admin/articles/new" className="btn btn-primary"> <Link href="/admin/articles/new" className="btn btn-primary">
+ {t("newArticle")} + {t("newArticle")}
</Link> </Link>
</div> </div>
{error ? <p className="text-xs text-[var(--color-error-text-readable)] dark:text-[var(--color-error-text-readable)] mb-4">Error: {error}</p> : null} {error ? <p className="text-xs text-[var(--admin-error)] dark:text-[var(--admin-error)] mb-4">Error: {error}</p> : null}
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6"> <div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
<StatusCard label={t("articlesShown")} value={articles.length} icon="📰" /> <StatusCard label={t("articlesShown")} value={articles.length} icon="📰" />
+7 -7
View File
@@ -29,29 +29,29 @@ export default async function AdminBadges({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Award size={20} className="text-[var(--color-primary)]" /> <Award size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
{uploaded && ( {uploaded && (
<div className="mb-4 p-3 rounded-lg bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] dark:text-[var(--color-success-text-readable)] text-xs"> <div className="mb-4 p-3 rounded-lg bg-[var(--admin-success-subtle)] text-[var(--admin-success)] dark:text-[var(--admin-success)] text-xs">
{t("uploadedMessage", { code: uploaded })} {t("uploadedMessage", { code: uploaded })}
</div> </div>
)} )}
{error && ( {error && (
<div className="mb-4 p-3 rounded-lg bg-[var(--color-error-subtle)] text-[var(--color-error-text-readable)] dark:text-[var(--color-error-text-readable)] text-xs"> <div className="mb-4 p-3 rounded-lg bg-[var(--admin-error-subtle)] text-[var(--admin-error)] dark:text-[var(--admin-error)] text-xs">
{t("errorMessage", { error })} {t("errorMessage", { error })}
</div> </div>
)} )}
<section className="mt-6"> <section className="mt-6">
<h2 className="admin-section-title">{t("uploadTitle")}</h2> <h2 className="admin-section-title">{t("uploadTitle")}</h2>
<p className="text-xs text-[var(--color-text-muted)] -mt-2 mb-3">{t("uploadSubtitle")}</p> <p className="text-xs text-[var(--admin-text-muted)] -mt-2 mb-3">{t("uploadSubtitle")}</p>
<form action={uploadBadge} className="admin-card"> <form action={uploadBadge} className="admin-card">
<div className="flex gap-2 flex-wrap items-center"> <div className="flex gap-2 flex-wrap items-center">
<input type="file" name="file" accept=".gif,.png" required /> <input type="file" name="file" accept=".gif,.png" required />
+7 -7
View File
@@ -31,12 +31,12 @@ export default async function AdminBans() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Ban size={20} className="text-[var(--color-primary)]" /> <Ban size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -57,7 +57,7 @@ export default async function AdminBans() {
</div> </div>
<form action={createBan} className="admin-card mb-6"> <form action={createBan} className="admin-card mb-6">
<h3 className="text-sm font-bold text-[var(--color-text)] m-0 mb-3">{t("newBan")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] m-0 mb-3">{t("newBan")}</h3>
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<input name="userId" type="number" min={1} placeholder={t("userIdPlaceholder")} required /> <input name="userId" type="number" min={1} placeholder={t("userIdPlaceholder")} required />
<select name="type"> <select name="type">
@@ -97,14 +97,14 @@ export default async function AdminBans() {
<tr key={b.id}> <tr key={b.id}>
<td>{b.userId}</td> <td>{b.userId}</td>
<td> <td>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{b.type} {b.type}
</span> </span>
</td> </td>
<td>{b.banReason || "—"}</td> <td>{b.banReason || "—"}</td>
<td> <td>
{b.banExpire === 0 ? ( {b.banExpire === 0 ? (
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-error-subtle)] text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-error-subtle)] text-[var(--admin-text)]">
{t("permanent")} {t("permanent")}
</span> </span>
) : ( ) : (
+1 -1
View File
@@ -132,7 +132,7 @@ export default async function AdminCalendarCampaign({ params }: { params: Promis
</thead> </thead>
<tbody> <tbody>
{rewards.map((r) => ( {rewards.map((r) => (
<tr key={r.id} className="border-t border-[var(--border-subtle)]"> <tr key={r.id} className="border-t border-[var(--admin-border)]">
<td>{r.id}</td> <td>{r.id}</td>
<td> <td>
{r.productName || <span className="text-sm theme-text-muted dark:theme-text-muted">—</span>} {r.productName || <span className="text-sm theme-text-muted dark:theme-text-muted">—</span>}
+5 -5
View File
@@ -59,12 +59,12 @@ export default async function AdminCalendar() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Calendar size={20} className="text-[var(--color-primary)]" /> <Calendar size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t("subtitle", { count: campaigns.length })} {t("subtitle", { count: campaigns.length })}
</p> </p>
</div> </div>
@@ -100,7 +100,7 @@ export default async function AdminCalendar() {
<div className="flex items-center justify-between gap-2 mb-1"> <div className="flex items-center justify-between gap-2 mb-1">
<h3 className="m-0">{c.name || `#${c.id}`}</h3> <h3 className="m-0">{c.name || `#${c.id}`}</h3>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${enabled ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${enabled ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{enabled ? t("statusEnabled") : t("statusDisabled")} {enabled ? t("statusEnabled") : t("statusDisabled")}
</span> </span>
@@ -82,7 +82,7 @@ export function CatalogDetailTabs({
const furniSet = new Set(furniDataIdList) const furniSet = new Set(furniDataIdList)
const missing = baseItems.filter((b) => !furniSet.has(b.id)).length const missing = baseItems.filter((b) => !furniSet.has(b.id)).length
return missing > 0 ? ( return missing > 0 ? (
<span className="ml-1 rounded-full bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)] text-[10px] px-1.5 py-0.5 leading-none font-medium"> <span className="ml-1 rounded-full bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)] text-[10px] px-1.5 py-0.5 leading-none font-medium">
{missing} {missing}
</span> </span>
) : null ) : null
@@ -943,7 +943,7 @@ export function CatalogItemsTable({
{/* Unsaved changes banner */} {/* Unsaved changes banner */}
{hasUnsavedChanges && ( {hasUnsavedChanges && (
<div className="flex items-center gap-2 rounded-md border border-[var(--color-warning-border)] bg-[var(--color-warning-subtle)] px-3 py-2 text-sm text-[var(--color-warning-text-readable)]"> <div className="flex items-center gap-2 rounded-md border border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)] px-3 py-2 text-sm text-[var(--admin-warning)]">
<span className="flex-1"> <span className="flex-1">
You have unsaved changes ( You have unsaved changes (
{modifiedItems.length > 0 ? `${modifiedItems.length} item(s) modified` : ''} {modifiedItems.length > 0 ? `${modifiedItems.length} item(s) modified` : ''}
@@ -1051,7 +1051,7 @@ export function CatalogItemsTable({
<SortableRow <SortableRow
key={item.id} key={item.id}
id={item.id} id={item.id}
className={cn(modified && 'bg-[var(--color-warning-subtle)] dark:bg-[var(--color-warning-subtle)]')} className={cn(modified && 'bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]')}
> >
{({ dragHandleProps }) => ( {({ dragHandleProps }) => (
<> <>
@@ -1111,7 +1111,7 @@ export function CatalogItemsTable({
className="h-8 text-xs w-16" className="h-8 text-xs w-16"
title="Credits" title="Credits"
/> />
<span className="text-xs text-[var(--color-warning-text-readable)]">c</span> <span className="text-xs text-[var(--admin-warning)]">c</span>
<Input <Input
type="number" type="number"
value={edit?.costPoints ?? item.costPoints} value={edit?.costPoints ?? item.costPoints}
@@ -1132,7 +1132,7 @@ export function CatalogItemsTable({
) : ( ) : (
<div className="text-xs"> <div className="text-xs">
{item.costCredits > 0 && ( {item.costCredits > 0 && (
<span className="text-[var(--color-warning-text-readable)]">{item.costCredits}c </span> <span className="text-[var(--admin-warning)]">{item.costCredits}c </span>
)} )}
{item.costPoints > 0 && ( {item.costPoints > 0 && (
<span className={pts.color}> <span className={pts.color}>
@@ -1191,7 +1191,7 @@ export function CatalogItemsTable({
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7 text-[var(--color-success-text-readable)]" className="h-7 w-7 text-[var(--admin-success)]"
onClick={() => handleSaveOne(item)} onClick={() => handleSaveOne(item)}
disabled={isSaving || isPending} disabled={isSaving || isPending}
title="Save this item" title="Save this item"
@@ -213,7 +213,7 @@ export function MovePageCombobox({
value === page.id ? 'opacity-100' : 'opacity-0', value === page.id ? 'opacity-100' : 'opacity-0',
)} )}
/> />
<Star className="mr-1.5 h-3 w-3 text-[var(--color-warning-text-readable)]" /> <Star className="mr-1.5 h-3 w-3 text-[var(--admin-warning)]" />
<span className="text-muted-foreground font-mono text-xs mr-1.5"> <span className="text-muted-foreground font-mono text-xs mr-1.5">
#{page.id} #{page.id}
</span> </span>
@@ -55,12 +55,12 @@ export interface InlineEdit {
} }
export const POINTS_TYPES: Record<number, { label: string; color: string }> = { export const POINTS_TYPES: Record<number, { label: string; color: string }> = {
0: { label: 'Duckets', color: 'text-[var(--color-info-text-readable)]' }, 0: { label: 'Duckets', color: 'text-[var(--admin-info)]' },
5: { label: 'Diamonds', color: 'text-purple-500' }, 5: { label: 'Diamonds', color: 'text-[var(--admin-accent-text)]' },
101: { label: 'Snowflakes', color: 'text-[var(--color-info-text-readable)]' }, 101: { label: 'Snowflakes', color: 'text-[var(--admin-info)]' },
103: { label: 'Hearts', color: 'text-pink-500' }, 103: { label: 'Hearts', color: 'text-[var(--admin-error)]' },
104: { label: 'Stars', color: 'text-[var(--color-warning-text-readable)]' }, 104: { label: 'Stars', color: 'text-[var(--admin-warning)]' },
105: { label: 'Shells', color: 'text-[var(--color-warning-text-readable)]' }, 105: { label: 'Shells', color: 'text-[var(--admin-warning)]' },
} }
export const DEFAULT_NEW_ITEM = { export const DEFAULT_NEW_ITEM = {
@@ -271,7 +271,7 @@ export function CatalogTranslateTab({
</span> </span>
</div> </div>
{missingItems.length > 0 && ( {missingItems.length > 0 && (
<div className="flex items-center gap-1.5 mt-2 text-xs text-[var(--color-warning-text-readable)] dark:text-[var(--color-warning-text-readable)]"> <div className="flex items-center gap-1.5 mt-2 text-xs text-[var(--admin-warning)] dark:text-[var(--admin-warning)]">
<AlertTriangle className="h-3.5 w-3.5" /> <AlertTriangle className="h-3.5 w-3.5" />
<span> <span>
{missingItems.length} item(s) missing from FurnitureData.json — will be {missingItems.length} item(s) missing from FurnitureData.json — will be
@@ -399,9 +399,9 @@ export function CatalogTranslateTab({
key={item.id} key={item.id}
className={ className={
isChanged isChanged
? 'bg-[var(--color-warning-subtle)] dark:bg-[var(--color-warning-subtle)]' ? 'bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]'
: isMissing : isMissing
? 'bg-[var(--color-warning-subtle)] dark:bg-[var(--color-warning-subtle)]' ? 'bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]'
: undefined : undefined
} }
> >
@@ -442,7 +442,7 @@ export function CatalogTranslateTab({
value={editedNames[item.id] || ''} value={editedNames[item.id] || ''}
onChange={(e) => handleNameChange(item.id, e.target.value)} onChange={(e) => handleNameChange(item.id, e.target.value)}
disabled={!canEdit} disabled={!canEdit}
className={`h-8 text-sm ${nameChanged ? 'border-[var(--color-warning-border)] dark:border-[var(--color-warning-border)]' : ''}`} className={`h-8 text-sm ${nameChanged ? 'border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]' : ''}`}
/> />
{currentCatalogName && currentCatalogName !== item.publicName && ( {currentCatalogName && currentCatalogName !== item.publicName && (
<p className="text-[11px] text-muted-foreground mt-0.5 truncate"> <p className="text-[11px] text-muted-foreground mt-0.5 truncate">
@@ -456,7 +456,7 @@ export function CatalogTranslateTab({
onChange={(e) => handleDescChange(item.id, e.target.value)} onChange={(e) => handleDescChange(item.id, e.target.value)}
disabled={!canEdit} disabled={!canEdit}
placeholder="No description" placeholder="No description"
className={`h-8 text-sm ${descChanged ? 'border-[var(--color-warning-border)] dark:border-[var(--color-warning-border)]' : ''}`} className={`h-8 text-sm ${descChanged ? 'border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]' : ''}`}
/> />
</TableCell> </TableCell>
<TableCell> <TableCell>
@@ -464,7 +464,7 @@ export function CatalogTranslateTab({
{isChanged ? ( {isChanged ? (
<Badge <Badge
variant="secondary" variant="secondary"
className="text-xs bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)]" className="text-xs bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]"
> >
Modified Modified
</Badge> </Badge>
@@ -474,7 +474,7 @@ export function CatalogTranslateTab({
{isMissing ? ( {isMissing ? (
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<AlertTriangle className="h-3.5 w-3.5 text-[var(--color-warning-text-readable)] flex-shrink-0" /> <AlertTriangle className="h-3.5 w-3.5 text-[var(--admin-warning)] flex-shrink-0" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>Missing from FurnitureData.json</p> <p>Missing from FurnitureData.json</p>
@@ -486,7 +486,7 @@ export function CatalogTranslateTab({
) : ( ) : (
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<CheckCircle2 className="h-3.5 w-3.5 text-[var(--color-success-text-readable)] flex-shrink-0" /> <CheckCircle2 className="h-3.5 w-3.5 text-[var(--admin-success)] flex-shrink-0" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>In FurnitureData.json</TooltipContent> <TooltipContent>In FurnitureData.json</TooltipContent>
</Tooltip> </Tooltip>
@@ -124,7 +124,7 @@ export function BcPageDetail({ page, items, canEdit }: BcPageDetailProps) {
</Link> </Link>
<span className="text-muted-foreground">/</span> <span className="text-muted-foreground">/</span>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<HardHat className="h-5 w-5 text-[var(--color-warning-text-readable)]" /> <HardHat className="h-5 w-5 text-[var(--admin-warning)]" />
<h1 className="text-2xl font-bold tracking-tight">{page.caption}</h1> <h1 className="text-2xl font-bold tracking-tight">{page.caption}</h1>
<Badge variant="outline" className="font-mono text-xs"> <Badge variant="outline" className="font-mono text-xs">
#{page.id} #{page.id}
@@ -306,7 +306,7 @@ export function BcPageDetail({ page, items, canEdit }: BcPageDetailProps) {
type="button" type="button"
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-6 w-6 p-0 text-[var(--color-error-text-readable)] hover:text-[var(--color-error-text-readable)]" className="h-6 w-6 p-0 text-[var(--admin-error)] hover:text-[var(--admin-error)]"
onClick={() => { onClick={() => {
if (!confirm(`Delete item "${item.catalogName}"?`)) return if (!confirm(`Delete item "${item.catalogName}"?`)) return
run(() => deleteBcItem({ id: item.id }), { run(() => deleteBcItem({ id: item.id }), {
+37 -37
View File
@@ -89,12 +89,12 @@ export default async function CommandoCentrum() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Activity size={20} className="text-[var(--color-primary)]" /> <Activity size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -196,24 +196,24 @@ export default async function CommandoCentrum() {
<h2 className="admin-section-title">{t("emulatorControls")}</h2> <h2 className="admin-section-title">{t("emulatorControls")}</h2>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<form action={updateCatalog} className="admin-card"> <form action={updateCatalog} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("updateCatalog")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("updateCatalog")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-4">{t("updateCatalogDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-4">{t("updateCatalogDesc")}</p>
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
{t("updateCatalog")} {t("updateCatalog")}
</button> </button>
</form> </form>
<form action={updateWordFilter} className="admin-card"> <form action={updateWordFilter} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("updateWordFilter")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("updateWordFilter")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-4">{t("updateWordFilterDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-4">{t("updateWordFilterDesc")}</p>
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
{t("updateWordFilter")} {t("updateWordFilter")}
</button> </button>
</form> </form>
<form action={updateNavigator} className="admin-card"> <form action={updateNavigator} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("updateNavigator")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("updateNavigator")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-4">{t("updateNavigatorDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-4">{t("updateNavigatorDesc")}</p>
<button type="submit" className="btn btn-primary"> <button type="submit" className="btn btn-primary">
{t("updateNavigator")} {t("updateNavigator")}
</button> </button>
@@ -221,8 +221,8 @@ export default async function CommandoCentrum() {
</div> </div>
<form action={hotelAlert} className="admin-card mt-6"> <form action={hotelAlert} className="admin-card mt-6">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("hotelAlert")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("hotelAlert")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("hotelAlertDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("hotelAlertDesc")}</p>
<div className="flex gap-2"> <div className="flex gap-2">
<input <input
name="message" name="message"
@@ -241,12 +241,12 @@ export default async function CommandoCentrum() {
{/* ── User RCON actions ──────────────────────────────────── */} {/* ── User RCON actions ──────────────────────────────────── */}
<section className="mt-6"> <section className="mt-6">
<h2 className="admin-section-title">{t("userActions")}</h2> <h2 className="admin-section-title">{t("userActions")}</h2>
<p className="text-xs text-[var(--color-text-muted)] mb-4">{t("userActionsDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-4">{t("userActionsDesc")}</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<form action={disconnectUser} className="admin-card"> <form action={disconnectUser} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("actions.disconnect")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("actions.disconnect")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("disconnectDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("disconnectDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -270,8 +270,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={alertUser} className="admin-card"> <form action={alertUser} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("actions.alert")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("actions.alert")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("alertDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("alertDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -296,8 +296,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={forwardUser} className="admin-card"> <form action={forwardUser} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("forwardToRoom")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("forwardToRoom")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("forwardDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("forwardDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -324,8 +324,8 @@ export default async function CommandoCentrum() {
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
<form action={giveCredits} className="admin-card"> <form action={giveCredits} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("giveCredits")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("giveCredits")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("giveCreditsDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("giveCreditsDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -350,8 +350,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={giveDuckets} className="admin-card"> <form action={giveDuckets} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("giveDuckets")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("giveDuckets")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("giveDucketsDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("giveDucketsDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -376,8 +376,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={giveDiamonds} className="admin-card"> <form action={giveDiamonds} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("giveDiamonds")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("giveDiamonds")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("giveDiamondsDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("giveDiamondsDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -404,8 +404,8 @@ export default async function CommandoCentrum() {
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
<form action={giveBadge} className="admin-card"> <form action={giveBadge} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("giveBadge")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("giveBadge")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("giveBadgeDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("giveBadgeDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -430,8 +430,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={setMotto} className="admin-card"> <form action={setMotto} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("setMotto")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("setMotto")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("setMottoDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("setMottoDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -456,8 +456,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={setRank} className="admin-card"> <form action={setRank} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("setRank")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("setRank")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("setRankDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("setRankDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -485,8 +485,8 @@ export default async function CommandoCentrum() {
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-4">
<form action={executeCommand} className="admin-card"> <form action={executeCommand} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("executeCommand")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("executeCommand")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("executeCommandDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("executeCommandDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -511,8 +511,8 @@ export default async function CommandoCentrum() {
</form> </form>
<form action={sendGift} className="admin-card"> <form action={sendGift} className="admin-card">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("sendGift")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("sendGift")}</h3>
<p className="text-xs text-[var(--color-text-muted)] mb-3">{t("sendGiftDesc")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-3">{t("sendGiftDesc")}</p>
<div className="space-y-2"> <div className="space-y-2">
<input <input
name="userId" name="userId"
@@ -570,7 +570,7 @@ export default async function CommandoCentrum() {
</td> </td>
<td>{nameById.get(Number(a.userId)) ?? `#${a.userId}`}</td> <td>{nameById.get(Number(a.userId)) ?? `#${a.userId}`}</td>
<td> <td>
<span className="inline-block text-[0.6rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.6rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{a.action} {a.action}
</span> </span>
</td> </td>
@@ -616,7 +616,7 @@ export default async function CommandoCentrum() {
{e.version} {e.version}
</td> </td>
<td> <td>
<pre className="text-xs p-2 bg-[var(--color-background)] text-[var(--color-text-readable)] rounded overflow-auto max-h-[160px]"> <pre className="text-xs p-2 bg-[var(--admin-canvas)] text-[var(--color-text-readable)] rounded overflow-auto max-h-[160px]">
{snippet} {snippet}
</pre> </pre>
</td> </td>
+4 -4
View File
@@ -21,19 +21,19 @@ export default async function AdminEmailTemplates() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<span className="text-lg">✉️</span> <span className="text-lg">✉️</span>
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })} {t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })}
</p> </p>
</div> </div>
</div> </div>
<form action={createEmailTemplate} className="admin-card mb-6"> <form action={createEmailTemplate} className="admin-card mb-6">
<h3 className="text-sm font-bold text-[var(--color-text)] mb-2">{t("newTemplate")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] mb-2">{t("newTemplate")}</h3>
<div className="flex gap-2 flex-wrap items-center mb-3"> <div className="flex gap-2 flex-wrap items-center mb-3">
<input <input
name="name" name="name"
+2 -2
View File
@@ -56,7 +56,7 @@ export default async function AdminEmulator() {
</p> </p>
<form action={updateEmulatorSetting} className="admin-card mb-6"> <form action={updateEmulatorSetting} className="admin-card mb-6">
<h3 className="text-sm font-bold text-[var(--color-text)] m-0 mb-3">{t("addOverwriteSetting")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] m-0 mb-3">{t("addOverwriteSetting")}</h3>
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<input name="key" placeholder={t("key")} maxLength={100} required /> <input name="key" placeholder={t("key")} maxLength={100} required />
<input name="value" placeholder={t("value")} maxLength={512} className="flex-1 min-w-[200px]" /> <input name="value" placeholder={t("value")} maxLength={512} className="flex-1 min-w-[200px]" />
@@ -105,7 +105,7 @@ export default async function AdminEmulator() {
</p> </p>
<form action={updateEmulatorText} className="admin-card mb-6"> <form action={updateEmulatorText} className="admin-card mb-6">
<h3 className="text-sm font-bold text-[var(--color-text)] m-0 mb-3">{t("addOverwriteText")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] m-0 mb-3">{t("addOverwriteText")}</h3>
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<input name="key" placeholder={t("key")} maxLength={100} required /> <input name="key" placeholder={t("key")} maxLength={100} required />
<input name="value" placeholder={t("value")} maxLength={4096} className="flex-1 min-w-[200px]" /> <input name="value" placeholder={t("value")} maxLength={4096} className="flex-1 min-w-[200px]" />
+8 -8
View File
@@ -64,10 +64,10 @@ export function FaviconForm({ currentUrl }: { currentUrl: string | null }) {
return ( return (
<div className="grid gap-6 lg:grid-cols-2"> <div className="grid gap-6 lg:grid-cols-2">
<div className="admin-card"> <div className="admin-card">
<h3 className="text-sm font-extrabold text-[var(--color-text)] mb-4">{t("uploadLabel")}</h3> <h3 className="text-sm font-extrabold text-[var(--admin-text)] mb-4">{t("uploadLabel")}</h3>
{preview && ( {preview && (
<div className="mb-6"> <div className="mb-6">
<label className="block text-xs font-bold text-[var(--color-text-muted)] uppercase tracking-wider mb-2"> <label className="block text-xs font-bold text-[var(--admin-text-muted)] uppercase tracking-wider mb-2">
{t("current")} {t("current")}
</label> </label>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
@@ -76,10 +76,10 @@ export function FaviconForm({ currentUrl }: { currentUrl: string | null }) {
alt="Favicon preview" alt="Favicon preview"
width={64} width={64}
height={64} height={64}
className="rounded-lg border-2 border-[var(--border-subtle)] object-contain bg-white" className="rounded-lg border-2 border-[var(--admin-border)] object-contain bg-white"
unoptimized unoptimized
/> />
<div className="text-xs text-[var(--color-text-muted)] break-all">{preview}</div> <div className="text-xs text-[var(--admin-text-muted)] break-all">{preview}</div>
</div> </div>
</div> </div>
)} )}
@@ -91,9 +91,9 @@ export function FaviconForm({ currentUrl }: { currentUrl: string | null }) {
name="file" name="file"
accept=".png,.jpg,.jpeg,.gif,.webp,.ico,.svg" accept=".png,.jpg,.jpeg,.gif,.webp,.ico,.svg"
required required
className="block w-full text-sm text-[var(--color-text-readable)] file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-[var(--color-primary)] file:text-[var(--color-primary-foreground-readable)] cursor-pointer" className="block w-full text-sm text-[var(--color-text-readable)] file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-[var(--admin-accent)] file:text-[var(--color-primary-foreground-readable)] cursor-pointer"
/> />
<p className="mt-1 text-xs text-[var(--color-text-muted)]">{t("uploadHint")}</p> <p className="mt-1 text-xs text-[var(--admin-text-muted)]">{t("uploadHint")}</p>
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
@@ -112,8 +112,8 @@ export function FaviconForm({ currentUrl }: { currentUrl: string | null }) {
<div <div
className={`mt-4 p-3 rounded-lg text-sm ${ className={`mt-4 p-3 rounded-lg text-sm ${
message.type === "success" message.type === "success"
? "bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] dark:text-[var(--color-success-text-readable)]" ? "bg-[var(--admin-success-subtle)] text-[var(--admin-success)] dark:text-[var(--admin-success)]"
: "bg-[var(--color-error-subtle)] text-[var(--color-error-text-readable)] dark:text-[var(--color-error-text-readable)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-error)] dark:text-[var(--admin-error)]"
}`} }`}
> >
{message.text} {message.text}
+11 -11
View File
@@ -87,21 +87,21 @@ export function FaviconGenerator({ onSave }: Props) {
return ( return (
<div className="admin-card"> <div className="admin-card">
<h3 className="text-sm font-extrabold text-[var(--color-text)] mb-4">{t("title")}</h3> <h3 className="text-sm font-extrabold text-[var(--admin-text)] mb-4">{t("title")}</h3>
<div className="flex justify-center mb-6"> <div className="flex justify-center mb-6">
<canvas <canvas
ref={canvasRef} ref={canvasRef}
width={SIZE} width={SIZE}
height={SIZE} height={SIZE}
className="border-2 border-[var(--border-subtle)] rounded-xl shadow-lg" className="border-2 border-[var(--admin-border)] rounded-xl shadow-lg"
style={{ width: 128, height: 128, imageRendering: "pixelated" }} style={{ width: 128, height: 128, imageRendering: "pixelated" }}
/> />
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label className="block text-xs font-bold text-[var(--color-text-muted)] uppercase tracking-wider mb-2"> <label className="block text-xs font-bold text-[var(--admin-text-muted)] uppercase tracking-wider mb-2">
{t("text")} {t("text")}
</label> </label>
<input <input
@@ -115,7 +115,7 @@ export function FaviconGenerator({ onSave }: Props) {
</div> </div>
<div> <div>
<label className="block text-xs font-bold text-[var(--color-text-muted)] uppercase tracking-wider mb-2"> <label className="block text-xs font-bold text-[var(--admin-text-muted)] uppercase tracking-wider mb-2">
{t("bgColor")} {t("bgColor")}
</label> </label>
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
@@ -123,9 +123,9 @@ export function FaviconGenerator({ onSave }: Props) {
type="color" type="color"
value={bgColor} value={bgColor}
onChange={(e) => setBgColor(e.target.value)} onChange={(e) => setBgColor(e.target.value)}
className="w-10 h-10 rounded-lg cursor-pointer border border-[var(--border-subtle)]" className="w-10 h-10 rounded-lg cursor-pointer border border-[var(--admin-border)]"
/> />
<span className="text-xs text-[var(--color-text-muted)] font-mono">{bgColor}</span> <span className="text-xs text-[var(--admin-text-muted)] font-mono">{bgColor}</span>
</div> </div>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
{swatches.map((c) => ( {swatches.map((c) => (
@@ -134,7 +134,7 @@ export function FaviconGenerator({ onSave }: Props) {
type="button" type="button"
onClick={() => setBgColor(c)} onClick={() => setBgColor(c)}
className={`w-7 h-7 rounded-lg border-2 transition-all ${ className={`w-7 h-7 rounded-lg border-2 transition-all ${
bgColor === c ? "border-[var(--color-primary)] scale-110" : "border-transparent" bgColor === c ? "border-[var(--admin-accent)] scale-110" : "border-transparent"
}`} }`}
style={{ backgroundColor: c }} style={{ backgroundColor: c }}
/> />
@@ -143,7 +143,7 @@ export function FaviconGenerator({ onSave }: Props) {
</div> </div>
<div> <div>
<label className="block text-xs font-bold text-[var(--color-text-muted)] uppercase tracking-wider mb-2"> <label className="block text-xs font-bold text-[var(--admin-text-muted)] uppercase tracking-wider mb-2">
{t("textColor")} {t("textColor")}
</label> </label>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -151,9 +151,9 @@ export function FaviconGenerator({ onSave }: Props) {
type="color" type="color"
value={textColor} value={textColor}
onChange={(e) => setTextColor(e.target.value)} onChange={(e) => setTextColor(e.target.value)}
className="w-10 h-10 rounded-lg cursor-pointer border border-[var(--border-subtle)]" className="w-10 h-10 rounded-lg cursor-pointer border border-[var(--admin-border)]"
/> />
<span className="text-xs text-[var(--color-text-muted)] font-mono">{textColor}</span> <span className="text-xs text-[var(--admin-text-muted)] font-mono">{textColor}</span>
</div> </div>
</div> </div>
@@ -165,7 +165,7 @@ export function FaviconGenerator({ onSave }: Props) {
onChange={(e) => setRounded(e.target.checked)} onChange={(e) => setRounded(e.target.checked)}
className="rounded" className="rounded"
/> />
<label htmlFor="rounded" className="text-sm text-[var(--color-text)] cursor-pointer"> <label htmlFor="rounded" className="text-sm text-[var(--admin-text)] cursor-pointer">
{t("rounded")} {t("rounded")}
</label> </label>
</div> </div>
+4 -4
View File
@@ -19,12 +19,12 @@ export default async function AdminFaviconPage() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Image size={20} className="text-[var(--color-primary)]" /> <Image size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
+4 -4
View File
@@ -28,12 +28,12 @@ export default async function EditHelpQuestion({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<HelpCircle size={20} className="text-[var(--color-primary)]" /> <HelpCircle size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">Edit help-center entry</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">Edit help-center entry</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/help-questions">← Help Center</Link> <Link href="/admin/help-questions">← Help Center</Link>
</p> </p>
</div> </div>
+4 -4
View File
@@ -12,12 +12,12 @@ export default async function NewHelpQuestion({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<HelpCircle size={20} className="text-[var(--color-primary)]" /> <HelpCircle size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">New help-center entry</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">New help-center entry</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
<Link href="/admin/help-questions">← Help Center</Link> <Link href="/admin/help-questions">← Help Center</Link>
</p> </p>
</div> </div>
+5 -5
View File
@@ -24,12 +24,12 @@ export default async function AdminHelpQuestions() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<HelpCircle size={20} className="text-[var(--color-primary)]" /> <HelpCircle size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
<Link href="/admin/help-questions/new" className="btn btn-primary"> <Link href="/admin/help-questions/new" className="btn btn-primary">
+ {t("newEntry")} + {t("newEntry")}
@@ -69,7 +69,7 @@ export default async function AdminHelpQuestions() {
</td> </td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${e.smallBox ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-primary)]/18 text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${e.smallBox ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]"}`}
> >
{e.smallBox ? "Yes" : "No"} {e.smallBox ? "Yes" : "No"}
</span> </span>
+6 -6
View File
@@ -48,12 +48,12 @@ export default async function AdminHousekeeping({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<span className="text-lg">🔑</span> <span className="text-lg">🔑</span>
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -107,7 +107,7 @@ export default async function AdminHousekeeping({
) : ( ) : (
<section> <section>
<h2 className="admin-section-title">{t("importPermissions")}</h2> <h2 className="admin-section-title">{t("importPermissions")}</h2>
<p className="text-xs text-[var(--color-text-muted)] -mt-2 mb-3">{t("importHint")}</p> <p className="text-xs text-[var(--admin-text-muted)] -mt-2 mb-3">{t("importHint")}</p>
<form action={upsertPermission} className="admin-card"> <form action={upsertPermission} className="admin-card">
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<input <input
@@ -127,7 +127,7 @@ export default async function AdminHousekeeping({
<section> <section>
<h2 className="admin-section-title"> <h2 className="admin-section-title">
{t("permissions")}{" "} {t("permissions")}{" "}
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{filtered.length} {filtered.length}
</span> </span>
</h2> </h2>
@@ -151,7 +151,7 @@ export default async function AdminHousekeeping({
key={String(p.id)} key={String(p.id)}
style={ style={
mode === "import" && selectedIds.has(i) mode === "import" && selectedIds.has(i)
? { backgroundColor: "color-mix(in srgb, var(--color-primary) 5%, transparent)" } ? { backgroundColor: "color-mix(in srgb, var(--admin-accent) 5%, transparent)" }
: {} : {}
} }
> >
@@ -202,8 +202,8 @@ export function ImportBadgesClient() {
<span className="text-muted-foreground">results</span> <span className="text-muted-foreground">results</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<CheckCircle2 className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <CheckCircle2 className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{importedCount}</span> <span className="font-medium text-[var(--admin-success)]">{importedCount}</span>
<span className="text-muted-foreground">imported</span> <span className="text-muted-foreground">imported</span>
</div> </div>
{importableBadges.length > 0 && ( {importableBadges.length > 0 && (
@@ -350,7 +350,7 @@ export function ImportBadgesClient() {
key={badge.code} key={badge.code}
className={cn( className={cn(
'rounded-lg border overflow-hidden bg-card group relative hover:border-border transition-all', 'rounded-lg border overflow-hidden bg-card group relative hover:border-border transition-all',
badge.alreadyImported && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', badge.alreadyImported && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -419,7 +419,7 @@ export function ImportBadgesClient() {
<div className="pt-1"> <div className="pt-1">
{badge.alreadyImported ? ( {badge.alreadyImported ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 flex-1 justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 flex-1 justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
@@ -546,8 +546,8 @@ function FurniGrid({ source }: FurniGridProps) {
<span className="text-muted-foreground">total</span> <span className="text-muted-foreground">total</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <Check className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{stats.present}</span> <span className="font-medium text-[var(--admin-success)]">{stats.present}</span>
<span className="text-muted-foreground">present</span> <span className="text-muted-foreground">present</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
@@ -708,7 +708,7 @@ function FurniGrid({ source }: FurniGridProps) {
key={it.classname} key={it.classname}
className={cn( className={cn(
'group rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all', 'group rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all',
it.present && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', it.present && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -741,9 +741,9 @@ function FurniGrid({ source }: FurniGridProps) {
/> />
{/* Hover detail tooltip */} {/* Hover detail tooltip */}
<div className="absolute bottom-1 left-1 right-1 z-10 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"> <div className="absolute bottom-1 left-1 right-1 z-10 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
<div className="bg-black/80 backdrop-blur-sm rounded px-2 py-1.5 text-[10px] text-white space-y-0.5"> <div className="bg-[var(--admin-overlay)] backdrop-blur-sm rounded px-2 py-1.5 text-[10px] text-[var(--admin-sidebar-text)] space-y-0.5">
{it.description ? <p className="truncate">{it.description}</p> : null} {it.description ? <p className="truncate">{it.description}</p> : null}
<div className="flex gap-2 text-white/70"> <div className="flex gap-2 text-[var(--admin-sidebar-text)]/70">
{it.revision ? <span>Rev: {it.revision}</span> : null} {it.revision ? <span>Rev: {it.revision}</span> : null}
<span>{it.itemType === 's' ? 'Floor' : 'Wall'}</span> <span>{it.itemType === 's' ? 'Floor' : 'Wall'}</span>
<span> <span>
@@ -766,7 +766,7 @@ function FurniGrid({ source }: FurniGridProps) {
</div> </div>
<div className="pt-1"> <div className="pt-1">
{it.present ? ( {it.present ? (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 w-full justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 w-full justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Present Present
</Badge> </Badge>
@@ -257,8 +257,8 @@ function LibsView() {
<span className="text-muted-foreground">libraries</span> <span className="text-muted-foreground">libraries</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <Check className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{importedCount}</span> <span className="font-medium text-[var(--admin-success)]">{importedCount}</span>
<span className="text-muted-foreground">on this page</span> <span className="text-muted-foreground">on this page</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
@@ -372,7 +372,7 @@ function LibsView() {
key={p.lib} key={p.lib}
className={cn( className={cn(
'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all', 'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all',
p.nitroExists && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', p.nitroExists && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -400,7 +400,7 @@ function LibsView() {
<div className="pt-1"> <div className="pt-1">
{p.nitroExists ? ( {p.nitroExists ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 flex-1 justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 flex-1 justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
@@ -591,8 +591,8 @@ function SetsView() {
<span className="text-muted-foreground">sets</span> <span className="text-muted-foreground">sets</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <Check className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{presentCount}</span> <span className="font-medium text-[var(--admin-success)]">{presentCount}</span>
<span className="text-muted-foreground">on this page</span> <span className="text-muted-foreground">on this page</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
@@ -707,7 +707,7 @@ function SetsView() {
key={key} key={key}
className={cn( className={cn(
'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all', 'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all',
it.present && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', it.present && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -737,19 +737,19 @@ function SetsView() {
{it.gender} {it.gender}
</Badge> </Badge>
{it.club > 0 && ( {it.club > 0 && (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)] border-0"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)] border-0">
HC HC
</Badge> </Badge>
)} )}
{it.sellable && ( {it.sellable && (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-info-subtle)] text-[var(--color-info-text-readable)] border-0"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-info-subtle)] text-[var(--admin-info)] border-0">
$ $
</Badge> </Badge>
)} )}
</div> </div>
<div className="pt-1"> <div className="pt-1">
{it.present ? ( {it.present ? (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 w-full justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 w-full justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
@@ -185,8 +185,8 @@ export function ImportEffectsClient() {
<span className="text-muted-foreground">effects</span> <span className="text-muted-foreground">effects</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <Check className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{importedCount}</span> <span className="font-medium text-[var(--admin-success)]">{importedCount}</span>
<span className="text-muted-foreground">imported</span> <span className="text-muted-foreground">imported</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
@@ -310,7 +310,7 @@ export function ImportEffectsClient() {
key={e.lib} key={e.lib}
className={cn( className={cn(
'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all', 'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all',
imported && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', imported && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -332,14 +332,14 @@ export function ImportEffectsClient() {
className={cn( className={cn(
'flex flex-col items-center justify-center h-20 gap-1', 'flex flex-col items-center justify-center h-20 gap-1',
e.type === 'dance' e.type === 'dance'
? 'bg-gradient-to-br from-amber-500/25 to-orange-500/10' ? 'bg-gradient-to-br from-[var(--admin-warning)]/25 to-[var(--admin-warning)]/10'
: 'bg-gradient-to-br from-violet-500/25 to-fuchsia-500/10', : 'bg-gradient-to-br from-[var(--admin-accent)]/25 to-[var(--admin-accent)]/10',
)} )}
> >
{e.type === 'dance' ? ( {e.type === 'dance' ? (
<Music className="w-7 h-7 text-[var(--color-warning-text-readable)]/80" /> <Music className="w-7 h-7 text-[var(--admin-warning)]/80" />
) : ( ) : (
<Sparkles className="w-7 h-7 text-violet-500/80" /> <Sparkles className="w-7 h-7 text-[var(--admin-accent-text)]" />
)} )}
<span className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground"> <span className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
{e.type} {e.type}
@@ -351,7 +351,7 @@ export function ImportEffectsClient() {
<div className="pt-1"> <div className="pt-1">
{imported ? ( {imported ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 flex-1 justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 flex-1 justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
@@ -883,11 +883,11 @@ export function ImportFurniClient() {
<div className="text-[10px] text-muted-foreground">on this page</div> <div className="text-[10px] text-muted-foreground">on this page</div>
</div> </div>
<div className="rounded-lg border bg-card p-3"> <div className="rounded-lg border bg-card p-3">
<div className="flex items-center gap-2 text-xs text-[var(--color-warning-text-readable)]"> <div className="flex items-center gap-2 text-xs text-[var(--admin-warning)]">
<FileWarning className="w-3.5 h-3.5" /> <FileWarning className="w-3.5 h-3.5" />
Missing .nitro Missing .nitro
</div> </div>
<div className="text-xl font-bold mt-1 text-[var(--color-warning-text-readable)]"> <div className="text-xl font-bold mt-1 text-[var(--admin-warning)]">
{stats.missingNitro.toLocaleString()} {stats.missingNitro.toLocaleString()}
</div> </div>
</div> </div>
@@ -913,12 +913,12 @@ export function ImportFurniClient() {
<div className="rounded-lg border p-4 space-y-3 bg-card"> <div className="rounded-lg border p-4 space-y-3 bg-card">
<h3 className="text-sm font-semibold">Import Complete</h3> <h3 className="text-sm font-semibold">Import Complete</h3>
<div className="flex gap-4 text-sm"> <div className="flex gap-4 text-sm">
<span className="text-[var(--color-success-text-readable)]">{batchSummary.succeeded} succeeded</span> <span className="text-[var(--admin-success)]">{batchSummary.succeeded} succeeded</span>
{batchSummary.failed > 0 && ( {batchSummary.failed > 0 && (
<span className="text-destructive">{batchSummary.failed} failed</span> <span className="text-destructive">{batchSummary.failed} failed</span>
)} )}
{batchSummary.warnings > 0 && ( {batchSummary.warnings > 0 && (
<span className="text-[var(--color-warning-text-readable)]">{batchSummary.warnings} with warnings</span> <span className="text-[var(--admin-warning)]">{batchSummary.warnings} with warnings</span>
)} )}
<span className="text-muted-foreground"> <span className="text-muted-foreground">
{(batchSummary.duration / 1000).toFixed(1)}s {(batchSummary.duration / 1000).toFixed(1)}s
@@ -937,14 +937,14 @@ export function ImportFurniClient() {
{item.status === 'failed' ? ( {item.status === 'failed' ? (
<XCircle className="w-3 h-3 text-destructive mt-0.5 shrink-0" /> <XCircle className="w-3 h-3 text-destructive mt-0.5 shrink-0" />
) : ( ) : (
<AlertCircle className="w-3 h-3 text-[var(--color-warning-text-readable)] mt-0.5 shrink-0" /> <AlertCircle className="w-3 h-3 text-[var(--admin-warning)] mt-0.5 shrink-0" />
)} )}
<div className="min-w-0"> <div className="min-w-0">
<span className="font-mono">{item.classname}</span> <span className="font-mono">{item.classname}</span>
{item.message && <p className="text-destructive">{item.message}</p>} {item.message && <p className="text-destructive">{item.message}</p>}
{item.warnings?.map((w, i) => ( {item.warnings?.map((w, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey: warnings list is append-only and not reordered // biome-ignore lint/suspicious/noArrayIndexKey: warnings list is append-only and not reordered
<p key={i} className="text-[var(--color-warning-text-readable)]"> <p key={i} className="text-[var(--admin-warning)]">
{w} {w}
</p> </p>
))} ))}
@@ -987,7 +987,7 @@ export function ImportFurniClient() {
{progressStats.completed}/{progressStats.total} completed {progressStats.completed}/{progressStats.total} completed
</span> </span>
{progressStats.done > 0 && ( {progressStats.done > 0 && (
<span className="text-[var(--color-success-text-readable)]">{progressStats.done} OK</span> <span className="text-[var(--admin-success)]">{progressStats.done} OK</span>
)} )}
{progressStats.failed > 0 && ( {progressStats.failed > 0 && (
<span className="text-destructive">{progressStats.failed} failed</span> <span className="text-destructive">{progressStats.failed} failed</span>
@@ -999,7 +999,7 @@ export function ImportFurniClient() {
key={item.classname} key={item.classname}
className="flex items-center gap-2 text-xs py-1 px-2 rounded hover:bg-muted/50" className="flex items-center gap-2 text-xs py-1 px-2 rounded hover:bg-muted/50"
> >
{item.status === 'done' && <Check className="w-3 h-3 text-[var(--color-success-text-readable)] shrink-0" />} {item.status === 'done' && <Check className="w-3 h-3 text-[var(--admin-success)] shrink-0" />}
{item.status === 'failed' && ( {item.status === 'failed' && (
<XCircle className="w-3 h-3 text-destructive shrink-0" /> <XCircle className="w-3 h-3 text-destructive shrink-0" />
)} )}
@@ -1049,7 +1049,7 @@ export function ImportFurniClient() {
<span> <span>
{completed}/{arr.length} {completed}/{arr.length}
</span> </span>
{done > 0 && <span className="text-[var(--color-success-text-readable)]">{done} OK</span>} {done > 0 && <span className="text-[var(--admin-success)]">{done} OK</span>}
{failed > 0 && <span className="text-destructive">{failed} failed</span>} {failed > 0 && <span className="text-destructive">{failed} failed</span>}
</div> </div>
</div> </div>
@@ -1062,7 +1062,7 @@ export function ImportFurniClient() {
<h3 className="text-sm font-semibold">Regeneration Complete</h3> <h3 className="text-sm font-semibold">Regeneration Complete</h3>
<div className="flex gap-4 text-sm"> <div className="flex gap-4 text-sm">
{regenSummary.succeeded > 0 && ( {regenSummary.succeeded > 0 && (
<span className="text-[var(--color-success-text-readable)]">{regenSummary.succeeded} regenerated</span> <span className="text-[var(--admin-success)]">{regenSummary.succeeded} regenerated</span>
)} )}
{regenSummary.failed > 0 && ( {regenSummary.failed > 0 && (
<span className="text-destructive">{regenSummary.failed} failed</span> <span className="text-destructive">{regenSummary.failed} failed</span>
@@ -1305,7 +1305,7 @@ export function ImportFurniClient() {
size="sm" size="sm"
onClick={regenAllMissing} onClick={regenAllMissing}
disabled={batchRegenerating || regeneratingNitro.size > 0} disabled={batchRegenerating || regeneratingNitro.size > 0}
className="h-8 text-[var(--color-warning-text-readable)] border-[var(--color-warning-border)] hover:bg-[var(--color-warning-subtle)]" className="h-8 text-[var(--admin-warning)] border-[var(--admin-warning-border)] hover:bg-[var(--admin-warning-subtle)]"
> >
{batchRegenerating ? ( {batchRegenerating ? (
<Loader2 className="w-4 h-4 mr-1 animate-spin" /> <Loader2 className="w-4 h-4 mr-1 animate-spin" />
@@ -1428,11 +1428,11 @@ export function ImportFurniClient() {
<td className="px-2 py-1.5"> <td className="px-2 py-1.5">
{item.alreadyImported ? ( {item.alreadyImported ? (
item.nitroExists ? ( item.nitroExists ? (
<Badge className="text-[10px] bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0"> <Badge className="text-[10px] bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0">
OK OK
</Badge> </Badge>
) : ( ) : (
<Badge className="text-[10px] bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)] border-0"> <Badge className="text-[10px] bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)] border-0">
No .nitro No .nitro
</Badge> </Badge>
) )
@@ -1471,7 +1471,7 @@ export function ImportFurniClient() {
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-6 w-6 p-0 text-[var(--color-warning-text-readable)]" className="h-6 w-6 p-0 text-[var(--admin-warning)]"
onClick={() => regenNitro(item)} onClick={() => regenNitro(item)}
disabled={regeneratingNitro.has(item.classname)} disabled={regeneratingNitro.has(item.classname)}
> >
@@ -1510,7 +1510,7 @@ export function ImportFurniClient() {
key={item.classname} key={item.classname}
className={`rounded-lg border border-border/50 overflow-hidden bg-card hover:border-border transition-all group ${ className={`rounded-lg border border-border/50 overflow-hidden bg-card hover:border-border transition-all group ${
item.alreadyImported item.alreadyImported
? 'shadow-[0_0_8px_rgba(34,197,94,0.15)] hover:shadow-[0_0_12px_rgba(34,197,94,0.25)]' ? 'ring-1 ring-[var(--admin-success)]/30 hover:ring-2'
: 'hover:shadow-md' : 'hover:shadow-md'
}`} }`}
> >
@@ -1549,9 +1549,9 @@ export function ImportFurniClient() {
)} )}
{/* Hover detail tooltip */} {/* Hover detail tooltip */}
<div className="absolute bottom-1 left-1 right-1 z-10 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"> <div className="absolute bottom-1 left-1 right-1 z-10 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none">
<div className="bg-black/80 backdrop-blur-sm rounded px-2 py-1.5 text-[10px] text-white space-y-0.5"> <div className="bg-[var(--admin-overlay)] backdrop-blur-sm rounded px-2 py-1.5 text-[10px] text-[var(--admin-sidebar-text)] space-y-0.5">
{item.description && <p className="truncate">{item.description}</p>} {item.description && <p className="truncate">{item.description}</p>}
<div className="flex gap-2 text-white/70"> <div className="flex gap-2 text-[var(--admin-sidebar-text)]/70">
<span>Rev: {item.revision}</span> <span>Rev: {item.revision}</span>
<span>{item.type === 'wallitem' ? 'Wall' : 'Floor'}</span> <span>{item.type === 'wallitem' ? 'Wall' : 'Floor'}</span>
<span>{item.category || 'credit'}</span> <span>{item.category || 'credit'}</span>
@@ -1576,12 +1576,12 @@ export function ImportFurniClient() {
{item.alreadyImported && ( {item.alreadyImported && (
<> <>
{item.nitroExists ? ( {item.nitroExists ? (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-[var(--color-success-border)] gap-0.5 shrink-0"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-[var(--admin-success-border)] gap-0.5 shrink-0">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
) : ( ) : (
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)] border-[var(--color-warning-border)] gap-0.5 shrink-0"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)] border-[var(--admin-warning-border)] gap-0.5 shrink-0">
<AlertCircle className="w-2.5 h-2.5" /> <AlertCircle className="w-2.5 h-2.5" />
No .nitro No .nitro
</Badge> </Badge>
@@ -1603,7 +1603,7 @@ export function ImportFurniClient() {
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-5 w-5 p-0 shrink-0 text-[var(--color-warning-text-readable)]" className="h-5 w-5 p-0 shrink-0 text-[var(--admin-warning)]"
title="Regenerate .nitro file" title="Regenerate .nitro file"
disabled={regeneratingNitro.has(item.classname)} disabled={regeneratingNitro.has(item.classname)}
onClick={(e) => { onClick={(e) => {
@@ -1770,7 +1770,7 @@ export function ImportFurniClient() {
)} )}
{reorgPreview.repriced > 0 && ( {reorgPreview.repriced > 0 && (
<div className="rounded-lg border p-3 text-center"> <div className="rounded-lg border p-3 text-center">
<p className="text-lg font-bold text-[var(--color-warning-text-readable)]">{reorgPreview.repriced}</p> <p className="text-lg font-bold text-[var(--admin-warning)]">{reorgPreview.repriced}</p>
<p className="text-xs text-muted-foreground">Prices to update</p> <p className="text-xs text-muted-foreground">Prices to update</p>
</div> </div>
)} )}
@@ -83,7 +83,7 @@ function CopyClassname({ value }: { value: string }) {
> >
<span className="truncate">{value}</span> <span className="truncate">{value}</span>
{copied ? ( {copied ? (
<Check className="w-3 h-3 text-[var(--color-success-text-readable)] shrink-0" /> <Check className="w-3 h-3 text-[var(--admin-success)] shrink-0" />
) : ( ) : (
<Copy className="w-3 h-3 shrink-0" /> <Copy className="w-3 h-3 shrink-0" />
)} )}
@@ -178,8 +178,8 @@ export function ImportPetsClient() {
<span className="text-muted-foreground">pets</span> <span className="text-muted-foreground">pets</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> <Check className="h-4 w-4 text-[var(--admin-success)]" />
<span className="font-medium text-[var(--color-success-text-readable)]">{importedCount}</span> <span className="font-medium text-[var(--admin-success)]">{importedCount}</span>
<span className="text-muted-foreground">imported</span> <span className="text-muted-foreground">imported</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
@@ -287,7 +287,7 @@ export function ImportPetsClient() {
key={p.lib} key={p.lib}
className={cn( className={cn(
'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all', 'rounded-lg border overflow-hidden bg-card relative hover:border-border transition-all',
p.nitroExists && 'shadow-[0_0_6px_rgba(34,197,94,0.15)]', p.nitroExists && 'ring-1 ring-[var(--admin-success)]/30',
isSelected && 'ring-2 ring-primary border-primary', isSelected && 'ring-2 ring-primary border-primary',
)} )}
> >
@@ -313,7 +313,7 @@ export function ImportPetsClient() {
<div className="pt-1"> <div className="pt-1">
{p.nitroExists ? ( {p.nitroExists ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Badge className="text-[10px] px-1.5 py-0 bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-0 gap-0.5 flex-1 justify-center"> <Badge className="text-[10px] px-1.5 py-0 bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-0 gap-0.5 flex-1 justify-center">
<Check className="w-2.5 h-2.5" /> <Check className="w-2.5 h-2.5" />
Imported Imported
</Badge> </Badge>
@@ -435,7 +435,7 @@ export function RepairClient() {
{/* Table area */} {/* Table area */}
{summary && summary.items.length === 0 && ( {summary && summary.items.length === 0 && (
<div className="rounded-md border bg-muted/30 p-10 text-center"> <div className="rounded-md border bg-muted/30 p-10 text-center">
<CheckCircle2 className="mx-auto mb-3 h-10 w-10 text-[var(--color-success-text-readable)]" /> <CheckCircle2 className="mx-auto mb-3 h-10 w-10 text-[var(--admin-success)]" />
<p className="font-medium">All imported items have their assets on disk.</p> <p className="font-medium">All imported items have their assets on disk.</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{summary.total} items scanned, nothing to repair. {summary.total} items scanned, nothing to repair.
@@ -615,8 +615,8 @@ function StatCard({
const tones = { const tones = {
default: 'border-border', default: 'border-border',
danger: 'border-destructive/40 bg-destructive/5', danger: 'border-destructive/40 bg-destructive/5',
warn: 'border-[var(--color-warning-border)] bg-[var(--color-warning-subtle)]', warn: 'border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]',
info: 'border-sky-500/40 bg-sky-500/5', info: 'border-[var(--admin-info)]/40 bg-[var(--admin-info)]/5',
} }
return ( return (
<div className={`rounded-md border p-4 ${tones[tone]}`}> <div className={`rounded-md border p-4 ${tones[tone]}`}>
@@ -631,7 +631,7 @@ function FileBadge({ present }: { present: boolean }) {
return present ? ( return present ? (
<Badge <Badge
variant="secondary" variant="secondary"
className="border-[var(--color-success-border)] bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] dark:text-[var(--color-success-text-readable)]" className="border-[var(--admin-success-border)] bg-[var(--admin-success-subtle)] text-[var(--admin-success)] dark:text-[var(--admin-success)]"
> >
<CheckCircle2 className="mr-1 h-3 w-3" /> On disk <CheckCircle2 className="mr-1 h-3 w-3" /> On disk
</Badge> </Badge>
@@ -659,7 +659,7 @@ function StatusCell({ progress }: { progress?: ItemProgress }) {
progress.iconRepaired ? 'icon ✓' : null, progress.iconRepaired ? 'icon ✓' : null,
].filter(Boolean) ].filter(Boolean)
return ( return (
<span className="flex items-center gap-1 text-xs text-[var(--color-success-text-readable)] dark:text-[var(--color-success-text-readable)]"> <span className="flex items-center gap-1 text-xs text-[var(--admin-success)] dark:text-[var(--admin-success)]">
<CheckCircle2 className="h-3 w-3" /> <CheckCircle2 className="h-3 w-3" />
{parts.length > 0 ? parts.join(' · ') : 'Repaired'} {parts.length > 0 ? parts.join(' · ') : 'Repaired'}
</span> </span>
@@ -668,7 +668,7 @@ function StatusCell({ progress }: { progress?: ItemProgress }) {
if (progress.status === 'partial') { if (progress.status === 'partial') {
return ( return (
<span <span
className="flex items-center gap-1 text-xs text-[var(--color-warning-text-readable)] dark:text-[var(--color-warning-text-readable)]" className="flex items-center gap-1 text-xs text-[var(--admin-warning)] dark:text-[var(--admin-warning)]"
title={progress.message} title={progress.message}
> >
<AlertCircle className="h-3 w-3" /> <AlertCircle className="h-3 w-3" />
+4 -4
View File
@@ -43,12 +43,12 @@ export default async function AdminIp() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Shield size={20} className="text-[var(--color-primary)]" /> <Shield size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
+1 -1
View File
@@ -3,7 +3,7 @@ export default function AdminLoading() {
<div style={{ display: "flex", justifyContent: "center", padding: "4rem 0" }}> <div style={{ display: "flex", justifyContent: "center", padding: "4rem 0" }}>
<div <div
className="animate-spin w-8 h-8 border-4 rounded-full" className="animate-spin w-8 h-8 border-4 rounded-full"
style={{ borderColor: "var(--color-primary)", borderTopColor: "transparent" }} style={{ borderColor: "var(--admin-accent)", borderTopColor: "transparent" }}
/> />
</div> </div>
); );
+5 -5
View File
@@ -66,12 +66,12 @@ export default async function AdminStaffActivities({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<ScrollText size={20} className="text-[var(--color-primary)]" /> <ScrollText size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -150,7 +150,7 @@ export default async function AdminStaffActivities({
})} })}
</td> </td>
<td> <td>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{a.action} {a.action}
</span> </span>
</td> </td>
+5 -5
View File
@@ -33,12 +33,12 @@ export default async function AdminMaintenance() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Wrench size={20} className="text-[var(--color-primary)]" /> <Wrench size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })} {t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })}
</p> </p>
</div> </div>
@@ -65,7 +65,7 @@ export default async function AdminMaintenance() {
<h2 className="admin-section-title">{t("currentState")}</h2> <h2 className="admin-section-title">{t("currentState")}</h2>
<div className="admin-card flex items-center gap-3"> <div className="admin-card flex items-center gap-3">
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${enabled ? "bg-[var(--color-error-subtle)] text-[var(--color-text)]" : "bg-[var(--color-success-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${enabled ? "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]"}`}
> >
{enabled ? t("enabled") : t("disabled")} {enabled ? t("enabled") : t("disabled")}
</span> </span>
+4 -4
View File
@@ -9,12 +9,12 @@ export default async function AdminMediaPage() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Image size={20} className="text-[var(--color-primary)]" /> <Image size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
<Link <Link
href="/admin/articles" href="/admin/articles"
+5 -5
View File
@@ -32,7 +32,7 @@ type PublicRow = {
function YesNo({ on }: { on: boolean }) { function YesNo({ on }: { on: boolean }) {
return ( return (
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${on ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${on ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{on ? "Yes" : "No"} {on ? "Yes" : "No"}
</span> </span>
@@ -110,12 +110,12 @@ export default async function AdminNavigation() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Navigation size={20} className="text-[var(--color-primary)]" /> <Navigation size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
+20 -20
View File
@@ -51,12 +51,12 @@ export default async function AdminDashboard() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Activity size={20} className="text-[var(--color-primary)]" /> <Activity size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -90,11 +90,11 @@ export default async function AdminDashboard() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<section className="admin-card p-5"> <section className="admin-card p-5">
<h2 className="text-sm font-bold text-[var(--color-text)] mb-4 admin-section-title"> <h2 className="text-sm font-bold text-[var(--admin-text)] mb-4 admin-section-title">
{t("usersByRank")} {t("usersByRank")}
</h2> </h2>
{rankCounts.length === 0 ? ( {rankCounts.length === 0 ? (
<p className="text-xs text-[var(--color-text-muted)]"> <p className="text-xs text-[var(--admin-text-muted)]">
{dbOk ? t("noAccountsToBreakDown") : t("databaseDown")} {dbOk ? t("noAccountsToBreakDown") : t("databaseDown")}
</p> </p>
) : ( ) : (
@@ -107,20 +107,20 @@ export default async function AdminDashboard() {
className="grid items-center gap-3" className="grid items-center gap-3"
style={{ gridTemplateColumns: "5rem 1fr 2.5rem" }} style={{ gridTemplateColumns: "5rem 1fr 2.5rem" }}
> >
<span className="text-xs font-medium text-[var(--color-text-muted)]"> <span className="text-xs font-medium text-[var(--admin-text-muted)]">
{t("rankLabel", { rank: r.rank })} {t("rankLabel", { rank: r.rank })}
</span> </span>
<div <div
className="h-3 rounded-md overflow-hidden bg-[var(--color-primary)]/10" className="h-3 rounded-md overflow-hidden bg-[var(--admin-accent)]/10"
role="img" role="img"
aria-label={t("rankWithCount", { rank: r.rank, count: r.count })} aria-label={t("rankWithCount", { rank: r.rank, count: r.count })}
> >
<div <div
className="h-full rounded-md bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary-hover)] transition-all duration-500" className="h-full rounded-md bg-gradient-to-r from-[var(--admin-accent)] to-[var(--admin-accent)] transition-all duration-500"
style={{ width: `${Math.max(pct, r.count > 0 ? 4 : 0)}%` }} style={{ width: `${Math.max(pct, r.count > 0 ? 4 : 0)}%` }}
/> />
</div> </div>
<strong className="text-xs text-right text-[var(--color-text-muted)]">{r.count}</strong> <strong className="text-xs text-right text-[var(--admin-text-muted)]">{r.count}</strong>
</div> </div>
); );
})} })}
@@ -130,39 +130,39 @@ export default async function AdminDashboard() {
<section className="admin-card p-5"> <section className="admin-card p-5">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h2 className="text-sm font-bold text-[var(--color-text)] admin-section-title"> <h2 className="text-sm font-bold text-[var(--admin-text)] admin-section-title">
{t("recentStaffActivity")} {t("recentStaffActivity")}
</h2> </h2>
<Link <Link
href="/admin/commandocentrum" href="/admin/commandocentrum"
className="text-xs font-medium text-[var(--color-primary)] hover:text-[var(--color-primary-hover)] flex items-center gap-1 no-underline" className="text-xs font-medium text-[var(--admin-accent)] hover:text-[var(--admin-accent)] flex items-center gap-1 no-underline"
> >
{t("viewAll")} <ArrowRight size={12} /> {t("viewAll")} <ArrowRight size={12} />
</Link> </Link>
</div> </div>
{activity.length === 0 ? ( {activity.length === 0 ? (
<p className="text-xs text-[var(--color-text-muted)]">{t("noStaffActivityLogged")}</p> <p className="text-xs text-[var(--admin-text-muted)]">{t("noStaffActivityLogged")}</p>
) : ( ) : (
<div className="space-y-0"> <div className="space-y-0">
{activity.map((a, i) => ( {activity.map((a, i) => (
<div <div
key={String(a.id)} key={String(a.id)}
className={`flex items-center gap-3 py-2.5 ${i > 0 ? "border-t border-[var(--border-subtle)]" : ""}`} className={`flex items-center gap-3 py-2.5 ${i > 0 ? "border-t border-[var(--admin-border)]" : ""}`}
> >
<div className="w-7 h-7 rounded-full bg-[var(--color-primary)]/10 grid place-items-center flex-none"> <div className="w-7 h-7 rounded-full bg-[var(--admin-accent)]/10 grid place-items-center flex-none">
<Activity size={12} className="text-[var(--color-primary)]" /> <Activity size={12} className="text-[var(--admin-accent)]" />
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="text-sm font-medium text-[var(--color-text)] truncate"> <div className="text-sm font-medium text-[var(--admin-text)] truncate">
{nameById.get(Number(a.userId)) ?? `#${a.userId}`} {nameById.get(Number(a.userId)) ?? `#${a.userId}`}
</div> </div>
<div className="text-xs text-[var(--color-text-muted)] truncate">{a.description}</div> <div className="text-xs text-[var(--admin-text-muted)] truncate">{a.description}</div>
</div> </div>
<div className="text-right flex-none"> <div className="text-right flex-none">
<span className="inline-block text-[0.6rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)] admin-card inline-flex items-center justify-center min-w-[60px]"> <span className="inline-block text-[0.6rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)] admin-card inline-flex items-center justify-center min-w-[60px]">
{a.action} {a.action}
</span> </span>
<div className="text-[0.6rem] text-[var(--color-text-muted)] mt-0.5"> <div className="text-[0.6rem] text-[var(--admin-text-muted)] mt-0.5">
{fromDate(a.createdAt)} {fromDate(a.createdAt)}
</div> </div>
</div> </div>
@@ -110,10 +110,10 @@ function PermToggle({
'w-7 h-6 text-[11px] font-mono rounded transition-all font-medium', 'w-7 h-6 text-[11px] font-mono rounded transition-all font-medium',
v === value v === value
? v === '0' ? v === '0'
? 'bg-[var(--color-text-muted)] text-[var(--color-surface-text-readable)] shadow-sm' ? 'bg-[var(--admin-text-muted)] text-[var(--color-surface-text-readable)] shadow-sm'
: v === '1' : v === '1'
? 'bg-[var(--color-success)] text-[var(--color-success-foreground-readable)] shadow-sm' ? 'bg-[var(--admin-success)] text-[var(--admin-success-foreground)] shadow-sm'
: 'bg-[var(--color-warning)] text-[var(--color-warning-foreground-readable)] shadow-sm' : 'bg-[var(--admin-warning)] text-[var(--admin-warning-foreground)] shadow-sm'
: 'bg-muted/60 text-muted-foreground/40 hover:bg-muted hover:text-muted-foreground', : 'bg-muted/60 text-muted-foreground/40 hover:bg-muted hover:text-muted-foreground',
)} )}
> >
@@ -291,7 +291,7 @@ export function RankEditClient({
key={key} key={key}
className={cn( className={cn(
'flex items-center justify-between px-3 py-2 bg-card', 'flex items-center justify-between px-3 py-2 bg-card',
(perms[key] ?? '0') !== '0' && 'bg-[var(--color-success-subtle)]', (perms[key] ?? '0') !== '0' && 'bg-[var(--admin-success-subtle)]',
)} )}
> >
<span className="font-mono text-xs truncate mr-3">{key}</span> <span className="font-mono text-xs truncate mr-3">{key}</span>
@@ -326,9 +326,9 @@ export function RankEditClient({
</Button> </Button>
</Link> </Link>
{initialRank.id >= 7 ? ( {initialRank.id >= 7 ? (
<Crown className="h-6 w-6 text-[var(--color-warning-text-readable)]" /> <Crown className="h-6 w-6 text-[var(--admin-warning)]" />
) : initialRank.id >= 5 ? ( ) : initialRank.id >= 5 ? (
<ShieldCheck className="h-6 w-6 text-[var(--color-info-text-readable)]" /> <ShieldCheck className="h-6 w-6 text-[var(--admin-info)]" />
) : ( ) : (
<Shield className="h-6 w-6 text-muted-foreground" /> <Shield className="h-6 w-6 text-muted-foreground" />
)} )}
@@ -545,13 +545,13 @@ export function RankEditClient({
</div> </div>
<div className="flex gap-2 text-[11px] text-muted-foreground"> <div className="flex gap-2 text-[11px] text-muted-foreground">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-zinc-500 inline-block" /> Off <span className="w-3 h-3 rounded bg-[var(--admin-text-muted)] inline-block" /> Off
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-[var(--color-success-subtle)] inline-block" /> On <span className="w-3 h-3 rounded bg-[var(--admin-success-subtle)] inline-block" /> On
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<span className="w-3 h-3 rounded bg-[var(--color-warning-subtle)] inline-block" /> Owner <span className="w-3 h-3 rounded bg-[var(--admin-warning-subtle)] inline-block" /> Owner
</span> </span>
</div> </div>
</div> </div>
@@ -660,7 +660,7 @@ export function RankEditClient({
key={perm.id} key={perm.id}
className={cn( className={cn(
'flex items-center gap-3 rounded-lg border px-4 py-3 transition-all', 'flex items-center gap-3 rounded-lg border px-4 py-3 transition-all',
on ? 'border-[var(--color-success-border)] bg-[var(--color-success-subtle)]' : 'hover:bg-muted/50', on ? 'border-[var(--admin-success-border)] bg-[var(--admin-success-subtle)]' : 'hover:bg-muted/50',
)} )}
> >
<Checkbox checked={on} onCheckedChange={() => toggleCms(perm.slug)} /> <Checkbox checked={on} onCheckedChange={() => toggleCms(perm.slug)} />
@@ -671,7 +671,7 @@ export function RankEditClient({
)} )}
</div> </div>
{on && ( {on && (
<Badge className="bg-[var(--color-success-subtle)] text-[var(--color-success-text-readable)] border-[var(--color-success-border)] text-[10px]"> <Badge className="bg-[var(--admin-success-subtle)] text-[var(--admin-success)] border-[var(--admin-success-border)] text-[10px]">
Active Active
</Badge> </Badge>
)} )}
@@ -45,17 +45,17 @@ interface Props {
} }
const RANK_ICONS: Record<number, React.ReactNode> = { const RANK_ICONS: Record<number, React.ReactNode> = {
7: <Crown className="h-4 w-4 text-[var(--color-warning-text-readable)]" />, 7: <Crown className="h-4 w-4 text-[var(--admin-warning)]" />,
6: <ShieldCheck className="h-4 w-4 text-[var(--color-error-text-readable)]" />, 6: <ShieldCheck className="h-4 w-4 text-[var(--admin-error)]" />,
5: <Shield className="h-4 w-4 text-[var(--color-info-text-readable)]" />, 5: <Shield className="h-4 w-4 text-[var(--admin-info)]" />,
4: <UserCheck className="h-4 w-4 text-[var(--color-success-text-readable)]" />, 4: <UserCheck className="h-4 w-4 text-[var(--admin-success)]" />,
} }
const RANK_COLORS: Record<number, string> = { const RANK_COLORS: Record<number, string> = {
7: 'border-[var(--color-warning-border)] bg-[var(--color-warning-subtle)]', 7: 'border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)]',
6: 'border-[var(--color-error-border)] bg-[var(--color-error-subtle)]', 6: 'border-[var(--admin-error-border)] bg-[var(--admin-error-subtle)]',
5: 'border-[var(--color-info-border)] bg-[var(--color-info-subtle)]', 5: 'border-[var(--admin-info-border)] bg-[var(--admin-info-subtle)]',
4: 'border-[var(--color-success-border)] bg-[var(--color-success-subtle)]', 4: 'border-[var(--admin-success-border)] bg-[var(--admin-success-subtle)]',
} }
export function PermissionsList({ ranks: initialRanks }: Props) { export function PermissionsList({ ranks: initialRanks }: Props) {
+4 -4
View File
@@ -24,12 +24,12 @@ export default async function AdminPhotos() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Image size={20} className="text-[var(--color-primary)]" /> <Image size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
+5 -5
View File
@@ -79,12 +79,12 @@ export default async function AdminRadioApiKeysPage() {
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Key size={20} className="text-[var(--color-primary)]" /> <Key size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("apiKeysForm.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("apiKeysForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("apiKeysForm.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("apiKeysForm.subtitle")}</p>
</div> </div>
</div> </div>
@@ -207,7 +207,7 @@ export default async function AdminRadioApiKeysPage() {
</td> </td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${k.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${k.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{k.isActive ? t("apiKeysForm.activeLabel") : t("apiKeysForm.inactiveLabel")} {k.isActive ? t("apiKeysForm.activeLabel") : t("apiKeysForm.inactiveLabel")}
</span> </span>
+5 -5
View File
@@ -82,12 +82,12 @@ export default async function AdminRadioAutoDjPage() {
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Disc3 size={20} className="text-[var(--color-primary)]" /> <Disc3 size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("autoDjForm.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("autoDjForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("autoDjForm.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("autoDjForm.subtitle")}</p>
</div> </div>
</div> </div>
@@ -250,7 +250,7 @@ export default async function AdminRadioAutoDjPage() {
</td> </td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${track.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${track.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{track.isActive ? t("autoDjForm.activeLabel") : t("autoDjForm.inactiveLabel")} {track.isActive ? t("autoDjForm.activeLabel") : t("autoDjForm.inactiveLabel")}
</span> </span>
+5 -5
View File
@@ -62,12 +62,12 @@ export default async function AdminRadioBannersPage() {
</nav> </nav>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<LucideImage size={20} className="text-[var(--color-primary)]" /> <LucideImage size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("banners.heading")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("banners.heading")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("banners.description")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("banners.description")}</p>
</div> </div>
</div> </div>
@@ -160,7 +160,7 @@ export default async function AdminRadioBannersPage() {
<span className="text-sm theme-text-muted dark:theme-text-muted inline-flex gap-2 items-center"> <span className="text-sm theme-text-muted dark:theme-text-muted inline-flex gap-2 items-center">
{t("banners.byUser", { id: String(b.userId) })} · {formatDate(b.createdAt)} ·{" "} {t("banners.byUser", { id: String(b.userId) })} · {formatDate(b.createdAt)} ·{" "}
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${b.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${b.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{b.isActive ? t("banners.active") : t("banners.hidden")} {b.isActive ? t("banners.active") : t("banners.hidden")}
</span> </span>
+1 -1
View File
@@ -39,7 +39,7 @@ export function EmbedSnippet({
return ( return (
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center justify-between gap-4 mb-1"> <div className="flex items-center justify-between gap-4 mb-1">
<h2 className="m-0 text-base font-bold text-[var(--color-text)]">{title}</h2> <h2 className="m-0 text-base font-bold text-[var(--admin-text)]">{title}</h2>
<button type="button" className="btn btn-primary" onClick={copy}> <button type="button" className="btn btn-primary" onClick={copy}>
{copied ? "Copied!" : "Copy"} {copied ? "Copied!" : "Copy"}
</button> </button>
+4 -4
View File
@@ -54,12 +54,12 @@ export default async function AdminRadioEmbedPage() {
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Code size={20} className="text-[var(--color-primary)]" /> <Code size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("embedPage.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("embedPage.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("embedPage.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("embedPage.subtitle")}</p>
</div> </div>
</div> </div>
+5 -5
View File
@@ -82,12 +82,12 @@ export default async function AdminRadioHistoryPage() {
</nav> </nav>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Clock size={20} className="text-[var(--color-primary)]" /> <Clock size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("historyPage.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("historyPage.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("historyPage.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("historyPage.subtitle")}</p>
</div> </div>
</div> </div>
@@ -142,7 +142,7 @@ export default async function AdminRadioHistoryPage() {
{formatDate(h.endedAt)} {formatDate(h.endedAt)}
</span> </span>
) : ( ) : (
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-success-subtle)] text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-success-subtle)] text-[var(--admin-text)]">
{t("historyPage.onAir")} {t("historyPage.onAir")}
</span> </span>
)} )}
+4 -4
View File
@@ -51,12 +51,12 @@ export default async function AdminRadioModerationPage() {
</nav> </nav>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Shield size={20} className="text-[var(--color-primary)]" /> <Shield size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("moderationPage.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("moderationPage.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("moderationPage.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("moderationPage.subtitle")}</p>
</div> </div>
</div> </div>
+4 -4
View File
@@ -205,12 +205,12 @@ export default async function AdminRadioMonitoringPage() {
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Radio size={20} className="text-[var(--color-primary)]" /> <Radio size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("monitoring.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("monitoring.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t("monitoring.subtitle", { timeout: FETCH_TIMEOUT_MS / 1000 })} {t("monitoring.subtitle", { timeout: FETCH_TIMEOUT_MS / 1000 })}
</p> </p>
</div> </div>
+2 -2
View File
@@ -208,7 +208,7 @@ export default async function AdminRadioPage() {
<td>{a.discordUsername ?? "\u2014"}</td> <td>{a.discordUsername ?? "\u2014"}</td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${statusBadgeClass(a.status) === "ok" ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : statusBadgeClass(a.status) === "danger" ? "bg-[var(--color-error-subtle)] text-[var(--color-text)]" : "bg-[var(--color-primary)]/18 text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${statusBadgeClass(a.status) === "ok" ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : statusBadgeClass(a.status) === "danger" ? "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]"}`}
> >
{a.status} {a.status}
</span> </span>
@@ -251,7 +251,7 @@ export default async function AdminRadioPage() {
<td>{sc.showName ?? "\u2014"}</td> <td>{sc.showName ?? "\u2014"}</td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${sc.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${sc.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{sc.isActive ? t("active") : t("inactive")} {sc.isActive ? t("active") : t("inactive")}
</span> </span>
+5 -5
View File
@@ -68,18 +68,18 @@ export default async function AdminRadioPointsPage({
<section className="mt-6"> <section className="mt-6">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Star size={20} className="text-[var(--color-primary)]" /> <Star size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("pointsForm.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("pointsForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("pointsForm.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("pointsForm.subtitle")}</p>
</div> </div>
</div> </div>
{saved === "1" ? ( {saved === "1" ? (
<p> <p>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-success-subtle)] text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-success-subtle)] text-[var(--admin-text)]">
{t("pointsForm.saved")} {t("pointsForm.saved")}
</span> </span>
</p> </p>
+5 -5
View File
@@ -50,12 +50,12 @@ export default async function AdminRadioRanksPage() {
</nav> </nav>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Medal size={20} className="text-[var(--color-primary)]" /> <Medal size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("ranksForm.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("ranksForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("ranksForm.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("ranksForm.subtitle")}</p>
</div> </div>
</div> </div>
@@ -122,7 +122,7 @@ export default async function AdminRadioRanksPage() {
<div className="flex justify-between items-baseline gap-3 mb-2"> <div className="flex justify-between items-baseline gap-3 mb-2">
<strong>#{String(r.id)}</strong> <strong>#{String(r.id)}</strong>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${r.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${r.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{r.isActive ? t("ranksForm.activeLabel") : t("ranksForm.inactiveLabel")} {r.isActive ? t("ranksForm.activeLabel") : t("ranksForm.inactiveLabel")}
</span> </span>
+4 -4
View File
@@ -225,12 +225,12 @@ export default async function AdminRadioSettingsPage() {
</nav> </nav>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Settings size={20} className="text-[var(--color-primary)]" /> <Settings size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("settingsForm.title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("settingsForm.title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("settingsForm.subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("settingsForm.subtitle")}</p>
</div> </div>
</div> </div>
+8 -8
View File
@@ -40,12 +40,12 @@ export default async function AdminRareValues() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Gem size={20} className="text-[var(--color-primary)]" /> <Gem size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -55,7 +55,7 @@ export default async function AdminRareValues() {
</div> </div>
<form action={createCategory} className="admin-card mb-6"> <form action={createCategory} className="admin-card mb-6">
<h3 className="text-sm font-bold text-[var(--color-text)] m-0 mb-3">{t("addCategory")}</h3> <h3 className="text-sm font-bold text-[var(--admin-text)] m-0 mb-3">{t("addCategory")}</h3>
<div className="flex gap-2 flex-wrap"> <div className="flex gap-2 flex-wrap">
<input <input
name="name" name="name"
@@ -89,10 +89,10 @@ export default async function AdminRareValues() {
<div className="flex gap-3 items-baseline justify-between flex-wrap"> <div className="flex gap-3 items-baseline justify-between flex-wrap">
<div style={{ display: "flex", gap: "0.6rem", alignItems: "baseline", flexWrap: "wrap" }}> <div style={{ display: "flex", gap: "0.6rem", alignItems: "baseline", flexWrap: "wrap" }}>
<strong>{cat.name}</strong> <strong>{cat.name}</strong>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{t("badgeLabel", { badge: cat.badge })} {t("badgeLabel", { badge: cat.badge })}
</span> </span>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{t("priorityLabel", { priority: cat.priority })} {t("priorityLabel", { priority: cat.priority })}
</span> </span>
<span className="text-sm theme-text-muted dark:theme-text-muted"> <span className="text-sm theme-text-muted dark:theme-text-muted">
@@ -129,7 +129,7 @@ export default async function AdminRareValues() {
<td>{v.creditValue ?? "—"}</td> <td>{v.creditValue ?? "—"}</td>
<td>{v.currencyValue ?? "—"}</td> <td>{v.currencyValue ?? "—"}</td>
<td> <td>
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{v.currencyType} {v.currencyType}
</span> </span>
</td> </td>
+1 -1
View File
@@ -78,7 +78,7 @@ function getColumns(canEdit: boolean): DataTableColumn<RoomRow>[] {
sortable: true, sortable: true,
render: (_, row) => ( render: (_, row) => (
<span className="flex items-center gap-1.5 text-sm"> <span className="flex items-center gap-1.5 text-sm">
{row.users > 0 && <Wifi className="h-3 w-3 text-[var(--color-success-text-readable)]" />} {row.users > 0 && <Wifi className="h-3 w-3 text-[var(--admin-success)]" />}
<span className={row.users > 0 ? 'font-medium' : 'text-muted-foreground'}> <span className={row.users > 0 ? 'font-medium' : 'text-muted-foreground'}>
{row.users} {row.users}
</span> </span>
+4 -4
View File
@@ -19,12 +19,12 @@ export default async function AdminSettings() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<span className="text-lg">⚙️</span> <span className="text-lg">⚙️</span>
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })} {t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })}
</p> </p>
</div> </div>
@@ -52,7 +52,7 @@ export default async function AdminSettings() {
<section> <section>
<h2 className="admin-section-title"> <h2 className="admin-section-title">
{t("settings")}{" "} {t("settings")}{" "}
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{settings.length} {settings.length}
</span> </span>
</h2> </h2>
+4 -4
View File
@@ -22,14 +22,14 @@ export default async function EditShopArticle({ params }: { params: Promise<{ id
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Package size={20} className="text-[var(--color-primary)]" /> <Package size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("editPackageTitle")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("editPackageTitle")}</h1>
<Link <Link
href="/admin/shop" href="/admin/shop"
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)]" className="text-xs text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)]"
> >
← {t("backToShop")} ← {t("backToShop")}
</Link> </Link>
+4 -4
View File
@@ -9,14 +9,14 @@ export default async function NewShopArticle() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Package size={20} className="text-[var(--color-primary)]" /> <Package size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("newPackageTitle")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("newPackageTitle")}</h1>
<Link <Link
href="/admin/shop" href="/admin/shop"
className="text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)]" className="text-xs text-[var(--admin-text-muted)] hover:text-[var(--admin-accent)]"
> >
← {t("backToShop")} ← {t("backToShop")}
</Link> </Link>
+1 -1
View File
@@ -144,7 +144,7 @@ export default async function AdminShop() {
<td> <td>
{o.status ? ( {o.status ? (
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${state === "ok" ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : state === "danger" ? "bg-[var(--color-error-subtle)] text-[var(--color-text)]" : "bg-[var(--color-primary)]/18 text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${state === "ok" ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : state === "danger" ? "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]"}`}
> >
{o.status} {o.status}
</span> </span>
+5 -5
View File
@@ -48,12 +48,12 @@ export default async function AdminSubscriptions() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<CreditCard size={20} className="text-[var(--color-primary)]" /> <CreditCard size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -93,7 +93,7 @@ export default async function AdminSubscriptions() {
<td>{s.subscriptionType ?? "—"}</td> <td>{s.subscriptionType ?? "—"}</td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${soon ? "bg-[var(--color-primary)]/18 text-[var(--color-text)]" : "bg-[var(--color-success-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${soon ? "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]" : "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]"}`}
> >
{soon ? t("statusExpiring") : t("statusActive")} {soon ? t("statusExpiring") : t("statusActive")}
</span> </span>
+6 -6
View File
@@ -47,12 +47,12 @@ export default async function AdminTagsPage() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Hash size={20} className="text-[var(--color-primary)]" /> <Hash size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -153,8 +153,8 @@ export default async function AdminTagsPage() {
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${ className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${
(usage.get(String(tag.id)) ?? 0) > 0 (usage.get(String(tag.id)) ?? 0) > 0
? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]"
: "bg-[var(--color-primary)]/18 text-[var(--color-text)]" : "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]"
}`} }`}
> >
{usage.get(String(tag.id)) ?? 0} {usage.get(String(tag.id)) ?? 0}
+6 -6
View File
@@ -39,12 +39,12 @@ export default async function AdminTeams() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Users size={20} className="text-[var(--color-primary)]" /> <Users size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -137,7 +137,7 @@ export default async function AdminTeams() {
width: 14, width: 14,
height: 14, height: 14,
borderRadius: 4, borderRadius: 4,
border: "1px solid var(--border-color)", border: "1px solid var(--admin-border)",
background: team.staffColor, background: team.staffColor,
}} }}
/> />
@@ -146,7 +146,7 @@ export default async function AdminTeams() {
</td> </td>
<td> <td>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${team.hiddenRank ? "bg-[var(--color-primary)]/18 text-[var(--color-text)]" : "bg-[var(--color-success-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${team.hiddenRank ? "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]" : "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]"}`}
> >
{team.hiddenRank ? t("hidden") : t("visible")} {team.hiddenRank ? t("hidden") : t("visible")}
</span> </span>
+5 -5
View File
@@ -112,12 +112,12 @@ export default async function AdminTheme({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<span className="text-lg">🎨</span> <span className="text-lg">🎨</span>
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })} {t.rich("subtitle", { code: (chunks) => <code>{chunks}</code> })}
</p> </p>
</div> </div>
@@ -178,7 +178,7 @@ export default async function AdminTheme({
<section className="mt-6"> <section className="mt-6">
<h2 className="admin-section-title">Gradient mix</h2> <h2 className="admin-section-title">Gradient mix</h2>
<p className="text-xs text-[var(--color-text-muted)] mb-2"> <p className="text-xs text-[var(--admin-text-muted)] mb-2">
Control the card header and hero background gradients. Control the card header and hero background gradients.
</p> </p>
<div className="admin-card">{colorGrid(GRADIENT_FIELDS, current)}</div> <div className="admin-card">{colorGrid(GRADIENT_FIELDS, current)}</div>
@@ -242,7 +242,7 @@ export default async function AdminTheme({
<section className="mt-6"> <section className="mt-6">
<h2 className="admin-section-title">{t("cssTitle")}</h2> <h2 className="admin-section-title">{t("cssTitle")}</h2>
<p className="text-xs text-[var(--color-text-muted)] mb-2">{t("cssSubtitle")}</p> <p className="text-xs text-[var(--admin-text-muted)] mb-2">{t("cssSubtitle")}</p>
<textarea <textarea
name="custom_css" name="custom_css"
defaultValue={customCss} defaultValue={customCss}
@@ -57,17 +57,17 @@ interface StaffMember {
} }
const STATUS_COLORS: Record<string, string> = { const STATUS_COLORS: Record<string, string> = {
open: 'bg-[var(--color-info-subtle)] text-[var(--color-info-text-readable)]', open: 'bg-[var(--admin-info-subtle)] text-[var(--admin-info)]',
in_progress: 'bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)]', in_progress: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]',
waiting: 'bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)]', waiting: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]',
closed: 'bg-muted text-muted-foreground', closed: 'bg-muted text-muted-foreground',
} }
const PRIORITY_COLORS: Record<string, string> = { const PRIORITY_COLORS: Record<string, string> = {
low: 'text-muted-foreground', low: 'text-muted-foreground',
normal: 'text-[var(--color-info-text-readable)]', normal: 'text-[var(--admin-info)]',
high: 'text-[var(--color-warning-text-readable)]', high: 'text-[var(--admin-warning)]',
urgent: 'text-[var(--color-error-text-readable)]', urgent: 'text-[var(--admin-error)]',
} }
export function AdminTicketDetail({ export function AdminTicketDetail({
+9 -9
View File
@@ -7,17 +7,17 @@ import { canAccess, getAdminContext, PERMS } from '@/lib/permissions'
import { prisma } from '@/lib/prisma' import { prisma } from '@/lib/prisma'
const STATUS_CONFIG = { const STATUS_CONFIG = {
open: { label: 'Open', icon: AlertCircle, class: 'bg-[var(--color-info-subtle)] text-[var(--color-info-text-readable)]' }, open: { label: 'Open', icon: AlertCircle, class: 'bg-[var(--admin-info-subtle)] text-[var(--admin-info)]' },
in_progress: { label: 'In Progress', icon: Loader2, class: 'bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)]' }, in_progress: { label: 'In Progress', icon: Loader2, class: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]' },
waiting: { label: 'Waiting', icon: Clock, class: 'bg-[var(--color-warning-subtle)] text-[var(--color-warning-text-readable)]' }, waiting: { label: 'Waiting', icon: Clock, class: 'bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]' },
closed: { label: 'Closed', icon: CheckCircle2, class: 'bg-muted text-muted-foreground' }, closed: { label: 'Closed', icon: CheckCircle2, class: 'bg-muted text-muted-foreground' },
} as const } as const
const PRIORITY_COLORS = { const PRIORITY_COLORS = {
low: '', low: '',
normal: 'text-[var(--color-info-text-readable)]', normal: 'text-[var(--admin-info)]',
high: 'text-[var(--color-warning-text-readable)]', high: 'text-[var(--admin-warning)]',
urgent: 'text-[var(--color-error-text-readable)] font-bold', urgent: 'text-[var(--admin-error)] font-bold',
} }
interface PageProps { interface PageProps {
@@ -77,7 +77,7 @@ export default async function AdminTicketsPage({ searchParams }: PageProps) {
<div className="grid gap-4 grid-cols-2 lg:grid-cols-4"> <div className="grid gap-4 grid-cols-2 lg:grid-cols-4">
<Card> <Card>
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-[var(--color-info-text-readable)]">Open</CardTitle> <CardTitle className="text-sm font-medium text-[var(--admin-info)]">Open</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold">{openCount}</div> <div className="text-2xl font-bold">{openCount}</div>
@@ -85,7 +85,7 @@ export default async function AdminTicketsPage({ searchParams }: PageProps) {
</Card> </Card>
<Card> <Card>
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-[var(--color-warning-text-readable)]">In Progress</CardTitle> <CardTitle className="text-sm font-medium text-[var(--admin-warning)]">In Progress</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold">{inProgressCount}</div> <div className="text-2xl font-bold">{inProgressCount}</div>
@@ -93,7 +93,7 @@ export default async function AdminTicketsPage({ searchParams }: PageProps) {
</Card> </Card>
<Card> <Card>
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-[var(--color-warning-text-readable)]">Waiting</CardTitle> <CardTitle className="text-sm font-medium text-[var(--admin-warning)]">Waiting</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="text-2xl font-bold">{waitingCount}</div> <div className="text-2xl font-bold">{waitingCount}</div>
+5 -5
View File
@@ -80,12 +80,12 @@ export default async function AdminTransactions({
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<ArrowLeftRight size={20} className="text-[var(--color-primary)]" /> <ArrowLeftRight size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -141,7 +141,7 @@ export default async function AdminTransactions({
<td> <td>
{x.status ? ( {x.status ? (
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${state === "ok" ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : state === "danger" ? "bg-[var(--color-error-subtle)] text-[var(--color-text)]" : "bg-[var(--color-primary)]/18 text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${state === "ok" ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : state === "danger" ? "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-accent)]/18 text-[var(--admin-text)]"}`}
> >
{x.status} {x.status}
</span> </span>
@@ -170,7 +170,7 @@ export function ClientTranslations({ files, activeFile, entries, parseError }: P
)} )}
{activeFile.readOnly && ( {activeFile.readOnly && (
<div className="rounded-md border border-[var(--color-warning-border)] bg-[var(--color-warning-subtle)] px-3 py-2 text-xs text-[var(--color-warning-text-readable)] dark:text-[var(--color-warning-text-readable)] flex items-start gap-2"> <div className="rounded-md border border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)] px-3 py-2 text-xs text-[var(--admin-warning)] dark:text-[var(--admin-warning)] flex items-start gap-2">
<Lock className="w-4 h-4 flex-shrink-0 mt-0.5" /> <Lock className="w-4 h-4 flex-shrink-0 mt-0.5" />
<div> <div>
Questo file è di sola lettura.{' '} Questo file è di sola lettura.{' '}
@@ -180,7 +180,7 @@ export function ClientTranslations({ files, activeFile, entries, parseError }: P
)} )}
{activeFile.hasComments && !activeFile.readOnly && ( {activeFile.hasComments && !activeFile.readOnly && (
<div className="rounded-md border border-[var(--color-info-border)] bg-[var(--color-info-subtle)] px-3 py-2 text-xs text-[var(--color-info-text-readable)] dark:text-[var(--color-info-text-readable)] flex items-start gap-2"> <div className="rounded-md border border-[var(--admin-info-border)] bg-[var(--admin-info-subtle)] px-3 py-2 text-xs text-[var(--admin-info)] dark:text-[var(--admin-info)] flex items-start gap-2">
<Info className="w-4 h-4 flex-shrink-0 mt-0.5" /> <Info className="w-4 h-4 flex-shrink-0 mt-0.5" />
<div> <div>
I commenti vengono preservati: il salvataggio modifica solo le righe dei valori I commenti vengono preservati: il salvataggio modifica solo le righe dei valori
+4 -4
View File
@@ -80,14 +80,14 @@ export default async function AdminUserEdit({ params }: { params: Promise<{ id:
<Link href={`/admin/users/${user.id}`}>← {user.username}</Link> <Link href={`/admin/users/${user.id}`}>← {user.username}</Link>
</p> </p>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<User size={20} className="text-[var(--color-primary)]" /> <User size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]"> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
{t("title", { username: user.username })} {t("title", { username: user.username })}
</h1> </h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("idLabel", { id: user.id })}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("idLabel", { id: user.id })}</p>
</div> </div>
</div> </div>
@@ -127,7 +127,7 @@ export function UserQuickActions({ userId, username, isOnline }: UserQuickAction
<Unplug className="mr-2 h-4 w-4" /> <Unplug className="mr-2 h-4 w-4" />
Disconnect Disconnect
{isOnline && ( {isOnline && (
<Badge variant="default" className="ml-auto text-[10px] bg-[var(--color-success-subtle)]"> <Badge variant="default" className="ml-auto text-[10px] bg-[var(--admin-success-subtle)]">
Online Online
</Badge> </Badge>
)} )}
@@ -209,7 +209,7 @@ export function UserQuickActions({ userId, username, isOnline }: UserQuickAction
{newPassword} {newPassword}
</code> </code>
<Button variant="ghost" size="icon" onClick={copyPassword}> <Button variant="ghost" size="icon" onClick={copyPassword}>
{copied ? <Check className="h-4 w-4 text-[var(--color-success-text-readable)]" /> : <Copy className="h-4 w-4" />} {copied ? <Check className="h-4 w-4 text-[var(--admin-success)]" /> : <Copy className="h-4 w-4" />}
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>
@@ -82,10 +82,10 @@ export function UserHero({
className={`absolute -top-1 -right-1 flex h-4 w-4 ${isOnline ? '' : 'opacity-50'}`} className={`absolute -top-1 -right-1 flex h-4 w-4 ${isOnline ? '' : 'opacity-50'}`}
> >
{isOnline && ( {isOnline && (
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--color-success)] opacity-75" /> <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--admin-success)] opacity-75" />
)} )}
<span <span
className={`relative inline-flex rounded-full h-4 w-4 border-2 border-card ${isOnline ? 'bg-[var(--color-success-subtle)]' : 'bg-zinc-400'}`} className={`relative inline-flex rounded-full h-4 w-4 border-2 border-card ${isOnline ? 'bg-[var(--admin-success-subtle)]' : 'bg-[var(--admin-text-muted)]'}`}
/> />
</span> </span>
</div> </div>
@@ -129,7 +129,7 @@ export function UserHero({
<Badge>{user.permission.rankName || `Rank ${user.rank}`}</Badge> <Badge>{user.permission.rankName || `Rank ${user.rank}`}</Badge>
<Badge <Badge
variant={isOnline ? 'default' : 'outline'} variant={isOnline ? 'default' : 'outline'}
className={isOnline ? 'bg-[var(--color-success-subtle)]' : ''} className={isOnline ? 'bg-[var(--admin-success-subtle)]' : ''}
> >
{isOnline ? ( {isOnline ? (
<> <>
@@ -143,7 +143,7 @@ export function UserHero({
</Badge> </Badge>
{activeBan && <Badge variant="destructive">Banned</Badge>} {activeBan && <Badge variant="destructive">Banned</Badge>}
{user.twoFactorConfirmed ? ( {user.twoFactorConfirmed ? (
<Badge variant="outline" className="border-[var(--color-success-border)] text-[var(--color-success-text-readable)]"> <Badge variant="outline" className="border-[var(--admin-success-border)] text-[var(--admin-success)]">
<Shield className="mr-1 h-3 w-3" /> 2FA <Shield className="mr-1 h-3 w-3" /> 2FA
</Badge> </Badge>
) : null} ) : null}
@@ -152,22 +152,22 @@ export function UserHero({
{/* Currency row */} {/* Currency row */}
<div className="flex flex-wrap gap-3 pt-1"> <div className="flex flex-wrap gap-3 pt-1">
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Coins className="h-4 w-4 text-[var(--color-warning-text-readable)]" /> <Coins className="h-4 w-4 text-[var(--admin-warning)]" />
<span className="font-semibold">{user.credits.toLocaleString()}</span> <span className="font-semibold">{user.credits.toLocaleString()}</span>
<span className="text-muted-foreground text-xs">credits</span> <span className="text-muted-foreground text-xs">credits</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Diamond className="h-4 w-4 text-[var(--color-info-text-readable)]" /> <Diamond className="h-4 w-4 text-[var(--admin-info)]" />
<span className="font-semibold">{diamonds.toLocaleString()}</span> <span className="font-semibold">{diamonds.toLocaleString()}</span>
<span className="text-muted-foreground text-xs">diamonds</span> <span className="text-muted-foreground text-xs">diamonds</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Sparkles className="h-4 w-4 text-purple-400" /> <Sparkles className="h-4 w-4 text-[var(--admin-accent-text)]" />
<span className="font-semibold">{duckets.toLocaleString()}</span> <span className="font-semibold">{duckets.toLocaleString()}</span>
<span className="text-muted-foreground text-xs">duckets</span> <span className="text-muted-foreground text-xs">duckets</span>
</div> </div>
<div className="flex items-center gap-1.5 text-sm"> <div className="flex items-center gap-1.5 text-sm">
<Gem className="h-4 w-4 text-pink-400" /> <Gem className="h-4 w-4 text-[var(--admin-error)]" />
<span className="font-semibold">{user.pixels.toLocaleString()}</span> <span className="font-semibold">{user.pixels.toLocaleString()}</span>
<span className="text-muted-foreground text-xs">pixels</span> <span className="text-muted-foreground text-xs">pixels</span>
</div> </div>
+1 -1
View File
@@ -93,7 +93,7 @@ export default async function MultiAccountsPage({
<span className="text-xs text-muted-foreground">#{acc.id}</span> <span className="text-xs text-muted-foreground">#{acc.id}</span>
{acc.rank >= 5 && <Badge variant="outline">staff</Badge>} {acc.rank >= 5 && <Badge variant="outline">staff</Badge>}
{acc.online === '1' && ( {acc.online === '1' && (
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-success-subtle)]" /> <span className="h-1.5 w-1.5 rounded-full bg-[var(--admin-success-subtle)]" />
)} )}
</Link> </Link>
))} ))}
+1 -1
View File
@@ -122,7 +122,7 @@ function getColumns(canEdit: boolean): DataTableColumn<UserRow>[] {
], ],
render: (value) => render: (value) =>
value === '1' ? ( value === '1' ? (
<span className="flex items-center gap-1.5 text-[var(--color-success-text-readable)]"> <span className="flex items-center gap-1.5 text-[var(--admin-success)]">
<Wifi className="h-3.5 w-3.5" /> Online <Wifi className="h-3.5 w-3.5" /> Online
</span> </span>
) : ( ) : (
+7 -7
View File
@@ -55,12 +55,12 @@ export default async function AdminVouchers() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Gift size={20} className="text-[var(--color-primary)]" /> <Gift size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -138,15 +138,15 @@ export default async function AdminVouchers() {
</td> </td>
<td> <td>
{expired ? ( {expired ? (
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-error-subtle)] text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-error-subtle)] text-[var(--admin-text)]">
{t("statusExpired")} {t("statusExpired")}
</span> </span>
) : usedUp ? ( ) : usedUp ? (
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{t("statusUsedUp")} {t("statusUsedUp")}
</span> </span>
) : ( ) : (
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-success-subtle)] text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-success-subtle)] text-[var(--admin-text)]">
{t("statusActive")} {t("statusActive")}
</span> </span>
)} )}
+5 -5
View File
@@ -29,12 +29,12 @@ export default async function AdminVpn({ searchParams }: { searchParams: Promise
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<ShieldBan size={20} className="text-[var(--color-primary)]" /> <ShieldBan size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5"> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
{t.rich("subtitle", { ipLink: (chunks) => <Link href="/admin/ip">{chunks}</Link> })} {t.rich("subtitle", { ipLink: (chunks) => <Link href="/admin/ip">{chunks}</Link> })}
</p> </p>
</div> </div>
@@ -42,7 +42,7 @@ export default async function AdminVpn({ searchParams }: { searchParams: Promise
<section className="mt-6"> <section className="mt-6">
{sp.saved ? ( {sp.saved ? (
<p className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-success-subtle)] text-[var(--color-text)]"> <p className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-success-subtle)] text-[var(--admin-text)]">
{t("settingsSaved")} {t("settingsSaved")}
</p> </p>
) : null} ) : null}
+5 -5
View File
@@ -25,12 +25,12 @@ export default async function AdminWordFilter() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Filter size={20} className="text-[var(--color-primary)]" /> <Filter size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -59,7 +59,7 @@ export default async function AdminWordFilter() {
<section className="mt-6"> <section className="mt-6">
<h2 className="admin-section-title"> <h2 className="admin-section-title">
{t("words")}{" "} {t("words")}{" "}
<span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--color-primary)]/18 text-[var(--color-text)]"> <span className="inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full bg-[var(--admin-accent)]/18 text-[var(--admin-text)]">
{words.length} {words.length}
</span> </span>
</h2> </h2>
+5 -5
View File
@@ -22,12 +22,12 @@ export default async function AdminWriteableBoxes() {
return ( return (
<main> <main>
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center"> <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
<Layout size={20} className="text-[var(--color-primary)]" /> <Layout size={20} className="text-[var(--admin-accent)]" />
</div> </div>
<div> <div>
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1> <h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p> <p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
</div> </div>
</div> </div>
@@ -118,7 +118,7 @@ export default async function AdminWriteableBoxes() {
{box.icon ? <span aria-hidden>{box.icon}</span> : null} {box.icon ? <span aria-hidden>{box.icon}</span> : null}
<strong className="flex-1">{box.title}</strong> <strong className="flex-1">{box.title}</strong>
<span <span
className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${box.isActive ? "bg-[var(--color-success-subtle)] text-[var(--color-text)]" : "bg-[var(--color-error-subtle)] text-[var(--color-text)]"}`} className={`inline-block text-[0.72rem] font-bold px-2 py-0.5 rounded-full ${box.isActive ? "bg-[var(--admin-success-subtle)] text-[var(--admin-text)]" : "bg-[var(--admin-error-subtle)] text-[var(--admin-text)]"}`}
> >
{box.isActive ? t("active") : t("hidden")} {box.isActive ? t("active") : t("hidden")}
</span> </span>
+12 -1
View File
@@ -60,7 +60,7 @@ describe("admin theme source audit", () => {
...(source.match(PUBLIC_STRUCTURAL_TOKEN) ?? []), ...(source.match(PUBLIC_STRUCTURAL_TOKEN) ?? []),
...(source.match(HARDCODED_UI_PALETTE) ?? []), ...(source.match(HARDCODED_UI_PALETTE) ?? []),
]; ];
const filtered = DATA_COLOR_ALLOWLIST.includes(normalized) const filtered = [...DATA_COLOR_ALLOWLIST, ...GRAPHICAL_ALLOWLIST].includes(normalized)
? risky.filter((match) => match.startsWith("var(")) ? risky.filter((match) => match.startsWith("var("))
: risky; : risky;
if (filtered.length) violations.push(`${normalized}: ${[...new Set(filtered)].join(", ")}`); if (filtered.length) violations.push(`${normalized}: ${[...new Set(filtered)].join(", ")}`);
@@ -68,4 +68,15 @@ describe("admin theme source audit", () => {
} }
expect(violations).toEqual([]); expect(violations).toEqual([]);
}); });
it("keeps every import workflow on semantic admin status and overlay colors", () => {
const violations = sourceFiles("src/app/admin/import").flatMap((file) => {
const source = readFileSync(file, "utf8");
const risky = source.match(/rgba?\([^)]*\)|(?:bg-black|text-white)(?:\/\d+)?/g) ?? [];
return risky.length
? [`${relative(process.cwd(), file).replaceAll("\\", "/")}: ${[...new Set(risky)].join(", ")}`]
: [];
});
expect(violations).toEqual([]);
});
}); });