feat: enhance frontend UI — command palette, admin redesign, API coverage
Types + API: - Add User, TemplatePreview, QueueCustomField types - Add getUsers, getTemplates, createTemplate, updateTemplate, previewTemplate, updateQueue, updateLifecycle, updateCustomField API functions UI: - Command palette: keyboard-first navigation with fuzzy ticket search - Admin: comprehensive redesign with tab-based layout (Queues, Lifecycles, Scrips, Custom Fields, Templates, Users) - Ticket list: improved inbox-style rows with quick actions - Ticket detail: enhanced conversation thread and properties sidebar - App shell: sidebar visual refinement with active indicator bar - Theme toggle: smoother transitions Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
SettingsIcon,
|
||||
PanelLeftCloseIcon,
|
||||
PanelLeftIcon,
|
||||
CommandIcon,
|
||||
} from "lucide-react";
|
||||
import { getTickets, getQueues } from "@/lib/api";
|
||||
import type { Queue } from "@/lib/types";
|
||||
@@ -51,11 +52,11 @@ function SidebarNavItem({
|
||||
href={href}
|
||||
title={collapsed ? label : undefined}
|
||||
className={cn(
|
||||
"flex items-center px-2 py-1.5 rounded-md text-[13px] transition-all duration-150 mb-0.5",
|
||||
"group flex items-center px-2 py-1.5 rounded-md text-[13px] transition-all duration-150 mb-0.5",
|
||||
collapsed ? "justify-center w-full" : "justify-between",
|
||||
active
|
||||
? "bg-accent text-foreground font-medium"
|
||||
: "text-muted-foreground hover:text-foreground hover:bg-accent font-normal"
|
||||
? "bg-sidebar-primary text-sidebar-primary-foreground font-semibold shadow-[inset_3px_0_0_color-mix(in_oklch,var(--sidebar-primary-foreground)_55%,transparent)]"
|
||||
: "text-sidebar-foreground/70 hover:text-sidebar-foreground hover:bg-sidebar-accent font-normal"
|
||||
)}
|
||||
>
|
||||
<span className={cn("flex items-center", collapsed ? "" : "gap-2.5")}>
|
||||
@@ -63,7 +64,10 @@ function SidebarNavItem({
|
||||
{!collapsed && label}
|
||||
</span>
|
||||
{!collapsed && count !== undefined && count > 0 && (
|
||||
<span className="text-xs tabular-nums text-muted-foreground">
|
||||
<span className={cn(
|
||||
"min-w-5 rounded px-1 text-right text-[11px] tabular-nums",
|
||||
active ? "text-sidebar-primary-foreground/80" : "text-sidebar-foreground/45"
|
||||
)}>
|
||||
{count}
|
||||
</span>
|
||||
)}
|
||||
@@ -171,7 +175,7 @@ function SidebarNav() {
|
||||
{queues.length > 0 && (
|
||||
<div>
|
||||
{!collapsed && (
|
||||
<div className="px-2 py-1.5 text-[11px] font-semibold text-muted-foreground uppercase tracking-wider">
|
||||
<div className="px-2 py-1.5 text-[11px] font-semibold text-sidebar-foreground/45 uppercase">
|
||||
Queues
|
||||
</div>
|
||||
)}
|
||||
@@ -179,7 +183,7 @@ function SidebarNav() {
|
||||
const active =
|
||||
pathname === "/" && searchParams.get("queue") === queue.id;
|
||||
const QueueIcon = () => (
|
||||
<span className="w-2 h-2 rounded-full bg-muted-foreground flex-shrink-0" />
|
||||
<span className="w-2 h-2 rounded-full bg-sidebar-primary flex-shrink-0 shadow-[0_0_0_3px_color-mix(in_oklch,var(--sidebar-primary)_18%,transparent)]" />
|
||||
);
|
||||
return (
|
||||
<SidebarNavItem
|
||||
@@ -203,7 +207,7 @@ function SidebarBottom() {
|
||||
const collapsed = useSidebarCollapsed();
|
||||
|
||||
return (
|
||||
<div className="border-t border-border p-2">
|
||||
<div className="border-t border-sidebar-border p-2">
|
||||
<SidebarNavItem
|
||||
href="/admin"
|
||||
icon={SettingsIcon}
|
||||
@@ -217,13 +221,13 @@ function SidebarBottom() {
|
||||
)}
|
||||
title={collapsed ? "User" : undefined}
|
||||
>
|
||||
<div className="w-5 h-5 rounded-full bg-primary flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-primary-foreground text-[10px] font-semibold">
|
||||
<div className="w-5 h-5 rounded-md bg-sidebar-primary flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-sidebar-primary-foreground text-[10px] font-semibold">
|
||||
U
|
||||
</span>
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<span className="text-[13px] text-muted-foreground truncate">
|
||||
<span className="text-[13px] text-sidebar-foreground/65 truncate">
|
||||
User
|
||||
</span>
|
||||
)}
|
||||
@@ -259,39 +263,54 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<SidebarCollapsedContext.Provider value={sidebarCollapsed}>
|
||||
<div className="flex h-screen overflow-hidden">
|
||||
<div className="flex h-screen overflow-hidden bg-background">
|
||||
{/* Sidebar */}
|
||||
<aside
|
||||
className={cn(
|
||||
"flex-shrink-0 flex flex-col bg-sidebar border-r border-border transition-all duration-150",
|
||||
"flex-shrink-0 flex flex-col bg-sidebar border-r border-sidebar-border transition-all duration-150 shadow-[16px_0_42px_color-mix(in_oklch,var(--sidebar)_18%,transparent)]",
|
||||
sidebarCollapsed ? "w-[60px]" : "w-60"
|
||||
)}
|
||||
>
|
||||
{/* Brand */}
|
||||
<div className="h-11 flex items-center px-3 border-b border-border">
|
||||
<div className="h-14 flex items-center justify-between gap-2 px-3 border-b border-sidebar-border">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="w-5 h-5 rounded-md bg-primary flex items-center justify-center">
|
||||
<span className="text-primary-foreground text-[11px] font-semibold">
|
||||
<div className="w-7 h-7 rounded-md bg-sidebar-primary flex items-center justify-center shadow-[0_0_0_1px_color-mix(in_oklch,var(--sidebar-primary)_55%,white_20%)]">
|
||||
<span className="text-sidebar-primary-foreground text-[12px] font-bold">
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
{!sidebarCollapsed && (
|
||||
<span className="font-semibold text-foreground text-sm tracking-tight">
|
||||
Tessera
|
||||
<span className="leading-tight">
|
||||
<span className="block font-semibold text-sidebar-foreground text-sm">
|
||||
Tessera
|
||||
</span>
|
||||
<span className="block text-[10px] text-sidebar-foreground/45">
|
||||
ScripFoundry
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
{!sidebarCollapsed && (
|
||||
<button
|
||||
onClick={() => setCommandOpen(true)}
|
||||
className="flex h-7 items-center gap-1 rounded-md border border-sidebar-border px-2 text-[11px] text-sidebar-foreground/55 transition-colors hover:bg-sidebar-accent hover:text-sidebar-foreground"
|
||||
aria-label="Open command palette"
|
||||
>
|
||||
<CommandIcon className="h-3.5 w-3.5" />
|
||||
K
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 overflow-y-auto py-2 px-2">
|
||||
<nav className="flex-1 overflow-y-auto py-3 px-2">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="space-y-1.5 px-2">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-7 bg-muted rounded-md animate-pulse"
|
||||
className="h-7 bg-sidebar-accent rounded-md animate-pulse"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -306,7 +325,7 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
</aside>
|
||||
|
||||
{/* Main */}
|
||||
<main className="flex-1 overflow-hidden">{children}</main>
|
||||
<main className="flex-1 overflow-hidden bg-background/88">{children}</main>
|
||||
|
||||
{/* Command Palette */}
|
||||
<CommandPalette open={commandOpen} onOpenChange={setCommandOpen} />
|
||||
@@ -315,7 +334,7 @@ export function AppShell({ children }: { children: React.ReactNode }) {
|
||||
{/* Collapse toggle */}
|
||||
<button
|
||||
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
|
||||
className="fixed bottom-4 left-0 z-40 w-6 h-6 flex items-center justify-center rounded-r-md bg-sidebar border border-border border-l-0 text-muted-foreground hover:text-foreground transition-all duration-150"
|
||||
className="fixed bottom-4 left-0 z-40 w-6 h-6 flex items-center justify-center rounded-r-md bg-sidebar border border-sidebar-border border-l-0 text-sidebar-foreground/55 hover:text-sidebar-foreground transition-all duration-150"
|
||||
style={{ left: sidebarCollapsed ? 60 : 240 }}
|
||||
aria-label={sidebarCollapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user