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:
Gjermund Høsøien Wiggen
2026-06-09 10:43:28 +02:00
parent b96ba21e99
commit 06cc7c79a3
14 changed files with 3987 additions and 1331 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -49,72 +49,72 @@
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--background: oklch(0.982 0.006 106);
--foreground: oklch(0.19 0.018 248);
--card: oklch(0.996 0.003 106);
--card-foreground: oklch(0.19 0.018 248);
--popover: oklch(0.996 0.003 106);
--popover-foreground: oklch(0.19 0.018 248);
--primary: oklch(0.31 0.046 243);
--primary-foreground: oklch(0.99 0.003 106);
--secondary: oklch(0.945 0.01 105);
--secondary-foreground: oklch(0.25 0.026 244);
--muted: oklch(0.948 0.008 106);
--muted-foreground: oklch(0.49 0.023 250);
--accent: oklch(0.925 0.024 184);
--accent-foreground: oklch(0.21 0.028 246);
--destructive: oklch(0.55 0.18 27);
--border: oklch(0.865 0.014 102);
--input: oklch(0.84 0.015 102);
--ring: oklch(0.58 0.068 185);
--chart-1: oklch(0.62 0.095 184);
--chart-2: oklch(0.53 0.078 243);
--chart-3: oklch(0.64 0.12 77);
--chart-4: oklch(0.55 0.15 28);
--chart-5: oklch(0.44 0.055 257);
--radius: 0.5rem;
--sidebar: oklch(0.245 0.026 248);
--sidebar-foreground: oklch(0.93 0.012 108);
--sidebar-primary: oklch(0.69 0.105 184);
--sidebar-primary-foreground: oklch(0.18 0.022 248);
--sidebar-accent: oklch(0.31 0.031 248);
--sidebar-accent-foreground: oklch(0.98 0.006 106);
--sidebar-border: oklch(1 0 0 / 11%);
--sidebar-ring: oklch(0.66 0.102 184);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--background: oklch(0.18 0.018 248);
--foreground: oklch(0.94 0.011 105);
--card: oklch(0.225 0.022 248);
--card-foreground: oklch(0.94 0.011 105);
--popover: oklch(0.225 0.022 248);
--popover-foreground: oklch(0.94 0.011 105);
--primary: oklch(0.74 0.105 184);
--primary-foreground: oklch(0.17 0.018 248);
--secondary: oklch(0.27 0.026 248);
--secondary-foreground: oklch(0.94 0.011 105);
--muted: oklch(0.28 0.023 248);
--muted-foreground: oklch(0.7 0.019 105);
--accent: oklch(0.31 0.043 184);
--accent-foreground: oklch(0.94 0.011 105);
--destructive: oklch(0.68 0.17 24);
--border: oklch(1 0 0 / 12%);
--input: oklch(1 0 0 / 16%);
--ring: oklch(0.68 0.095 184);
--chart-1: oklch(0.74 0.105 184);
--chart-2: oklch(0.7 0.105 74);
--chart-3: oklch(0.66 0.12 25);
--chart-4: oklch(0.61 0.08 245);
--chart-5: oklch(0.8 0.04 108);
--sidebar: oklch(0.145 0.018 248);
--sidebar-foreground: oklch(0.94 0.011 105);
--sidebar-primary: oklch(0.74 0.105 184);
--sidebar-primary-foreground: oklch(0.17 0.018 248);
--sidebar-accent: oklch(0.24 0.026 248);
--sidebar-accent-foreground: oklch(0.94 0.011 105);
--sidebar-border: oklch(1 0 0 / 11%);
--sidebar-ring: oklch(0.68 0.095 184);
}
@keyframes slide-in-right {
@@ -135,8 +135,12 @@
body {
@apply bg-background text-foreground;
font-feature-settings: "cv01" 1, "ss03" 1;
background-image:
linear-gradient(to right, color-mix(in oklch, var(--border) 45%, transparent) 1px, transparent 1px),
linear-gradient(to bottom, color-mix(in oklch, var(--border) 45%, transparent) 1px, transparent 1px);
background-size: 44px 44px;
}
html {
@apply font-sans;
}
}
}

View File

@@ -1,13 +1,14 @@
import type { Metadata } from "next";
import { Inter, JetBrains_Mono } from "next/font/google";
import { IBM_Plex_Sans, JetBrains_Mono } from "next/font/google";
import { Suspense } from "react";
import { ThemeProvider } from "next-themes";
import "./globals.css";
import { AppShell } from "@/components/app-shell";
const inter = Inter({
const ibmPlexSans = IBM_Plex_Sans({
subsets: ["latin"],
variable: "--font-inter",
weight: ["400", "500", "600", "700"],
variable: "--font-sans",
});
const jetbrainsMono = JetBrains_Mono({
@@ -26,7 +27,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${inter.variable} ${jetbrainsMono.variable} font-sans antialiased`}
className={`${ibmPlexSans.variable} ${jetbrainsMono.variable} font-sans antialiased`}
style={{ fontSize: "15px", lineHeight: 1.5 }}
>
<ThemeProvider attribute="class" defaultTheme="light" enableSystem={false}>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff