fix: replace all remaining old brand tokens (text-brand-dark, border-brand-border, bg-white opacity)
30 references to removed CSS variables: border-brand-border → border-[#1e2130], text-brand-text-muted → text-[#4f5666], text-brand-dark → text-white, bg-white/[0.04] → bg-[#191c25], hover:border-white/[0.12] → hover:border-[#2a2f3d]. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
128
Design/DESIGN-SYSTEM-SETUP-GUIDE.md
Normal file
128
Design/DESIGN-SYSTEM-SETUP-GUIDE.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# How To Make Claude Code Follow The New Design System
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
|
||||||
|
Your project currently has MULTIPLE contradicting design documents:
|
||||||
|
- CLAUDE.md references purple gradients, glass-morphism, AND cyan accents
|
||||||
|
- Old docs like DESIGN_SYSTEM_GUIDE.md describe monochrome glass-card patterns
|
||||||
|
- UI-DESIGN-SYSTEM.md describes a purple gradient workspace system
|
||||||
|
- REBRAND-IMPLEMENTATION-GUIDE.md has the old Patherly → ResolutionFlow rebrand
|
||||||
|
- Your actual CSS has Tailwind v4 with OKLCH cyan tokens
|
||||||
|
|
||||||
|
Claude Code sees ALL of these and gets confused about which to follow.
|
||||||
|
|
||||||
|
## The Fix — 4 Steps
|
||||||
|
|
||||||
|
### Step 1: Place the new DESIGN-SYSTEM.md in your project root
|
||||||
|
|
||||||
|
Copy `DESIGN-SYSTEM.md` (the file I created alongside this one) to:
|
||||||
|
```
|
||||||
|
C:\Dev\Projects\patherly\DESIGN-SYSTEM.md
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the SINGLE SOURCE OF TRUTH for all design decisions going forward.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2: Update CLAUDE.md — Branding Section
|
||||||
|
|
||||||
|
Find the `### Branding` section and REPLACE EVERYTHING between `**Brand details:**`
|
||||||
|
and the next `---` separator with this:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
**Brand details:**
|
||||||
|
|
||||||
|
- **Design system:** [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) — THE source of truth for all design decisions
|
||||||
|
- **Design aesthetic:** Flat, high-contrast dark theme (Sentry/PostHog-inspired). No glass morphism, no gradients on surfaces, no ambient effects.
|
||||||
|
- **Accent color:** Cyan (#22d3ee / #06b6d4). Used sparingly — ≤5% of the UI.
|
||||||
|
- **Fonts:** IBM Plex Sans (body), Bricolage Grotesque (headings), JetBrains Mono (code) — loaded via Google Fonts
|
||||||
|
- **Logo:** 30px gradient square (cyan) + "ResolutionFlow" in Bricolage Grotesque 700
|
||||||
|
- **Layout:** Icon rail sidebar (72px, default) with hover flyout panels. Pinnable to full 260px sidebar.
|
||||||
|
- **Brand assets:** `brand-assets/` (source SVGs), `frontend/src/assets/brand/` (app assets), `frontend/public/icons/` (favicon)
|
||||||
|
- **Landing page:** Shares color system and typography. Full-width marketing layout at `/` route.
|
||||||
|
- **Reference mockups:** `docs/mockups/` (HTML files, open in browser)
|
||||||
|
|
||||||
|
**Component rules:**
|
||||||
|
- Primary buttons: solid `accent` background (#22d3ee), white text
|
||||||
|
- Cards: `bg-card` with 1px `border-default`, 8px radius. NO shadows, NO blur, NO gradients.
|
||||||
|
- Badges: pill-shaped (20px radius), semantic dim background + matching text color
|
||||||
|
- Active nav: `accent-dim` background + `accent-text` color + 3px left accent bar
|
||||||
|
- Stat cards: 3px colored left border (accent/success/warning by position)
|
||||||
|
- Code blocks: `bg-code` with JetBrains Mono, material-inspired syntax highlighting
|
||||||
|
- Status colors: green (success), amber (warning), red (danger) — ONLY for semantic meaning
|
||||||
|
|
||||||
|
When adding new pages/components: reference DESIGN-SYSTEM.md. Use flat dark surfaces, 1px borders, no decorative effects. All colors via CSS variables.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3: Update CLAUDE.md — Tech Stack Line
|
||||||
|
|
||||||
|
Find the styling line under Tech Stack → Frontend and change it to:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
- **Styling:** Tailwind CSS v4 — flat dark theme with cyan accent (see DESIGN-SYSTEM.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove any references to "monochrome glass-morphism" or "dark-only" limitations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4: Archive Old Design Docs
|
||||||
|
|
||||||
|
Move these files into an `archive/` folder (don't delete — you might want to reference them later):
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir archive
|
||||||
|
mkdir archive\design-docs
|
||||||
|
|
||||||
|
move DESIGN_SYSTEM_GUIDE.md archive\design-docs\
|
||||||
|
move UI-DESIGN-SYSTEM.md archive\design-docs\
|
||||||
|
move REBRAND-IMPLEMENTATION-GUIDE.md archive\design-docs\
|
||||||
|
move COMPONENT_EXAMPLES.md archive\design-docs\ (if it exists in root)
|
||||||
|
move docs\plans\Frontend\DESIGN_SYSTEM_GUIDE.md archive\design-docs\ (if it exists here)
|
||||||
|
```
|
||||||
|
|
||||||
|
If any of these files are referenced elsewhere in CLAUDE.md, remove those references.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why This Works
|
||||||
|
|
||||||
|
Claude Code prioritizes files in this order:
|
||||||
|
1. **CLAUDE.md** — reads this first, every time
|
||||||
|
2. **Files referenced in CLAUDE.md** — follows links to read referenced docs
|
||||||
|
3. **Files in the project root** — scans these for context
|
||||||
|
4. **Files deeper in the tree** — only reads if specifically asked
|
||||||
|
|
||||||
|
By putting DESIGN-SYSTEM.md in the root AND linking it from CLAUDE.md's branding section,
|
||||||
|
Claude Code will find it immediately and treat it as authoritative. By archiving the old
|
||||||
|
docs, Claude Code won't stumble on contradicting information.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After making these changes, test by asking Claude Code:
|
||||||
|
1. "What color system does this project use?" — Should say cyan/teal flat dark theme, NOT purple gradients or monochrome
|
||||||
|
2. "Create a new card component" — Should use bg-card with 1px border, NOT glass-card with backdrop-blur
|
||||||
|
3. "What fonts does this project use?" — Should say IBM Plex Sans + Bricolage Grotesque, NOT Inter + Plus Jakarta Sans
|
||||||
|
4. "Add a sidebar nav item" — Should follow the icon rail pattern, NOT a traditional full sidebar
|
||||||
|
|
||||||
|
If any of these come back wrong, check that the old docs are actually archived and not still being found.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Place These Mockup Files in docs/mockups/
|
||||||
|
|
||||||
|
Also copy the HTML mockups we created into your project so Claude Code has visual references:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir docs\mockups
|
||||||
|
|
||||||
|
copy resolutionflow-redesign.html docs\mockups\
|
||||||
|
copy resolutionflow-sidebar-final.html docs\mockups\
|
||||||
|
copy resolutionflow-landing.html docs\mockups\
|
||||||
|
```
|
||||||
|
|
||||||
|
Then Claude Code can open these in a browser to see exactly what the target looks like.
|
||||||
378
Design/DESIGN-SYSTEM.md
Normal file
378
Design/DESIGN-SYSTEM.md
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
# ResolutionFlow Design System v4
|
||||||
|
|
||||||
|
> **Status:** ACTIVE — This document is the single source of truth for all frontend design decisions.
|
||||||
|
> **Supersedes:** All previous design system docs including `DESIGN_SYSTEM_GUIDE.md`, `UI-DESIGN-SYSTEM.md`, `REBRAND-IMPLEMENTATION-GUIDE.md`, and any `COMPONENT_EXAMPLES.md` files.
|
||||||
|
> **Last Updated:** March 21, 2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design Philosophy
|
||||||
|
|
||||||
|
ResolutionFlow uses a **flat, high-contrast dark theme** inspired by Sentry and PostHog. The aesthetic is premium, clean, and minimal — no glass morphism, no backdrop blur, no ambient orbs, no gradient backgrounds on surfaces. The accent color appears in ≤5% of the UI. Every design decision prioritizes **readability over decoration**.
|
||||||
|
|
||||||
|
**Light mode** is a planned addition (dark/light toggle). Design all components with CSS variables so theming is a variable swap, not a rewrite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Color System
|
||||||
|
|
||||||
|
All colors are defined as CSS custom properties in `index.css` inside the `@theme` block (Tailwind v4) or `:root` / `.dark` blocks (Tailwind v3).
|
||||||
|
|
||||||
|
### Dark Mode (Default)
|
||||||
|
|
||||||
|
```
|
||||||
|
Page background: #0c0d10
|
||||||
|
Sidebar background: #0f1118
|
||||||
|
Card background: #14161d
|
||||||
|
Card hover: #191c25
|
||||||
|
Input background: #191c25
|
||||||
|
Code background: #0e1017
|
||||||
|
Elevated surface: #1c1f2a
|
||||||
|
|
||||||
|
Text primary: #e2e5eb
|
||||||
|
Text heading: #f0f2f5
|
||||||
|
Text secondary: #848b9b
|
||||||
|
Text muted: #4f5666
|
||||||
|
Text rail label: #6b7280
|
||||||
|
|
||||||
|
Border default: #1e2130
|
||||||
|
Border hover: #2a2f3d
|
||||||
|
|
||||||
|
Accent (cyan): #22d3ee
|
||||||
|
Accent hover: #06b6d4
|
||||||
|
Accent dim (8-10%): rgba(34,211,238,0.10)
|
||||||
|
Accent text: #67e8f9
|
||||||
|
|
||||||
|
Success: #34d399
|
||||||
|
Success dim: rgba(52,211,153,0.10)
|
||||||
|
Warning: #fbbf24
|
||||||
|
Warning dim: rgba(251,191,36,0.10)
|
||||||
|
Danger: #f87171
|
||||||
|
Danger dim: rgba(248,113,113,0.10)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Light Mode (Planned)
|
||||||
|
|
||||||
|
```
|
||||||
|
Page background: #f3f4f7
|
||||||
|
Sidebar background: #ffffff
|
||||||
|
Card background: #ffffff
|
||||||
|
Card hover: #f8f9fb
|
||||||
|
Input background: #eef0f4
|
||||||
|
Code background: #f5f6f9
|
||||||
|
Elevated surface: #e8eaef
|
||||||
|
|
||||||
|
Text primary: #1a1d24
|
||||||
|
Text heading: #0d0f13
|
||||||
|
Text secondary: #5a6274
|
||||||
|
Text muted: #8b92a1
|
||||||
|
|
||||||
|
Border default: #dde0e7
|
||||||
|
Border hover: #c5c9d3
|
||||||
|
|
||||||
|
Accent: #0891b2
|
||||||
|
Accent dim: rgba(8,145,178,0.07)
|
||||||
|
Accent text: #0e7490
|
||||||
|
```
|
||||||
|
|
||||||
|
### What NOT To Use
|
||||||
|
|
||||||
|
- No glass morphism (`backdrop-filter: blur`)
|
||||||
|
- No gradient backgrounds on cards or surfaces
|
||||||
|
- No ambient orbs or floating glow elements
|
||||||
|
- No `bg-white/[0.04]` opacity-based backgrounds
|
||||||
|
- No purple gradient accent (`#818cf8 → #a78bfa`) — this is deprecated
|
||||||
|
- No `text-gradient-brand` utility — replaced by solid `accent-text` color
|
||||||
|
- No `glass-card`, `glass-stat`, `glass-card-glow` CSS utilities
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
### Font Stack
|
||||||
|
|
||||||
|
| Role | Font | Weights | CSS Variable |
|
||||||
|
|------|------|---------|-------------|
|
||||||
|
| Body text | IBM Plex Sans | 400, 500, 600, 700 | `--font-sans` / `font-sans` |
|
||||||
|
| Headings | Bricolage Grotesque | 600, 700, 800 | `--font-heading` / `font-heading` |
|
||||||
|
| Code / Monospace | JetBrains Mono | 400, 500 | `--font-mono` / `font-mono` |
|
||||||
|
|
||||||
|
### Typography Scale
|
||||||
|
|
||||||
|
| Element | Size | Weight | Color | Letter Spacing |
|
||||||
|
|---------|------|--------|-------|---------------|
|
||||||
|
| Page title (h1) | 22px | 700 (Bricolage) | text-heading | -0.03em |
|
||||||
|
| Section title | 15px | 600 (Bricolage) | text-heading | normal |
|
||||||
|
| Card title | 14px | 600 (IBM Plex) | text-heading | normal |
|
||||||
|
| Body text | 14px | 400 (IBM Plex) | text-primary | normal |
|
||||||
|
| Secondary text | 13px | 400 (IBM Plex) | text-secondary | normal |
|
||||||
|
| Label / hint | 12px | 500 (IBM Plex) | text-muted | normal |
|
||||||
|
| Nav section header | 10px | 600 (IBM Plex) | text-muted | 1.2px, uppercase |
|
||||||
|
| Rail icon label | 10px | 500 (IBM Plex) | text-rail-label | normal |
|
||||||
|
| Code | 12px | 400 (JetBrains) | text-primary | normal |
|
||||||
|
| Stat number | 28px | 700 (Bricolage) | text-heading | -0.03em |
|
||||||
|
| Badge | 11px | 550 (IBM Plex) | varies | normal |
|
||||||
|
|
||||||
|
### What NOT To Use
|
||||||
|
|
||||||
|
- No Inter, Plus Jakarta Sans, or Outfit — these are deprecated
|
||||||
|
- No `font-label` utility — use `font-mono` for monospace or `font-sans` at small size
|
||||||
|
- No font sizes below 10px
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Layout Architecture
|
||||||
|
|
||||||
|
### App Shell
|
||||||
|
|
||||||
|
The app uses a CSS Grid layout with two states:
|
||||||
|
|
||||||
|
**Icon Rail (Default):**
|
||||||
|
```
|
||||||
|
[icon-rail: 72px] [main-content: 1fr]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pinned Sidebar:**
|
||||||
|
```
|
||||||
|
[full-sidebar: 260px] [main-content: 1fr]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Icon Rail Sidebar
|
||||||
|
|
||||||
|
The default navigation is a narrow icon rail (72px) with:
|
||||||
|
- Logo mark at top (30px square, gradient background, lightning bolt icon)
|
||||||
|
- Nav items as vertical column: icon (20px) + label text (10px) underneath
|
||||||
|
- Dividers between nav sections
|
||||||
|
- User avatar at bottom
|
||||||
|
- Pin/expand button below avatar
|
||||||
|
|
||||||
|
**Hover behavior:** Hovering a rail item opens a flyout panel (220px) to the right of the rail with sub-navigation items for that section. The flyout stays open while the cursor is on either the rail item or the flyout panel.
|
||||||
|
|
||||||
|
**Pinned state:** Clicking the pin button expands to a traditional 260px sidebar with full text labels, section headers, badges, and the active left-bar accent. An unpin button in the header returns to the icon rail.
|
||||||
|
|
||||||
|
### Active Nav Indicator
|
||||||
|
|
||||||
|
- **Icon rail:** `accent-dim` background on the item, `accent-text` color on icon and label
|
||||||
|
- **Pinned sidebar:** Same as above, plus a 3px left border (`border-radius: 0 3px 3px 0`) in `accent` color
|
||||||
|
|
||||||
|
### Main Content Area
|
||||||
|
|
||||||
|
- Padding: 28px top/bottom, 36px left/right
|
||||||
|
- Max content width: not constrained (fills available space)
|
||||||
|
- Page title: Bricolage Grotesque, 22px, weight 700
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Component Patterns
|
||||||
|
|
||||||
|
### Stat Cards
|
||||||
|
|
||||||
|
```
|
||||||
|
Background: bg-card
|
||||||
|
Border: 1px solid border-default
|
||||||
|
Border-left: 3px solid [varies by position - accent, success, warning, accent]
|
||||||
|
Border-radius: 8px
|
||||||
|
Padding: 18px 16px
|
||||||
|
```
|
||||||
|
|
||||||
|
- Label: 11px, uppercase, 600 weight, text-muted
|
||||||
|
- Value: 28px, Bricolage Grotesque 700, text-heading
|
||||||
|
- Delta: 12px, 500 weight, success/danger color with ↑/↓ prefix
|
||||||
|
|
||||||
|
### Cards
|
||||||
|
|
||||||
|
```
|
||||||
|
Background: bg-card
|
||||||
|
Border: 1px solid border-default
|
||||||
|
Border-radius: 8px
|
||||||
|
Padding: 20px
|
||||||
|
Hover: border-color transitions to border-hover
|
||||||
|
```
|
||||||
|
|
||||||
|
No shadows. No gradients. No glow effects.
|
||||||
|
|
||||||
|
### Flow List Items
|
||||||
|
|
||||||
|
```
|
||||||
|
Layout: flex row, 12px gap
|
||||||
|
Padding: 10px 8px
|
||||||
|
Border-bottom: 1px solid border-default (divider style, not bordered cards)
|
||||||
|
Hover: bg-card-hover
|
||||||
|
```
|
||||||
|
|
||||||
|
- Icon: 34px square, border-radius 5px, semantic dim background + colored stroke
|
||||||
|
- Name: 13px, 500 weight, text-heading
|
||||||
|
- Meta: 11.5px, text-muted
|
||||||
|
- Badge: right-aligned status badge
|
||||||
|
|
||||||
|
### Badges
|
||||||
|
|
||||||
|
```
|
||||||
|
Font: 11px, 550 weight
|
||||||
|
Padding: 3px 9px
|
||||||
|
Border-radius: 20px (pill)
|
||||||
|
```
|
||||||
|
|
||||||
|
| Type | Background | Text Color |
|
||||||
|
|------|-----------|------------|
|
||||||
|
| Info/Accent | accent-dim | accent-text |
|
||||||
|
| Success | success-dim | success |
|
||||||
|
| Warning | warning-dim | warning |
|
||||||
|
| Danger | danger-dim | danger |
|
||||||
|
|
||||||
|
### Form Inputs
|
||||||
|
|
||||||
|
```
|
||||||
|
Background: bg-input
|
||||||
|
Border: 1px solid border-default
|
||||||
|
Border-radius: 5px
|
||||||
|
Padding: 9px 12px
|
||||||
|
Font: 13px, IBM Plex Sans
|
||||||
|
Focus: border-color accent, box-shadow 0 0 0 2px accent-dim
|
||||||
|
Placeholder: text-muted
|
||||||
|
```
|
||||||
|
|
||||||
|
### Buttons
|
||||||
|
|
||||||
|
**Primary:**
|
||||||
|
```
|
||||||
|
Background: accent (#22d3ee)
|
||||||
|
Color: #fff
|
||||||
|
Border: none
|
||||||
|
Border-radius: 5px
|
||||||
|
Padding: 9px 16px
|
||||||
|
Font: 13px, 550 weight
|
||||||
|
Hover: brightness(1.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ghost:**
|
||||||
|
```
|
||||||
|
Background: transparent
|
||||||
|
Color: text-secondary
|
||||||
|
Border: 1px solid border-default
|
||||||
|
Hover: bg-elevated, text-primary, border-hover
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Blocks
|
||||||
|
|
||||||
|
```
|
||||||
|
Background: bg-code (#0e1017)
|
||||||
|
Border: 1px solid border-default
|
||||||
|
Border-radius: 8px
|
||||||
|
Padding: 18px 20px
|
||||||
|
Font: JetBrains Mono, 12px, line-height 1.7
|
||||||
|
```
|
||||||
|
|
||||||
|
**Syntax colors (dark mode):**
|
||||||
|
|
||||||
|
| Token | Color |
|
||||||
|
|-------|-------|
|
||||||
|
| Comment | #4a5568 |
|
||||||
|
| Keyword | #c792ea |
|
||||||
|
| Function/Cmdlet | #82aaff |
|
||||||
|
| String | #c3e88d |
|
||||||
|
| Variable | #89ddff |
|
||||||
|
| Parameter | #8c93a4 |
|
||||||
|
| Number | #f78c6c |
|
||||||
|
|
||||||
|
### Chips / Tags
|
||||||
|
|
||||||
|
```
|
||||||
|
Font: 11.5px, 500 weight
|
||||||
|
Padding: 4px 11px
|
||||||
|
Border-radius: 20px
|
||||||
|
Background: accent-dim
|
||||||
|
Color: accent-text
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logo
|
||||||
|
|
||||||
|
- **Mark:** 30-32px square, border-radius 8px, `linear-gradient(135deg, #06b6d4, #22d3ee)`, white lightning bolt SVG
|
||||||
|
- **Wordmark:** "ResolutionFlow" in Bricolage Grotesque, 16-17px, weight 700, text-heading color
|
||||||
|
- **Combined:** Mark + wordmark horizontally, 10px gap
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spacing System
|
||||||
|
|
||||||
|
- Component internal gaps: 6px, 8px, 12px, 14px, 16px
|
||||||
|
- Card padding: 20px
|
||||||
|
- Section gaps: 24px between major sections
|
||||||
|
- Page padding: 28px vertical, 36px horizontal
|
||||||
|
- Stat grid gap: 12px
|
||||||
|
- Two-column gap: 16px
|
||||||
|
- Nav item padding: 8px 10px (pinned), 8px 4px (rail)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Border Radius
|
||||||
|
|
||||||
|
| Token | Value | Use |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| radius-sm | 5px | Inputs, buttons, small elements |
|
||||||
|
| radius | 8px | Cards, stat cards, code blocks |
|
||||||
|
| radius-lg | 12px | Large cards, modals |
|
||||||
|
| radius-xl | 16px | Hero elements, CTA boxes |
|
||||||
|
| radius-pill | 100px | Badges, chips, toggle tracks |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shadows
|
||||||
|
|
||||||
|
Minimal to none. No decorative shadows.
|
||||||
|
|
||||||
|
- Cards: none (border only)
|
||||||
|
- Dropdowns/flyouts: `0 4px 12px rgba(0,0,0,0.3)` if needed
|
||||||
|
- Logo mark: `0 2px 8px rgba(14,165,233,0.25)` (subtle brand glow on logo only)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Icons
|
||||||
|
|
||||||
|
All icons use Lucide React (`lucide-react` package).
|
||||||
|
- Default size: 16-18px
|
||||||
|
- Stroke width: 1.6-1.8
|
||||||
|
- Color: `currentColor` (inherits from parent text color)
|
||||||
|
- Nav icons: 20px at stroke-width 1.6
|
||||||
|
- Rail icon opacity: 0.6 default, 0.85 on hover, 1.0 when active
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Landing Page
|
||||||
|
|
||||||
|
The marketing landing page at `/` shares the same color system and typography but has its own layout (no sidebar, full-width sections). Key differences:
|
||||||
|
|
||||||
|
- Navigation: fixed top bar with logo, links, and CTAs
|
||||||
|
- Hero: centered layout with glow effect (radial gradient, accent at 15% opacity)
|
||||||
|
- Sections: 100px vertical padding, max-width 1120px container
|
||||||
|
- Section labels: JetBrains Mono, 12px, uppercase, accent-text color
|
||||||
|
- Section titles: Bricolage Grotesque, clamp(28px, 4vw, 42px), weight 800
|
||||||
|
- Pricing cards: same card pattern but with featured state (accent border + subtle glow)
|
||||||
|
- Product mockup: embedded in hero, shows the actual app UI with browser chrome
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files That Define The Design System
|
||||||
|
|
||||||
|
These are the source-of-truth files in the codebase:
|
||||||
|
|
||||||
|
| File | What It Controls |
|
||||||
|
|------|-----------------|
|
||||||
|
| `frontend/src/index.css` | CSS variables, `@theme` block, base styles |
|
||||||
|
| `frontend/tailwind.config.js` (v3) or inline in index.css (v4) | Color tokens, font families, spacing extensions |
|
||||||
|
| `frontend/index.html` | Google Font imports |
|
||||||
|
| `DESIGN-SYSTEM.md` (this file) | Design decisions, component specs, rules |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deprecated Files — DO NOT Reference
|
||||||
|
|
||||||
|
These files contain outdated design information and should be ignored:
|
||||||
|
|
||||||
|
- `DESIGN_SYSTEM_GUIDE.md` — Old monochrome glass-morphism system
|
||||||
|
- `UI-DESIGN-SYSTEM.md` — Old workspace/purple gradient system
|
||||||
|
- `REBRAND-IMPLEMENTATION-GUIDE.md` — Old purple rebrand from Patherly
|
||||||
|
- `COMPONENT_EXAMPLES.md` — Old monochrome component patterns
|
||||||
|
- Any file referencing `glass-card`, `glass-stat`, `bg-gradient-brand`, or `text-gradient-brand`
|
||||||
@@ -69,7 +69,7 @@ export function DeleteAccountModal({ onClose }: Props) {
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg px-4 py-2 text-sm font-medium',
|
'rounded-lg px-4 py-2 text-sm font-medium',
|
||||||
'bg-white/[0.04] border border-brand-border text-[#e2e5eb]'
|
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function LeaveAccountModal({ accountName, onClose }: Props) {
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg px-4 py-2 text-sm font-medium',
|
'rounded-lg px-4 py-2 text-sm font-medium',
|
||||||
'bg-white/[0.04] border border-brand-border text-[#e2e5eb]'
|
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg px-4 py-2 text-sm font-medium',
|
'rounded-lg px-4 py-2 text-sm font-medium',
|
||||||
'bg-white/[0.04] border border-brand-border text-[#e2e5eb]'
|
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
@@ -100,7 +100,7 @@ export function TransferOwnershipModal({ members, onClose, onTransferred }: Prop
|
|||||||
disabled={isSubmitting || !password}
|
disabled={isSubmitting || !password}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg px-4 py-2 text-sm font-semibold',
|
'rounded-lg px-4 py-2 text-sm font-semibold',
|
||||||
'bg-amber-500 text-brand-dark hover:bg-amber-400',
|
'bg-amber-500 text-white hover:bg-amber-400',
|
||||||
'disabled:opacity-50'
|
'disabled:opacity-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps)
|
|||||||
className={`rounded-2xl px-4 py-3 text-[0.875rem] leading-relaxed ${
|
className={`rounded-2xl px-4 py-3 text-[0.875rem] leading-relaxed ${
|
||||||
role === 'user'
|
role === 'user'
|
||||||
? 'bg-primary/15 text-[#e2e5eb]'
|
? 'bg-primary/15 text-[#e2e5eb]'
|
||||||
: 'bg-white/[0.04] text-[#e2e5eb] border border-brand-border'
|
: 'bg-[#191c25] text-[#e2e5eb] border border-[#1e2130]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<MarkdownContent content={content} className="text-[0.875rem] leading-relaxed" />
|
<MarkdownContent content={content} className="text-[0.875rem] leading-relaxed" />
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function ChatSidebar({
|
|||||||
<div className="px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--glass-border)' }}>
|
<div className="px-4 py-3 border-b shrink-0" style={{ borderColor: 'var(--glass-border)' }}>
|
||||||
<button
|
<button
|
||||||
onClick={onNewChat}
|
onClick={onNewChat}
|
||||||
className="w-full flex items-center justify-center gap-2 bg-[#22d3ee] text-brand-dark font-semibold text-sm rounded-lg px-4 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all"
|
className="w-full flex items-center justify-center gap-2 bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-4 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all"
|
||||||
>
|
>
|
||||||
<Plus size={16} />
|
<Plus size={16} />
|
||||||
New Chat
|
New Chat
|
||||||
@@ -103,7 +103,7 @@ function ChatItem({
|
|||||||
'group flex items-center gap-2 px-3 py-2.5 mx-1.5 rounded-lg cursor-pointer transition-colors',
|
'group flex items-center gap-2 px-3 py-2.5 mx-1.5 rounded-lg cursor-pointer transition-colors',
|
||||||
isActive
|
isActive
|
||||||
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb]'
|
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb]'
|
||||||
: 'text-[#848b9b] hover:bg-white/[0.04] hover:text-[#e2e5eb]'
|
: 'text-[#848b9b] hover:bg-[#191c25] hover:text-[#e2e5eb]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<MessageSquare size={14} className="shrink-0" />
|
<MessageSquare size={14} className="shrink-0" />
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export function ConcludeSessionModal({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-2 rounded-lg hover:bg-brand-border text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
className="p-2 rounded-lg hover:bg-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||||
>
|
>
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
</button>
|
</button>
|
||||||
@@ -196,7 +196,7 @@ export function ConcludeSessionModal({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'w-6 h-6 rounded-full flex items-center justify-center text-[0.6875rem] font-sans text-xs font-medium transition-colors',
|
'w-6 h-6 rounded-full flex items-center justify-center text-[0.6875rem] font-sans text-xs font-medium transition-colors',
|
||||||
step === s
|
step === s
|
||||||
? 'bg-[#22d3ee] text-brand-dark'
|
? 'bg-[#22d3ee] text-white'
|
||||||
: (i < ['select-outcome', 'add-notes', 'summary'].indexOf(step))
|
: (i < ['select-outcome', 'add-notes', 'summary'].indexOf(step))
|
||||||
? 'bg-primary/20 text-[#22d3ee]'
|
? 'bg-primary/20 text-[#22d3ee]'
|
||||||
: 'bg-brand-border text-[#848b9b]'
|
: 'bg-brand-border text-[#848b9b]'
|
||||||
@@ -233,8 +233,8 @@ export function ConcludeSessionModal({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'w-full flex items-center gap-4 p-4 rounded-xl border transition-all text-left',
|
'w-full flex items-center gap-4 p-4 rounded-xl border transition-all text-left',
|
||||||
'hover:scale-[1.01] active:scale-[0.99]',
|
'hover:scale-[1.01] active:scale-[0.99]',
|
||||||
'bg-white/[0.02] border-brand-border',
|
'bg-[#14161d] border-[#1e2130]',
|
||||||
'hover:border-white/[0.12] hover:bg-white/[0.04]'
|
'hover:border-[#2a2f3d] hover:bg-[#191c25]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={cn('w-10 h-10 rounded-xl flex items-center justify-center', o.bg)}>
|
<div className={cn('w-10 h-10 rounded-xl flex items-center justify-center', o.bg)}>
|
||||||
@@ -308,7 +308,7 @@ export function ConcludeSessionModal({
|
|||||||
|
|
||||||
{/* Generated summary */}
|
{/* Generated summary */}
|
||||||
<div
|
<div
|
||||||
className="rounded-xl border p-5 bg-white/[0.02]"
|
className="rounded-xl border p-5 bg-[#14161d]"
|
||||||
style={{ borderColor: 'var(--glass-border)' }}
|
style={{ borderColor: 'var(--glass-border)' }}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
@@ -335,7 +335,7 @@ export function ConcludeSessionModal({
|
|||||||
<div />
|
<div />
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-white/[0.04] border border-brand-border hover:border-white/[0.12] transition-all"
|
className="px-4 py-2 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-[#191c25] border border-[#1e2130] hover:border-[#2a2f3d] transition-all"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
@@ -346,14 +346,14 @@ export function ConcludeSessionModal({
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => setStep('select-outcome')}
|
onClick={() => setStep('select-outcome')}
|
||||||
className="px-4 py-2 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-white/[0.04] border border-brand-border hover:border-white/[0.12] transition-all"
|
className="px-4 py-2 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-[#191c25] border border-[#1e2130] hover:border-[#2a2f3d] transition-all"
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleGenerate}
|
onClick={handleGenerate}
|
||||||
disabled={generating}
|
disabled={generating}
|
||||||
className="flex items-center gap-2 bg-[#22d3ee] text-brand-dark font-semibold text-sm rounded-lg px-5 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-50"
|
className="flex items-center gap-2 bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-5 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{generating ? (
|
{generating ? (
|
||||||
<>
|
<>
|
||||||
@@ -390,7 +390,7 @@ export function ConcludeSessionModal({
|
|||||||
'flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-semibold transition-all',
|
'flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-semibold transition-all',
|
||||||
copied
|
copied
|
||||||
? 'bg-emerald-400/15 text-emerald-400 border border-emerald-400/30'
|
? 'bg-emerald-400/15 text-emerald-400 border border-emerald-400/30'
|
||||||
: 'bg-[#22d3ee] text-brand-dark hover:brightness-110 active:scale-[0.98]'
|
: 'bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{copied ? (
|
{copied ? (
|
||||||
@@ -407,7 +407,7 @@ export function ConcludeSessionModal({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2.5 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-white/[0.04] border border-brand-border hover:border-white/[0.12] transition-all"
|
className="px-4 py-2.5 rounded-lg text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-[#191c25] border border-[#1e2130] hover:border-[#2a2f3d] transition-all"
|
||||||
>
|
>
|
||||||
Done
|
Done
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function SuggestedFlowCard({ flow }: SuggestedFlowCardProps) {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className="w-full text-left card-flat p-3 rounded-xl hover:border-white/[0.12] transition-colors group"
|
className="w-full text-left card-flat p-3 rounded-xl hover:border-[#2a2f3d] transition-colors group"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<Box size={14} className="text-[#22d3ee] mt-0.5 shrink-0" />
|
<Box size={14} className="text-[#22d3ee] mt-0.5 shrink-0" />
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export function ContextMenu({ position, items, onClose }: ContextMenuProps) {
|
|||||||
'flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
|
'flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
|
||||||
item.variant === 'danger'
|
item.variant === 'danger'
|
||||||
? 'text-rose-400 hover:bg-rose-500/10'
|
? 'text-rose-400 hover:bg-rose-500/10'
|
||||||
: 'text-[#e2e5eb] hover:bg-brand-border'
|
: 'text-[#e2e5eb] hover:bg-[#1e2130]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{item.icon && (
|
{item.icon && (
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 rounded-lg hover:bg-brand-border text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
className="p-1.5 rounded-lg hover:bg-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||||
>
|
>
|
||||||
<X size={16} />
|
<X size={16} />
|
||||||
</button>
|
</button>
|
||||||
@@ -124,7 +124,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
|
|||||||
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
|
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
|
||||||
msg.role === 'user'
|
msg.role === 'user'
|
||||||
? 'bg-primary/15 text-[#e2e5eb]'
|
? 'bg-primary/15 text-[#e2e5eb]'
|
||||||
: 'bg-white/[0.04] text-[#e2e5eb] border border-brand-border'
|
: 'bg-[#191c25] text-[#e2e5eb] border border-[#1e2130]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
|
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
|
||||||
@@ -133,7 +133,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
|
|||||||
))}
|
))}
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className="flex justify-start">
|
<div className="flex justify-start">
|
||||||
<div className="bg-white/[0.04] border border-brand-border rounded-xl px-3.5 py-2.5">
|
<div className="bg-[#191c25] border border-[#1e2130] rounded-xl px-3.5 py-2.5">
|
||||||
<Loader2 size={16} className="animate-spin text-[#22d3ee]" />
|
<Loader2 size={16} className="animate-spin text-[#22d3ee]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -171,7 +171,7 @@ export function CopilotPanel({ isOpen, onClose, treeId, sessionId, currentNodeId
|
|||||||
<button
|
<button
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={!input.trim() || loading || initializing}
|
disabled={!input.trim() || loading || initializing}
|
||||||
className="bg-[#22d3ee] text-brand-dark p-2.5 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
|
className="bg-[#22d3ee] text-white p-2.5 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
|
||||||
>
|
>
|
||||||
<Send size={16} />
|
<Send size={16} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export function AIPromptDialog({
|
|||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
className="rounded-[10px] bg-white/[0.04] border border-brand-border px-4 py-2 text-sm text-foreground hover:border-white/[0.12] transition-colors disabled:opacity-50"
|
className="rounded-[10px] bg-[#191c25] border border-[#1e2130] px-4 py-2 text-sm text-foreground hover:border-[#2a2f3d] transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
|
|||||||
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
|
className={`max-w-[85%] rounded-xl px-3.5 py-2.5 text-[0.8125rem] leading-relaxed ${
|
||||||
msg.role === 'user'
|
msg.role === 'user'
|
||||||
? 'bg-primary/15 text-foreground'
|
? 'bg-primary/15 text-foreground'
|
||||||
: 'bg-white/[0.04] text-foreground border border-brand-border'
|
: 'bg-[#191c25] text-foreground border border-[#1e2130]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
|
<MarkdownContent content={msg.content} className="text-[0.8125rem] leading-relaxed" />
|
||||||
@@ -59,7 +59,7 @@ export function ChatTab({ messages, input, onInputChange, onSend, isLoading }: C
|
|||||||
))}
|
))}
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="flex justify-start">
|
<div className="flex justify-start">
|
||||||
<div className="bg-white/[0.04] border border-brand-border rounded-xl px-3.5 py-2.5">
|
<div className="bg-[#191c25] border border-[#1e2130] rounded-xl px-3.5 py-2.5">
|
||||||
<Loader2 size={16} className="animate-spin text-primary" />
|
<Loader2 size={16} className="animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export function EditorAIPanel({
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
|
className="p-1.5 rounded-lg hover:bg-[#1e2130] text-muted-foreground hover:text-foreground transition-colors"
|
||||||
>
|
>
|
||||||
<X size={16} />
|
<X size={16} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
|
|||||||
const config = STATUS_CONFIG[s.status]
|
const config = STATUS_CONFIG[s.status]
|
||||||
const StatusIcon = config.icon
|
const StatusIcon = config.icon
|
||||||
return (
|
return (
|
||||||
<div key={s.id} className="rounded-lg border border-border bg-white/[0.02] px-3 py-2">
|
<div key={s.id} className="rounded-lg border border-border bg-[#14161d] px-3 py-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||||
{s.action_type.replace(/_/g, ' ')}
|
{s.action_type.replace(/_/g, ' ')}
|
||||||
@@ -39,7 +39,7 @@ export function SuggestionsTab({ suggestions }: SuggestionsTabProps) {
|
|||||||
{s.target_node_id && (
|
{s.target_node_id && (
|
||||||
<p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p>
|
<p className="mt-1 text-xs text-muted-foreground truncate">Node: {s.target_node_id}</p>
|
||||||
)}
|
)}
|
||||||
<p className="mt-0.5 font-label text-[0.625rem] text-brand-text-muted">
|
<p className="mt-0.5 font-label text-[0.625rem] text-[#4f5666]">
|
||||||
{new Date(s.created_at).toLocaleDateString()}
|
{new Date(s.created_at).toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export function ImportFlowModal({ onClose }: ImportFlowModalProps) {
|
|||||||
'flex flex-col items-center justify-center rounded-lg border-2 border-dashed px-4 py-8 text-center transition-colors cursor-pointer',
|
'flex flex-col items-center justify-center rounded-lg border-2 border-dashed px-4 py-8 text-center transition-colors cursor-pointer',
|
||||||
isDragging
|
isDragging
|
||||||
? 'border-primary/50 bg-primary/5'
|
? 'border-primary/50 bg-primary/5'
|
||||||
: 'border-[#1e2130] hover:border-white/[0.12]'
|
: 'border-[#1e2130] hover:border-[#2a2f3d]'
|
||||||
)}
|
)}
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
onDragOver={(e) => { e.preventDefault(); setIsDragging(true) }}
|
onDragOver={(e) => { e.preventDefault(); setIsDragging(true) }}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export function FallbackSteps({
|
|||||||
key={fbStep.id}
|
key={fbStep.id}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg border p-3 transition-colors',
|
'rounded-lg border p-3 transition-colors',
|
||||||
'bg-white/[0.02] border-[#1e2130]/50',
|
'bg-[#14161d] border-[#1e2130]/50',
|
||||||
isCompleted && 'border-emerald-500/30 bg-emerald-500/5'
|
isCompleted && 'border-emerald-500/30 bg-emerald-500/5'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function ParameterCard({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setExpanded(v => !v)}
|
onClick={() => setExpanded(v => !v)}
|
||||||
className="w-full flex items-center gap-2 px-3 py-2.5 bg-white/[0.02] hover:bg-white/[0.04] transition-colors"
|
className="w-full flex items-center gap-2 px-3 py-2.5 bg-[#14161d] hover:bg-[#191c25] transition-colors"
|
||||||
>
|
>
|
||||||
<GripVertical size={14} className="text-[#848b9b]/50 shrink-0" />
|
<GripVertical size={14} className="text-[#848b9b]/50 shrink-0" />
|
||||||
{expanded ? <ChevronDown size={14} className="text-[#848b9b]" /> : <ChevronRight size={14} className="text-[#848b9b]" />}
|
{expanded ? <ChevronDown size={14} className="text-[#848b9b]" /> : <ChevronRight size={14} className="text-[#848b9b]" />}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
|
|||||||
{templates.map(t => (
|
{templates.map(t => (
|
||||||
<tr
|
<tr
|
||||||
key={t.id}
|
key={t.id}
|
||||||
className="border-b border-[#1e2130] last:border-b-0 hover:bg-white/[0.02] transition-colors"
|
className="border-b border-[#1e2130] last:border-b-0 hover:bg-[#14161d] transition-colors"
|
||||||
>
|
>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<span className="text-[#e2e5eb] font-medium">{t.name}</span>
|
<span className="text-[#e2e5eb] font-medium">{t.name}</span>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function ScriptParameterForm({ canGenerate }: Props) {
|
|||||||
|
|
||||||
if (parameters.length === 0) {
|
if (parameters.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 rounded-lg border border-border bg-white/[0.02] px-3 py-3">
|
<div className="flex items-center gap-2 rounded-lg border border-border bg-[#14161d] px-3 py-3">
|
||||||
<Terminal size={14} className="text-muted-foreground shrink-0" />
|
<Terminal size={14} className="text-muted-foreground shrink-0" />
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
This template has no parameters — click <span className="text-foreground font-medium">Generate</span> to produce the script.
|
This template has no parameters — click <span className="text-foreground font-medium">Generate</span> to produce the script.
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ export function TicketPickerModal({ open, onClose, sessionId, onLinked, onSelect
|
|||||||
disabled={isLooking}
|
disabled={isLooking}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-full rounded-lg border border-transparent px-3 py-2.5 text-left transition-all',
|
'w-full rounded-lg border border-transparent px-3 py-2.5 text-left transition-all',
|
||||||
'hover:border-[#1e2130] hover:bg-white/[0.04]',
|
'hover:border-[#1e2130] hover:bg-[#191c25]',
|
||||||
'disabled:opacity-50',
|
'disabled:opacity-50',
|
||||||
result.closed && 'opacity-60'
|
result.closed && 'opacity-60'
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ const buttonVariants = cva(
|
|||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
primary:
|
primary:
|
||||||
'bg-[#22d3ee] text-brand-dark font-semibold hover:brightness-110',
|
'bg-[#22d3ee] text-white font-semibold hover:brightness-110',
|
||||||
secondary:
|
secondary:
|
||||||
'bg-white/[0.04] border border-brand-border text-[#e2e5eb] hover:border-white/[0.12] hover:bg-brand-border',
|
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb] hover:border-[#2a2f3d] hover:bg-[#1e2130]',
|
||||||
destructive:
|
destructive:
|
||||||
'bg-red-400/10 text-red-400 border border-red-400/20 hover:bg-red-400/20',
|
'bg-red-400/10 text-red-400 border border-red-400/20 hover:bg-red-400/20',
|
||||||
ghost:
|
ghost:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function ListSkeleton({ count = 5, className }: { count?: number; classNa
|
|||||||
return (
|
return (
|
||||||
<div className={cn('space-y-3', className)}>
|
<div className={cn('space-y-3', className)}>
|
||||||
{Array.from({ length: count }).map((_, i) => (
|
{Array.from({ length: count }).map((_, i) => (
|
||||||
<div key={i} className="flex items-center gap-3 px-4 py-3 rounded-lg bg-white/[0.02]">
|
<div key={i} className="flex items-center gap-3 px-4 py-3 rounded-lg bg-[#14161d]">
|
||||||
<Skeleton className="size-8 rounded-full shrink-0" />
|
<Skeleton className="size-8 rounded-full shrink-0" />
|
||||||
<div className="flex-1 space-y-2">
|
<div className="flex-1 space-y-2">
|
||||||
<Skeleton className="h-4 w-2/3" />
|
<Skeleton className="h-4 w-2/3" />
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ export default function AssistantChatPage() {
|
|||||||
<div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center">
|
<div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center">
|
||||||
<Sparkles size={14} className="text-[#22d3ee]" />
|
<Sparkles size={14} className="text-[#22d3ee]" />
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white/[0.04] border border-brand-border rounded-2xl px-4 py-3">
|
<div className="bg-[#191c25] border border-[#1e2130] rounded-2xl px-4 py-3">
|
||||||
<Loader2 size={16} className="animate-spin text-[#22d3ee]" />
|
<Loader2 size={16} className="animate-spin text-[#22d3ee]" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -302,7 +302,7 @@ export default function AssistantChatPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
disabled={!input.trim() || loading}
|
disabled={!input.trim() || loading}
|
||||||
className="bg-[#22d3ee] text-brand-dark p-3 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
|
className="bg-[#22d3ee] text-white p-3 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
|
||||||
title="Send message"
|
title="Send message"
|
||||||
>
|
>
|
||||||
<Send size={18} />
|
<Send size={18} />
|
||||||
@@ -338,7 +338,7 @@ export default function AssistantChatPage() {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={handleNewChat}
|
onClick={handleNewChat}
|
||||||
className="bg-[#22d3ee] text-brand-dark font-semibold text-sm rounded-lg px-6 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all"
|
className="bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-6 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all"
|
||||||
>
|
>
|
||||||
Start a Conversation
|
Start a Conversation
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function GuideDetailPage() {
|
|||||||
<p className="text-sm text-[#848b9b] mb-4">The guide you're looking for doesn't exist.</p>
|
<p className="text-sm text-[#848b9b] mb-4">The guide you're looking for doesn't exist.</p>
|
||||||
<Link
|
<Link
|
||||||
to="/guides"
|
to="/guides"
|
||||||
className="bg-[#22d3ee] text-brand-dark font-semibold text-sm rounded-lg px-5 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
|
className="bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-5 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
|
||||||
>
|
>
|
||||||
Back to Guides
|
Back to Guides
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function LoginPage() {
|
|||||||
data-testid="login-submit"
|
data-testid="login-submit"
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-full rounded-lg px-4 py-2.5 text-sm font-semibold',
|
'w-full rounded-lg px-4 py-2.5 text-sm font-semibold',
|
||||||
'bg-[#22d3ee] text-brand-dark hover:brightness-110 active:scale-[0.98]',
|
'bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98]',
|
||||||
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
|
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
|
||||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
'transition-all'
|
'transition-all'
|
||||||
|
|||||||
@@ -399,12 +399,12 @@ export default function SurveyPage() {
|
|||||||
</button>
|
</button>
|
||||||
) : <div />}
|
) : <div />}
|
||||||
{si < SLIDES.length - 1 ? (
|
{si < SLIDES.length - 1 ? (
|
||||||
<button onClick={() => goSlide(si + 1)} className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold bg-[#22d3ee] text-brand-dark transition-all duration-150 hover:brightness-110 active:scale-[0.98]">
|
<button onClick={() => goSlide(si + 1)} className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold bg-[#22d3ee] text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98]">
|
||||||
Next
|
Next
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button onClick={handleSubmit} disabled={isSubmitting} className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold bg-[#22d3ee] text-brand-dark transition-all duration-150 hover:brightness-110 active:scale-[0.98] disabled:opacity-40 disabled:cursor-not-allowed">
|
<button onClick={handleSubmit} disabled={isSubmitting} className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold bg-[#22d3ee] text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98] disabled:opacity-40 disabled:cursor-not-allowed">
|
||||||
{isSubmitting ? 'Submitting...' : 'Submit'}
|
{isSubmitting ? 'Submitting...' : 'Submit'}
|
||||||
{!isSubmitting && <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20 6L9 17l-5-5"/></svg>}
|
{!isSubmitting && <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20 6L9 17l-5-5"/></svg>}
|
||||||
</button>
|
</button>
|
||||||
@@ -443,7 +443,7 @@ export default function SurveyPage() {
|
|||||||
value={emailInput}
|
value={emailInput}
|
||||||
onChange={e => setEmailInput(e.target.value)}
|
onChange={e => setEmailInput(e.target.value)}
|
||||||
placeholder="your@email.com"
|
placeholder="your@email.com"
|
||||||
className="flex-1 rounded-[9px] px-3.5 py-2.5 text-sm text-[#e2e5eb] placeholder:text-brand-text-muted focus:outline-hidden"
|
className="flex-1 rounded-[9px] px-3.5 py-2.5 text-sm text-[#e2e5eb] placeholder:text-[#4f5666] focus:outline-hidden"
|
||||||
style={{ background: 'rgba(16, 17, 20, 0.6)', border: '1px solid var(--glass-border)' }}
|
style={{ background: 'rgba(16, 17, 20, 0.6)', border: '1px solid var(--glass-border)' }}
|
||||||
onFocus={e => { e.currentTarget.style.borderColor = 'var(--color-primary)' }}
|
onFocus={e => { e.currentTarget.style.borderColor = 'var(--color-primary)' }}
|
||||||
onBlur={e => { e.currentTarget.style.borderColor = 'var(--glass-border)' }}
|
onBlur={e => { e.currentTarget.style.borderColor = 'var(--glass-border)' }}
|
||||||
@@ -473,7 +473,7 @@ export default function SurveyPage() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={!emailInput.trim() || emailSending}
|
disabled={!emailInput.trim() || emailSending}
|
||||||
className="inline-flex items-center justify-center gap-2 px-5 py-2.5 rounded-[9px] text-sm font-semibold bg-[#22d3ee] text-brand-dark transition-all duration-150 hover:brightness-110 active:scale-[0.98] disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap"
|
className="inline-flex items-center justify-center gap-2 px-5 py-2.5 rounded-[9px] text-sm font-semibold bg-[#22d3ee] text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98] disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{emailSending ? (
|
{emailSending ? (
|
||||||
<>
|
<>
|
||||||
@@ -502,7 +502,7 @@ export default function SurveyPage() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/survey/thank-you')}
|
onClick={() => navigate('/survey/thank-you')}
|
||||||
className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 rounded-lg text-sm font-semibold bg-[#22d3ee] text-brand-dark transition-all duration-150 hover:brightness-110 active:scale-[0.98]"
|
className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 rounded-lg text-sm font-semibold bg-[#22d3ee] text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98]"
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg>
|
||||||
@@ -600,7 +600,7 @@ function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQues
|
|||||||
value={(answer as string) || ''}
|
value={(answer as string) || ''}
|
||||||
onChange={e => setAnswer(q.id, e.target.value)}
|
onChange={e => setAnswer(q.id, e.target.value)}
|
||||||
placeholder="Type your answer here..."
|
placeholder="Type your answer here..."
|
||||||
className="w-full min-h-[100px] rounded-[9px] p-3 sm:p-3.5 text-[13px] sm:text-sm text-[#e2e5eb] leading-relaxed resize-y transition-all duration-200 placeholder:text-brand-text-muted focus:outline-hidden"
|
className="w-full min-h-[100px] rounded-[9px] p-3 sm:p-3.5 text-[13px] sm:text-sm text-[#e2e5eb] leading-relaxed resize-y transition-all duration-200 placeholder:text-[#4f5666] focus:outline-hidden"
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(16, 17, 20, 0.6)',
|
background: 'rgba(16, 17, 20, 0.6)',
|
||||||
border: '1px solid var(--glass-border)',
|
border: '1px solid var(--glass-border)',
|
||||||
@@ -736,7 +736,7 @@ function DragRank({ items, onChange }: { items: string[]; onChange: (items: stri
|
|||||||
color: 'var(--color-muted-foreground)',
|
color: 'var(--color-muted-foreground)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="shrink-0 text-brand-text-muted">
|
<div className="shrink-0 text-[#4f5666]">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="9" cy="6" r="1"/><circle cx="15" cy="6" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="18" r="1"/><circle cx="15" cy="18" r="1"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="9" cy="6" r="1"/><circle cx="15" cy="6" r="1"/><circle cx="9" cy="12" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="9" cy="18" r="1"/><circle cx="15" cy="18" r="1"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="font-sans text-xs text-[11px] font-semibold w-5 text-center shrink-0" style={{ color: 'var(--color-primary)' }}>{idx + 1}</div>
|
<div className="font-sans text-xs text-[11px] font-semibold w-5 text-center shrink-0" style={{ color: 'var(--color-primary)' }}>{idx + 1}</div>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function VerifyEmailPage() {
|
|||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className={cn(
|
className={cn(
|
||||||
'mt-6 inline-flex items-center rounded-lg bg-[#22d3ee] px-6 py-2 text-sm font-semibold text-brand-dark',
|
'mt-6 inline-flex items-center rounded-lg bg-[#22d3ee] px-6 py-2 text-sm font-semibold text-white',
|
||||||
'hover:brightness-110'
|
'hover:brightness-110'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -58,8 +58,8 @@ export function VerifyEmailPage() {
|
|||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className={cn(
|
className={cn(
|
||||||
'mt-6 inline-flex items-center rounded-lg bg-white/[0.04] border border-brand-border px-6 py-2 text-sm font-medium text-[#e2e5eb]',
|
'mt-6 inline-flex items-center rounded-lg bg-[#191c25] border border-[#1e2130] px-6 py-2 text-sm font-medium text-[#e2e5eb]',
|
||||||
'hover:border-white/[0.12]'
|
'hover:border-[#2a2f3d]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Go to Dashboard
|
Go to Dashboard
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export default function ChatRetentionSettingsPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-[#22d3ee] text-brand-dark font-semibold text-sm rounded-lg px-5 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40 flex items-center gap-2"
|
className="bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-5 py-2.5 hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
{saving ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
|
{saving ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
|
||||||
Save Settings
|
Save Settings
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export function ProfileSettingsPage() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
disabled={isSaving || !hasChanges}
|
disabled={isSaving || !hasChanges}
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-4 py-2 text-sm font-semibold text-brand-dark',
|
'inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-4 py-2 text-sm font-semibold text-white',
|
||||||
'hover:brightness-110 active:scale-[0.98]',
|
'hover:brightness-110 active:scale-[0.98]',
|
||||||
'disabled:opacity-50 disabled:cursor-not-allowed'
|
'disabled:opacity-50 disabled:cursor-not-allowed'
|
||||||
)}
|
)}
|
||||||
@@ -145,8 +145,8 @@ export function ProfileSettingsPage() {
|
|||||||
to="/change-password"
|
to="/change-password"
|
||||||
className={cn(
|
className={cn(
|
||||||
'inline-flex items-center rounded-lg px-4 py-2 text-sm font-medium',
|
'inline-flex items-center rounded-lg px-4 py-2 text-sm font-medium',
|
||||||
'bg-white/[0.04] border border-brand-border text-[#e2e5eb]',
|
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb]',
|
||||||
'hover:border-white/[0.12]'
|
'hover:border-[#2a2f3d]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Change Password
|
Change Password
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default function SurveyInvitesPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleCreate(false)}
|
onClick={() => handleCreate(false)}
|
||||||
disabled={creating || !name.trim()}
|
disabled={creating || !name.trim()}
|
||||||
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-4 py-2 text-sm font-semibold text-brand-dark hover:brightness-110 active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-4 py-2 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||||
>
|
>
|
||||||
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Link2 className="h-4 w-4" />}
|
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Link2 className="h-4 w-4" />}
|
||||||
Generate Link
|
Generate Link
|
||||||
@@ -109,7 +109,7 @@ export default function SurveyInvitesPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleCreate(true)}
|
onClick={() => handleCreate(true)}
|
||||||
disabled={creating || !name.trim() || !email.trim()}
|
disabled={creating || !name.trim() || !email.trim()}
|
||||||
className="inline-flex items-center gap-2 rounded-lg bg-white/[0.04] border border-brand-border px-4 py-2 text-sm font-medium text-[#e2e5eb] hover:border-white/[0.12] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
className="inline-flex items-center gap-2 rounded-lg bg-[#191c25] border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#e2e5eb] hover:border-[#2a2f3d] active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||||
>
|
>
|
||||||
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||||
Send Email
|
Send Email
|
||||||
@@ -132,7 +132,7 @@ export default function SurveyInvitesPage() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleCopy(lastCreated.survey_url)}
|
onClick={() => handleCopy(lastCreated.survey_url)}
|
||||||
className="shrink-0 rounded-lg p-2 text-[#848b9b] hover:bg-brand-border hover:text-[#e2e5eb] transition-colors"
|
className="shrink-0 rounded-lg p-2 text-[#848b9b] hover:bg-[#1e2130] hover:text-[#e2e5eb] transition-colors"
|
||||||
>
|
>
|
||||||
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
||||||
</button>
|
</button>
|
||||||
@@ -163,7 +163,7 @@ export default function SurveyInvitesPage() {
|
|||||||
<tr><td colSpan={7} className="px-4 py-8 text-center text-sm text-[#848b9b]">No invites yet</td></tr>
|
<tr><td colSpan={7} className="px-4 py-8 text-center text-sm text-[#848b9b]">No invites yet</td></tr>
|
||||||
) : (
|
) : (
|
||||||
invites.map(invite => (
|
invites.map(invite => (
|
||||||
<tr key={invite.id} className="border-b border-[#1e2130]/50 hover:bg-white/[0.02] transition-colors">
|
<tr key={invite.id} className="border-b border-[#1e2130]/50 hover:bg-[#14161d] transition-colors">
|
||||||
<td className="px-4 py-3 text-sm text-[#e2e5eb]">{invite.recipient_name}</td>
|
<td className="px-4 py-3 text-sm text-[#e2e5eb]">{invite.recipient_name}</td>
|
||||||
<td className="px-4 py-3 text-sm text-[#848b9b]">{invite.recipient_email || '—'}</td>
|
<td className="px-4 py-3 text-sm text-[#848b9b]">{invite.recipient_email || '—'}</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
@@ -188,7 +188,7 @@ export default function SurveyInvitesPage() {
|
|||||||
<td className="px-4 py-3 text-center">
|
<td className="px-4 py-3 text-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleCopy(invite.survey_url)}
|
onClick={() => handleCopy(invite.survey_url)}
|
||||||
className="rounded-lg p-1.5 text-[#848b9b] hover:bg-brand-border hover:text-[#e2e5eb] transition-colors"
|
className="rounded-lg p-1.5 text-[#848b9b] hover:bg-[#1e2130] hover:text-[#e2e5eb] transition-colors"
|
||||||
title="Copy survey link"
|
title="Copy survey link"
|
||||||
>
|
>
|
||||||
<Copy className="h-3.5 w-3.5" />
|
<Copy className="h-3.5 w-3.5" />
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ function ResponseRow({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'border-b border-[#1e2130]/50 transition-colors cursor-pointer',
|
'border-b border-[#1e2130]/50 transition-colors cursor-pointer',
|
||||||
!response.is_read && 'bg-primary/3',
|
!response.is_read && 'bg-primary/3',
|
||||||
'hover:bg-white/[0.02]'
|
'hover:bg-[#14161d]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Checkbox */}
|
{/* Checkbox */}
|
||||||
@@ -214,7 +214,7 @@ function ResponseRow({
|
|||||||
<td className="px-3 py-3 w-10 relative">
|
<td className="px-3 py-3 w-10 relative">
|
||||||
<button
|
<button
|
||||||
onClick={e => { e.stopPropagation(); setShowMenu(!showMenu) }}
|
onClick={e => { e.stopPropagation(); setShowMenu(!showMenu) }}
|
||||||
className="p-1.5 rounded-lg hover:bg-brand-border text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
className="p-1.5 rounded-lg hover:bg-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||||
>
|
>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
@@ -227,14 +227,14 @@ function ResponseRow({
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => { onMarkRead(); setShowMenu(false) }}
|
onClick={() => { onMarkRead(); setShowMenu(false) }}
|
||||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:bg-white/[0.04] transition-colors"
|
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#191c25] transition-colors"
|
||||||
>
|
>
|
||||||
{response.is_read ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
{response.is_read ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
||||||
{response.is_read ? 'Mark Unread' : 'Mark Read'}
|
{response.is_read ? 'Mark Unread' : 'Mark Read'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => { onArchive(); setShowMenu(false) }}
|
onClick={() => { onArchive(); setShowMenu(false) }}
|
||||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:bg-white/[0.04] transition-colors"
|
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#191c25] transition-colors"
|
||||||
>
|
>
|
||||||
{response.archived_at ? <ArchiveRestore className="h-3.5 w-3.5" /> : <Archive className="h-3.5 w-3.5" />}
|
{response.archived_at ? <ArchiveRestore className="h-3.5 w-3.5" /> : <Archive className="h-3.5 w-3.5" />}
|
||||||
{response.archived_at ? 'Unarchive' : 'Archive'}
|
{response.archived_at ? 'Unarchive' : 'Archive'}
|
||||||
@@ -437,7 +437,7 @@ export default function SurveyResponsesPage() {
|
|||||||
'inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-medium transition-colors border',
|
'inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-medium transition-colors border',
|
||||||
showArchived
|
showArchived
|
||||||
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border-primary/20'
|
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border-primary/20'
|
||||||
: 'bg-white/[0.04] text-[#848b9b] border-brand-border hover:border-white/[0.12]'
|
: 'bg-[#191c25] text-[#848b9b] border-[#1e2130] hover:border-[#2a2f3d]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Archive className="h-3.5 w-3.5" />
|
<Archive className="h-3.5 w-3.5" />
|
||||||
@@ -446,7 +446,7 @@ export default function SurveyResponsesPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={exporting || responses.length === 0}
|
disabled={exporting || responses.length === 0}
|
||||||
className="inline-flex items-center gap-2 rounded-lg bg-white/[0.04] border border-brand-border px-4 py-2 text-sm font-medium text-[#e2e5eb] transition-colors hover:border-white/[0.12] disabled:opacity-50"
|
className="inline-flex items-center gap-2 rounded-lg bg-[#191c25] border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#e2e5eb] transition-colors hover:border-[#2a2f3d] disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{exporting ? (
|
{exporting ? (
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
@@ -508,21 +508,21 @@ export default function SurveyResponsesPage() {
|
|||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBulkAction('mark_read')}
|
onClick={() => handleBulkAction('mark_read')}
|
||||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-brand-border transition-colors"
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] transition-colors"
|
||||||
>
|
>
|
||||||
<Eye className="h-3.5 w-3.5" />
|
<Eye className="h-3.5 w-3.5" />
|
||||||
Mark Read
|
Mark Read
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBulkAction('mark_unread')}
|
onClick={() => handleBulkAction('mark_unread')}
|
||||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-brand-border transition-colors"
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] transition-colors"
|
||||||
>
|
>
|
||||||
<EyeOff className="h-3.5 w-3.5" />
|
<EyeOff className="h-3.5 w-3.5" />
|
||||||
Mark Unread
|
Mark Unread
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleBulkAction('archive')}
|
onClick={() => handleBulkAction('archive')}
|
||||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-brand-border transition-colors"
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] transition-colors"
|
||||||
>
|
>
|
||||||
<Archive className="h-3.5 w-3.5" />
|
<Archive className="h-3.5 w-3.5" />
|
||||||
Archive
|
Archive
|
||||||
@@ -536,7 +536,7 @@ export default function SurveyResponsesPage() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedIds(new Set())}
|
onClick={() => setSelectedIds(new Set())}
|
||||||
className="px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-brand-border transition-colors"
|
className="px-3 py-1.5 rounded-lg text-xs font-medium text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] transition-colors"
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user