refactor: replace hardcoded hex values with Tailwind semantic tokens

3,200+ hardcoded color values replaced with CSS variable-backed
Tailwind classes (bg-card, text-foreground, border-border, etc.).
Enables light mode via CSS variable swap. Only syntax highlighting
colors and intentional one-offs remain hardcoded (~15 values).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-22 04:34:35 -04:00
parent 123fc50af9
commit 303a558432
251 changed files with 3310 additions and 3310 deletions

View File

@@ -167,23 +167,23 @@ export function AccountSettingsPage() {
<div>
<div className="mb-8">
<div className="flex items-center gap-3">
<Building2 className="h-8 w-8 text-[#848b9b]" />
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">Account Settings</h1>
<Building2 className="h-8 w-8 text-muted-foreground" />
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">Account Settings</h1>
</div>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
Manage your account, subscription, and team
</p>
</div>
<div className="max-w-3xl space-y-6">
{/* Account Info Section */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<h2 className="text-lg font-semibold text-[#e2e5eb]">Account Information</h2>
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<h2 className="text-lg font-semibold text-foreground">Account Information</h2>
<div className="mt-4 space-y-4">
{/* Account Name */}
<div>
<label className="block text-sm font-medium text-[#e2e5eb]">
<label className="block text-sm font-medium text-foreground">
Account Name
</label>
{isEditingName ? (
@@ -193,8 +193,8 @@ export function AccountSettingsPage() {
value={editedName}
onChange={(e) => setEditedName(e.target.value)}
className={cn(
'flex-1 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'flex-1 rounded-md 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'
)}
autoFocus
@@ -226,11 +226,11 @@ export function AccountSettingsPage() {
</div>
) : (
<div className="mt-1 flex items-center gap-2">
<span className="text-sm text-[#e2e5eb]">{account?.name}</span>
<span className="text-sm text-foreground">{account?.name}</span>
{isAccountOwner && (
<button
onClick={() => setIsEditingName(true)}
className="text-xs text-[#e2e5eb] hover:underline"
className="text-xs text-foreground hover:underline"
>
Edit
</button>
@@ -241,10 +241,10 @@ export function AccountSettingsPage() {
{/* Display Code */}
<div>
<label className="block text-sm font-medium text-[#e2e5eb]">
<label className="block text-sm font-medium text-foreground">
Display Code
</label>
<p className="mt-1 text-sm font-mono text-[#848b9b]">
<p className="mt-1 text-sm font-mono text-muted-foreground">
{account?.display_code}
</p>
</div>
@@ -252,8 +252,8 @@ export function AccountSettingsPage() {
</div>
{/* Subscription Section */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<h2 className="text-lg font-semibold text-[#e2e5eb]">Subscription</h2>
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<h2 className="text-lg font-semibold text-foreground">Subscription</h2>
<div className="mt-4 space-y-4">
{/* Plan & Status */}
@@ -261,9 +261,9 @@ export function AccountSettingsPage() {
<span
className={cn(
'inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-medium',
plan === 'free' && 'bg-accent text-[#848b9b]',
plan === 'pro' && 'bg-accent text-[#e2e5eb]',
plan === 'team' && 'bg-accent text-[#e2e5eb]'
plan === 'free' && 'bg-accent text-muted-foreground',
plan === 'pro' && 'bg-accent text-foreground',
plan === 'team' && 'bg-accent text-foreground'
)}
>
<Crown className="h-3.5 w-3.5" />
@@ -277,7 +277,7 @@ export function AccountSettingsPage() {
sub.status === 'trialing' && 'bg-blue-500/10 text-blue-400',
sub.status === 'past_due' && 'bg-yellow-500/10 text-yellow-400',
sub.status === 'canceled' && 'bg-red-400/10 text-red-400',
sub.status === 'orphaned' && 'bg-accent text-[#848b9b]'
sub.status === 'orphaned' && 'bg-accent text-muted-foreground'
)}
>
{sub.status.charAt(0).toUpperCase() + sub.status.slice(1).replace('_', ' ')}
@@ -286,7 +286,7 @@ export function AccountSettingsPage() {
</div>
{sub?.current_period_end && (
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
Current period ends: {new Date(sub.current_period_end).toLocaleDateString()}
</p>
)}
@@ -329,14 +329,14 @@ export function AccountSettingsPage() {
{/* Team Members Section (owners only) */}
{isAccountOwner && (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<div className="flex items-center gap-2">
<Users className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Team Members</h2>
<Users className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Team Members</h2>
</div>
{members.length === 0 ? (
<p className="mt-4 text-sm text-[#848b9b]">No team members yet.</p>
<p className="mt-4 text-sm text-muted-foreground">No team members yet.</p>
) : (
<div className="mt-4 divide-y divide-border">
{members.map((member) => (
@@ -345,12 +345,12 @@ export function AccountSettingsPage() {
className="flex items-center justify-between py-3 first:pt-0 last:pb-0"
>
<div>
<p className="text-sm font-medium text-[#e2e5eb]">{member.name}</p>
<p className="text-xs text-[#848b9b]">{member.email}</p>
<p className="text-sm font-medium text-foreground">{member.name}</p>
<p className="text-xs text-muted-foreground">{member.email}</p>
</div>
<div className="flex items-center gap-3">
{member.account_role === 'owner' ? (
<span className="rounded-full px-2.5 py-0.5 text-xs font-medium bg-accent text-[#e2e5eb]">
<span className="rounded-full px-2.5 py-0.5 text-xs font-medium bg-accent text-foreground">
owner
</span>
) : (
@@ -366,8 +366,8 @@ export function AccountSettingsPage() {
}
}}
className={cn(
'rounded-md border border-[#1e2130] bg-[#14161d] px-2 py-0.5 text-xs',
'text-[#e2e5eb] focus:border-primary focus:outline-hidden'
'rounded-md border border-border bg-card px-2 py-0.5 text-xs',
'text-foreground focus:border-primary focus:outline-hidden'
)}
>
<option value="engineer">engineer</option>
@@ -382,7 +382,7 @@ export function AccountSettingsPage() {
{member.account_role !== 'owner' && (
<button
onClick={() => handleRemoveMember(member.id)}
className="text-[#848b9b] hover:text-red-400"
className="text-muted-foreground hover:text-red-400"
title="Remove member"
>
<X className="h-4 w-4" />
@@ -398,10 +398,10 @@ export function AccountSettingsPage() {
{/* Invite Member Section (owners only) */}
{isAccountOwner && (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<div className="flex items-center gap-2">
<Mail className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Invite Member</h2>
<Mail className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Invite Member</h2>
</div>
<form onSubmit={handleInvite} className="mt-4 space-y-3">
@@ -413,8 +413,8 @@ export function AccountSettingsPage() {
onChange={(e) => setInviteEmail(e.target.value)}
required
className={cn(
'flex-1 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'flex-1 rounded-md 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'
)}
/>
@@ -422,8 +422,8 @@ export function AccountSettingsPage() {
value={inviteRole}
onChange={(e) => setInviteRole(e.target.value)}
className={cn(
'rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="engineer">Engineer</option>
@@ -449,7 +449,7 @@ export function AccountSettingsPage() {
{/* Pending Invites */}
{invites.length > 0 && (
<div className="mt-6">
<h3 className="text-sm font-medium text-[#e2e5eb]">Pending Invites</h3>
<h3 className="text-sm font-medium text-foreground">Pending Invites</h3>
<div className="mt-2 divide-y divide-border">
{invites
.filter((inv) => !inv.used_at)
@@ -459,21 +459,21 @@ export function AccountSettingsPage() {
className="flex items-center justify-between py-2"
>
<div>
<p className="text-sm text-[#e2e5eb]">{invite.email}</p>
<p className="text-xs text-[#848b9b]">
<p className="text-sm text-foreground">{invite.email}</p>
<p className="text-xs text-muted-foreground">
{invite.expires_at
? `Expires ${new Date(invite.expires_at).toLocaleDateString()}`
: 'No expiration'}
</p>
</div>
<div className="flex items-center gap-2">
<span className="rounded-full bg-accent px-2.5 py-0.5 text-xs text-[#848b9b]">
<span className="rounded-full bg-accent px-2.5 py-0.5 text-xs text-muted-foreground">
{invite.role}
</span>
<button
onClick={() => handleResendInvite(invite.id)}
disabled={resendingId === invite.id}
className="text-[#848b9b] hover:text-[#e2e5eb] disabled:opacity-50"
className="text-muted-foreground hover:text-foreground disabled:opacity-50"
title="Resend invite"
>
{resendingId === invite.id ? (
@@ -494,32 +494,32 @@ export function AccountSettingsPage() {
{/* Profile Settings Link */}
<Link
to="/account/profile"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<UserCog className="h-5 w-5 text-[#848b9b]" />
<UserCog className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Profile Settings</h2>
<p className="text-sm text-[#848b9b]">Update your name, email, and personal details</p>
<h2 className="text-lg font-semibold text-foreground">Profile Settings</h2>
<p className="text-sm text-muted-foreground">Update your name, email, and personal details</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
{/* Team Categories Link (owners only) */}
{isAccountOwner && (
<Link
to="/account/categories"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<FolderTree className="h-5 w-5 text-[#848b9b]" />
<FolderTree className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Team Categories</h2>
<p className="text-sm text-[#848b9b]">Manage tree categories for your team</p>
<h2 className="text-lg font-semibold text-foreground">Team Categories</h2>
<p className="text-sm text-muted-foreground">Manage tree categories for your team</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
)}
@@ -527,16 +527,16 @@ export function AccountSettingsPage() {
{isAccountOwner && (
<Link
to="/account/target-lists"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<Server className="h-5 w-5 text-[#848b9b]" />
<Server className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Target Lists</h2>
<p className="text-sm text-[#848b9b]">Saved server lists for maintenance flow batch launching</p>
<h2 className="text-lg font-semibold text-foreground">Target Lists</h2>
<p className="text-sm text-muted-foreground">Saved server lists for maintenance flow batch launching</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
)}
@@ -544,16 +544,16 @@ export function AccountSettingsPage() {
{isAccountOwner && (
<Link
to="/account/chat-retention"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<Clock className="h-5 w-5 text-[#848b9b]" />
<Clock className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Chat Retention</h2>
<p className="text-sm text-[#848b9b]">Configure AI assistant conversation retention policies</p>
<h2 className="text-lg font-semibold text-foreground">Chat Retention</h2>
<p className="text-sm text-muted-foreground">Configure AI assistant conversation retention policies</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
)}
@@ -561,16 +561,16 @@ export function AccountSettingsPage() {
{isAccountOwner && (
<Link
to="/account/integrations"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<Plug className="h-5 w-5 text-[#848b9b]" />
<Plug className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Integrations</h2>
<p className="text-sm text-[#848b9b]">Connect your PSA to sync session documentation to tickets</p>
<h2 className="text-lg font-semibold text-foreground">Integrations</h2>
<p className="text-sm text-muted-foreground">Connect your PSA to sync session documentation to tickets</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
)}
@@ -578,32 +578,32 @@ export function AccountSettingsPage() {
{isAccountOwner && (
<Link
to="/account/branding"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<Palette className="h-5 w-5 text-[#848b9b]" />
<Palette className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Branding</h2>
<p className="text-sm text-[#848b9b]">Customize logo, accent color, and company name</p>
<h2 className="text-lg font-semibold text-foreground">Branding</h2>
<p className="text-sm text-muted-foreground">Customize logo, accent color, and company name</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
)}
{/* Feedback Link (all users) */}
<Link
to="/feedback"
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-[#1e2130] transition-all"
className="bg-card border border-border rounded-xl p-4 sm:p-6 flex items-center justify-between group hover:border-border transition-all"
>
<div className="flex items-center gap-3">
<MessageSquareText className="h-5 w-5 text-[#848b9b]" />
<MessageSquareText className="h-5 w-5 text-muted-foreground" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Send Feedback</h2>
<p className="text-sm text-[#848b9b]">Report bugs, request features, or share your thoughts</p>
<h2 className="text-lg font-semibold text-foreground">Send Feedback</h2>
<p className="text-sm text-muted-foreground">Report bugs, request features, or share your thoughts</p>
</div>
</div>
<span className="text-[#848b9b] group-hover:text-[#e2e5eb] transition-colors">&rarr;</span>
<span className="text-muted-foreground group-hover:text-foreground transition-colors">&rarr;</span>
</Link>
{/* Branding Section (owners only) */}
@@ -612,20 +612,20 @@ export function AccountSettingsPage() {
)}
{/* Preferences Section */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<div className="flex items-center gap-2">
<Settings className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Preferences</h2>
<Settings className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Preferences</h2>
</div>
<div className="mt-4">
<label
htmlFor="export-format"
className="block text-sm font-medium text-[#e2e5eb]"
className="block text-sm font-medium text-foreground"
>
Default Export Format
</label>
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
This format will be pre-selected when exporting sessions
</p>
<select
@@ -636,8 +636,8 @@ export function AccountSettingsPage() {
toast.success('Preference saved')
}}
className={cn(
'mt-2 block w-full max-w-xs rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-sm text-[#e2e5eb]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
>
@@ -649,15 +649,15 @@ export function AccountSettingsPage() {
</div>
{/* SSO Section (Task 11) */}
{isAccountOwner && (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6">
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
<div className="flex items-center gap-2 mb-3">
<ShieldCheck className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Single Sign-On (SSO)</h2>
<span className="inline-flex items-center rounded-full bg-[rgba(34,211,238,0.10)] px-2.5 py-0.5 text-xs font-sans text-xs font-medium text-[#22d3ee]">
<ShieldCheck className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Single Sign-On (SSO)</h2>
<span className="inline-flex items-center rounded-full bg-accent-dim px-2.5 py-0.5 text-xs font-sans text-xs font-medium text-primary">
Enterprise
</span>
</div>
<p className="text-sm text-[#848b9b] mb-4">
<p className="text-sm text-muted-foreground mb-4">
SAML and OIDC single sign-on is available for enterprise plans. Contact us to enable SSO for
your organization.
</p>
@@ -665,7 +665,7 @@ export function AccountSettingsPage() {
href="mailto:support@resolutionflow.com?subject=SSO%20Setup%20Request"
className={cn(
'inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb]',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground',
'hover:border-[rgba(255,255,255,0.12)] transition-all'
)}
>
@@ -678,7 +678,7 @@ export function AccountSettingsPage() {
<div className="rounded-xl border border-rose-500/20 p-4 sm:p-6">
<div className="flex items-center gap-2 mb-4">
<AlertTriangle className="h-5 w-5 text-rose-500" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Danger Zone</h2>
<h2 className="text-lg font-semibold text-foreground">Danger Zone</h2>
</div>
<div className="space-y-3">
@@ -686,8 +686,8 @@ export function AccountSettingsPage() {
<>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[#e2e5eb]">Transfer Ownership</p>
<p className="text-xs text-[#848b9b]">Make another member the account owner</p>
<p className="text-sm font-medium text-foreground">Transfer Ownership</p>
<p className="text-xs text-muted-foreground">Make another member the account owner</p>
</div>
<Button
variant="secondary"
@@ -698,10 +698,10 @@ export function AccountSettingsPage() {
Transfer
</Button>
</div>
<div className="flex items-center justify-between border-t border-[#1e2130] pt-3">
<div className="flex items-center justify-between border-t border-border pt-3">
<div>
<p className="text-sm font-medium text-[#e2e5eb]">Delete Account</p>
<p className="text-xs text-[#848b9b]">Permanently delete your account and all data</p>
<p className="text-sm font-medium text-foreground">Delete Account</p>
<p className="text-xs text-muted-foreground">Permanently delete your account and all data</p>
</div>
<Button
variant="destructive"
@@ -715,8 +715,8 @@ export function AccountSettingsPage() {
) : (
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-[#e2e5eb]">Leave Account</p>
<p className="text-xs text-[#848b9b]">Leave this account and create a personal one</p>
<p className="text-sm font-medium text-foreground">Leave Account</p>
<p className="text-xs text-muted-foreground">Leave this account and create a personal one</p>
</div>
<Button
variant="destructive"
@@ -769,16 +769,16 @@ function UsageStat({
const isAtLimit = !isUnlimited && current >= max
return (
<div className="rounded-md border border-[#1e2130] bg-[#14161d] p-3">
<p className="text-xs font-medium text-[#848b9b]">{label}</p>
<div className="rounded-md border border-border bg-card p-3">
<p className="text-xs font-medium text-muted-foreground">{label}</p>
<p
className={cn(
'mt-1 text-lg font-semibold',
isAtLimit ? 'text-red-400' : isNearLimit ? 'text-yellow-400' : 'text-[#e2e5eb]'
isAtLimit ? 'text-red-400' : isNearLimit ? 'text-yellow-400' : 'text-foreground'
)}
>
{current}
<span className="text-sm font-normal text-[#848b9b]">
<span className="text-sm font-normal text-muted-foreground">
{' '}/ {isUnlimited ? 'Unlimited' : max}
</span>
</p>

View File

@@ -250,13 +250,13 @@ export default function AssistantChatPage() {
<div className="flex-1 overflow-y-auto px-6 py-4 space-y-4">
{messages.length === 0 && !loading && (
<div className="flex flex-col items-center justify-center h-full text-center">
<div className="w-16 h-16 rounded-full bg-[rgba(34,211,238,0.10)] flex items-center justify-center mb-4">
<Sparkles size={28} className="text-[#22d3ee]" />
<div className="w-16 h-16 rounded-full bg-accent-dim flex items-center justify-center mb-4">
<Sparkles size={28} className="text-primary" />
</div>
<h2 className="text-lg font-heading font-semibold text-[#e2e5eb] mb-2">
<h2 className="text-lg font-heading font-semibold text-foreground mb-2">
AI Assistant
</h2>
<p className="text-sm text-[#848b9b] max-w-md">
<p className="text-sm text-muted-foreground max-w-md">
Ask me anything about IT infrastructure, networking, Active Directory,
cloud platforms, or troubleshooting. I'll also suggest relevant flows from your team's library.
</p>
@@ -273,10 +273,10 @@ export default function AssistantChatPage() {
{loading && (
<div className="flex gap-3">
<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-primary" />
</div>
<div className="bg-[#191c25] border border-[#1e2130] rounded-2xl px-4 py-3">
<Loader2 size={16} className="animate-spin text-[#22d3ee]" />
<div className="bg-input border border-border rounded-2xl px-4 py-3">
<Loader2 size={16} className="animate-spin text-primary" />
</div>
</div>
)}
@@ -294,7 +294,7 @@ export default function AssistantChatPage() {
onKeyDown={handleKeyDown}
placeholder="Ask about IT, networking, troubleshooting..."
rows={3}
className="flex-1 resize-none rounded-xl border bg-[#14161d] text-[#e2e5eb] text-sm placeholder:text-[#848b9b] px-4 py-3 focus:outline-hidden focus:border-primary/30"
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-primary/30"
style={{ borderColor: 'var(--glass-border)' }}
disabled={loading}
/>
@@ -302,7 +302,7 @@ export default function AssistantChatPage() {
<button
onClick={handleSend}
disabled={!input.trim() || loading}
className="bg-[#22d3ee] text-white p-3 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
className="bg-primary text-white p-3 rounded-xl hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-40"
title="Send message"
>
<Send size={18} />
@@ -311,7 +311,7 @@ export default function AssistantChatPage() {
<button
onClick={() => setShowConclude(true)}
disabled={loading}
className="p-3 rounded-xl border text-[#848b9b] hover:text-amber-400 hover:border-amber-400/30 hover:bg-amber-400/10 transition-all disabled:opacity-40"
className="p-3 rounded-xl border text-muted-foreground hover:text-amber-400 hover:border-amber-400/30 hover:bg-amber-400/10 transition-all disabled:opacity-40"
style={{ borderColor: 'var(--glass-border)' }}
title="Conclude session"
>
@@ -320,25 +320,25 @@ export default function AssistantChatPage() {
)}
</div>
</div>
<p className="text-[0.625rem] text-[#848b9b] mt-1.5 px-1">Shift + Enter for a new line</p>
<p className="text-[0.625rem] text-muted-foreground mt-1.5 px-1">Shift + Enter for a new line</p>
</div>
</div>
</>
) : (
<div className="flex flex-col items-center justify-center h-full text-center">
<div className="w-20 h-20 rounded-full bg-[rgba(34,211,238,0.10)] flex items-center justify-center mb-4">
<Sparkles size={32} className="text-[#22d3ee]" />
<div className="w-20 h-20 rounded-full bg-accent-dim flex items-center justify-center mb-4">
<Sparkles size={32} className="text-primary" />
</div>
<h2 className="text-xl font-heading font-semibold text-[#e2e5eb] mb-2">
<h2 className="text-xl font-heading font-semibold text-foreground mb-2">
AI Assistant
</h2>
<p className="text-sm text-[#848b9b] max-w-md mb-6">
<p className="text-sm text-muted-foreground max-w-md mb-6">
Your Senior Systems & Network Engineer. Ask anything about IT infrastructure,
or start a new chat to get personalized help with your team's flows.
</p>
<button
onClick={handleNewChat}
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"
className="bg-primary 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
</button>

View File

@@ -104,7 +104,7 @@ export default function BatchStatusPage() {
<div className="flex items-center justify-between">
<button
onClick={() => treeId && navigate(`/flows/${treeId}/maintenance`)}
className="flex items-center gap-1.5 text-[0.875rem] text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="flex items-center gap-1.5 text-[0.875rem] text-muted-foreground hover:text-foreground transition-colors"
>
<ChevronLeft className="h-4 w-4" />
{tree?.name ?? 'Maintenance Flow'}
@@ -112,7 +112,7 @@ export default function BatchStatusPage() {
<button
onClick={() => loadSessions(true)}
disabled={isRefreshing}
className="flex items-center gap-1.5 rounded-lg border border-[#1e2130] px-3 py-1.5 text-[0.8125rem] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] transition-colors disabled:opacity-50"
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-[0.8125rem] text-muted-foreground hover:bg-accent hover:text-foreground transition-colors disabled:opacity-50"
>
<RefreshCw className={cn('h-3.5 w-3.5', isRefreshing && 'animate-spin')} />
Refresh
@@ -125,9 +125,9 @@ export default function BatchStatusPage() {
<Wrench className="h-5 w-5" />
</div>
<div>
<h1 className="text-xl font-semibold text-[#e2e5eb]">Batch Run</h1>
<h1 className="text-xl font-semibold text-foreground">Batch Run</h1>
{batchDate && (
<p className="text-[0.875rem] text-[#848b9b]">
<p className="text-[0.875rem] text-muted-foreground">
{batchDate.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}
</p>
)}
@@ -136,9 +136,9 @@ export default function BatchStatusPage() {
{/* Progress bar */}
{total > 0 && (
<div className="rounded-xl border border-[#1e2130] bg-[#14161d] p-5 space-y-3">
<div className="rounded-xl border border-border bg-card p-5 space-y-3">
<div className="flex items-center justify-between text-[0.875rem]">
<span className="font-medium text-[#e2e5eb]">
<span className="font-medium text-foreground">
{completed} of {total} complete
</span>
<span className={cn(
@@ -147,7 +147,7 @@ export default function BatchStatusPage() {
? 'text-emerald-400 bg-emerald-500/10'
: inProgress > 0
? 'text-amber-400 bg-amber-500/10'
: 'text-[#848b9b] bg-muted'
: 'text-muted-foreground bg-muted'
)}>
{allDone ? 'Complete' : inProgress > 0 ? `${inProgress} in progress` : 'Not started'}
</span>
@@ -175,7 +175,7 @@ export default function BatchStatusPage() {
<span className="text-[0.8125rem] text-amber-400">{outcomeCounts.workaround} workaround</span>
)}
{outcomeCounts.unresolved && (
<span className="text-[0.8125rem] text-[#848b9b]">{outcomeCounts.unresolved} unresolved</span>
<span className="text-[0.8125rem] text-muted-foreground">{outcomeCounts.unresolved} unresolved</span>
)}
</div>
)}
@@ -189,13 +189,13 @@ export default function BatchStatusPage() {
<p className="text-sm text-red-400 mb-3">{loadError}</p>
<button
onClick={() => loadSessions(true)}
className="rounded-md border border-[#1e2130] px-3 py-1.5 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] transition-colors"
className="rounded-md border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
>
Try again
</button>
</div>
) : sessions.length === 0 ? (
<p className="text-center text-[0.875rem] text-[#848b9b] py-8">
<p className="text-center text-[0.875rem] text-muted-foreground py-8">
No sessions found for this batch.
</p>
) : (

View File

@@ -66,7 +66,7 @@ export function ChangePasswordPage() {
<BrandLogo size="lg" className="h-10 w-10 invert sm:h-12 sm:w-12" />
</div>
</div>
<h1 className="text-3xl font-bold font-heading text-[#e2e5eb] tracking-tight">
<h1 className="text-3xl font-bold font-heading text-foreground tracking-tight">
Change Password
</h1>
{isForced && (
@@ -77,7 +77,7 @@ export function ChangePasswordPage() {
</div>
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
{error && (
<div className="rounded-xl border border-red-400/20 bg-red-400/10 p-3 text-sm text-red-400">
{error}
@@ -85,7 +85,7 @@ export function ChangePasswordPage() {
)}
<div>
<label htmlFor="current-password" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="current-password" className="mb-1 block text-sm font-medium text-foreground">
Current Password
</label>
<PasswordInput
@@ -95,8 +95,8 @@ export function ChangePasswordPage() {
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -104,7 +104,7 @@ export function ChangePasswordPage() {
</div>
<div>
<label htmlFor="new-password" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="new-password" className="mb-1 block text-sm font-medium text-foreground">
New Password
</label>
<PasswordInput
@@ -114,20 +114,20 @@ export function ChangePasswordPage() {
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="At least 10 characters"
/>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Must include uppercase, lowercase, and a digit.
</p>
</div>
<div>
<label htmlFor="confirm-password" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="confirm-password" className="mb-1 block text-sm font-medium text-foreground">
Confirm New Password
</label>
<PasswordInput
@@ -137,8 +137,8 @@ export function ChangePasswordPage() {
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -150,7 +150,7 @@ export function ChangePasswordPage() {
disabled={isLoading}
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-[#22d3ee] text-white hover:brightness-110',
'bg-primary text-white hover:brightness-110',
'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'

View File

@@ -9,8 +9,8 @@ export default function EscalationQueuePage() {
<AlertTriangle size={16} className="text-amber-400" />
</span>
<div>
<h1 className="font-heading text-xl font-bold text-[#e2e5eb]">Escalation Queue</h1>
<p className="text-sm text-[#848b9b]">Sessions from your team waiting for pickup</p>
<h1 className="font-heading text-xl font-bold text-foreground">Escalation Queue</h1>
<p className="text-sm text-muted-foreground">Sessions from your team waiting for pickup</p>
</div>
</div>

View File

@@ -137,34 +137,34 @@ export function FeedbackPage() {
{/* Page header */}
<div className="mb-8">
<div className="flex items-center gap-3">
<MessageSquareText className="h-8 w-8 text-[#848b9b]" />
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">Send Feedback</h1>
<MessageSquareText className="h-8 w-8 text-muted-foreground" />
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">Send Feedback</h1>
</div>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
Help us improve ResolutionFlow. Report bugs, request features, or share your thoughts.
</p>
</div>
<div className="max-w-2xl">
{submitted ? (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-8 text-center">
<div className="bg-card border border-border rounded-xl p-8 text-center">
<CheckCircle2 className="mx-auto h-12 w-12 text-green-500 mb-4" />
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-2">Thank you for your feedback!</h2>
<p className="text-[#848b9b] mb-6">
<h2 className="text-xl font-semibold text-foreground mb-2">Thank you for your feedback!</h2>
<p className="text-muted-foreground mb-6">
We've received your submission and will review it shortly. Check your email for a confirmation.
</p>
<button
onClick={handleNewFeedback}
className="bg-[#22d3ee] text-white font-medium px-6 py-2.5 rounded-lg hover:brightness-110 transition-opacity"
className="bg-primary text-white font-medium px-6 py-2.5 rounded-lg hover:brightness-110 transition-opacity"
>
Send More Feedback
</button>
</div>
) : (
<form onSubmit={handleSubmit} className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 sm:p-6 space-y-5">
<form onSubmit={handleSubmit} className="bg-card border border-border rounded-xl p-4 sm:p-6 space-y-5">
{/* Email */}
<div>
<label htmlFor="feedback-email" className="block text-sm font-medium text-[#e2e5eb] mb-1.5">
<label htmlFor="feedback-email" className="block text-sm font-medium text-foreground mb-1.5">
Email Address
</label>
<input
@@ -174,14 +174,14 @@ export function FeedbackPage() {
onChange={e => setEmail(e.target.value)}
placeholder="your@email.com"
required
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden"
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-[#848b9b]">We'll reply to this address if we need more details.</p>
<p className="mt-1 text-xs text-muted-foreground">We'll reply to this address if we need more details.</p>
</div>
{/* Feedback Type — custom selector with descriptions */}
<div>
<label className="block text-sm font-medium text-[#e2e5eb] mb-1.5">
<label className="block text-sm font-medium text-foreground mb-1.5">
Feedback Type
</label>
<div className="relative">
@@ -196,8 +196,8 @@ export function FeedbackPage() {
onClick={() => setTypeDropdownOpen(!typeDropdownOpen)}
onKeyDown={handleDropdownKeyDown}
className={cn(
"w-full rounded-lg border border-[#1e2130] bg-[#14161d] 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-[#e2e5eb]" : "text-[#848b9b]"
"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"
)}
>
<span>{selectedType?.value ?? 'Select a type...'}</span>
@@ -208,7 +208,7 @@ export function FeedbackPage() {
ref={listboxRef}
id="feedback-type-listbox"
role="listbox"
className="absolute z-10 mt-1 w-full rounded-lg border border-[#1e2130] bg-[#14161d] shadow-lg overflow-hidden"
className="absolute z-10 mt-1 w-full rounded-lg border border-border bg-card shadow-lg overflow-hidden"
>
{FEEDBACK_TYPES.map((type, index) => (
<button
@@ -225,8 +225,8 @@ export function FeedbackPage() {
highlightedIndex === index && feedbackType !== type.value && "bg-accent/50"
)}
>
<div className="text-sm font-medium text-[#e2e5eb]">{type.value}</div>
<div className="text-xs text-[#848b9b] mt-0.5">{type.description}</div>
<div className="text-sm font-medium text-foreground">{type.value}</div>
<div className="text-xs text-muted-foreground mt-0.5">{type.description}</div>
</button>
))}
</div>
@@ -236,7 +236,7 @@ export function FeedbackPage() {
{/* Message */}
<div>
<label htmlFor="feedback-message" className="block text-sm font-medium text-[#e2e5eb] mb-1.5">
<label htmlFor="feedback-message" className="block text-sm font-medium text-foreground mb-1.5">
Your Feedback
</label>
<textarea
@@ -247,9 +247,9 @@ export function FeedbackPage() {
required
minLength={10}
rows={6}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-hidden 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-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
{message.trim().length < 10
? `Minimum 10 characters (${message.trim().length}/10)`
: `${message.trim().length} characters`}
@@ -264,8 +264,8 @@ export function FeedbackPage() {
className={cn(
"flex items-center gap-2 rounded-lg px-6 py-2.5 font-medium text-white transition-opacity",
canSubmit && !isSubmitting
? "bg-[#22d3ee] hover:brightness-110"
: "bg-[#22d3ee] opacity-50 cursor-not-allowed"
? "bg-primary hover:brightness-110"
: "bg-primary opacity-50 cursor-not-allowed"
)}
>
<Send size={16} />

View File

@@ -4,23 +4,23 @@ export default function FlowAssistPage() {
return (
<div className="p-6">
<div className="mb-6">
<h1 className="font-heading text-2xl font-bold tracking-tight text-[#e2e5eb]">
<h1 className="font-heading text-2xl font-bold tracking-tight text-foreground">
<span className="inline-flex items-center gap-2">
<WandSparkles size={24} style={{ color: '#f472b6' }} />
Flow Assist
</span>
</h1>
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
Build flows from natural language describe what you need and Flow Assist will generate the decision tree or procedural steps.
</p>
</div>
<div className="card-flat p-8 text-center">
<WandSparkles size={40} className="mx-auto mb-4 text-[#848b9b]" />
<h2 className="font-heading text-lg font-semibold text-[#e2e5eb] mb-2">
<WandSparkles size={40} className="mx-auto mb-4 text-muted-foreground" />
<h2 className="font-heading text-lg font-semibold text-foreground mb-2">
Coming Soon
</h2>
<p className="text-sm text-[#848b9b] max-w-md mx-auto">
<p className="text-sm text-muted-foreground max-w-md mx-auto">
Flow Assist will be available here as a dedicated conversational flow builder.
In the meantime, use the AI panel in the Flow Editor to generate flows.
</p>

View File

@@ -144,7 +144,7 @@ export default function FlowPilotAnalyticsPage() {
if (loading) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<Loader2 size={24} className="animate-spin text-[#848b9b]" />
<Loader2 size={24} className="animate-spin text-muted-foreground" />
</div>
)
}
@@ -152,7 +152,7 @@ export default function FlowPilotAnalyticsPage() {
if (!dashboard) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-sm text-[#848b9b]">Failed to load analytics</p>
<p className="text-sm text-muted-foreground">Failed to load analytics</p>
</div>
)
}
@@ -165,21 +165,21 @@ export default function FlowPilotAnalyticsPage() {
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center gap-3">
<span title="FlowPilot Analytics">
<BarChart3 size={24} className="text-[#e2e5eb]" />
<BarChart3 size={24} className="text-foreground" />
</span>
<h1 className="text-xl sm:text-2xl font-bold font-heading text-[#e2e5eb]">FlowPilot Analytics</h1>
<h1 className="text-xl sm:text-2xl font-bold font-heading text-foreground">FlowPilot Analytics</h1>
</div>
<div className="flex items-center gap-3">
<Link
to="/analytics"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Team Analytics
</Link>
<select
value={period}
onChange={(e) => setPeriod(e.target.value)}
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:outline-hidden focus:ring-1 focus:ring-primary/20 [&>option]:bg-[#1a1c21] [&>option]:text-[#e2e5eb]"
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 [&>option]:bg-card [&>option]:text-foreground"
>
{PERIOD_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
@@ -189,7 +189,7 @@ export default function FlowPilotAnalyticsPage() {
</div>
{/* Tab bar */}
<div className="flex gap-1 border-b border-[#1e2130] overflow-x-auto">
<div className="flex gap-1 border-b border-border overflow-x-auto">
{TABS.map((tab) => (
<button
key={tab.id}
@@ -197,8 +197,8 @@ export default function FlowPilotAnalyticsPage() {
className={cn(
'px-4 py-2 text-sm transition-colors',
activeTab === tab.id
? 'border-b-2 border-primary text-[#e2e5eb] font-medium'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'border-b-2 border-primary text-foreground font-medium'
: 'text-muted-foreground hover:text-foreground'
)}
>
{tab.label}
@@ -247,7 +247,7 @@ export default function FlowPilotAnalyticsPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* MTTR Trend */}
<div className="card-flat p-3 sm:p-5">
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
MTTR Trend
</h3>
{dashboard.mttr_trend.length > 0 ? (
@@ -284,7 +284,7 @@ export default function FlowPilotAnalyticsPage() {
</AreaChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-[220px] text-sm text-[#848b9b]">
<div className="flex items-center justify-center h-[220px] text-sm text-muted-foreground">
No data for this period
</div>
)}
@@ -292,7 +292,7 @@ export default function FlowPilotAnalyticsPage() {
{/* Domain Breakdown */}
<div className="card-flat p-3 sm:p-5">
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
Sessions by Domain
</h3>
{dashboard.sessions_by_domain.length > 0 ? (
@@ -315,7 +315,7 @@ export default function FlowPilotAnalyticsPage() {
</BarChart>
</ResponsiveContainer>
) : (
<div className="flex items-center justify-center h-[220px] text-sm text-[#848b9b]">
<div className="flex items-center justify-center h-[220px] text-sm text-muted-foreground">
No domain data
</div>
)}
@@ -326,7 +326,7 @@ export default function FlowPilotAnalyticsPage() {
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Confidence Breakdown */}
<div className="card-flat p-3 sm:p-5">
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
Confidence Tiers
</h3>
<div className="space-y-3">
@@ -356,31 +356,31 @@ export default function FlowPilotAnalyticsPage() {
{/* Knowledge Coverage */}
<div className="card-flat p-3 sm:p-5">
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
Knowledge Coverage
</h3>
<div className="grid grid-cols-2 gap-3 mb-4">
<div className="rounded-lg bg-[#14161d]/50 p-3">
<p className="text-xs text-[#848b9b]">Total Flows</p>
<p className="text-lg font-semibold text-[#e2e5eb]">{dashboard.knowledge_coverage.total_flows}</p>
<div className="rounded-lg bg-card/50 p-3">
<p className="text-xs text-muted-foreground">Total Flows</p>
<p className="text-lg font-semibold text-foreground">{dashboard.knowledge_coverage.total_flows}</p>
</div>
<div className="rounded-lg bg-[#14161d]/50 p-3">
<p className="text-xs text-[#848b9b]">AI-Generated</p>
<p className="text-lg font-semibold text-[#67e8f9]">{dashboard.knowledge_coverage.ai_generated_flows}</p>
<div className="rounded-lg bg-card/50 p-3">
<p className="text-xs text-muted-foreground">AI-Generated</p>
<p className="text-lg font-semibold text-accent-text">{dashboard.knowledge_coverage.ai_generated_flows}</p>
</div>
<div className="rounded-lg bg-[#14161d]/50 p-3">
<p className="text-xs text-[#848b9b]">Pending Review</p>
<div className="rounded-lg bg-card/50 p-3">
<p className="text-xs text-muted-foreground">Pending Review</p>
<p className="text-lg font-semibold text-amber-400">{dashboard.knowledge_coverage.total_proposals_pending}</p>
</div>
<div className="rounded-lg bg-[#14161d]/50 p-3">
<p className="text-xs text-[#848b9b]">Approved This Period</p>
<div className="rounded-lg bg-card/50 p-3">
<p className="text-xs text-muted-foreground">Approved This Period</p>
<p className="text-lg font-semibold text-emerald-400">{dashboard.knowledge_coverage.proposals_approved_this_period}</p>
</div>
</div>
{dashboard.knowledge_coverage.total_proposals_pending > 0 && (
<Link
to="/review-queue"
className="flex items-center gap-2 text-xs text-[#22d3ee] hover:underline"
className="flex items-center gap-2 text-xs text-primary hover:underline"
>
<ArrowUpRight size={12} />
Review {dashboard.knowledge_coverage.total_proposals_pending} pending proposals
@@ -394,7 +394,7 @@ export default function FlowPilotAnalyticsPage() {
<div className="space-y-3">
<div className="flex items-center gap-2 px-1">
<AlertTriangle size={14} className="text-amber-400" />
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb]">
<h3 className="font-heading text-sm font-semibold text-foreground">
Knowledge Gaps ({gaps.gaps.length})
</h3>
</div>
@@ -408,8 +408,8 @@ export default function FlowPilotAnalyticsPage() {
<div className="card-flat p-4 flex items-center gap-3">
<CheckCircle2 size={20} className="text-emerald-400 shrink-0" />
<div>
<p className="text-sm text-[#e2e5eb]">No knowledge gaps detected</p>
<p className="text-xs text-[#848b9b]">Your flow coverage looks healthy for this period.</p>
<p className="text-sm text-foreground">No knowledge gaps detected</p>
<p className="text-xs text-muted-foreground">Your flow coverage looks healthy for this period.</p>
</div>
</div>
)}
@@ -422,7 +422,7 @@ export default function FlowPilotAnalyticsPage() {
<ErrorRetry label="coverage data" onRetry={() => { setCoverageError(false); coveragePeriodRef.current = null; setRetryKey((k) => k + 1) }} />
) : coverageLoading ? (
<div className="flex items-center justify-center min-h-[200px]">
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
<Loader2 size={20} className="animate-spin text-muted-foreground" />
</div>
) : coverageData ? (
<CoverageHeatmap data={coverageData} />
@@ -436,7 +436,7 @@ export default function FlowPilotAnalyticsPage() {
<ErrorRetry label="flow quality data" onRetry={() => { setQualityError(false); qualityPeriodRef.current = null; setRetryKey((k) => k + 1) }} />
) : qualityLoading ? (
<div className="flex items-center justify-center min-h-[200px]">
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
<Loader2 size={20} className="animate-spin text-muted-foreground" />
</div>
) : qualityData ? (
<FlowQualityTable data={qualityData} />
@@ -450,7 +450,7 @@ export default function FlowPilotAnalyticsPage() {
<ErrorRetry label="PSA metrics" onRetry={() => { setPsaError(false); psaPeriodRef.current = null; setRetryKey((k) => k + 1) }} />
) : psaLoading ? (
<div className="flex items-center justify-center min-h-[200px]">
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
<Loader2 size={20} className="animate-spin text-muted-foreground" />
</div>
) : psaData ? (
<PsaMetricsPanel data={psaData} />
@@ -484,8 +484,8 @@ function MetricCard({
<Icon size={16} style={{ color: iconColor }} />
</span>
<div>
<p className="font-sans text-xs text-[0.5625rem] uppercase tracking-wider text-[#5a6170]">{label}</p>
<p className="text-lg font-semibold text-[#e2e5eb]">{value}</p>
<p className="font-sans text-xs text-[0.5625rem] uppercase tracking-wider text-text-muted">{label}</p>
<p className="text-lg font-semibold text-foreground">{value}</p>
</div>
</div>
</div>
@@ -509,12 +509,12 @@ function ConfidenceTierRow({
return (
<div className="space-y-1">
<div className="flex items-center justify-between text-xs">
<span className="text-[#e2e5eb] font-medium">{label}</span>
<span className="text-[#848b9b]">
<span className="text-foreground font-medium">{label}</span>
<span className="text-muted-foreground">
{count} sessions · {rate.toFixed(1)}% resolved
</span>
</div>
<div className="h-2 rounded-full bg-[#14161d]/80 overflow-hidden">
<div className="h-2 rounded-full bg-card/80 overflow-hidden">
<div
className="h-full rounded-full transition-all"
style={{ width: `${pct}%`, background: color }}
@@ -526,12 +526,12 @@ function ConfidenceTierRow({
function ErrorRetry({ label, onRetry }: { label: string; onRetry: () => void }) {
return (
<div className="flex flex-col items-center justify-center py-12 text-[#848b9b]">
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
<AlertTriangle size={24} className="mb-2 text-amber-400" />
<p className="text-sm mb-3">Failed to load {label}</p>
<button
onClick={onRetry}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] transition-colors"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors"
>
<RotateCcw size={12} />
Retry
@@ -545,15 +545,15 @@ function GapCard({ gap }: { gap: KnowledgeGap }) {
return (
<div className="card-flat p-4 space-y-2">
<div className="flex items-start justify-between gap-2">
<p className="text-sm font-semibold text-[#e2e5eb]">{gap.title}</p>
<p className="text-sm font-semibold text-foreground">{gap.title}</p>
<span className={`shrink-0 rounded-md border px-1.5 py-0.5 font-sans text-xs text-[0.5625rem] uppercase tracking-wider ${severityStyle}`}>
{gap.severity}
</span>
</div>
<p className="text-xs text-[#848b9b]">{gap.description}</p>
<p className="text-xs text-muted-foreground">{gap.description}</p>
<div className="flex items-start gap-1.5 pt-1">
<Lightbulb size={12} className="text-[#22d3ee] shrink-0 mt-0.5" />
<p className="text-xs text-[#22d3ee]">{gap.suggested_action}</p>
<Lightbulb size={12} className="text-primary shrink-0 mt-0.5" />
<p className="text-xs text-primary">{gap.suggested_action}</p>
</div>
</div>
)

View File

@@ -81,7 +81,7 @@ export default function FlowPilotSessionPage() {
<p className="text-sm text-rose-400">{fp.error}</p>
<button
onClick={() => window.location.reload()}
className="mt-3 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="mt-3 text-xs text-muted-foreground hover:text-foreground transition-colors"
>
Try again
</button>
@@ -94,7 +94,7 @@ export default function FlowPilotSessionPage() {
if (fp.isLoading && !fp.session) {
return (
<div className="flex items-center justify-center min-h-[50vh]">
<Loader2 size={24} className="animate-spin text-[#848b9b]" />
<Loader2 size={24} className="animate-spin text-muted-foreground" />
</div>
)
}
@@ -134,7 +134,7 @@ export default function FlowPilotSessionPage() {
<Sparkles size={14} className="text-amber-400" />
</span>
<div className="flex-1 min-w-0">
<h1 className="font-heading text-sm font-semibold text-[#e2e5eb] truncate">
<h1 className="font-heading text-sm font-semibold text-foreground truncate">
Escalation Pickup {fp.session.problem_summary || 'FlowPilot Session'}
</h1>
</div>
@@ -164,14 +164,14 @@ export default function FlowPilotSessionPage() {
{/* Navigation guard modal */}
{blocker.state === 'blocked' && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl w-full max-w-md p-6 shadow-lg">
<div className="bg-card border border-border rounded-xl w-full max-w-md p-6 shadow-lg">
<div className="flex items-center gap-3 mb-3">
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-amber-500/10">
<AlertTriangle size={18} className="text-amber-400" />
</span>
<h2 className="text-lg font-heading font-semibold text-[#e2e5eb]">Active Session</h2>
<h2 className="text-lg font-heading font-semibold text-foreground">Active Session</h2>
</div>
<p className="mb-4 text-sm text-[#848b9b]">
<p className="mb-4 text-sm text-muted-foreground">
You have an active troubleshooting session. If you leave, your session will be paused and you can resume it later.
</p>
<div className="flex gap-2">
@@ -186,7 +186,7 @@ export default function FlowPilotSessionPage() {
fp.pauseSession()
blocker.proceed()
}}
className="flex-1 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2.5 text-sm font-medium text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] transition-all"
className="flex-1 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2.5 text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] transition-all"
>
Pause & Leave
</button>
@@ -200,15 +200,15 @@ export default function FlowPilotSessionPage() {
className="flex items-center gap-3 border-b px-5 py-3 shrink-0"
style={{ borderColor: 'var(--glass-border)' }}
>
<span className="flex h-7 w-7 items-center justify-center rounded-lg bg-[rgba(34,211,238,0.10)]">
<Sparkles size={14} className="text-[#22d3ee]" />
<span className="flex h-7 w-7 items-center justify-center rounded-lg bg-accent-dim">
<Sparkles size={14} className="text-primary" />
</span>
<div className="flex-1 min-w-0">
<h1 className="font-heading text-sm font-semibold text-[#e2e5eb] truncate">
<h1 className="font-heading text-sm font-semibold text-foreground truncate">
{fp.session.problem_summary || 'FlowPilot Session'}
</h1>
</div>
<span className="font-sans text-xs rounded-md bg-[#14161d] px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-[#848b9b] border border-[#1e2130]">
<span className="font-sans text-xs rounded-md bg-card px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-muted-foreground border border-border">
{fp.session.status}
</span>
</div>

View File

@@ -28,26 +28,26 @@ export function ForgotPasswordPage() {
return (
<>
<PageMeta title="Forgot Password" description="Reset your ResolutionFlow password" />
<div className="flex min-h-screen items-center justify-center bg-[#14161d] px-4">
<div className="flex min-h-screen items-center justify-center bg-card px-4">
<div className="pointer-events-none fixed inset-0 bg-[radial-gradient(circle_at_50%_0%,rgba(100,100,120,0.03),transparent_50%)]" />
<div className="relative w-full max-w-md space-y-8">
<div className="text-center">
<div className="mb-4 flex justify-center sm:mb-6">
<div className="w-16 h-16 rounded-2xl bg-[#22d3ee] flex items-center justify-center sm:w-20 sm:h-20">
<div className="w-16 h-16 rounded-2xl bg-primary flex items-center justify-center sm:w-20 sm:h-20">
<BrandLogo size="lg" className="h-10 w-10 invert sm:h-12 sm:w-12" />
</div>
</div>
<h1 className="text-3xl font-bold font-heading text-[#e2e5eb] tracking-tight">
<h1 className="text-3xl font-bold font-heading text-foreground tracking-tight">
Reset Password
</h1>
<p className="mt-2 text-sm text-[#848b9b]">
<p className="mt-2 text-sm text-muted-foreground">
Enter your email and we'll send you a link to reset your password.
</p>
</div>
{submitted ? (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
<div className="rounded-xl border border-green-400/20 bg-green-400/10 p-4 text-sm text-green-400">
If an account with that email exists, we've sent a password reset link.
Check your inbox and follow the instructions.
@@ -55,7 +55,7 @@ export function ForgotPasswordPage() {
<div className="text-center">
<Link
to="/login"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Back to sign in
</Link>
@@ -63,9 +63,9 @@ export function ForgotPasswordPage() {
</div>
) : (
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
<div>
<label htmlFor="email" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="email" className="mb-1 block text-sm font-medium text-foreground">
Email Address
</label>
<input
@@ -76,8 +76,8 @@ export function ForgotPasswordPage() {
value={email}
onChange={(e) => setEmail(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -90,7 +90,7 @@ export function ForgotPasswordPage() {
disabled={isLoading || !email}
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-[#22d3ee] text-white hover:brightness-110',
'bg-primary text-white hover:brightness-110',
'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'
@@ -102,7 +102,7 @@ export function ForgotPasswordPage() {
<div className="text-center">
<Link
to="/login"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Back to sign in
</Link>

View File

@@ -10,11 +10,11 @@ export default function GuideDetailPage() {
if (!guide) {
return (
<div className="flex flex-col items-center justify-center h-full text-center p-6 overflow-y-auto">
<h2 className="text-lg font-heading font-semibold text-[#e2e5eb] mb-2">Guide Not Found</h2>
<p className="text-sm text-[#848b9b] mb-4">The guide you're looking for doesn't exist.</p>
<h2 className="text-lg font-heading font-semibold text-foreground mb-2">Guide Not Found</h2>
<p className="text-sm text-muted-foreground mb-4">The guide you're looking for doesn't exist.</p>
<Link
to="/guides"
className="bg-[#22d3ee] text-white font-semibold text-sm rounded-lg px-5 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
className="bg-primary text-white font-semibold text-sm rounded-lg px-5 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
>
Back to Guides
</Link>
@@ -27,30 +27,30 @@ export default function GuideDetailPage() {
return (
<div className="overflow-y-auto h-full p-6 max-w-3xl mx-auto">
{/* Breadcrumb */}
<nav className="flex items-center gap-1.5 text-xs text-[#848b9b] mb-6">
<Link to="/guides" className="hover:text-[#22d3ee] transition-colors">
<nav className="flex items-center gap-1.5 text-xs text-muted-foreground mb-6">
<Link to="/guides" className="hover:text-primary transition-colors">
User Guides
</Link>
<ChevronRight size={12} />
<span className="text-[#e2e5eb]">{guide.title}</span>
<span className="text-foreground">{guide.title}</span>
</nav>
{/* Header */}
<div className="card-flat rounded-2xl p-6 mb-6">
<div className="flex items-center gap-3 mb-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-[rgba(34,211,238,0.10)]">
<Icon size={20} className="text-[#22d3ee]" />
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-accent-dim">
<Icon size={20} className="text-primary" />
</div>
<div>
<h1 className="text-xl font-heading font-bold text-[#e2e5eb]">{guide.title}</h1>
<p className="text-sm text-[#848b9b]">{guide.summary}</p>
<h1 className="text-xl font-heading font-bold text-foreground">{guide.title}</h1>
<p className="text-sm text-muted-foreground">{guide.summary}</p>
</div>
</div>
<div className="flex items-center gap-4 mt-4 pt-4 border-t" style={{ borderColor: 'var(--glass-border)' }}>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.length} {guide.sections.length === 1 ? 'section' : 'sections'}
</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
{guide.sections.reduce((acc, s) => acc + s.steps.length, 0)} steps
</span>
</div>
@@ -67,7 +67,7 @@ export default function GuideDetailPage() {
<div className="mt-6">
<Link
to="/guides"
className="inline-flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#22d3ee] transition-colors"
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-primary transition-colors"
>
<ArrowLeft size={14} />
Back to all guides

View File

@@ -11,12 +11,12 @@ export default function GuidesHubPage() {
{/* Header */}
<div className="mb-8">
<div className="flex items-center gap-3 mb-2">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-[rgba(34,211,238,0.10)]">
<BookOpen size={20} className="text-[#22d3ee]" />
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-accent-dim">
<BookOpen size={20} className="text-primary" />
</div>
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">User Guides</h1>
<h1 className="text-2xl font-heading font-bold text-foreground">User Guides</h1>
</div>
<p className="text-sm text-[#848b9b] ml-[52px]">
<p className="text-sm text-muted-foreground ml-[52px]">
Learn how to use ResolutionFlow with step-by-step instructions for every feature.
</p>
</div>

View File

@@ -180,8 +180,8 @@ export default function KBAcceleratorPage() {
<div className="flex flex-col h-full min-h-0 p-6">
{/* Page title */}
<div className="shrink-0 flex items-center gap-3 mb-6">
<Sparkles size={24} className="text-[#22d3ee]" />
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">KB Accelerator</h1>
<Sparkles size={24} className="text-primary" />
<h1 className="text-2xl font-heading font-bold text-foreground">KB Accelerator</h1>
</div>
{/* Phases */}
@@ -196,12 +196,12 @@ export default function KBAcceleratorPage() {
{phase === 'processing' && (
<div className="flex-1 flex flex-col items-center justify-center gap-4">
<Loader2 size={40} className="text-[#22d3ee] animate-spin" />
<Loader2 size={40} className="text-primary animate-spin" />
<div className="text-center">
<p className="text-lg font-heading font-semibold text-[#e2e5eb]">
<p className="text-lg font-heading font-semibold text-foreground">
Converting your KB article...
</p>
<p className="text-sm text-[#848b9b] mt-1">
<p className="text-sm text-muted-foreground mt-1">
AI is analyzing your content and generating an interactive flow.
</p>
</div>

View File

@@ -43,7 +43,7 @@ export function LoginPage() {
return (
<>
<PageMeta title="Sign In" description="Sign in to your ResolutionFlow account" />
<div className="flex min-h-screen items-center justify-center bg-[#0c0d10] px-4">
<div className="flex min-h-screen items-center justify-center bg-background px-4">
{/* Atmosphere orbs */}
<div
className="pointer-events-none fixed z-0"
@@ -75,13 +75,13 @@ export function LoginPage() {
<div className="mb-4 flex justify-center sm:mb-6">
<BrandLogo size="lg" />
</div>
<h1 className="text-3xl font-bold font-heading text-[#e2e5eb] tracking-tight">
<span>Resolution</span><span className="text-[#67e8f9]">Flow</span>
<h1 className="text-3xl font-bold font-heading text-foreground tracking-tight">
<span>Resolution</span><span className="text-accent-text">Flow</span>
</h1>
<p className="mt-2 text-base font-medium text-[#848b9b] sm:mt-3 sm:text-lg">
<p className="mt-2 text-base font-medium text-muted-foreground sm:mt-3 sm:text-lg">
AI-Powered Troubleshooting for MSPs
</p>
<p className="mt-1 text-sm text-[#848b9b]/70 sm:mt-2">
<p className="mt-1 text-sm text-muted-foreground/70 sm:mt-2">
Sign in to your account
</p>
</div>
@@ -95,7 +95,7 @@ export function LoginPage() {
)}
<div>
<label htmlFor="email" className="mb-1.5 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="email" className="mb-1.5 block text-sm font-medium text-foreground">
Email address
</label>
<input
@@ -107,8 +107,8 @@ export function LoginPage() {
value={email}
onChange={(e) => setEmail(e.target.value)}
className={cn(
'block w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2.5',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'block w-full rounded-lg border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary/30 focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -117,7 +117,7 @@ export function LoginPage() {
</div>
<div>
<label htmlFor="password" className="mb-1.5 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="password" className="mb-1.5 block text-sm font-medium text-foreground">
Password
</label>
<PasswordInput
@@ -128,8 +128,8 @@ export function LoginPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
className={cn(
'block w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2.5',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'block w-full rounded-lg border border-border bg-card px-3 py-2.5',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary/30 focus:outline-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -138,7 +138,7 @@ export function LoginPage() {
</div>
<div className="text-right">
<Link to="/forgot-password" className="text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors">
<Link to="/forgot-password" className="text-xs text-muted-foreground hover:text-foreground transition-colors">
Forgot password?
</Link>
</div>
@@ -149,7 +149,7 @@ export function LoginPage() {
data-testid="login-submit"
className={cn(
'w-full rounded-lg px-4 py-2.5 text-sm font-semibold',
'bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98]',
'bg-primary 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',
'disabled:cursor-not-allowed disabled:opacity-50',
'transition-all'
@@ -159,9 +159,9 @@ export function LoginPage() {
</button>
</div>
<p className="text-center text-sm text-[#848b9b]">
<p className="text-center text-sm text-muted-foreground">
Don't have an account?{' '}
<Link to="/register" className="font-medium text-[#e2e5eb] hover:underline">
<Link to="/register" className="font-medium text-foreground hover:underline">
Register
</Link>
</p>

View File

@@ -101,7 +101,7 @@ export default function MaintenanceFlowDetailPage() {
action={(
<button
onClick={() => navigate('/trees?type=maintenance')}
className="rounded-md border border-[#1e2130] px-4 py-2 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md border border-border px-4 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
Back to Maintenance Flows
</button>
@@ -148,7 +148,7 @@ export default function MaintenanceFlowDetailPage() {
<div className="flex gap-2">
<button
onClick={() => navigate(`/flows/${id}/edit`)}
className="flex items-center gap-1.5 rounded-lg border border-[#1e2130] px-3 py-2 text-[0.875rem] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-[0.875rem] text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Settings className="h-3.5 w-3.5" />
Edit Flow
@@ -156,7 +156,7 @@ export default function MaintenanceFlowDetailPage() {
<button
onClick={handleRun}
disabled={isRunning}
className="flex items-center gap-1.5 rounded-lg bg-[#22d3ee] px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110 disabled:opacity-70"
className="flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110 disabled:opacity-70"
>
{isRunning
? <Spinner size="sm" className="h-3.5 w-3.5 border-white border-t-transparent" />
@@ -165,7 +165,7 @@ export default function MaintenanceFlowDetailPage() {
</button>
<button
onClick={() => setShowBatchModal(true)}
className="flex items-center gap-1.5 rounded-lg border border-[#1e2130] px-3 py-2 text-[0.875rem] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-[0.875rem] text-muted-foreground hover:bg-accent hover:text-foreground"
>
Batch Launch
</button>
@@ -174,10 +174,10 @@ export default function MaintenanceFlowDetailPage() {
/>
{/* Schedule Panel */}
<div className="rounded-xl border border-[#1e2130] bg-[#14161d] p-5">
<div className="rounded-xl border border-border bg-card p-5">
<div className="flex items-center gap-2 mb-3">
<Calendar className="h-4 w-4 text-[#848b9b]" />
<h2 className="font-semibold text-[#e2e5eb]">Schedule</h2>
<Calendar className="h-4 w-4 text-muted-foreground" />
<h2 className="font-semibold text-foreground">Schedule</h2>
</div>
{schedule ? (
<div className="space-y-2 text-[0.875rem]">
@@ -186,39 +186,39 @@ export default function MaintenanceFlowDetailPage() {
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wide",
schedule.is_active
? "bg-emerald-500/10 text-emerald-400"
: "bg-muted text-[#848b9b]"
: "bg-muted text-muted-foreground"
)}>
{schedule.is_active
? <CheckCircle className="h-3 w-3" />
: <AlertCircle className="h-3 w-3" />}
{schedule.is_active ? 'Active' : 'Paused'}
</span>
<code className="rounded bg-accent px-1.5 py-0.5 text-[0.8125rem] text-[#e2e5eb]">
<code className="rounded bg-accent px-1.5 py-0.5 text-[0.8125rem] text-foreground">
{schedule.cron_expression}
</code>
<span className="text-[#848b9b]">({schedule.timezone})</span>
<span className="text-muted-foreground">({schedule.timezone})</span>
</div>
{schedule.next_run_at && (
<p className="text-[#848b9b]">
<p className="text-muted-foreground">
Next run: {new Date(schedule.next_run_at).toLocaleString()}
</p>
)}
</div>
) : (
<p className="text-[0.875rem] text-[#848b9b]">
<p className="text-[0.875rem] text-muted-foreground">
No schedule configured. Sessions can still be launched manually via Batch Launch.
</p>
)}
</div>
{/* Run History */}
<div className="rounded-xl border border-[#1e2130] bg-[#14161d] p-5">
<div className="rounded-xl border border-border bg-card p-5">
<div className="flex items-center gap-2 mb-4">
<Clock className="h-4 w-4 text-[#848b9b]" />
<h2 className="font-semibold text-[#e2e5eb]">Run History</h2>
<Clock className="h-4 w-4 text-muted-foreground" />
<h2 className="font-semibold text-foreground">Run History</h2>
</div>
{batches.length === 0 ? (
<p className="text-[0.875rem] text-[#848b9b]">No runs yet. Launch a batch to get started.</p>
<p className="text-[0.875rem] text-muted-foreground">No runs yet. Launch a batch to get started.</p>
) : (
<div className="space-y-2">
{batches.map(([batchKey, batchSessions]) => {
@@ -253,25 +253,25 @@ export default function MaintenanceFlowDetailPage() {
<button
key={batchKey}
onClick={handleRowClick}
className="w-full flex items-center justify-between rounded-lg border border-[#1e2130] px-4 py-3 hover:bg-accent transition-colors text-left"
className="w-full flex items-center justify-between rounded-lg border border-border px-4 py-3 hover:bg-accent transition-colors text-left"
>
<div>
<div className="flex items-center gap-2">
{isActive && (
<span className="inline-block h-2 w-2 rounded-full bg-amber-400 animate-pulse" />
)}
<p className="text-[0.875rem] font-medium text-[#e2e5eb]">
<p className="text-[0.875rem] font-medium text-foreground">
{isSingleRun ? 'Manual run' : `${total} target${total !== 1 ? 's' : ''}`}
</p>
</div>
<div className="flex items-center gap-2 mt-0.5">
{date && (
<p className="text-[0.8125rem] text-[#848b9b]">
<p className="text-[0.8125rem] text-muted-foreground">
{new Date(date).toLocaleDateString()}
</p>
)}
{outcomeParts.length > 0 && (
<p className="text-[0.8125rem] text-[#848b9b]">
<p className="text-[0.8125rem] text-muted-foreground">
· {outcomeParts.join(' · ')}
</p>
)}
@@ -290,13 +290,13 @@ export default function MaintenanceFlowDetailPage() {
/>
))}
{extraDots > 0 && (
<span className="ml-1 text-[0.6875rem] text-[#848b9b]">+{extraDots}</span>
<span className="ml-1 text-[0.6875rem] text-muted-foreground">+{extraDots}</span>
)}
</div>
)}
<span className={cn(
"font-sans text-xs text-[0.75rem] uppercase tracking-wide",
isActive ? "text-amber-400" : completed === total ? "text-emerald-400" : "text-[#848b9b]"
isActive ? "text-amber-400" : completed === total ? "text-emerald-400" : "text-muted-foreground"
)}>
{isActive ? 'In Progress' : `${completed}/${total}`}
</span>

View File

@@ -76,7 +76,7 @@ export default function MyAnalyticsPage() {
action={
<Link
to="/trees"
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Run Your First Session
</Link>
@@ -96,16 +96,16 @@ export default function MyAnalyticsPage() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<span title="My Analytics">
<BarChart3 size={24} className="text-[#e2e5eb]" />
<BarChart3 size={24} className="text-foreground" />
</span>
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb]">My Analytics</h1>
<h1 className="text-2xl font-bold font-heading text-foreground">My Analytics</h1>
</div>
<div className="flex items-center gap-3">
{(isAccountOwner || isSuperAdmin) && (
<Link
to="/analytics"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Team Analytics
</Link>
@@ -113,7 +113,7 @@ export default function MyAnalyticsPage() {
<select
value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:outline-hidden 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}>
@@ -149,8 +149,8 @@ export default function MyAnalyticsPage() {
</div>
{/* Area Chart — Sessions over Time */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
My Sessions Over Time
</h2>
<ResponsiveContainer width="100%" height={300}>
@@ -236,7 +236,7 @@ export default function MyAnalyticsPage() {
className="h-2.5 w-2.5 rounded-full"
style={{ backgroundColor: color }}
/>
<span className="text-xs text-[#848b9b] capitalize">
<span className="text-xs text-muted-foreground capitalize">
{key}
</span>
</div>
@@ -247,27 +247,27 @@ export default function MyAnalyticsPage() {
{/* Two-Column: Top Flows & Outcome Distribution */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* My Top Flows */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
My Top Flows
</h2>
{top_flows.length === 0 ? (
<p className="text-sm text-[#848b9b]">No flow data for this period.</p>
<p className="text-sm text-muted-foreground">No flow data for this period.</p>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1e2130]">
<th className="text-left py-2 text-[#e2e5eb] font-medium">
<tr className="border-b border-border">
<th className="text-left py-2 text-foreground font-medium">
Name
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Sessions
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Completion
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Median
</th>
</tr>
@@ -276,18 +276,18 @@ export default function MyAnalyticsPage() {
{top_flows.map((flow) => (
<tr
key={flow.tree_id}
className="border-b border-[#1e2130] last:border-0"
className="border-b border-border last:border-0"
>
<td className="py-2 text-[#848b9b] truncate max-w-[200px]">
<td className="py-2 text-muted-foreground truncate max-w-[200px]">
{flow.name}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{flow.sessions}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{(flow.completion_rate * 100).toFixed(1)}%
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{flow.median_duration_minutes} min
</td>
</tr>
@@ -299,12 +299,12 @@ export default function MyAnalyticsPage() {
</div>
{/* Outcome Distribution */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
Outcome Distribution
</h2>
{summary.total_sessions === 0 ? (
<p className="text-sm text-[#848b9b]">No session data for this period.</p>
<p className="text-sm text-muted-foreground">No session data for this period.</p>
) : (
<div className="space-y-3">
{(
@@ -327,11 +327,11 @@ export default function MyAnalyticsPage() {
OUTCOME_COLORS[outcome] ?? '#94a3b8',
}}
/>
<span className="text-sm text-[#848b9b] capitalize">
<span className="text-sm text-muted-foreground capitalize">
{outcome}
</span>
</div>
<span className="text-sm text-[#e2e5eb] font-medium">
<span className="text-sm text-foreground font-medium">
{count} ({pct.toFixed(1)}%)
</span>
</div>
@@ -366,12 +366,12 @@ function StatCard({
value: string
}) {
return (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<div className="bg-card border border-border rounded-xl p-6">
<div className="flex items-center gap-2 mb-2">
<Icon size={16} className="text-[#848b9b]" />
<span className="text-sm text-[#848b9b]">{label}</span>
<Icon size={16} className="text-muted-foreground" />
<span className="text-sm text-muted-foreground">{label}</span>
</div>
<p className="text-3xl font-bold text-[#e2e5eb]">{value}</p>
<p className="text-3xl font-bold text-foreground">{value}</p>
</div>
)
}

View File

@@ -110,7 +110,7 @@ export default function MySharesPage() {
if (error) {
return (
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
<div className="bg-[#14161d] border border-red-400/20 rounded-xl p-6">
<div className="bg-card border border-red-400/20 rounded-xl p-6">
<div className="text-center">
<p className="text-red-400 text-sm mb-4">{error}</p>
<Button onClick={fetchShares}>
@@ -129,7 +129,7 @@ export default function MySharesPage() {
{/* Back link */}
<Link
to="/sessions"
className="inline-flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors mb-6"
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors mb-6"
>
<ArrowLeft className="h-4 w-4" />
Back to sessions
@@ -137,8 +137,8 @@ export default function MySharesPage() {
{/* Header */}
<div className="mb-6">
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">My Shared Sessions</h1>
<p className="text-[#848b9b] mt-1">Manage your session share links</p>
<h1 className="text-2xl font-heading font-bold text-foreground">My Shared Sessions</h1>
<p className="text-muted-foreground mt-1">Manage your session share links</p>
</div>
{/* Empty state */}
@@ -161,10 +161,10 @@ export default function MySharesPage() {
const isCopied = copiedId === share.id
return (
<div key={share.id} className="bg-[#14161d] border border-[#1e2130] rounded-xl p-5">
<div key={share.id} className="bg-card border border-border rounded-xl p-5">
{/* Top row: badge + name */}
<div className="flex items-center gap-3 mb-3">
<span className="inline-flex items-center gap-1.5 text-xs rounded-full px-2 py-0.5 bg-accent text-[#848b9b]">
<span className="inline-flex items-center gap-1.5 text-xs rounded-full px-2 py-0.5 bg-accent text-muted-foreground">
{share.visibility === 'public' ? (
<Globe className="h-3 w-3" />
) : (
@@ -172,18 +172,18 @@ export default function MySharesPage() {
)}
{share.visibility === 'public' ? 'Public' : 'Account Only'}
</span>
<span className="text-sm font-medium text-[#e2e5eb]">
<span className="text-sm font-medium text-foreground">
{share.share_name || 'Untitled share'}
</span>
</div>
{/* Session info */}
<p className="text-sm text-[#848b9b] mb-2">
<p className="text-sm text-muted-foreground mb-2">
Session ID: {share.session_id.slice(0, 8)}...
</p>
{/* Meta row */}
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-[#848b9b] mb-4">
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground mb-4">
<span>Created {formatRelativeTime(share.created_at)}</span>
<span className="hidden sm:inline">·</span>
<span>
@@ -214,7 +214,7 @@ export default function MySharesPage() {
<Link
to={`/sessions/${share.session_id}`}
className="inline-flex items-center gap-1.5 border border-[#1e2130] text-[#848b9b] hover:bg-accent rounded-md px-3 py-1.5 text-sm transition-colors"
className="inline-flex items-center gap-1.5 border border-border text-muted-foreground hover:bg-accent rounded-md px-3 py-1.5 text-sm transition-colors"
>
<ExternalLink className="h-3.5 w-3.5" />
View Session

View File

@@ -123,8 +123,8 @@ export function MyTreesPage() {
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
<div className="mb-6 flex items-center justify-between sm:mb-8">
<div>
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">My Flows</h1>
<p className="mt-2 text-[#848b9b]">
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">My Flows</h1>
<p className="mt-2 text-muted-foreground">
Your forked and custom flows
</p>
</div>
@@ -140,38 +140,38 @@ 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-[#1e2130] bg-[#14161d] p-1 shadow-xl">
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl">
<Link
to="/trees/new"
onClick={() => setShowCreateMenu(false)}
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-[#e2e5eb] hover:bg-accent"
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-foreground hover:bg-accent"
>
<FolderTree className="h-4 w-4 text-[#848b9b]" />
<FolderTree className="h-4 w-4 text-muted-foreground" />
<div>
<div className="font-medium">Troubleshooting Tree</div>
<div className="text-xs text-[#848b9b]">Branching decision flow</div>
<div className="text-xs text-muted-foreground">Branching decision flow</div>
</div>
</Link>
<Link
to="/flows/new"
onClick={() => setShowCreateMenu(false)}
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-[#e2e5eb] hover:bg-accent"
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-foreground hover:bg-accent"
>
<ListOrdered className="h-4 w-4 text-[#848b9b]" />
<ListOrdered className="h-4 w-4 text-muted-foreground" />
<div>
<div className="font-medium">Procedural Flow</div>
<div className="text-xs text-[#848b9b]">Step-by-step procedure</div>
<div className="text-xs text-muted-foreground">Step-by-step procedure</div>
</div>
</Link>
<Link
to="/flows/new?type=maintenance"
onClick={() => setShowCreateMenu(false)}
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-[#e2e5eb] hover:bg-accent"
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-foreground hover:bg-accent"
>
<Wrench className="h-4 w-4 text-amber-400" />
<div>
<div className="font-medium">Maintenance Flow</div>
<div className="text-xs text-[#848b9b]">Scheduled multi-target tasks</div>
<div className="text-xs text-muted-foreground">Scheduled multi-target tasks</div>
</div>
</Link>
</div>
@@ -187,17 +187,17 @@ export function MyTreesPage() {
<Spinner />
</div>
) : trees.length === 0 ? (
<div className="rounded-lg border border-dashed border-[#1e2130] bg-accent px-4 py-12 text-center">
<FolderTree className="mx-auto mb-4 h-12 w-12 text-[#848b9b]" />
<h2 className="mb-2 text-lg font-semibold text-[#e2e5eb]">No personal flows yet</h2>
<p className="mb-4 text-sm text-[#848b9b]">
<div className="rounded-lg border border-dashed border-border bg-accent px-4 py-12 text-center">
<FolderTree className="mx-auto mb-4 h-12 w-12 text-muted-foreground" />
<h2 className="mb-2 text-lg font-semibold text-foreground">No personal flows yet</h2>
<p className="mb-4 text-sm text-muted-foreground">
Fork a flow from the library to customize it for your workflow
</p>
<div className="flex items-center justify-center gap-3">
<Link
to="/trees"
className={cn(
'inline-flex items-center gap-2 rounded-md bg-[#22d3ee] text-white px-4 py-2 text-sm font-medium',
'inline-flex items-center gap-2 rounded-md bg-primary text-white px-4 py-2 text-sm font-medium',
'hover:brightness-110'
)}
>
@@ -207,8 +207,8 @@ export function MyTreesPage() {
<Link
to="/trees/new"
className={cn(
'inline-flex items-center gap-2 rounded-md border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]'
'inline-flex items-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
>
<Plus className="h-4 w-4" />
@@ -222,18 +222,18 @@ export function MyTreesPage() {
{trees.map((tree) => (
<div
key={tree.id}
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 transition-all hover:border-[#1e2130]/80 sm:p-6"
className="bg-card border border-border rounded-xl p-4 transition-all hover:border-border/80 sm:p-6"
>
{/* Header */}
<div className="mb-3 flex items-start justify-between gap-2">
<div className="flex items-center gap-2">
{tree.tree_type === 'procedural' && (
<ListOrdered className="h-4 w-4 shrink-0 text-[#848b9b]" />
<ListOrdered className="h-4 w-4 shrink-0 text-muted-foreground" />
)}
{tree.tree_type === 'maintenance' && (
<Wrench className="h-4 w-4 shrink-0 text-amber-400" />
)}
<h3 className="font-semibold text-[#e2e5eb]">{tree.name}</h3>
<h3 className="font-semibold text-foreground">{tree.name}</h3>
{tree.parent_tree_id && (
<span className="shrink-0 rounded-full bg-violet-400/15 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-violet-400">
Fork
@@ -252,7 +252,7 @@ export function MyTreesPage() {
</span>
)}
{tree.category_info && (
<span className="rounded-full bg-accent px-2 py-0.5 text-xs text-[#848b9b]">
<span className="rounded-full bg-accent px-2 py-0.5 text-xs text-muted-foreground">
{tree.category_info.name}
</span>
)}
@@ -260,19 +260,19 @@ export function MyTreesPage() {
</div>
{/* Description */}
<p className="mb-3 text-sm text-[#848b9b] line-clamp-2">
<p className="mb-3 text-sm text-muted-foreground line-clamp-2">
{tree.description || 'No description available'}
</p>
{/* Fork Badge */}
{tree.parent_tree_id && (
<div className="mb-3 flex items-center gap-2 rounded-md bg-accent px-2 py-1.5 text-sm">
<GitBranch className="h-4 w-4 text-[#848b9b]" />
<span className="text-[#848b9b]">
<GitBranch className="h-4 w-4 text-muted-foreground" />
<span className="text-muted-foreground">
Forked from{' '}
<Link
to={`/trees/${tree.parent_tree_id}/navigate`}
className="font-medium text-[#e2e5eb] hover:underline"
className="font-medium text-foreground hover:underline"
>
original
</Link>
@@ -288,7 +288,7 @@ export function MyTreesPage() {
)}
{/* Stats */}
<div className="mb-4 flex items-center gap-4 text-xs text-[#848b9b]">
<div className="mb-4 flex items-center gap-4 text-xs text-muted-foreground">
<div className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5" />
<span>{formatDate(tree.lastUsed)}</span>
@@ -312,8 +312,8 @@ export function MyTreesPage() {
<Link
to={getEditPath(tree)}
className={cn(
'rounded-md border border-[#1e2130] p-2 text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]'
'rounded-md border border-border p-2 text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
title="Edit tree"
>

View File

@@ -5,36 +5,36 @@ export default function PrivacyPage() {
return (
<>
<PageMeta title="Privacy Policy" description="ResolutionFlow Privacy Policy" />
<div className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div className="min-h-screen bg-background text-foreground">
<div className="mx-auto max-w-3xl px-6 py-16">
<Link to="/landing" className="text-sm text-[#848b9b] hover:text-[#e2e5eb] mb-8 inline-block">&larr; Back to home</Link>
<Link to="/landing" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">&larr; Back to home</Link>
<h1 className="text-3xl font-bold font-heading mb-8">Privacy Policy</h1>
<p className="text-[#848b9b] mb-6">Last updated: March 21, 2026</p>
<p className="text-muted-foreground mb-6">Last updated: March 21, 2026</p>
<div className="space-y-6 text-[#848b9b] leading-relaxed">
<div className="space-y-6 text-muted-foreground leading-relaxed">
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">1. Information We Collect</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">1. Information We Collect</h2>
<p>When you use ResolutionFlow, we collect information you provide directly: your name, email address, and account credentials. We also collect usage data including session logs, flow configurations, and troubleshooting documentation generated through the platform.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">2. How We Use Your Information</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">2. How We Use Your Information</h2>
<p>We use your information to provide and improve ResolutionFlow, including: operating your account, generating documentation from troubleshooting sessions, providing AI-powered guidance through FlowPilot, and communicating with you about the service.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">3. Data Storage & Security</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">3. Data Storage & Security</h2>
<p>Your data is stored securely using industry-standard encryption. PSA credentials (ConnectWise, Atera, Syncro) are encrypted at rest. We do not sell your data to third parties.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">4. AI & Data Processing</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">4. AI & Data Processing</h2>
<p>FlowPilot uses AI models to provide troubleshooting guidance. Session data sent to AI providers is used only for generating responses within your session and is not used to train AI models.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">5. Contact</h2>
<p>Questions about this policy? Email us at <a href="mailto:hello@resolutionflow.com" className="text-[#22d3ee] hover:underline">hello@resolutionflow.com</a>.</p>
<h2 className="text-xl font-semibold text-foreground mb-3">5. Contact</h2>
<p>Questions about this policy? Email us at <a href="mailto:hello@resolutionflow.com" className="text-primary hover:underline">hello@resolutionflow.com</a>.</p>
</section>
</div>
</div>

View File

@@ -265,37 +265,37 @@ export function ProceduralEditorPage() {
{/* Main content column */}
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
{/* Toolbar — sticky */}
<div className="flex shrink-0 items-center justify-between border-b border-[#1e2130] bg-[#14161d] px-4 py-2">
<div className="flex shrink-0 items-center justify-between border-b border-border bg-card px-4 py-2">
<div className="flex items-center gap-3">
<button
onClick={() => navigate('/trees')}
className="rounded-md p-2 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md p-2 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<ArrowLeft className="h-5 w-5" />
</button>
<div className="flex items-center gap-2">
{isMaintenance
? <Wrench className="h-5 w-5 text-amber-400" />
: <ListOrdered className="h-5 w-5 text-[#848b9b]" />}
<h1 className="text-lg font-bold text-[#e2e5eb]">
: <ListOrdered className="h-5 w-5 text-muted-foreground" />}
<h1 className="text-lg font-bold text-foreground">
{isEditMode ? `Edit ${flowLabel}` : `New ${flowLabel}`}
{name && <span className="ml-2 font-normal text-[#848b9b]"> {name}</span>}
{name && <span className="ml-2 font-normal text-muted-foreground"> {name}</span>}
</h1>
</div>
</div>
<div className="flex items-center gap-2">
{isDirty && (
<span className="text-xs text-[#848b9b]">Unsaved changes</span>
<span className="text-xs text-muted-foreground">Unsaved changes</span>
)}
<button
onClick={() => editorAI.isOpen ? editorAI.closePanel() : editorAI.openPanel()}
title="Toggle AI Assist panel"
className={cn(
'flex items-center gap-1.5 rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium transition-colors',
'flex items-center gap-1.5 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors',
editorAI.isOpen
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border-primary/30'
: 'text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]'
? 'bg-accent-dim text-primary border-primary/30'
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
)}
>
<Sparkles className="h-4 w-4" />
@@ -329,40 +329,40 @@ export function ProceduralEditorPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#848b9b]">Name</label>
<label className="mb-1 block text-sm font-medium text-muted-foreground">Name</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g. Domain Controller Build"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:outline-hidden 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>
<div>
<label className="mb-1 block text-sm font-medium text-[#848b9b]">Description</label>
<label className="mb-1 block text-sm font-medium text-muted-foreground">Description</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Brief description of this procedure..."
rows={2}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:outline-hidden 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>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#848b9b]">Tags</label>
<label className="mb-1 block text-sm font-medium text-muted-foreground">Tags</label>
<TagInput tags={tags} onChange={setTags} />
</div>
<div className="flex items-end pb-1">
<label className="flex items-center gap-2 text-sm text-[#848b9b]">
<label className="flex items-center gap-2 text-sm text-muted-foreground">
<input
type="checkbox"
checked={isPublic}
onChange={(e) => setIsPublic(e.target.checked)}
className="rounded border-[#1e2130]"
className="rounded border-border"
/>
Public (visible to all users)
</label>

View File

@@ -630,17 +630,17 @@ export function ProceduralNavigationPage() {
return (
<div className="flex h-full min-h-0 flex-col overflow-hidden">
{/* Top bar */}
<div className="border-b border-[#1e2130] px-4 py-3 sm:px-6">
<div className="border-b border-border px-4 py-3 sm:px-6">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="rounded-md p-1.5 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] lg:hidden"
className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground lg:hidden"
>
{sidebarOpen ? <ChevronLeft className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
</button>
<ListOrdered className="h-5 w-5 text-[#848b9b]" />
<h1 className="text-sm font-semibold text-[#e2e5eb] sm:text-base">{tree.name}</h1>
<ListOrdered className="h-5 w-5 text-muted-foreground" />
<h1 className="text-sm font-semibold text-foreground sm:text-base">{tree.name}</h1>
</div>
<button
onClick={() => {
@@ -650,7 +650,7 @@ export function ProceduralNavigationPage() {
navigate('/trees')
}
}}
className="rounded-md border border-[#1e2130] px-3 py-1.5 text-xs font-medium text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] transition-colors"
className="rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
>
<X className="mr-1 inline h-3.5 w-3.5" />
Exit
@@ -693,7 +693,7 @@ export function ProceduralNavigationPage() {
{/* Left sidebar - step checklist */}
<div
className={cn(
'min-h-0 border-r border-[#1e2130] bg-[#14161d] transition-all duration-200',
'min-h-0 border-r border-border bg-card transition-all duration-200',
sidebarOpen ? 'w-72 overflow-y-auto p-3' : 'w-0 overflow-hidden p-0'
)}
>
@@ -708,7 +708,7 @@ export function ProceduralNavigationPage() {
{/* PSA Ticket Context Panel */}
{session?.psa_ticket_id && (
<div className="mt-3 border-t border-[#1e2130] pt-3">
<div className="mt-3 border-t border-border pt-3">
<TicketContextPanel
context={ticketContext}
loading={ticketContextLoading}
@@ -720,10 +720,10 @@ export function ProceduralNavigationPage() {
{/* Session Variables button */}
{intakeFields.length > 0 && (
<div className="mt-3 border-t border-[#1e2130] pt-3">
<div className="mt-3 border-t border-border pt-3">
<button
onClick={() => setParamsOpen(true)}
className="flex w-full items-center gap-2 rounded-lg border border-[#1e2130] px-3 py-2 text-xs text-[#848b9b] hover:bg-accent hover:text-[#848b9b]"
className="flex w-full items-center gap-2 rounded-lg border border-border px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-muted-foreground"
>
<Settings2 className="h-3.5 w-3.5" />
Session Variables
@@ -780,7 +780,7 @@ export function ProceduralNavigationPage() {
<div className="mt-4">
<button
onClick={() => setShowCustomStepModal(true)}
className="flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-[#1e2130] px-4 py-2.5 text-sm text-[#848b9b] transition-colors hover:border-primary/50 hover:text-[#e2e5eb]"
className="flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border px-4 py-2.5 text-sm text-muted-foreground transition-colors hover:border-primary/50 hover:text-foreground"
>
<Plus className="h-4 w-4" />
Add Step
@@ -845,15 +845,15 @@ export function ProceduralNavigationPage() {
{paramsOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div
className="absolute inset-0 bg-[#0c0d10]/60"
className="absolute inset-0 bg-background/60"
onClick={() => setParamsOpen(false)}
/>
<div className="relative w-full max-w-md rounded-2xl border border-[#1e2130] bg-[#14161d] shadow-2xl">
<div className="flex items-center justify-between border-b border-[#1e2130] px-5 py-4">
<h3 className="text-sm font-semibold text-[#e2e5eb]">Session Variables</h3>
<div className="relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl">
<div className="flex items-center justify-between border-b border-border px-5 py-4">
<h3 className="text-sm font-semibold text-foreground">Session Variables</h3>
<button
onClick={() => setParamsOpen(false)}
className="rounded-lg p-1 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-lg p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<X className="h-4 w-4" />
</button>
@@ -872,7 +872,7 @@ export function ProceduralNavigationPage() {
key={field.variable_name}
className={cn(
'rounded-lg border px-3 py-2.5',
isFilled ? 'border-[#1e2130] bg-accent' : 'border-cyan-500/20 bg-cyan-500/5'
isFilled ? 'border-border bg-accent' : 'border-cyan-500/20 bg-cyan-500/5'
)}
>
<div className="flex items-center justify-between gap-2">
@@ -882,7 +882,7 @@ export function ProceduralNavigationPage() {
) : (
<AlertCircle className="h-3.5 w-3.5 shrink-0 text-cyan-400" />
)}
<span className="text-xs font-medium text-[#848b9b] truncate">
<span className="text-xs font-medium text-muted-foreground truncate">
{field.label}
{field.required && <span className="ml-1 text-amber-400">*</span>}
</span>
@@ -890,7 +890,7 @@ export function ProceduralNavigationPage() {
{!isEditing && (
<button
onClick={() => startEditingVar(field.variable_name)}
className="shrink-0 rounded px-2 py-0.5 text-xs text-[#848b9b] hover:bg-[#14161d] hover:text-[#e2e5eb]"
className="shrink-0 rounded px-2 py-0.5 text-xs text-muted-foreground hover:bg-card hover:text-foreground"
>
{isFilled ? 'Edit' : 'Fill'}
</button>
@@ -903,7 +903,7 @@ export function ProceduralNavigationPage() {
value={editingVarValue}
onChange={(e) => setEditingVarValue(e.target.value)}
autoFocus
className="w-full rounded-md border border-cyan-500/30 bg-[#14161d] px-2.5 py-1.5 text-sm text-[#e2e5eb] focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
className="w-full rounded-md border border-cyan-500/30 bg-card px-2.5 py-1.5 text-sm text-foreground focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
>
<option value="">{field.placeholder || 'Select...'}</option>
{field.options.map((opt) => (
@@ -917,7 +917,7 @@ export function ProceduralNavigationPage() {
autoFocus
rows={3}
placeholder={field.placeholder}
className="w-full rounded-md border border-cyan-500/30 bg-[#14161d] px-2.5 py-1.5 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
className="w-full rounded-md border border-cyan-500/30 bg-card px-2.5 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
/>
) : (
<input
@@ -927,30 +927,30 @@ export function ProceduralNavigationPage() {
onKeyDown={(e) => { if (e.key === 'Enter') saveEditingVar() }}
autoFocus
placeholder={field.placeholder}
className="w-full rounded-md border border-cyan-500/30 bg-[#14161d] px-2.5 py-1.5 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
className="w-full rounded-md border border-cyan-500/30 bg-card px-2.5 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-cyan-400 focus:outline-hidden focus:ring-1 focus:ring-cyan-400/30"
/>
)}
{field.help_text && (
<p className="mt-1 text-[0.625rem] text-[#848b9b]">{field.help_text}</p>
<p className="mt-1 text-[0.625rem] text-muted-foreground">{field.help_text}</p>
)}
<div className="mt-2 flex justify-end gap-2">
<button
onClick={() => { setEditingVarName(null); setEditingVarValue('') }}
className="rounded px-2.5 py-1 text-xs text-[#848b9b] hover:text-[#e2e5eb]"
className="rounded px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground"
>
Cancel
</button>
<button
onClick={saveEditingVar}
disabled={!editingVarValue.trim()}
className="rounded bg-[#22d3ee] px-2.5 py-1 text-xs font-medium text-white disabled:opacity-40"
className="rounded bg-primary px-2.5 py-1 text-xs font-medium text-white disabled:opacity-40"
>
Save
</button>
</div>
</div>
) : isFilled ? (
<p className="mt-1 text-sm text-[#e2e5eb] truncate">{value}</p>
<p className="mt-1 text-sm text-foreground truncate">{value}</p>
) : null}
</div>
)
@@ -961,8 +961,8 @@ export function ProceduralNavigationPage() {
.filter(([key]) => !intakeFields.some(f => f.variable_name === key))
.map(([key, value]) => (
<div key={key} className="flex items-baseline justify-between gap-4 rounded-lg bg-accent px-3 py-2">
<span className="text-xs font-medium text-[#848b9b]">{key.replace(/_/g, ' ')}</span>
<span className="text-right text-sm text-[#848b9b]">{value || 'N/A'}</span>
<span className="text-xs font-medium text-muted-foreground">{key.replace(/_/g, ' ')}</span>
<span className="text-right text-sm text-muted-foreground">{value || 'N/A'}</span>
</div>
))}
</div>

View File

@@ -156,7 +156,7 @@ export default function PublicTemplatesPage() {
description="Browse battle-tested troubleshooting flows and scripts built by MSP engineers. Free to explore."
/>
<div className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div className="min-h-screen bg-background text-foreground">
{/* Ambient orbs */}
<div
className="fixed top-[-20%] right-[-10%] w-[600px] h-[600px] rounded-full pointer-events-none"
@@ -172,25 +172,25 @@ export default function PublicTemplatesPage() {
/>
{/* Header */}
<header className="sticky top-0 z-40 border-b border-[#1e2130] bg-[#0c0d10]/80">
<header className="sticky top-0 z-40 border-b border-border bg-background/80">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<Link to="/landing" className="flex items-center gap-2.5">
<BrandLogo size="sm" />
<span className="font-heading text-lg font-semibold">
<span className="text-[#e2e5eb]">Resolution</span>
<span className="text-[#67e8f9]">Flow</span>
<span className="text-foreground">Resolution</span>
<span className="text-accent-text">Flow</span>
</span>
</Link>
<div className="flex items-center gap-3">
<Link
to="/login"
className="px-4 py-2 rounded-lg text-sm font-medium text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
>
Sign In
</Link>
<Link
to="/register"
className="px-4 py-2 rounded-lg text-sm font-semibold bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="px-4 py-2 rounded-lg text-sm font-semibold bg-primary text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Sign Up Free
</Link>
@@ -203,21 +203,21 @@ export default function PublicTemplatesPage() {
<div className="max-w-3xl mx-auto text-center">
<h1 className="font-heading text-4xl lg:text-5xl font-bold tracking-tight">
MSP Troubleshooting{' '}
<span className="text-[#67e8f9]">Templates</span>
<span className="text-accent-text">Templates</span>
</h1>
<p className="mt-4 text-lg text-[#848b9b] max-w-2xl mx-auto">
<p className="mt-4 text-lg text-muted-foreground max-w-2xl mx-auto">
Battle-tested flows and scripts built by MSP engineers. Free to browse, powerful when connected to FlowPilot.
</p>
{/* Search */}
<div className="mt-8 max-w-xl mx-auto relative">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-[#848b9b]" />
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<input
type="text"
placeholder="Search templates..."
value={searchQuery}
onChange={(e) => handleSearchChange(e.target.value)}
className="w-full pl-12 pr-4 py-3.5 rounded-2xl text-sm bg-[#14161d] border border-[#1e2130] text-[#e2e5eb] placeholder:text-[#848b9b] focus:outline-none focus:border-[rgba(6,182,212,0.3)] transition-colors"
className="w-full pl-12 pr-4 py-3.5 rounded-2xl text-sm bg-card border border-border text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-[rgba(6,182,212,0.3)] transition-colors"
/>
</div>
</div>
@@ -235,8 +235,8 @@ export default function PublicTemplatesPage() {
className={cn(
'px-3 py-1.5 rounded-full text-sm font-medium border transition-colors',
!activeCategory
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb] border-primary/30'
: 'bg-[#14161d] border-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)]'
? 'bg-accent-dim text-foreground border-primary/30'
: 'bg-card border-border text-muted-foreground hover:text-foreground hover:border-[rgba(255,255,255,0.12)]'
)}
>
All
@@ -249,8 +249,8 @@ export default function PublicTemplatesPage() {
className={cn(
'px-3 py-1.5 rounded-full text-sm font-medium border transition-colors',
cat === activeCategory
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb] border-primary/30'
: 'bg-[#14161d] border-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)]'
? 'bg-accent-dim text-foreground border-primary/30'
: 'bg-card border-border text-muted-foreground hover:text-foreground hover:border-[rgba(255,255,255,0.12)]'
)}
>
{cat}
@@ -262,7 +262,7 @@ export default function PublicTemplatesPage() {
{/* Type toggle + Sort */}
<div className="flex items-center justify-between gap-4 flex-wrap">
{/* Type segmented control */}
<div className="flex items-center rounded-xl border border-[#1e2130] bg-[#14161d] overflow-hidden">
<div className="flex items-center rounded-xl border border-border bg-card overflow-hidden">
{(['all', 'flows', 'scripts'] as TypeFilter[]).map((t) => (
<button
key={t}
@@ -271,8 +271,8 @@ export default function PublicTemplatesPage() {
className={cn(
'px-4 py-2 text-sm font-medium transition-colors',
t === typeFilter
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb]'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'bg-accent-dim text-foreground'
: 'text-muted-foreground hover:text-foreground'
)}
>
{t === 'all' ? 'All' : t === 'flows' ? 'Flows' : 'Scripts'}
@@ -285,16 +285,16 @@ export default function PublicTemplatesPage() {
<button
type="button"
onClick={() => setSortOpen(!sortOpen)}
className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] transition-colors"
className="flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors"
>
<SlidersHorizontal className="w-4 h-4 text-[#848b9b]" />
<SlidersHorizontal className="w-4 h-4 text-muted-foreground" />
{sortLabels[sort]}
<ChevronDown className={cn('w-4 h-4 text-[#848b9b] transition-transform', sortOpen && 'rotate-180')} />
<ChevronDown className={cn('w-4 h-4 text-muted-foreground transition-transform', sortOpen && 'rotate-180')} />
</button>
{sortOpen && (
<>
<div className="fixed inset-0 z-10" onClick={() => setSortOpen(false)} />
<div className="absolute right-0 mt-1 z-20 w-52 rounded-xl border border-[#1e2130] bg-[#14161d] shadow-lg shadow-black/30 overflow-hidden">
<div className="absolute right-0 mt-1 z-20 w-52 rounded-xl border border-border bg-card shadow-lg shadow-black/30 overflow-hidden">
{(Object.entries(sortLabels) as [SortOption, string][]).map(([key, label]) => (
<button
key={key}
@@ -306,8 +306,8 @@ export default function PublicTemplatesPage() {
className={cn(
'w-full text-left px-4 py-2.5 text-sm transition-colors',
key === sort
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb]'
: 'text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[rgba(255,255,255,0.04)]'
? 'bg-accent-dim text-foreground'
: 'text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.04)]'
)}
>
{label}
@@ -341,11 +341,11 @@ export default function PublicTemplatesPage() {
{/* Error */}
{!loading && error && (
<div className="text-center py-16">
<p className="text-[#848b9b] text-sm">{error}</p>
<p className="text-muted-foreground text-sm">{error}</p>
<button
type="button"
onClick={fetchGallery}
className="mt-4 px-4 py-2 rounded-lg text-sm font-medium bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="mt-4 px-4 py-2 rounded-lg text-sm font-medium bg-primary text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Try Again
</button>
@@ -355,7 +355,7 @@ export default function PublicTemplatesPage() {
{/* Empty */}
{!loading && !error && flows.length === 0 && scripts.length === 0 && (
<div className="text-center py-16">
<p className="text-[#848b9b] text-sm">
<p className="text-muted-foreground text-sm">
No templates found. Try a different search or category.
</p>
</div>
@@ -368,7 +368,7 @@ export default function PublicTemplatesPage() {
{showFlows && flows.length > 0 && (
<div>
{typeFilter === 'all' && (
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] mb-4">
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-4">
Flows ({data?.total_flows || flows.length})
</h2>
)}
@@ -388,7 +388,7 @@ export default function PublicTemplatesPage() {
{showScripts && scripts.length > 0 && (
<div>
{typeFilter === 'all' && (
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] mb-4">
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-4">
Scripts ({data?.total_scripts || scripts.length})
</h2>
)}
@@ -409,18 +409,18 @@ export default function PublicTemplatesPage() {
</section>
{/* Footer */}
<footer className="border-t border-[#1e2130] py-8 px-4 sm:px-6 lg:px-8">
<footer className="border-t border-border py-8 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<Link
to="/landing"
className="text-[#848b9b] text-sm hover:text-[#e2e5eb] transition-colors"
className="text-muted-foreground text-sm hover:text-foreground transition-colors"
>
Powered by <span className="font-semibold">ResolutionFlow</span>
</Link>
<div className="flex items-center gap-4">
<Link
to="/register"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Get Started
</Link>
@@ -431,7 +431,7 @@ export default function PublicTemplatesPage() {
{/* Detail loading overlay */}
{detailLoading && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
<Loader2 className="w-8 h-8 text-[#22d3ee] animate-spin" />
<Loader2 className="w-8 h-8 text-primary animate-spin" />
</div>
)}

View File

@@ -18,7 +18,7 @@ export function QuickStartPage() {
<div className="p-6 space-y-5 max-w-5xl mx-auto">
{/* Greeting */}
<div className="fade-in" style={{ animationDelay: '100ms' }}>
<h1 className="font-heading text-3xl font-extrabold tracking-tight text-[#e2e5eb]">
<h1 className="font-heading text-3xl font-extrabold tracking-tight text-foreground">
Good{' '}
{new Date().getHours() < 12
? 'morning'
@@ -27,7 +27,7 @@ export function QuickStartPage() {
: 'evening'}
, {user?.name?.split(' ')[0] || 'there'}
</h1>
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
{new Date().toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',

View File

@@ -88,19 +88,19 @@ export function RegisterPage() {
<div className="mb-4 flex justify-center sm:mb-6">
<BrandLogo size="lg" />
</div>
<h1 className="text-3xl font-bold font-heading text-[#e2e5eb] tracking-tight">
<h1 className="text-3xl font-bold font-heading text-foreground tracking-tight">
ResolutionFlow
</h1>
<p className="mt-2 text-base font-medium text-[#848b9b] sm:mt-3 sm:text-lg">
<p className="mt-2 text-base font-medium text-muted-foreground sm:mt-3 sm:text-lg">
AI-Powered Troubleshooting for MSPs
</p>
<p className="mt-1 text-sm text-[#848b9b] sm:mt-2">
<p className="mt-1 text-sm text-muted-foreground sm:mt-2">
Create your account
</p>
</div>
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
{(error || localError) && (
<div className="rounded-xl border border-red-400/20 bg-red-400/10 p-3 text-sm text-red-400">
{localError || error}
@@ -108,7 +108,7 @@ export function RegisterPage() {
)}
<div>
<label htmlFor="inviteCode" className="block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="inviteCode" className="block text-sm font-medium text-foreground">
Invite code
</label>
<input
@@ -122,18 +122,18 @@ export function RegisterPage() {
}}
onBlur={(e) => validateInviteCode(e.target.value)}
className={cn(
'mt-1 block w-full rounded-xl border bg-[#14161d] px-3 py-2 font-mono tracking-wider',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-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-[#1e2130] focus:border-primary focus:ring-primary/20',
inviteCodeStatus === 'checking' && 'border-[#1e2130] focus:border-primary focus:ring-primary/20'
inviteCodeStatus === 'idle' && 'border-border focus:border-primary focus:ring-primary/20',
inviteCodeStatus === 'checking' && 'border-border focus:border-primary focus:ring-primary/20'
)}
placeholder="ABCD1234"
/>
{inviteCodeStatus === 'checking' && (
<p className="mt-1 text-xs text-[#848b9b]">Validating...</p>
<p className="mt-1 text-xs text-muted-foreground">Validating...</p>
)}
{inviteCodeStatus === 'valid' && (
<p className="mt-1 text-xs text-emerald-400">{inviteCodeMessage}</p>
@@ -144,7 +144,7 @@ export function RegisterPage() {
</div>
<div>
<label htmlFor="name" className="block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="name" className="block text-sm font-medium text-foreground">
Full name
</label>
<input
@@ -156,8 +156,8 @@ export function RegisterPage() {
value={name}
onChange={(e) => setName(e.target.value)}
className={cn(
'mt-1 block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
placeholder="John Smith"
@@ -165,7 +165,7 @@ export function RegisterPage() {
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="email" className="block text-sm font-medium text-foreground">
Email address
</label>
<input
@@ -177,8 +177,8 @@ export function RegisterPage() {
value={email}
onChange={(e) => setEmail(e.target.value)}
className={cn(
'mt-1 block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
placeholder="you@example.com"
@@ -186,7 +186,7 @@ export function RegisterPage() {
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="password" className="block text-sm font-medium text-foreground">
Password
</label>
<PasswordInput
@@ -197,19 +197,19 @@ export function RegisterPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
className={cn(
'mt-1 block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
placeholder="••••••••••"
/>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Must be at least 10 characters
</p>
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="confirmPassword" className="block text-sm font-medium text-foreground">
Confirm password
</label>
<PasswordInput
@@ -220,8 +220,8 @@ export function RegisterPage() {
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className={cn(
'mt-1 block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
placeholder="••••••••••"
@@ -233,7 +233,7 @@ export function RegisterPage() {
disabled={isLoading}
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-[#22d3ee] text-white hover:brightness-110',
'bg-primary text-white hover:brightness-110',
'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'
@@ -243,9 +243,9 @@ export function RegisterPage() {
</button>
</div>
<p className="text-center text-sm text-[#848b9b]">
<p className="text-center text-sm text-muted-foreground">
Already have an account?{' '}
<Link to="/login" className="font-medium text-[#e2e5eb] hover:underline">
<Link to="/login" className="font-medium text-foreground hover:underline">
Sign in
</Link>
</p>

View File

@@ -85,24 +85,24 @@ export function ResetPasswordPage() {
<BrandLogo size="lg" className="h-10 w-10 invert sm:h-12 sm:w-12" />
</div>
</div>
<h1 className="text-3xl font-bold font-heading text-[#e2e5eb] tracking-tight">
<h1 className="text-3xl font-bold font-heading text-foreground tracking-tight">
Reset Password
</h1>
</div>
{verifying ? (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 text-center">
<p className="text-[#848b9b]">Verifying reset link...</p>
<div className="bg-card border border-border rounded-xl p-6 text-center">
<p className="text-muted-foreground">Verifying reset link...</p>
</div>
) : !token || !valid ? (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
<div className="rounded-xl border border-red-400/20 bg-red-400/10 p-4 text-sm text-red-400">
This reset link is invalid or has expired. Please request a new one.
</div>
<div className="text-center">
<Link
to="/forgot-password"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Request new reset link
</Link>
@@ -110,10 +110,10 @@ export function ResetPasswordPage() {
</div>
) : (
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 space-y-4">
<div className="bg-card border border-border rounded-xl p-6 space-y-4">
{email && (
<p className="text-sm text-[#848b9b]">
Resetting password for <span className="font-medium text-[#e2e5eb]">{email}</span>
<p className="text-sm text-muted-foreground">
Resetting password for <span className="font-medium text-foreground">{email}</span>
</p>
)}
@@ -124,7 +124,7 @@ export function ResetPasswordPage() {
)}
<div>
<label htmlFor="new-password" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="new-password" className="mb-1 block text-sm font-medium text-foreground">
New Password
</label>
<PasswordInput
@@ -134,20 +134,20 @@ export function ResetPasswordPage() {
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
placeholder="At least 10 characters"
/>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Must include uppercase, lowercase, and a digit.
</p>
</div>
<div>
<label htmlFor="confirm-password" className="mb-1 block text-sm font-medium text-[#e2e5eb]">
<label htmlFor="confirm-password" className="mb-1 block text-sm font-medium text-foreground">
Confirm New Password
</label>
<PasswordInput
@@ -157,8 +157,8 @@ export function ResetPasswordPage() {
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className={cn(
'block w-full rounded-xl border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20',
'transition-colors'
)}
@@ -170,7 +170,7 @@ export function ResetPasswordPage() {
disabled={isLoading}
className={cn(
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
'bg-[#22d3ee] text-white hover:brightness-110',
'bg-primary text-white hover:brightness-110',
'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'

View File

@@ -93,16 +93,16 @@ export default function ReviewQueuePage() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Lightbulb size={16} className="text-amber-400" />
<h1 className="font-heading text-base font-semibold text-[#e2e5eb]">Review Queue</h1>
<h1 className="font-heading text-base font-semibold text-foreground">Review Queue</h1>
</div>
<button onClick={loadProposals} className="text-[#848b9b] hover:text-[#e2e5eb] transition-colors">
<button onClick={loadProposals} className="text-muted-foreground hover:text-foreground transition-colors">
<RefreshCw size={14} />
</button>
</div>
{/* Stats bar */}
{stats && (
<div className="flex items-center gap-3 text-xs text-[#848b9b]">
<div className="flex items-center gap-3 text-xs text-muted-foreground">
<span className="flex items-center gap-1">
<Clock size={10} className="text-amber-400" />
{stats.pending_count} pending
@@ -112,7 +112,7 @@ export default function ReviewQueuePage() {
{stats.approved_this_week} approved
</span>
<span className="flex items-center gap-1">
<Sparkles size={10} className="text-[#22d3ee]" />
<Sparkles size={10} className="text-primary" />
{stats.auto_reinforced_this_week} reinforced
</span>
</div>
@@ -126,8 +126,8 @@ export default function ReviewQueuePage() {
onClick={() => setActiveTab(tab.key)}
className={`shrink-0 rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${
activeTab === tab.key
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee]'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'bg-accent-dim text-primary'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{tab.label}
@@ -144,7 +144,7 @@ export default function ReviewQueuePage() {
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-xs text-[#e2e5eb]"
className="w-full rounded-lg border border-border bg-card px-3 py-1.5 text-xs text-foreground"
>
<option value="newest">Newest first</option>
<option value="confidence">Highest confidence</option>
@@ -156,12 +156,12 @@ export default function ReviewQueuePage() {
<div className="p-3 space-y-2">
{isLoading ? (
<div className="flex justify-center py-12">
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
<Loader2 size={20} className="animate-spin text-muted-foreground" />
</div>
) : proposals.length === 0 ? (
<div className="py-12 text-center">
<Lightbulb size={24} className="mx-auto mb-2 text-[#848b9b]/40" />
<p className="text-sm text-[#848b9b]">No proposals found</p>
<Lightbulb size={24} className="mx-auto mb-2 text-muted-foreground/40" />
<p className="text-sm text-muted-foreground">No proposals found</p>
</div>
) : (
proposals.map((proposal) => (
@@ -180,7 +180,7 @@ export default function ReviewQueuePage() {
<div className={`flex-1 overflow-y-auto ${!detail && !isLoadingDetail ? 'hidden lg:flex' : ''}`}>
{isLoadingDetail ? (
<div className="flex items-center justify-center h-full">
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
<Loader2 size={20} className="animate-spin text-muted-foreground" />
</div>
) : detail ? (
<ProposalDetail
@@ -190,8 +190,8 @@ export default function ReviewQueuePage() {
) : (
<div className="flex items-center justify-center h-full">
<div className="text-center">
<Lightbulb size={32} className="mx-auto mb-3 text-[#848b9b]/30" />
<p className="text-sm text-[#848b9b]">Select a proposal to review</p>
<Lightbulb size={32} className="mx-auto mb-3 text-muted-foreground/30" />
<p className="text-sm text-muted-foreground">Select a proposal to review</p>
</div>
</div>
)}

View File

@@ -131,8 +131,8 @@ export default function ScriptBuilderPage() {
<Terminal size={18} className="text-fuchsia-400" />
</span>
<div>
<h1 className="text-base font-heading font-bold text-[#e2e5eb]">Script Builder</h1>
<p className="text-xs text-[#848b9b]">Describe what you need, AI generates the script</p>
<h1 className="text-base font-heading font-bold text-foreground">Script Builder</h1>
<p className="text-xs text-muted-foreground">Describe what you need, AI generates the script</p>
</div>
</div>
@@ -146,10 +146,10 @@ export default function ScriptBuilderPage() {
className={cn(
"px-3 py-1.5 rounded-md text-xs font-sans text-xs font-medium transition-all",
language === lang.value
? "bg-[#22d3ee] text-white"
? "bg-primary text-white"
: hasMessages
? "text-[#5a6170] cursor-not-allowed"
: "text-[#848b9b] hover:text-[#e2e5eb] hover:bg-[rgba(255,255,255,0.04)]"
? "text-text-muted cursor-not-allowed"
: "text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.04)]"
)}
>
{lang.label}

View File

@@ -57,22 +57,22 @@ export default function ScriptLibraryPage() {
const tabClass = (tab: LibraryTab) =>
tab === activeTab
? 'border-b-2 border-primary text-[#e2e5eb]'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'border-b-2 border-primary text-foreground'
: 'text-muted-foreground hover:text-foreground'
return (
<div className="flex flex-col gap-4 p-6 h-full">
{/* Page header */}
<div className="flex items-start justify-between">
<div>
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">Script Library</h1>
<p className="text-sm text-[#848b9b] mt-1">
<h1 className="text-2xl font-heading font-bold text-foreground">Script Library</h1>
<p className="text-sm text-muted-foreground mt-1">
Browse PowerShell templates, fill in parameters, and generate ready-to-run scripts.
</p>
{isEngineer && (
<Link
to="/scripts/manage"
className="inline-flex items-center gap-1.5 text-xs text-[#22d3ee] bg-[rgba(34,211,238,0.10)] hover:bg-primary/15 px-2.5 py-1 rounded-full transition-colors mt-2 group"
className="inline-flex items-center gap-1.5 text-xs text-primary bg-accent-dim hover:bg-primary/15 px-2.5 py-1 rounded-full transition-colors mt-2 group"
>
<Settings size={12} className="group-hover:rotate-90 transition-transform duration-300" />
Manage Templates
@@ -81,7 +81,7 @@ export default function ScriptLibraryPage() {
</div>
<Link
to="/script-builder"
className="inline-flex items-center gap-2 bg-[#22d3ee] text-white font-semibold rounded-lg px-4 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
className="inline-flex items-center gap-2 bg-primary text-white font-semibold rounded-lg px-4 py-2 hover:brightness-110 active:scale-[0.98] transition-all"
>
<Wand2 size={16} />
Build a New Script
@@ -89,7 +89,7 @@ export default function ScriptLibraryPage() {
</div>
{/* Tab bar */}
<div className="flex gap-6 border-b border-[#1e2130]">
<div className="flex gap-6 border-b border-border">
<button
type="button"
onClick={() => onTabChange('mine')}
@@ -129,8 +129,8 @@ export default function ScriptLibraryPage() {
{/* Right pane — read-only ScriptPreview */}
{selectedTemplate === null ? (
<div className="card-flat h-full flex flex-col items-center justify-center gap-3 text-center p-8">
<Terminal size={40} className="text-[#848b9b]/40" />
<p className="text-sm text-[#848b9b]">Select a template to get started</p>
<Terminal size={40} className="text-muted-foreground/40" />
<p className="text-sm text-muted-foreground">Select a template to get started</p>
</div>
) : (
<div className="card-flat h-full overflow-y-auto p-4">

View File

@@ -380,7 +380,7 @@ export function SessionDetailPage() {
</div>
<button
onClick={() => navigate('/sessions')}
className="mt-4 text-[#e2e5eb] hover:underline"
className="mt-4 text-foreground hover:underline"
>
Back to sessions
</button>
@@ -393,7 +393,7 @@ export function SessionDetailPage() {
resolved: { icon: <CheckCircle2 className="h-5 w-5" />, color: 'text-emerald-400', bg: 'bg-emerald-500/10', border: 'border-emerald-500/20' },
workaround: { icon: <AlertTriangle className="h-5 w-5" />, color: 'text-amber-400', bg: 'bg-amber-500/10', border: 'border-amber-500/20' },
escalated: { icon: <ArrowUpRight className="h-5 w-5" />, color: 'text-red-400', bg: 'bg-red-500/10', border: 'border-red-500/20' },
unresolved: { icon: <HelpCircle className="h-5 w-5" />, color: 'text-[#848b9b]', bg: 'bg-muted', border: 'border-[#1e2130]' },
unresolved: { icon: <HelpCircle className="h-5 w-5" />, color: 'text-muted-foreground', bg: 'bg-muted', border: 'border-border' },
}
const outcomeConfig = session.outcome ? OUTCOME_CONFIG[session.outcome] : null
@@ -402,7 +402,7 @@ export function SessionDetailPage() {
{/* Back nav */}
<button
onClick={() => navigate('/sessions')}
className="mb-4 text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="mb-4 text-sm text-muted-foreground hover:text-foreground"
>
Back to sessions
</button>
@@ -410,10 +410,10 @@ export function SessionDetailPage() {
{/* Page title row */}
<div className="mb-6 flex items-start justify-between gap-4">
<div>
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb] sm:text-3xl">
<h1 className="text-2xl font-heading font-bold text-foreground sm:text-3xl">
{session.ticket_number || 'Session Details'}
</h1>
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
{session.tree_snapshot?.name}
{session.client_name && <> · Client: {session.client_name}</>}
{session.started_at && <>{' · '}{new Date(session.started_at).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}</>}
@@ -436,15 +436,15 @@ export function SessionDetailPage() {
<div>
<div className="flex items-center gap-2">
<span className={cn('text-base font-semibold', outcomeConfig.color)}>{outcomeLabel}</span>
<span className="text-sm text-[#848b9b]">· {getTotalDuration()}</span>
<span className="text-sm text-muted-foreground">· {getTotalDuration()}</span>
</div>
{session.outcome_notes && (
<p className="mt-1 text-sm text-[#848b9b]">{session.outcome_notes}</p>
<p className="mt-1 text-sm text-muted-foreground">{session.outcome_notes}</p>
)}
{session.next_steps && (
<div className="mt-2">
<span className="font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-[#848b9b]">Next Steps</span>
<p className="mt-0.5 text-sm text-[#848b9b] whitespace-pre-wrap">{session.next_steps}</p>
<span className="font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-muted-foreground">Next Steps</span>
<p className="mt-0.5 text-sm text-muted-foreground whitespace-pre-wrap">{session.next_steps}</p>
</div>
)}
</div>
@@ -464,7 +464,7 @@ export function SessionDetailPage() {
<Button
onClick={handleCreateFlowFromSession}
disabled={isGeneratingFlow}
className="bg-[#22d3ee] text-white font-semibold rounded-lg hover:brightness-110 active:scale-[0.98] disabled:opacity-60"
className="bg-primary text-white font-semibold rounded-lg hover:brightness-110 active:scale-[0.98] disabled:opacity-60"
>
{isGeneratingFlow ? (
<Loader2 className="h-4 w-4 animate-spin" />
@@ -483,7 +483,7 @@ export function SessionDetailPage() {
<Flag className="h-4 w-4 shrink-0 text-amber-400" />
<div>
<p className="text-sm font-medium text-amber-300">Session in progress</p>
<p className="text-xs text-[#848b9b]">Set an outcome to finalize this session and generate documentation.</p>
<p className="text-xs text-muted-foreground">Set an outcome to finalize this session and generate documentation.</p>
</div>
</div>
<Button onClick={() => setShowOutcomeModal(true)} className="shrink-0">
@@ -498,7 +498,7 @@ export function SessionDetailPage() {
value={exportFormat}
onChange={(e) => setExportFormat(e.target.value as typeof exportFormat)}
aria-label="Export format"
className="rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-primary focus:outline-hidden 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>
@@ -511,7 +511,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-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-primary focus:outline-hidden 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) => (
@@ -523,7 +523,7 @@ export function SessionDetailPage() {
value={detailLevel}
onChange={(e) => setDetailLevel(e.target.value as 'standard' | 'full')}
aria-label="Detail level"
className="rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-primary focus:outline-hidden 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>
@@ -532,7 +532,7 @@ export function SessionDetailPage() {
onClick={handleCopy}
disabled={isExporting}
title="Copy to clipboard"
className="rounded-md border border-[#1e2130] bg-[#14161d] p-1.5 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] disabled:opacity-50"
className="rounded-md border border-border bg-card p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50"
>
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
</button>
@@ -549,7 +549,7 @@ export function SessionDetailPage() {
{session.completed_at && (
<button
onClick={handleCopyForTicket}
className="flex items-center gap-1.5 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex items-center gap-1.5 rounded-md border border-border bg-card px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
{copiedPsa ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
{copiedPsa ? 'Copied!' : 'Copy for Ticket'}

View File

@@ -280,8 +280,8 @@ export function SessionHistoryPage() {
<PageMeta title="Sessions" />
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
<div className="mb-8">
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb] sm:text-3xl">Sessions</h1>
<p className="mt-2 text-[#848b9b]">
<h1 className="text-2xl font-heading font-bold text-foreground sm:text-3xl">Sessions</h1>
<p className="mt-2 text-muted-foreground">
View and manage all your sessions
</p>
</div>
@@ -295,13 +295,13 @@ export function SessionHistoryPage() {
<div className="flex gap-3">
<Link
to="/trees"
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Start a Flow
</Link>
<Link
to="/pilot"
className="inline-flex items-center gap-2 rounded-lg border border-[#1e2130] bg-[rgba(255,255,255,0.04)] px-5 py-2.5 text-sm font-semibold text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] transition-all"
className="inline-flex items-center gap-2 rounded-lg border border-border bg-[rgba(255,255,255,0.04)] px-5 py-2.5 text-sm font-semibold text-foreground hover:border-[rgba(255,255,255,0.12)] transition-all"
>
Start AI Session
</Link>
@@ -314,20 +314,20 @@ export function SessionHistoryPage() {
{/* FlowPilot Sessions Section */}
{showAiSection && (
<>
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] mb-3">FlowPilot Sessions</h2>
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">FlowPilot Sessions</h2>
{/* AI Session Filter Bar */}
<div className="card-flat p-3 mb-4">
<div className="flex flex-wrap gap-3 items-center">
{/* Search input */}
<div className="relative flex-1 min-w-[180px]">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[#848b9b] pointer-events-none" />
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" />
<input
type="text"
value={aiSearchInput}
onChange={(e) => setAiSearchInput(e.target.value)}
placeholder="Search sessions..."
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] pl-8 pr-3 py-1.5 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
className="w-full rounded-lg border border-border bg-card pl-8 pr-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
/>
</div>
@@ -336,7 +336,7 @@ export function SessionHistoryPage() {
value={aiFilters.problem_domain}
onChange={(e) => setAiFilters((f) => ({ ...f, problem_domain: e.target.value }))}
title="Filter by problem domain"
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-[rgba(6,182,212,0.3)] focus:outline-none [&>option]:bg-[#1a1c21] [&>option]:text-[#e2e5eb]"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none [&>option]:bg-card [&>option]:text-foreground"
>
<option value="">All domains</option>
<option value="Active Directory">Active Directory</option>
@@ -360,8 +360,8 @@ export function SessionHistoryPage() {
className={cn(
'rounded-full border px-3 py-1 text-xs font-sans text-xs transition-colors',
aiFilters.confidence_tier === tier
? 'bg-[rgba(34,211,238,0.10)] text-[#e2e5eb] border-primary/30'
: 'bg-[#14161d] text-[#848b9b] border-[#1e2130] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)]'
? 'bg-accent-dim text-foreground border-primary/30'
: 'bg-card text-muted-foreground border-border hover:text-foreground hover:border-[rgba(255,255,255,0.12)]'
)}
>
{tier === '' ? 'All' : tier.charAt(0).toUpperCase() + tier.slice(1)}
@@ -376,15 +376,15 @@ export function SessionHistoryPage() {
value={aiFilters.date_from}
onChange={(e) => setAiFilters((f) => ({ ...f, date_from: e.target.value }))}
title="From date"
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-[rgba(6,182,212,0.3)] focus:outline-none [color-scheme:dark]"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none [color-scheme:dark]"
/>
<span className="text-xs text-[#848b9b]">to</span>
<span className="text-xs text-muted-foreground">to</span>
<input
type="date"
value={aiFilters.date_to}
onChange={(e) => setAiFilters((f) => ({ ...f, date_to: e.target.value }))}
title="To date"
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:border-[rgba(6,182,212,0.3)] focus:outline-none [color-scheme:dark]"
className="rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none [color-scheme:dark]"
/>
</div>
@@ -395,7 +395,7 @@ export function SessionHistoryPage() {
setAiSearchInput('')
setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' })
}}
className="text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
>
Clear
</button>
@@ -417,7 +417,7 @@ export function SessionHistoryPage() {
setAiSearchInput('')
setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' })
}}
className="text-[#e2e5eb] hover:underline text-sm"
className="text-foreground hover:underline text-sm"
>
Clear all filters
</button>
@@ -433,7 +433,7 @@ export function SessionHistoryPage() {
{/* Divider between sections */}
{showFlowSection && (
<div className="my-8 border-t border-[#1e2130]" />
<div className="my-8 border-t border-border" />
)}
</>
)}
@@ -441,10 +441,10 @@ export function SessionHistoryPage() {
{/* Flow Sessions Section */}
{showFlowSection && (
<>
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] mb-3">Flow Sessions</h2>
<h2 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">Flow Sessions</h2>
{/* Filter Tabs */}
<div className="mb-6 flex gap-2 border-b border-[#1e2130]">
<div className="mb-6 flex gap-2 border-b border-border">
{(['active', 'prepared', 'completed', 'all'] as const).map((tab) => (
<button
key={tab}
@@ -452,8 +452,8 @@ export function SessionHistoryPage() {
className={cn(
'px-4 py-2 text-sm font-medium transition-colors',
filter === tab
? 'border-b-2 border-primary text-[#e2e5eb]'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'border-b-2 border-primary text-foreground'
: 'text-muted-foreground hover:text-foreground'
)}
>
{tab.charAt(0).toUpperCase() + tab.slice(1)}
@@ -481,7 +481,7 @@ export function SessionHistoryPage() {
title="No sessions match your filters"
description="Try adjusting your search or filters."
action={
<button onClick={handleClearFilters} className="text-[#e2e5eb] hover:underline text-sm">
<button onClick={handleClearFilters} className="text-foreground hover:underline text-sm">
Clear all filters
</button>
}
@@ -494,7 +494,7 @@ export function SessionHistoryPage() {
action={
<Link
to="/trees"
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Start a Session
</Link>
@@ -515,7 +515,7 @@ export function SessionHistoryPage() {
style={{ '--stagger-index': i } as React.CSSProperties}
>
<div
className="bg-[#14161d] border border-[#1e2130] rounded-xl p-4 transition-all hover:bg-accent/50"
className="bg-card border border-border rounded-xl p-4 transition-all hover:bg-accent/50"
>
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div className="flex-1">
@@ -527,11 +527,11 @@ export function SessionHistoryPage() {
session.completed_at ? 'bg-green-500' : 'bg-yellow-500'
)}
/>
<span className="font-medium text-[#e2e5eb]">
<span className="font-medium text-foreground">
{session.ticket_number || 'No ticket'}
</span>
{session.client_name && (
<span className="rounded-full bg-accent px-2.5 py-0.5 text-xs font-medium text-[#e2e5eb]">
<span className="rounded-full bg-accent px-2.5 py-0.5 text-xs font-medium text-foreground">
{session.client_name}
</span>
)}
@@ -545,7 +545,7 @@ export function SessionHistoryPage() {
session.outcome === 'unresolved' && 'bg-rose-500/20 text-rose-300',
session.outcome === 'cancelled' && 'bg-zinc-500/20 text-zinc-300',
session.outcome === 'resolved_externally' && 'bg-cyan-500/20 text-cyan-300',
!session.outcome && 'bg-accent text-[#848b9b]'
!session.outcome && 'bg-accent text-muted-foreground'
)}
>
{formatOutcomeLabel(session.outcome)}
@@ -554,12 +554,12 @@ export function SessionHistoryPage() {
</div>
{/* Tree Name */}
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
<span className="font-medium">Tree:</span> {getTreeName(session)}
</p>
{/* Timestamps */}
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
Started: {session.started_at ? formatDate(session.started_at) : 'Not started'}
{session.completed_at && (
<> · Completed: {formatDate(session.completed_at)}</>
@@ -567,7 +567,7 @@ export function SessionHistoryPage() {
</p>
{/* Stats */}
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
{session.decisions.length} decision{session.decisions.length !== 1 ? 's' : ''} recorded
{session.scratchpad && session.scratchpad.trim() && (
<span> · Has notes</span>
@@ -580,8 +580,8 @@ export function SessionHistoryPage() {
<button
onClick={() => navigate(`/sessions/${session.id}`)}
className={cn(
'rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]'
'rounded-md border border-border px-3 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
>
View Details
@@ -595,9 +595,9 @@ export function SessionHistoryPage() {
setCloseNotes('')
}}
className={cn(
'rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]',
closingSessionId === session.id && 'bg-accent text-[#e2e5eb]'
'rounded-md border border-border px-3 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground',
closingSessionId === session.id && 'bg-accent text-foreground'
)}
>
Close
@@ -605,7 +605,7 @@ export function SessionHistoryPage() {
<button
onClick={() => navigate(getSessionResumePath(session.tree_id, session.tree_snapshot?.tree_type), { state: { sessionId: session.id } })}
className={cn(
'rounded-md bg-[#22d3ee] px-3 py-2 text-sm font-medium text-white',
'rounded-md bg-primary px-3 py-2 text-sm font-medium text-white',
'hover:brightness-110'
)}
>
@@ -618,16 +618,16 @@ export function SessionHistoryPage() {
{closingSessionId === session.id && (
<div
ref={closePopoverRef}
className="absolute right-0 top-full z-20 mt-2 w-72 rounded-xl border border-[#1e2130] bg-[#14161d] p-4 shadow-xl"
className="absolute right-0 top-full z-20 mt-2 w-72 rounded-xl border border-border bg-card p-4 shadow-xl"
>
<p className="text-sm font-heading font-medium text-[#e2e5eb] mb-3">Close Session</p>
<p className="text-sm font-heading font-medium text-foreground mb-3">Close Session</p>
<label className="block text-xs font-sans text-xs text-[#848b9b] mb-1">Outcome</label>
<label className="block text-xs font-sans text-xs text-muted-foreground mb-1">Outcome</label>
<select
value={closeOutcome}
onChange={(e) => setCloseOutcome(e.target.value as SessionOutcome)}
title="Session outcome"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] focus:border-[rgba(6,182,212,0.3)] focus:outline-none mb-3"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none mb-3"
>
<option value="">Select outcome...</option>
<option value="resolved">Resolved</option>
@@ -638,13 +638,13 @@ export function SessionHistoryPage() {
<option value="resolved_externally">Resolved Externally</option>
</select>
<label className="block text-xs font-sans text-xs text-[#848b9b] mb-1">Notes (optional)</label>
<label className="block text-xs font-sans text-xs text-muted-foreground mb-1">Notes (optional)</label>
<textarea
value={closeNotes}
onChange={(e) => setCloseNotes(e.target.value)}
rows={2}
placeholder="Add closure notes..."
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none mb-3"
className="w-full rounded-lg 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 resize-none mb-3"
/>
<div className="flex items-center justify-end gap-2">
@@ -654,7 +654,7 @@ export function SessionHistoryPage() {
setCloseOutcome('')
setCloseNotes('')
}}
className="rounded-lg px-3 py-1.5 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-lg px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
Cancel
</button>
@@ -664,8 +664,8 @@ export function SessionHistoryPage() {
className={cn(
'rounded-lg px-4 py-1.5 text-sm font-medium transition-opacity',
closeOutcome
? 'bg-[#22d3ee] text-white hover:brightness-110'
: 'bg-[#22d3ee] text-white opacity-50 cursor-not-allowed'
? 'bg-primary text-white hover:brightness-110'
: 'bg-primary text-white opacity-50 cursor-not-allowed'
)}
>
{closeLoading ? 'Closing...' : 'Confirm'}
@@ -680,11 +680,11 @@ export function SessionHistoryPage() {
))}
</div>
{hasMore ? (
<p className="text-center text-sm text-[#848b9b] py-4">
<p className="text-center text-sm text-muted-foreground py-4">
Showing the 50 most recent sessions
</p>
) : sessions.length > 0 ? (
<p className="text-center text-sm text-[#848b9b] py-4">
<p className="text-center text-sm text-muted-foreground py-4">
Showing all {sessions.length} sessions
</p>
) : null}

View File

@@ -55,16 +55,16 @@ function ErrorCard({ error }: { error: ErrorState }) {
const Icon = iconMap[error.type]
return (
<div className="flex min-h-screen items-center justify-center bg-[#0c0d10] px-4">
<div className="bg-[#14161d] border border-[#1e2130] w-full max-w-md rounded-xl p-8 text-center">
<div className="flex min-h-screen items-center justify-center bg-background px-4">
<div className="bg-card border border-border w-full max-w-md rounded-xl p-8 text-center">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-accent">
<Icon className="h-8 w-8 text-[#848b9b]" />
<Icon className="h-8 w-8 text-muted-foreground" />
</div>
<h1 className="mb-2 text-xl font-heading font-semibold text-[#e2e5eb]">{titleMap[error.type]}</h1>
<p className="mb-6 text-sm text-[#848b9b]">{error.message}</p>
<h1 className="mb-2 text-xl font-heading font-semibold text-foreground">{titleMap[error.type]}</h1>
<p className="mb-6 text-sm text-muted-foreground">{error.message}</p>
<Link
to="/"
className="inline-block rounded-lg bg-[#22d3ee] px-6 py-2.5 text-sm font-medium text-white hover:brightness-110"
className="inline-block rounded-lg bg-primary px-6 py-2.5 text-sm font-medium text-white hover:brightness-110"
>
Go to ResolutionFlow
</Link>
@@ -144,10 +144,10 @@ export function SharedSessionPage() {
if (loading) {
return (
<div className="flex min-h-screen items-center justify-center bg-[#0c0d10]">
<div className="flex min-h-screen items-center justify-center bg-background">
<div className="flex flex-col items-center gap-4">
<Spinner />
<p className="text-sm text-[#848b9b]">Loading shared session...</p>
<p className="text-sm text-muted-foreground">Loading shared session...</p>
</div>
</div>
)
@@ -162,21 +162,21 @@ export function SharedSessionPage() {
}
return (
<div className="min-h-screen bg-[#0c0d10]">
<div className="min-h-screen bg-background">
<PageMeta
title={data ? `Shared Session - ${data.tree_name}` : 'Shared Session'}
description="View a shared troubleshooting session on ResolutionFlow"
/>
{/* Minimal header */}
<header className="border-b border-[#1e2130] px-6 py-4">
<header className="border-b border-border px-6 py-4">
<div className="mx-auto flex max-w-7xl items-center justify-between">
<Link to="/" className="flex items-center gap-2">
<BrandLogo size="sm" />
<span className="text-lg font-heading font-semibold text-[#e2e5eb]">ResolutionFlow</span>
<span className="text-lg font-heading font-semibold text-foreground">ResolutionFlow</span>
</Link>
<Link
to="/login"
className="rounded-lg border border-[#1e2130] px-4 py-2 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-lg border border-border px-4 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
Sign In
</Link>
@@ -188,14 +188,14 @@ export function SharedSessionPage() {
{/* Metadata section */}
<div className="mb-8">
{data.share_name && (
<h1 className="mb-2 text-2xl font-heading font-bold text-[#e2e5eb]">{data.share_name}</h1>
<h1 className="mb-2 text-2xl font-heading font-bold text-foreground">{data.share_name}</h1>
)}
<p className="text-lg text-[#848b9b]">
<span className="text-[#848b9b]">Tree:</span> {data.tree_name}
<p className="text-lg text-muted-foreground">
<span className="text-muted-foreground">Tree:</span> {data.tree_name}
</p>
{(data.ticket_number || data.client_name) && (
<p className="mt-1 text-sm text-[#848b9b]">
<p className="mt-1 text-sm text-muted-foreground">
{data.ticket_number && (
<span>Ticket: #{data.ticket_number}</span>
)}
@@ -208,7 +208,7 @@ export function SharedSessionPage() {
</p>
)}
<div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-[#848b9b]">
<div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground">
<span>Started: {formatDate(data.started_at)}</span>
{data.completed_at && (
<>
@@ -256,9 +256,9 @@ export function SharedSessionPage() {
</main>
{/* Footer */}
<footer className="py-8 text-center text-sm text-[#848b9b]">
<footer className="py-8 text-center text-sm text-muted-foreground">
Powered by{' '}
<Link to="/" className="underline hover:text-[#e2e5eb]">
<Link to="/" className="underline hover:text-foreground">
ResolutionFlow
</Link>
</footer>

View File

@@ -92,14 +92,14 @@ export default function StepLibraryPage() {
<PageMeta title="Step Library" />
<div className="flex h-full flex-col">
{/* Page Header */}
<div className="flex items-center justify-between border-b border-[#1e2130] px-6 py-4">
<div className="flex items-center justify-between border-b border-border px-6 py-4">
<div className="flex items-center gap-3">
<span title="Step Library">
<Bookmark className="h-6 w-6 text-[#848b9b]" />
<Bookmark className="h-6 w-6 text-muted-foreground" />
</span>
<div>
<h1 className="text-xl font-bold font-heading text-[#e2e5eb]">Step Library</h1>
<p className="text-sm text-[#848b9b]">Reusable steps you can insert into any flow</p>
<h1 className="text-xl font-bold font-heading text-foreground">Step Library</h1>
<p className="text-sm text-muted-foreground">Reusable steps you can insert into any flow</p>
</div>
</div>
{canCreateSteps && (
@@ -132,18 +132,18 @@ export default function StepLibraryPage() {
{/* Delete Confirmation Dialog */}
{deletingStep && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4">
<div className="w-full max-w-sm rounded-xl bg-[#14161d] border border-[#1e2130] p-6 shadow-lg">
<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">
<Trash2 className="h-5 w-5 text-red-400" />
</div>
<h2 className="text-base font-semibold text-[#e2e5eb]">Delete Step</h2>
<h2 className="text-base font-semibold text-foreground">Delete Step</h2>
</div>
<p className="mb-2 text-sm text-[#848b9b]">
<p className="mb-2 text-sm text-muted-foreground">
Are you sure you want to delete{' '}
<span className="font-medium text-[#e2e5eb]">"{deletingStep.title}"</span>?
<span className="font-medium text-foreground">"{deletingStep.title}"</span>?
</p>
<p className="mb-6 text-xs text-[#848b9b]">This cannot be undone.</p>
<p className="mb-6 text-xs text-muted-foreground">This cannot be undone.</p>
{deleteError && (
<p className="mb-4 text-sm text-red-400">{deleteError}</p>
)}

View File

@@ -264,15 +264,15 @@ export default function SurveyPage() {
if (tokenLoading) {
return (
<div className="min-h-screen bg-[#0c0d10] flex items-center justify-center">
<div className="text-[#848b9b] text-sm">Loading...</div>
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-muted-foreground text-sm">Loading...</div>
</div>
)
}
if (alreadyCompleted) {
return (
<div className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div className="min-h-screen bg-background text-foreground">
<div className="pointer-events-none fixed inset-0 overflow-hidden" aria-hidden="true">
<div className="absolute -top-32 right-0 h-[500px] w-[500px] rounded-full opacity-[0.03]" style={{ background: 'radial-gradient(circle, var(--color-primary), transparent 70%)' }} />
<div className="absolute -bottom-32 left-0 h-[400px] w-[400px] rounded-full opacity-[0.02]" style={{ background: 'radial-gradient(circle, #a855f7, transparent 70%)' }} />
@@ -283,16 +283,16 @@ export default function SurveyPage() {
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" strokeWidth="2.5"><path d="M20 6L9 17l-5-5"/></svg>
</div>
<h2 className="font-heading text-2xl font-bold mb-2.5">Already Submitted</h2>
<p className="text-[#848b9b] text-sm max-w-[440px] mx-auto leading-relaxed mb-3">
<p className="text-muted-foreground text-sm max-w-[440px] mx-auto leading-relaxed mb-3">
{inviteName ? `Thanks ${inviteName} — y` : 'Y'}our response has already been recorded. We appreciate your time!
</p>
<p className="text-sm text-[#848b9b]/70 max-w-[400px] mx-auto leading-relaxed mb-8">
<p className="text-sm text-muted-foreground/70 max-w-[400px] mx-auto leading-relaxed mb-8">
You can safely close this browser window now.
</p>
<div className="card-flat p-4 sm:p-5 max-w-[400px] mx-auto text-center">
<p className="text-xs text-[#848b9b] leading-relaxed">
<p className="text-xs text-muted-foreground leading-relaxed">
Have feedback unrelated to the survey?{' '}
<a href="mailto:feedback@resolutionflow.com" className="text-[#22d3ee] hover:underline font-medium">
<a href="mailto:feedback@resolutionflow.com" className="text-primary hover:underline font-medium">
feedback@resolutionflow.com
</a>
</p>
@@ -304,7 +304,7 @@ export default function SurveyPage() {
}
return (
<div ref={topRef} className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div ref={topRef} className="min-h-screen bg-background text-foreground">
<PageMeta
title="Product Survey"
description="Help shape the future of ResolutionFlow by sharing your feedback"
@@ -324,15 +324,15 @@ export default function SurveyPage() {
{/* Top bar */}
<div className="sticky top-0 z-50" style={{ WebkitBackdropFilter: 'blur(20px)', background: 'rgba(16, 17, 20, 0.85)', borderBottom: '1px solid var(--glass-border)' }}>
<div className="mx-auto flex max-w-[680px] items-center justify-between gap-3 px-4 py-3 sm:px-5 sm:py-3.5">
<a href="https://resolutionflow.com" target="_blank" rel="noreferrer" className="flex items-center gap-2 sm:gap-2.5 text-sm font-heading font-bold text-[#848b9b] no-underline shrink-0">
<a href="https://resolutionflow.com" target="_blank" rel="noreferrer" className="flex items-center gap-2 sm:gap-2.5 text-sm font-heading font-bold text-muted-foreground no-underline shrink-0">
<BrandLogo size="sm" />
<span className="hidden sm:inline">Resolution<span className="text-[#67e8f9]">Flow</span></span>
<span className="hidden sm:inline">Resolution<span className="text-accent-text">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: 'var(--color-border)' }}>
<div className="h-full rounded-full bg-[#22d3ee] transition-[width] duration-500" style={{ width: `${progressPct}%` }} />
<div className="h-full rounded-full bg-primary transition-[width] duration-500" style={{ width: `${progressPct}%` }} />
</div>
<span className="text-[11px] font-sans text-xs text-[#848b9b] whitespace-nowrap tabular-nums">{answeredCount}/{TOTAL_QUESTIONS}</span>
<span className="text-[11px] font-sans text-xs text-muted-foreground whitespace-nowrap tabular-nums">{answeredCount}/{TOTAL_QUESTIONS}</span>
</div>
</div>
</div>
@@ -346,12 +346,12 @@ export default function SurveyPage() {
FlowPilot Research
</div>
<h1 className="font-heading text-[clamp(24px,5vw,36px)] font-extrabold leading-tight mb-3">
Help Build an AI That<br/>Thinks Like <span className="text-[#67e8f9]">You</span>
Help Build an AI That<br/>Thinks Like <span className="text-accent-text">You</span>
</h1>
<p className="text-[14px] sm:text-[15px] text-[#848b9b] max-w-[500px] mx-auto leading-relaxed">
<p className="text-[14px] sm:text-[15px] text-muted-foreground max-w-[500px] mx-auto leading-relaxed">
We're building an AI assistant for MSP engineers. Your expertise shapes how it thinks. Takes about 5 minutes.
</p>
<div className="flex flex-wrap justify-center gap-4 sm:gap-7 mt-4 sm:mt-5 text-[11px] sm:text-[12px] text-[#848b9b]">
<div className="flex flex-wrap justify-center gap-4 sm:gap-7 mt-4 sm:mt-5 text-[11px] sm:text-[12px] text-muted-foreground">
<span className="flex items-center gap-1.5">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
~5 minutes
@@ -393,18 +393,18 @@ export default function SurveyPage() {
))}
<div className="flex justify-between mt-6 sm:mt-7 gap-3">
{si > 0 ? (
<button onClick={() => goSlide(si - 1)} className="inline-flex items-center gap-2 px-4 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold text-[#848b9b] transition-all duration-150 hover:text-[#e2e5eb]" style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)' }}>
<button onClick={() => goSlide(si - 1)} className="inline-flex items-center gap-2 px-4 py-2.5 sm:px-6 sm:py-3 rounded-lg text-sm font-semibold text-muted-foreground transition-all duration-150 hover:text-foreground" style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)' }}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg>
Back
</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-lg text-sm font-semibold bg-[#22d3ee] text-white 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-primary text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98]">
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-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">
<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-primary text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98] 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>
@@ -413,7 +413,7 @@ export default function SurveyPage() {
{submitError && (
<div className="mt-4 p-3 rounded-lg text-sm text-rose-400" style={{ background: 'rgba(244, 63, 94, 0.1)', border: '1px solid rgba(244, 63, 94, 0.2)' }}>
{submitError}
<button onClick={copyAll} className="ml-3 underline text-[#848b9b] hover:text-[#e2e5eb]">Copy responses instead</button>
<button onClick={copyAll} className="ml-3 underline text-muted-foreground hover:text-foreground">Copy responses instead</button>
</div>
)}
</div>
@@ -427,13 +427,13 @@ export default function SurveyPage() {
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="oklch(0.76 0.15 163)" strokeWidth="2.5"><path d="M20 6L9 17l-5-5"/></svg>
</div>
<h2 className="font-heading text-2xl font-bold mb-2.5">Response Submitted!</h2>
<p className="text-[#848b9b] text-sm max-w-[440px] mx-auto mb-6 sm:mb-8 leading-relaxed">
<p className="text-muted-foreground text-sm max-w-[440px] mx-auto mb-6 sm:mb-8 leading-relaxed">
Your answers will directly shape how FlowPilot troubleshoots. Would you like a copy of your responses?
</p>
{/* Email a copy */}
<div className="card-flat p-4 sm:p-6 max-w-[420px] mx-auto mb-5">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-3">
<p className="font-sans text-xs 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-[#e2e5eb] placeholder:text-[#4f5666] focus:outline-hidden"
className="flex-1 rounded-[9px] px-3.5 py-2.5 text-sm text-foreground placeholder:text-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 = 'var(--color-primary)' }}
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-[#22d3ee] text-white 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-primary text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98] disabled:opacity-40 disabled:cursor-not-allowed whitespace-nowrap"
>
{emailSending ? (
<>
@@ -496,13 +496,13 @@ export default function SurveyPage() {
{/* Copy + Finish buttons */}
<div className="flex gap-2.5 justify-center flex-wrap">
<button onClick={copyAll} className="inline-flex items-center gap-2 px-4 py-2.5 sm:px-5 rounded-lg text-sm font-semibold transition-all duration-150 text-[#848b9b] hover:text-[#e2e5eb]" style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)' }}>
<button onClick={copyAll} className="inline-flex items-center gap-2 px-4 py-2.5 sm:px-5 rounded-lg text-sm font-semibold transition-all duration-150 text-muted-foreground hover:text-foreground" style={{ background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)' }}>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
Copy to Clipboard
</button>
<button
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-white 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-primary text-white transition-all duration-150 hover:brightness-110 active:scale-[0.98]"
>
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>
@@ -522,12 +522,12 @@ function ScenarioBox({ scenario }: { scenario: { title: string; symptom: string;
<div className="rounded-lg p-3.5 px-4 sm:p-4 sm:px-5 mb-4 text-[13px]" style={{ background: 'linear-gradient(135deg, rgba(6, 182, 212, 0.06), rgba(139, 92, 246, 0.03))', border: '1px solid color-mix(in srgb, var(--color-primary) 12%, transparent)' }}>
<div className="font-sans text-xs text-[10px] uppercase tracking-widest mb-2 font-semibold" style={{ color: 'var(--color-primary)' }}>{scenario.title}</div>
<div className="sm:flex gap-2 mb-1">
<span className="text-[#848b9b] font-medium whitespace-nowrap">Symptom:</span>
<span className="text-[#848b9b]/80">{scenario.symptom}</span>
<span className="text-muted-foreground font-medium whitespace-nowrap">Symptom:</span>
<span className="text-muted-foreground/80">{scenario.symptom}</span>
</div>
<div className="sm:flex gap-2">
<span className="text-[#848b9b] font-medium whitespace-nowrap">Details:</span>
<span className="text-[#848b9b]/80">{scenario.details}</span>
<span className="text-muted-foreground font-medium whitespace-nowrap">Details:</span>
<span className="text-muted-foreground/80">{scenario.details}</span>
</div>
</div>
)
@@ -537,8 +537,8 @@ function QuestionCard({ question: q, answer, setAnswer }: { question: SurveyQues
return (
<div className="card-flat p-4 sm:p-7 mb-3 sm:mb-4 transition-[border-color] duration-200 focus-within:border-primary/25!">
<div className="font-sans text-xs text-[11px] mb-1.5 font-medium" style={{ color: 'var(--color-primary)' }}>Q{q.num}</div>
<div className="font-heading text-[14px] sm:text-[15px] font-semibold text-[#e2e5eb] leading-snug mb-1">{q.text}</div>
{q.hint && <div className="text-[12px] text-[#848b9b] mb-3 sm:mb-4 leading-snug">{q.hint}</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>}
{!q.hint && <div className="mb-3 sm:mb-4" />}
{q.type === 'mc' && q.options && (
@@ -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-[#e2e5eb] leading-relaxed resize-y transition-all duration-200 placeholder:text-[#4f5666] focus:outline-hidden"
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-text-muted focus:outline-hidden"
style={{
background: 'rgba(16, 17, 20, 0.6)',
border: '1px solid var(--glass-border)',
@@ -636,7 +636,7 @@ function RangeInput({ question: q, value, onChange }: { question: SurveyQuestion
background: `linear-gradient(to right, var(--color-primary) 0%, var(--color-primary) ${((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-[#848b9b] mt-2.5">
<div className="flex justify-between text-[11px] text-muted-foreground mt-2.5">
<span>{q.low_label}</span>
<span>{q.high_label}</span>
</div>
@@ -736,7 +736,7 @@ function DragRank({ items, onChange }: { items: string[]; onChange: (items: stri
color: 'var(--color-muted-foreground)',
}}
>
<div className="shrink-0 text-[#4f5666]">
<div className="shrink-0 text-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-sans text-xs text-[11px] font-semibold w-5 text-center shrink-0" style={{ color: 'var(--color-primary)' }}>{idx + 1}</div>

View File

@@ -5,7 +5,7 @@ export default function SurveyThankYouPage() {
return (
<>
<PageMeta title="Thank You" description="Thank you for your feedback on ResolutionFlow" />
<div className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div className="min-h-screen bg-background text-foreground">
{/* Atmosphere orbs */}
<div className="pointer-events-none fixed inset-0 z-0 overflow-hidden" aria-hidden="true">
<div
@@ -21,9 +21,9 @@ export default function SurveyThankYouPage() {
{/* Top bar */}
<div className="sticky top-0 z-50" style={{ WebkitBackdropFilter: 'blur(20px)', background: 'rgba(16, 17, 20, 0.85)', borderBottom: '1px solid var(--glass-border)' }}>
<div className="mx-auto flex max-w-[680px] items-center justify-between gap-3 px-5 py-3.5">
<a href="https://resolutionflow.com" target="_blank" rel="noreferrer" className="flex items-center gap-2.5 text-sm font-heading font-bold text-[#848b9b] no-underline">
<a href="https://resolutionflow.com" target="_blank" rel="noreferrer" className="flex items-center gap-2.5 text-sm font-heading font-bold text-muted-foreground no-underline">
<BrandLogo size="sm" />
<span>Resolution<span className="text-[#67e8f9]">Flow</span></span>
<span>Resolution<span className="text-accent-text">Flow</span></span>
</a>
</div>
</div>
@@ -40,10 +40,10 @@ export default function SurveyThankYouPage() {
<h1 className="font-heading text-[clamp(24px,4vw,32px)] font-extrabold leading-tight mb-3">
Thank You!
</h1>
<p className="text-[15px] text-[#848b9b] max-w-[460px] mx-auto leading-relaxed mb-3">
<p className="text-[15px] text-muted-foreground max-w-[460px] mx-auto leading-relaxed mb-3">
Your response has been recorded. Your expertise will directly shape how FlowPilot thinks about troubleshooting.
</p>
<p className="text-sm text-[#848b9b]/70 max-w-[400px] mx-auto leading-relaxed mb-10">
<p className="text-sm text-muted-foreground/70 max-w-[400px] mx-auto leading-relaxed mb-10">
You can safely close this browser window now.
</p>
@@ -58,15 +58,15 @@ export default function SurveyThankYouPage() {
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" 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-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#22d3ee] font-semibold">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-primary font-semibold">
Have Feedback?
</span>
</div>
<p className="text-sm text-[#848b9b] leading-relaxed">
<p className="text-sm text-muted-foreground leading-relaxed">
If you have any feedback unrelated to the survey, we'd love to hear from you at{' '}
<a
href="mailto:feedback@resolutionflow.com"
className="text-[#22d3ee] hover:underline font-medium"
className="text-primary hover:underline font-medium"
>
feedback@resolutionflow.com
</a>

View File

@@ -89,7 +89,7 @@ export default function TeamAnalyticsPage() {
action={
<Link
to="/trees"
className="inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-white hover:brightness-110 active:scale-[0.98] transition-all"
>
Run Your First Session
</Link>
@@ -108,22 +108,22 @@ export default function TeamAnalyticsPage() {
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<span title="Team Analytics">
<BarChart3 size={24} className="text-[#e2e5eb]" />
<BarChart3 size={24} className="text-foreground" />
</span>
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb]">Team Analytics</h1>
<h1 className="text-2xl font-bold font-heading text-foreground">Team Analytics</h1>
</div>
<div className="flex items-center gap-3">
<Link
to="/analytics/me"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
My Stats
</Link>
<select
value={period}
onChange={(e) => setPeriod(e.target.value as AnalyticsPeriod)}
className="rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] focus:outline-hidden 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}>
@@ -159,8 +159,8 @@ export default function TeamAnalyticsPage() {
</div>
{/* Area Chart — Sessions over Time */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
Sessions Over Time
</h2>
<ResponsiveContainer width="100%" height={300}>
@@ -246,7 +246,7 @@ export default function TeamAnalyticsPage() {
className="h-2.5 w-2.5 rounded-full"
style={{ backgroundColor: color }}
/>
<span className="text-xs text-[#848b9b] capitalize">
<span className="text-xs text-muted-foreground capitalize">
{key}
</span>
</div>
@@ -257,27 +257,27 @@ export default function TeamAnalyticsPage() {
{/* Two-Column: Top Flows & Top Engineers */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Top Flows */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
Top Flows
</h2>
{top_flows.length === 0 ? (
<p className="text-sm text-[#848b9b]">No flow data for this period.</p>
<p className="text-sm text-muted-foreground">No flow data for this period.</p>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1e2130]">
<th className="text-left py-2 text-[#e2e5eb] font-medium">
<tr className="border-b border-border">
<th className="text-left py-2 text-foreground font-medium">
Name
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Sessions
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Completion
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Median
</th>
</tr>
@@ -286,18 +286,18 @@ export default function TeamAnalyticsPage() {
{top_flows.map((flow) => (
<tr
key={flow.tree_id}
className="border-b border-[#1e2130] last:border-0"
className="border-b border-border last:border-0"
>
<td className="py-2 text-[#848b9b] truncate max-w-[200px]">
<td className="py-2 text-muted-foreground truncate max-w-[200px]">
{flow.name}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{flow.sessions}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{(flow.completion_rate * 100).toFixed(1)}%
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{flow.median_duration_minutes} min
</td>
</tr>
@@ -309,27 +309,27 @@ export default function TeamAnalyticsPage() {
</div>
{/* Top Engineers */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="text-sm font-semibold text-[#e2e5eb] mb-4">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="text-sm font-semibold text-foreground mb-4">
Top Engineers
</h2>
{top_engineers.length === 0 ? (
<p className="text-sm text-[#848b9b]">No engineer data for this period.</p>
<p className="text-sm text-muted-foreground">No engineer data for this period.</p>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1e2130]">
<th className="text-left py-2 text-[#e2e5eb] font-medium">
<tr className="border-b border-border">
<th className="text-left py-2 text-foreground font-medium">
Name
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Sessions
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Completion
</th>
<th className="text-right py-2 text-[#e2e5eb] font-medium">
<th className="text-right py-2 text-foreground font-medium">
Median
</th>
</tr>
@@ -338,18 +338,18 @@ export default function TeamAnalyticsPage() {
{top_engineers.map((eng) => (
<tr
key={eng.user_id}
className="border-b border-[#1e2130] last:border-0"
className="border-b border-border last:border-0"
>
<td className="py-2 text-[#848b9b] truncate max-w-[200px]">
<td className="py-2 text-muted-foreground truncate max-w-[200px]">
{eng.name}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{eng.sessions}
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{(eng.completion_rate * 100).toFixed(1)}%
</td>
<td className="py-2 text-right text-[#848b9b]">
<td className="py-2 text-right text-muted-foreground">
{eng.median_duration_minutes} min
</td>
</tr>
@@ -374,12 +374,12 @@ function StatCard({
value: string
}) {
return (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<div className="bg-card border border-border rounded-xl p-6">
<div className="flex items-center gap-2 mb-2">
<Icon size={16} className="text-[#848b9b]" />
<span className="text-sm text-[#848b9b]">{label}</span>
<Icon size={16} className="text-muted-foreground" />
<span className="text-sm text-muted-foreground">{label}</span>
</div>
<p className="text-3xl font-bold text-[#e2e5eb]">{value}</p>
<p className="text-3xl font-bold text-foreground">{value}</p>
</div>
)
}

View File

@@ -5,41 +5,41 @@ export default function TermsPage() {
return (
<>
<PageMeta title="Terms of Service" description="ResolutionFlow Terms of Service" />
<div className="min-h-screen bg-[#0c0d10] text-[#e2e5eb]">
<div className="min-h-screen bg-background text-foreground">
<div className="mx-auto max-w-3xl px-6 py-16">
<Link to="/landing" className="text-sm text-[#848b9b] hover:text-[#e2e5eb] mb-8 inline-block">&larr; Back to home</Link>
<Link to="/landing" className="text-sm text-muted-foreground hover:text-foreground mb-8 inline-block">&larr; Back to home</Link>
<h1 className="text-3xl font-bold font-heading mb-8">Terms of Service</h1>
<p className="text-[#848b9b] mb-6">Last updated: March 21, 2026</p>
<p className="text-muted-foreground mb-6">Last updated: March 21, 2026</p>
<div className="space-y-6 text-[#848b9b] leading-relaxed">
<div className="space-y-6 text-muted-foreground leading-relaxed">
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">1. Acceptance of Terms</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">1. Acceptance of Terms</h2>
<p>By accessing or using ResolutionFlow, you agree to be bound by these Terms of Service. If you are using ResolutionFlow on behalf of an organization, you represent that you have authority to bind that organization to these terms.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">2. Description of Service</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">2. Description of Service</h2>
<p>ResolutionFlow provides AI-guided troubleshooting decision trees, automated documentation generation, and PSA integration tools for managed service providers (MSPs). The service includes FlowPilot AI copilot, flow editor, session management, and team collaboration features.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">3. Account Responsibilities</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">3. Account Responsibilities</h2>
<p>You are responsible for maintaining the security of your account credentials and for all activities that occur under your account. You must notify us immediately of any unauthorized use.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">4. Data Ownership</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">4. Data Ownership</h2>
<p>You retain ownership of all content you create within ResolutionFlow, including flows, session documentation, and team knowledge. We do not claim ownership of your data.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">5. Service Availability</h2>
<h2 className="text-xl font-semibold text-foreground mb-3">5. Service Availability</h2>
<p>ResolutionFlow is currently in beta. We strive for high availability but do not guarantee uninterrupted access during the beta period. We will provide reasonable notice of planned maintenance.</p>
</section>
<section>
<h2 className="text-xl font-semibold text-[#e2e5eb] mb-3">6. Contact</h2>
<p>Questions about these terms? Email us at <a href="mailto:hello@resolutionflow.com" className="text-[#22d3ee] hover:underline">hello@resolutionflow.com</a>.</p>
<h2 className="text-xl font-semibold text-foreground mb-3">6. Contact</h2>
<p>Questions about these terms? Email us at <a href="mailto:hello@resolutionflow.com" className="text-primary hover:underline">hello@resolutionflow.com</a>.</p>
</section>
</div>
</div>

View File

@@ -509,9 +509,9 @@ export function TreeEditorPage() {
if (isMobile) {
return (
<div className="flex h-full flex-col items-center justify-center px-6 text-center">
<Monitor className="mb-4 h-12 w-12 text-[#848b9b]" />
<h2 className="mb-2 text-xl font-heading font-semibold text-[#e2e5eb]">Desktop Required</h2>
<p className="mb-6 max-w-sm text-sm text-[#848b9b]">
<Monitor className="mb-4 h-12 w-12 text-muted-foreground" />
<h2 className="mb-2 text-xl font-heading font-semibold text-foreground">Desktop Required</h2>
<p className="mb-6 max-w-sm text-sm text-muted-foreground">
The tree editor requires a larger screen for the best experience. Please open this page on a desktop or tablet in landscape mode.
</p>
<Button onClick={() => navigate('/trees')}>
@@ -529,9 +529,9 @@ export function TreeEditorPage() {
{/* Draft Restore Prompt */}
{showDraftPrompt && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl w-full max-w-md p-6 shadow-lg">
<h2 className="mb-2 text-lg font-heading font-semibold text-[#e2e5eb]">Restore Draft?</h2>
<p className="mb-4 text-sm text-[#848b9b]">
<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">
You have an unsaved draft from a previous session. Would you like to restore it?
</p>
<div className="flex gap-2">
@@ -549,9 +549,9 @@ export function TreeEditorPage() {
{/* Unsaved Changes Dialog */}
{blocker.state === 'blocked' && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl w-full max-w-md p-6 shadow-lg">
<h2 className="mb-2 text-lg font-heading font-semibold text-[#e2e5eb]">Unsaved Changes</h2>
<p className="mb-4 text-sm text-[#848b9b]">
<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">
You have unsaved changes. Are you sure you want to leave?
</p>
<div className="flex gap-2">
@@ -567,17 +567,17 @@ export function TreeEditorPage() {
)}
{/* Toolbar */}
<div className="flex items-center justify-between border-b border-[#1e2130] bg-[#14161d] px-4 py-2">
<div className="flex items-center justify-between border-b border-border bg-card px-4 py-2">
<div className="flex items-center gap-4">
<button
onClick={() => navigate('/trees')}
className="text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="text-sm text-muted-foreground hover:text-foreground"
>
Back to Library
</button>
<h1 className="text-lg font-heading font-semibold text-[#e2e5eb]">
<h1 className="text-lg font-heading font-semibold text-foreground">
{isEditMode ? 'Edit Tree' : 'Create New Tree'}
{name && <span className="ml-2 text-[#848b9b]">- {name}</span>}
{name && <span className="ml-2 text-muted-foreground">- {name}</span>}
</h1>
<div className="flex items-center gap-2">
{treeStatus === 'draft' && (
@@ -596,7 +596,7 @@ export function TreeEditorPage() {
<div className="flex items-center gap-2">
{/* Mode Toggle */}
<div className="flex items-center rounded-md border border-[#1e2130]">
<div className="flex items-center rounded-md border border-border">
<button
type="button"
onClick={() => setEditorMode('form')}
@@ -604,8 +604,8 @@ export function TreeEditorPage() {
className={cn(
'flex items-center gap-1.5 rounded-l-md px-3 py-1.5 text-xs font-medium transition-colors',
editorMode === 'form'
? 'bg-accent text-[#e2e5eb]'
: 'text-[#848b9b] hover:bg-accent/50 hover:text-[#e2e5eb]'
? 'bg-accent text-foreground'
: 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
)}
>
<LayoutList className="h-3.5 w-3.5" />
@@ -623,8 +623,8 @@ export function TreeEditorPage() {
className={cn(
'flex items-center gap-1.5 rounded-r-md px-3 py-1.5 text-xs font-medium transition-colors',
editorMode === 'code'
? 'bg-accent text-[#e2e5eb]'
: 'text-[#848b9b] hover:bg-accent/50 hover:text-[#e2e5eb]'
? 'bg-accent text-foreground'
: 'text-muted-foreground hover:bg-accent/50 hover:text-foreground'
)}
>
<Code2 className="h-3.5 w-3.5" />
@@ -635,7 +635,7 @@ export function TreeEditorPage() {
<div className="mx-1 h-6 w-px bg-border" />
{/* Undo/Redo */}
<div className="flex items-center rounded-md border border-[#1e2130]">
<div className="flex items-center rounded-md border border-border">
<button
type="button"
onClick={handleUndo}
@@ -644,8 +644,8 @@ export function TreeEditorPage() {
className={cn(
'rounded-l-md p-2 transition-colors',
pastStates.length > 0
? 'text-[#e2e5eb] hover:bg-accent/50 active:bg-accent'
: 'text-[#848b9b]/50 cursor-not-allowed'
? 'text-foreground hover:bg-accent/50 active:bg-accent'
: 'text-muted-foreground/50 cursor-not-allowed'
)}
>
<Undo2 className="h-4 w-4" />
@@ -659,8 +659,8 @@ export function TreeEditorPage() {
className={cn(
'rounded-r-md p-2 transition-colors',
futureStates.length > 0
? 'text-[#e2e5eb] hover:bg-accent/50 active:bg-accent'
: 'text-[#848b9b]/50 cursor-not-allowed'
? 'text-foreground hover:bg-accent/50 active:bg-accent'
: 'text-muted-foreground/50 cursor-not-allowed'
)}
>
<Redo2 className="h-4 w-4" />
@@ -681,10 +681,10 @@ export function TreeEditorPage() {
}}
title="Edit flow metadata (name, description, category, tags)"
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium transition-colors',
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors',
isMetadataOpen
? 'bg-accent text-[#e2e5eb]'
: 'bg-[#14161d] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]'
? 'bg-accent text-foreground'
: 'bg-card text-muted-foreground hover:bg-accent hover:text-foreground'
)}
>
<Settings className="h-4 w-4" />
@@ -698,10 +698,10 @@ export function TreeEditorPage() {
onClick={() => setShowAnalytics(!showAnalytics)}
title="Toggle flow analytics panel"
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium transition-colors',
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors',
showAnalytics
? 'bg-accent text-[#e2e5eb]'
: 'bg-[#14161d] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]'
? 'bg-accent text-foreground'
: 'bg-card text-muted-foreground hover:bg-accent hover:text-foreground'
)}
>
<BarChart3 className="h-4 w-4" />
@@ -715,8 +715,8 @@ export function TreeEditorPage() {
onClick={() => setShowExportModal(true)}
title="Export flow as .rfflow file"
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]'
'flex items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
>
<Download className="h-4 w-4" />
@@ -739,10 +739,10 @@ export function TreeEditorPage() {
}}
title="Toggle AI Assist panel"
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] px-3 py-2 text-sm font-medium transition-colors',
'flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors',
editorAI.isOpen
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border-primary/30'
: 'bg-[#14161d] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]'
? 'bg-accent-dim text-primary border-primary/30'
: 'bg-card text-muted-foreground hover:bg-accent hover:text-foreground'
)}
>
<Sparkles className="h-4 w-4" />
@@ -754,8 +754,8 @@ export function TreeEditorPage() {
disabled={isSaving}
title="Validate tree structure (checks for errors and warnings)"
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb] disabled:opacity-50'
'flex items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground disabled:opacity-50'
)}
>
<CheckCircle2 className="h-4 w-4" />
@@ -800,7 +800,7 @@ export function TreeEditorPage() {
{/* Import provenance */}
{importMetadata && (
<div className="mx-4 mb-2 flex items-center gap-2 text-xs font-sans text-xs text-[#848b9b]">
<div className="mx-4 mb-2 flex items-center gap-2 text-xs font-sans text-xs text-muted-foreground">
<FileText className="h-3 w-3" />
<span>
Imported{importMetadata.original_author_name ? ` from ${importMetadata.original_author_name}` : ''}
@@ -825,7 +825,7 @@ export function TreeEditorPage() {
{/* Flow Analytics Panel (collapsible) */}
{showAnalytics && id && (
<div className="border-t border-[#1e2130] p-6 overflow-y-auto max-h-[50vh]">
<div className="border-t border-border p-6 overflow-y-auto max-h-[50vh]">
<FlowAnalyticsPanel treeId={id} />
</div>
)}

View File

@@ -291,10 +291,10 @@ export function TreeLibraryPage() {
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
<div className="mb-6 flex flex-col gap-4 sm:mb-8 sm:flex-row sm:items-start sm:justify-between">
<div>
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">
{typeFilter === 'procedural' ? 'Projects' : typeFilter === 'troubleshooting' ? 'Troubleshooting Flows' : typeFilter === 'maintenance' ? 'Maintenance Flows' : 'Flow Library'}
</h1>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
{typeFilter === 'procedural'
? 'Step-by-step projects and runbooks'
: typeFilter === 'troubleshooting'
@@ -333,8 +333,8 @@ export function TreeLibraryPage() {
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
className={cn(
'flex-1 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'flex-1 rounded-md 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'
)}
/>
@@ -348,8 +348,8 @@ export function TreeLibraryPage() {
onChange={(e) => setSelectedCategoryId(e.target.value)}
aria-label="Filter by category"
className={cn(
'rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
'rounded-md border border-border bg-card px-3 py-2',
'text-foreground focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)}
>
<option value="">All Categories</option>
@@ -364,7 +364,7 @@ export function TreeLibraryPage() {
{/* View Controls */}
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
{/* Type filter tabs */}
<div className="flex rounded-lg border border-[#1e2130] p-0.5">
<div className="flex rounded-lg border border-border p-0.5">
{(['all', 'troubleshooting', 'procedural', 'maintenance'] as const).map((t) => (
<button
key={t}
@@ -372,8 +372,8 @@ export function TreeLibraryPage() {
className={cn(
'rounded-md px-3 py-1 text-xs font-medium transition-colors',
typeFilter === t
? 'bg-accent text-[#e2e5eb]'
: 'text-[#848b9b] hover:text-[#e2e5eb]'
? 'bg-accent text-foreground'
: 'text-muted-foreground hover:text-foreground'
)}
>
{t === 'all' ? 'All' : t === 'troubleshooting' ? 'Troubleshooting' : t === 'procedural' ? 'Projects' : 'Maintenance'}
@@ -392,9 +392,9 @@ export function TreeLibraryPage() {
{/* Active Filters */}
{hasActiveFilters && (
<div className="mb-6 flex flex-wrap items-center gap-2">
<span className="text-sm text-[#848b9b]">Filters:</span>
<span className="text-sm text-muted-foreground">Filters:</span>
{selectedFolderId && (
<span className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-[#e2e5eb]">
<span className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-foreground">
Folder
<button
onClick={() => setSelectedFolderId(null)}
@@ -405,7 +405,7 @@ export function TreeLibraryPage() {
</span>
)}
{selectedCategoryId && (
<span className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-[#e2e5eb]">
<span className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-foreground">
{categories.find((c) => c.id === selectedCategoryId)?.name}
<button
onClick={() => setSelectedCategoryId('')}
@@ -418,7 +418,7 @@ export function TreeLibraryPage() {
{selectedTags.map((tag) => (
<span
key={tag}
className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-[#e2e5eb]"
className="inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1 text-sm text-foreground"
>
{tag}
<button
@@ -431,7 +431,7 @@ export function TreeLibraryPage() {
))}
<button
onClick={clearAllFilters}
className="text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="text-sm text-muted-foreground hover:text-foreground"
>
Clear all
</button>
@@ -442,12 +442,12 @@ export function TreeLibraryPage() {
{visibleIncompleteSessions.length > 0 && (
<div className="mb-6 space-y-2">
{visibleIncompleteSessions.map(s => (
<div key={s.id} className="bg-[#14161d] border border-[#1e2130] flex items-center justify-between rounded-xl p-4">
<div key={s.id} className="bg-card border border-border flex items-center justify-between rounded-xl p-4">
<div className="min-w-0 flex-1">
<p className="truncate font-medium text-[#e2e5eb]">
<p className="truncate font-medium text-foreground">
{s.tree_snapshot?.name || 'Unknown tree'}
</p>
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
{s.client_name && `${s.client_name} · `}
{s.started_at ? `Started ${formatTimeAgo(s.started_at)}` : 'Not started'}
</p>
@@ -462,7 +462,7 @@ export function TreeLibraryPage() {
</Button>
<button
onClick={() => dismissSession(s.id)}
className="rounded-md p-1.5 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<X className="h-4 w-4" />
</button>
@@ -480,8 +480,8 @@ export function TreeLibraryPage() {
state: { prefillClientName: lastSessionData.client_name, prefillTicketNumber: lastSessionData.ticket_number },
})}
className={cn(
'flex items-center gap-2 rounded-lg border border-[#1e2130] px-4 py-2.5 text-sm text-[#848b9b]',
'hover:border-[#1e2130] hover:bg-accent hover:text-[#e2e5eb]'
'flex items-center gap-2 rounded-lg border border-border px-4 py-2.5 text-sm text-muted-foreground',
'hover:border-border hover:bg-accent hover:text-foreground'
)}
>
<RotateCcw className="h-4 w-4" />

View File

@@ -603,7 +603,7 @@ export function TreeNavigationPage() {
</div>
<button
onClick={() => navigate('/trees')}
className="mt-4 text-[#848b9b] hover:text-[#e2e5eb] hover:underline"
className="mt-4 text-muted-foreground hover:text-foreground hover:underline"
>
Back to trees
</button>
@@ -615,17 +615,17 @@ export function TreeNavigationPage() {
if (showMetadataForm) {
return (
<div className="container mx-auto max-w-lg px-4 py-8">
<h1 className="mb-2 text-2xl font-bold font-heading text-[#e2e5eb]">{tree.name}</h1>
<p className="mb-6 text-[#848b9b]">{tree.description}</p>
<h1 className="mb-2 text-2xl font-bold font-heading text-foreground">{tree.name}</h1>
<p className="mb-6 text-muted-foreground">{tree.description}</p>
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl space-y-4 p-6">
<h2 className="font-semibold text-[#e2e5eb]">Session Details</h2>
<p className="text-sm text-[#848b9b]">
<div className="bg-card border border-border rounded-xl space-y-4 p-6">
<h2 className="font-semibold text-foreground">Session Details</h2>
<p className="text-sm text-muted-foreground">
Optional: Add ticket and client info for easier tracking
</p>
<div>
<label className="block text-sm font-medium text-[#e2e5eb]">
<label className="block text-sm font-medium text-foreground">
Ticket Number
</label>
<input
@@ -634,15 +634,15 @@ export function TreeNavigationPage() {
onChange={(e) => setTicketNumber(e.target.value)}
placeholder="e.g., INC0012345"
className={cn(
'mt-1 block w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
</div>
<div>
<label className="block text-sm font-medium text-[#e2e5eb]">
<label className="block text-sm font-medium text-foreground">
Client Name
</label>
<input
@@ -651,8 +651,8 @@ export function TreeNavigationPage() {
onChange={(e) => setClientName(e.target.value)}
placeholder="e.g., Acme Corp"
className={cn(
'mt-1 block w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
@@ -685,9 +685,9 @@ export function TreeNavigationPage() {
<div className="mb-6 flex items-center justify-between">
<div>
<div className="flex items-center gap-3">
<h1 className="text-xl font-bold font-heading text-[#e2e5eb]">{tree.name}</h1>
<h1 className="text-xl font-bold font-heading text-foreground">{tree.name}</h1>
{timerDisplay && (
<span className="flex items-center gap-1.5 rounded-full bg-accent px-2 py-0.5 text-[0.6875rem] font-sans text-xs text-[#848b9b]">
<span className="flex items-center gap-1.5 rounded-full bg-accent px-2 py-0.5 text-[0.6875rem] font-sans text-xs text-muted-foreground">
<Clock className="h-4 w-4" />
{timerDisplay}
</span>
@@ -695,14 +695,14 @@ export function TreeNavigationPage() {
<button
type="button"
onClick={() => setShortcutsModalOpen(true)}
className="flex items-center justify-center rounded-full p-1 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex items-center justify-center rounded-full p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
title="Keyboard shortcuts"
>
<HelpCircle className="h-4 w-4" />
</button>
</div>
{(ticketNumber || clientName) && (
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
{ticketNumber && `Ticket: ${ticketNumber}`}
{ticketNumber && clientName && ' · '}
{clientName && `Client: ${clientName}`}
@@ -725,8 +725,8 @@ export function TreeNavigationPage() {
<button
onClick={() => setShowSharePopover(!showSharePopover)}
className={cn(
'flex items-center gap-1.5 rounded-md border border-[#1e2130] px-3 py-1.5 text-xs font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb] transition-colors'
'flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground transition-colors'
)}
>
<Copy className="h-3.5 w-3.5" />
@@ -734,7 +734,7 @@ export function TreeNavigationPage() {
<ChevronDown className="h-3 w-3" />
</button>
{showSharePopover && (
<div className="absolute right-0 mt-1 z-50 bg-[#14161d] border border-[#1e2130] rounded-xl p-1 min-w-[220px]">
<div className="absolute right-0 mt-1 z-50 bg-card border border-border rounded-xl p-1 min-w-[220px]">
{/* Copy Progress Summary */}
<button
onClick={() => {
@@ -743,8 +743,8 @@ export function TreeNavigationPage() {
}}
disabled={isCopyingForTicket}
className={cn(
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-[#848b9b] w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-[#e2e5eb] disabled:opacity-50'
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-muted-foreground w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-foreground disabled:opacity-50'
)}
>
{copiedForTicket ? <Check className="h-4 w-4 text-emerald-400" /> : <Clipboard className="h-4 w-4" />}
@@ -758,15 +758,15 @@ export function TreeNavigationPage() {
}}
disabled={isCopyingShareLink}
className={cn(
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-[#848b9b] w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-[#e2e5eb] disabled:opacity-50'
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-muted-foreground w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-foreground disabled:opacity-50'
)}
>
{copiedShareLink ? <Check className="h-4 w-4 text-emerald-400" /> : <Link2 className="h-4 w-4" />}
{isCopyingShareLink ? 'Loading...' : copiedShareLink ? 'Copied!' : 'Copy Share Link'}
</button>
{/* Divider */}
<div className="border-t border-[#1e2130] my-1" />
<div className="border-t border-border my-1" />
{/* Manage Share Links */}
<button
onClick={() => {
@@ -774,8 +774,8 @@ export function TreeNavigationPage() {
setShowShareModal(true)
}}
className={cn(
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-[#848b9b] w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-[#e2e5eb]'
'flex items-center gap-2 rounded-md px-3 py-2 text-sm text-muted-foreground w-full text-left cursor-pointer transition-colors',
'hover:bg-accent hover:text-foreground'
)}
>
<Settings className="h-4 w-4" />
@@ -786,7 +786,7 @@ export function TreeNavigationPage() {
</div>
<button
onClick={() => navigate('/sessions')}
className="rounded-md px-3 py-1.5 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
Exit
</button>
@@ -814,17 +814,17 @@ export function TreeNavigationPage() {
const truncatedLabel = label.length > 30 ? `${label.slice(0, 30)}...` : label
return (
<span key={nodeId} className="flex items-center gap-2 whitespace-nowrap">
{index > 0 && <span className="text-[#848b9b]"></span>}
{index > 0 && <span className="text-muted-foreground"></span>}
{index < pathTaken.length - 1 ? (
<button
type="button"
onClick={() => handleBreadcrumbJump(nodeId, index)}
className="text-[#848b9b] hover:text-[#e2e5eb] hover:underline"
className="text-muted-foreground hover:text-foreground hover:underline"
>
{truncatedLabel}
</button>
) : (
<span className="font-medium text-[#e2e5eb]">
<span className="font-medium text-foreground">
{truncatedLabel}
</span>
)}
@@ -834,7 +834,7 @@ export function TreeNavigationPage() {
</div>
{/* Current Node */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6 shadow-xs">
<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">
@@ -847,11 +847,11 @@ export function TreeNavigationPage() {
{/* Decision Node */}
{currentNode && currentNode.type === 'decision' && (
<>
<h2 className="mb-2 text-xl font-semibold font-heading text-[#e2e5eb]">
<h2 className="mb-2 text-xl font-semibold font-heading text-foreground">
{currentNode.question}
</h2>
{currentNode.help_text && (
<div className="mb-4 text-sm text-[#848b9b]">
<div className="mb-4 text-sm text-muted-foreground">
<MarkdownContent content={currentNode.help_text} />
</div>
)}
@@ -862,7 +862,7 @@ export function TreeNavigationPage() {
onClick={() => handleSelectOption(option.id, option.label, option.next_node_id)}
disabled={!!selectingOption}
className={cn(
'w-full rounded-md border border-[#1e2130] p-3 text-left text-[#e2e5eb] transition-colors',
'w-full rounded-md border border-border p-3 text-left text-foreground transition-colors',
'hover:border-primary/30 hover:bg-accent',
'flex items-center gap-3',
selectingOption && selectingOption !== option.id && 'opacity-50 pointer-events-none'
@@ -874,7 +874,7 @@ export function TreeNavigationPage() {
<Spinner size="sm" className="h-4 w-4 border-t-foreground" />
</span>
) : (
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded bg-accent text-xs font-medium text-[#848b9b]">
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded bg-accent text-xs font-medium text-muted-foreground">
{index + 1}
</span>
)
@@ -886,7 +886,7 @@ export function TreeNavigationPage() {
{/* Previously-created custom steps at this node */}
{customStepFlow.customSteps.filter(cs => cs.inserted_after_node_id === currentNodeId).length > 0 && (
<div className="mt-2 space-y-2">
<p className="text-xs font-medium uppercase tracking-wide text-[#848b9b]">
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
Your Custom Steps
</p>
{customStepFlow.customSteps
@@ -897,12 +897,12 @@ export function TreeNavigationPage() {
key={cs.id}
onClick={() => customStepFlow.handleNavigateToCustomStep(cs)}
className={cn(
'w-full rounded-md border border-primary/30 bg-[rgba(34,211,238,0.10)] p-3 text-left text-[#e2e5eb] transition-colors',
'w-full rounded-md border border-primary/30 bg-accent-dim p-3 text-left text-foreground transition-colors',
'hover:border-primary/50 hover:bg-primary/20',
'flex items-center gap-3'
)}
>
<span className="shrink-0 rounded-full bg-primary/20 px-2 py-0.5 text-xs font-medium text-[#22d3ee]">
<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>
@@ -914,7 +914,7 @@ export function TreeNavigationPage() {
{/* Add Custom Step Button */}
<button
onClick={() => customStepFlow.setShowCustomStepModal(true)}
className="mt-2 inline-flex items-center gap-1 rounded-md px-2 py-1.5 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="mt-2 inline-flex items-center gap-1 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Plus className="h-3.5 w-3.5" />
Add Custom Step
@@ -924,18 +924,18 @@ export function TreeNavigationPage() {
{/* Custom Step Node */}
{currentCustomStep && (
<div className="rounded-lg border border-primary/30 bg-[rgba(34,211,238,0.10)] p-4">
<div className="rounded-lg border border-primary/30 bg-accent-dim p-4">
{/* Custom Step Badge */}
<span className="mb-2 inline-block rounded-full bg-primary/20 px-2 py-1 text-xs font-medium text-[#22d3ee]">
<span className="mb-2 inline-block rounded-full bg-primary/20 px-2 py-1 text-xs font-medium text-primary">
Custom Step
</span>
<h2 className="mb-2 text-xl font-semibold font-heading text-[#e2e5eb]">
<h2 className="mb-2 text-xl font-semibold font-heading text-foreground">
{currentCustomStep.step_data.title}
</h2>
{currentCustomStep.step_data.content.instructions && (
<div className="mb-4 text-[#848b9b]">
<div className="mb-4 text-muted-foreground">
<MarkdownContent content={currentCustomStep.step_data.content.instructions} />
</div>
)}
@@ -948,11 +948,11 @@ export function TreeNavigationPage() {
{currentCustomStep.step_data.content.commands && currentCustomStep.step_data.content.commands.length > 0 && (
<div className="mb-4">
<p className="mb-2 text-sm font-medium text-[#e2e5eb]">Commands:</p>
<p className="mb-2 text-sm font-medium text-foreground">Commands:</p>
<div className="space-y-2">
{currentCustomStep.step_data.content.commands.map((cmd, index) => (
<div key={index}>
<p className="mb-1 text-xs text-[#848b9b]">{cmd.label}</p>
<p className="mb-1 text-xs text-muted-foreground">{cmd.label}</p>
<div className="group relative">
<code className="block rounded bg-accent p-2 pr-8 text-sm font-mono">
{cmd.command}
@@ -966,7 +966,7 @@ export function TreeNavigationPage() {
{copiedCommand === cmd.command ? (
<Check className="h-3.5 w-3.5 text-green-400" />
) : (
<Clipboard className="h-3.5 w-3.5 text-[#848b9b] hover:text-[#e2e5eb]" />
<Clipboard className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
)}
</button>
</div>
@@ -978,7 +978,7 @@ export function TreeNavigationPage() {
<button
type="button"
onClick={() => setCommandOutputOpen(!commandOutputOpen)}
className="flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground"
>
<Terminal className="h-3.5 w-3.5" />
<span>Paste Output (Optional)</span>
@@ -993,12 +993,12 @@ export function TreeNavigationPage() {
rows={4}
maxLength={10000}
className={cn(
'block w-full rounded-md border border-[#1e2130] bg-accent px-3 py-2',
'font-mono text-sm text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
<p className="mt-1 text-right text-xs text-[#848b9b]">
<p className="mt-1 text-right text-xs text-muted-foreground">
{commandOutput.length.toLocaleString()} / 10,000
</p>
</div>
@@ -1035,8 +1035,8 @@ export function TreeNavigationPage() {
<button
onClick={() => customStepFlow.setShowCustomStepModal(true)}
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#848b9b]',
'hover:bg-accent hover:text-[#e2e5eb]'
'flex items-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground',
'hover:bg-accent hover:text-foreground'
)}
>
<Plus className="h-4 w-4" />
@@ -1062,17 +1062,17 @@ export function TreeNavigationPage() {
{/* Action Node */}
{currentNode && currentNode.type === 'action' && (
<>
<h2 className="mb-2 text-xl font-semibold font-heading text-[#e2e5eb]">
<h2 className="mb-2 text-xl font-semibold font-heading text-foreground">
{currentNode.title}
</h2>
{currentNode.description && (
<div className="mb-4 text-[#848b9b]">
<div className="mb-4 text-muted-foreground">
<MarkdownContent content={currentNode.description} />
</div>
)}
{currentNode.commands && currentNode.commands.length > 0 && (
<div className="mb-4">
<p className="mb-2 text-sm font-medium text-[#e2e5eb]">Commands:</p>
<p className="mb-2 text-sm font-medium text-foreground">Commands:</p>
<div className="space-y-1">
{currentNode.commands.map((cmd, index) => (
<div key={index} className="group relative">
@@ -1088,7 +1088,7 @@ export function TreeNavigationPage() {
{copiedCommand === cmd ? (
<Check className="h-3.5 w-3.5 text-green-400" />
) : (
<Clipboard className="h-3.5 w-3.5 text-[#848b9b] hover:text-[#e2e5eb]" />
<Clipboard className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
)}
</button>
</div>
@@ -1099,7 +1099,7 @@ export function TreeNavigationPage() {
<button
type="button"
onClick={() => setCommandOutputOpen(!commandOutputOpen)}
className="flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground"
>
<Terminal className="h-3.5 w-3.5" />
<span>Paste Output (Optional)</span>
@@ -1114,12 +1114,12 @@ export function TreeNavigationPage() {
rows={4}
maxLength={10000}
className={cn(
'block w-full rounded-md border border-[#1e2130] bg-accent px-3 py-2',
'font-mono text-sm text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
<p className="mt-1 text-right text-xs text-[#848b9b]">
<p className="mt-1 text-right text-xs text-muted-foreground">
{commandOutput.length.toLocaleString()} / 10,000
</p>
</div>
@@ -1128,7 +1128,7 @@ export function TreeNavigationPage() {
</div>
)}
{currentNode.expected_outcome && (
<p className="mb-4 text-sm text-[#848b9b]">
<p className="mb-4 text-sm text-muted-foreground">
<strong>Expected outcome:</strong> {currentNode.expected_outcome}
</p>
)}
@@ -1148,18 +1148,18 @@ export function TreeNavigationPage() {
Solution
</span>
</div>
<h2 className="mb-2 text-xl font-semibold font-heading text-[#e2e5eb]">
<h2 className="mb-2 text-xl font-semibold font-heading text-foreground">
{currentNode.title}
</h2>
{currentNode.description && (
<div className="mb-4 text-[#848b9b]">
<div className="mb-4 text-muted-foreground">
<MarkdownContent content={currentNode.description} />
</div>
)}
{currentNode.resolution_steps && currentNode.resolution_steps.length > 0 && (
<div className="mb-4">
<p className="mb-2 text-sm font-medium text-[#e2e5eb]">Resolution steps:</p>
<ol className="list-inside list-decimal space-y-1 text-sm text-[#848b9b]">
<p className="mb-2 text-sm font-medium text-foreground">Resolution steps:</p>
<ol className="list-inside list-decimal space-y-1 text-sm text-muted-foreground">
{currentNode.resolution_steps.map((step, index) => (
<li key={index}>{step}</li>
))}
@@ -1181,14 +1181,14 @@ export function TreeNavigationPage() {
{/* Step Feedback */}
{session && (currentNode || currentCustomStep) && (
<div className="mt-3 flex justify-end border-t border-[#1e2130] pt-3">
<div className="mt-3 flex justify-end border-t border-border pt-3">
<StepFeedback stepId={currentCustomStep?.id || currentNodeId} sessionId={session.id} />
</div>
)}
{/* Notes */}
<div className="mt-6 border-t border-[#1e2130] pt-4">
<label className="block text-sm font-medium text-[#e2e5eb]">
<div className="mt-6 border-t border-border pt-4">
<label className="block text-sm font-medium text-foreground">
Notes (optional)
</label>
<textarea
@@ -1198,8 +1198,8 @@ export function TreeNavigationPage() {
placeholder="Add any notes for this step..."
rows={2}
className={cn(
'mt-1 block w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'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-hidden focus:ring-1 focus:ring-primary/20'
)}
/>
@@ -1216,15 +1216,15 @@ export function TreeNavigationPage() {
{pathTaken.length > 1 && (
<button
onClick={handleGoBack}
className="mt-4 text-sm text-[#848b9b] hover:text-[#e2e5eb]"
className="mt-4 text-sm text-muted-foreground hover:text-foreground"
>
Go back <span className="text-xs text-[#848b9b]">[Esc]</span>
Go back <span className="text-xs text-muted-foreground">[Esc]</span>
</button>
)}
{/* Keyboard Shortcuts Hint */}
{currentNode && (
<div className="mt-4 border-t border-[#1e2130] pt-3 text-xs text-[#848b9b]">
<div className="mt-4 border-t border-border pt-3 text-xs text-muted-foreground">
<span className="font-medium">Keyboard:</span>{' '}
{currentNode.type === 'decision' && currentOptions.length > 0 && (
<span>1-{Math.min(currentOptions.length, 9)} select option</span>
@@ -1301,24 +1301,24 @@ export function TreeNavigationPage() {
>
<div className="space-y-3 text-sm">
<div className="flex items-center justify-between">
<span className="text-[#848b9b]">Select option</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-[#e2e5eb]">1-9</span>
<span className="text-muted-foreground">Select option</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-foreground">1-9</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[#848b9b]">Go back</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-[#e2e5eb]">Esc</span>
<span className="text-muted-foreground">Go back</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-foreground">Esc</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[#848b9b]">Continue / Complete</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-[#e2e5eb]">Enter</span>
<span className="text-muted-foreground">Continue / Complete</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-foreground">Enter</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[#848b9b]">Focus notes</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-[#e2e5eb]">Tab</span>
<span className="text-muted-foreground">Focus notes</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-foreground">Tab</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[#848b9b]">Show shortcuts</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-[#e2e5eb]">?</span>
<span className="text-muted-foreground">Show shortcuts</span>
<span className="rounded bg-accent px-2 py-0.5 font-mono text-xs text-foreground">?</span>
</div>
</div>
</Modal>

View File

@@ -26,23 +26,23 @@ export function VerifyEmailPage() {
return (
<>
<PageMeta title="Verify Email" description="Verify your ResolutionFlow email address" />
<div className="flex min-h-screen items-center justify-center bg-[#0c0d10] p-4">
<div className="flex min-h-screen items-center justify-center bg-background p-4">
<div className="card-flat w-full max-w-md p-8 text-center">
{status === 'loading' && (
<>
<Loader2 className="mx-auto h-12 w-12 animate-spin text-[#22d3ee]" />
<p className="mt-4 text-[#e2e5eb]">Verifying your email...</p>
<Loader2 className="mx-auto h-12 w-12 animate-spin text-primary" />
<p className="mt-4 text-foreground">Verifying your email...</p>
</>
)}
{status === 'success' && (
<>
<CheckCircle2 className="mx-auto h-12 w-12 text-emerald-400" />
<h1 className="mt-4 text-xl font-bold font-heading text-[#e2e5eb]">Email Verified</h1>
<p className="mt-2 text-[#848b9b]">Your email has been successfully verified.</p>
<h1 className="mt-4 text-xl font-bold font-heading text-foreground">Email Verified</h1>
<p className="mt-2 text-muted-foreground">Your email has been successfully verified.</p>
<Link
to="/"
className={cn(
'mt-6 inline-flex items-center rounded-lg bg-[#22d3ee] px-6 py-2 text-sm font-semibold text-white',
'mt-6 inline-flex items-center rounded-lg bg-primary px-6 py-2 text-sm font-semibold text-white',
'hover:brightness-110'
)}
>
@@ -53,13 +53,13 @@ export function VerifyEmailPage() {
{status === 'error' && (
<>
<XCircle className="mx-auto h-12 w-12 text-rose-500" />
<h1 className="mt-4 text-xl font-bold font-heading text-[#e2e5eb]">Verification Failed</h1>
<p className="mt-2 text-[#848b9b]">{errorMessage}</p>
<h1 className="mt-4 text-xl font-bold font-heading text-foreground">Verification Failed</h1>
<p className="mt-2 text-muted-foreground">{errorMessage}</p>
<Link
to="/"
className={cn(
'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-[#2a2f3d]'
'mt-6 inline-flex items-center rounded-lg bg-input border border-border px-6 py-2 text-sm font-medium text-foreground',
'hover:border-border-hover'
)}
>
Go to Dashboard

View File

@@ -75,7 +75,7 @@ export function BrandingSettingsPage() {
<>
<PageMeta title="Branding Settings" />
<div className="flex justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-[#848b9b]" />
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
</>
)
@@ -103,12 +103,12 @@ export function BrandingSettingsPage() {
<div>
<div className="mb-8">
<div className="flex items-center gap-3">
<Palette className="h-8 w-8 text-[#848b9b]" />
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">
<Palette className="h-8 w-8 text-muted-foreground" />
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">
Branding Settings
</h1>
</div>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
Customize your account branding logo, accent color, and company name.
</p>
</div>
@@ -116,14 +116,14 @@ export function BrandingSettingsPage() {
<div className="max-w-2xl space-y-6">
{/* Branding Form */}
<div className="card-flat p-6">
<h2 className="text-lg font-semibold text-[#e2e5eb] mb-6">Custom Branding</h2>
<h2 className="text-lg font-semibold text-foreground mb-6">Custom Branding</h2>
<div className="space-y-5">
{/* Company Name */}
<div>
<label
htmlFor="company-name"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"
>
Company Name
</label>
@@ -135,12 +135,12 @@ export function BrandingSettingsPage() {
placeholder="Your Company Name"
maxLength={200}
className={cn(
'mt-1 w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'mt-1 w-full rounded-lg 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 focus:ring-1 focus:ring-primary/20'
)}
/>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Displayed in the sidebar and exported documents.
</p>
</div>
@@ -149,7 +149,7 @@ export function BrandingSettingsPage() {
<div>
<label
htmlFor="logo-url"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"
>
Logo URL
</label>
@@ -161,16 +161,16 @@ export function BrandingSettingsPage() {
placeholder="https://example.com/logo.png"
maxLength={500}
className={cn(
'mt-1 w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm',
'text-[#e2e5eb] placeholder:text-[#848b9b] font-mono',
'mt-1 w-full rounded-lg border border-border bg-card px-3 py-2 text-sm',
'text-foreground placeholder:text-muted-foreground font-mono',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20'
)}
/>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Publicly accessible URL to your logo image (PNG, SVG, or JPEG).
</p>
{logoUrl && (
<div className="mt-3 inline-flex items-center rounded-lg border border-[#1e2130] bg-[#14161d]/50 p-3">
<div className="mt-3 inline-flex items-center rounded-lg border border-border bg-card/50 p-3">
<img
src={logoUrl}
alt="Logo preview"
@@ -187,7 +187,7 @@ export function BrandingSettingsPage() {
<div>
<label
htmlFor="primary-color"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]"
className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground"
>
Accent Color
</label>
@@ -197,7 +197,7 @@ export function BrandingSettingsPage() {
type="color"
value={previewColor}
onChange={(e) => setPrimaryColor(e.target.value)}
className="h-9 w-14 cursor-pointer rounded-lg border border-[#1e2130] bg-[#14161d] p-0.5"
className="h-9 w-14 cursor-pointer rounded-lg border border-border bg-card p-0.5"
title="Pick accent color"
/>
<input
@@ -210,20 +210,20 @@ export function BrandingSettingsPage() {
placeholder="#06b6d4"
maxLength={7}
className={cn(
'w-32 rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm',
'text-[#e2e5eb] placeholder:text-[#848b9b] font-mono',
'w-32 rounded-lg border border-border bg-card px-3 py-2 text-sm',
'text-foreground placeholder:text-muted-foreground font-mono',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none focus:ring-1 focus:ring-primary/20'
)}
/>
<button
type="button"
onClick={() => setPrimaryColor(DEFAULT_COLOR)}
className="text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-xs text-muted-foreground hover:text-foreground transition-colors"
>
Reset to default
</button>
</div>
<p className="mt-1 text-xs text-[#848b9b]">
<p className="mt-1 text-xs text-muted-foreground">
Hex color code for the primary accent color (e.g. #06b6d4).
</p>
</div>
@@ -237,7 +237,7 @@ export function BrandingSettingsPage() {
disabled={isSaving || !isDirty}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-5 py-2.5 text-sm font-semibold',
'bg-[#22d3ee] text-white',
'bg-primary text-white',
'hover:brightness-110 active:scale-[0.98] transition-all',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -250,15 +250,15 @@ export function BrandingSettingsPage() {
Save Branding
</button>
{!isDirty && !isSaving && (
<span className="text-xs text-[#848b9b]">No changes</span>
<span className="text-xs text-muted-foreground">No changes</span>
)}
</div>
</div>
{/* Preview Section */}
<div className="card-flat p-6">
<h2 className="text-lg font-semibold text-[#e2e5eb] mb-4">Preview</h2>
<p className="text-sm text-[#848b9b] mb-4">
<h2 className="text-lg font-semibold text-foreground mb-4">Preview</h2>
<p className="text-sm text-muted-foreground mb-4">
This is how your accent color will appear on interactive elements.
</p>
@@ -293,7 +293,7 @@ export function BrandingSettingsPage() {
</div>
{companyName && (
<p className="text-sm text-[#e2e5eb]">
<p className="text-sm text-foreground">
Company: <span className="font-medium">{companyName}</span>
</p>
)}

View File

@@ -45,7 +45,7 @@ export default function ChatRetentionSettingsPage() {
if (loading) {
return (
<div className="flex items-center justify-center py-20">
<Loader2 className="animate-spin text-[#22d3ee]" size={24} />
<Loader2 className="animate-spin text-primary" size={24} />
</div>
)
}
@@ -53,18 +53,18 @@ export default function ChatRetentionSettingsPage() {
return (
<div className="max-w-2xl mx-auto py-8 px-6">
<div className="flex items-center gap-3 mb-6">
<Clock size={20} className="text-[#22d3ee]" />
<h1 className="text-xl font-heading font-bold text-[#e2e5eb]">Chat Retention</h1>
<Clock size={20} className="text-primary" />
<h1 className="text-xl font-heading font-bold text-foreground">Chat Retention</h1>
</div>
<div className="card-flat rounded-2xl p-6 space-y-6">
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
Configure how long AI assistant conversations are retained. Pinned chats are never automatically deleted.
</p>
<div className="space-y-4">
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] block mb-1.5">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-1.5">
Retention Period (days)
</label>
<input
@@ -73,16 +73,16 @@ export default function ChatRetentionSettingsPage() {
onChange={e => setRetentionDays(e.target.value)}
min={1}
max={365}
className="w-full rounded-xl border bg-[#14161d] text-[#e2e5eb] text-sm px-4 py-2.5 focus:outline-hidden focus:border-primary/30"
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-hidden focus:border-primary/30"
style={{ borderColor: 'var(--glass-border)' }}
/>
<p className="text-xs text-[#848b9b] mt-1">
<p className="text-xs text-muted-foreground mt-1">
Chats older than this will be automatically deleted (1-365 days)
</p>
</div>
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] block mb-1.5">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground block mb-1.5">
Max Conversations
</label>
<input
@@ -91,10 +91,10 @@ export default function ChatRetentionSettingsPage() {
onChange={e => setMaxCount(e.target.value)}
min={10}
max={10000}
className="w-full rounded-xl border bg-[#14161d] text-[#e2e5eb] text-sm px-4 py-2.5 focus:outline-hidden focus:border-primary/30"
className="w-full rounded-xl border bg-card text-foreground text-sm px-4 py-2.5 focus:outline-hidden focus:border-primary/30"
style={{ borderColor: 'var(--glass-border)' }}
/>
<p className="text-xs text-[#848b9b] mt-1">
<p className="text-xs text-muted-foreground mt-1">
When this limit is exceeded, oldest unpinned chats are deleted
</p>
</div>
@@ -104,7 +104,7 @@ export default function ChatRetentionSettingsPage() {
<button
onClick={handleSave}
disabled={saving}
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"
className="bg-primary 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} />}
Save Settings

View File

@@ -197,7 +197,7 @@ export function IntegrationsPage() {
<>
<PageMeta title="Integrations" />
<div className="flex justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-[#848b9b]" />
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
</>
)
@@ -223,16 +223,16 @@ export function IntegrationsPage() {
<div>
<div className="mb-8">
<div className="flex items-center gap-3">
<Plug className="h-8 w-8 text-[#848b9b]" />
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">Integrations</h1>
<Plug className="h-8 w-8 text-muted-foreground" />
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">Integrations</h1>
</div>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
Connect your PSA to post session documentation directly to tickets.
</p>
</div>
{/* Tabs */}
<div className="mb-6 flex gap-1 border-b border-[#1e2130]">
<div className="mb-6 flex gap-1 border-b border-border">
{([
{ id: 'connection' as Tab, label: 'Connection', icon: Plug },
{ id: 'member-mapping' as Tab, label: 'Member Mapping', icon: Users },
@@ -246,8 +246,8 @@ export function IntegrationsPage() {
className={cn(
'inline-flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors -mb-px',
activeTab === id
? 'border-primary text-[#e2e5eb]'
: 'border-transparent text-[#848b9b] hover:text-[#e2e5eb] hover:border-[#1e2130]'
? 'border-primary text-foreground'
: 'border-transparent text-muted-foreground hover:text-foreground hover:border-border'
)}
>
<Icon className="h-4 w-4" />
@@ -261,7 +261,7 @@ export function IntegrationsPage() {
<div className="max-w-3xl">
{/* PSA Provider Grid */}
<div className="mb-6">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] mb-3">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground mb-3">
Available PSA Integrations
</p>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
@@ -270,43 +270,43 @@ export function IntegrationsPage() {
'rounded-xl border p-4 flex flex-col gap-2',
connection
? 'border-primary/40 bg-primary/5'
: 'border-[#1e2130] bg-[#14161d]/30'
: 'border-border bg-card/30'
)}>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-[#e2e5eb]">ConnectWise PSA</span>
<span className="text-sm font-medium text-foreground">ConnectWise PSA</span>
{connection ? (
<span className="inline-flex items-center rounded-full bg-emerald-400/10 px-2 py-0.5 text-[0.625rem] font-sans text-xs text-emerald-400">
Connected
</span>
) : (
<span className="inline-flex items-center rounded-full bg-[rgba(34,211,238,0.10)] px-2 py-0.5 text-[0.625rem] font-sans text-xs text-[#22d3ee]">
<span className="inline-flex items-center rounded-full bg-accent-dim px-2 py-0.5 text-[0.625rem] font-sans text-xs text-primary">
Available
</span>
)}
</div>
<p className="text-xs text-[#848b9b]">Manage, ticket linking, time entries</p>
<p className="text-xs text-muted-foreground">Manage, ticket linking, time entries</p>
</div>
{/* Autotask — coming soon */}
<div className="rounded-xl border border-[#1e2130] bg-[#14161d]/20 p-4 flex flex-col gap-2 opacity-60 cursor-not-allowed select-none">
<div className="rounded-xl border border-border bg-card/20 p-4 flex flex-col gap-2 opacity-60 cursor-not-allowed select-none">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-[#e2e5eb]">Autotask PSA</span>
<span className="text-sm font-medium text-foreground">Autotask PSA</span>
<span className="inline-flex items-center rounded-full bg-amber-400/10 px-2 py-0.5 text-[0.625rem] font-sans text-xs text-amber-400">
Coming Soon
</span>
</div>
<p className="text-xs text-[#848b9b]">Datto / Kaseya integration</p>
<p className="text-xs text-muted-foreground">Datto / Kaseya integration</p>
</div>
{/* Halo PSA — coming soon */}
<div className="rounded-xl border border-[#1e2130] bg-[#14161d]/20 p-4 flex flex-col gap-2 opacity-60 cursor-not-allowed select-none">
<div className="rounded-xl border border-border bg-card/20 p-4 flex flex-col gap-2 opacity-60 cursor-not-allowed select-none">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-[#e2e5eb]">Halo PSA</span>
<span className="text-sm font-medium text-foreground">Halo PSA</span>
<span className="inline-flex items-center rounded-full bg-amber-400/10 px-2 py-0.5 text-[0.625rem] font-sans text-xs text-amber-400">
Coming Soon
</span>
</div>
<p className="text-xs text-[#848b9b]">HaloPSA / HaloITSM integration</p>
<p className="text-xs text-muted-foreground">HaloPSA / HaloITSM integration</p>
</div>
</div>
</div>
@@ -327,15 +327,15 @@ export function IntegrationsPage() {
{(mode === 'setup' || mode === 'edit') && (
<div className="card-flat p-6">
<div className="flex items-center gap-2 mb-6">
<Shield className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">
<Shield className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">
{mode === 'setup' ? 'Connect to ConnectWise PSA' : 'Edit Connection'}
</h2>
</div>
<form onSubmit={mode === 'setup' ? handleCreate : handleUpdate} className="space-y-4">
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Display Name
</label>
<Input
@@ -349,7 +349,7 @@ export function IntegrationsPage() {
</div>
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Site URL
</label>
<Input
@@ -363,7 +363,7 @@ export function IntegrationsPage() {
</div>
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Company ID
</label>
<Input
@@ -377,7 +377,7 @@ export function IntegrationsPage() {
</div>
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Public Key
</label>
<Input
@@ -391,7 +391,7 @@ export function IntegrationsPage() {
</div>
<div>
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
Private Key
</label>
<Input
@@ -417,7 +417,7 @@ export function IntegrationsPage() {
disabled={isSaving}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-5 py-2.5 text-sm font-semibold',
'bg-[#22d3ee] text-white',
'bg-primary text-white',
'hover:brightness-110 active:scale-[0.98] transition-all',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -430,7 +430,7 @@ export function IntegrationsPage() {
<button
type="button"
onClick={cancelEdit}
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Cancel
</button>
@@ -447,10 +447,10 @@ export function IntegrationsPage() {
<div className="card-flat p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<span className="inline-flex items-center rounded-full bg-[rgba(34,211,238,0.10)] px-2.5 py-0.5 text-xs font-sans text-xs font-medium text-[#22d3ee]">
<span className="inline-flex items-center rounded-full bg-accent-dim px-2.5 py-0.5 text-xs font-sans text-xs font-medium text-primary">
ConnectWise
</span>
<h2 className="text-lg font-semibold text-[#e2e5eb]">{connection.display_name}</h2>
<h2 className="text-lg font-semibold text-foreground">{connection.display_name}</h2>
</div>
<div className="flex items-center gap-2">
<span
@@ -470,24 +470,24 @@ export function IntegrationsPage() {
<div className="grid gap-4 sm:grid-cols-2">
<div>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Site URL</p>
<p className="mt-1 text-sm text-[#e2e5eb]">{connection.site_url}</p>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Site URL</p>
<p className="mt-1 text-sm text-foreground">{connection.site_url}</p>
</div>
<div>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Company ID</p>
<p className="mt-1 text-sm text-[#e2e5eb]">{connection.company_id}</p>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Company ID</p>
<p className="mt-1 text-sm text-foreground">{connection.company_id}</p>
</div>
<div>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Public Key</p>
<p className="mt-1 text-sm text-[#e2e5eb] font-mono">{connection.public_key_hint}</p>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Public Key</p>
<p className="mt-1 text-sm text-foreground font-mono">{connection.public_key_hint}</p>
</div>
<div>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Private Key</p>
<p className="mt-1 text-sm text-[#e2e5eb] font-mono">{connection.private_key_hint}</p>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Private Key</p>
<p className="mt-1 text-sm text-foreground font-mono">{connection.private_key_hint}</p>
</div>
<div>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Last Validated</p>
<p className="mt-1 text-sm text-[#e2e5eb]">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Last Validated</p>
<p className="mt-1 text-sm text-foreground">
{connection.last_validated_at ? formatRelativeTime(connection.last_validated_at) : 'Never'}
</p>
</div>
@@ -511,7 +511,7 @@ export function IntegrationsPage() {
)}
<span>{testResult.message}</span>
{testResult.server_version && (
<span className="ml-auto text-xs text-[#848b9b]">
<span className="ml-auto text-xs text-muted-foreground">
v{testResult.server_version}
</span>
)}
@@ -525,7 +525,7 @@ export function IntegrationsPage() {
disabled={isTesting}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb]',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground',
'hover:border-[rgba(255,255,255,0.12)] transition-all',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -542,7 +542,7 @@ export function IntegrationsPage() {
onClick={startEdit}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb]',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground',
'hover:border-[rgba(255,255,255,0.12)] transition-all'
)}
>
@@ -552,7 +552,7 @@ export function IntegrationsPage() {
{showDeleteConfirm ? (
<div className="flex items-center gap-2 ml-auto">
<span className="text-sm text-[#848b9b]">Disconnect?</span>
<span className="text-sm text-muted-foreground">Disconnect?</span>
<button
onClick={handleDelete}
disabled={isDeleting}
@@ -563,7 +563,7 @@ export function IntegrationsPage() {
</button>
<button
onClick={() => setShowDeleteConfirm(false)}
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Cancel
</button>
@@ -593,10 +593,10 @@ export function IntegrationsPage() {
<div className="max-w-3xl">
<div className="card-flat p-6">
<div className="flex items-center gap-3 mb-4">
<Ticket className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Post History</h2>
<Ticket className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Post History</h2>
</div>
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
View post history from individual sessions by clicking on linked tickets.
When a session has a ConnectWise ticket linked, use the Update button to post
session documentation and view previous posts.
@@ -729,10 +729,10 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
<div className="max-w-3xl">
<div className="card-flat p-6">
<div className="flex items-center gap-3 mb-4">
<Users className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Member Mapping</h2>
<Users className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Member Mapping</h2>
</div>
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
Set up a PSA connection first to map team members to ConnectWise members.
</p>
</div>
@@ -746,8 +746,8 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
<div className="card-flat p-6">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<Users className="h-5 w-5 text-[#848b9b]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">Member Mapping</h2>
<Users className="h-5 w-5 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">Member Mapping</h2>
</div>
<button
type="button"
@@ -755,7 +755,7 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
disabled={isAutoMatching || isLoadingData}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-[#e2e5eb]',
'bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] text-foreground',
'hover:border-[rgba(255,255,255,0.12)] transition-all',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -768,7 +768,7 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
Auto-Match by Email
</button>
</div>
<p className="text-sm text-[#848b9b]">
<p className="text-sm text-muted-foreground">
Map your ResolutionFlow users to ConnectWise members so session posts are attributed correctly.
</p>
</div>
@@ -776,7 +776,7 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
{/* Loading state */}
{isLoadingData && (
<div className="flex justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-[#848b9b]" />
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
)}
@@ -784,17 +784,17 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
{hasLoaded && !isLoadingData && (
<div className="card-flat overflow-hidden">
{uniqueUsers.length === 0 ? (
<div className="p-6 text-center text-sm text-[#848b9b]">
<div className="p-6 text-center text-sm text-muted-foreground">
No users found. Use Auto-Match to discover and map users.
</div>
) : (
<>
{/* Table header */}
<div className="grid grid-cols-[1fr_1fr_1fr_auto] gap-4 border-b border-[#1e2130] px-6 py-3">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">User</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Email</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Mapped To</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] w-20 text-center">Method</span>
<div className="grid grid-cols-[1fr_1fr_1fr_auto] gap-4 border-b border-border px-6 py-3">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">User</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Email</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Mapped To</span>
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground w-20 text-center">Method</span>
</div>
{/* Rows */}
@@ -803,18 +803,18 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
return (
<div
key={user.user_id}
className="grid grid-cols-[1fr_1fr_1fr_auto] gap-4 items-center border-b border-[#1e2130]/50 px-6 py-3 last:border-b-0"
className="grid grid-cols-[1fr_1fr_1fr_auto] gap-4 items-center border-b border-border/50 px-6 py-3 last:border-b-0"
>
<span className="text-sm text-[#e2e5eb] truncate">{user.user_name}</span>
<span className="text-sm text-[#848b9b] truncate">{user.user_email}</span>
<span className="text-sm text-foreground truncate">{user.user_name}</span>
<span className="text-sm text-muted-foreground truncate">{user.user_email}</span>
<select
title={`Map ${user.user_name} to a ConnectWise member`}
value={currentMapping?.external_member_id || ''}
onChange={(e) => handleMemberChange(user.user_id, e.target.value)}
className={cn(
'w-full rounded-lg border bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb]',
'border-[#1e2130] focus:border-[rgba(6,182,212,0.3)] focus:outline-none',
!currentMapping && 'text-[#848b9b]'
'w-full rounded-lg border bg-card px-3 py-1.5 text-sm text-foreground',
'border-border focus:border-[rgba(6,182,212,0.3)] focus:outline-none',
!currentMapping && 'text-muted-foreground'
)}
>
<option value="">-- Unmapped --</option>
@@ -829,17 +829,17 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
<span className={cn(
'inline-flex items-center rounded-full px-2 py-0.5 text-[0.625rem] font-sans text-xs',
user.matched_by === 'auto_email'
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee]'
: 'bg-[#14161d] border border-[#1e2130] text-[#848b9b]'
? 'bg-accent-dim text-primary'
: 'bg-card border border-border text-muted-foreground'
)}>
{user.matched_by === 'auto_email' ? 'auto' : 'manual'}
</span>
) : currentMapping ? (
<span className="inline-flex items-center rounded-full px-2 py-0.5 text-[0.625rem] font-sans text-xs bg-[#14161d] border border-[#1e2130] text-[#848b9b]">
<span className="inline-flex items-center rounded-full px-2 py-0.5 text-[0.625rem] font-sans text-xs bg-card border border-border text-muted-foreground">
manual
</span>
) : (
<span className="text-[0.625rem] text-[#848b9b]/50"></span>
<span className="text-[0.625rem] text-muted-foreground/50"></span>
)}
</span>
</div>
@@ -859,7 +859,7 @@ function MemberMappingTab({ connection }: { connection: PsaConnectionResponse |
disabled={isSavingMappings}
className={cn(
'inline-flex items-center gap-2 rounded-lg px-5 py-2.5 text-sm font-semibold',
'bg-[#22d3ee] text-white',
'bg-primary text-white',
'hover:brightness-110 active:scale-[0.98] transition-all',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -915,7 +915,7 @@ function FlowPilotSettingsTab({ connection }: { connection: PsaConnectionRespons
return (
<div className="max-w-3xl">
<div className="card-flat p-6 text-center">
<p className="text-sm text-[#848b9b]">Connect your PSA first to configure FlowPilot settings.</p>
<p className="text-sm text-muted-foreground">Connect your PSA first to configure FlowPilot settings.</p>
</div>
</div>
)
@@ -924,7 +924,7 @@ function FlowPilotSettingsTab({ connection }: { connection: PsaConnectionRespons
if (isLoading) {
return (
<div className="flex justify-center py-12">
<Loader2 className="h-5 w-5 animate-spin text-[#848b9b]" />
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
</div>
)
}
@@ -935,10 +935,10 @@ function FlowPilotSettingsTab({ connection }: { connection: PsaConnectionRespons
<div className="max-w-3xl space-y-4">
<div className="card-flat p-6">
<div className="flex items-center gap-3 mb-4">
<Zap className="h-5 w-5 text-[#22d3ee]" />
<h2 className="text-lg font-semibold text-[#e2e5eb]">FlowPilot Settings</h2>
<Zap className="h-5 w-5 text-primary" />
<h2 className="text-lg font-semibold text-foreground">FlowPilot Settings</h2>
</div>
<p className="text-sm text-[#848b9b] mb-6">
<p className="text-sm text-muted-foreground mb-6">
Configure how FlowPilot integrates with your ConnectWise PSA when sessions are resolved or escalated.
</p>
@@ -1039,8 +1039,8 @@ function SettingToggle({
return (
<div className="flex items-start justify-between gap-4">
<div>
<p className="text-sm font-medium text-[#e2e5eb]">{label}</p>
<p className="text-xs text-[#848b9b] mt-0.5">{description}</p>
<p className="text-sm font-medium text-foreground">{label}</p>
<p className="text-xs text-muted-foreground mt-0.5">{description}</p>
</div>
<button
onClick={() => onChange(!checked)}
@@ -1078,13 +1078,13 @@ function SettingSelect({
}) {
return (
<div>
<p className="text-sm font-medium text-[#e2e5eb]">{label}</p>
<p className="text-xs text-[#848b9b] mt-0.5 mb-2">{description}</p>
<p className="text-sm font-medium text-foreground">{label}</p>
<p className="text-xs text-muted-foreground mt-0.5 mb-2">{description}</p>
<select
value={value}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
className="w-full max-w-xs rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] focus:border-[rgba(6,182,212,0.3)] focus:outline-none disabled:opacity-50"
className="w-full max-w-xs rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none disabled:opacity-50"
>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>

View File

@@ -8,8 +8,8 @@ import { toast } from '@/lib/toast'
import type { UserUpdate } from '@/types'
const inputClass = cn(
'mt-1 block w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2',
'text-[#e2e5eb] placeholder:text-[#848b9b]',
'mt-1 block w-full rounded-lg border border-border bg-card px-3 py-2',
'text-foreground placeholder:text-muted-foreground',
'focus:border-primary/30 focus:outline-hidden focus:ring-1 focus:ring-primary/20'
)
@@ -67,10 +67,10 @@ export function ProfileSettingsPage() {
<div>
<div className="mb-8">
<div className="flex items-center gap-3">
<UserIcon className="h-8 w-8 text-[#848b9b]" />
<h1 className="text-2xl font-bold font-heading text-[#e2e5eb] sm:text-3xl">Profile Settings</h1>
<UserIcon className="h-8 w-8 text-muted-foreground" />
<h1 className="text-2xl font-bold font-heading text-foreground sm:text-3xl">Profile Settings</h1>
</div>
<p className="mt-2 text-[#848b9b]">
<p className="mt-2 text-muted-foreground">
Update your name, email, and personal details
</p>
</div>
@@ -79,40 +79,40 @@ export function ProfileSettingsPage() {
<form onSubmit={handleSave} className="card-flat p-6 space-y-5">
{/* Name */}
<div>
<label htmlFor="profile-name" className="block text-sm font-medium text-[#e2e5eb]">Name</label>
<label htmlFor="profile-name" className="block text-sm font-medium text-foreground">Name</label>
<input id="profile-name" type="text" value={name} onChange={(e) => setName(e.target.value)} required className={inputClass} />
</div>
{/* Email */}
<div>
<label htmlFor="profile-email" className="block text-sm font-medium text-[#e2e5eb]">Email</label>
<label htmlFor="profile-email" className="block text-sm font-medium text-foreground">Email</label>
<input id="profile-email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required className={inputClass} />
</div>
{/* Password confirmation for email change */}
{emailChanged && (
<div>
<label htmlFor="profile-password" className="block text-sm font-medium text-[#e2e5eb]">Current Password</label>
<p className="text-xs text-[#848b9b]">Required to change your email address</p>
<label htmlFor="profile-password" className="block text-sm font-medium text-foreground">Current Password</label>
<p className="text-xs text-muted-foreground">Required to change your email address</p>
<input id="profile-password" type="password" value={currentPassword} onChange={(e) => setCurrentPassword(e.target.value)} required className={inputClass} />
</div>
)}
{/* Phone */}
<div>
<label htmlFor="profile-phone" className="block text-sm font-medium text-[#e2e5eb]">Phone</label>
<label htmlFor="profile-phone" className="block text-sm font-medium text-foreground">Phone</label>
<input id="profile-phone" type="tel" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="Optional" className={inputClass} />
</div>
{/* Job Title */}
<div>
<label htmlFor="profile-job-title" className="block text-sm font-medium text-[#e2e5eb]">Job Title</label>
<label htmlFor="profile-job-title" className="block text-sm font-medium text-foreground">Job Title</label>
<input id="profile-job-title" type="text" value={jobTitle} onChange={(e) => setJobTitle(e.target.value)} placeholder="e.g. Network Engineer" className={inputClass} />
</div>
{/* Timezone */}
<div>
<label htmlFor="profile-timezone" className="block text-sm font-medium text-[#e2e5eb]">Timezone</label>
<label htmlFor="profile-timezone" className="block text-sm font-medium text-foreground">Timezone</label>
<select id="profile-timezone" value={timezone} onChange={(e) => setTimezone(e.target.value)} className={inputClass}>
{COMMON_TIMEZONES.map((tz) => (
<option key={tz} value={tz}>{tz}</option>
@@ -132,7 +132,7 @@ export function ProfileSettingsPage() {
type="submit"
disabled={isSaving || !hasChanges}
className={cn(
'inline-flex items-center gap-2 rounded-lg bg-[#22d3ee] px-4 py-2 text-sm font-semibold text-white',
'inline-flex items-center gap-2 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-white',
'hover:brightness-110 active:scale-[0.98]',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
@@ -145,8 +145,8 @@ export function ProfileSettingsPage() {
to="/change-password"
className={cn(
'inline-flex items-center rounded-lg px-4 py-2 text-sm font-medium',
'bg-[#191c25] border border-[#1e2130] text-[#e2e5eb]',
'hover:border-[#2a2f3d]'
'bg-input border border-border text-foreground',
'hover:border-border-hover'
)}
>
Change Password

View File

@@ -114,7 +114,7 @@ export default function TargetListsPage() {
action={(
<button
onClick={() => openEditor()}
className="flex items-center gap-1.5 rounded-lg bg-[#22d3ee] px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110"
className="flex items-center gap-1.5 rounded-lg bg-primary px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110"
>
<Plus className="h-4 w-4" />
New List
@@ -137,14 +137,14 @@ export default function TargetListsPage() {
{lists.map(list => (
<div
key={list.id}
className="flex items-center justify-between rounded-xl border border-[#1e2130] bg-[#14161d] px-5 py-4"
className="flex items-center justify-between rounded-xl border border-border bg-card px-5 py-4"
>
<div>
<p className="font-medium text-[#e2e5eb]">{list.name}</p>
<p className="font-medium text-foreground">{list.name}</p>
{list.description && (
<p className="text-[0.8125rem] text-[#848b9b]">{list.description}</p>
<p className="text-[0.8125rem] text-muted-foreground">{list.description}</p>
)}
<p className="mt-0.5 font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-[#848b9b]">
<p className="mt-0.5 font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-muted-foreground">
{list.targets.length} target{list.targets.length !== 1 ? 's' : ''}
{list.targets.length > 0 && (
<> &middot; {list.targets.slice(0, 3).map(t => t.label).join(', ')}{list.targets.length > 3 ? '\u2026' : ''}</>
@@ -154,14 +154,14 @@ export default function TargetListsPage() {
<div className="flex items-center gap-1">
<button
onClick={() => openEditor(list)}
className="rounded-lg p-2 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-lg p-2 text-muted-foreground hover:bg-accent hover:text-foreground"
title="Edit"
>
<Pencil className="h-4 w-4" />
</button>
<button
onClick={() => setDeleteTarget(list)}
className="rounded-lg p-2 text-[#848b9b] hover:bg-accent hover:text-red-400"
className="rounded-lg p-2 text-muted-foreground hover:bg-accent hover:text-red-400"
title="Delete"
>
<Trash2 className="h-4 w-4" />
@@ -182,14 +182,14 @@ export default function TargetListsPage() {
<div className="flex justify-end gap-2">
<button
onClick={() => setShowEditor(false)}
className="rounded-lg border border-[#1e2130] px-4 py-2 text-[0.875rem] text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-lg border border-border px-4 py-2 text-[0.875rem] text-muted-foreground hover:bg-accent hover:text-foreground"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={isSaving}
className="rounded-lg bg-[#22d3ee] px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110 disabled:opacity-50"
className="rounded-lg bg-primary px-4 py-2 text-[0.875rem] font-medium text-white hover:brightness-110 disabled:opacity-50"
>
{isSaving ? 'Saving\u2026' : 'Save'}
</button>
@@ -198,38 +198,38 @@ export default function TargetListsPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-[#848b9b]">
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-muted-foreground">
Name
</label>
<input
type="text"
value={editorName}
onChange={e => setEditorName(e.target.value)}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[0.875rem] text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:outline-hidden 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>
<div>
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-[#848b9b]">
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-muted-foreground">
Description (optional)
</label>
<input
type="text"
value={editorDescription}
onChange={e => setEditorDescription(e.target.value)}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[0.875rem] text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:outline-hidden 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>
<div>
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-[#848b9b]">
<label className="mb-1 block font-sans text-xs text-[0.6875rem] uppercase tracking-wide text-muted-foreground">
Targets &mdash; one per line (add notes after #)
</label>
<textarea
value={editorTargets}
onChange={e => setEditorTargets(e.target.value)}
rows={6}
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-[0.875rem] text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary focus:outline-hidden 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>

View File

@@ -99,26 +99,26 @@ export function TeamCategoriesPage() {
))}
</div>
) : categories.length === 0 ? (
<div className="flex flex-col items-center justify-center bg-[#14161d] border border-[#1e2130] rounded-xl py-16">
<FolderTree className="h-12 w-12 text-[#848b9b]" />
<h3 className="mt-4 font-medium text-[#e2e5eb]">No team categories</h3>
<p className="mt-1 text-sm text-[#848b9b]">Create categories to organize your team's trees.</p>
<div className="flex flex-col items-center justify-center bg-card border border-border rounded-xl py-16">
<FolderTree className="h-12 w-12 text-muted-foreground" />
<h3 className="mt-4 font-medium text-foreground">No team categories</h3>
<p className="mt-1 text-sm text-muted-foreground">Create categories to organize your team's trees.</p>
</div>
) : (
<div className="space-y-2">
{categories.map((cat) => (
<div key={cat.id} className="flex items-center justify-between rounded-lg border border-[#1e2130] px-4 py-3">
<div key={cat.id} className="flex items-center justify-between rounded-lg border border-border px-4 py-3">
<div>
<span className="font-medium text-[#e2e5eb]">{cat.name}</span>
<span className="ml-3 text-sm text-[#848b9b]">{cat.slug}</span>
{cat.description && <span className="ml-3 text-sm text-[#848b9b]">- {cat.description}</span>}
<span className="ml-3 text-xs text-[#848b9b]">{cat.tree_count} trees</span>
<span className="font-medium text-foreground">{cat.name}</span>
<span className="ml-3 text-sm text-muted-foreground">{cat.slug}</span>
{cat.description && <span className="ml-3 text-sm text-muted-foreground">- {cat.description}</span>}
<span className="ml-3 text-xs text-muted-foreground">{cat.tree_count} trees</span>
</div>
<div className="flex items-center gap-1">
<button onClick={() => openEdit(cat)} className="rounded-md p-1.5 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]">
<button onClick={() => openEdit(cat)} className="rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground">
<Pencil className="h-4 w-4" />
</button>
<button onClick={() => handleDelete(cat.id)} className="rounded-md p-1.5 text-[#848b9b] hover:bg-red-400/10 hover:text-red-400">
<button onClick={() => handleDelete(cat.id)} className="rounded-md p-1.5 text-muted-foreground hover:bg-red-400/10 hover:text-red-400">
<Trash2 className="h-4 w-4" />
</button>
</div>
@@ -138,15 +138,15 @@ export function TeamCategoriesPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Name</label>
<Input type="text" value={form.name} onChange={(e) => { const name = e.target.value; setForm(f => ({ ...f, name, slug: generateSlug(name) })) }} placeholder="e.g. Networking" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Slug</label>
<label className="mb-1 block text-sm font-medium text-foreground">Slug</label>
<Input type="text" value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Description</label>
<label className="mb-1 block text-sm font-medium text-foreground">Description</label>
<Input type="text" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} placeholder="Optional" />
</div>
</div>
@@ -163,15 +163,15 @@ export function TeamCategoriesPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Name</label>
<Input type="text" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Slug</label>
<label className="mb-1 block text-sm font-medium text-foreground">Slug</label>
<Input type="text" value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Description</label>
<label className="mb-1 block text-sm font-medium text-foreground">Description</label>
<Input type="text" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} placeholder="Optional" />
</div>
</div>

View File

@@ -64,7 +64,7 @@ export function AuditLogsPage() {
render: (log) => (
<button
onClick={() => setExpandedId(expandedId === log.id ? null : log.id)}
className="p-1 text-[#848b9b] hover:text-[#e2e5eb]"
className="p-1 text-muted-foreground hover:text-foreground"
>
{expandedId === log.id ? (
<ChevronDown className="h-4 w-4" />
@@ -78,14 +78,14 @@ export function AuditLogsPage() {
key: 'action',
header: 'Action',
render: (log) => (
<span className="text-sm font-medium text-[#e2e5eb]">{log.action}</span>
<span className="text-sm font-medium text-foreground">{log.action}</span>
),
},
{
key: 'resource',
header: 'Resource',
render: (log) => (
<span className="text-sm text-[#848b9b]">
<span className="text-sm text-muted-foreground">
{log.resource_type}{log.resource_id ? ` (${log.resource_id.slice(0, 8)}...)` : ''}
</span>
),
@@ -94,14 +94,14 @@ export function AuditLogsPage() {
key: 'user',
header: 'User',
render: (log) => (
<span className="text-sm text-[#848b9b]">{log.user_email || 'System'}</span>
<span className="text-sm text-muted-foreground">{log.user_email || 'System'}</span>
),
},
{
key: 'created_at',
header: 'Time',
render: (log) => (
<span className="text-sm text-[#848b9b]">
<span className="text-sm text-muted-foreground">
{new Date(log.created_at).toLocaleString()}
</span>
),
@@ -117,8 +117,8 @@ export function AuditLogsPage() {
<button
onClick={handleExport}
className={cn(
'flex items-center gap-2 rounded-md border border-[#1e2130] px-4 py-2 text-sm font-medium',
'text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]'
'flex items-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium',
'text-muted-foreground hover:bg-accent hover:text-foreground'
)}
>
<Download className="h-4 w-4" />
@@ -134,8 +134,8 @@ export function AuditLogsPage() {
onChange={(e) => { setActionFilter(e.target.value); setPage(1) }}
placeholder="Filter by action..."
className={cn(
'h-9 rounded-md border border-[#1e2130] bg-[#14161d] px-3 text-sm text-[#e2e5eb]',
'placeholder:text-[#848b9b] focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
'h-9 rounded-md border border-border bg-card px-3 text-sm text-foreground',
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
/>
<input
@@ -144,8 +144,8 @@ export function AuditLogsPage() {
onChange={(e) => { setResourceFilter(e.target.value); setPage(1) }}
placeholder="Filter by resource type..."
className={cn(
'h-9 rounded-md border border-[#1e2130] bg-[#14161d] px-3 text-sm text-[#e2e5eb]',
'placeholder:text-[#848b9b] focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
'h-9 rounded-md border border-border bg-card px-3 text-sm text-foreground',
'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
/>
</div>
@@ -166,9 +166,9 @@ export function AuditLogsPage() {
{/* Expanded details row */}
{expandedId && logs.find(l => l.id === expandedId)?.details && (
<div className="rounded-md border border-[#1e2130] bg-accent p-4">
<h4 className="mb-2 text-sm font-medium text-[#e2e5eb]">Details</h4>
<pre className="overflow-x-auto rounded bg-[#14161d] p-3 text-xs text-[#848b9b]">
<div className="rounded-md border border-border bg-accent p-4">
<h4 className="mb-2 text-sm font-medium text-foreground">Details</h4>
<pre className="overflow-x-auto rounded bg-card p-3 text-xs text-muted-foreground">
{JSON.stringify(logs.find(l => l.id === expandedId)?.details, null, 2)}
</pre>
</div>

View File

@@ -14,13 +14,13 @@ interface MetricCardProps {
function MetricCard({ label, value, icon }: MetricCardProps) {
return (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<div className="bg-card border border-border rounded-xl p-6">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[#848b9b]">{label}</p>
<p className="mt-1 text-3xl font-bold text-[#e2e5eb]">{value}</p>
<p className="text-sm text-muted-foreground">{label}</p>
<p className="mt-1 text-3xl font-bold text-foreground">{value}</p>
</div>
<div className="rounded-lg bg-accent p-3 text-[#848b9b]">{icon}</div>
<div className="rounded-lg bg-accent p-3 text-muted-foreground">{icon}</div>
</div>
</div>
)
@@ -71,18 +71,18 @@ export function DashboardPage() {
{/* Recent Activity */}
{activity.length > 0 && (
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Recent Activity</h2>
<h2 className="text-lg font-semibold text-foreground">Recent Activity</h2>
<div className="mt-3 space-y-2">
{activity.slice(0, 10).map((entry) => (
<div key={entry.id} className="flex items-center justify-between rounded-md border border-[#1e2130] px-4 py-3 text-sm">
<div key={entry.id} className="flex items-center justify-between rounded-md border border-border px-4 py-3 text-sm">
<div>
<span className="font-medium text-[#e2e5eb]">{entry.action}</span>
<span className="ml-2 text-[#848b9b]">{entry.resource_type}</span>
<span className="font-medium text-foreground">{entry.action}</span>
<span className="ml-2 text-muted-foreground">{entry.resource_type}</span>
{entry.user_email && (
<span className="ml-2 text-[#848b9b]">by {entry.user_email}</span>
<span className="ml-2 text-muted-foreground">by {entry.user_email}</span>
)}
</div>
<span className="text-xs text-[#848b9b]">
<span className="text-xs text-muted-foreground">
{new Date(entry.created_at).toLocaleString()}
</span>
</div>
@@ -93,18 +93,18 @@ export function DashboardPage() {
{/* Quick Links */}
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Quick Links</h2>
<h2 className="text-lg font-semibold text-foreground">Quick Links</h2>
<div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
{quickLinks.map((link) => (
<Link
key={link.to}
to={link.to}
className={cn(
'flex items-center gap-3 bg-[#14161d] border border-[#1e2130] rounded-xl p-4',
'text-sm font-medium text-[#e2e5eb] transition-colors hover:bg-accent'
'flex items-center gap-3 bg-card border border-border rounded-xl p-4',
'text-sm font-medium text-foreground transition-colors hover:bg-accent'
)}
>
<link.icon className="h-5 w-5 text-[#848b9b]" />
<link.icon className="h-5 w-5 text-muted-foreground" />
{link.label}
</Link>
))}

View File

@@ -95,11 +95,11 @@ export function FeatureFlagsPage() {
const flagColumns: Column<FeatureFlagResponse>[] = [
{ key: 'name', header: 'Name', render: (f) => (
<div>
<div className="font-medium text-[#e2e5eb]">{f.display_name}</div>
<div className="text-xs text-[#848b9b]">{f.flag_key}</div>
<div className="font-medium text-foreground">{f.display_name}</div>
<div className="text-xs text-muted-foreground">{f.flag_key}</div>
</div>
)},
{ key: 'description', header: 'Description', render: (f) => <span className="text-sm text-[#848b9b]">{f.description || '-'}</span> },
{ key: 'description', header: 'Description', render: (f) => <span className="text-sm text-muted-foreground">{f.description || '-'}</span> },
...PLANS.map(plan => ({
key: plan,
header: plan.charAt(0).toUpperCase() + plan.slice(1),
@@ -133,10 +133,10 @@ export function FeatureFlagsPage() {
]
const overrideColumns: Column<AccountFeatureOverrideResponse>[] = [
{ key: 'account', header: 'Account', render: (o) => <span className="text-sm font-medium text-[#e2e5eb]">{o.account_display_code || o.account_id.slice(0, 8)}</span> },
{ key: 'flag', header: 'Flag', render: (o) => <span className="text-sm text-[#848b9b]">{o.flag_display_name || o.flag_key || o.flag_id.slice(0, 8)}</span> },
{ key: 'account', header: 'Account', render: (o) => <span className="text-sm font-medium text-foreground">{o.account_display_code || o.account_id.slice(0, 8)}</span> },
{ key: 'flag', header: 'Flag', render: (o) => <span className="text-sm text-muted-foreground">{o.flag_display_name || o.flag_key || o.flag_id.slice(0, 8)}</span> },
{ key: 'enabled', header: 'Enabled', render: (o) => <StatusBadge variant={o.enabled ? 'success' : 'destructive'}>{o.enabled ? 'Yes' : 'No'}</StatusBadge> },
{ key: 'note', header: 'Note', render: (o) => <span className="text-sm text-[#848b9b]">{o.note || '-'}</span> },
{ key: 'note', header: 'Note', render: (o) => <span className="text-sm text-muted-foreground">{o.note || '-'}</span> },
{
key: 'actions', header: '', className: 'w-12',
render: (o) => (
@@ -147,7 +147,7 @@ export function FeatureFlagsPage() {
},
]
const selectClass = cn('w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]', 'placeholder:text-[#848b9b] focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20')
const selectClass = 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">
@@ -163,7 +163,7 @@ export function FeatureFlagsPage() {
/>
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Feature Matrix</h2>
<h2 className="text-lg font-semibold text-foreground">Feature Matrix</h2>
<div className="mt-3">
<DataTable columns={flagColumns} data={flags} keyExtractor={(f) => f.id} isLoading={loading}
emptyState={<EmptyState icon={<ToggleLeft className="h-12 w-12" />} title="No feature flags" description="Create feature flags to control availability per plan." />}
@@ -173,7 +173,7 @@ export function FeatureFlagsPage() {
<div>
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold text-[#e2e5eb]">Account Overrides</h2>
<h2 className="text-lg font-semibold text-foreground">Account Overrides</h2>
<Button onClick={() => setOverrideOpen(true)}>
<Plus className="h-4 w-4" />
Add Override
@@ -197,15 +197,15 @@ export function FeatureFlagsPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Flag Key</label>
<label className="mb-1 block text-sm font-medium text-foreground">Flag Key</label>
<Input type="text" value={createForm.flag_key} onChange={(e) => setCreateForm({ ...createForm, flag_key: e.target.value })} placeholder="e.g. custom_branding" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Display Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Display Name</label>
<Input type="text" value={createForm.display_name} onChange={(e) => setCreateForm({ ...createForm, display_name: e.target.value })} placeholder="e.g. Custom Branding" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Description</label>
<label className="mb-1 block text-sm font-medium text-foreground">Description</label>
<Input type="text" value={createForm.description ?? ''} onChange={(e) => setCreateForm({ ...createForm, description: e.target.value || null })} placeholder="Optional description" />
</div>
</div>
@@ -222,22 +222,22 @@ export function FeatureFlagsPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Display Code</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Display Code</label>
<Input type="text" value={overrideForm.account_display_code} onChange={(e) => setOverrideForm({ ...overrideForm, account_display_code: e.target.value })} placeholder="e.g. ABC-1234" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Feature Flag</label>
<label className="mb-1 block text-sm font-medium text-foreground">Feature Flag</label>
<select value={overrideForm.flag_id} onChange={(e) => setOverrideForm({ ...overrideForm, flag_id: e.target.value })} className={selectClass}>
<option value="">Select a flag...</option>
{flags.map(f => <option key={f.id} value={f.id}>{f.display_name}</option>)}
</select>
</div>
<div className="flex items-center gap-2">
<input type="checkbox" id="override-enabled" checked={overrideForm.enabled} onChange={(e) => setOverrideForm({ ...overrideForm, enabled: e.target.checked })} className="h-4 w-4 rounded border-[#1e2130]" />
<label htmlFor="override-enabled" className="text-sm font-medium text-[#e2e5eb]">Enabled</label>
<input type="checkbox" id="override-enabled" checked={overrideForm.enabled} onChange={(e) => setOverrideForm({ ...overrideForm, enabled: e.target.checked })} className="h-4 w-4 rounded border-border" />
<label htmlFor="override-enabled" className="text-sm font-medium text-foreground">Enabled</label>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Note</label>
<label className="mb-1 block text-sm font-medium text-foreground">Note</label>
<Input type="text" value={overrideForm.note ?? ''} onChange={(e) => setOverrideForm({ ...overrideForm, note: e.target.value || null })} placeholder="Reason" />
</div>
</div>

View File

@@ -89,7 +89,7 @@ function SortOrderInput({
}
}}
className={cn(
'w-20 rounded-[8px] border border-[#1e2130] bg-[#14161d] px-2 py-1 text-sm text-[#e2e5eb]',
'w-20 rounded-[8px] border border-border bg-card px-2 py-1 text-sm text-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none',
disabled && 'cursor-not-allowed opacity-50',
)}
@@ -117,19 +117,19 @@ function FilterBar({
return (
<div className="flex flex-wrap items-center gap-3">
<div className="relative flex-1 min-w-[180px] max-w-xs">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#848b9b] pointer-events-none" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" />
<input
type="text"
placeholder="Search by name…"
value={search}
onChange={e => onSearchChange(e.target.value)}
className={cn(
'w-full rounded-lg border border-[#1e2130] bg-[#14161d] pl-9 pr-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b]',
'w-full rounded-lg border border-border bg-card pl-9 pr-3 py-2 text-sm text-foreground placeholder:text-muted-foreground',
'focus:border-[rgba(6,182,212,0.3)] focus:outline-none',
)}
/>
</div>
<div className="flex rounded-lg border border-[#1e2130] overflow-hidden text-sm">
<div className="flex rounded-lg border border-border overflow-hidden text-sm">
{(['all', 'featured', 'unfeatured'] as FilterMode[]).map(mode => (
<button
key={mode}
@@ -138,7 +138,7 @@ function FilterBar({
'px-3 py-1.5 capitalize transition-colors',
filter === mode
? 'bg-primary text-white font-semibold'
: 'text-[#848b9b] hover:text-[#e2e5eb] bg-[#14161d]',
: 'text-muted-foreground hover:text-foreground bg-card',
)}
>
{mode}
@@ -166,7 +166,7 @@ function FlowsTable({
}) {
if (flows.length === 0) {
return (
<p className="py-8 text-center text-sm text-[#848b9b]">No flows match the current filter.</p>
<p className="py-8 text-center text-sm text-muted-foreground">No flows match the current filter.</p>
)
}
@@ -174,19 +174,19 @@ function FlowsTable({
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1e2130] text-left">
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Name</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Type</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Featured</th>
<th className="pb-3 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Sort Order</th>
<tr className="border-b border-border text-left">
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Name</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Type</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Featured</th>
<th className="pb-3 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Sort Order</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{flows.map(flow => (
<tr key={flow.id} className="group hover:bg-[rgba(255,255,255,0.02)] transition-colors">
<td className="py-3 pr-4 text-[#e2e5eb] font-medium">{flow.name}</td>
<td className="py-3 pr-4 text-foreground font-medium">{flow.name}</td>
<td className="py-3 pr-4">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] rounded-full px-2 py-0.5 border border-[#1e2130] text-[#848b9b]">
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] rounded-full px-2 py-0.5 border border-border text-muted-foreground">
{flow.tree_type}
</span>
</td>
@@ -234,7 +234,7 @@ function ScriptsTable({
}) {
if (scripts.length === 0) {
return (
<p className="py-8 text-center text-sm text-[#848b9b]">No scripts match the current filter.</p>
<p className="py-8 text-center text-sm text-muted-foreground">No scripts match the current filter.</p>
)
}
@@ -242,24 +242,24 @@ function ScriptsTable({
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1e2130] text-left">
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Name</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Status</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Featured</th>
<th className="pb-3 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Sort Order</th>
<tr className="border-b border-border text-left">
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Name</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Status</th>
<th className="pb-3 pr-4 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Featured</th>
<th className="pb-3 font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Sort Order</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{scripts.map(script => (
<tr key={script.id} className="group hover:bg-[rgba(255,255,255,0.02)] transition-colors">
<td className="py-3 pr-4 text-[#e2e5eb] font-medium">{script.name}</td>
<td className="py-3 pr-4 text-foreground font-medium">{script.name}</td>
<td className="py-3 pr-4">
<span
className={cn(
'font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] rounded-full px-2 py-0.5 border',
script.is_active
? 'border-emerald-400/30 text-emerald-400 bg-emerald-400/10'
: 'border-[#1e2130] text-[#848b9b]',
: 'border-border text-muted-foreground',
)}
>
{script.is_active ? 'Active' : 'Inactive'}
@@ -417,7 +417,7 @@ export default function GalleryManagementPage() {
/>
{loading ? (
<div className="flex items-center justify-center py-16 text-[#848b9b] text-sm">
<div className="flex items-center justify-center py-16 text-muted-foreground text-sm">
Loading gallery items
</div>
) : (
@@ -426,14 +426,14 @@ export default function GalleryManagementPage() {
<div className="flex gap-4 flex-wrap">
<div className="card-flat rounded-[12px] px-4 py-3 flex items-center gap-3">
<Star className="h-4 w-4 text-amber-400 fill-amber-400" />
<span className="text-sm text-[#848b9b]">
<span className="text-[#e2e5eb] font-semibold">{featuredFlowCount}</span> featured flow{featuredFlowCount !== 1 ? 's' : ''}
<span className="text-sm text-muted-foreground">
<span className="text-foreground font-semibold">{featuredFlowCount}</span> featured flow{featuredFlowCount !== 1 ? 's' : ''}
</span>
</div>
<div className="card-flat rounded-[12px] px-4 py-3 flex items-center gap-3">
<Star className="h-4 w-4 text-amber-400 fill-amber-400" />
<span className="text-sm text-[#848b9b]">
<span className="text-[#e2e5eb] font-semibold">{featuredScriptCount}</span> featured script{featuredScriptCount !== 1 ? 's' : ''}
<span className="text-sm text-muted-foreground">
<span className="text-foreground font-semibold">{featuredScriptCount}</span> featured script{featuredScriptCount !== 1 ? 's' : ''}
</span>
</div>
</div>
@@ -442,8 +442,8 @@ export default function GalleryManagementPage() {
<section>
<div className="mb-4 flex items-center justify-between gap-4 flex-wrap">
<div>
<h2 className="text-base font-semibold text-[#e2e5eb]">Featured Flows</h2>
<p className="text-xs text-[#848b9b] mt-0.5">
<h2 className="text-base font-semibold text-foreground">Featured Flows</h2>
<p className="text-xs text-muted-foreground mt-0.5">
Toggle flows to show in the gallery. Lower sort order = shown first.
</p>
</div>
@@ -469,8 +469,8 @@ export default function GalleryManagementPage() {
<section>
<div className="mb-4 flex items-center justify-between gap-4 flex-wrap">
<div>
<h2 className="text-base font-semibold text-[#e2e5eb]">Featured Scripts</h2>
<p className="text-xs text-[#848b9b] mt-0.5">
<h2 className="text-base font-semibold text-foreground">Featured Scripts</h2>
<p className="text-xs text-muted-foreground mt-0.5">
Toggle scripts to show in the gallery. Lower sort order = shown first.
</p>
</div>

View File

@@ -73,10 +73,10 @@ export function GlobalCategoriesPage() {
}
const columns: Column<AdminCategory>[] = [
{ key: 'name', header: 'Name', render: (c) => <span className="font-medium text-[#e2e5eb]">{c.name}</span> },
{ key: 'slug', header: 'Slug', render: (c) => <span className="text-sm text-[#848b9b]">{c.slug}</span> },
{ key: 'description', header: 'Description', render: (c) => <span className="text-sm text-[#848b9b]">{c.description || '-'}</span> },
{ key: 'tree_count', header: 'Trees', render: (c) => <span className="text-sm text-[#848b9b]">{c.tree_count}</span> },
{ key: 'name', header: 'Name', render: (c) => <span className="font-medium text-foreground">{c.name}</span> },
{ key: 'slug', header: 'Slug', render: (c) => <span className="text-sm text-muted-foreground">{c.slug}</span> },
{ key: 'description', header: 'Description', render: (c) => <span className="text-sm text-muted-foreground">{c.description || '-'}</span> },
{ key: 'tree_count', header: 'Trees', render: (c) => <span className="text-sm text-muted-foreground">{c.tree_count}</span> },
{
key: 'actions', header: '', className: 'w-12',
render: (c) => (
@@ -124,15 +124,15 @@ export function GlobalCategoriesPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Name</label>
<Input type="text" value={form.name} onChange={(e) => { const name = e.target.value; setForm(f => ({ ...f, name, slug: generateSlug(name) })) }} placeholder="e.g. Networking" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Slug</label>
<label className="mb-1 block text-sm font-medium text-foreground">Slug</label>
<Input type="text" value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} placeholder="e.g. networking" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Description</label>
<label className="mb-1 block text-sm font-medium text-foreground">Description</label>
<Input type="text" value={form.description ?? ''} onChange={(e) => setForm({ ...form, description: e.target.value || null })} placeholder="Optional description" />
</div>
</div>
@@ -153,15 +153,15 @@ export function GlobalCategoriesPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Name</label>
<Input type="text" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="e.g. Networking" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Slug</label>
<label className="mb-1 block text-sm font-medium text-foreground">Slug</label>
<Input type="text" value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} placeholder="e.g. networking" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Description</label>
<label className="mb-1 block text-sm font-medium text-foreground">Description</label>
<Input type="text" value={form.description ?? ''} onChange={(e) => setForm({ ...form, description: e.target.value || null })} placeholder="Optional description" />
</div>
</div>

View File

@@ -110,8 +110,8 @@ export function InviteCodesPage() {
}
const selectClass = cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'placeholder:text-[#848b9b] focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
'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'
)
const columns: Column<InviteCodeResponse>[] = [
@@ -119,7 +119,7 @@ export function InviteCodesPage() {
key: 'code',
header: 'Code',
render: (c) => (
<code className="rounded bg-accent px-2 py-1 text-sm font-mono text-[#848b9b]">{c.code}</code>
<code className="rounded bg-accent px-2 py-1 text-sm font-mono text-muted-foreground">{c.code}</code>
),
},
{
@@ -130,12 +130,12 @@ export function InviteCodesPage() {
{c.email_sent ? (
<MailCheck className="h-3.5 w-3.5 text-emerald-400" />
) : (
<Mail className="h-3.5 w-3.5 text-[#848b9b]" />
<Mail className="h-3.5 w-3.5 text-muted-foreground" />
)}
<span className="text-sm text-[#848b9b]">{c.email}</span>
<span className="text-sm text-muted-foreground">{c.email}</span>
</div>
) : (
<span className="text-sm text-[#848b9b]">&mdash;</span>
<span className="text-sm text-muted-foreground">&mdash;</span>
),
},
{
@@ -151,9 +151,9 @@ export function InviteCodesPage() {
key: 'trial',
header: 'Trial',
render: (c) => c.has_trial ? (
<span className="text-sm text-[#848b9b]">{c.trial_duration_days}d</span>
<span className="text-sm text-muted-foreground">{c.trial_duration_days}d</span>
) : (
<span className="text-sm text-[#848b9b]">&mdash;</span>
<span className="text-sm text-muted-foreground">&mdash;</span>
),
},
{
@@ -170,7 +170,7 @@ export function InviteCodesPage() {
key: 'expires_at',
header: 'Expires',
render: (c) => (
<span className="text-sm text-[#848b9b]">
<span className="text-sm text-muted-foreground">
{c.expires_at ? new Date(c.expires_at).toLocaleDateString() : 'Never'}
</span>
),
@@ -179,7 +179,7 @@ export function InviteCodesPage() {
key: 'created_at',
header: 'Created',
render: (c) => (
<span className="text-sm text-[#848b9b]">
<span className="text-sm text-muted-foreground">
{new Date(c.created_at).toLocaleDateString()}
</span>
),
@@ -254,7 +254,7 @@ export function InviteCodesPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Recipient Email</label>
<label className="mb-1 block text-sm font-medium text-foreground">Recipient Email</label>
<Input
type="email"
value={email}
@@ -264,7 +264,7 @@ export function InviteCodesPage() {
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Plan</label>
<label className="mb-1 block text-sm font-medium text-foreground">Plan</label>
<select
aria-label="Plan"
value={assignedPlan}
@@ -283,7 +283,7 @@ export function InviteCodesPage() {
{assignedPlan !== 'free' && (
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Trial Duration (days)</label>
<label className="mb-1 block text-sm font-medium text-foreground">Trial Duration (days)</label>
<Input
type="number"
value={trialDays}
@@ -292,12 +292,12 @@ export function InviteCodesPage() {
min={1}
max={90}
/>
<p className="mt-1 text-xs text-[#848b9b]">Leave empty for no trial account gets full plan immediately.</p>
<p className="mt-1 text-xs text-muted-foreground">Leave empty for no trial account gets full plan immediately.</p>
</div>
)}
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Expires in (days)</label>
<label className="mb-1 block text-sm font-medium text-foreground">Expires in (days)</label>
<Input
type="number"
value={expiresInDays}
@@ -307,7 +307,7 @@ export function InviteCodesPage() {
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Note</label>
<label className="mb-1 block text-sm font-medium text-foreground">Note</label>
<Input
type="text"
value={note}

View File

@@ -76,16 +76,16 @@ export function PlanLimitsPage() {
}
const planColumns: Column<PlanLimitConfig>[] = [
{ key: 'plan', header: 'Plan', render: (p) => <span className="font-medium text-[#e2e5eb] capitalize">{p.plan}</span> },
{ key: 'max_trees', header: 'Max Trees', render: (p) => <span className="text-sm text-[#848b9b]">{p.max_trees ?? 'Unlimited'}</span> },
{ key: 'max_sessions', header: 'Sessions/Month', render: (p) => <span className="text-sm text-[#848b9b]">{p.max_sessions_per_month ?? 'Unlimited'}</span> },
{ key: 'max_users', header: 'Max Users', render: (p) => <span className="text-sm text-[#848b9b]">{p.max_users ?? 'Unlimited'}</span> },
{ key: 'plan', header: 'Plan', render: (p) => <span className="font-medium text-foreground capitalize">{p.plan}</span> },
{ key: 'max_trees', header: 'Max Trees', render: (p) => <span className="text-sm text-muted-foreground">{p.max_trees ?? 'Unlimited'}</span> },
{ key: 'max_sessions', header: 'Sessions/Month', render: (p) => <span className="text-sm text-muted-foreground">{p.max_sessions_per_month ?? 'Unlimited'}</span> },
{ key: 'max_users', header: 'Max Users', render: (p) => <span className="text-sm text-muted-foreground">{p.max_users ?? 'Unlimited'}</span> },
{
key: 'actions', header: '', className: 'w-12',
render: (p) => (
<button
onClick={() => setEditPlan({ ...p })}
className="rounded-md px-3 py-1 text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md px-3 py-1 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
Edit
</button>
@@ -94,11 +94,11 @@ export function PlanLimitsPage() {
]
const overrideColumns: Column<AccountOverrideResponse>[] = [
{ key: 'account', header: 'Account', render: (o) => <span className="text-sm font-medium text-[#e2e5eb]">{o.account_display_code || o.account_id.slice(0, 8)}</span> },
{ key: 'max_trees', header: 'Max Trees', render: (o) => <span className="text-sm text-[#848b9b]">{o.override_max_trees ?? '-'}</span> },
{ key: 'max_sessions', header: 'Sessions/Month', render: (o) => <span className="text-sm text-[#848b9b]">{o.override_max_sessions_per_month ?? '-'}</span> },
{ key: 'max_users', header: 'Max Users', render: (o) => <span className="text-sm text-[#848b9b]">{o.override_max_users ?? '-'}</span> },
{ key: 'note', header: 'Note', render: (o) => <span className="text-sm text-[#848b9b]">{o.note || '-'}</span> },
{ key: 'account', header: 'Account', render: (o) => <span className="text-sm font-medium text-foreground">{o.account_display_code || o.account_id.slice(0, 8)}</span> },
{ key: 'max_trees', header: 'Max Trees', render: (o) => <span className="text-sm text-muted-foreground">{o.override_max_trees ?? '-'}</span> },
{ key: 'max_sessions', header: 'Sessions/Month', render: (o) => <span className="text-sm text-muted-foreground">{o.override_max_sessions_per_month ?? '-'}</span> },
{ key: 'max_users', header: 'Max Users', render: (o) => <span className="text-sm text-muted-foreground">{o.override_max_users ?? '-'}</span> },
{ key: 'note', header: 'Note', render: (o) => <span className="text-sm text-muted-foreground">{o.note || '-'}</span> },
{
key: 'actions', header: '', className: 'w-12',
render: (o) => (
@@ -114,7 +114,7 @@ export function PlanLimitsPage() {
<PageHeader title="Plan Limits" description="Configure plan tier limits and account-specific overrides" />
<div>
<h2 className="text-lg font-semibold text-[#e2e5eb]">Plan Defaults</h2>
<h2 className="text-lg font-semibold text-foreground">Plan Defaults</h2>
<div className="mt-3">
<DataTable columns={planColumns} data={plans} keyExtractor={(p) => p.plan} isLoading={loading} />
</div>
@@ -122,7 +122,7 @@ export function PlanLimitsPage() {
<div>
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold text-[#e2e5eb]">Account Overrides</h2>
<h2 className="text-lg font-semibold text-foreground">Account Overrides</h2>
<Button onClick={() => setCreateOverride(true)}>
<Plus className="h-4 w-4" />
Add Override
@@ -155,15 +155,15 @@ export function PlanLimitsPage() {
{editPlan && (
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Trees (empty = unlimited)</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Trees (empty = unlimited)</label>
<Input type="number" value={editPlan.max_trees ?? ''} onChange={(e) => setEditPlan({ ...editPlan, max_trees: e.target.value ? parseInt(e.target.value) : null })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Sessions/Month (empty = unlimited)</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Sessions/Month (empty = unlimited)</label>
<Input type="number" value={editPlan.max_sessions_per_month ?? ''} onChange={(e) => setEditPlan({ ...editPlan, max_sessions_per_month: e.target.value ? parseInt(e.target.value) : null })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Users (empty = unlimited)</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Users (empty = unlimited)</label>
<Input type="number" value={editPlan.max_users ?? ''} onChange={(e) => setEditPlan({ ...editPlan, max_users: e.target.value ? parseInt(e.target.value) : null })} />
</div>
</div>
@@ -185,23 +185,23 @@ export function PlanLimitsPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Display Code</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Display Code</label>
<Input type="text" value={overrideForm.account_display_code} onChange={(e) => setOverrideForm({ ...overrideForm, account_display_code: e.target.value })} placeholder="e.g. ABC-1234" />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Trees Override</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Trees Override</label>
<Input type="number" value={overrideForm.override_max_trees ?? ''} onChange={(e) => setOverrideForm({ ...overrideForm, override_max_trees: e.target.value ? parseInt(e.target.value) : null })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Sessions/Month Override</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Sessions/Month Override</label>
<Input type="number" value={overrideForm.override_max_sessions_per_month ?? ''} onChange={(e) => setOverrideForm({ ...overrideForm, override_max_sessions_per_month: e.target.value ? parseInt(e.target.value) : null })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Max Users Override</label>
<label className="mb-1 block text-sm font-medium text-foreground">Max Users Override</label>
<Input type="number" value={overrideForm.override_max_users ?? ''} onChange={(e) => setOverrideForm({ ...overrideForm, override_max_users: e.target.value ? parseInt(e.target.value) : null })} />
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Note</label>
<label className="mb-1 block text-sm font-medium text-foreground">Note</label>
<Input type="text" value={overrideForm.note ?? ''} onChange={(e) => setOverrideForm({ ...overrideForm, note: e.target.value || null })} placeholder="Reason for override" />
</div>
</div>

View File

@@ -47,11 +47,11 @@ export function SettingsPage() {
<div className="space-y-6">
<PageHeader title="Platform Settings" description="Global platform configuration" />
<div className="max-w-xl space-y-6 bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<div className="max-w-xl space-y-6 bg-card border border-border rounded-xl p-6">
<div className="flex items-center justify-between">
<div>
<h3 className="font-medium text-[#e2e5eb]">Email Verification</h3>
<p className="text-sm text-[#848b9b]">
<h3 className="font-medium text-foreground">Email Verification</h3>
<p className="text-sm text-muted-foreground">
When enabled, unverified users see a banner prompting them to verify their email.
</p>
</div>
@@ -59,7 +59,7 @@ export function SettingsPage() {
onClick={() => setSettings({ ...settings, email_verification_enabled: !emailVerificationEnabled })}
className={cn(
'h-6 w-10 rounded-full transition-colors',
emailVerificationEnabled ? 'bg-[#22d3ee]' : 'bg-accent'
emailVerificationEnabled ? 'bg-primary' : 'bg-accent'
)}
>
<div className={cn(
@@ -71,8 +71,8 @@ export function SettingsPage() {
<div className="flex items-center justify-between">
<div>
<h3 className="font-medium text-[#e2e5eb]">Maintenance Mode</h3>
<p className="text-sm text-[#848b9b]">
<h3 className="font-medium text-foreground">Maintenance Mode</h3>
<p className="text-sm text-muted-foreground">
When enabled, users will see a maintenance message instead of the app.
</p>
</div>
@@ -93,7 +93,7 @@ export function SettingsPage() {
{maintenanceMode && (
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Maintenance Message</label>
<label className="mb-1 block text-sm font-medium text-foreground">Maintenance Message</label>
<Textarea
value={maintenanceMessage}
onChange={(e) => setSettings({ ...settings, maintenance_message: e.target.value })}
@@ -103,13 +103,13 @@ export function SettingsPage() {
</div>
)}
<div className="border-t border-[#1e2130] pt-4">
<div className="border-t border-border pt-4">
<button
onClick={handleSave}
disabled={saving}
className={cn(
'rounded-md px-4 py-2 text-sm font-medium',
'bg-[#22d3ee] text-white hover:brightness-110',
'bg-primary text-white hover:brightness-110',
'disabled:opacity-50'
)}
>

View File

@@ -71,10 +71,10 @@ export default function SurveyInvitesPage() {
{/* Create Invite Section */}
<div className="card-flat p-6">
<h3 className="font-heading text-sm font-semibold text-[#e2e5eb] mb-4">Create Invite</h3>
<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-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-1.5 block">
<label className="font-sans text-xs 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-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary/30 focus:outline-hidden"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary/30 focus:outline-hidden"
/>
</div>
<div className="flex-1">
<label className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-1.5 block">
<label className="font-sans text-xs 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-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-primary/30 focus:outline-hidden"
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary/30 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-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"
className="inline-flex items-center gap-2 rounded-lg bg-primary 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" />}
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-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"
className="inline-flex items-center gap-2 rounded-lg bg-input border border-border px-4 py-2 text-sm font-medium text-foreground hover:border-border-hover 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" />}
Send Email
@@ -125,14 +125,14 @@ export default function SurveyInvitesPage() {
<div className="mt-4 rounded-lg border border-primary/[0.15] bg-primary/[0.04] p-4">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0 flex-1">
<p className="text-xs text-[#848b9b] mb-1">
<p className="text-xs text-muted-foreground mb-1">
{lastCreated.email_sent ? 'Email sent to ' + lastCreated.recipient_email + '! Link:' : 'Share this link with ' + lastCreated.recipient_name + ':'}
</p>
<p className="truncate text-sm font-mono text-[#e2e5eb]">{lastCreated.survey_url}</p>
<p className="truncate text-sm font-mono text-foreground">{lastCreated.survey_url}</p>
</div>
<button
onClick={() => handleCopy(lastCreated.survey_url)}
className="shrink-0 rounded-lg p-2 text-[#848b9b] hover:bg-[#1e2130] hover:text-[#e2e5eb] transition-colors"
className="shrink-0 rounded-lg p-2 text-muted-foreground hover:bg-border hover:text-foreground transition-colors"
>
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
</button>
@@ -146,26 +146,26 @@ export default function SurveyInvitesPage() {
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-[#1e2130]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Name</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Email</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Status</th>
<th className="px-4 py-3 text-center font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Sent</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Created</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Completed</th>
<th className="px-4 py-3 text-center font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">Link</th>
<tr className="border-b border-border">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Name</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Email</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Status</th>
<th className="px-4 py-3 text-center font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Sent</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Created</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Completed</th>
<th className="px-4 py-3 text-center font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">Link</th>
</tr>
</thead>
<tbody>
{loading ? (
<tr><td colSpan={7} className="px-4 py-8 text-center text-sm text-[#848b9b]">Loading...</td></tr>
<tr><td colSpan={7} className="px-4 py-8 text-center text-sm text-muted-foreground">Loading...</td></tr>
) : invites.length === 0 ? (
<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-muted-foreground">No invites yet</td></tr>
) : (
invites.map(invite => (
<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-[#848b9b]">{invite.recipient_email || '—'}</td>
<tr key={invite.id} className="border-b border-border/50 hover:bg-card transition-colors">
<td className="px-4 py-3 text-sm text-foreground">{invite.recipient_name}</td>
<td className="px-4 py-3 text-sm text-muted-foreground">{invite.recipient_email || '—'}</td>
<td className="px-4 py-3">
<span className={cn(
'inline-flex items-center rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider',
@@ -178,17 +178,17 @@ export default function SurveyInvitesPage() {
</td>
<td className="px-4 py-3 text-center">
{invite.email_sent ? (
<Mail className="mx-auto h-4 w-4 text-[#848b9b]" />
<Mail className="mx-auto h-4 w-4 text-muted-foreground" />
) : (
<span className="text-[#848b9b]/50"></span>
<span className="text-muted-foreground/50"></span>
)}
</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-[#848b9b]">{formatDate(invite.created_at)}</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-[#848b9b]">{invite.completed_at ? formatDate(invite.completed_at) : '—'}</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-muted-foreground">{formatDate(invite.created_at)}</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-muted-foreground">{invite.completed_at ? formatDate(invite.completed_at) : '—'}</td>
<td className="px-4 py-3 text-center">
<button
onClick={() => handleCopy(invite.survey_url)}
className="rounded-lg p-1.5 text-[#848b9b] hover:bg-[#1e2130] hover:text-[#e2e5eb] transition-colors"
className="rounded-lg p-1.5 text-muted-foreground hover:bg-border hover:text-foreground transition-colors"
title="Copy survey link"
>
<Copy className="h-3.5 w-3.5" />

View File

@@ -41,7 +41,7 @@ const QUESTIONS: { id: string; num: string; text: string; type: 'mc' | 'mc-multi
function AnswerDisplay({ value, type }: { value: string | string[] | undefined; type: string }) {
if (!value || (Array.isArray(value) && value.length === 0)) {
return <p className="text-sm italic text-[#848b9b]/60">No answer</p>
return <p className="text-sm italic text-muted-foreground/60">No answer</p>
}
if (type === 'mc-multi' && Array.isArray(value)) {
@@ -50,7 +50,7 @@ function AnswerDisplay({ value, type }: { value: string | string[] | undefined;
{value.map((v, i) => (
<span
key={i}
className="inline-block rounded-full bg-[rgba(34,211,238,0.10)] px-2.5 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#22d3ee]"
className="inline-block rounded-full bg-accent-dim px-2.5 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider text-primary"
>
{v}
</span>
@@ -63,8 +63,8 @@ function AnswerDisplay({ value, type }: { value: string | string[] | undefined;
return (
<ol className="space-y-1">
{value.map((v, i) => (
<li key={i} className="flex items-start gap-2 text-sm text-[#e2e5eb]/90">
<span className="font-sans text-xs text-xs font-bold text-[#22d3ee]">{i + 1}.</span>
<li key={i} className="flex items-start gap-2 text-sm text-foreground/90">
<span className="font-sans text-xs text-xs font-bold text-primary">{i + 1}.</span>
{v}
</li>
))}
@@ -75,12 +75,12 @@ function AnswerDisplay({ value, type }: { value: string | string[] | undefined;
if (type === 'text') {
return (
<div className="border-l-2 border-primary/30 pl-3">
<p className="text-sm text-[#e2e5eb]/90 whitespace-pre-wrap">{String(value)}</p>
<p className="text-sm text-foreground/90 whitespace-pre-wrap">{String(value)}</p>
</div>
)
}
return <p className="text-sm text-[#e2e5eb]/90">{String(value)}</p>
return <p className="text-sm text-foreground/90">{String(value)}</p>
}
function ExpandedDetail({ response }: { response: SurveyResponseDetail }) {
@@ -104,10 +104,10 @@ function ExpandedDetail({ response }: { response: SurveyResponseDetail }) {
border: '1px solid var(--glass-border)',
}}
>
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#22d3ee] mb-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-primary mb-1">
Q{q.num}
</p>
<p className="text-xs text-[#848b9b] mb-2">{q.text}</p>
<p className="text-xs text-muted-foreground mb-2">{q.text}</p>
<AnswerDisplay value={response.responses[q.id]} type={q.type} />
</div>
))}
@@ -150,24 +150,24 @@ function ResponseRow({
<>
<tr
className={cn(
'border-b border-[#1e2130]/50 transition-colors cursor-pointer',
'border-b border-border/50 transition-colors cursor-pointer',
!response.is_read && 'bg-primary/3',
'hover:bg-[#14161d]'
'hover:bg-card'
)}
>
{/* Checkbox */}
<td className="px-2 py-3 w-8" onClick={e => { e.stopPropagation(); onSelect() }}>
{isSelected ? (
<CheckSquare className="h-4 w-4 text-[#22d3ee] cursor-pointer" />
<CheckSquare className="h-4 w-4 text-primary cursor-pointer" />
) : (
<Square className="h-4 w-4 text-[#848b9b]/40 cursor-pointer hover:text-[#848b9b]" />
<Square className="h-4 w-4 text-muted-foreground/40 cursor-pointer hover:text-muted-foreground" />
)}
</td>
{/* Unread dot */}
<td className="px-1 py-3 w-6" onClick={onToggle}>
{!response.is_read && (
<Circle className="h-2.5 w-2.5 fill-primary text-[#22d3ee]" />
<Circle className="h-2.5 w-2.5 fill-primary text-primary" />
)}
</td>
@@ -175,46 +175,46 @@ function ResponseRow({
<td className="px-2 py-3 w-8" onClick={onToggle}>
<ChevronDown
className={cn(
'h-4 w-4 text-[#848b9b] transition-transform',
'h-4 w-4 text-muted-foreground transition-transform',
isExpanded && 'rotate-180'
)}
/>
</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-[#848b9b]" onClick={onToggle}>{index + 1}</td>
<td className={cn('px-4 py-3 text-sm', !response.is_read ? 'text-[#e2e5eb] font-medium' : 'text-[#e2e5eb]')} onClick={onToggle}>
{response.respondent_name || <span className="text-[#848b9b] italic">Anonymous</span>}
<td className="px-4 py-3 font-sans text-xs text-xs text-muted-foreground" onClick={onToggle}>{index + 1}</td>
<td className={cn('px-4 py-3 text-sm', !response.is_read ? 'text-foreground font-medium' : 'text-foreground')} onClick={onToggle}>
{response.respondent_name || <span className="text-muted-foreground italic">Anonymous</span>}
</td>
<td className="px-4 py-3" onClick={onToggle}>
{response.source === 'invite' ? (
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider bg-[rgba(34,211,238,0.10)] text-[#22d3ee]">
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider bg-accent-dim text-primary">
<User className="h-3 w-3" />
Invite
{response.invite_name && (
<span className="text-[#22d3ee]/70">({response.invite_name})</span>
<span className="text-primary/70">({response.invite_name})</span>
)}
</span>
) : (
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider bg-brand-border text-[#848b9b]">
<span className="inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider bg-brand-border text-muted-foreground">
<Link2 className="h-3 w-3" />
Direct
</span>
)}
</td>
<td className="px-4 py-3 font-sans text-xs text-xs text-[#848b9b]" onClick={onToggle}>
<td className="px-4 py-3 font-sans text-xs text-xs text-muted-foreground" onClick={onToggle}>
{new Date(response.created_at).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</td>
<td className="px-4 py-3 text-sm text-[#848b9b]" onClick={onToggle}>
<td className="px-4 py-3 text-sm text-muted-foreground" onClick={onToggle}>
{answeredCount} / {QUESTIONS.length}
</td>
{/* Actions */}
<td className="px-3 py-3 w-10 relative">
<button
onClick={e => { e.stopPropagation(); setShowMenu(!showMenu) }}
className="p-1.5 rounded-lg hover:bg-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
className="p-1.5 rounded-lg hover:bg-border text-muted-foreground hover:text-foreground transition-colors"
>
<MoreHorizontal className="h-4 w-4" />
</button>
@@ -227,14 +227,14 @@ function ResponseRow({
>
<button
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-[#191c25] transition-colors"
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-input transition-colors"
>
{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'}
</button>
<button
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-[#191c25] transition-colors"
className="flex w-full items-center gap-2.5 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-input transition-colors"
>
{response.archived_at ? <ArchiveRestore className="h-3.5 w-3.5" /> : <Archive className="h-3.5 w-3.5" />}
{response.archived_at ? 'Unarchive' : 'Archive'}
@@ -408,7 +408,7 @@ export default function SurveyResponsesPage() {
if (loading) {
return (
<div className="flex items-center justify-center py-20">
<Loader2 className="h-6 w-6 animate-spin text-[#22d3ee]" />
<Loader2 className="h-6 w-6 animate-spin text-primary" />
</div>
)
}
@@ -436,8 +436,8 @@ export default function SurveyResponsesPage() {
className={cn(
'inline-flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-medium transition-colors border',
showArchived
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border-primary/20'
: 'bg-[#191c25] text-[#848b9b] border-[#1e2130] hover:border-[#2a2f3d]'
? 'bg-accent-dim text-primary border-primary/20'
: 'bg-input text-muted-foreground border-border hover:border-border-hover'
)}
>
<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-lg bg-[#191c25] border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#e2e5eb] transition-colors hover:border-[#2a2f3d] disabled:opacity-50"
className="inline-flex items-center gap-2 rounded-lg bg-input border border-border px-4 py-2 text-sm font-medium text-foreground transition-colors hover:border-border-hover disabled:opacity-50"
>
{exporting ? (
<Loader2 className="h-4 w-4 animate-spin" />
@@ -468,28 +468,28 @@ export default function SurveyResponsesPage() {
{/* Stat cards */}
<div className="flex gap-4">
<div className="card-flat px-5 py-4 flex-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
Total Responses
</p>
<p className="text-2xl font-heading font-bold text-[#67e8f9]">
<p className="text-2xl font-heading font-bold text-accent-text">
{data?.total ?? 0}
</p>
</div>
<div className="card-flat px-5 py-4 flex-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
This Week
</p>
<p className="text-2xl font-heading font-bold text-[#e2e5eb]">
<p className="text-2xl font-heading font-bold text-foreground">
{data?.this_week ?? 0}
</p>
</div>
<div className="card-flat px-5 py-4 flex-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b] mb-1">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground mb-1">
Unread
</p>
<p className={cn(
'text-2xl font-heading font-bold',
(data?.unread ?? 0) > 0 ? 'text-[#22d3ee]' : 'text-[#e2e5eb]'
(data?.unread ?? 0) > 0 ? 'text-primary' : 'text-foreground'
)}>
{data?.unread ?? 0}
</p>
@@ -502,27 +502,27 @@ export default function SurveyResponsesPage() {
className="flex items-center gap-3 rounded-xl px-4 py-2.5"
style={{ background: 'rgba(6, 182, 212, 0.08)', border: '1px solid rgba(6, 182, 212, 0.15)' }}
>
<span className="text-sm text-[#22d3ee] font-medium">
<span className="text-sm text-primary font-medium">
{selectedIds.size} selected
</span>
<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-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] 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-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-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] 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-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-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] 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-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-[#848b9b] hover:text-[#e2e5eb] hover:bg-[#1e2130] transition-colors"
className="px-3 py-1.5 rounded-lg text-xs font-medium text-muted-foreground hover:text-foreground hover:bg-border transition-colors"
>
Clear
</button>
@@ -547,11 +547,11 @@ export default function SurveyResponsesPage() {
<div className="card-flat overflow-hidden">
<table className="w-full">
<thead>
<tr className="border-b border-[#1e2130]/50">
<tr className="border-b border-border/50">
<th className="px-2 py-3 w-8">
<button onClick={toggleSelectAll} className="text-[#848b9b]/40 hover:text-[#848b9b]">
<button onClick={toggleSelectAll} className="text-muted-foreground/40 hover:text-muted-foreground">
{selectedIds.size > 0 && selectedIds.size === responses.length ? (
<CheckSquare className="h-4 w-4 text-[#22d3ee]" />
<CheckSquare className="h-4 w-4 text-primary" />
) : (
<Square className="h-4 w-4" />
)}
@@ -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-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
#
</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Respondent
</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Source
</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Date
</th>
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-[#848b9b]">
<th className="px-4 py-3 text-left font-sans text-xs text-[0.625rem] uppercase tracking-widest text-muted-foreground">
Answered
</th>
<th className="px-3 py-3 w-10" />
@@ -580,7 +580,7 @@ export default function SurveyResponsesPage() {
<tbody>
{responses.length === 0 ? (
<tr>
<td colSpan={9} className="px-4 py-12 text-center text-sm text-[#848b9b]">
<td colSpan={9} className="px-4 py-12 text-center text-sm text-muted-foreground">
{showArchived ? 'No archived responses.' : 'No survey responses yet.'}
</td>
</tr>

View File

@@ -189,8 +189,8 @@ export function UserDetailPage() {
}
const selectClass = cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'placeholder:text-[#848b9b] focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
'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'
)
if (loading) {
@@ -224,15 +224,15 @@ export function UserDetailPage() {
<div className="flex items-center gap-4">
<button
onClick={() => navigate('/admin/users')}
className="rounded-md border border-[#1e2130] p-2 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="rounded-md border border-border p-2 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<ArrowLeft className="h-4 w-4" />
</button>
<div className="flex-1">
<h1 className="text-xl font-heading font-semibold text-[#e2e5eb]">
<h1 className="text-xl font-heading font-semibold text-foreground">
{user.full_name || user.email}
</h1>
<p className="text-sm text-[#848b9b]">{user.email}</p>
<p className="text-sm text-muted-foreground">{user.email}</p>
</div>
<div className="flex items-center gap-2">
{user.is_super_admin && (
@@ -252,21 +252,21 @@ export function UserDetailPage() {
{/* Account & Subscription */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-[#848b9b]">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-muted-foreground">
Account & Subscription
</h2>
<dl className="space-y-3">
{user.account && (
<>
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Account</dt>
<dd className="text-sm text-[#e2e5eb]">{user.account.name}</dd>
<dt className="text-sm text-muted-foreground">Account</dt>
<dd className="text-sm text-foreground">{user.account.name}</dd>
</div>
{user.account.display_code && (
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Display Code</dt>
<dd className="text-sm font-mono text-[#848b9b]">{user.account.display_code}</dd>
<dt className="text-sm text-muted-foreground">Display Code</dt>
<dd className="text-sm font-mono text-muted-foreground">{user.account.display_code}</dd>
</div>
)}
</>
@@ -274,13 +274,13 @@ export function UserDetailPage() {
{user.subscription ? (
<>
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Plan</dt>
<dd className="text-sm font-semibold text-[#e2e5eb]">
<dt className="text-sm text-muted-foreground">Plan</dt>
<dd className="text-sm font-semibold text-foreground">
{user.subscription.plan.charAt(0).toUpperCase() + user.subscription.plan.slice(1)}
</dd>
</div>
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Status</dt>
<dt className="text-sm text-muted-foreground">Status</dt>
<dd>
<StatusBadge variant={user.subscription.status === 'trialing' ? 'warning' : 'success'}>
{user.subscription.status}
@@ -289,24 +289,24 @@ export function UserDetailPage() {
</div>
{user.subscription.current_period_end && (
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Period End</dt>
<dd className="text-sm text-[#848b9b]">{fmt(user.subscription.current_period_end)}</dd>
<dt className="text-sm text-muted-foreground">Period End</dt>
<dd className="text-sm text-muted-foreground">{fmt(user.subscription.current_period_end)}</dd>
</div>
)}
</>
) : (
<div className="text-sm text-[#848b9b]">No subscription</div>
<div className="text-sm text-muted-foreground">No subscription</div>
)}
<div className="flex justify-between">
<dt className="text-sm text-[#848b9b]">Joined</dt>
<dd className="text-sm text-[#848b9b]">{fmt(user.created_at)}</dd>
<dt className="text-sm text-muted-foreground">Joined</dt>
<dd className="text-sm text-muted-foreground">{fmt(user.created_at)}</dd>
</div>
</dl>
</div>
{/* Admin Actions */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-[#848b9b]">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-muted-foreground">
Admin Actions
</h2>
<div className="space-y-3">
@@ -317,16 +317,16 @@ export function UserDetailPage() {
setSelectedPlan(user.subscription?.plan || 'free')
setPlanModalOpen(true)
}}
className="flex w-full items-center gap-3 rounded-lg border border-[#1e2130] px-4 py-3 text-left text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex w-full items-center gap-3 rounded-lg border border-border px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Shield className="h-4 w-4 text-[#848b9b]" />
<Shield className="h-4 w-4 text-muted-foreground" />
Change Plan
</button>
<button
onClick={() => setTrialModalOpen(true)}
className="flex w-full items-center gap-3 rounded-lg border border-[#1e2130] px-4 py-3 text-left text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex w-full items-center gap-3 rounded-lg border border-border px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Clock className="h-4 w-4 text-[#848b9b]" />
<Clock className="h-4 w-4 text-muted-foreground" />
{user.subscription?.status === 'trialing' ? 'Extend Trial' : 'Start Trial'}
</button>
</>
@@ -349,9 +349,9 @@ export function UserDetailPage() {
setResetTempPassword(null)
setResetModalOpen(true)
}}
className="flex w-full items-center gap-3 rounded-lg border border-[#1e2130] px-4 py-3 text-left text-sm text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb]"
className="flex w-full items-center gap-3 rounded-lg border border-border px-4 py-3 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
>
<KeyRound className="h-4 w-4 text-[#848b9b]" />
<KeyRound className="h-4 w-4 text-muted-foreground" />
Reset Password
</button>
<button
@@ -402,42 +402,42 @@ export function UserDetailPage() {
{/* Invite Code Used */}
{user.invite_code_used && (
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-[#848b9b]">
<div className="bg-card border border-border rounded-xl p-6">
<h2 className="mb-4 text-sm font-semibold uppercase tracking-wider text-muted-foreground">
<Ticket className="mr-2 inline h-4 w-4" />
Invite Code Used
</h2>
<dl className="grid grid-cols-2 gap-4 md:grid-cols-4">
<div>
<dt className="text-xs text-[#848b9b]">Code</dt>
<dd className="mt-1 font-mono text-sm text-[#848b9b]">{user.invite_code_used.code}</dd>
<dt className="text-xs text-muted-foreground">Code</dt>
<dd className="mt-1 font-mono text-sm text-muted-foreground">{user.invite_code_used.code}</dd>
</div>
<div>
<dt className="text-xs text-[#848b9b]">Plan Assigned</dt>
<dd className="mt-1 text-sm text-[#848b9b]">
<dt className="text-xs text-muted-foreground">Plan Assigned</dt>
<dd className="mt-1 text-sm text-muted-foreground">
{user.invite_code_used.assigned_plan.charAt(0).toUpperCase() + user.invite_code_used.assigned_plan.slice(1)}
</dd>
</div>
<div>
<dt className="text-xs text-[#848b9b]">Trial Days</dt>
<dd className="mt-1 text-sm text-[#848b9b]">{user.invite_code_used.trial_duration_days ?? '—'}</dd>
<dt className="text-xs text-muted-foreground">Trial Days</dt>
<dd className="mt-1 text-sm text-muted-foreground">{user.invite_code_used.trial_duration_days ?? '—'}</dd>
</div>
<div>
<dt className="text-xs text-[#848b9b]">Created By</dt>
<dd className="mt-1 text-sm text-[#848b9b]">{user.invite_code_used.created_by_email ?? '—'}</dd>
<dt className="text-xs text-muted-foreground">Created By</dt>
<dd className="mt-1 text-sm text-muted-foreground">{user.invite_code_used.created_by_email ?? '—'}</dd>
</div>
</dl>
</div>
)}
{/* Tabs: Sessions / Audit Logs */}
<div className="bg-[#14161d] border border-[#1e2130] rounded-xl">
<div className="flex border-b border-[#1e2130]">
<div className="bg-card border border-border rounded-xl">
<div className="flex border-b border-border">
<button
onClick={() => setActiveTab('sessions')}
className={cn(
'px-6 py-3 text-sm font-medium',
activeTab === 'sessions' ? 'border-b-2 border-foreground text-[#e2e5eb]' : 'text-[#848b9b] hover:text-[#e2e5eb]'
activeTab === 'sessions' ? 'border-b-2 border-foreground text-foreground' : 'text-muted-foreground hover:text-foreground'
)}
>
Sessions ({user.total_sessions})
@@ -446,7 +446,7 @@ export function UserDetailPage() {
onClick={() => setActiveTab('audit')}
className={cn(
'px-6 py-3 text-sm font-medium',
activeTab === 'audit' ? 'border-b-2 border-foreground text-[#e2e5eb]' : 'text-[#848b9b] hover:text-[#e2e5eb]'
activeTab === 'audit' ? 'border-b-2 border-foreground text-foreground' : 'text-muted-foreground hover:text-foreground'
)}
>
Audit Logs ({user.total_audit_logs})
@@ -458,7 +458,7 @@ export function UserDetailPage() {
user.recent_sessions.length > 0 ? (
<table className="w-full">
<thead>
<tr className="border-b border-[#1e2130] text-left text-xs text-[#848b9b]">
<tr className="border-b border-border text-left text-xs text-muted-foreground">
<th className="pb-2 font-medium">Tree</th>
<th className="pb-2 font-medium">Started</th>
<th className="pb-2 font-medium">Completed</th>
@@ -467,17 +467,17 @@ export function UserDetailPage() {
</thead>
<tbody>
{user.recent_sessions.map(s => (
<tr key={s.id} className="border-b border-[#1e2130]">
<td className="py-3 text-sm text-[#848b9b]">{s.tree_name ?? '—'}</td>
<td className="py-3 text-sm text-[#848b9b]">{fmtFull(s.started_at)}</td>
<td className="py-3 text-sm text-[#848b9b]">{fmtFull(s.completed_at)}</td>
<tr key={s.id} className="border-b border-border">
<td className="py-3 text-sm text-muted-foreground">{s.tree_name ?? '—'}</td>
<td className="py-3 text-sm text-muted-foreground">{fmtFull(s.started_at)}</td>
<td className="py-3 text-sm text-muted-foreground">{fmtFull(s.completed_at)}</td>
<td className="py-3">
{s.outcome ? (
<StatusBadge variant={s.outcome === 'resolved' ? 'success' : 'default'}>
{s.outcome}
</StatusBadge>
) : (
<span className="text-sm text-[#848b9b]"></span>
<span className="text-sm text-muted-foreground"></span>
)}
</td>
</tr>
@@ -485,7 +485,7 @@ export function UserDetailPage() {
</tbody>
</table>
) : (
<div className="py-8 text-center text-sm text-[#848b9b]">No sessions yet</div>
<div className="py-8 text-center text-sm text-muted-foreground">No sessions yet</div>
)
)}
@@ -493,7 +493,7 @@ export function UserDetailPage() {
user.recent_audit_logs.length > 0 ? (
<table className="w-full">
<thead>
<tr className="border-b border-[#1e2130] text-left text-xs text-[#848b9b]">
<tr className="border-b border-border text-left text-xs text-muted-foreground">
<th className="pb-2 font-medium">Action</th>
<th className="pb-2 font-medium">Resource</th>
<th className="pb-2 font-medium">Time</th>
@@ -501,16 +501,16 @@ export function UserDetailPage() {
</thead>
<tbody>
{user.recent_audit_logs.map(a => (
<tr key={a.id} className="border-b border-[#1e2130]">
<td className="py-3 text-sm text-[#848b9b]">{a.action}</td>
<td className="py-3 text-sm text-[#848b9b]">{a.resource_type ?? '—'}</td>
<td className="py-3 text-sm text-[#848b9b]">{fmtFull(a.created_at)}</td>
<tr key={a.id} className="border-b border-border">
<td className="py-3 text-sm text-muted-foreground">{a.action}</td>
<td className="py-3 text-sm text-muted-foreground">{a.resource_type ?? '—'}</td>
<td className="py-3 text-sm text-muted-foreground">{fmtFull(a.created_at)}</td>
</tr>
))}
</tbody>
</table>
) : (
<div className="py-8 text-center text-sm text-[#848b9b]">No audit logs yet</div>
<div className="py-8 text-center text-sm text-muted-foreground">No audit logs yet</div>
)
)}
</div>
@@ -530,7 +530,7 @@ export function UserDetailPage() {
}
>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Plan</label>
<label className="mb-1 block text-sm font-medium text-foreground">Plan</label>
<select
aria-label="Subscription plan"
value={selectedPlan}
@@ -560,11 +560,11 @@ export function UserDetailPage() {
}
>
<div className="space-y-4">
<p className="text-sm text-[#848b9b]">
Choose how to reset the password for <span className="font-medium text-[#e2e5eb]">{user.full_name || user.email}</span>.
<p className="text-sm text-muted-foreground">
Choose how to reset the password for <span className="font-medium text-foreground">{user.full_name || user.email}</span>.
</p>
<div className="space-y-2">
<label className="flex items-start gap-3 rounded-lg border border-[#1e2130] p-3 cursor-pointer hover:bg-accent">
<label className="flex items-start gap-3 rounded-lg border border-border p-3 cursor-pointer hover:bg-accent">
<input
type="radio"
name="reset-mode"
@@ -574,11 +574,11 @@ export function UserDetailPage() {
className="mt-0.5"
/>
<div>
<div className="text-sm font-medium text-[#e2e5eb]">Send Reset Email</div>
<div className="text-xs text-[#848b9b]">User receives an email with a reset link (30 min expiry)</div>
<div className="text-sm font-medium text-foreground">Send Reset Email</div>
<div className="text-xs text-muted-foreground">User receives an email with a reset link (30 min expiry)</div>
</div>
</label>
<label className="flex items-start gap-3 rounded-lg border border-[#1e2130] p-3 cursor-pointer hover:bg-accent">
<label className="flex items-start gap-3 rounded-lg border border-border p-3 cursor-pointer hover:bg-accent">
<input
type="radio"
name="reset-mode"
@@ -588,8 +588,8 @@ export function UserDetailPage() {
className="mt-0.5"
/>
<div>
<div className="text-sm font-medium text-[#e2e5eb]">Generate Temp Password</div>
<div className="text-xs text-[#848b9b]">A temporary password is generated. You share it manually.</div>
<div className="text-sm font-medium text-foreground">Generate Temp Password</div>
<div className="text-xs text-muted-foreground">A temporary password is generated. You share it manually.</div>
</div>
</label>
</div>
@@ -613,18 +613,18 @@ export function UserDetailPage() {
This password will not be shown again. Copy it now.
</div>
<div className="flex items-center gap-2">
<code className="flex-1 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] font-mono">
<code className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground font-mono">
{resetTempPassword}
</code>
<button
onClick={handleCopyResetPassword}
className="rounded-md border border-[#1e2130] p-2 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] transition-colors"
className="rounded-md border border-border p-2 text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
title="Copy password"
>
{resetCopied ? <Check className="h-4 w-4 text-green-400" /> : <Copy className="h-4 w-4" />}
</button>
</div>
<p className="text-xs text-[#848b9b]">
<p className="text-xs text-muted-foreground">
The user will be required to change this password on next login.
</p>
</div>
@@ -646,7 +646,7 @@ export function UserDetailPage() {
}
>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Days to add</label>
<label className="mb-1 block text-sm font-medium text-foreground">Days to add</label>
<Input
type="number"
value={trialDays}
@@ -654,7 +654,7 @@ export function UserDetailPage() {
min={1}
max={90}
/>
<p className="mt-1 text-xs text-[#848b9b]">1-90 days. Will convert to trialing status if not already.</p>
<p className="mt-1 text-xs text-muted-foreground">1-90 days. Will convert to trialing status if not already.</p>
</div>
</Modal>
@@ -712,11 +712,11 @@ export function UserDetailPage() {
<div className="rounded-xl border border-red-400/20 bg-red-400/10 p-3 text-sm text-red-400">
This user cannot be deleted because they have dependencies:
</div>
<ul className="space-y-1 text-sm text-[#848b9b]">
<ul className="space-y-1 text-sm text-muted-foreground">
{Object.entries(hardDeleteBlockers).map(([key, count]) => (
<li key={key} className="flex justify-between">
<span>{key.replace(/_/g, ' ')}</span>
<span className="font-mono text-[#848b9b]">{count}</span>
<span className="font-mono text-muted-foreground">{count}</span>
</li>
))}
</ul>

View File

@@ -190,8 +190,8 @@ export function UsersPage() {
sortable: true,
render: (u) => (
<div>
<div className="font-medium text-[#e2e5eb]">{u.name}</div>
<div className="text-xs text-[#848b9b]">{u.email}</div>
<div className="font-medium text-foreground">{u.name}</div>
<div className="text-xs text-muted-foreground">{u.email}</div>
</div>
),
},
@@ -226,7 +226,7 @@ export function UsersPage() {
header: 'Joined',
sortable: true,
render: (u) => (
<span className="text-sm text-[#848b9b]">
<span className="text-sm text-muted-foreground">
{new Date(u.created_at).toLocaleDateString()}
</span>
),
@@ -286,12 +286,12 @@ export function UsersPage() {
placeholder="Search by name or email..."
className="max-w-sm"
/>
<label className="flex items-center gap-2 text-sm text-[#848b9b]">
<label className="flex items-center gap-2 text-sm text-muted-foreground">
<input
type="checkbox"
checked={showArchived}
onChange={(e) => { setShowArchived(e.target.checked); setPage(1) }}
className="rounded border-[#1e2130] bg-[#14161d]"
className="rounded border-border bg-card"
/>
Show archived
</label>
@@ -326,14 +326,14 @@ export function UsersPage() {
}
>
<div className="space-y-4">
<p className="text-sm text-[#848b9b]">
Changing role for <span className="font-medium text-[#e2e5eb]">{roleModalUser?.name}</span>
<p className="text-sm text-muted-foreground">
Changing role for <span className="font-medium text-foreground">{roleModalUser?.name}</span>
</p>
<select
value={newRole}
onChange={(e) => setNewRole(e.target.value)}
className={cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
>
@@ -357,11 +357,11 @@ export function UsersPage() {
}
>
<div className="space-y-4">
<p className="text-sm text-[#848b9b]">
Moving <span className="font-medium text-[#e2e5eb]">{moveModalUser?.name}</span> to a new account.
<p className="text-sm text-muted-foreground">
Moving <span className="font-medium text-foreground">{moveModalUser?.name}</span> to a new account.
</p>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Display Code</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Display Code</label>
<Input
type="text"
value={displayCode}
@@ -389,7 +389,7 @@ export function UsersPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Name</label>
<label className="mb-1 block text-sm font-medium text-foreground">Name</label>
<Input
type="text"
value={createForm.name}
@@ -398,7 +398,7 @@ export function UsersPage() {
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Email</label>
<label className="mb-1 block text-sm font-medium text-foreground">Email</label>
<Input
type="email"
value={createForm.email}
@@ -407,12 +407,12 @@ export function UsersPage() {
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Mode</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Mode</label>
<select
value={createForm.account_mode}
onChange={(e) => setCreateForm(f => ({ ...f, account_mode: e.target.value as 'existing' | 'personal' }))}
className={cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
>
@@ -423,7 +423,7 @@ export function UsersPage() {
{createForm.account_mode === 'existing' && (
<>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Display Code</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Display Code</label>
<Input
type="text"
value={createForm.account_display_code}
@@ -432,12 +432,12 @@ export function UsersPage() {
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Role</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Role</label>
<select
value={createForm.account_role}
onChange={(e) => setCreateForm(f => ({ ...f, account_role: e.target.value as 'engineer' | 'viewer' }))}
className={cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
>
@@ -453,9 +453,9 @@ export function UsersPage() {
id="send-email"
checked={createForm.send_email}
onChange={(e) => setCreateForm(f => ({ ...f, send_email: e.target.checked }))}
className="rounded border-[#1e2130] bg-[#14161d]"
className="rounded border-border bg-card"
/>
<label htmlFor="send-email" className="text-sm text-[#848b9b]">
<label htmlFor="send-email" className="text-sm text-muted-foreground">
Send welcome email with temporary password
</label>
</div>
@@ -479,21 +479,21 @@ export function UsersPage() {
This password will not be shown again. Copy it now.
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Temporary Password</label>
<label className="mb-1 block text-sm font-medium text-foreground">Temporary Password</label>
<div className="flex items-center gap-2">
<code className="flex-1 rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] font-mono">
<code className="flex-1 rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground font-mono">
{tempPassword}
</code>
<button
onClick={handleCopyPassword}
className="rounded-md border border-[#1e2130] p-2 text-[#848b9b] hover:bg-accent hover:text-[#e2e5eb] transition-colors"
className="rounded-md border border-border p-2 text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
title="Copy password"
>
{copied ? <Check className="h-4 w-4 text-green-400" /> : <Copy className="h-4 w-4" />}
</button>
</div>
</div>
<p className="text-xs text-[#848b9b]">
<p className="text-xs text-muted-foreground">
The user will be required to change this password on first login.
</p>
</div>
@@ -516,7 +516,7 @@ export function UsersPage() {
>
<div className="space-y-4">
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Email</label>
<label className="mb-1 block text-sm font-medium text-foreground">Email</label>
<Input
type="email"
value={inviteForm.email}
@@ -525,7 +525,7 @@ export function UsersPage() {
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Account Display Code</label>
<label className="mb-1 block text-sm font-medium text-foreground">Account Display Code</label>
<Input
type="text"
value={inviteForm.account_display_code}
@@ -534,12 +534,12 @@ export function UsersPage() {
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium text-[#e2e5eb]">Role</label>
<label className="mb-1 block text-sm font-medium text-foreground">Role</label>
<select
value={inviteForm.role}
onChange={(e) => setInviteForm(f => ({ ...f, role: e.target.value as 'engineer' | 'viewer' }))}
className={cn(
'w-full rounded-md border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb]',
'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
'focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20'
)}
>