chore: Tailwind CSS v3 → v4 migration (#99)
* chore: run Tailwind v4 upgrade tool (Phase 1) - Upgraded tailwindcss v3 → v4.2.1, postcss plugin to @tailwindcss/postcss - Deleted tailwind.config.js, migrated theme to CSS @theme block in index.css - Replaced @tailwind directives with @import 'tailwindcss' - Added @custom-variant dark, @utility blocks for custom utilities - Updated class names across 128 files (shadow-sm → shadow-xs, etc.) - Removed autoprefixer (built into v4) - Added migration plan doc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: switch from @tailwindcss/postcss to @tailwindcss/vite (Phase 2) - Replaced @tailwindcss/postcss with @tailwindcss/vite plugin - Deleted postcss.config.js (no longer needed) - Tailwind now runs as a native Vite plugin for faster HMR Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: convert to OKLCH colors, move keyframes into @theme (Phase 3-4) - Replaced all HSL color indirection with direct OKLCH values in @theme - Moved all keyframes inside @theme block (v4 pattern) - Eliminated hsl(var(--x)) double-indirection across 17 component files - Replaced hsl() inline styles with var(--color-*) theme references - Cleaned up redundant rdp-* utility blocks - Fixed @custom-variant dark syntax to use :where() - Added sidebar/glass/shadow vars as OKLCH in :root Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #99.
This commit is contained in:
@@ -189,7 +189,7 @@ export function AccountSettingsPage() {
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
autoFocus
|
||||
onKeyDown={(e) => {
|
||||
@@ -367,7 +367,7 @@ export function AccountSettingsPage() {
|
||||
}}
|
||||
className={cn(
|
||||
'rounded-md border border-border bg-card px-2 py-0.5 text-xs',
|
||||
'text-foreground focus:border-primary focus:outline-none'
|
||||
'text-foreground focus:border-primary focus:outline-hidden'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">engineer</option>
|
||||
@@ -415,7 +415,7 @@ export function AccountSettingsPage() {
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<select
|
||||
@@ -423,7 +423,7 @@ export function AccountSettingsPage() {
|
||||
onChange={(e) => setInviteRole(e.target.value)}
|
||||
className={cn(
|
||||
'rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">Engineer</option>
|
||||
@@ -609,7 +609,7 @@ export function AccountSettingsPage() {
|
||||
className={cn(
|
||||
'mt-2 block w-full max-w-xs rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-sm text-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="markdown">Markdown (.md)</option>
|
||||
|
||||
@@ -223,7 +223,7 @@ export default function AssistantChatPage() {
|
||||
<div className="w-8 h-8 rounded-full bg-primary/15 flex items-center justify-center">
|
||||
<Sparkles size={14} className="text-primary" />
|
||||
</div>
|
||||
<div className="bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] rounded-2xl px-4 py-3">
|
||||
<div className="bg-[rgba(255,255,255,0.04)] border border-brand-border rounded-2xl px-4 py-3">
|
||||
<Loader2 size={16} className="animate-spin text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,7 +242,7 @@ export default function AssistantChatPage() {
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Ask about IT, networking, troubleshooting..."
|
||||
rows={3}
|
||||
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
|
||||
className="flex-1 resize-none rounded-xl border bg-card text-foreground text-sm placeholder:text-muted-foreground px-4 py-3 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
|
||||
style={{ borderColor: 'var(--glass-border)' }}
|
||||
disabled={loading}
|
||||
/>
|
||||
@@ -250,7 +250,7 @@ export default function AssistantChatPage() {
|
||||
<button
|
||||
onClick={handleSend}
|
||||
disabled={!input.trim() || loading}
|
||||
className="bg-gradient-brand text-[#101114] p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
|
||||
className="bg-gradient-brand text-brand-dark p-3 rounded-xl hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40"
|
||||
title="Send message"
|
||||
>
|
||||
<Send size={18} />
|
||||
@@ -286,7 +286,7 @@ export default function AssistantChatPage() {
|
||||
</p>
|
||||
<button
|
||||
onClick={handleNewChat}
|
||||
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-6 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
>
|
||||
Start a Conversation
|
||||
</button>
|
||||
|
||||
@@ -97,7 +97,7 @@ export function ChangePasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
/>
|
||||
@@ -116,7 +116,7 @@ export function ChangePasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="At least 10 characters"
|
||||
@@ -139,7 +139,7 @@ export function ChangePasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
/>
|
||||
@@ -151,7 +151,7 @@ export function ChangePasswordPage() {
|
||||
className={cn(
|
||||
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'transition-all'
|
||||
)}
|
||||
|
||||
@@ -171,7 +171,7 @@ export function FeedbackPage() {
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-muted-foreground">We'll reply to this address if we need more details.</p>
|
||||
</div>
|
||||
@@ -193,7 +193,7 @@ export function FeedbackPage() {
|
||||
onClick={() => setTypeDropdownOpen(!typeDropdownOpen)}
|
||||
onKeyDown={handleDropdownKeyDown}
|
||||
className={cn(
|
||||
"w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none",
|
||||
"w-full rounded-lg border border-border bg-card px-3 py-2 text-left flex items-center justify-between focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden",
|
||||
feedbackType ? "text-foreground" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
@@ -244,7 +244,7 @@ export function FeedbackPage() {
|
||||
required
|
||||
minLength={10}
|
||||
rows={6}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none resize-y"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden resize-y"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{message.trim().length < 10
|
||||
|
||||
@@ -75,7 +75,7 @@ export function ForgotPasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="you@example.com"
|
||||
@@ -88,7 +88,7 @@ export function ForgotPasswordPage() {
|
||||
className={cn(
|
||||
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'focus:outline-none 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',
|
||||
'transition-all'
|
||||
)}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function GuideDetailPage() {
|
||||
<p className="text-sm text-muted-foreground mb-4">The guide you're looking for doesn't exist.</p>
|
||||
<Link
|
||||
to="/guides"
|
||||
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
>
|
||||
Back to Guides
|
||||
</Link>
|
||||
@@ -47,10 +47,10 @@ export default function GuideDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mt-4 pt-4 border-t" style={{ borderColor: 'var(--glass-border)' }}>
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
|
||||
</span>
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
{guide.sections.reduce((acc, s) => acc + s.steps.length, 0)} steps
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -106,7 +106,7 @@ export function LoginPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="you@example.com"
|
||||
@@ -127,7 +127,7 @@ export function LoginPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-[10px] border border-border bg-card px-3 py-2.5',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="••••••••••"
|
||||
@@ -145,8 +145,8 @@ export function LoginPage() {
|
||||
disabled={isLoading}
|
||||
className={cn(
|
||||
'w-full rounded-[10px] px-4 py-2.5 text-sm font-semibold',
|
||||
'bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
|
||||
'bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
|
||||
'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',
|
||||
'transition-all'
|
||||
)}
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function MyAnalyticsPage() {
|
||||
<select
|
||||
value={period}
|
||||
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
|
||||
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
{PERIOD_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
@@ -134,31 +134,31 @@ export default function MyAnalyticsPage() {
|
||||
<AreaChart data={time_series}>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="hsl(var(--border))"
|
||||
stroke="var(--color-border)"
|
||||
vertical={false}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
|
||||
tick={{ fill: 'var(--color-muted-foreground)', fontSize: 12 }}
|
||||
tickLine={false}
|
||||
axisLine={{ stroke: 'hsl(var(--border))' }}
|
||||
axisLine={{ stroke: 'var(--color-border)' }}
|
||||
tickFormatter={(value: string) => {
|
||||
const d = new Date(value)
|
||||
return `${d.getMonth() + 1}/${d.getDate()}`
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
|
||||
tick={{ fill: 'var(--color-muted-foreground)', fontSize: 12 }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
allowDecimals={false}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--card))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
backgroundColor: 'var(--color-card)',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '8px',
|
||||
color: 'hsl(var(--foreground))',
|
||||
color: 'var(--color-foreground)',
|
||||
fontSize: '13px',
|
||||
}}
|
||||
labelFormatter={(value) => {
|
||||
@@ -298,7 +298,7 @@ export default function MyAnalyticsPage() {
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="h-2.5 w-2.5 rounded-full flex-shrink-0"
|
||||
className="h-2.5 w-2.5 rounded-full shrink-0"
|
||||
style={{
|
||||
backgroundColor:
|
||||
OUTCOME_COLORS[outcome] ?? '#94a3b8',
|
||||
|
||||
@@ -136,7 +136,7 @@ export function MyTreesPage() {
|
||||
{showCreateMenu && (
|
||||
<>
|
||||
<div className="fixed inset-0 z-10" onClick={() => setShowCreateMenu(false)} />
|
||||
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm">
|
||||
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-xs">
|
||||
<Link
|
||||
to="/trees/new"
|
||||
onClick={() => setShowCreateMenu(false)}
|
||||
|
||||
@@ -255,7 +255,7 @@ export function ProceduralEditorPage() {
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="e.g. Domain Controller Build"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -266,7 +266,7 @@ export function ProceduralEditorPage() {
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
placeholder="Brief description of this procedure..."
|
||||
rows={2}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -671,10 +671,10 @@ export function ProceduralNavigationPage() {
|
||||
{paramsOpen && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div
|
||||
className="absolute inset-0 bg-background/60 backdrop-blur-sm"
|
||||
className="absolute inset-0 bg-background/60 backdrop-blur-xs"
|
||||
onClick={() => setParamsOpen(false)}
|
||||
/>
|
||||
<div className="relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl backdrop-blur-sm">
|
||||
<div className="relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl backdrop-blur-xs">
|
||||
<div className="flex items-center justify-between border-b border-border px-5 py-4">
|
||||
<h3 className="text-sm font-semibold text-foreground">Project Parameters</h3>
|
||||
<button
|
||||
|
||||
@@ -316,7 +316,7 @@ export function QuickStartPage() {
|
||||
className={cn('glass-card p-4 flex flex-col justify-between fade-in', stat.glow && 'active-glow')}
|
||||
style={{ animationDelay: `${500 + i * 70}ms` }}
|
||||
>
|
||||
<p className="font-label text-[0.625rem] font-medium uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<p className="font-label text-[0.625rem] font-medium uppercase tracking-widest text-muted-foreground">
|
||||
{stat.label}
|
||||
</p>
|
||||
<p className={cn('font-heading text-2xl font-extrabold tracking-tight', stat.gradient && 'text-gradient-brand')}>
|
||||
@@ -343,7 +343,7 @@ export function QuickStartPage() {
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onFocus={() => query.length >= 2 && setShowResults(true)}
|
||||
placeholder="Search flows, sessions, tags…"
|
||||
className="w-full rounded-lg border border-border bg-card py-2.5 pl-9 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card py-2.5 pl-9 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
{showResults && (
|
||||
<div className="absolute z-10 mt-1 w-full rounded-lg border border-border bg-card shadow-xl overflow-hidden">
|
||||
@@ -566,7 +566,7 @@ export function QuickStartPage() {
|
||||
const val = e.target.value
|
||||
setPageSize(val === 'all' ? 'all' : parseInt(val, 10))
|
||||
}}
|
||||
className="rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none"
|
||||
className="rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-hidden"
|
||||
>
|
||||
{pageSizeOptions.map((opt) => (
|
||||
<option key={String(opt)} value={String(opt)}>
|
||||
@@ -587,7 +587,7 @@ export function QuickStartPage() {
|
||||
const val = e.target.value
|
||||
setPageSize(val === 'all' ? 'all' : parseInt(val, 10))
|
||||
}}
|
||||
className="rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none"
|
||||
className="rounded-md border border-border bg-card px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-hidden"
|
||||
>
|
||||
{pageSizeOptions.map((opt) => (
|
||||
<option key={String(opt)} value={String(opt)}>
|
||||
@@ -605,7 +605,7 @@ export function QuickStartPage() {
|
||||
|
||||
{/* Fork Modal */}
|
||||
{forkTarget && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-xs">
|
||||
<div className="w-full max-w-sm rounded-xl border border-border bg-card p-5 shadow-xl">
|
||||
<h3 className="mb-1 text-sm font-semibold text-foreground">Fork this flow?</h3>
|
||||
<p className="mb-4 text-xs text-muted-foreground">
|
||||
@@ -620,7 +620,7 @@ export function QuickStartPage() {
|
||||
onChange={(e) => setForkReason(e.target.value)}
|
||||
placeholder="e.g. Adding Cisco Meraki steps for our network"
|
||||
maxLength={255}
|
||||
className="mb-4 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="mb-4 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleFork()}
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -123,7 +123,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-xl border bg-card px-3 py-2 font-mono tracking-wider',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:outline-none focus:ring-1',
|
||||
'focus:outline-hidden focus:ring-1',
|
||||
inviteCodeStatus === 'valid' && 'border-emerald-400/50 focus:border-emerald-400 focus:ring-emerald-400/30',
|
||||
inviteCodeStatus === 'invalid' && 'border-red-400/50 focus:border-red-400 focus:ring-red-400/30',
|
||||
inviteCodeStatus === 'idle' && 'border-border focus:border-primary focus:ring-primary/20',
|
||||
@@ -157,7 +157,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
placeholder="John Smith"
|
||||
/>
|
||||
@@ -178,7 +178,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
placeholder="you@example.com"
|
||||
/>
|
||||
@@ -198,7 +198,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
placeholder="••••••••••"
|
||||
/>
|
||||
@@ -221,7 +221,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
placeholder="••••••••••"
|
||||
/>
|
||||
@@ -233,7 +233,7 @@ export function RegisterPage() {
|
||||
className={cn(
|
||||
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'transition-all'
|
||||
)}
|
||||
|
||||
@@ -133,7 +133,7 @@ export function ResetPasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="At least 10 characters"
|
||||
@@ -156,7 +156,7 @@ export function ResetPasswordPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
/>
|
||||
@@ -168,7 +168,7 @@ export function ResetPasswordPage() {
|
||||
className={cn(
|
||||
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'focus:outline-hidden focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'transition-all'
|
||||
)}
|
||||
|
||||
@@ -425,7 +425,7 @@ export function SessionDetailPage() {
|
||||
value={exportFormat}
|
||||
onChange={(e) => setExportFormat(e.target.value as typeof exportFormat)}
|
||||
aria-label="Export format"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
<option value="markdown">Markdown</option>
|
||||
<option value="text">Plain Text</option>
|
||||
@@ -437,7 +437,7 @@ export function SessionDetailPage() {
|
||||
value={maxStepIndex ?? ''}
|
||||
onChange={(e) => setMaxStepIndex(e.target.value ? Number(e.target.value) : null)}
|
||||
aria-label="Export through step"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
<option value="">All steps</option>
|
||||
{session.decisions.map((_, idx) => (
|
||||
@@ -449,7 +449,7 @@ export function SessionDetailPage() {
|
||||
value={detailLevel}
|
||||
onChange={(e) => setDetailLevel(e.target.value as 'standard' | 'full')}
|
||||
aria-label="Detail level"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="rounded-md border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
<option value="standard">Standard</option>
|
||||
<option value="full">Full Detail</option>
|
||||
|
||||
@@ -130,7 +130,7 @@ export default function StepLibraryPage() {
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
{deletingStep && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm p-4">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs p-4">
|
||||
<div className="w-full max-w-sm rounded-xl bg-card border border-border p-6 shadow-lg">
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
<div className="rounded-full bg-red-400/10 p-2">
|
||||
|
||||
@@ -329,7 +329,7 @@ export default function SurveyPage() {
|
||||
<span className="hidden sm:inline">Resolution<span className="text-gradient-brand">Flow</span></span>
|
||||
</a>
|
||||
<div className="flex flex-1 items-center gap-2 sm:gap-2.5" style={{ maxWidth: '280px' }}>
|
||||
<div className="flex-1 h-[3px] rounded-full overflow-hidden" style={{ background: 'hsl(var(--border))' }}>
|
||||
<div className="flex-1 h-[3px] rounded-full overflow-hidden" style={{ background: 'var(--color-border)' }}>
|
||||
<div className="h-full rounded-full bg-gradient-brand transition-[width] duration-500" style={{ width: `${progressPct}%` }} />
|
||||
</div>
|
||||
<span className="text-[11px] font-label text-muted-foreground whitespace-nowrap tabular-nums">{answeredCount}/{TOTAL_QUESTIONS}</span>
|
||||
@@ -376,7 +376,7 @@ export default function SurveyPage() {
|
||||
key={i}
|
||||
className="flex-1 h-1 sm:h-[3px] rounded-full transition-colors duration-300"
|
||||
style={{
|
||||
background: i < currentSlide ? '#34d399' : i === currentSlide ? 'linear-gradient(90deg, #06b6d4, #22d3ee)' : 'hsl(var(--border))',
|
||||
background: i < currentSlide ? '#34d399' : i === currentSlide ? 'linear-gradient(90deg, #06b6d4, #22d3ee)' : 'var(--color-border)',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
@@ -399,12 +399,12 @@ export default function SurveyPage() {
|
||||
</button>
|
||||
) : <div />}
|
||||
{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-[10px] text-sm font-semibold bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97]">
|
||||
<button onClick={() => goSlide(si + 1)} className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 sm:py-3 rounded-[10px] text-sm font-semibold bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97]">
|
||||
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>
|
||||
</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-[10px] text-sm font-semibold bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97] 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-[10px] text-sm font-semibold bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 disabled:cursor-not-allowed">
|
||||
{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>}
|
||||
</button>
|
||||
@@ -433,7 +433,7 @@ export default function SurveyPage() {
|
||||
|
||||
{/* Email a copy */}
|
||||
<div className="glass-card-static p-4 sm:p-6 max-w-[420px] mx-auto mb-5">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-3">
|
||||
Email a copy to yourself
|
||||
</p>
|
||||
{!emailSent ? (
|
||||
@@ -443,7 +443,7 @@ export default function SurveyPage() {
|
||||
value={emailInput}
|
||||
onChange={e => setEmailInput(e.target.value)}
|
||||
placeholder="your@email.com"
|
||||
className="flex-1 rounded-[9px] px-3.5 py-2.5 text-sm text-foreground placeholder:text-[#5a6170] focus:outline-none"
|
||||
className="flex-1 rounded-[9px] px-3.5 py-2.5 text-sm text-foreground placeholder:text-brand-text-muted focus:outline-hidden"
|
||||
style={{ background: 'rgba(16, 17, 20, 0.6)', border: '1px solid var(--glass-border)' }}
|
||||
onFocus={e => { e.currentTarget.style.borderColor = 'rgba(6, 182, 212, 0.4)' }}
|
||||
onBlur={e => { e.currentTarget.style.borderColor = 'var(--glass-border)' }}
|
||||
@@ -473,7 +473,7 @@ export default function SurveyPage() {
|
||||
}
|
||||
}}
|
||||
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-gradient-brand text-[#101114] transition-all duration-150 hover:opacity-90 active:scale-[0.97] 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-gradient-brand text-brand-dark transition-all duration-150 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap"
|
||||
>
|
||||
{emailSending ? (
|
||||
<>
|
||||
@@ -502,7 +502,7 @@ export default function SurveyPage() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigate('/survey/thank-you')}
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 rounded-[10px] text-sm font-semibold bg-gradient-brand text-[#101114] shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97]"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 sm:px-6 rounded-[10px] text-sm font-semibold bg-gradient-brand text-brand-dark shadow-lg shadow-primary/20 transition-all duration-150 hover:opacity-90 active:scale-[0.97]"
|
||||
>
|
||||
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>
|
||||
@@ -535,7 +535,7 @@ function ScenarioBox({ scenario }: { scenario: { title: string; symptom: string;
|
||||
|
||||
function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQuestion; answer?: string | string[]; setAnswer: (id: string, val: string | string[]) => void }) {
|
||||
return (
|
||||
<div className="glass-card-static p-4 sm:p-7 mb-3 sm:mb-4 transition-[border-color] duration-200 focus-within:!border-[rgba(6,182,212,0.25)]">
|
||||
<div className="glass-card-static p-4 sm:p-7 mb-3 sm:mb-4 transition-[border-color] duration-200 focus-within:border-[rgba(6,182,212,0.25)]!">
|
||||
<div className="font-label text-[11px] mb-1.5 font-medium" style={{ color: '#06b6d4' }}>Q{q.num}</div>
|
||||
<div className="font-heading text-[14px] sm:text-[15px] font-semibold text-foreground leading-snug mb-1">{q.text}</div>
|
||||
{q.hint && <div className="text-[12px] text-muted-foreground mb-3 sm:mb-4 leading-snug">{q.hint}</div>}
|
||||
@@ -551,10 +551,10 @@ function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQues
|
||||
style={{
|
||||
background: answer === opt ? 'rgba(6, 182, 212, 0.1)' : 'rgba(16, 17, 20, 0.6)',
|
||||
border: `1px solid ${answer === opt ? '#06b6d4' : 'var(--glass-border)'}`,
|
||||
color: answer === opt ? 'hsl(var(--foreground))' : 'hsl(var(--muted-foreground))',
|
||||
color: answer === opt ? 'var(--color-foreground)' : 'var(--color-muted-foreground)',
|
||||
}}
|
||||
>
|
||||
<div className="w-[18px] h-[18px] rounded-full flex-shrink-0 flex items-center justify-center transition-all duration-150 mt-0.5" style={{ border: `2px solid ${answer === opt ? '#06b6d4' : 'var(--glass-border)'}` }}>
|
||||
<div className="w-[18px] h-[18px] rounded-full shrink-0 flex items-center justify-center transition-all duration-150 mt-0.5" style={{ border: `2px solid ${answer === opt ? '#06b6d4' : 'var(--glass-border)'}` }}>
|
||||
{answer === opt && <div className="w-2 h-2 rounded-full" style={{ background: '#06b6d4' }} />}
|
||||
</div>
|
||||
<span className="leading-snug">{opt}</span>
|
||||
@@ -578,10 +578,10 @@ function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQues
|
||||
style={{
|
||||
background: selected ? 'rgba(6, 182, 212, 0.1)' : 'rgba(16, 17, 20, 0.6)',
|
||||
border: `1px solid ${selected ? '#06b6d4' : 'var(--glass-border)'}`,
|
||||
color: selected ? 'hsl(var(--foreground))' : 'hsl(var(--muted-foreground))',
|
||||
color: selected ? 'var(--color-foreground)' : 'var(--color-muted-foreground)',
|
||||
}}
|
||||
>
|
||||
<div className="w-[18px] h-[18px] rounded-[5px] flex-shrink-0 flex items-center justify-center text-[11px] transition-all duration-150 mt-0.5" style={{ border: `2px solid ${selected ? '#06b6d4' : 'var(--glass-border)'}`, background: selected ? '#06b6d4' : 'transparent', color: selected ? 'white' : 'transparent' }}>
|
||||
<div className="w-[18px] h-[18px] rounded-[5px] shrink-0 flex items-center justify-center text-[11px] transition-all duration-150 mt-0.5" style={{ border: `2px solid ${selected ? '#06b6d4' : 'var(--glass-border)'}`, background: selected ? '#06b6d4' : 'transparent', color: selected ? 'white' : 'transparent' }}>
|
||||
{'\u2713'}
|
||||
</div>
|
||||
<span className="leading-snug">{opt}</span>
|
||||
@@ -600,7 +600,7 @@ function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQues
|
||||
value={(answer as string) || ''}
|
||||
onChange={e => setAnswer(q.id, e.target.value)}
|
||||
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-foreground leading-relaxed resize-y transition-all duration-200 placeholder:text-[#5a6170] focus:outline-none"
|
||||
className="w-full min-h-[100px] rounded-[9px] p-3 sm:p-3.5 text-[13px] sm:text-sm text-foreground leading-relaxed resize-y transition-all duration-200 placeholder:text-brand-text-muted focus:outline-hidden"
|
||||
style={{
|
||||
background: 'rgba(16, 17, 20, 0.6)',
|
||||
border: '1px solid var(--glass-border)',
|
||||
@@ -633,7 +633,7 @@ function RangeInput({ question: q, value, onChange }: { question: SurveyQuestion
|
||||
onChange={e => onChange(e.target.value + (q.suffix || ''))}
|
||||
className="w-full h-2 sm:h-1 rounded-full appearance-none cursor-pointer touch-none"
|
||||
style={{
|
||||
background: `linear-gradient(to right, #06b6d4 0%, #06b6d4 ${((numVal - (q.min || 0)) / ((q.max || 10) - (q.min || 0))) * 100}%, hsl(var(--border)) ${((numVal - (q.min || 0)) / ((q.max || 10) - (q.min || 0))) * 100}%, hsl(var(--border)) 100%)`,
|
||||
background: `linear-gradient(to right, #06b6d4 0%, #06b6d4 ${((numVal - (q.min || 0)) / ((q.max || 10) - (q.min || 0))) * 100}%, var(--color-border) ${((numVal - (q.min || 0)) / ((q.max || 10) - (q.min || 0))) * 100}%, var(--color-border) 100%)`,
|
||||
}}
|
||||
/>
|
||||
<div className="flex justify-between text-[11px] text-muted-foreground mt-2.5">
|
||||
@@ -733,13 +733,13 @@ function DragRank({ items, onChange }: { items: string[]; onChange: (items: stri
|
||||
border: `1px solid ${overIdx === idx || draggingIdx === idx ? '#06b6d4' : 'var(--glass-border)'}`,
|
||||
opacity: draggingIdx === idx ? 0.5 : 1,
|
||||
cursor: 'grab',
|
||||
color: 'hsl(var(--muted-foreground))',
|
||||
color: 'var(--color-muted-foreground)',
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0 text-[#5a6170]">
|
||||
<div className="shrink-0 text-brand-text-muted">
|
||||
<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 className="font-label text-[11px] font-semibold w-5 text-center flex-shrink-0" style={{ color: '#06b6d4' }}>{idx + 1}</div>
|
||||
<div className="font-label text-[11px] font-semibold w-5 text-center shrink-0" style={{ color: '#06b6d4' }}>{idx + 1}</div>
|
||||
<div className="flex-1 leading-snug">{item}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function SurveyThankYouPage() {
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#06b6d4" strokeWidth="2">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</svg>
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-primary font-semibold">
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-widest text-primary font-semibold">
|
||||
Have Feedback?
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -100,7 +100,7 @@ export default function TeamAnalyticsPage() {
|
||||
<select
|
||||
value={period}
|
||||
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
|
||||
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
>
|
||||
{PERIOD_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
@@ -144,31 +144,31 @@ export default function TeamAnalyticsPage() {
|
||||
<AreaChart data={time_series}>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="hsl(var(--border))"
|
||||
stroke="var(--color-border)"
|
||||
vertical={false}
|
||||
/>
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
|
||||
tick={{ fill: 'var(--color-muted-foreground)', fontSize: 12 }}
|
||||
tickLine={false}
|
||||
axisLine={{ stroke: 'hsl(var(--border))' }}
|
||||
axisLine={{ stroke: 'var(--color-border)' }}
|
||||
tickFormatter={(value: string) => {
|
||||
const d = new Date(value)
|
||||
return `${d.getMonth() + 1}/${d.getDate()}`
|
||||
}}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
|
||||
tick={{ fill: 'var(--color-muted-foreground)', fontSize: 12 }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
allowDecimals={false}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: 'hsl(var(--card))',
|
||||
border: '1px solid hsl(var(--border))',
|
||||
backgroundColor: 'var(--color-card)',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '8px',
|
||||
color: 'hsl(var(--foreground))',
|
||||
color: 'var(--color-foreground)',
|
||||
fontSize: '13px',
|
||||
}}
|
||||
labelFormatter={(value) => {
|
||||
|
||||
@@ -528,7 +528,7 @@ export function TreeEditorPage() {
|
||||
|
||||
{/* Draft Restore Prompt */}
|
||||
{showDraftPrompt && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
|
||||
<div className="bg-card border border-border rounded-xl w-full max-w-md p-6 shadow-lg">
|
||||
<h2 className="mb-2 text-lg font-heading font-semibold text-foreground">Restore Draft?</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
@@ -560,7 +560,7 @@ export function TreeEditorPage() {
|
||||
|
||||
{/* Unsaved Changes Dialog */}
|
||||
{blocker.state === 'blocked' && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-xs">
|
||||
<div className="bg-card border border-border rounded-xl w-full max-w-md p-6 shadow-lg">
|
||||
<h2 className="mb-2 text-lg font-heading font-semibold text-foreground">Unsaved Changes</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
|
||||
@@ -312,7 +312,7 @@ export function TreeLibraryPage() {
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
@@ -332,7 +332,7 @@ export function TreeLibraryPage() {
|
||||
aria-label="Filter by category"
|
||||
className={cn(
|
||||
'rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="">All Categories</option>
|
||||
|
||||
@@ -579,7 +579,7 @@ export function TreeNavigationPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -596,7 +596,7 @@ export function TreeNavigationPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -761,7 +761,7 @@ export function TreeNavigationPage() {
|
||||
</div>
|
||||
|
||||
{/* Current Node */}
|
||||
<div className="bg-card border border-border rounded-xl p-6 shadow-sm">
|
||||
<div className="bg-card border border-border rounded-xl p-6 shadow-xs">
|
||||
{/* Answer placeholder guard */}
|
||||
{currentNode && currentNode.type === 'answer' && (
|
||||
<div className="rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-6 text-center">
|
||||
@@ -829,7 +829,7 @@ export function TreeNavigationPage() {
|
||||
'flex items-center gap-3'
|
||||
)}
|
||||
>
|
||||
<span className="flex-shrink-0 rounded-full bg-primary/20 px-2 py-0.5 text-xs font-medium text-primary">
|
||||
<span className="shrink-0 rounded-full bg-primary/20 px-2 py-0.5 text-xs font-medium text-primary">
|
||||
Custom
|
||||
</span>
|
||||
<span>{cs.step_data.title}</span>
|
||||
@@ -922,7 +922,7 @@ export function TreeNavigationPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-border bg-accent px-3 py-2',
|
||||
'font-mono text-sm text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<p className="mt-1 text-right text-xs text-muted-foreground">
|
||||
@@ -949,7 +949,7 @@ export function TreeNavigationPage() {
|
||||
)}
|
||||
>
|
||||
<span>Continue to: {targetLabel.length > 50 ? `${targetLabel.slice(0, 50)}...` : targetLabel}</span>
|
||||
<ArrowRight className="h-4 w-4 flex-shrink-0" />
|
||||
<ArrowRight className="h-4 w-4 shrink-0" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
@@ -1046,7 +1046,7 @@ export function TreeNavigationPage() {
|
||||
className={cn(
|
||||
'block w-full rounded-md border border-border bg-accent px-3 py-2',
|
||||
'font-mono text-sm text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<p className="mt-1 text-right text-xs text-muted-foreground">
|
||||
@@ -1136,7 +1136,7 @@ export function TreeNavigationPage() {
|
||||
className={cn(
|
||||
'mt-1 block w-full rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ export function VerifyEmailPage() {
|
||||
<Link
|
||||
to="/"
|
||||
className={cn(
|
||||
'mt-6 inline-flex items-center rounded-[10px] bg-gradient-brand px-6 py-2 text-sm font-semibold text-[#101114]',
|
||||
'mt-6 inline-flex items-center rounded-[10px] bg-gradient-brand px-6 py-2 text-sm font-semibold text-brand-dark',
|
||||
'shadow-lg shadow-primary/20 hover:opacity-90'
|
||||
)}
|
||||
>
|
||||
@@ -55,7 +55,7 @@ export function VerifyEmailPage() {
|
||||
<Link
|
||||
to="/"
|
||||
className={cn(
|
||||
'mt-6 inline-flex items-center rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-6 py-2 text-sm font-medium text-foreground',
|
||||
'mt-6 inline-flex items-center rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-brand-border px-6 py-2 text-sm font-medium text-foreground',
|
||||
'hover:border-[rgba(255,255,255,0.12)]'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function ChatRetentionSettingsPage() {
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground block mb-1.5">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-1.5">
|
||||
Retention Period (days)
|
||||
</label>
|
||||
<input
|
||||
@@ -73,7 +73,7 @@ export default function ChatRetentionSettingsPage() {
|
||||
onChange={e => setRetentionDays(e.target.value)}
|
||||
min={1}
|
||||
max={365}
|
||||
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
|
||||
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
|
||||
style={{ borderColor: 'var(--glass-border)' }}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
@@ -82,7 +82,7 @@ export default function ChatRetentionSettingsPage() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground block mb-1.5">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-1.5">
|
||||
Max Conversations
|
||||
</label>
|
||||
<input
|
||||
@@ -91,7 +91,7 @@ export default function ChatRetentionSettingsPage() {
|
||||
onChange={e => setMaxCount(e.target.value)}
|
||||
min={10}
|
||||
max={10000}
|
||||
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
|
||||
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-hidden focus:border-[rgba(6,182,212,0.3)]"
|
||||
style={{ borderColor: 'var(--glass-border)' }}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
@@ -104,7 +104,7 @@ export default function ChatRetentionSettingsPage() {
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="bg-gradient-brand text-[#101114] font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40 flex items-center gap-2"
|
||||
className="bg-gradient-brand text-brand-dark font-semibold text-sm rounded-[10px] px-5 py-2.5 hover:opacity-90 active:scale-[0.97] transition-all disabled:opacity-40 flex items-center gap-2"
|
||||
>
|
||||
{saving ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
|
||||
Save Settings
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { UserUpdate } from '@/types'
|
||||
const inputClass = cn(
|
||||
'mt-1 block w-full rounded-[10px] border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
'focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden focus:ring-1 focus:ring-primary/20'
|
||||
)
|
||||
|
||||
export function ProfileSettingsPage() {
|
||||
@@ -122,7 +122,7 @@ export function ProfileSettingsPage() {
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 text-sm text-rose-500">
|
||||
<AlertCircle className="h-4 w-4 flex-shrink-0" />
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
@@ -132,7 +132,7 @@ export function ProfileSettingsPage() {
|
||||
type="submit"
|
||||
disabled={isSaving || !hasChanges}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114]',
|
||||
'inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-brand-dark',
|
||||
'shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97]',
|
||||
'disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
)}
|
||||
@@ -145,7 +145,7 @@ export function ProfileSettingsPage() {
|
||||
to="/change-password"
|
||||
className={cn(
|
||||
'inline-flex items-center rounded-[10px] px-4 py-2 text-sm font-medium',
|
||||
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground',
|
||||
'bg-[rgba(255,255,255,0.04)] border border-brand-border text-foreground',
|
||||
'hover:border-[rgba(255,255,255,0.12)]'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -205,7 +205,7 @@ export default function TargetListsPage() {
|
||||
type="text"
|
||||
value={editorName}
|
||||
onChange={e => setEditorName(e.target.value)}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
placeholder="e.g. RDS Farm A"
|
||||
/>
|
||||
</div>
|
||||
@@ -217,7 +217,7 @@ export default function TargetListsPage() {
|
||||
type="text"
|
||||
value={editorDescription}
|
||||
onChange={e => setEditorDescription(e.target.value)}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
placeholder="e.g. Production RDS servers"
|
||||
/>
|
||||
</div>
|
||||
@@ -229,7 +229,7 @@ export default function TargetListsPage() {
|
||||
value={editorTargets}
|
||||
onChange={e => setEditorTargets(e.target.value)}
|
||||
rows={6}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-[0.875rem] text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20"
|
||||
placeholder={"RDS-01 # 192.168.1.10\nRDS-02\nRDS-03 # Backup server"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -78,7 +78,7 @@ export function TeamCategoriesPage() {
|
||||
setForm({ name: cat.name, slug: cat.slug, description: cat.description || '' })
|
||||
}
|
||||
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
@@ -135,7 +135,7 @@ export function AuditLogsPage() {
|
||||
placeholder="Filter by action..."
|
||||
className={cn(
|
||||
'h-9 rounded-md border border-border bg-card px-3 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<input
|
||||
@@ -145,7 +145,7 @@ export function AuditLogsPage() {
|
||||
placeholder="Filter by resource type..."
|
||||
className={cn(
|
||||
'h-9 rounded-md border border-border bg-card px-3 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -145,7 +145,7 @@ export function FeatureFlagsPage() {
|
||||
},
|
||||
]
|
||||
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
|
||||
@@ -87,7 +87,7 @@ export function GlobalCategoriesPage() {
|
||||
},
|
||||
]
|
||||
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
|
||||
@@ -109,7 +109,7 @@ export function InviteCodesPage() {
|
||||
|
||||
const inputClass = cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)
|
||||
|
||||
const columns: Column<InviteCodeResponse>[] = [
|
||||
|
||||
@@ -110,7 +110,7 @@ export function PlanLimitsPage() {
|
||||
|
||||
const inputCn = cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -100,7 +100,7 @@ export function SettingsPage() {
|
||||
placeholder="We're performing scheduled maintenance. Please check back later."
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -74,7 +74,7 @@ export default function SurveyInvitesPage() {
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">Create Invite</h3>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||||
<div className="flex-1">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-1.5 block">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1.5 block">
|
||||
Recipient Name
|
||||
</label>
|
||||
<input
|
||||
@@ -82,11 +82,11 @@ export default function SurveyInvitesPage() {
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
placeholder="John Smith"
|
||||
className="w-full rounded-[10px] border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
|
||||
className="w-full rounded-[10px] border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-1.5 block">
|
||||
<label className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1.5 block">
|
||||
Email (optional)
|
||||
</label>
|
||||
<input
|
||||
@@ -94,14 +94,14 @@ export default function SurveyInvitesPage() {
|
||||
value={email}
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
placeholder="john@example.com"
|
||||
className="w-full rounded-[10px] border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
|
||||
className="w-full rounded-[10px] border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => handleCreate(false)}
|
||||
disabled={creating || !name.trim()}
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-gradient-brand px-4 py-2 text-sm font-semibold text-brand-dark shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
>
|
||||
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Link2 className="h-4 w-4" />}
|
||||
Generate Link
|
||||
@@ -109,7 +109,7 @@ export default function SurveyInvitesPage() {
|
||||
<button
|
||||
onClick={() => handleCreate(true)}
|
||||
disabled={creating || !name.trim() || !email.trim()}
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] active:scale-[0.97] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-brand-border px-4 py-2 text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] active:scale-[0.97] disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||
>
|
||||
{creating ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||
Send Email
|
||||
@@ -132,7 +132,7 @@ export default function SurveyInvitesPage() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleCopy(lastCreated.survey_url)}
|
||||
className="shrink-0 rounded-lg p-2 text-muted-foreground hover:bg-[rgba(255,255,255,0.06)] hover:text-foreground transition-colors"
|
||||
className="shrink-0 rounded-lg p-2 text-muted-foreground hover:bg-brand-border hover:text-foreground transition-colors"
|
||||
>
|
||||
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
||||
</button>
|
||||
@@ -147,13 +147,13 @@ export default function SurveyInvitesPage() {
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Name</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Email</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Status</th>
|
||||
<th className="px-4 py-3 text-center font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Sent</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Created</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Completed</th>
|
||||
<th className="px-4 py-3 text-center font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Link</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Name</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Email</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Status</th>
|
||||
<th className="px-4 py-3 text-center font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Sent</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Created</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Completed</th>
|
||||
<th className="px-4 py-3 text-center font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -188,7 +188,7 @@ export default function SurveyInvitesPage() {
|
||||
<td className="px-4 py-3 text-center">
|
||||
<button
|
||||
onClick={() => handleCopy(invite.survey_url)}
|
||||
className="rounded-lg p-1.5 text-muted-foreground hover:bg-[rgba(255,255,255,0.06)] hover:text-foreground transition-colors"
|
||||
className="rounded-lg p-1.5 text-muted-foreground hover:bg-brand-border hover:text-foreground transition-colors"
|
||||
title="Copy survey link"
|
||||
>
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
|
||||
@@ -104,7 +104,7 @@ function ExpandedDetail({ response }: { response: SurveyResponseDetail }) {
|
||||
border: '1px solid var(--glass-border)',
|
||||
}}
|
||||
>
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-primary mb-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-widest text-primary mb-1">
|
||||
Q{q.num}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mb-2">{q.text}</p>
|
||||
@@ -151,7 +151,7 @@ function ResponseRow({
|
||||
<tr
|
||||
className={cn(
|
||||
'border-b border-border/50 transition-colors cursor-pointer',
|
||||
!response.is_read && 'bg-primary/[0.03]',
|
||||
!response.is_read && 'bg-primary/3',
|
||||
'hover:bg-[rgba(255,255,255,0.02)]'
|
||||
)}
|
||||
>
|
||||
@@ -194,7 +194,7 @@ function ResponseRow({
|
||||
)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wider bg-[rgba(255,255,255,0.06)] text-muted-foreground">
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-label text-[0.625rem] uppercase tracking-wider bg-brand-border text-muted-foreground">
|
||||
<Link2 className="h-3 w-3" />
|
||||
Direct
|
||||
</span>
|
||||
@@ -214,7 +214,7 @@ function ResponseRow({
|
||||
<td className="px-3 py-3 w-10 relative">
|
||||
<button
|
||||
onClick={e => { e.stopPropagation(); setShowMenu(!showMenu) }}
|
||||
className="p-1.5 rounded-lg hover:bg-[rgba(255,255,255,0.06)] text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-1.5 rounded-lg hover:bg-brand-border text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -437,7 +437,7 @@ export default function SurveyResponsesPage() {
|
||||
'inline-flex items-center gap-2 rounded-[10px] px-3 py-2 text-xs font-medium transition-colors border',
|
||||
showArchived
|
||||
? 'bg-primary/10 text-primary border-primary/20'
|
||||
: 'bg-[rgba(255,255,255,0.04)] text-muted-foreground border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)]'
|
||||
: 'bg-[rgba(255,255,255,0.04)] text-muted-foreground border-brand-border hover:border-[rgba(255,255,255,0.12)]'
|
||||
)}
|
||||
>
|
||||
<Archive className="h-3.5 w-3.5" />
|
||||
@@ -446,7 +446,7 @@ export default function SurveyResponsesPage() {
|
||||
<button
|
||||
onClick={handleExport}
|
||||
disabled={exporting || responses.length === 0}
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 text-sm font-medium text-foreground transition-colors hover:border-[rgba(255,255,255,0.12)] disabled:opacity-50"
|
||||
className="inline-flex items-center gap-2 rounded-[10px] bg-[rgba(255,255,255,0.04)] border border-brand-border px-4 py-2 text-sm font-medium text-foreground transition-colors hover:border-[rgba(255,255,255,0.12)] disabled:opacity-50"
|
||||
>
|
||||
{exporting ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
@@ -468,7 +468,7 @@ export default function SurveyResponsesPage() {
|
||||
{/* Stat cards */}
|
||||
<div className="flex gap-4">
|
||||
<div className="glass-card-static px-5 py-4 flex-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
|
||||
Total Responses
|
||||
</p>
|
||||
<p className="text-2xl font-heading font-bold text-gradient-brand">
|
||||
@@ -476,7 +476,7 @@ export default function SurveyResponsesPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="glass-card-static px-5 py-4 flex-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
|
||||
This Week
|
||||
</p>
|
||||
<p className="text-2xl font-heading font-bold text-foreground">
|
||||
@@ -484,7 +484,7 @@ export default function SurveyResponsesPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="glass-card-static px-5 py-4 flex-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-1">
|
||||
<p className="font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
|
||||
Unread
|
||||
</p>
|
||||
<p className={cn(
|
||||
@@ -508,21 +508,21 @@ export default function SurveyResponsesPage() {
|
||||
<div className="flex-1" />
|
||||
<button
|
||||
onClick={() => handleBulkAction('mark_read')}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-brand-border transition-colors"
|
||||
>
|
||||
<Eye className="h-3.5 w-3.5" />
|
||||
Mark Read
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleBulkAction('mark_unread')}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-brand-border transition-colors"
|
||||
>
|
||||
<EyeOff className="h-3.5 w-3.5" />
|
||||
Mark Unread
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleBulkAction('archive')}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-brand-border transition-colors"
|
||||
>
|
||||
<Archive className="h-3.5 w-3.5" />
|
||||
Archive
|
||||
@@ -536,7 +536,7 @@ export default function SurveyResponsesPage() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedIds(new Set())}
|
||||
className="px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||
className="px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-brand-border transition-colors"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
@@ -559,19 +559,19 @@ export default function SurveyResponsesPage() {
|
||||
</th>
|
||||
<th className="px-1 py-3 w-6" />
|
||||
<th className="px-2 py-3 w-8" />
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
#
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
Respondent
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
Source
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
Date
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<th className="px-4 py-3 text-left font-label text-[0.625rem] uppercase tracking-widest text-muted-foreground">
|
||||
Answered
|
||||
</th>
|
||||
<th className="px-3 py-3 w-10" />
|
||||
|
||||
@@ -188,7 +188,7 @@ export function UserDetailPage() {
|
||||
|
||||
const inputClass = cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)
|
||||
|
||||
if (loading) {
|
||||
|
||||
@@ -348,7 +348,7 @@ export function UsersPage() {
|
||||
onChange={(e) => setNewRole(e.target.value)}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">Engineer</option>
|
||||
@@ -394,7 +394,7 @@ export function UsersPage() {
|
||||
placeholder="e.g. ABC-1234"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -435,7 +435,7 @@ export function UsersPage() {
|
||||
placeholder="Full name"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -448,7 +448,7 @@ export function UsersPage() {
|
||||
placeholder="user@example.com"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -459,7 +459,7 @@ export function UsersPage() {
|
||||
onChange={(e) => setCreateForm(f => ({ ...f, account_mode: e.target.value as 'existing' | 'personal' }))}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="personal">Personal (new account)</option>
|
||||
@@ -477,7 +477,7 @@ export function UsersPage() {
|
||||
placeholder="e.g. ABC12345"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -488,7 +488,7 @@ export function UsersPage() {
|
||||
onChange={(e) => setCreateForm(f => ({ ...f, account_role: e.target.value as 'engineer' | 'viewer' }))}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">Engineer</option>
|
||||
@@ -588,7 +588,7 @@ export function UsersPage() {
|
||||
placeholder="user@example.com"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -601,7 +601,7 @@ export function UsersPage() {
|
||||
placeholder="e.g. ABC12345"
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@@ -612,7 +612,7 @@ export function UsersPage() {
|
||||
onChange={(e) => setInviteForm(f => ({ ...f, role: e.target.value as 'engineer' | 'viewer' }))}
|
||||
className={cn(
|
||||
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
|
||||
'focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">Engineer</option>
|
||||
|
||||
Reference in New Issue
Block a user