P0: Next.js shell — tokens, motion, SlidePanel, LeftRail, auth plumbing

- Tailwind v4 @theme block encoding all spec §2 tokens; full §3 keyframe
  inventory with prefers-reduced-motion handling; Geist vendored (air-gap)
- SlidePanel width-animation primitive (component-tested: exit-transition
  unmount, fixed-width inner content, a11y region semantics)
- session-key.ts mirroring the Rust codec + URL-param encoding; nuqs
  panel-params with spec ?sessions=1 flag shape and routines->scheduled alias
- Zod-typed API client; httpOnly cookie session bridge (/auth/session);
  login page; (workspace) layout with LeftRail roster/nav/user; Team and
  Credits pages on real endpoints (+ GET /api/team/members in tc-api)
- Vitest + Testing Library harness (26 tests); ESLint max-lines 1250 +
  no-warning-comments mirroring the CI gates

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-09 22:43:50 -05:00
co-authored by Claude Fable 5
parent c4349bf292
commit 172a3c8fed
50 changed files with 9415 additions and 1 deletions
+47
View File
@@ -0,0 +1,47 @@
// Zod schemas mirroring the Rust API responses (spec §13/§14). The backend
// is a separate codebase: every response is parsed so drift fails loudly in
// tests, not silently in the UI.
import { z } from "zod";
export const RoleSchema = z.enum(["owner", "member"]);
export type Role = z.infer<typeof RoleSchema>;
export const UserSchema = z.object({
id: z.string().uuid(),
workspace_id: z.string().uuid(),
email: z.string(),
role: RoleSchema,
display_name: z.string(),
created_at: z.string(),
});
export type User = z.infer<typeof UserSchema>;
export const AgentStatusSchema = z.enum(["provisioning", "online", "offline"]);
export type AgentStatus = z.infer<typeof AgentStatusSchema>;
export const AgentSchema = z.object({
id: z.string().uuid(),
workspace_id: z.string().uuid(),
name: z.string(),
job_title: z.string(),
system_prompt: z.string(),
avatar: z.string(),
accent: z.string(),
wallpaper: z.string(),
managed_by: z.string().uuid(),
status: AgentStatusSchema,
});
export type Agent = z.infer<typeof AgentSchema>;
export const PermissionsSchema = z.object({
role: RoleSchema,
can_manage_team: z.boolean(),
can_manage_billing: z.boolean(),
});
export type Permissions = z.infer<typeof PermissionsSchema>;
export const CreditsSchema = z.object({
available: z.number(),
});
export type Credits = z.infer<typeof CreditsSchema>;