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:
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user