feat: implement monochrome design system across entire frontend

Migrate all 84 frontend files from the old themed/colored design to a
monochrome glass-morphism design system. Pure black backgrounds, white
text with opacity levels, glass-card components with backdrop-blur, and
functional color reserved for status indicators only.

Foundation: remap CSS variables to monochrome, simplify Tailwind config,
remove theme toggle, convert brand logo/wordmark to white. Pages: all
14 pages updated. Components: all common, library, session, step-library,
tree-editor, tree-preview, admin, and subscription components converted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-09 21:41:29 -05:00
parent 1381aaae99
commit f4ce1595d6
88 changed files with 2976 additions and 1596 deletions

View File

@@ -51,7 +51,7 @@ export function DynamicArrayField<T>({
type="button"
onClick={() => handleMoveUp(index)}
disabled={index === 0}
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground disabled:opacity-30"
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
title="Move up"
>
<ChevronUp className="h-3 w-3" />
@@ -60,7 +60,7 @@ export function DynamicArrayField<T>({
type="button"
onClick={() => handleMoveDown(index)}
disabled={index === items.length - 1}
className="rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground disabled:opacity-30"
className="rounded p-0.5 text-white/50 hover:bg-white/[0.06] hover:text-white disabled:opacity-30"
title="Move down"
>
<ChevronDown className="h-3 w-3" />
@@ -76,7 +76,7 @@ export function DynamicArrayField<T>({
<button
type="button"
onClick={() => onRemove(index)}
className="mt-1 rounded p-1 text-muted-foreground hover:bg-destructive/20 hover:text-destructive"
className="mt-1 rounded p-1 text-white/50 hover:bg-red-400/20 hover:text-red-400"
title="Remove"
>
<Trash2 className="h-4 w-4" />
@@ -91,9 +91,9 @@ export function DynamicArrayField<T>({
type="button"
onClick={onAdd}
className={cn(
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-input',
'px-3 py-2 text-sm text-muted-foreground',
'hover:border-primary hover:text-primary'
'flex w-full items-center justify-center gap-1 rounded-md border border-dashed border-white/10',
'px-3 py-2 text-sm text-white/50',
'hover:border-white/30 hover:text-white'
)}
>
<Plus className="h-4 w-4" />
@@ -103,7 +103,7 @@ export function DynamicArrayField<T>({
{/* Empty state */}
{items.length === 0 && !canAdd && (
<p className="text-center text-sm text-muted-foreground">No items</p>
<p className="text-center text-sm text-white/40">No items</p>
)}
</div>
)

View File

@@ -68,14 +68,14 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
<button
type="button"
onClick={handleCancel}
className="rounded-md border border-input bg-background px-4 py-2 text-sm font-medium text-foreground hover:bg-accent"
className="rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60 hover:bg-white/10 hover:text-white"
>
Cancel
</button>
<button
type="button"
onClick={handleSave}
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
className="rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
>
Done
</button>
@@ -85,8 +85,8 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
return (
<Modal isOpen={true} onClose={onClose} title={getTitle()} size="lg" footer={footerContent}>
{/* Node ID display */}
<div className="mb-4 text-xs text-muted-foreground">
Node ID: <code className="rounded bg-muted px-1 py-0.5">{node.id}</code>
<div className="mb-4 text-xs text-white/40">
Node ID: <code className="rounded bg-white/10 px-1 py-0.5">{node.id}</code>
</div>
{/* Validation errors */}
@@ -97,8 +97,8 @@ export function NodeEditorModal({ node, onClose, isNewNode = false }: NodeEditor
key={i}
className={`rounded-md px-3 py-2 text-sm ${
error.severity === 'error'
? 'bg-destructive/10 text-destructive'
: 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400'
? 'bg-red-400/10 text-red-400'
: 'bg-yellow-400/10 text-yellow-400'
}`}
>
{error.message}

View File

@@ -52,8 +52,8 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
<div className="space-y-4">
{/* Title */}
<div>
<label className="block text-sm font-medium text-foreground">
Title <span className="text-destructive">*</span>
<label className="block text-sm font-medium text-white">
Title <span className="text-red-400">*</span>
</label>
<input
type="text"
@@ -62,37 +62,37 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
placeholder="e.g., Restart the Service"
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
titleError ? 'border-destructive' : 'border-input'
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
titleError ? 'border-red-400' : 'border-white/10'
)}
/>
{titleError && (
<p className="mt-1 text-xs text-destructive">{titleError.message}</p>
<p className="mt-1 text-xs text-red-400">{titleError.message}</p>
)}
</div>
{/* Description */}
<div>
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Description
</label>
{node.description && (
<button
type="button"
onClick={() => setShowPreview(!showPreview)}
className="text-xs text-primary hover:underline"
className="text-xs text-white/50 hover:text-white hover:underline"
>
{showPreview ? 'Edit' : 'Preview'}
</button>
)}
</div>
<p className="mb-1 text-xs text-muted-foreground">
<p className="mb-1 text-xs text-white/40">
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
</p>
{showPreview && node.description ? (
<div className="mt-1 rounded-md border border-input bg-muted/50 p-3 text-sm">
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
<MarkdownContent content={node.description} />
</div>
) : (
@@ -108,7 +108,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
**Note:** Important information here"
rows={5}
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}
@@ -118,10 +118,10 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
{/* Commands */}
<div>
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Commands
</label>
<p className="mb-2 text-xs text-muted-foreground">
<p className="mb-2 text-xs text-white/40">
PowerShell or CLI commands to execute
</p>
<DynamicArrayField
@@ -137,7 +137,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
onChange={(e) => handleUpdateCommand(index, e.target.value)}
placeholder="e.g., Get-Service BrokerAgent"
className={cn(
'block w-full rounded-md border border-input px-3 py-2 font-mono text-sm',
'block w-full rounded-md border border-white/10 px-3 py-2 font-mono text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}
@@ -148,7 +148,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
{/* Expected Outcome */}
<div>
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Expected Outcome
</label>
<input
@@ -157,7 +157,7 @@ export function NodeFormAction({ node, onUpdate }: NodeFormActionProps) {
onChange={(e) => onUpdate({ expected_outcome: e.target.value })}
placeholder="e.g., Service should show as Running"
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}

View File

@@ -64,10 +64,10 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
<Play className="h-5 w-5 text-blue-500" />
</div>
<div>
<h3 className="font-semibold text-blue-600 dark:text-blue-400">
<h3 className="font-semibold text-blue-400">
Starting Question
</h3>
<p className="mt-1 text-sm text-muted-foreground">
<p className="mt-1 text-sm text-white/40">
This is the first question users will see when they start this troubleshooting tree.
Each option below creates a different troubleshooting path.
</p>
@@ -78,11 +78,11 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
{/* Question */}
<div>
<label className="block text-sm font-medium text-foreground">
{isRootNode ? 'Starting Question' : 'Question'} <span className="text-destructive">*</span>
<label className="block text-sm font-medium text-white">
{isRootNode ? 'Starting Question' : 'Question'} <span className="text-red-400">*</span>
</label>
{isRootNode && (
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-xs text-white/40">
What's the main question to diagnose the issue?
</p>
)}
@@ -95,19 +95,19 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
: "e.g., Can you ping the server?"}
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
questionError ? 'border-destructive' : 'border-input'
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
questionError ? 'border-red-400' : 'border-white/10'
)}
/>
{questionError && (
<p className="mt-1 text-xs text-destructive">{questionError.message}</p>
<p className="mt-1 text-xs text-red-400">{questionError.message}</p>
)}
</div>
{/* Help Text */}
<div>
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Help Text
</label>
<textarea
@@ -116,7 +116,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
placeholder="Additional context or instructions for this decision..."
rows={2}
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}
@@ -125,20 +125,20 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
{/* Options */}
<div>
<label className="block text-sm font-medium text-foreground">
{isRootNode ? 'Answer Options (Branches)' : 'Options'} <span className="text-destructive">*</span>
<label className="block text-sm font-medium text-white">
{isRootNode ? 'Answer Options (Branches)' : 'Options'} <span className="text-red-400">*</span>
</label>
{isRootNode ? (
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-xs text-white/40">
Add as many options as needed (A, B, C, D...). Each option leads to a completely different troubleshooting path.
</p>
) : (
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-xs text-white/40">
Each option can branch to a different next step.
</p>
)}
{optionsError && (
<p className="mt-1 text-xs text-destructive">{optionsError.message}</p>
<p className="mt-1 text-xs text-red-400">{optionsError.message}</p>
)}
<div className="mt-2">
<DynamicArrayField
@@ -158,14 +158,14 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
const letter = indexToLetter(index)
return (
<div className="rounded-md border border-input bg-muted/30 p-3">
<div className="rounded-md border border-white/10 bg-white/[0.04] p-3">
<div className="mb-2 flex items-center gap-2">
{/* Letter badge */}
<span className={cn(
'flex h-6 w-6 items-center justify-center rounded-full text-xs font-bold',
isRootNode
? 'bg-blue-500/20 text-blue-600 dark:text-blue-400'
: 'bg-muted text-muted-foreground'
? 'bg-blue-500/20 text-blue-400'
: 'bg-white/10 text-white/50'
)}>
{letter}
</span>
@@ -180,12 +180,12 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
'block flex-1 rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
optionLabelError ? 'border-destructive' : 'border-input'
optionLabelError ? 'border-red-400' : 'border-white/10'
)}
/>
</div>
{optionLabelError && (
<p className="mb-2 text-xs text-destructive">{optionLabelError.message}</p>
<p className="mb-2 text-xs text-red-400">{optionLabelError.message}</p>
)}
<div className="pl-8">
<NodePicker
@@ -207,7 +207,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
{/* Example hint for root node */}
{isRootNode && (node.options?.length || 0) < 2 && (
<div className="mt-3 rounded-md border border-dashed border-muted-foreground/30 bg-muted/20 p-3 text-xs text-muted-foreground">
<div className="mt-3 rounded-md border border-dashed border-white/10 bg-white/[0.02] p-3 text-xs text-white/40">
<strong>Tip:</strong> Most troubleshooting trees start with 2-5 main branches.
For example: "Connection Issues", "Performance Problems", "Error Messages", "Other".
</div>

View File

@@ -47,8 +47,8 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
<div className="space-y-4">
{/* Title */}
<div>
<label className="block text-sm font-medium text-foreground">
Title <span className="text-destructive">*</span>
<label className="block text-sm font-medium text-white">
Title <span className="text-red-400">*</span>
</label>
<input
type="text"
@@ -57,37 +57,37 @@ export function NodeFormResolution({ node, onUpdate }: NodeFormResolutionProps)
placeholder="e.g., VDA Successfully Registered"
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
titleError ? 'border-destructive' : 'border-input'
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
titleError ? 'border-red-400' : 'border-white/10'
)}
/>
{titleError && (
<p className="mt-1 text-xs text-destructive">{titleError.message}</p>
<p className="mt-1 text-xs text-red-400">{titleError.message}</p>
)}
</div>
{/* Description */}
<div>
<div className="flex items-center justify-between">
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Description
</label>
{node.description && (
<button
type="button"
onClick={() => setShowPreview(!showPreview)}
className="text-xs text-primary hover:underline"
className="text-xs text-white/50 hover:text-white hover:underline"
>
{showPreview ? 'Edit' : 'Preview'}
</button>
)}
</div>
<p className="mb-1 text-xs text-muted-foreground">
<p className="mb-1 text-xs text-white/40">
Supports markdown: **bold**, *italic*, - lists, 1. numbered lists, `code`
</p>
{showPreview && node.description ? (
<div className="mt-1 rounded-md border border-input bg-muted/50 p-3 text-sm">
<div className="mt-1 rounded-md border border-white/10 bg-white/[0.04] p-3 text-sm">
<MarkdownContent content={node.description} />
</div>
) : (
@@ -102,7 +102,7 @@ Document what was done and the outcome.
**Close ticket as:** Resolved"
rows={5}
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}
@@ -112,10 +112,10 @@ Document what was done and the outcome.
{/* Resolution Steps */}
<div>
<label className="block text-sm font-medium text-foreground">
<label className="block text-sm font-medium text-white">
Resolution Steps
</label>
<p className="mb-2 text-xs text-muted-foreground">
<p className="mb-2 text-xs text-white/40">
Step-by-step instructions for resolving the issue
</p>
<DynamicArrayField
@@ -126,7 +126,7 @@ Document what was done and the outcome.
addLabel="Add Step"
renderItem={(step, index) => (
<div className="flex items-start gap-2">
<span className="mt-2 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-xs font-medium text-primary">
<span className="mt-2 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-emerald-400/10 text-xs font-medium text-emerald-400">
{index + 1}
</span>
<input
@@ -135,7 +135,7 @@ Document what was done and the outcome.
onChange={(e) => handleUpdateStep(index, e.target.value)}
placeholder={`Step ${index + 1}`}
className={cn(
'block w-full rounded-md border border-input px-3 py-2 text-sm',
'block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
)}
@@ -146,7 +146,7 @@ Document what was done and the outcome.
</div>
{/* Note about terminal node */}
<div className="rounded-md bg-green-500/10 p-3 text-sm text-green-600 dark:text-green-400">
<div className="rounded-md bg-emerald-400/10 p-3 text-sm text-emerald-400">
<strong>Note:</strong> Solution nodes are terminal - they end the troubleshooting flow.
The session will be marked complete when the user reaches this node.
</div>

View File

@@ -139,7 +139,7 @@ export function NodePicker({
return (
<div className={className}>
{label && (
<label className="mb-1 block text-sm font-medium text-foreground">
<label className="mb-1 block text-sm font-medium text-white">
{label}
</label>
)}
@@ -147,8 +147,8 @@ export function NodePicker({
{/* Inline node creation UI */}
{creatingNodeType ? (
<div className="space-y-2">
<div className="flex items-center gap-2 rounded-md border border-primary bg-primary/5 p-2">
<span className="text-xs font-medium text-primary">
<div className="flex items-center gap-2 rounded-md border border-white/20 bg-white/[0.04] p-2">
<span className="text-xs font-medium text-white">
New {NODE_TYPE_LABELS[creatingNodeType]}:
</span>
<input
@@ -159,9 +159,9 @@ export function NodePicker({
onKeyDown={handleKeyDown}
placeholder={creatingNodeType === 'decision' ? 'Enter question...' : 'Enter title...'}
className={cn(
'flex-1 rounded-md border border-input px-2 py-1 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'flex-1 rounded-md border border-white/10 px-2 py-1 text-sm',
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
)}
/>
</div>
@@ -169,7 +169,7 @@ export function NodePicker({
<button
type="button"
onClick={handleCancelCreate}
className="flex-1 rounded-md border border-input px-3 py-1.5 text-xs font-medium hover:bg-accent"
className="flex-1 rounded-md border border-white/10 px-3 py-1.5 text-xs font-medium text-white/60 hover:bg-white/10 hover:text-white"
>
Cancel
</button>
@@ -179,7 +179,7 @@ export function NodePicker({
disabled={!newNodeTitle.trim()}
className={cn(
'flex-1 rounded-md px-3 py-1.5 text-xs font-medium',
'bg-primary text-primary-foreground hover:bg-primary/90',
'bg-white text-black hover:bg-white/90',
'disabled:opacity-50 disabled:cursor-not-allowed'
)}
>
@@ -194,9 +194,9 @@ export function NodePicker({
onChange={(e) => handleChange(e.target.value)}
className={cn(
'block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
error ? 'border-destructive' : 'border-input'
'bg-black/50 text-white',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
error ? 'border-red-400' : 'border-white/10'
)}
>
<option value="">{placeholder}</option>
@@ -242,14 +242,14 @@ export function NodePicker({
{/* Show what's selected */}
{value && selectedNode && (
<p className="mt-1 text-xs text-muted-foreground">
<p className="mt-1 text-xs text-white/40">
{selectedNode.label}
</p>
)}
</>
)}
{error && <p className="mt-1 text-xs text-destructive">{error}</p>}
{error && <p className="mt-1 text-xs text-red-400">{error}</p>}
</div>
)
}

View File

@@ -18,7 +18,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
{/* Left Panel - Form Editor */}
<div
className={cn(
'flex flex-col overflow-y-auto border-border bg-background',
'flex flex-col overflow-y-auto border-white/[0.06]',
isMobile ? 'h-full w-full border-b' : 'w-3/5 border-r'
)}
>
@@ -31,7 +31,7 @@ export function TreeEditorLayout({ isMobile = false }: TreeEditorLayoutProps) {
{/* Right Panel - Preview */}
<div
className={cn(
'flex-1 overflow-hidden bg-muted/30',
'flex-1 overflow-hidden bg-white/[0.02]',
isMobile ? 'hidden' : 'block'
)}
>

View File

@@ -56,13 +56,13 @@ export function TreeMetadataForm() {
)
return (
<div className="space-y-4 rounded-lg border border-border bg-card p-4">
<h2 className="text-sm font-semibold text-card-foreground">Tree Details</h2>
<div className="space-y-4 glass-card rounded-2xl p-4">
<h2 className="text-sm font-semibold text-white">Tree Details</h2>
{/* Name */}
<div>
<label htmlFor="tree-name" className="block text-sm font-medium text-foreground">
Name <span className="text-destructive">*</span>
<label htmlFor="tree-name" className="block text-sm font-medium text-white">
Name <span className="text-red-400">*</span>
</label>
<input
id="tree-name"
@@ -72,17 +72,17 @@ export function TreeMetadataForm() {
placeholder="e.g., VDA Registration Troubleshooting"
className={cn(
'mt-1 block w-full rounded-md border px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary',
nameError ? 'border-destructive' : 'border-input'
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20',
nameError ? 'border-red-400' : 'border-white/10'
)}
/>
{nameError && <p className="mt-1 text-xs text-destructive">{nameError.message}</p>}
{nameError && <p className="mt-1 text-xs text-red-400">{nameError.message}</p>}
</div>
{/* Description */}
<div>
<label htmlFor="tree-description" className="block text-sm font-medium text-foreground">
<label htmlFor="tree-description" className="block text-sm font-medium text-white">
Description
</label>
<textarea
@@ -92,16 +92,16 @@ export function TreeMetadataForm() {
placeholder="Brief description of what this tree troubleshoots..."
rows={2}
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
)}
/>
</div>
{/* Category */}
<div>
<label htmlFor="tree-category" className="block text-sm font-medium text-foreground">
<label htmlFor="tree-category" className="block text-sm font-medium text-white">
Category
</label>
{!customCategory ? (
@@ -110,9 +110,9 @@ export function TreeMetadataForm() {
value={categoryId || ''}
onChange={(e) => handleCategoryChange(e.target.value)}
className={cn(
'mt-1 block w-full rounded-md border border-input px-3 py-2 text-sm',
'bg-background text-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'mt-1 block w-full rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-black/50 text-white',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
)}
>
<option value="">No category</option>
@@ -132,9 +132,9 @@ export function TreeMetadataForm() {
onChange={(e) => setCategory(e.target.value)}
placeholder="Enter new category"
className={cn(
'block flex-1 rounded-md border border-input px-3 py-2 text-sm',
'bg-background text-foreground placeholder:text-muted-foreground',
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
'block flex-1 rounded-md border border-white/10 px-3 py-2 text-sm',
'bg-black/50 text-white placeholder:text-white/40',
'focus:border-white/30 focus:outline-none focus:ring-1 focus:ring-white/20'
)}
autoFocus
/>
@@ -144,7 +144,7 @@ export function TreeMetadataForm() {
setCategory('')
setCategoryId(null)
}}
className="rounded-md border border-input px-3 py-2 text-sm hover:bg-accent"
className="rounded-md border border-white/10 px-3 py-2 text-sm text-white/60 hover:bg-white/10 hover:text-white"
>
Cancel
</button>
@@ -154,7 +154,7 @@ export function TreeMetadataForm() {
{/* Tags */}
<div>
<label className="block text-sm font-medium text-foreground">Tags</label>
<label className="block text-sm font-medium text-white">Tags</label>
<div className="mt-1">
<TagInput tags={tags} onChange={setTags} maxTags={10} placeholder="Add tags..." />
</div>
@@ -162,13 +162,13 @@ export function TreeMetadataForm() {
{/* Visibility */}
<div>
<label className="block text-sm font-medium text-foreground">Visibility</label>
<label className="block text-sm font-medium text-white">Visibility</label>
<div className="mt-2 flex gap-4">
<label
className={cn(
'flex cursor-pointer items-center gap-2 rounded-md border px-4 py-2',
'transition-colors',
!isPublic ? 'border-primary bg-primary/10' : 'border-input hover:bg-accent'
!isPublic ? 'border-white/30 bg-white/10 text-white' : 'border-white/10 text-white/60 hover:bg-white/[0.06]'
)}
>
<input
@@ -185,7 +185,7 @@ export function TreeMetadataForm() {
className={cn(
'flex cursor-pointer items-center gap-2 rounded-md border px-4 py-2',
'transition-colors',
isPublic ? 'border-primary bg-primary/10' : 'border-input hover:bg-accent'
isPublic ? 'border-white/30 bg-white/10 text-white' : 'border-white/10 text-white/60 hover:bg-white/[0.06]'
)}
>
<input
@@ -199,7 +199,7 @@ export function TreeMetadataForm() {
<span className="text-sm">Public</span>
</label>
</div>
<p className="mt-1 text-xs text-muted-foreground">
<p className="mt-1 text-xs text-white/40">
{isPublic
? 'Anyone can view this tree'
: 'Only you and your team can view this tree'}

View File

@@ -27,16 +27,16 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
className={cn(
'rounded-lg border',
errorItems.length > 0
? 'border-destructive/50 bg-destructive/5'
: 'border-yellow-500/50 bg-yellow-50 dark:bg-yellow-900/10'
? 'border-red-400/30 bg-red-400/5'
: 'border-yellow-400/30 bg-yellow-400/5'
)}
>
{/* Header */}
<button
onClick={() => setIsExpanded(!isExpanded)}
className={cn(
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-black/5 dark:hover:bg-white/5',
errorItems.length > 0 ? 'text-destructive' : 'text-yellow-700 dark:text-yellow-500'
'flex w-full items-center justify-between p-3 text-left transition-colors hover:bg-white/5',
errorItems.length > 0 ? 'text-red-400' : 'text-yellow-400'
)}
>
<div className="flex items-center gap-2">
@@ -73,15 +73,15 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
className={cn(
'flex w-full items-start gap-2 rounded p-2 text-left text-sm transition-colors',
error.nodeId
? 'cursor-pointer hover:bg-destructive/10'
? 'cursor-pointer hover:bg-red-400/10'
: 'cursor-default'
)}
>
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-destructive" />
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0 text-red-400" />
<div className="flex-1">
<p className="text-destructive">{error.message}</p>
<p className="text-red-400">{error.message}</p>
{error.nodeId && (
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-xs text-white/40">
Click to select node: {error.nodeId}
</p>
)}
@@ -97,15 +97,15 @@ export function ValidationSummary({ errors, onSelectNode }: ValidationSummaryPro
className={cn(
'flex w-full items-start gap-2 rounded p-2 text-left text-sm transition-colors',
warning.nodeId
? 'cursor-pointer hover:bg-yellow-100 dark:hover:bg-yellow-900/20'
? 'cursor-pointer hover:bg-yellow-400/10'
: 'cursor-default'
)}
>
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-600 dark:text-yellow-500" />
<AlertTriangle className="mt-0.5 h-4 w-4 flex-shrink-0 text-yellow-400" />
<div className="flex-1">
<p className="text-yellow-700 dark:text-yellow-500">{warning.message}</p>
<p className="text-yellow-400">{warning.message}</p>
{warning.nodeId && (
<p className="mt-0.5 text-xs text-muted-foreground">
<p className="mt-0.5 text-xs text-white/40">
Click to select node: {warning.nodeId}
</p>
)}