Clean up admin hub UX: tabs, chrome, and redundant headers.
Local Build and Deploy / deploy (push) Failing after 53s
Local Build and Deploy / deploy (push) Failing after 53s
Wire orphan routes into hub tabs, group Radio into primary/tools rows, show theme/language once on desktop, and remove duplicate page titles under AdminHubChrome. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
378705dd73
commit
d2c0619f2f
75 files changed
+244
-915
No files matched your search
@@ -1,5 +1,3 @@
|
||||
import { Key } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
createApiKey,
|
||||
@@ -71,30 +69,7 @@ export default async function AdminRadioApiKeysPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/api-keys">{t("apiKeys")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/autodj">{t("autoDj")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/embed">{t("embed")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/points">{t("points")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/monitoring">{t("monitoring.title")}</Link>
|
||||
</nav>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("apiKeysForm.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("apiKeysForm.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card" style={{ marginTop: "1rem" }}>
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { Disc3 } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
createTrack,
|
||||
@@ -75,29 +72,8 @@ export default async function AdminRadioAutoDjPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/autodj">{t("autoDj")}</Link>
|
||||
</nav>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("autoDjForm.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("autoDjForm.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card mb-6">
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Image as LucideImage } from "lucide-react";
|
||||
import NextImage from "next/image";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
createRadioBanner,
|
||||
@@ -57,28 +55,6 @@ export default async function AdminRadioBannersPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("banners.heading")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("banners.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card mb-6">
|
||||
<p className="text-xs theme-text-muted dark:theme-text-muted m-0">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Code } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
@@ -41,30 +40,7 @@ export default async function AdminRadioEmbedPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/embed">{t("embed")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("embedPage.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("embedPage.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
||||
<StatusCard
|
||||
label={t("embedPage.streamUrl")}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Clock } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
@@ -76,28 +74,6 @@ export default async function AdminRadioHistoryPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("historyPage.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("historyPage.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card mb-6">
|
||||
<p className="text-xs theme-text-muted dark:theme-text-muted m-0">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Shield } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { deleteShout } from "@/actions/admin-radio-moderation";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
@@ -41,28 +39,6 @@ export default async function AdminRadioModerationPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/moderation">{t("moderation")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("moderationPage.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("moderationPage.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
||||
<StatusCard
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Radio } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { DiagnosticRow, StatusCard } from "@/components/admin/dashboard";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
@@ -213,30 +211,8 @@ export default async function AdminRadioMonitoringPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/monitoring">{t("monitoring.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("monitoring.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("monitoring.subtitle", { timeout: FETCH_TIMEOUT_MS / 1000 })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
||||
<StatusCard
|
||||
label={t("monitoring.stream")}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { deleteShout } from "@/actions/admin-radio-moderation";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
@@ -110,19 +109,6 @@ export default async function AdminRadioPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<h1>{t("title")}</h1>
|
||||
<p className="text-xs theme-text-muted dark:theme-text-muted">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
|
||||
<StatusCard
|
||||
label={t("shouts")}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Star } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { savePoints } from "@/actions/admin-radio-points";
|
||||
import { StatusCard } from "@/components/admin/dashboard";
|
||||
@@ -63,30 +61,8 @@ export default async function AdminRadioPointsPage({
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/points">{t("points")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("pointsForm.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("pointsForm.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{saved === "1" ? (
|
||||
<p>
|
||||
<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)]">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Medal } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
createRadioRank,
|
||||
@@ -45,28 +43,6 @@ export default async function AdminRadioRanksPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("ranksForm.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("ranksForm.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card mb-6">
|
||||
<p className="text-xs theme-text-muted dark:theme-text-muted m-0">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { Settings } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import {
|
||||
saveRadioSetting,
|
||||
@@ -397,28 +395,6 @@ export default async function AdminRadioSettingsPage() {
|
||||
|
||||
return (
|
||||
<main>
|
||||
<nav className="text-xs theme-text-muted dark:theme-text-muted mb-2">
|
||||
<Link href="/admin/radio">{t("title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/settings">{t("settings")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/banners">{t("banners.title")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/ranks">{t("ranks")}</Link> ·{" "}
|
||||
<Link href="/admin/radio/history">{t("history")}</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<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(--admin-accent)]" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("settingsForm.title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">
|
||||
{t("settingsForm.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dbError ? (
|
||||
<div className="admin-card mb-6">
|
||||
<p className="text-xs theme-text-muted dark:theme-text-muted m-0">
|
||||
|
||||
Reference in new issue
Block a user