Files
Froidefond Julien 6947af10fe perf(api,indexer): optimiser pages, thumbnails, watcher et robustesse fd
- Pages: mode Original (zero-transcoding), ETag/304, cache index CBZ,
  préfetch next 2 pages, filtre Triangle par défaut
- Thumbnails: DCT scaling JPEG via jpeg-decoder (decode 7x plus rapide),
  img.thumbnail() pour resize, support format Original, fix JPEG RGBA8
- API fallback thumbnail: OutputFormat::Original + DCT scaling au lieu
  de WebP full-decode, retour (bytes, content_type) dynamique
- Watcher: remplacement notify par poll léger sans inotify/fd,
  skip poll quand job actif, snapshots en mémoire
- Jobs: mutex exclusif corrigé (tous statuts actifs, tous types exclusifs)
- Robustesse: suppression fs::canonicalize (problèmes fd Docker),
  list_folders avec erreurs explicites, has_children default true
- Backoffice: FormRow items-start pour alignement inputs avec helper text,
  labels settings clarifiés

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 23:07:42 +01:00

147 lines
3.9 KiB
TypeScript

import { ReactNode, LabelHTMLAttributes, InputHTMLAttributes, SelectHTMLAttributes } from "react";
// Form Field Container
interface FormFieldProps {
children: ReactNode;
className?: string;
}
export function FormField({ children, className = "" }: FormFieldProps) {
return <div className={`flex flex-col space-y-1.5 ${className}`}>{children}</div>;
}
// Form Label
interface FormLabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
children: ReactNode;
required?: boolean;
}
export function FormLabel({ children, required, className = "", ...props }: FormLabelProps) {
return (
<label
className={`text-sm font-medium text-foreground leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 ${className}`}
{...props}
>
{children}
{required && <span className="text-destructive ml-1">*</span>}
</label>
);
}
// Form Input
interface FormInputProps extends InputHTMLAttributes<HTMLInputElement> {
error?: string;
}
export function FormInput({ className = "", error, ...props }: FormInputProps) {
return (
<input
className={`
flex h-10 w-full
rounded-md border border-input
bg-background px-3 py-2
text-sm
shadow-sm
transition-colors duration-200
file:border-0 file:bg-transparent file:text-sm file:font-medium
placeholder:text-muted-foreground/90
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
disabled:cursor-not-allowed disabled:opacity-50
${error ? "border-destructive focus-visible:ring-destructive" : ""}
${className}
`}
{...props}
/>
);
}
// Form Select
interface FormSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
children: ReactNode;
error?: string;
}
export function FormSelect({ children, className = "", error, ...props }: FormSelectProps) {
return (
<select
className={`
flex h-10 w-full
rounded-md border border-input
bg-background px-3 py-2
text-sm
shadow-sm
transition-colors duration-200
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
disabled:cursor-not-allowed disabled:opacity-50
${error ? "border-destructive focus-visible:ring-destructive" : ""}
${className}
`}
{...props}
>
{children}
</select>
);
}
// Form Row (horizontal layout)
interface FormRowProps {
children: ReactNode;
className?: string;
}
export function FormRow({ children, className = "" }: FormRowProps) {
return <div className={`flex flex-wrap items-start gap-4 ${className}`}>{children}</div>;
}
// Form Section
interface FormSectionProps {
title?: string;
description?: string;
children: ReactNode;
className?: string;
}
export function FormSection({ title, description, children, className = "" }: FormSectionProps) {
return (
<div className={`space-y-4 ${className}`}>
{(title || description) && (
<div className="space-y-1">
{title && <h3 className="text-lg font-medium text-foreground">{title}</h3>}
{description && <p className="text-sm text-muted-foreground">{description}</p>}
</div>
)}
<div className="space-y-4">
{children}
</div>
</div>
);
}
// Form Error Message
interface FormErrorProps {
children: ReactNode;
className?: string;
}
export function FormError({ children, className = "" }: FormErrorProps) {
return (
<p className={`text-xs text-destructive ${className}`}>
{children}
</p>
);
}
// Form Description
interface FormDescriptionProps {
children: ReactNode;
className?: string;
}
export function FormDescription({ children, className = "" }: FormDescriptionProps) {
return (
<p className={`text-xs text-muted-foreground ${className}`}>
{children}
</p>
);
}