9.6 KiB
Task: Add Input Validation for User Data Form
Objective
Add length/format/range validation for the fields listed below, enforced at three layers: frontend (UX), backend (source of truth), and database (last line of defense). Do not change unrelated form behavior, styling, or fields not listed here.
Scope
Only touch the files responsible for these fields: the form component(s), the API endpoint(s) that receive this data, the validation schema/middleware, and the DB migration/schema for these columns. If a field doesn't exist yet in the DB/API, flag it and ask before creating new schema — don't assume the shape.
Field Rules
| Field | Type | Rule |
|---|---|---|
| firstName | string | required, trim, 1–50 chars, Unicode letters/spaces/hyphens/apostrophes allowed |
| lastName | string | required, trim, 1–50 chars, same charset as firstName |
| string | required, trim, max 254 chars, valid email format (use an existing validation library, don't hand-roll regex) | |
| nationality | enum | required, must be a valid ISO 3166-1 country code (dropdown-backed, not free text) |
| phoneNumber | string | required, normalize to E.164 on save (use existing phone validation lib if the project has one; otherwise ask before adding a dependency), max 20 raw input chars |
| driverLicense | string | optional unless told otherwise, trim, 5–30 chars, alphanumeric only |
| date (generic) | date | must be a valid ISO 8601 date (YYYY-MM-DD); reject free text |
| address | string | required, trim, 1–255 chars |
| idPassport | string | required, trim, 5–30 chars, alphanumeric |
| country | enum | required, valid ISO 3166-1 country code (dropdown-backed) |
| issuedDate | date | required, ISO 8601, must be ≤ today |
| expirationDate | date | required, ISO 8601, must be ≥ issuedDate |
| dateTime | datetime | required, store as ISO 8601 with UTC offset — do not store naive local time |
| vinNumber | string | required, exactly 17 chars, alphanumeric excluding I/O/Q, uppercase |
| city | string | required, trim, 1–85 chars |
| amount | number | required, numeric, > 0, max 2 decimal places, reasonable upper bound (ask what "reasonable" means for this use case if not obvious from context — don't guess a business rule) |
Cross-field rules
expirationDatemust be afterissuedDate.issuedDatemust not be in the future.- Do not implement any cross-field rule not listed here without asking first.
Implementation steps
- Locate existing validation approach. Check if the project already uses a validation library (Zod, Yup, Joi, class-validator, pydantic, etc.) or a pattern for form validation. Use whatever already exists — do not introduce a new validation library if one is already in use.
- Frontend: add
maxlength/type-appropriate input constraints and inline error messages matching the rules above. Reuse existing form error-display patterns in the codebase. - Backend: add/extend the validation schema or middleware for the relevant endpoint(s) to enforce the same rules server-side. This is the authoritative check — assume the frontend check can be bypassed.
- Database: only if column definitions currently allow unbounded/incorrect lengths, add matching length constraints via migration. Do not touch unrelated columns or tables.
- Normalize country/nationality to ISO codes, phone to E.164, and dates to ISO 8601 at the point of validation/save — not scattered across multiple layers.
Explicitly out of scope (ask before doing any of this)
- Adding a new third-party validation/phone/country-list library if one doesn't already exist in the project.
- Changing the DB schema shape (splitting address into multiple columns, etc.) — flag it as a suggestion instead.
- Any field not in the list above.
- Deep format validation for driverLicense/idPassport beyond length + charset (these vary too much by issuing country to hardcode safely) — ask if stricter per-country validation is actually wanted.
Validation before reporting done
- Confirm each rule above has a corresponding check in frontend AND backend (list them field by field in your report).
- Confirm existing tests still pass; add new tests only for the fields touched here.
- Report any field where the existing DB column type/length conflicts with the rule above, rather than silently changing it.
Task: Add Language-Aware Input Handling (EN / FR / AR)
Objective
Each record has ONE selected language (EN, FR, or AR). Free-text fields are typed and stored in that language's script. Add a language field to the record, validate free-text fields against the script rules for the selected language, and handle RTL display for Arabic. This extends input-validation-plan.md — do not redo work already covered there; only add what's described here.
Scope
Touch only: the form component(s) already handling these fields, the language selector (add one if it doesn't exist), the validation schema from the previous task, the API endpoint(s), and the DB column for storing the selected language. Do not touch fields/logic unrelated to language handling.
1. Data model change
- Add a
languagecolumn/field to the record: enum, one ofen,fr,ar. Required, no default — must be explicitly selected (unless the project already has a default locale convention; ask if unsure). - This is a single value per record, not per field. All free-text fields in that record are assumed to be in this language/script.
2. Which fields are affected
Only free-text, human-language fields are language/script-dependent:
- firstName, lastName, address, city
These are not language-dependent — keep exactly as specified in input-validation-plan.md, do not add script validation to them:
- email, phoneNumber, driverLicense, idPassport, vinNumber, amount, all dates, nationality, country
nationality and country stay as ISO codes regardless of selected language — only their displayed label is translated (a UI/i18n concern, not a data validation concern). Do not store translated country names as the value.
3. Script validation per language
Update the charset rule for firstName, lastName, address, city based on the record's language value:
| Language | Allowed script | Notes |
|---|---|---|
| en | Latin (a-z, A-Z), spaces, hyphens, apostrophes | Same as current rule in input-validation-plan.md |
| fr | Latin + French diacritics (à, â, ç, é, è, ê, ë, î, ï, ô, œ, ù, û, ü, ÿ, etc.), spaces, hyphens, apostrophes | Do not reject accented characters |
| ar | Arabic script (Unicode range \u0600-\u06FF and \u0750-\u077F for extended Arabic), spaces, standard Arabic punctuation |
Do not force Latin transliteration |
Implement this as a per-language regex/charset map, not a single hardcoded regex — so a future 4th language is a config addition, not a rewrite. Length limits (50 for names, 255 for address, 85 for city) stay the same as input-validation-plan.md regardless of language — do not add or remove digits from those limits.
Do not attempt to auto-detect the language from the text itself — the language field is user-selected and is the source of truth for which validation rule applies.
4. Frontend
- Add a language selector if one doesn't already exist (dropdown or toggle: EN / FR / عربي).
- When
aris selected, applydir="rtl"to the affected text inputs (firstName, lastName, address, city) and their containing form section. Do not flip the entire page layout unless explicitly asked — scope this to the form only. - Switch the input validation pattern (
patternattribute / live validation) to match the table above based on the current language selection. - If the project already has an i18n/translation library in use (e.g. i18next, react-intl), use it for labels and error messages. Do not introduce a new i18n library if one already exists.
5. Backend
- Validate
languageis one ofen/fr/ar. - Apply the matching charset rule from the table above to firstName, lastName, address, city based on the submitted
languagevalue — reject if the text doesn't match the selected language's script (don't silently strip characters). - Store the text as submitted (UTF-8), no transliteration or normalization beyond standard Unicode NFC normalization (consistent with the rest of the validation plan).
6. Database
- Ensure the column encoding/collation supports UTF-8 fully (
utf8mb4if MySQL; Postgres is UTF-8 by default) — Arabic and accented French characters will break under olderutf8/latin1collations. Only change this if it's actually currently a problem; check first, don't assume. - Add the
languagecolumn via migration if it doesn't exist. Do not alter unrelated columns in the same migration.
Explicitly out of scope (ask before doing)
- Translating the actual stored data between languages (e.g. auto-translating an English name to Arabic) — this task is about validating/storing what the user typed, not translation.
- Full-page RTL layout changes beyond the form fields listed.
- Adding a new i18n library if the project doesn't already have one — flag and ask which one to use.
- Any language beyond en/fr/ar.
- Changing how nationality/country are stored (they remain ISO codes).
Validation before reporting done
- Confirm each of firstName/lastName/address/city is validated against the correct script for the selected
languagevalue, in both frontend and backend. - Confirm
languageis required and validated as an enum. - Confirm DB encoding supports Arabic/French characters (test by saving a record with Arabic text and reading it back unchanged).
- Confirm unrelated fields (email, phone, VIN, dates, amount, nationality, country) are untouched by this change.