refactor: migrate remaining pages to new design system
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -130,7 +130,7 @@ export function AccountSettingsPage() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center py-12">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -154,23 +154,23 @@ export function AccountSettingsPage() {
|
||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<Building2 className="h-8 w-8 text-white/50" />
|
||||
<h1 className="text-2xl font-bold text-white sm:text-3xl">Account Settings</h1>
|
||||
<Building2 className="h-8 w-8 text-muted-foreground" />
|
||||
<h1 className="text-2xl font-bold text-foreground sm:text-3xl">Account Settings</h1>
|
||||
</div>
|
||||
<p className="mt-2 text-white/40">
|
||||
<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="glass-card rounded-2xl p-4 sm:p-6">
|
||||
<h2 className="text-lg font-semibold text-white">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-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Account Name
|
||||
</label>
|
||||
{isEditingName ? (
|
||||
@@ -180,9 +180,9 @@ export function AccountSettingsPage() {
|
||||
value={editedName}
|
||||
onChange={(e) => setEditedName(e.target.value)}
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'flex-1 rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
autoFocus
|
||||
onKeyDown={(e) => {
|
||||
@@ -197,8 +197,8 @@ export function AccountSettingsPage() {
|
||||
onClick={handleSaveName}
|
||||
disabled={isSavingName}
|
||||
className={cn(
|
||||
'rounded-md bg-white p-2 text-black',
|
||||
'hover:bg-white/90 disabled:opacity-50'
|
||||
'rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 p-2',
|
||||
'hover:opacity-90 disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
{isSavingName ? (
|
||||
@@ -212,18 +212,18 @@ export function AccountSettingsPage() {
|
||||
setEditedName(account?.name ?? '')
|
||||
setIsEditingName(false)
|
||||
}}
|
||||
className="rounded-md border border-white/10 p-2 text-white/40 hover:bg-white/10"
|
||||
className="rounded-md border border-border p-2 text-muted-foreground hover:bg-accent"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-1 flex items-center gap-2">
|
||||
<span className="text-sm text-white">{account?.name}</span>
|
||||
<span className="text-sm text-foreground">{account?.name}</span>
|
||||
{isAccountOwner && (
|
||||
<button
|
||||
onClick={() => setIsEditingName(true)}
|
||||
className="text-xs text-white hover:underline"
|
||||
className="text-xs text-foreground hover:underline"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
@@ -234,10 +234,10 @@ export function AccountSettingsPage() {
|
||||
|
||||
{/* Display Code */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Display Code
|
||||
</label>
|
||||
<p className="mt-1 text-sm font-mono text-white/40">
|
||||
<p className="mt-1 text-sm font-mono text-muted-foreground">
|
||||
{account?.display_code}
|
||||
</p>
|
||||
</div>
|
||||
@@ -245,8 +245,8 @@ export function AccountSettingsPage() {
|
||||
</div>
|
||||
|
||||
{/* Subscription Section */}
|
||||
<div className="glass-card rounded-2xl p-4 sm:p-6">
|
||||
<h2 className="text-lg font-semibold text-white">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 */}
|
||||
@@ -254,9 +254,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-white/10 text-white/70',
|
||||
plan === 'pro' && 'bg-white/10 text-white',
|
||||
plan === 'team' && 'bg-white/10 text-white'
|
||||
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" />
|
||||
@@ -270,7 +270,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-white/10 text-white/40'
|
||||
sub.status === 'orphaned' && 'bg-accent text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{sub.status.charAt(0).toUpperCase() + sub.status.slice(1).replace('_', ' ')}
|
||||
@@ -279,7 +279,7 @@ export function AccountSettingsPage() {
|
||||
</div>
|
||||
|
||||
{sub?.current_period_end && (
|
||||
<p className="text-sm text-white/40">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Current period ends: {new Date(sub.current_period_end).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
@@ -322,32 +322,32 @@ export function AccountSettingsPage() {
|
||||
|
||||
{/* Team Members Section (owners only) */}
|
||||
{isAccountOwner && (
|
||||
<div className="glass-card rounded-2xl 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-white/50" />
|
||||
<h2 className="text-lg font-semibold text-white">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-white/40">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-white/[0.06]">
|
||||
<div className="mt-4 divide-y divide-border">
|
||||
{members.map((member) => (
|
||||
<div
|
||||
key={member.id}
|
||||
className="flex items-center justify-between py-3 first:pt-0 last:pb-0"
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">{member.name}</p>
|
||||
<p className="text-xs text-white/40">{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">
|
||||
<span
|
||||
className={cn(
|
||||
'rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||
member.account_role === 'owner' && 'bg-white/10 text-white',
|
||||
member.account_role === 'engineer' && 'bg-white/10 text-white/70',
|
||||
member.account_role === 'viewer' && 'bg-white/10 text-white/40'
|
||||
member.account_role === 'owner' && 'bg-accent text-foreground',
|
||||
member.account_role === 'engineer' && 'bg-accent text-muted-foreground',
|
||||
member.account_role === 'viewer' && 'bg-accent text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{member.account_role}
|
||||
@@ -360,7 +360,7 @@ export function AccountSettingsPage() {
|
||||
{member.account_role !== 'owner' && (
|
||||
<button
|
||||
onClick={() => handleRemoveMember(member.id)}
|
||||
className="text-white/40 hover:text-red-400"
|
||||
className="text-muted-foreground hover:text-red-400"
|
||||
title="Remove member"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
@@ -376,10 +376,10 @@ export function AccountSettingsPage() {
|
||||
|
||||
{/* Invite Member Section (owners only) */}
|
||||
{isAccountOwner && (
|
||||
<div className="glass-card rounded-2xl 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-white/50" />
|
||||
<h2 className="text-lg font-semibold text-white">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">
|
||||
@@ -391,17 +391,17 @@ export function AccountSettingsPage() {
|
||||
onChange={(e) => setInviteEmail(e.target.value)}
|
||||
required
|
||||
className={cn(
|
||||
'flex-1 rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-white placeholder:text-white/40',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'flex-1 rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
/>
|
||||
<select
|
||||
value={inviteRole}
|
||||
onChange={(e) => setInviteRole(e.target.value)}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-white focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'rounded-md border border-border bg-card px-3 py-2',
|
||||
'text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="engineer">Engineer</option>
|
||||
@@ -411,8 +411,8 @@ export function AccountSettingsPage() {
|
||||
type="submit"
|
||||
disabled={isInviting || !inviteEmail.trim()}
|
||||
className={cn(
|
||||
'rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90 disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
'rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium',
|
||||
'hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
{isInviting ? (
|
||||
@@ -437,8 +437,8 @@ export function AccountSettingsPage() {
|
||||
{/* Pending Invites */}
|
||||
{invites.length > 0 && (
|
||||
<div className="mt-6">
|
||||
<h3 className="text-sm font-medium text-white">Pending Invites</h3>
|
||||
<div className="mt-2 divide-y divide-white/[0.06]">
|
||||
<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)
|
||||
.map((invite) => (
|
||||
@@ -447,21 +447,21 @@ export function AccountSettingsPage() {
|
||||
className="flex items-center justify-between py-2"
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm text-white">{invite.email}</p>
|
||||
<p className="text-xs text-white/40">
|
||||
<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-white/10 px-2.5 py-0.5 text-xs text-white/70">
|
||||
<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-white/40 hover:text-white disabled:opacity-50"
|
||||
className="text-muted-foreground hover:text-foreground disabled:opacity-50"
|
||||
title="Resend invite"
|
||||
>
|
||||
{resendingId === invite.id ? (
|
||||
@@ -483,34 +483,34 @@ export function AccountSettingsPage() {
|
||||
{isAccountOwner && (
|
||||
<Link
|
||||
to="/account/categories"
|
||||
className="glass-card rounded-2xl p-4 sm:p-6 flex items-center justify-between group hover:border-white/20 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-white/50" />
|
||||
<FolderTree className="h-5 w-5 text-muted-foreground" />
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-white">Team Categories</h2>
|
||||
<p className="text-sm text-white/40">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-white/30 group-hover:text-white transition-colors">→</span>
|
||||
<span className="text-muted-foreground group-hover:text-foreground transition-colors">→</span>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* Preferences Section */}
|
||||
<div className="glass-card rounded-2xl 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-white/50" />
|
||||
<h2 className="text-lg font-semibold text-white">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-white"
|
||||
className="block text-sm font-medium text-foreground"
|
||||
>
|
||||
Default Export Format
|
||||
</label>
|
||||
<p className="text-sm text-white/40">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This format will be pre-selected when exporting sessions
|
||||
</p>
|
||||
<select
|
||||
@@ -521,9 +521,9 @@ export function AccountSettingsPage() {
|
||||
toast.success('Preference saved')
|
||||
}}
|
||||
className={cn(
|
||||
'mt-2 block w-full max-w-xs rounded-xl border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-sm text-white',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
|
||||
'mt-2 block w-full max-w-xs rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-sm text-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20'
|
||||
)}
|
||||
>
|
||||
<option value="markdown">Markdown (.md)</option>
|
||||
@@ -554,24 +554,24 @@ function UsageStat({
|
||||
|
||||
return (
|
||||
<div className="glass-stat rounded-md p-3">
|
||||
<p className="text-xs font-medium text-white/40">{label}</p>
|
||||
<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-white'
|
||||
isAtLimit ? 'text-red-400' : isNearLimit ? 'text-yellow-400' : 'text-foreground'
|
||||
)}
|
||||
>
|
||||
{current}
|
||||
<span className="text-sm font-normal text-white/40">
|
||||
<span className="text-sm font-normal text-muted-foreground">
|
||||
{' '}/ {isUnlimited ? 'Unlimited' : max}
|
||||
</span>
|
||||
</p>
|
||||
{!isUnlimited && (
|
||||
<div className="mt-2 h-1.5 overflow-hidden rounded-full bg-white/10">
|
||||
<div className="mt-2 h-1.5 overflow-hidden rounded-full bg-accent">
|
||||
<div
|
||||
className={cn(
|
||||
'h-full rounded-full transition-all',
|
||||
isAtLimit ? 'bg-red-400' : isNearLimit ? 'bg-yellow-500' : 'bg-white'
|
||||
isAtLimit ? 'bg-red-400' : isNearLimit ? 'bg-yellow-500' : 'bg-primary'
|
||||
)}
|
||||
style={{ width: `${percentage}%` }}
|
||||
/>
|
||||
|
||||
@@ -145,7 +145,7 @@ export function AdminCategoriesPage() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -155,18 +155,18 @@ export function AdminCategoriesPage() {
|
||||
{/* Header */}
|
||||
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white sm:text-3xl">
|
||||
<h1 className="text-2xl font-bold text-foreground sm:text-3xl">
|
||||
Step Categories
|
||||
</h1>
|
||||
<p className="mt-2 text-white/40">
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Manage categories for organizing step library
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowCreateModal(true)}
|
||||
className={cn(
|
||||
'flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90'
|
||||
'flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium',
|
||||
'hover:opacity-90'
|
||||
)}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
@@ -181,16 +181,16 @@ export function AdminCategoriesPage() {
|
||||
type="checkbox"
|
||||
checked={includeArchived}
|
||||
onChange={(e) => setIncludeArchived(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-white/10 text-white focus:ring-2 focus:ring-white/20 focus:ring-offset-0"
|
||||
className="h-4 w-4 rounded border-border text-foreground focus:ring-2 focus:ring-primary/20 focus:ring-offset-0"
|
||||
/>
|
||||
<span className="text-sm text-white/40">Show archived categories</span>
|
||||
<span className="text-sm text-muted-foreground">Show archived categories</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Categories List */}
|
||||
{categories.length === 0 ? (
|
||||
<div className="glass-card rounded-2xl p-12 text-center">
|
||||
<p className="text-white/40">
|
||||
<div className="bg-card border border-border rounded-xl p-12 text-center">
|
||||
<p className="text-muted-foreground">
|
||||
No categories found. Create your first category to get started.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -25,26 +25,26 @@ export function ForgotPasswordPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-black 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-white flex items-center justify-center sm:w-20 sm:h-20">
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-brand 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 text-white tracking-tight">
|
||||
<h1 className="text-3xl font-bold text-foreground tracking-tight">
|
||||
Reset Password
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-white/40">
|
||||
<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="glass-card rounded-2xl 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.
|
||||
@@ -52,7 +52,7 @@ export function ForgotPasswordPage() {
|
||||
<div className="text-center">
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-sm text-white/60 hover:text-white transition-colors"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Back to sign in
|
||||
</Link>
|
||||
@@ -60,9 +60,9 @@ export function ForgotPasswordPage() {
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="mt-8 space-y-6">
|
||||
<div className="glass-card rounded-2xl 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-white">
|
||||
<label htmlFor="email" className="mb-1 block text-sm font-medium text-foreground">
|
||||
Email Address
|
||||
</label>
|
||||
<input
|
||||
@@ -73,9 +73,9 @@ export function ForgotPasswordPage() {
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className={cn(
|
||||
'block w-full rounded-xl border border-white/10 bg-black/50 px-3 py-2',
|
||||
'text-white placeholder:text-white/30',
|
||||
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
|
||||
'block w-full rounded-xl border border-border bg-card px-3 py-2',
|
||||
'text-foreground placeholder:text-muted-foreground',
|
||||
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20',
|
||||
'transition-colors'
|
||||
)}
|
||||
placeholder="you@example.com"
|
||||
@@ -87,8 +87,8 @@ export function ForgotPasswordPage() {
|
||||
disabled={isLoading || !email}
|
||||
className={cn(
|
||||
'w-full rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
|
||||
'bg-white text-black hover:bg-white/90',
|
||||
'focus:outline-none focus:ring-2 focus:ring-white/30 focus:ring-offset-2 focus:ring-offset-black',
|
||||
'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/30 focus:ring-offset-2 focus:ring-offset-background',
|
||||
'disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'transition-all'
|
||||
)}
|
||||
@@ -99,7 +99,7 @@ export function ForgotPasswordPage() {
|
||||
<div className="text-center">
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-sm text-white/60 hover:text-white transition-colors"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Back to sign in
|
||||
</Link>
|
||||
|
||||
@@ -112,8 +112,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 text-white sm:text-3xl">My Flows</h1>
|
||||
<p className="mt-2 text-white/40">
|
||||
<h1 className="text-2xl font-bold text-foreground sm:text-3xl">My Flows</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Your forked and custom flows
|
||||
</p>
|
||||
</div>
|
||||
@@ -121,7 +121,7 @@ export function MyTreesPage() {
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setShowCreateMenu(!showCreateMenu)}
|
||||
className="flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
||||
className="flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Create New
|
||||
@@ -130,27 +130,27 @@ 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-white/10 bg-black/95 p-1 shadow-xl backdrop-blur-sm">
|
||||
<div className="absolute right-0 z-20 mt-1 w-56 rounded-lg border border-border bg-card p-1 shadow-xl backdrop-blur-sm">
|
||||
<Link
|
||||
to="/trees/new"
|
||||
onClick={() => setShowCreateMenu(false)}
|
||||
className="flex items-center gap-3 rounded-md px-3 py-2.5 text-sm text-white hover:bg-white/10"
|
||||
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-white/50" />
|
||||
<FolderTree className="h-4 w-4 text-muted-foreground" />
|
||||
<div>
|
||||
<div className="font-medium">Troubleshooting Tree</div>
|
||||
<div className="text-xs text-white/40">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-white hover:bg-white/10"
|
||||
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-white/50" />
|
||||
<ListOrdered className="h-4 w-4 text-muted-foreground" />
|
||||
<div>
|
||||
<div className="font-medium">Procedural Flow</div>
|
||||
<div className="text-xs text-white/40">Step-by-step procedure</div>
|
||||
<div className="text-xs text-muted-foreground">Step-by-step procedure</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -163,21 +163,21 @@ export function MyTreesPage() {
|
||||
{/* Loading State */}
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center py-12">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
) : trees.length === 0 ? (
|
||||
<div className="rounded-lg border border-dashed border-white/10 bg-white/[0.02] px-4 py-12 text-center">
|
||||
<FolderTree className="mx-auto mb-4 h-12 w-12 text-white/20" />
|
||||
<h2 className="mb-2 text-lg font-semibold text-white">No personal flows yet</h2>
|
||||
<p className="mb-4 text-sm text-white/40">
|
||||
<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-white px-4 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90'
|
||||
'inline-flex items-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium',
|
||||
'hover:opacity-90'
|
||||
)}
|
||||
>
|
||||
Browse Library
|
||||
@@ -186,8 +186,8 @@ export function MyTreesPage() {
|
||||
<Link
|
||||
to="/trees/new"
|
||||
className={cn(
|
||||
'inline-flex items-center gap-2 rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'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" />
|
||||
@@ -201,15 +201,15 @@ export function MyTreesPage() {
|
||||
{trees.map((tree) => (
|
||||
<div
|
||||
key={tree.id}
|
||||
className="glass-card rounded-2xl p-4 transition-all hover:glass-card-hover 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-white/40" />
|
||||
<ListOrdered className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<h3 className="font-semibold text-white">{tree.name}</h3>
|
||||
<h3 className="font-semibold text-foreground">{tree.name}</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{tree.tree_type === 'procedural' && (
|
||||
@@ -218,7 +218,7 @@ export function MyTreesPage() {
|
||||
</span>
|
||||
)}
|
||||
{tree.category_info && (
|
||||
<span className="rounded-full bg-white/10 px-2 py-0.5 text-xs text-white/70">
|
||||
<span className="rounded-full bg-accent px-2 py-0.5 text-xs text-muted-foreground">
|
||||
{tree.category_info.name}
|
||||
</span>
|
||||
)}
|
||||
@@ -226,19 +226,19 @@ export function MyTreesPage() {
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p className="mb-3 text-sm text-white/40 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-white/5 px-2 py-1.5 text-sm">
|
||||
<GitBranch className="h-4 w-4 text-white/40" />
|
||||
<span className="text-white/40">
|
||||
<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-muted-foreground" />
|
||||
<span className="text-muted-foreground">
|
||||
Forked from{' '}
|
||||
<Link
|
||||
to={`/trees/${tree.parent_tree_id}/navigate`}
|
||||
className="font-medium text-white hover:underline"
|
||||
className="font-medium text-foreground hover:underline"
|
||||
>
|
||||
original
|
||||
</Link>
|
||||
@@ -254,7 +254,7 @@ export function MyTreesPage() {
|
||||
)}
|
||||
|
||||
{/* Stats */}
|
||||
<div className="mb-4 flex items-center gap-4 text-xs text-white/30">
|
||||
<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>
|
||||
@@ -271,8 +271,8 @@ export function MyTreesPage() {
|
||||
type="button"
|
||||
onClick={() => handleStartSession(tree)}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md bg-white px-3 py-2 text-sm font-medium text-black',
|
||||
'hover:bg-white/90'
|
||||
'flex flex-1 items-center justify-center gap-2 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-3 py-2 text-sm font-medium',
|
||||
'hover:opacity-90'
|
||||
)}
|
||||
>
|
||||
<Play className="h-4 w-4" />
|
||||
@@ -282,8 +282,8 @@ export function MyTreesPage() {
|
||||
<Link
|
||||
to={getEditPath(tree)}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-2 text-white/40',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-md border border-border p-2 text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Edit tree"
|
||||
>
|
||||
@@ -297,8 +297,8 @@ export function MyTreesPage() {
|
||||
setShowShareModal(true)
|
||||
}}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-2 text-white/40',
|
||||
'hover:bg-white/10 hover:text-white'
|
||||
'rounded-md border border-border p-2 text-muted-foreground',
|
||||
'hover:bg-accent hover:text-foreground'
|
||||
)}
|
||||
title="Share tree"
|
||||
>
|
||||
@@ -311,7 +311,7 @@ export function MyTreesPage() {
|
||||
setShowDeleteConfirm(true)
|
||||
}}
|
||||
className={cn(
|
||||
'rounded-md border border-white/10 p-2 text-white/40',
|
||||
'rounded-md border border-border p-2 text-muted-foreground',
|
||||
'hover:bg-red-400/10 hover:text-red-400'
|
||||
)}
|
||||
title="Delete tree"
|
||||
|
||||
@@ -97,7 +97,7 @@ export function ProceduralEditorPage() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-[50vh] items-center justify-center">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -109,13 +109,13 @@ export function ProceduralEditorPage() {
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => navigate('/my-trees')}
|
||||
className="rounded-md p-2 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
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">
|
||||
<ListOrdered className="h-5 w-5 text-white/50" />
|
||||
<h1 className="text-xl font-bold text-white sm:text-2xl">
|
||||
<ListOrdered className="h-5 w-5 text-muted-foreground" />
|
||||
<h1 className="text-xl font-bold text-foreground sm:text-2xl">
|
||||
{isEditMode ? 'Edit Procedure' : 'New Procedure'}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -123,19 +123,19 @@ export function ProceduralEditorPage() {
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{isDirty && (
|
||||
<span className="text-xs text-white/40">Unsaved changes</span>
|
||||
<span className="text-xs text-muted-foreground">Unsaved changes</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleSave('draft')}
|
||||
disabled={isSaving}
|
||||
className="flex items-center gap-1.5 rounded-md border border-white/10 px-3 py-2 text-sm text-white/60 hover:bg-white/10 hover:text-white disabled:opacity-50"
|
||||
className="flex items-center gap-1.5 rounded-md border border-border px-3 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground disabled:opacity-50"
|
||||
>
|
||||
Save Draft
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSave('published')}
|
||||
disabled={isSaving}
|
||||
className="flex items-center gap-1.5 rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90 disabled:opacity-50"
|
||||
className="flex items-center gap-1.5 rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90 disabled:opacity-50"
|
||||
>
|
||||
<Save className="h-4 w-4" />
|
||||
{isSaving ? 'Saving...' : 'Publish'}
|
||||
@@ -146,44 +146,44 @@ export function ProceduralEditorPage() {
|
||||
{/* Content */}
|
||||
<div className="space-y-6">
|
||||
{/* Metadata */}
|
||||
<div className="glass-card rounded-2xl p-4 sm:p-6">
|
||||
<h2 className="mb-4 text-lg font-semibold text-white">Details</h2>
|
||||
<div className="bg-card border border-border rounded-xl p-4 sm:p-6">
|
||||
<h2 className="mb-4 text-lg font-semibold text-foreground">Details</h2>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white/60">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-white/10 bg-black/50 px-3 py-2 text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/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-none focus:ring-1 focus:ring-primary/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white/60">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-white/10 bg-black/50 px-3 py-2 text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/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-none 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-white/60">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-white/60">
|
||||
<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-white/20"
|
||||
className="rounded border-border"
|
||||
/>
|
||||
Public (visible to all users)
|
||||
</label>
|
||||
|
||||
@@ -278,7 +278,7 @@ export function ProceduralNavigationPage() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-[50vh] items-center justify-center">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-white/20 border-t-white" />
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-border border-t-foreground" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -332,17 +332,17 @@ export function ProceduralNavigationPage() {
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
{/* Top bar */}
|
||||
<div className="border-b border-white/[0.06] 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-white/40 hover:bg-white/10 hover:text-white 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-white/40" />
|
||||
<h1 className="text-sm font-semibold text-white 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>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
@@ -360,7 +360,7 @@ export function ProceduralNavigationPage() {
|
||||
{/* Left sidebar - step checklist */}
|
||||
<div
|
||||
className={cn(
|
||||
'border-r border-white/[0.06] bg-black/30 transition-all duration-200',
|
||||
'border-r border-border bg-card transition-all duration-200',
|
||||
sidebarOpen ? 'w-72 p-3' : 'w-0 overflow-hidden p-0'
|
||||
)}
|
||||
>
|
||||
@@ -375,10 +375,10 @@ export function ProceduralNavigationPage() {
|
||||
|
||||
{/* View Parameters button */}
|
||||
{Object.keys(sessionVariables).length > 0 && (
|
||||
<div className="mt-3 border-t border-white/[0.06] 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-white/10 px-3 py-2 text-xs text-white/40 hover:bg-white/[0.06] hover:text-white/60"
|
||||
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" />
|
||||
View Parameters ({Object.keys(sessionVariables).length})
|
||||
@@ -413,15 +413,15 @@ export function ProceduralNavigationPage() {
|
||||
{paramsOpen && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
||||
className="absolute inset-0 bg-background/60 backdrop-blur-sm"
|
||||
onClick={() => setParamsOpen(false)}
|
||||
/>
|
||||
<div className="relative w-full max-w-md rounded-2xl border border-white/10 bg-black/95 shadow-2xl backdrop-blur-sm">
|
||||
<div className="flex items-center justify-between border-b border-white/[0.06] px-5 py-4">
|
||||
<h3 className="text-sm font-semibold text-white">Project Parameters</h3>
|
||||
<div className="relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl backdrop-blur-sm">
|
||||
<div className="flex items-center justify-between border-b border-border px-5 py-4">
|
||||
<h3 className="text-sm font-semibold text-foreground">Project Parameters</h3>
|
||||
<button
|
||||
onClick={() => setParamsOpen(false)}
|
||||
className="rounded-lg p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
||||
className="rounded-lg p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -429,9 +429,9 @@ export function ProceduralNavigationPage() {
|
||||
<div className="max-h-[60vh] overflow-y-auto p-5">
|
||||
<div className="space-y-2">
|
||||
{Object.entries(sessionVariables).map(([key, value]) => (
|
||||
<div key={key} className="flex items-baseline justify-between gap-4 rounded-lg bg-white/[0.03] px-3 py-2">
|
||||
<span className="text-xs font-medium text-white/40">{key.replace(/_/g, ' ')}</span>
|
||||
<span className="text-right text-sm text-white/70">{value || 'N/A'}</span>
|
||||
<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-muted-foreground">{key.replace(/_/g, ' ')}</span>
|
||||
<span className="text-right text-sm text-muted-foreground">{value || 'N/A'}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -77,16 +77,16 @@ export function TeamCategoriesPage() {
|
||||
setForm({ name: cat.name, slug: cat.slug, description: cat.description || '' })
|
||||
}
|
||||
|
||||
const inputCn = cn('w-full rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white', 'placeholder:text-white/40 focus:outline-none focus:border-white/30 focus:ring-2 focus:ring-white/20')
|
||||
const inputCn = cn('w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20')
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white">Team Categories</h1>
|
||||
<p className="mt-1 text-sm text-white/40">Manage tree categories for your team</p>
|
||||
<h1 className="text-2xl font-bold text-foreground">Team Categories</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">Manage tree categories for your team</p>
|
||||
</div>
|
||||
<button onClick={() => setCreateOpen(true)} className={cn('flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium', 'bg-white text-black hover:bg-white/90')}>
|
||||
<button onClick={() => setCreateOpen(true)} className={cn('flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium', 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90')}>
|
||||
<Plus className="h-4 w-4" />
|
||||
Create Category
|
||||
</button>
|
||||
@@ -95,30 +95,30 @@ export function TeamCategoriesPage() {
|
||||
{loading ? (
|
||||
<div className="space-y-3">
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<div key={i} className="h-16 animate-pulse rounded-lg bg-white/10" />
|
||||
<div key={i} className="h-16 animate-pulse rounded-lg bg-accent" />
|
||||
))}
|
||||
</div>
|
||||
) : categories.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center glass-card rounded-2xl py-16">
|
||||
<FolderTree className="h-12 w-12 text-white/30" />
|
||||
<h3 className="mt-4 font-medium text-white">No team categories</h3>
|
||||
<p className="mt-1 text-sm text-white/40">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-white/[0.06] 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-white">{cat.name}</span>
|
||||
<span className="ml-3 text-sm text-white/40">{cat.slug}</span>
|
||||
{cat.description && <span className="ml-3 text-sm text-white/40">- {cat.description}</span>}
|
||||
<span className="ml-3 text-xs text-white/40">{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-white/50 hover:bg-white/[0.06] hover:text-white">
|
||||
<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-white/50 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>
|
||||
@@ -131,22 +131,22 @@ export function TeamCategoriesPage() {
|
||||
<Modal isOpen={createOpen} onClose={() => setCreateOpen(false)} title="Create Category" size="sm"
|
||||
footer={
|
||||
<div className="flex justify-end gap-3">
|
||||
<button onClick={() => setCreateOpen(false)} className="rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white">Cancel</button>
|
||||
<button onClick={handleCreate} disabled={!form.name || !form.slug} className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90 disabled:opacity-50">Create</button>
|
||||
<button onClick={() => setCreateOpen(false)} className="rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground">Cancel</button>
|
||||
<button onClick={handleCreate} disabled={!form.name || !form.slug} className="rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90 disabled:opacity-50">Create</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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" className={inputCn} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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 })} className={inputCn} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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" className={inputCn} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,22 +156,22 @@ export function TeamCategoriesPage() {
|
||||
<Modal isOpen={!!editCategory} onClose={() => setEditCategory(null)} title="Edit Category" size="sm"
|
||||
footer={
|
||||
<div className="flex justify-end gap-3">
|
||||
<button onClick={() => setEditCategory(null)} className="rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white">Cancel</button>
|
||||
<button onClick={handleUpdate} disabled={!form.name || !form.slug} className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90 disabled:opacity-50">Save</button>
|
||||
<button onClick={() => setEditCategory(null)} className="rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground">Cancel</button>
|
||||
<button onClick={handleUpdate} disabled={!form.name || !form.slug} className="rounded-md bg-gradient-brand text-white shadow-lg shadow-primary/20 px-4 py-2 text-sm font-medium hover:opacity-90 disabled:opacity-50">Save</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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 })} className={inputCn} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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 })} className={inputCn} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-sm font-medium text-white">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" className={inputCn} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user