Mobile-responsive admin nav/thread header, real dark theme pass
Nav overflowed the viewport on phones (confirmed via screenshot) — header now wraps, nav row scrolls horizontally instead of pushing user/theme/ logout off-screen, ticket-thread's status/assignee selects stack full-width below the subject on narrow screens. Dark theme got a real pass instead of reusing near-identical values: deeper/richer surfaces, punchier accent (#8b5cf6, matches the already- validated dark chart-1), a subtle violet glow behind the page, and a dark-appropriate card shadow (inset highlight + soft shadow — the flat black shadow from light mode was invisible on a dark surface). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QcXH24ky6zjk2UyK5oZUPH
This commit is contained in:
1 parent
9f1413f0b9
commit
cda92de9d2
4 files changed
+42
-29
No files matched your search
@@ -14,22 +14,24 @@ export default async function AdminLayout({ children }: { children: React.ReactN
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen flex-col bg-bg">
|
<div className="flex min-h-screen flex-col bg-bg">
|
||||||
<header className="border-b border-border bg-surface">
|
<header className="border-b border-border bg-surface">
|
||||||
<div className="mx-auto flex max-w-7xl items-center gap-6 px-6 py-3">
|
<div className="mx-auto flex max-w-7xl flex-wrap items-center gap-x-6 gap-y-2 px-4 py-3 sm:px-6">
|
||||||
<div className="flex items-center gap-2 font-display text-[15px] font-bold tracking-tight">
|
<div className="flex items-center gap-2 font-display text-[15px] font-bold tracking-tight">
|
||||||
<div className="flex h-7 w-7 items-center justify-center rounded-md bg-accent text-white">
|
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-accent text-white">
|
||||||
<Ticket size={15} strokeWidth={2.5} />
|
<Ticket size={15} strokeWidth={2.5} />
|
||||||
</div>
|
</div>
|
||||||
top-tickets
|
top-tickets
|
||||||
</div>
|
</div>
|
||||||
<NavLinks />
|
<div className="ml-auto flex items-center gap-2 sm:gap-3">
|
||||||
<div className="ml-auto flex items-center gap-3">
|
<span className="hidden text-sm text-text-muted sm:inline">{session.user.name}</span>
|
||||||
<span className="text-sm text-text-muted">{session.user.name}</span>
|
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<LogoutButton />
|
<LogoutButton />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="order-last w-full overflow-x-auto sm:order-none sm:w-auto sm:overflow-visible">
|
||||||
|
<NavLinks />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="mx-auto w-full max-w-7xl flex-1 px-6 py-6">{children}</main>
|
<main className="mx-auto w-full max-w-7xl flex-1 px-4 py-6 sm:px-6">{children}</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ export function NavLinks() {
|
|||||||
<Link
|
<Link
|
||||||
key={href}
|
key={href}
|
||||||
href={href}
|
href={href}
|
||||||
className={`flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
className={`flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
||||||
active ? "bg-accent-soft text-accent-soft-text" : "text-text-muted hover:bg-surface-hover hover:text-text"
|
active ? "bg-accent-soft text-accent-soft-text" : "text-text-muted hover:bg-surface-hover hover:text-text"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ export function TicketThread({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-6.5rem)] flex-col">
|
<div className="flex h-[calc(100vh-11rem)] flex-col sm:h-[calc(100vh-6.5rem)]">
|
||||||
<div className="mb-4 flex items-start justify-between gap-4">
|
<div className="mb-4 flex flex-wrap items-start justify-between gap-3">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<Link href="/dashboard" className="mb-1 flex items-center gap-1 text-xs text-text-muted hover:text-text">
|
<Link href="/dashboard" className="mb-1 flex items-center gap-1 text-xs text-text-muted hover:text-text">
|
||||||
<ArrowLeft size={13} />
|
<ArrowLeft size={13} />
|
||||||
@@ -96,11 +96,11 @@ export function TicketThread({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 items-center gap-2">
|
<div className="flex w-full shrink-0 items-center gap-2 sm:w-auto">
|
||||||
<select
|
<select
|
||||||
value={ticket.status}
|
value={ticket.status}
|
||||||
onChange={(e) => updateStatus(e.target.value as TicketStatus)}
|
onChange={(e) => updateStatus(e.target.value as TicketStatus)}
|
||||||
className="rounded-md border border-border bg-surface px-2 py-1.5 text-sm outline-none focus:border-accent"
|
className="min-w-0 flex-1 rounded-md border border-border bg-surface px-2 py-1.5 text-sm outline-none focus:border-accent sm:flex-none"
|
||||||
>
|
>
|
||||||
{Object.entries(STATUS_LABEL).map(([value, label]) => (
|
{Object.entries(STATUS_LABEL).map(([value, label]) => (
|
||||||
<option key={value} value={value}>
|
<option key={value} value={value}>
|
||||||
@@ -111,7 +111,7 @@ export function TicketThread({
|
|||||||
<select
|
<select
|
||||||
value={ticket.assigneeId ?? ""}
|
value={ticket.assigneeId ?? ""}
|
||||||
onChange={(e) => updateAssignee(e.target.value)}
|
onChange={(e) => updateAssignee(e.target.value)}
|
||||||
className="rounded-md border border-border bg-surface px-2 py-1.5 text-sm outline-none focus:border-accent"
|
className="min-w-0 flex-1 rounded-md border border-border bg-surface px-2 py-1.5 text-sm outline-none focus:border-accent sm:flex-none"
|
||||||
>
|
>
|
||||||
<option value="">Не назначено</option>
|
<option value="">Не назначено</option>
|
||||||
{agents.map((agent) => (
|
{agents.map((agent) => (
|
||||||
|
|||||||
+28
-17
@@ -99,23 +99,26 @@
|
|||||||
--radius-md-val: 12px;
|
--radius-md-val: 12px;
|
||||||
--radius-lg-val: 18px;
|
--radius-lg-val: 18px;
|
||||||
|
|
||||||
|
--bg-glow: none;
|
||||||
|
--card-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root:not([data-theme="light"]) {
|
:root:not([data-theme="light"]) {
|
||||||
--bg: #0f0d17;
|
--bg: #0b0a12;
|
||||||
--surface: #17141f;
|
--surface: #161320;
|
||||||
--surface-hover: #1e1a2b;
|
--surface-hover: #1d1929;
|
||||||
--border: #2b2438;
|
--border: #2d2640;
|
||||||
--border-strong: #3a3150;
|
--border-strong: #453c60;
|
||||||
--text: #f1eefb;
|
--text: #f1eefb;
|
||||||
--text-muted: #a79fc4;
|
--text-muted: #a79fc4;
|
||||||
--text-faint: #6f6790;
|
--text-faint: #6f6790;
|
||||||
|
|
||||||
--accent: #a78bfa;
|
--accent: #8b5cf6;
|
||||||
--accent-hover: #c4b5fd;
|
--accent-hover: #7c3aed;
|
||||||
--accent-soft: #241b3d;
|
--accent-soft: #251c3f;
|
||||||
--accent-soft-text: #c4b5fd;
|
--accent-soft-text: #c4b5fd;
|
||||||
|
|
||||||
--danger: #f87171;
|
--danger: #f87171;
|
||||||
@@ -146,23 +149,26 @@
|
|||||||
--chart-4: #d97706;
|
--chart-4: #d97706;
|
||||||
--chart-muted: var(--border-strong);
|
--chart-muted: var(--border-strong);
|
||||||
|
|
||||||
|
--bg-glow: radial-gradient(ellipse 80% 50% at 50% -10%, rgba(139, 92, 246, 0.12), transparent 60%);
|
||||||
|
--card-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||||
|
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="dark"] {
|
:root[data-theme="dark"] {
|
||||||
--bg: #0f0d17;
|
--bg: #0b0a12;
|
||||||
--surface: #17141f;
|
--surface: #161320;
|
||||||
--surface-hover: #1e1a2b;
|
--surface-hover: #1d1929;
|
||||||
--border: #2b2438;
|
--border: #2d2640;
|
||||||
--border-strong: #3a3150;
|
--border-strong: #453c60;
|
||||||
--text: #f1eefb;
|
--text: #f1eefb;
|
||||||
--text-muted: #a79fc4;
|
--text-muted: #a79fc4;
|
||||||
--text-faint: #6f6790;
|
--text-faint: #6f6790;
|
||||||
|
|
||||||
--accent: #a78bfa;
|
--accent: #8b5cf6;
|
||||||
--accent-hover: #c4b5fd;
|
--accent-hover: #7c3aed;
|
||||||
--accent-soft: #241b3d;
|
--accent-soft: #251c3f;
|
||||||
--accent-soft-text: #c4b5fd;
|
--accent-soft-text: #c4b5fd;
|
||||||
|
|
||||||
--danger: #f87171;
|
--danger: #f87171;
|
||||||
@@ -191,6 +197,9 @@
|
|||||||
--chart-4: #d97706;
|
--chart-4: #d97706;
|
||||||
--chart-muted: var(--border-strong);
|
--chart-muted: var(--border-strong);
|
||||||
|
|
||||||
|
--bg-glow: radial-gradient(ellipse 80% 50% at 50% -10%, rgba(139, 92, 246, 0.12), transparent 60%);
|
||||||
|
--card-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||||
|
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +237,8 @@ body {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
|
background-image: var(--bg-glow, none);
|
||||||
|
background-attachment: fixed;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-family: var(--font-sans);
|
font-family: var(--font-sans);
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
@@ -261,7 +272,7 @@ body {
|
|||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
box-shadow: var(--card-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
|||||||
Reference in new issue
Block a user