refactor: Script Library and Builder design critique fixes

Library:
- Clear CTA hierarchy: "Build New Script" primary, "Import Script" ghost,
  "Manage" demoted to text link
- "New from Script" → "Import Script" (clearer label)

Script Builder:
- Add suggestion chips for first-time users (4 common MSP tasks)
- Chips auto-hide after first message

Design system normalization:
- ScriptPreviewModal: bg-black/80 → bg-black/40, text-blue-400 → text-accent-text,
  emerald save button → primary, inline rgba → CSS variables
- ScriptCodeBlock: bg-[rgba(0,0,0,0.3)] → bg-code, text-blue-400 → text-accent-text,
  text-text-muted typo fixed, emerald button → ghost style
- TemplateCard: emerald/amber/rose badges → success-dim/warning-dim/danger-dim,
  ShieldAlert amber → warning token
- ParameterDetectorStepper: blue focus ring → orange, amber → warning token,
  "Candidate" → "Variable" in stepper progress

Jargon clarification:
- "Detect Parameters" → "Find Variables"
- "Detected Parameters" → "Configurable Variables"
- "Parameters (N)" → "Variables (N)"
- Detection summary copy reworded for clarity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-30 01:26:35 +00:00
parent 9ce4a8bc8e
commit 32d48146bf
8 changed files with 112 additions and 101 deletions

View File

@@ -1,17 +1,27 @@
import { useState, useRef, useCallback, useEffect } from 'react'
import { Send } from 'lucide-react'
import { Send, Terminal, UserPlus, HardDrive, RotateCcw } from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import { cn } from '@/lib/utils'
const SUGGESTIONS: { icon: LucideIcon; label: string }[] = [
{ icon: UserPlus, label: 'Create a new AD user' },
{ icon: HardDrive, label: 'Check disk space on all servers' },
{ icon: RotateCcw, label: 'Restart a Windows service' },
{ icon: Terminal, label: 'Reset MFA for a user' },
]
interface ScriptBuilderInputProps {
onSend: (content: string) => void
disabled: boolean
placeholder?: string
showSuggestions?: boolean
}
export function ScriptBuilderInput({
onSend,
disabled,
placeholder = 'Describe the script you need...',
showSuggestions = false,
}: ScriptBuilderInputProps) {
const [value, setValue] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
@@ -44,35 +54,53 @@ export function ScriptBuilderInput({
const canSend = value.trim().length > 0 && !disabled
return (
<div className="flex items-end gap-2 p-3 border-t" style={{ borderColor: 'var(--color-border-default)' }}>
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder}
disabled={disabled}
rows={1}
className={cn(
"flex-1 resize-none rounded-xl px-4 py-2.5 text-sm",
"bg-card border border-border text-foreground placeholder:text-muted-foreground",
"focus:outline-none focus:border-[rgba(96,165,250,0.3)] transition-colors",
"disabled:opacity-50"
)}
style={{ maxHeight: 120 }}
/>
<button
onClick={handleSend}
disabled={!canSend}
className={cn(
"shrink-0 flex items-center justify-center w-10 h-10 rounded-xl transition-all",
canSend
? "bg-primary text-white hover:brightness-110 active:scale-[0.98]"
: "bg-[rgba(255,255,255,0.04)] text-text-muted cursor-not-allowed"
)}
>
<Send size={18} />
</button>
<div className="border-t border-border p-3 space-y-2">
<div className="flex items-end gap-2">
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder}
disabled={disabled}
rows={1}
className={cn(
"flex-1 resize-none rounded-xl px-4 py-2.5 text-sm",
"bg-card border border-border text-foreground placeholder:text-muted-foreground",
"focus:outline-none focus:border-[rgba(249,115,22,0.25)] focus:ring-1 focus:ring-[rgba(249,115,22,0.1)] transition-colors",
"disabled:opacity-50"
)}
style={{ maxHeight: 120 }}
/>
<button
onClick={handleSend}
disabled={!canSend}
className={cn(
"shrink-0 flex items-center justify-center w-10 h-10 rounded-xl transition-all",
canSend
? "bg-primary text-white hover:brightness-110 active:scale-[0.98]"
: "bg-[var(--color-bg-elevated)] text-muted-foreground cursor-not-allowed"
)}
>
<Send size={18} />
</button>
</div>
{showSuggestions && (
<div className="flex flex-wrap gap-2">
{SUGGESTIONS.map(({ icon: Icon, label }) => (
<button
key={label}
type="button"
onClick={() => onSend(label)}
className="group flex items-center gap-1.5 rounded-md border border-border bg-card px-3 py-1.5 text-xs text-muted-foreground transition-all hover:border-[var(--color-border-hover)] hover:bg-[var(--color-bg-elevated)] hover:text-foreground active:scale-[0.97]"
>
<Icon size={11} className="text-muted shrink-0 group-hover:text-[#f97316] transition-colors" />
{label}
</button>
))}
</div>
)}
</div>
)
}