refactor: migrate procedural and step library components to new design system
Migrate 10 components from monochrome glass-card design to purple gradient accent design system tokens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,49 +26,49 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
|
|||||||
const needsOptions = field.field_type === 'select' || field.field_type === 'multi_select'
|
const needsOptions = field.field_type === 'select' || field.field_type === 'multi_select'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="glass-card rounded-xl p-3">
|
<div className="bg-card border border-border rounded-xl p-3">
|
||||||
{/* Header row */}
|
{/* Header row */}
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-white/30" />
|
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-muted-foreground" />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.label}
|
value={field.label}
|
||||||
onChange={(e) => onUpdate({ label: e.target.value })}
|
onChange={(e) => onUpdate({ label: e.target.value })}
|
||||||
placeholder="Field label"
|
placeholder="Field label"
|
||||||
className="min-w-0 flex-1 rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="min-w-0 flex-1 rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
value={field.field_type}
|
value={field.field_type}
|
||||||
onChange={(e) => onUpdate({ field_type: e.target.value as IntakeFieldType })}
|
onChange={(e) => onUpdate({ field_type: e.target.value as IntakeFieldType })}
|
||||||
className="rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
{FIELD_TYPE_OPTIONS.map((opt) => (
|
{FIELD_TYPE_OPTIONS.map((opt) => (
|
||||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label className="flex items-center gap-1 text-xs text-white/50">
|
<label className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={field.required}
|
checked={field.required}
|
||||||
onChange={(e) => onUpdate({ required: e.target.checked })}
|
onChange={(e) => onUpdate({ required: e.target.checked })}
|
||||||
className="rounded border-white/20"
|
className="rounded border-border"
|
||||||
/>
|
/>
|
||||||
Req
|
Req
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
className="rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
{expanded ? <ChevronUp className="h-3.5 w-3.5" /> : <ChevronDown className="h-3.5 w-3.5" />}
|
{expanded ? <ChevronUp className="h-3.5 w-3.5" /> : <ChevronDown className="h-3.5 w-3.5" />}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={onRemove}
|
onClick={onRemove}
|
||||||
className="rounded p-1 text-white/40 hover:bg-red-500/20 hover:text-red-400"
|
className="rounded p-1 text-muted-foreground hover:bg-red-500/20 hover:text-red-400"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -76,66 +76,66 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
|
|||||||
|
|
||||||
{/* Expanded details */}
|
{/* Expanded details */}
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<div className="mt-3 grid grid-cols-2 gap-3 border-t border-white/[0.06] pt-3">
|
<div className="mt-3 grid grid-cols-2 gap-3 border-t border-border pt-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs text-white/50">Variable Name</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Variable Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.variable_name}
|
value={field.variable_name}
|
||||||
onChange={(e) => onUpdate({ variable_name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '') })}
|
onChange={(e) => onUpdate({ variable_name: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '') })}
|
||||||
placeholder="e.g. server_name"
|
placeholder="e.g. server_name"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm font-mono text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
<p className="mt-0.5 text-[10px] text-white/30">Used as [VAR:{field.variable_name}]</p>
|
<p className="mt-0.5 text-[10px] text-muted-foreground">Used as [VAR:{field.variable_name}]</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs text-white/50">Placeholder</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Placeholder</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.placeholder || ''}
|
value={field.placeholder || ''}
|
||||||
onChange={(e) => onUpdate({ placeholder: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ placeholder: e.target.value || undefined })}
|
||||||
placeholder="Hint text"
|
placeholder="Hint text"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<label className="mb-1 block text-xs text-white/50">Help Text</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Help Text</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.help_text || ''}
|
value={field.help_text || ''}
|
||||||
onChange={(e) => onUpdate({ help_text: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ help_text: e.target.value || undefined })}
|
||||||
placeholder="Description or instructions"
|
placeholder="Description or instructions"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs text-white/50">Default Value</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Default Value</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.default_value || ''}
|
value={field.default_value || ''}
|
||||||
onChange={(e) => onUpdate({ default_value: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ default_value: e.target.value || undefined })}
|
||||||
placeholder="Pre-filled value"
|
placeholder="Pre-filled value"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs text-white/50">Group Name</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Group Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={field.group_name || ''}
|
value={field.group_name || ''}
|
||||||
onChange={(e) => onUpdate({ group_name: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ group_name: e.target.value || undefined })}
|
||||||
placeholder="e.g. Network Settings"
|
placeholder="e.g. Network Settings"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{needsOptions && (
|
{needsOptions && (
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<label className="mb-1 block text-xs text-white/50">Options (one per line)</label>
|
<label className="mb-1 block text-xs text-muted-foreground">Options (one per line)</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={(field.options || []).join('\n')}
|
value={(field.options || []).join('\n')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -144,7 +144,7 @@ export function IntakeFieldEditor({ field, onUpdate, onRemove }: IntakeFieldEdit
|
|||||||
}}
|
}}
|
||||||
placeholder="Option 1 Option 2 Option 3"
|
placeholder="Option 1 Option 2 Option 3"
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-2 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ export function IntakeFormBuilder() {
|
|||||||
const { intakeForm, addField, removeField, updateField } = useProceduralEditorStore()
|
const { intakeForm, addField, removeField, updateField } = useProceduralEditorStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="glass-card rounded-2xl p-4 sm:p-6">
|
<div className="bg-card border border-border rounded-2xl p-4 sm:p-6">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FileText className="h-5 w-5 text-white/50" />
|
<FileText className="h-5 w-5 text-muted-foreground" />
|
||||||
<h2 className="text-lg font-semibold text-white">Intake Form</h2>
|
<h2 className="text-lg font-semibold text-foreground">Intake Form</h2>
|
||||||
<span className="text-sm text-white/40">
|
<span className="text-sm text-muted-foreground">
|
||||||
({intakeForm.length} field{intakeForm.length !== 1 ? 's' : ''})
|
({intakeForm.length} field{intakeForm.length !== 1 ? 's' : ''})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={addField}
|
onClick={addField}
|
||||||
className="flex items-center gap-1.5 rounded-md border border-white/10 px-3 py-1.5 text-sm text-white/60 hover:bg-white/10 hover:text-white"
|
className="flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="h-3.5 w-3.5" />
|
<Plus className="h-3.5 w-3.5" />
|
||||||
Add Field
|
Add Field
|
||||||
@@ -25,10 +25,10 @@ export function IntakeFormBuilder() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{intakeForm.length === 0 ? (
|
{intakeForm.length === 0 ? (
|
||||||
<div className="rounded-lg border border-dashed border-white/10 bg-white/[0.02] py-8 text-center">
|
<div className="rounded-lg border border-dashed border-border bg-white/[0.02] py-8 text-center">
|
||||||
<FileText className="mx-auto mb-2 h-8 w-8 text-white/20" />
|
<FileText className="mx-auto mb-2 h-8 w-8 text-muted-foreground" />
|
||||||
<p className="text-sm text-white/40">No intake form fields yet</p>
|
<p className="text-sm text-muted-foreground">No intake form fields yet</p>
|
||||||
<p className="mt-1 text-xs text-white/30">
|
<p className="mt-1 text-xs text-muted-foreground">
|
||||||
Add fields to collect project data before the procedure starts
|
Add fields to collect project data before the procedure starts
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
|
|||||||
|
|
||||||
const CONTENT_TYPE_OPTIONS: { value: StepContentType; label: string; color: string }[] = [
|
const CONTENT_TYPE_OPTIONS: { value: StepContentType; label: string; color: string }[] = [
|
||||||
{ value: 'action', label: 'Action', color: 'text-blue-400' },
|
{ value: 'action', label: 'Action', color: 'text-blue-400' },
|
||||||
{ value: 'informational', label: 'Info', color: 'text-white/60' },
|
{ value: 'informational', label: 'Info', color: 'text-muted-foreground' },
|
||||||
{ value: 'verification', label: 'Verify', color: 'text-emerald-400' },
|
{ value: 'verification', label: 'Verify', color: 'text-emerald-400' },
|
||||||
{ value: 'warning', label: 'Warning', color: 'text-yellow-400' },
|
{ value: 'warning', label: 'Warning', color: 'text-yellow-400' },
|
||||||
]
|
]
|
||||||
@@ -24,24 +24,24 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
// Section header steps get a minimal editor
|
// Section header steps get a minimal editor
|
||||||
if (step.type === 'section_header') {
|
if (step.type === 'section_header') {
|
||||||
return (
|
return (
|
||||||
<div className="glass-card rounded-xl border border-white/10 p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-white/50">Edit Section Header</span>
|
<span className="text-sm font-medium text-muted-foreground">Edit Section Header</span>
|
||||||
<button
|
<button
|
||||||
onClick={onCollapse}
|
onClick={onCollapse}
|
||||||
className="rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<ChevronUp className="h-4 w-4" />
|
<ChevronUp className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Title</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Title</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={step.title}
|
value={step.title}
|
||||||
onChange={(e) => onUpdate({ title: e.target.value })}
|
onChange={(e) => onUpdate({ title: e.target.value })}
|
||||||
placeholder="Section title"
|
placeholder="Section title"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,18 +49,18 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="glass-card rounded-xl border border-white/10 p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-white/10 text-xs font-medium text-white">
|
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-accent text-xs font-medium text-foreground">
|
||||||
{stepNumber}
|
{stepNumber}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-medium text-white">Edit Step</span>
|
<span className="text-sm font-medium text-foreground">Edit Step</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onCollapse}
|
onClick={onCollapse}
|
||||||
className="rounded p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<ChevronUp className="h-4 w-4" />
|
<ChevronUp className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
@@ -69,18 +69,18 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Title</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Title</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={step.title}
|
value={step.title}
|
||||||
onChange={(e) => onUpdate({ title: e.target.value })}
|
onChange={(e) => onUpdate({ title: e.target.value })}
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Est. Minutes */}
|
{/* Est. Minutes */}
|
||||||
<div className="w-40">
|
<div className="w-40">
|
||||||
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-white/50">
|
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||||
<Clock className="h-3 w-3" />
|
<Clock className="h-3 w-3" />
|
||||||
Est. Minutes
|
Est. Minutes
|
||||||
</label>
|
</label>
|
||||||
@@ -90,28 +90,28 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
onChange={(e) => onUpdate({ estimated_minutes: e.target.value ? parseInt(e.target.value) : undefined })}
|
onChange={(e) => onUpdate({ estimated_minutes: e.target.value ? parseInt(e.target.value) : undefined })}
|
||||||
placeholder="—"
|
placeholder="—"
|
||||||
min={1}
|
min={1}
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Description / Instructions</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Description / Instructions</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={step.description || ''}
|
value={step.description || ''}
|
||||||
onChange={(e) => onUpdate({ description: e.target.value })}
|
onChange={(e) => onUpdate({ description: e.target.value })}
|
||||||
placeholder="Step instructions. Use [VAR:name] for variables."
|
placeholder="Step instructions. Use [VAR:name] for variables."
|
||||||
rows={4}
|
rows={4}
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
{availableVariables.length > 0 && (
|
{availableVariables.length > 0 && (
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
<div className="mt-1 flex flex-wrap gap-1">
|
||||||
<span className="text-[10px] text-white/30">Variables:</span>
|
<span className="text-[10px] text-muted-foreground">Variables:</span>
|
||||||
{availableVariables.map((v) => (
|
{availableVariables.map((v) => (
|
||||||
<button
|
<button
|
||||||
key={v.variable_name}
|
key={v.variable_name}
|
||||||
onClick={() => onUpdate({ description: (step.description || '') + `[VAR:${v.variable_name}]` })}
|
onClick={() => onUpdate({ description: (step.description || '') + `[VAR:${v.variable_name}]` })}
|
||||||
className="rounded bg-white/5 px-1.5 py-0.5 font-mono text-[10px] text-white/50 hover:bg-white/10 hover:text-white/70"
|
className="rounded bg-accent/50 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground hover:bg-accent hover:text-muted-foreground"
|
||||||
>
|
>
|
||||||
{v.variable_name}
|
{v.variable_name}
|
||||||
</button>
|
</button>
|
||||||
@@ -122,7 +122,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
|
|
||||||
{/* Commands */}
|
{/* Commands */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-white/50">
|
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||||
<Terminal className="h-3 w-3" />
|
<Terminal className="h-3 w-3" />
|
||||||
Commands (optional)
|
Commands (optional)
|
||||||
</label>
|
</label>
|
||||||
@@ -131,7 +131,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
onChange={(e) => onUpdate({ commands: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ commands: e.target.value || undefined })}
|
||||||
placeholder="Install-WindowsFeature AD-Domain-Services -IncludeManagementTools"
|
placeholder="Install-WindowsFeature AD-Domain-Services -IncludeManagementTools"
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 font-mono text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowMore(!showMore)}
|
onClick={() => setShowMore(!showMore)}
|
||||||
className="flex items-center gap-1.5 text-xs text-white/40 hover:text-white/60"
|
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-muted-foreground"
|
||||||
>
|
>
|
||||||
<Settings2 className="h-3 w-3" />
|
<Settings2 className="h-3 w-3" />
|
||||||
More Options
|
More Options
|
||||||
@@ -147,10 +147,10 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showMore && (
|
{showMore && (
|
||||||
<div className="space-y-4 border-t border-white/[0.06] pt-4">
|
<div className="space-y-4 border-t border-border pt-4">
|
||||||
{/* Content Type */}
|
{/* Content Type */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Content Type</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Content Type</label>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
{CONTENT_TYPE_OPTIONS.map((opt) => (
|
{CONTENT_TYPE_OPTIONS.map((opt) => (
|
||||||
<button
|
<button
|
||||||
@@ -160,7 +160,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
'rounded px-2 py-1 text-xs font-medium transition-colors',
|
'rounded px-2 py-1 text-xs font-medium transition-colors',
|
||||||
step.content_type === opt.value
|
step.content_type === opt.value
|
||||||
? 'bg-white/15 ' + opt.color
|
? 'bg-white/15 ' + opt.color
|
||||||
: 'text-white/40 hover:bg-white/10 hover:text-white/60'
|
: 'text-muted-foreground hover:bg-accent hover:text-muted-foreground'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{opt.label}
|
{opt.label}
|
||||||
@@ -181,27 +181,27 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
onChange={(e) => onUpdate({ warning_text: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ warning_text: e.target.value || undefined })}
|
||||||
placeholder="Caution: This will restart the service..."
|
placeholder="Caution: This will restart the service..."
|
||||||
rows={2}
|
rows={2}
|
||||||
className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-yellow-400/30 focus:outline-none focus:ring-1 focus:ring-yellow-400/20"
|
className="w-full rounded border border-yellow-400/20 bg-yellow-400/5 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-yellow-400/30 focus:outline-none focus:ring-1 focus:ring-yellow-400/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Expected Outcome */}
|
{/* Expected Outcome */}
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Expected Outcome (optional)</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Expected Outcome (optional)</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={step.expected_outcome || ''}
|
value={step.expected_outcome || ''}
|
||||||
onChange={(e) => onUpdate({ expected_outcome: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ expected_outcome: e.target.value || undefined })}
|
||||||
placeholder="Server should respond with..."
|
placeholder="Server should respond with..."
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Verification */}
|
{/* Verification */}
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-white/50">
|
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||||
<CheckSquare className="h-3 w-3" />
|
<CheckSquare className="h-3 w-3" />
|
||||||
Verification Prompt (optional)
|
Verification Prompt (optional)
|
||||||
</label>
|
</label>
|
||||||
@@ -210,15 +210,15 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
value={step.verification_prompt || ''}
|
value={step.verification_prompt || ''}
|
||||||
onChange={(e) => onUpdate({ verification_prompt: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ verification_prompt: e.target.value || undefined })}
|
||||||
placeholder="Confirm the role was installed"
|
placeholder="Confirm the role was installed"
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Verification Type</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Verification Type</label>
|
||||||
<select
|
<select
|
||||||
value={step.verification_type || ''}
|
value={step.verification_type || ''}
|
||||||
onChange={(e) => onUpdate({ verification_type: e.target.value as 'checkbox' | 'text_input' || undefined })}
|
onChange={(e) => onUpdate({ verification_type: e.target.value as 'checkbox' | 'text_input' || undefined })}
|
||||||
className="w-full rounded 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"
|
className="w-full rounded 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="">None</option>
|
<option value="">None</option>
|
||||||
<option value="checkbox">Checkbox (confirm done)</option>
|
<option value="checkbox">Checkbox (confirm done)</option>
|
||||||
@@ -230,7 +230,7 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
{/* Reference URL + Notes toggle */}
|
{/* Reference URL + Notes toggle */}
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-white/50">
|
<label className="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
||||||
<ExternalLink className="h-3 w-3" />
|
<ExternalLink className="h-3 w-3" />
|
||||||
Reference URL (optional)
|
Reference URL (optional)
|
||||||
</label>
|
</label>
|
||||||
@@ -239,16 +239,16 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
|||||||
value={step.reference_url || ''}
|
value={step.reference_url || ''}
|
||||||
onChange={(e) => onUpdate({ reference_url: e.target.value || undefined })}
|
onChange={(e) => onUpdate({ reference_url: e.target.value || undefined })}
|
||||||
placeholder="https://learn.microsoft.com/..."
|
placeholder="https://learn.microsoft.com/..."
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20"
|
className="w-full rounded border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end pb-1">
|
<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
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={step.notes_enabled !== false}
|
checked={step.notes_enabled !== false}
|
||||||
onChange={(e) => onUpdate({ notes_enabled: e.target.checked })}
|
onChange={(e) => onUpdate({ notes_enabled: e.target.checked })}
|
||||||
className="rounded border-white/20"
|
className="rounded border-border"
|
||||||
/>
|
/>
|
||||||
Allow tech notes
|
Allow tech notes
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { cn } from '@/lib/utils'
|
|||||||
|
|
||||||
const contentTypeConfig: Record<StepContentType, { icon: typeof Zap; color: string; label: string }> = {
|
const contentTypeConfig: Record<StepContentType, { icon: typeof Zap; color: string; label: string }> = {
|
||||||
action: { icon: Zap, color: 'text-blue-400', label: 'Action' },
|
action: { icon: Zap, color: 'text-blue-400', label: 'Action' },
|
||||||
informational: { icon: Info, color: 'text-white/50', label: 'Info' },
|
informational: { icon: Info, color: 'text-muted-foreground', label: 'Info' },
|
||||||
verification: { icon: CheckCircle2, color: 'text-emerald-400', label: 'Verify' },
|
verification: { icon: CheckCircle2, color: 'text-emerald-400', label: 'Verify' },
|
||||||
warning: { icon: AlertTriangle, color: 'text-yellow-400', label: 'Warning' },
|
warning: { icon: AlertTriangle, color: 'text-yellow-400', label: 'Warning' },
|
||||||
}
|
}
|
||||||
@@ -27,26 +27,26 @@ export function StepList() {
|
|||||||
let stepCounter = 0
|
let stepCounter = 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="glass-card rounded-2xl p-4 sm:p-6">
|
<div className="bg-card border border-border rounded-2xl p-4 sm:p-6">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Shield className="h-5 w-5 text-white/50" />
|
<Shield className="h-5 w-5 text-muted-foreground" />
|
||||||
<h2 className="text-lg font-semibold text-white">Steps</h2>
|
<h2 className="text-lg font-semibold text-foreground">Steps</h2>
|
||||||
<span className="text-sm text-white/40">
|
<span className="text-sm text-muted-foreground">
|
||||||
({procedureSteps.length} step{procedureSteps.length !== 1 ? 's' : ''})
|
({procedureSteps.length} step{procedureSteps.length !== 1 ? 's' : ''})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => addSectionHeader()}
|
onClick={() => addSectionHeader()}
|
||||||
className="flex items-center gap-1.5 rounded-md border border-white/10 px-3 py-1.5 text-sm text-white/60 hover:bg-white/10 hover:text-white"
|
className="flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<SeparatorHorizontal className="h-3.5 w-3.5" />
|
<SeparatorHorizontal className="h-3.5 w-3.5" />
|
||||||
Add Section
|
Add Section
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => addStep()}
|
onClick={() => addStep()}
|
||||||
className="flex items-center gap-1.5 rounded-md border border-white/10 px-3 py-1.5 text-sm text-white/60 hover:bg-white/10 hover:text-white"
|
className="flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="h-3.5 w-3.5" />
|
<Plus className="h-3.5 w-3.5" />
|
||||||
Add Step
|
Add Step
|
||||||
@@ -60,17 +60,17 @@ export function StepList() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
className="flex items-center gap-2 rounded-lg border border-dashed border-white/10 bg-white/[0.02] px-3 py-2"
|
className="flex items-center gap-2 rounded-lg border border-dashed border-border bg-white/[0.02] px-3 py-2"
|
||||||
>
|
>
|
||||||
<CheckCircle2 className="h-4 w-4 text-emerald-400/50" />
|
<CheckCircle2 className="h-4 w-4 text-emerald-400/50" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={step.title}
|
value={step.title}
|
||||||
onChange={(e) => updateStep(step.id, { title: e.target.value })}
|
onChange={(e) => updateStep(step.id, { title: e.target.value })}
|
||||||
className="flex-1 bg-transparent text-sm text-white/50 focus:outline-none"
|
className="flex-1 bg-transparent text-sm text-muted-foreground focus:outline-none"
|
||||||
placeholder="Procedure Complete"
|
placeholder="Procedure Complete"
|
||||||
/>
|
/>
|
||||||
<span className="text-[10px] text-white/30">END</span>
|
<span className="text-[10px] text-muted-foreground">END</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -96,18 +96,18 @@ export function StepList() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
className="group flex items-center gap-2 border-b border-white/[0.06] pb-1 pt-3"
|
className="group flex items-center gap-2 border-b border-border pb-1 pt-3"
|
||||||
>
|
>
|
||||||
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-white/20 group-hover:text-white/40" />
|
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-muted-foreground group-hover:text-muted-foreground" />
|
||||||
<span
|
<span
|
||||||
className="min-w-0 flex-1 cursor-pointer text-xs font-semibold uppercase tracking-wider text-white/40 hover:text-white/60"
|
className="min-w-0 flex-1 cursor-pointer text-xs font-semibold uppercase tracking-wider text-muted-foreground hover:text-muted-foreground"
|
||||||
onClick={() => setExpandedStepId(step.id)}
|
onClick={() => setExpandedStepId(step.id)}
|
||||||
>
|
>
|
||||||
{step.title || 'Untitled Section'}
|
{step.title || 'Untitled Section'}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => removeStep(step.id)}
|
onClick={() => removeStep(step.id)}
|
||||||
className="shrink-0 rounded p-1 text-white/30 opacity-0 hover:bg-red-500/20 hover:text-red-400 group-hover:opacity-100"
|
className="shrink-0 rounded p-1 text-muted-foreground opacity-0 hover:bg-red-500/20 hover:text-red-400 group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -141,13 +141,13 @@ export function StepList() {
|
|||||||
<div key={step.id}>
|
<div key={step.id}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'group flex items-center gap-2 rounded-xl border border-white/[0.06] px-3 py-2.5 transition-colors',
|
'group flex items-center gap-2 rounded-xl border border-border px-3 py-2.5 transition-colors',
|
||||||
'hover:border-white/10 hover:bg-white/[0.03]'
|
'hover:border-primary/30 hover:bg-white/[0.03]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-white/20 group-hover:text-white/40" />
|
<GripVertical className="h-4 w-4 shrink-0 cursor-grab text-muted-foreground group-hover:text-muted-foreground" />
|
||||||
|
|
||||||
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-white/10 text-xs font-medium text-white/70">
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-accent text-xs font-medium text-muted-foreground">
|
||||||
{stepNumber}
|
{stepNumber}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -156,28 +156,28 @@ export function StepList() {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
className="min-w-0 flex-1 cursor-pointer truncate text-sm text-white"
|
className="min-w-0 flex-1 cursor-pointer truncate text-sm text-foreground"
|
||||||
onClick={() => setExpandedStepId(step.id)}
|
onClick={() => setExpandedStepId(step.id)}
|
||||||
>
|
>
|
||||||
{step.title || 'Untitled step'}
|
{step.title || 'Untitled step'}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{step.estimated_minutes && (
|
{step.estimated_minutes && (
|
||||||
<span className="shrink-0 text-[10px] text-white/30">
|
<span className="shrink-0 text-[10px] text-muted-foreground">
|
||||||
~{step.estimated_minutes}m
|
~{step.estimated_minutes}m
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setExpandedStepId(step.id)}
|
onClick={() => setExpandedStepId(step.id)}
|
||||||
className="shrink-0 rounded p-1 text-white/30 hover:bg-white/10 hover:text-white"
|
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<ChevronDown className="h-3.5 w-3.5" />
|
<ChevronDown className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => removeStep(step.id)}
|
onClick={() => removeStep(step.id)}
|
||||||
className="shrink-0 rounded p-1 text-white/30 opacity-0 hover:bg-red-500/20 hover:text-red-400 group-hover:opacity-100"
|
className="shrink-0 rounded p-1 text-muted-foreground opacity-0 hover:bg-red-500/20 hover:text-red-400 group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3.5 w-3.5" />
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -190,7 +190,7 @@ export function StepList() {
|
|||||||
{/* Add step button at bottom */}
|
{/* Add step button at bottom */}
|
||||||
<button
|
<button
|
||||||
onClick={() => addStep()}
|
onClick={() => addStep()}
|
||||||
className="mt-3 flex w-full items-center justify-center gap-1.5 rounded-lg border border-dashed border-white/10 py-2 text-sm text-white/40 transition-colors hover:border-white/20 hover:text-white/60"
|
className="mt-3 flex w-full items-center justify-center gap-1.5 rounded-lg border border-dashed border-border py-2 text-sm text-muted-foreground transition-colors hover:border-primary/30 hover:text-muted-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="h-3.5 w-3.5" />
|
<Plus className="h-3.5 w-3.5" />
|
||||||
Add Step
|
Add Step
|
||||||
|
|||||||
@@ -58,38 +58,38 @@ export function CompletionSummary({
|
|||||||
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-400/10">
|
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-400/10">
|
||||||
<CheckCircle2 className="h-8 w-8 text-emerald-400" />
|
<CheckCircle2 className="h-8 w-8 text-emerald-400" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl font-bold text-white">Procedure Complete</h1>
|
<h1 className="text-2xl font-bold text-foreground">Procedure Complete</h1>
|
||||||
<p className="mt-1 text-white/40">{treeName}</p>
|
<p className="mt-1 text-muted-foreground">{treeName}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary stats */}
|
{/* Summary stats */}
|
||||||
<div className="grid grid-cols-3 gap-3">
|
<div className="grid grid-cols-3 gap-3">
|
||||||
<div className="glass-card rounded-xl p-3 text-center">
|
<div className="bg-card border border-border rounded-xl p-3 text-center">
|
||||||
<CheckCircle2 className="mx-auto mb-1 h-5 w-5 text-emerald-400" />
|
<CheckCircle2 className="mx-auto mb-1 h-5 w-5 text-emerald-400" />
|
||||||
<div className="text-lg font-semibold text-white">{procedureSteps.length}</div>
|
<div className="text-lg font-semibold text-foreground">{procedureSteps.length}</div>
|
||||||
<div className="text-xs text-white/40">Steps Completed</div>
|
<div className="text-xs text-muted-foreground">Steps Completed</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="glass-card rounded-xl p-3 text-center">
|
<div className="bg-card border border-border rounded-xl p-3 text-center">
|
||||||
<Clock className="mx-auto mb-1 h-5 w-5 text-white/50" />
|
<Clock className="mx-auto mb-1 h-5 w-5 text-muted-foreground" />
|
||||||
<div className="text-lg font-semibold text-white">{formatTime(totalMinutes)}</div>
|
<div className="text-lg font-semibold text-foreground">{formatTime(totalMinutes)}</div>
|
||||||
<div className="text-xs text-white/40">Total Time</div>
|
<div className="text-xs text-muted-foreground">Total Time</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="glass-card rounded-xl p-3 text-center">
|
<div className="bg-card border border-border rounded-xl p-3 text-center">
|
||||||
<FileText className="mx-auto mb-1 h-5 w-5 text-white/50" />
|
<FileText className="mx-auto mb-1 h-5 w-5 text-muted-foreground" />
|
||||||
<div className="text-lg font-semibold text-white">{Object.keys(variables).length}</div>
|
<div className="text-lg font-semibold text-foreground">{Object.keys(variables).length}</div>
|
||||||
<div className="text-xs text-white/40">Parameters</div>
|
<div className="text-xs text-muted-foreground">Parameters</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Project parameters */}
|
{/* Project parameters */}
|
||||||
{Object.keys(variables).length > 0 && (
|
{Object.keys(variables).length > 0 && (
|
||||||
<div className="glass-card rounded-xl p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
<h3 className="mb-3 text-sm font-semibold text-white/60">Project Parameters</h3>
|
<h3 className="mb-3 text-sm font-semibold text-muted-foreground">Project Parameters</h3>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
{Object.entries(variables).map(([key, value]) => (
|
{Object.entries(variables).map(([key, value]) => (
|
||||||
<div key={key} className="flex items-baseline justify-between gap-4 text-sm">
|
<div key={key} className="flex items-baseline justify-between gap-4 text-sm">
|
||||||
<span className="font-mono text-white/40">{key}</span>
|
<span className="font-mono text-muted-foreground">{key}</span>
|
||||||
<span className="text-right text-white/70">{value}</span>
|
<span className="text-right text-muted-foreground">{value}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -97,8 +97,8 @@ export function CompletionSummary({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Step details */}
|
{/* Step details */}
|
||||||
<div className="glass-card rounded-xl p-4">
|
<div className="bg-card border border-border rounded-xl p-4">
|
||||||
<h3 className="mb-3 text-sm font-semibold text-white/60">Step Summary</h3>
|
<h3 className="mb-3 text-sm font-semibold text-muted-foreground">Step Summary</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{procedureSteps.map((step, index) => {
|
{procedureSteps.map((step, index) => {
|
||||||
const completion = completions.get(step.id)
|
const completion = completions.get(step.id)
|
||||||
@@ -107,15 +107,15 @@ export function CompletionSummary({
|
|||||||
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-emerald-400" />
|
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-emerald-400" />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-white/70">
|
<span className="text-muted-foreground">
|
||||||
{index + 1}. {step.title}
|
{index + 1}. {step.title}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{completion?.notes && (
|
{completion?.notes && (
|
||||||
<p className="mt-0.5 text-xs text-white/30">Note: {completion.notes}</p>
|
<p className="mt-0.5 text-xs text-muted-foreground">Note: {completion.notes}</p>
|
||||||
)}
|
)}
|
||||||
{completion?.verificationValue && (
|
{completion?.verificationValue && (
|
||||||
<p className="mt-0.5 text-xs text-white/30">
|
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||||
Verified: {completion.verificationValue}
|
Verified: {completion.verificationValue}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -130,14 +130,14 @@ export function CompletionSummary({
|
|||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={onExport}
|
onClick={onExport}
|
||||||
className="flex flex-1 items-center justify-center gap-2 rounded-lg border border-white/10 px-4 py-2.5 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
className="flex flex-1 items-center justify-center gap-2 rounded-lg border border-border px-4 py-2.5 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
<Download className="h-4 w-4" />
|
<Download className="h-4 w-4" />
|
||||||
Export Report
|
Export Report
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="flex flex-1 items-center justify-center gap-2 rounded-lg bg-white px-4 py-2.5 text-sm font-medium text-black hover:bg-white/90"
|
className="flex flex-1 items-center justify-center gap-2 rounded-lg bg-gradient-brand px-4 py-2.5 text-sm font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90"
|
||||||
>
|
>
|
||||||
Done
|
Done
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
const error = errors[field.variable_name]
|
const error = errors[field.variable_name]
|
||||||
|
|
||||||
const baseInputClass = cn(
|
const baseInputClass = cn(
|
||||||
'w-full rounded-lg border bg-black/50 px-3 py-2 text-sm text-white placeholder:text-white/30 focus:outline-none focus:ring-1',
|
'w-full rounded-lg border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1',
|
||||||
error
|
error
|
||||||
? 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20'
|
? 'border-red-400/50 focus:border-red-400 focus:ring-red-400/20'
|
||||||
: 'border-white/10 focus:border-white/30 focus:ring-white/20'
|
: 'border-border focus:border-primary focus:ring-primary/20'
|
||||||
)
|
)
|
||||||
|
|
||||||
let input: React.ReactNode
|
let input: React.ReactNode
|
||||||
@@ -111,9 +111,9 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={value === 'true'}
|
checked={value === 'true'}
|
||||||
onChange={(e) => setValue(field.variable_name, e.target.checked ? 'true' : 'false')}
|
onChange={(e) => setValue(field.variable_name, e.target.checked ? 'true' : 'false')}
|
||||||
className="rounded border-white/20"
|
className="rounded border-border"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-white/70">{field.placeholder || field.label}</span>
|
<span className="text-sm text-muted-foreground">{field.placeholder || field.label}</span>
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@@ -161,9 +161,9 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
: [...selected, opt]
|
: [...selected, opt]
|
||||||
setValue(field.variable_name, next.join(','))
|
setValue(field.variable_name, next.join(','))
|
||||||
}}
|
}}
|
||||||
className="rounded border-white/20"
|
className="rounded border-border"
|
||||||
/>
|
/>
|
||||||
<span className="text-sm text-white/70">{opt}</span>
|
<span className="text-sm text-muted-foreground">{opt}</span>
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -197,12 +197,12 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={field.variable_name}>
|
<div key={field.variable_name}>
|
||||||
<label className="mb-1 flex items-center gap-1 text-sm font-medium text-white/60">
|
<label className="mb-1 flex items-center gap-1 text-sm font-medium text-muted-foreground">
|
||||||
{field.label}
|
{field.label}
|
||||||
{field.required && <span className="text-red-400">*</span>}
|
{field.required && <span className="text-red-400">*</span>}
|
||||||
</label>
|
</label>
|
||||||
{field.help_text && (
|
{field.help_text && (
|
||||||
<p className="mb-1.5 text-xs text-white/30">{field.help_text}</p>
|
<p className="mb-1.5 text-xs text-muted-foreground">{field.help_text}</p>
|
||||||
)}
|
)}
|
||||||
{input}
|
{input}
|
||||||
{error && <p className="mt-1 text-xs text-red-400">{error}</p>}
|
{error && <p className="mt-1 text-xs text-red-400">{error}</p>}
|
||||||
@@ -212,12 +212,12 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||||
<div className="mx-4 w-full max-w-lg rounded-2xl border border-white/10 bg-[#0a0a0a] shadow-xl">
|
<div className="mx-4 w-full max-w-lg rounded-2xl border border-border bg-[#0a0a0a] shadow-xl">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="border-b border-white/[0.06] px-6 py-4">
|
<div className="border-b border-border px-6 py-4">
|
||||||
<h2 className="text-lg font-semibold text-white">Project Information</h2>
|
<h2 className="text-lg font-semibold text-foreground">Project Information</h2>
|
||||||
<p className="mt-0.5 text-sm text-white/40">
|
<p className="mt-0.5 text-sm text-muted-foreground">
|
||||||
Fill in the details for <span className="text-white/60">{treeName}</span>
|
Fill in the details for <span className="text-muted-foreground">{treeName}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
{Array.from(groups.entries()).map(([groupName, groupFields]) => (
|
{Array.from(groups.entries()).map(([groupName, groupFields]) => (
|
||||||
<div key={groupName}>
|
<div key={groupName}>
|
||||||
{groupName && (
|
{groupName && (
|
||||||
<h3 className="mb-3 border-b border-white/[0.06] pb-1 text-xs font-semibold uppercase tracking-wider text-white/40">
|
<h3 className="mb-3 border-b border-border pb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||||
{groupName}
|
{groupName}
|
||||||
</h3>
|
</h3>
|
||||||
)}
|
)}
|
||||||
@@ -239,17 +239,17 @@ export function IntakeFormModal({ isOpen, fields, treeName, onSubmit, onCancel }
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-end gap-2 border-t border-white/[0.06] px-6 py-4">
|
<div className="flex items-center justify-end gap-2 border-t border-border px-6 py-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
className="rounded-md border border-white/10 px-4 py-2 text-sm text-white/60 hover:bg-white/10 hover:text-white"
|
className="rounded-md border border-border px-4 py-2 text-sm text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
className="rounded-md bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90"
|
||||||
>
|
>
|
||||||
Start Procedure
|
Start Procedure
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function StepChecklist({ steps, currentStepIndex, completedStepIds, onSte
|
|||||||
return (
|
return (
|
||||||
<div key={step.id}>
|
<div key={step.id}>
|
||||||
{showSection && (
|
{showSection && (
|
||||||
<div className="mb-1 mt-3 border-b border-white/[0.06] pb-1 text-[10px] font-semibold uppercase tracking-wider text-white/40 first:mt-0">
|
<div className="mb-1 mt-3 border-b border-border pb-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground first:mt-0">
|
||||||
{step.section_header}
|
{step.section_header}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -39,24 +39,24 @@ export function StepChecklist({ steps, currentStepIndex, completedStepIds, onSte
|
|||||||
onClick={() => onStepClick(index)}
|
onClick={() => onStepClick(index)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm transition-colors',
|
'flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm transition-colors',
|
||||||
isCurrent && 'bg-white/10 text-white',
|
isCurrent && 'bg-accent text-foreground',
|
||||||
!isCurrent && isCompleted && 'text-white/40',
|
!isCurrent && isCompleted && 'text-muted-foreground',
|
||||||
!isCurrent && !isCompleted && 'text-white/50 hover:bg-white/[0.04]'
|
!isCurrent && !isCompleted && 'text-muted-foreground hover:bg-accent/50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isCompleted ? (
|
{isCompleted ? (
|
||||||
<CheckCircle2 className="h-4 w-4 shrink-0 text-emerald-400" />
|
<CheckCircle2 className="h-4 w-4 shrink-0 text-emerald-400" />
|
||||||
) : isCurrent ? (
|
) : isCurrent ? (
|
||||||
<ArrowRight className="h-4 w-4 shrink-0 text-white" />
|
<ArrowRight className="h-4 w-4 shrink-0 text-foreground" />
|
||||||
) : (
|
) : (
|
||||||
<Circle className="h-4 w-4 shrink-0 text-white/20" />
|
<Circle className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-white/10 text-[10px] font-medium">
|
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-accent text-[10px] font-medium">
|
||||||
{index + 1}
|
{index + 1}
|
||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 flex-1 truncate">{step.title || 'Untitled step'}</span>
|
<span className="min-w-0 flex-1 truncate">{step.title || 'Untitled step'}</span>
|
||||||
{step.estimated_minutes && (
|
{step.estimated_minutes && (
|
||||||
<span className="shrink-0 text-[10px] text-white/30">~{step.estimated_minutes}m</span>
|
<span className="shrink-0 text-[10px] text-muted-foreground">~{step.estimated_minutes}m</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { cn } from '@/lib/utils'
|
|||||||
|
|
||||||
const contentTypeConfig: Record<StepContentType, { icon: typeof Zap; color: string; bg: string; label: string }> = {
|
const contentTypeConfig: Record<StepContentType, { icon: typeof Zap; color: string; bg: string; label: string }> = {
|
||||||
action: { icon: Zap, color: 'text-blue-400', bg: 'bg-blue-400/10', label: 'Action' },
|
action: { icon: Zap, color: 'text-blue-400', bg: 'bg-blue-400/10', label: 'Action' },
|
||||||
informational: { icon: Info, color: 'text-white/50', bg: 'bg-white/10', label: 'Info' },
|
informational: { icon: Info, color: 'text-muted-foreground', bg: 'bg-accent', label: 'Info' },
|
||||||
verification: { icon: CheckCircle2, color: 'text-emerald-400', bg: 'bg-emerald-400/10', label: 'Verification' },
|
verification: { icon: CheckCircle2, color: 'text-emerald-400', bg: 'bg-emerald-400/10', label: 'Verification' },
|
||||||
warning: { icon: AlertTriangle, color: 'text-yellow-400', bg: 'bg-yellow-400/10', label: 'Warning' },
|
warning: { icon: AlertTriangle, color: 'text-yellow-400', bg: 'bg-yellow-400/10', label: 'Warning' },
|
||||||
}
|
}
|
||||||
@@ -81,21 +81,21 @@ export function StepDetail({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Step header */}
|
{/* Step header */}
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-white/10 text-sm font-semibold text-white">
|
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent text-sm font-semibold text-foreground">
|
||||||
{stepNumber}
|
{stepNumber}
|
||||||
</span>
|
</span>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<h2 className="text-lg font-semibold text-white">{step.title}</h2>
|
<h2 className="text-lg font-semibold text-foreground">{step.title}</h2>
|
||||||
<div className="mt-1 flex items-center gap-2">
|
<div className="mt-1 flex items-center gap-2">
|
||||||
<span className={cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs', config.bg, config.color)}>
|
<span className={cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs', config.bg, config.color)}>
|
||||||
<Icon className="h-3 w-3" />
|
<Icon className="h-3 w-3" />
|
||||||
{config.label}
|
{config.label}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-white/30">
|
<span className="text-xs text-muted-foreground">
|
||||||
Step {stepNumber} of {totalSteps}
|
Step {stepNumber} of {totalSteps}
|
||||||
</span>
|
</span>
|
||||||
{step.estimated_minutes && (
|
{step.estimated_minutes && (
|
||||||
<span className="text-xs text-white/30">~{step.estimated_minutes} min</span>
|
<span className="text-xs text-muted-foreground">~{step.estimated_minutes} min</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,7 +111,7 @@ export function StepDetail({
|
|||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
{step.description && (
|
{step.description && (
|
||||||
<div className="prose prose-invert prose-sm max-w-none text-white/70">
|
<div className="prose prose-invert prose-sm max-w-none text-muted-foreground">
|
||||||
<p className="whitespace-pre-wrap">{resolve(step.description)}</p>
|
<p className="whitespace-pre-wrap">{resolve(step.description)}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -120,14 +120,14 @@ export function StepDetail({
|
|||||||
{commandBlocks.length > 0 && (
|
{commandBlocks.length > 0 && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{commandBlocks.map((cmd, i) => (
|
{commandBlocks.map((cmd, i) => (
|
||||||
<div key={i} className="rounded-lg border border-white/[0.06] bg-black/50">
|
<div key={i} className="rounded-lg border border-border bg-card">
|
||||||
<div className="flex items-center justify-between border-b border-white/[0.06] px-3 py-1.5">
|
<div className="flex items-center justify-between border-b border-border px-3 py-1.5">
|
||||||
<span className="text-xs font-medium text-white/40">
|
<span className="text-xs font-medium text-muted-foreground">
|
||||||
{cmd.label || (cmd.language ? cmd.language : 'Command')}
|
{cmd.label || (cmd.language ? cmd.language : 'Command')}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleCopyCommand(cmd.code, i)}
|
onClick={() => handleCopyCommand(cmd.code, i)}
|
||||||
className="flex items-center gap-1 rounded px-2 py-0.5 text-xs text-white/40 hover:bg-white/10 hover:text-white"
|
className="flex items-center gap-1 rounded px-2 py-0.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
{copiedIndex === i ? <Check className="h-3 w-3 text-emerald-400" /> : <Copy className="h-3 w-3" />}
|
{copiedIndex === i ? <Check className="h-3 w-3 text-emerald-400" /> : <Copy className="h-3 w-3" />}
|
||||||
{copiedIndex === i ? 'Copied' : 'Copy'}
|
{copiedIndex === i ? 'Copied' : 'Copy'}
|
||||||
@@ -143,37 +143,37 @@ export function StepDetail({
|
|||||||
|
|
||||||
{/* Expected outcome */}
|
{/* Expected outcome */}
|
||||||
{step.expected_outcome && (
|
{step.expected_outcome && (
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-white/[0.02] p-3">
|
<div className="rounded-lg border border-border bg-white/[0.02] p-3">
|
||||||
<h4 className="mb-1 text-xs font-medium text-white/50">Expected Outcome</h4>
|
<h4 className="mb-1 text-xs font-medium text-muted-foreground">Expected Outcome</h4>
|
||||||
<p className="text-sm text-white/70">{resolve(step.expected_outcome)}</p>
|
<p className="text-sm text-muted-foreground">{resolve(step.expected_outcome)}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Verification */}
|
{/* Verification */}
|
||||||
{verificationPrompt && (
|
{verificationPrompt && (
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-white/[0.02] p-3">
|
<div className="rounded-lg border border-border bg-white/[0.02] p-3">
|
||||||
<h4 className="mb-2 text-xs font-medium text-white/50">Verification</h4>
|
<h4 className="mb-2 text-xs font-medium text-muted-foreground">Verification</h4>
|
||||||
{verificationType === 'checkbox' ? (
|
{verificationType === 'checkbox' ? (
|
||||||
<label className="flex items-center gap-2 text-sm text-white/70">
|
<label className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={!!verificationValue}
|
checked={!!verificationValue}
|
||||||
onChange={(e) => onVerificationChange(e.target.checked ? 'confirmed' : '')}
|
onChange={(e) => onVerificationChange(e.target.checked ? 'confirmed' : '')}
|
||||||
disabled={isCompleted}
|
disabled={isCompleted}
|
||||||
className="rounded border-white/20"
|
className="rounded border-border"
|
||||||
/>
|
/>
|
||||||
{resolve(verificationPrompt)}
|
{resolve(verificationPrompt)}
|
||||||
</label>
|
</label>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-2 text-sm text-white/70">{resolve(verificationPrompt)}</p>
|
<p className="mb-2 text-sm text-muted-foreground">{resolve(verificationPrompt)}</p>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={verificationValue}
|
value={verificationValue}
|
||||||
onChange={(e) => onVerificationChange(e.target.value)}
|
onChange={(e) => onVerificationChange(e.target.value)}
|
||||||
disabled={isCompleted}
|
disabled={isCompleted}
|
||||||
placeholder="Enter observed value..."
|
placeholder="Enter observed value..."
|
||||||
className="w-full rounded border border-white/10 bg-black/50 px-3 py-1.5 text-sm text-white placeholder:text-white/30 focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20 disabled:opacity-50"
|
className="w-full rounded border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20 disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -183,13 +183,13 @@ export function StepDetail({
|
|||||||
{/* Notes */}
|
{/* Notes */}
|
||||||
{step.notes_enabled !== false && (
|
{step.notes_enabled !== false && (
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs font-medium text-white/50">Notes</label>
|
<label className="mb-1 block text-xs font-medium text-muted-foreground">Notes</label>
|
||||||
<textarea
|
<textarea
|
||||||
value={notes}
|
value={notes}
|
||||||
onChange={(e) => onNotesChange(e.target.value)}
|
onChange={(e) => onNotesChange(e.target.value)}
|
||||||
placeholder="Add notes for this step..."
|
placeholder="Add notes for this step..."
|
||||||
rows={2}
|
rows={2}
|
||||||
className="w-full rounded-lg border border-white/10 bg-black/50 px-3 py-2 text-sm 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-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -200,7 +200,7 @@ export function StepDetail({
|
|||||||
href={resolve(step.reference_url)}
|
href={resolve(step.reference_url)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="inline-flex items-center gap-1.5 text-sm text-white/40 hover:text-white"
|
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
<ExternalLink className="h-3.5 w-3.5" />
|
<ExternalLink className="h-3.5 w-3.5" />
|
||||||
Reference Documentation
|
Reference Documentation
|
||||||
@@ -216,7 +216,7 @@ export function StepDetail({
|
|||||||
'flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-medium transition-colors',
|
'flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-medium transition-colors',
|
||||||
isCompleted
|
isCompleted
|
||||||
? 'bg-emerald-400/10 text-emerald-400'
|
? 'bg-emerald-400/10 text-emerald-400'
|
||||||
: 'bg-white text-black hover:bg-white/90 disabled:opacity-40 disabled:hover:bg-white'
|
: 'bg-gradient-brand text-white shadow-lg shadow-primary/20 hover:opacity-90 disabled:opacity-40 disabled:hover:opacity-100'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isCompleted ? (
|
{isCompleted ? (
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm">
|
||||||
<div className="relative flex h-[90vh] w-full max-w-3xl flex-col glass-card rounded-2xl shadow-lg">
|
<div className="relative flex h-[90vh] w-full max-w-3xl flex-col bg-card border border-border rounded-2xl shadow-lg">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-start justify-between border-b border-white/[0.06] p-6 pb-4">
|
<div className="flex items-start justify-between border-b border-border p-6 pb-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="h-6 w-48 animate-pulse rounded bg-white/10" />
|
<div className="h-6 w-48 animate-pulse rounded bg-accent" />
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<h2 className="text-lg font-semibold text-red-400">{error}</h2>
|
<h2 className="text-lg font-semibold text-red-400">{error}</h2>
|
||||||
) : step ? (
|
) : step ? (
|
||||||
@@ -90,7 +90,7 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{step.step_type}
|
{step.step_type}
|
||||||
</span>
|
</span>
|
||||||
{step.category_name && (
|
{step.category_name && (
|
||||||
<span className="text-xs text-white/40">📁 {step.category_name}</span>
|
<span className="text-xs text-muted-foreground">{step.category_name}</span>
|
||||||
)}
|
)}
|
||||||
{step.is_featured && (
|
{step.is_featured && (
|
||||||
<span className="rounded bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400">
|
<span className="rounded bg-yellow-400/10 px-2 py-0.5 text-xs font-medium text-yellow-400">
|
||||||
@@ -99,16 +99,16 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
)}
|
)}
|
||||||
{step.is_verified && (
|
{step.is_verified && (
|
||||||
<span className="rounded bg-emerald-400/10 px-2 py-0.5 text-xs font-medium text-emerald-400">
|
<span className="rounded bg-emerald-400/10 px-2 py-0.5 text-xs font-medium text-emerald-400">
|
||||||
✓ Verified
|
Verified
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-xl font-semibold text-white">{step.title}</h2>
|
<h2 className="text-xl font-semibold text-foreground">{step.title}</h2>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="rounded-md p-1 text-white/40 hover:bg-white/10 hover:text-white"
|
className="rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
<X className="h-5 w-5" />
|
<X className="h-5 w-5" />
|
||||||
@@ -119,18 +119,18 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
<div className="flex-1 overflow-y-auto p-6">
|
<div className="flex-1 overflow-y-auto p-6">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="h-4 w-full animate-pulse rounded bg-white/10" />
|
<div className="h-4 w-full animate-pulse rounded bg-accent" />
|
||||||
<div className="h-4 w-3/4 animate-pulse rounded bg-white/10" />
|
<div className="h-4 w-3/4 animate-pulse rounded bg-accent" />
|
||||||
<div className="h-4 w-5/6 animate-pulse rounded bg-white/10" />
|
<div className="h-4 w-5/6 animate-pulse rounded bg-accent" />
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<p className="text-sm text-white/40">{error}</p>
|
<p className="text-sm text-muted-foreground">{error}</p>
|
||||||
) : step ? (
|
) : step ? (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Rating */}
|
{/* Rating */}
|
||||||
{hasRating && (
|
{hasRating && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-sm font-semibold text-white">Rating</h3>
|
<h3 className="mb-2 text-sm font-semibold text-foreground">Rating</h3>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{[1, 2, 3, 4, 5].map(i => (
|
{[1, 2, 3, 4, 5].map(i => (
|
||||||
@@ -140,12 +140,12 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
'h-4 w-4',
|
'h-4 w-4',
|
||||||
i <= Math.round(step.rating_average)
|
i <= Math.round(step.rating_average)
|
||||||
? 'fill-yellow-400 text-yellow-400'
|
? 'fill-yellow-400 text-yellow-400'
|
||||||
: 'text-white/20'
|
: 'text-muted-foreground'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-white/70">
|
<span className="text-sm text-muted-foreground">
|
||||||
{step.rating_average.toFixed(1)} ({step.rating_count} {step.rating_count === 1 ? 'rating' : 'ratings'})
|
{step.rating_average.toFixed(1)} ({step.rating_count} {step.rating_count === 1 ? 'rating' : 'ratings'})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -155,12 +155,12 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{/* Tags */}
|
{/* Tags */}
|
||||||
{step.tags.length > 0 && (
|
{step.tags.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-sm font-semibold text-white">Tags</h3>
|
<h3 className="mb-2 text-sm font-semibold text-foreground">Tags</h3>
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{step.tags.map(tag => (
|
{step.tags.map(tag => (
|
||||||
<span
|
<span
|
||||||
key={tag}
|
key={tag}
|
||||||
className="rounded-full bg-white/10 px-2 py-1 text-xs text-white/70"
|
className="rounded-full bg-accent px-2 py-1 text-xs text-muted-foreground"
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
@@ -172,7 +172,7 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{/* Instructions */}
|
{/* Instructions */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-sm font-semibold">Instructions</h3>
|
<h3 className="mb-2 text-sm font-semibold">Instructions</h3>
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-white/5 p-4">
|
<div className="rounded-lg border border-border bg-accent/50 p-4">
|
||||||
<MarkdownContent content={step.content.instructions} />
|
<MarkdownContent content={step.content.instructions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,8 +180,8 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{/* Help Text */}
|
{/* Help Text */}
|
||||||
{step.content.help_text && (
|
{step.content.help_text && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-sm font-semibold text-white">Help Text</h3>
|
<h3 className="mb-2 text-sm font-semibold text-foreground">Help Text</h3>
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-blue-400/5 p-4 text-sm">
|
<div className="rounded-lg border border-border bg-blue-400/5 p-4 text-sm">
|
||||||
<MarkdownContent content={step.content.help_text} />
|
<MarkdownContent content={step.content.help_text} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,19 +190,19 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{/* Commands */}
|
{/* Commands */}
|
||||||
{step.content.commands && step.content.commands.length > 0 && (
|
{step.content.commands && step.content.commands.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-2 text-sm font-semibold text-white">Commands</h3>
|
<h3 className="mb-2 text-sm font-semibold text-foreground">Commands</h3>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{step.content.commands.map((cmd, index) => (
|
{step.content.commands.map((cmd, index) => (
|
||||||
<div key={index} className="group relative">
|
<div key={index} className="group relative">
|
||||||
<div className="mb-1 flex items-center justify-between">
|
<div className="mb-1 flex items-center justify-between">
|
||||||
<span className="text-xs font-medium text-white/40">{cmd.label}</span>
|
<span className="text-xs font-medium text-muted-foreground">{cmd.label}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleCopyCommand(cmd.command, index)}
|
onClick={() => handleCopyCommand(cmd.command, index)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
|
'flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors',
|
||||||
copiedCommandIndex === index
|
copiedCommandIndex === index
|
||||||
? 'bg-emerald-400/10 text-emerald-400'
|
? 'bg-emerald-400/10 text-emerald-400'
|
||||||
: 'bg-white/10 text-white/40 hover:bg-white/20 hover:text-white'
|
: 'bg-accent text-muted-foreground hover:bg-accent hover:text-foreground'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{copiedCommandIndex === index ? (
|
{copiedCommandIndex === index ? (
|
||||||
@@ -218,7 +218,7 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="overflow-x-auto rounded bg-black/50 p-3 text-xs text-white">
|
<pre className="overflow-x-auto rounded bg-card p-3 text-xs text-foreground">
|
||||||
<code>{cmd.command}</code>
|
<code>{cmd.command}</code>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -231,23 +231,23 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
{topReviews.length > 0 && (
|
{topReviews.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<h3 className="text-sm font-semibold text-white">Reviews</h3>
|
<h3 className="text-sm font-semibold text-foreground">Reviews</h3>
|
||||||
{reviews.length > 3 && (
|
{reviews.length > 3 && (
|
||||||
<button className="text-xs text-white/70 hover:text-white hover:underline">
|
<button className="text-xs text-muted-foreground hover:text-foreground hover:underline">
|
||||||
See all {reviews.length} reviews
|
See all {reviews.length} reviews
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{topReviews.map(review => (
|
{topReviews.map(review => (
|
||||||
<div key={review.id} className="rounded-lg border border-white/[0.06] bg-white/5 p-3">
|
<div key={review.id} className="rounded-lg border border-border bg-accent/50 p-3">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<User className="h-3.5 w-3.5" />
|
<User className="h-3.5 w-3.5" />
|
||||||
<span className="font-medium text-white">{review.user_name || 'Anonymous'}</span>
|
<span className="font-medium text-foreground">{review.user_name || 'Anonymous'}</span>
|
||||||
{review.verified_use && (
|
{review.verified_use && (
|
||||||
<span className="rounded bg-emerald-400/10 px-1.5 py-0.5 text-xs text-emerald-400">
|
<span className="rounded bg-emerald-400/10 px-1.5 py-0.5 text-xs text-emerald-400">
|
||||||
✓ Verified Use
|
Verified Use
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -259,14 +259,14 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
'h-3 w-3',
|
'h-3 w-3',
|
||||||
i <= review.rating
|
i <= review.rating
|
||||||
? 'fill-yellow-400 text-yellow-400'
|
? 'fill-yellow-400 text-yellow-400'
|
||||||
: 'text-white/20'
|
: 'text-muted-foreground'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-white/70">{review.review_text}</p>
|
<p className="text-sm text-muted-foreground">{review.review_text}</p>
|
||||||
<div className="mt-2 flex items-center gap-2 text-xs text-white/40">
|
<div className="mt-2 flex items-center gap-2 text-xs text-muted-foreground">
|
||||||
<Calendar className="h-3 w-3" />
|
<Calendar className="h-3 w-3" />
|
||||||
{new Date(review.created_at).toLocaleDateString()}
|
{new Date(review.created_at).toLocaleDateString()}
|
||||||
</div>
|
</div>
|
||||||
@@ -277,22 +277,22 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Metadata */}
|
{/* Metadata */}
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-white/5 p-4">
|
<div className="rounded-lg border border-border bg-accent/50 p-4">
|
||||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-white/40">Author:</span>
|
<span className="text-muted-foreground">Author:</span>
|
||||||
<span className="ml-2 font-medium text-white">{step.author_name || 'Unknown'}</span>
|
<span className="ml-2 font-medium text-foreground">{step.author_name || 'Unknown'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-white/40">Usage Count:</span>
|
<span className="text-muted-foreground">Usage Count:</span>
|
||||||
<span className="ml-2 font-medium text-white">{step.usage_count}</span>
|
<span className="ml-2 font-medium text-foreground">{step.usage_count}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-white/40">Created:</span>
|
<span className="text-muted-foreground">Created:</span>
|
||||||
<span className="ml-2 font-medium text-white">{new Date(step.created_at).toLocaleDateString()}</span>
|
<span className="ml-2 font-medium text-foreground">{new Date(step.created_at).toLocaleDateString()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-white/40">Visibility:</span>
|
<span className="text-muted-foreground">Visibility:</span>
|
||||||
<span className="ml-2 font-medium capitalize">{step.visibility}</span>
|
<span className="ml-2 font-medium capitalize">{step.visibility}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -302,10 +302,10 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer - Actions */}
|
{/* Footer - Actions */}
|
||||||
<div className="flex gap-2 border-t border-white/[0.06] p-4">
|
<div className="flex gap-2 border-t border-border p-4">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="flex-1 rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
|
className="flex-1 rounded-md border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-foreground"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
@@ -313,8 +313,8 @@ export function StepDetailModal({ stepId, onClose, onInsert }: StepDetailModalPr
|
|||||||
onClick={handleInsert}
|
onClick={handleInsert}
|
||||||
disabled={!step}
|
disabled={!step}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex-1 rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
'flex-1 rounded-md bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20',
|
||||||
'hover:bg-white/90 disabled:opacity-50'
|
'hover:opacity-90 disabled:opacity-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Insert Into Session
|
Insert Into Session
|
||||||
|
|||||||
@@ -133,16 +133,16 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col">
|
<div className="flex h-full flex-col">
|
||||||
{/* Header - Filters */}
|
{/* Header - Filters */}
|
||||||
<div className="space-y-4 border-b border-white/[0.06] p-4">
|
<div className="space-y-4 border-b border-border p-4">
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-white/40" />
|
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search steps..."
|
placeholder="Search steps..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="w-full rounded-md border border-white/10 bg-black/50 py-2 pl-10 pr-4 text-sm text-white placeholder:text-white/40 focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
className="w-full rounded-md border border-border bg-card py-2 pl-10 pr-4 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
aria-label="Filter by category"
|
aria-label="Filter by category"
|
||||||
value={selectedCategoryId || ''}
|
value={selectedCategoryId || ''}
|
||||||
onChange={(e) => setSelectedCategoryId(e.target.value || undefined)}
|
onChange={(e) => setSelectedCategoryId(e.target.value || undefined)}
|
||||||
className="rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
<option value="">All Categories</option>
|
<option value="">All Categories</option>
|
||||||
{categories.map(cat => (
|
{categories.map(cat => (
|
||||||
@@ -166,7 +166,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
aria-label="Filter by step type"
|
aria-label="Filter by step type"
|
||||||
value={selectedStepType || ''}
|
value={selectedStepType || ''}
|
||||||
onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)}
|
onChange={(e) => setSelectedStepType((e.target.value as 'decision' | 'action' | 'solution') || undefined)}
|
||||||
className="rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
<option value="">All Types</option>
|
<option value="">All Types</option>
|
||||||
<option value="decision">Decision</option>
|
<option value="decision">Decision</option>
|
||||||
@@ -179,7 +179,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
aria-label="Filter by minimum rating"
|
aria-label="Filter by minimum rating"
|
||||||
value={minRating?.toString() || ''}
|
value={minRating?.toString() || ''}
|
||||||
onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : undefined)}
|
onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : undefined)}
|
||||||
className="rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
<option value="">Any Rating</option>
|
<option value="">Any Rating</option>
|
||||||
<option value="4">4+ Stars</option>
|
<option value="4">4+ Stars</option>
|
||||||
@@ -192,7 +192,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
aria-label="Sort steps by"
|
aria-label="Sort steps by"
|
||||||
value={sortBy}
|
value={sortBy}
|
||||||
onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')}
|
onChange={(e) => setSortBy(e.target.value as 'recent' | 'popular' | 'highest_rated' | 'most_used')}
|
||||||
className="rounded-md border border-white/10 bg-black/50 px-3 py-2 text-sm text-white focus:outline-none focus:border-white/30 focus:ring-1 focus:ring-white/20"
|
className="rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
<option value="recent">Most Recent</option>
|
<option value="recent">Most Recent</option>
|
||||||
<option value="popular">Most Popular</option>
|
<option value="popular">Most Popular</option>
|
||||||
@@ -204,7 +204,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
{/* Popular Tags */}
|
{/* Popular Tags */}
|
||||||
{popularTags.length > 0 && (
|
{popularTags.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-2 text-xs font-medium text-white/40">Popular Tags:</div>
|
<div className="mb-2 text-xs font-medium text-muted-foreground">Popular Tags:</div>
|
||||||
<div className="flex flex-wrap gap-1.5">
|
<div className="flex flex-wrap gap-1.5">
|
||||||
{popularTags.map(tag => (
|
{popularTags.map(tag => (
|
||||||
<button
|
<button
|
||||||
@@ -213,8 +213,8 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
className={cn(
|
className={cn(
|
||||||
'rounded-full px-2.5 py-1 text-xs transition-colors',
|
'rounded-full px-2.5 py-1 text-xs transition-colors',
|
||||||
selectedTag === tag.tag
|
selectedTag === tag.tag
|
||||||
? 'bg-white text-black'
|
? 'bg-gradient-brand text-white shadow-lg shadow-primary/20'
|
||||||
: 'bg-white/10 text-white/70 hover:bg-white/20'
|
: 'bg-accent text-muted-foreground hover:bg-accent'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{tag.tag} ({tag.count})
|
{tag.tag} ({tag.count})
|
||||||
@@ -228,7 +228,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
{hasActiveFilters && (
|
{hasActiveFilters && (
|
||||||
<button
|
<button
|
||||||
onClick={clearFilters}
|
onClick={clearFilters}
|
||||||
className="text-sm text-white/70 hover:text-white hover:underline"
|
className="text-sm text-muted-foreground hover:text-foreground hover:underline"
|
||||||
>
|
>
|
||||||
Clear all filters
|
Clear all filters
|
||||||
</button>
|
</button>
|
||||||
@@ -239,16 +239,16 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
<div className="flex-1 overflow-y-auto p-4">
|
<div className="flex-1 overflow-y-auto p-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-white/40" />
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
<div className="rounded-lg border border-red-400/20 bg-red-400/10 p-4 text-center text-sm text-red-400">
|
<div className="rounded-lg border border-red-400/20 bg-red-400/10 p-4 text-center text-sm text-red-400">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
) : steps.length === 0 ? (
|
) : steps.length === 0 ? (
|
||||||
<div className="rounded-lg border border-white/[0.06] bg-white/5 p-12 text-center">
|
<div className="rounded-lg border border-border bg-accent/50 p-12 text-center">
|
||||||
<p className="mb-2 text-lg font-medium text-white">No steps found</p>
|
<p className="mb-2 text-lg font-medium text-foreground">No steps found</p>
|
||||||
<p className="text-sm text-white/40">
|
<p className="text-sm text-muted-foreground">
|
||||||
{hasActiveFilters ? 'Try adjusting your filters' : 'Create your first step to get started!'}
|
{hasActiveFilters ? 'Try adjusting your filters' : 'Create your first step to get started!'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -261,7 +261,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
onClick={() => toggleSection('private')}
|
onClick={() => toggleSection('private')}
|
||||||
className="mb-3 flex w-full items-center justify-between"
|
className="mb-3 flex w-full items-center justify-between"
|
||||||
>
|
>
|
||||||
<h3 className="text-sm font-semibold text-white">My Steps ({groupedSteps.private.length})</h3>
|
<h3 className="text-sm font-semibold text-foreground">My Steps ({groupedSteps.private.length})</h3>
|
||||||
{collapsedSections.private ? (
|
{collapsedSections.private ? (
|
||||||
<ChevronDown className="h-4 w-4" />
|
<ChevronDown className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
@@ -290,7 +290,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
onClick={() => toggleSection('team')}
|
onClick={() => toggleSection('team')}
|
||||||
className="mb-3 flex w-full items-center justify-between"
|
className="mb-3 flex w-full items-center justify-between"
|
||||||
>
|
>
|
||||||
<h3 className="text-sm font-semibold text-white">Team Steps ({groupedSteps.team.length})</h3>
|
<h3 className="text-sm font-semibold text-foreground">Team Steps ({groupedSteps.team.length})</h3>
|
||||||
{collapsedSections.team ? (
|
{collapsedSections.team ? (
|
||||||
<ChevronDown className="h-4 w-4" />
|
<ChevronDown className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
@@ -319,7 +319,7 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
onClick={() => toggleSection('public')}
|
onClick={() => toggleSection('public')}
|
||||||
className="mb-3 flex w-full items-center justify-between"
|
className="mb-3 flex w-full items-center justify-between"
|
||||||
>
|
>
|
||||||
<h3 className="text-sm font-semibold text-white">Community ({groupedSteps.public.length})</h3>
|
<h3 className="text-sm font-semibold text-foreground">Community ({groupedSteps.public.length})</h3>
|
||||||
{collapsedSections.public ? (
|
{collapsedSections.public ? (
|
||||||
<ChevronDown className="h-4 w-4" />
|
<ChevronDown className="h-4 w-4" />
|
||||||
) : (
|
) : (
|
||||||
@@ -346,10 +346,10 @@ export function StepLibraryBrowser({ onInsert, onCreateNew, showCreateButton = f
|
|||||||
|
|
||||||
{/* Footer - Optional Create Button */}
|
{/* Footer - Optional Create Button */}
|
||||||
{showCreateButton && onCreateNew && (
|
{showCreateButton && onCreateNew && (
|
||||||
<div className="border-t border-white/[0.06] p-4">
|
<div className="border-t border-border p-4">
|
||||||
<button
|
<button
|
||||||
onClick={onCreateNew}
|
onClick={onCreateNew}
|
||||||
className="w-full rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
className="w-full rounded-md bg-gradient-brand px-4 py-2 text-sm font-medium text-white shadow-lg shadow-primary/20 hover:opacity-90"
|
||||||
>
|
>
|
||||||
+ Create New Step
|
+ Create New Step
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user