feat: P7 FormField komponenta + migrace Vehicles

- FormField.jsx: wrapper pro label + input + error pattern (aria-invalid podpora)
- Vehicles.jsx: migrovano na FormField (demonstrace patternu)
- Postupna migrace dalsich formularovych stranek v budoucnu

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 19:02:54 +01:00
parent f88ae25057
commit 21f08593e4
2 changed files with 34 additions and 14 deletions

View File

@@ -0,0 +1,23 @@
/**
* Formularovy wrapper pro label + input + error.
* Nahrazuje opakovany pattern admin-form-group + has-error + admin-form-error.
*
* @param {string} label - Text labelu
* @param {string} [error] - Chybova zprava (zobrazi se pod inputem)
* @param {boolean} [required] - Zobrazi cervenu hvezdicku
* @param {string} [className] - Extra CSS trida na wrapperu
* @param {React.ReactNode} children - Input/select/textarea element
*/
export default function FormField({ label, error, required, className, children }) {
const groupClass = `admin-form-group${error ? ' has-error' : ''}${className ? ` ${className}` : ''}`
return (
<div className={groupClass}>
<label className={`admin-form-label${required ? ' required' : ''}`}>
{label}
</label>
{children}
{error && <span className="admin-form-error">{error}</span>}
</div>
)
}

View File

@@ -8,6 +8,7 @@ import useModalLock from '../hooks/useModalLock'
import { formatKm } from '../utils/formatters'
import apiFetch from '../utils/api'
import FormField from '../components/FormField'
const API_BASE = '/api/admin'
export default function Vehicles() {
@@ -338,8 +339,7 @@ export default function Vehicles() {
<div className="admin-modal-body">
<div className="admin-form">
<div className="admin-form-row">
<div className={`admin-form-group${errors.spz ? ' has-error' : ''}`}>
<label className="admin-form-label required">SPZ</label>
<FormField label="SPZ" error={errors.spz} required>
<input
type="text"
value={form.spz}
@@ -349,12 +349,11 @@ export default function Vehicles() {
}}
className="admin-form-input"
placeholder="1AB 2345"
aria-invalid={!!errors.spz}
/>
{errors.spz && <span className="admin-form-error">{errors.spz}</span>}
</div>
</FormField>
<div className={`admin-form-group${errors.name ? ' has-error' : ''}`}>
<label className="admin-form-label required">Název</label>
<FormField label="Název" error={errors.name} required>
<input
type="text"
value={form.name}
@@ -364,14 +363,13 @@ export default function Vehicles() {
}}
className="admin-form-input"
placeholder="Služební #1"
aria-invalid={!!errors.name}
/>
{errors.name && <span className="admin-form-error">{errors.name}</span>}
</div>
</FormField>
</div>
<div className="admin-form-row">
<div className="admin-form-group">
<label className="admin-form-label">Značka</label>
<FormField label="Značka">
<input
type="text"
value={form.brand}
@@ -379,10 +377,9 @@ export default function Vehicles() {
className="admin-form-input"
placeholder="Škoda"
/>
</div>
</FormField>
<div className="admin-form-group">
<label className="admin-form-label">Model</label>
<FormField label="Model">
<input
type="text"
value={form.model}
@@ -390,7 +387,7 @@ export default function Vehicles() {
className="admin-form-input"
placeholder="Octavia Combi"
/>
</div>
</FormField>
</div>
<div className="admin-form-group">