fix: wire error prop to Input/Textarea components and eliminate duplicate loadTemplates on mount

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-13 02:47:23 -04:00
parent f988d70bca
commit 64b02159e0
2 changed files with 40 additions and 20 deletions

View File

@@ -15,9 +15,15 @@ export function ScriptFilterBar({ inputValue, setInputValue }: Props) {
const setCategory = useScriptGeneratorStore(s => s.setCategory) const setCategory = useScriptGeneratorStore(s => s.setCategory)
const setSearch = useScriptGeneratorStore(s => s.setSearch) const setSearch = useScriptGeneratorStore(s => s.setSearch)
// Debounce: 300ms after the input value settles, push to store // Debounce: 300ms after the input value settles, push to store.
// Skip on initial mount (store.searchQuery is already '' and page already called loadTemplates).
const isFirstRender = useRef(true)
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null) const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
useEffect(() => { useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false
return
}
if (debounceRef.current) clearTimeout(debounceRef.current) if (debounceRef.current) clearTimeout(debounceRef.current)
debounceRef.current = setTimeout(() => { debounceRef.current = setTimeout(() => {
setSearch(inputValue) setSearch(inputValue)

View File

@@ -28,7 +28,12 @@ export function ScriptParameterField({ param, value, error, disabled }: Props) {
let input: React.ReactNode let input: React.ReactNode
// Track whether the shared Input/Textarea component renders the error internally
// (so we skip the manual <p> at the bottom for these types)
let errorRenderedByComponent = false
if (param.type === 'text' || param.type === 'multi_text' || param.type === 'number') { if (param.type === 'text' || param.type === 'multi_text' || param.type === 'number') {
errorRenderedByComponent = true
input = ( input = (
<Input <Input
id={id} id={id}
@@ -41,31 +46,37 @@ export function ScriptParameterField({ param, value, error, disabled }: Props) {
: (param.placeholder ?? undefined) : (param.placeholder ?? undefined)
} }
disabled={disabled} disabled={disabled}
error={error}
/> />
) )
} else if (param.type === 'password') { } else if (param.type === 'password') {
errorRenderedByComponent = true
input = ( input = (
<div className="relative"> <div className="flex flex-col gap-1">
<Input <div className="relative">
id={id} <Input
type={showPassword ? 'text' : 'password'} id={id}
value={value} type={showPassword ? 'text' : 'password'}
onChange={handleChange} value={value}
placeholder={param.placeholder ?? undefined} onChange={handleChange}
disabled={disabled} placeholder={param.placeholder ?? undefined}
/> disabled={disabled}
<button error={error}
type="button" />
onClick={() => setShowPassword(v => !v)} <button
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" type="button"
tabIndex={-1} onClick={() => setShowPassword(v => !v)}
aria-label={showPassword ? 'Hide password' : 'Show password'} className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
> tabIndex={-1}
{showPassword ? <EyeOff size={14} /> : <Eye size={14} />} aria-label={showPassword ? 'Hide password' : 'Show password'}
</button> >
{showPassword ? <EyeOff size={14} /> : <Eye size={14} />}
</button>
</div>
</div> </div>
) )
} else if (param.type === 'textarea') { } else if (param.type === 'textarea') {
errorRenderedByComponent = true
input = ( input = (
<Textarea <Textarea
id={id} id={id}
@@ -74,6 +85,7 @@ export function ScriptParameterField({ param, value, error, disabled }: Props) {
placeholder={param.placeholder ?? undefined} placeholder={param.placeholder ?? undefined}
disabled={disabled} disabled={disabled}
rows={4} rows={4}
error={error}
/> />
) )
} else if (param.type === 'select') { } else if (param.type === 'select') {
@@ -111,12 +123,14 @@ export function ScriptParameterField({ param, value, error, disabled }: Props) {
) )
} else { } else {
// Fallback for unknown types // Fallback for unknown types
errorRenderedByComponent = true
input = ( input = (
<Input <Input
id={id} id={id}
value={value} value={value}
onChange={handleChange} onChange={handleChange}
disabled={disabled} disabled={disabled}
error={error}
/> />
) )
} }
@@ -136,7 +150,7 @@ export function ScriptParameterField({ param, value, error, disabled }: Props) {
{param.help_text && ( {param.help_text && (
<p className="text-xs text-muted-foreground mt-1">{param.help_text}</p> <p className="text-xs text-muted-foreground mt-1">{param.help_text}</p>
)} )}
{error && ( {!errorRenderedByComponent && error && (
<p className="mt-1.5 text-xs text-red-400">{error}</p> <p className="mt-1.5 text-xs text-red-400">{error}</p>
)} )}
</div> </div>