ui: shadcn-ui + React island shells for pages, Redis session store

Rewrite the dashboard, machines, users, dns, and settings pages as thin
Astro shells that mount a matching React island (Dashboard, MachinesPage,
UsersPage, DnsPage, SettingsPage) built on the shadcn-ui component
library. Alpine.js templates in those pages are replaced wholesale;
Alpine still ships as the runtime for the ACL editor's dependencies.

- src/components/ui/*: shadcn primitives (button, card, tabs, select,
  dropdown-menu, avatar, badge, input, switch, separator)
- src/components/{dashboard,dns,machines,settings,users}/*: page-level
  React shells that consume server props from the Astro parent
- src/components/shell/AppShell.tsx: shared chrome (nav, user menu)
- src/lib/utils.ts: shadcn's cn() helper
- src/lib/auth/session-manager.ts: pluggable SessionStore interface with
  Redis (via REDIS_URL) or in-memory backends; both honor absolute
  expiration via TTL. In-memory logs a warning that sessions vanish on
  restart.
- src/lib/auth/oidc-client.ts, oidc-state.ts: shrink OIDC handlers now
  that PKCE + nonce state travels in a signed JWT cookie
- src/pages/api/auth/*: match the simplified handlers
- src/styles/global.css, tailwind.config.mjs, tsconfig.json: shadcn
  design tokens and the '@/*' path alias
- docker-compose.local.yml: local dev tweaks
- package.json, pnpm-lock.yaml: shadcn + Radix + ioredis + zod
This commit is contained in:
Ryan Malloy 2026-08-20 10:25:35 -06:00
parent 36c1b18724
commit 09b1bae157
38 changed files with 4966 additions and 3972 deletions

View file

@ -1,15 +1,59 @@
/** @type {import('tailwindcss').Config} */
import tailwindcssAnimate from 'tailwindcss-animate';
export default {
darkMode: 'class',
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
'./src/**/*.{astro,html,js,jsx,md,mdx,ts,tsx}',
'./src/pages/**/*.astro',
'./src/layouts/**/*.astro',
'./src/components/**/*.astro',
'./src/components/**/*.{astro,tsx}',
],
theme: {
container: {
center: true,
padding: '2rem',
screens: { '2xl': '1400px' },
},
extend: {
// shadcn-ui (new-york) reads colors from CSS variables defined in
// src/styles/global.css. Everything below maps Tailwind utility
// classes to those variables.
colors: {
// Heady brand colors
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
// Brand accents kept from the original Heady config.
heady: {
50: '#f0f9ff',
100: '#e0f2fe',
@ -23,21 +67,30 @@ export default {
900: '#0c4a6e',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
mono: [
'Fira Code',
'Monaco',
'Cascadia Code',
'Roboto Mono',
'monospace',
],
mono: ['Fira Code', 'Monaco', 'Cascadia Code', 'monospace'],
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
},
},
},
plugins: [
// Add any Tailwind plugins you need
],
plugins: [tailwindcssAnimate],
};