{{-- Add New User — docs/wireframes-v12/add-user.html (the card at lines 76-291), minus everything CR-13 §0 removes. ## Shape ONE `.card`. Inside it, a `.form-section` per heading — each with its `.form-section-title` and the wireframe's own icon — and a `.card-footer` at the bottom. Fields are laid out by v12's `.form-row.form-row-2/-3` grids, not by Bootstrap columns, which is why no field carries a `group-class`: the row owns the columns and `.form-group` owns its own margin. ## The twin rule (recipe §9) This file and users/edit.blade.php are twins. Every field is written out in both, and neither includes a shared form partial. **A field added, removed or renamed here must be mirrored in edit.blade.php in the same commit.** ### These twins are NOT in TwinViewParityTest, and that is deliberate Every other twin pair in this application is in that test's dataset. This one cannot be, and the reason is the client's own instruction rather than an omission: CR-13 §0.6 makes Password and Confirm Password **required on add and optional on edit**, so the two screens' required-attribute sets differ by design — which is precisely the thing `it('marks the same field names required on the create and edit twins')` exists to fail on. Adding the pair to the dataset would mean either weakening that assertion for every other module or asserting something untrue about this one. CR-13 §0.1 also forbids new test files and lists the pinned tests that may be touched; TwinViewParityTest is on that list "if it enumerates" and it does, but what it would enumerate here is a pair it cannot compare. So the guardrail this pair actually gets is the docblock rule above, and whoever edits one of them owes the other. ### What this screen has that its twin does not * the action (`users.store`) and the method (POST, not PUT); * the submit label — "Save User", where the twin says "Update User"; * the rule source — StoreUserRequest, where the twin names UpdateUserRequest (whose email rule ignores the row being edited and whose password rules are nullable); * the header wording and the breadcrumb's last crumb; * **required Password and Confirm Password**, with the wireframe's own placeholders. The twin's are optional and say so in the placeholder; * **a longer set of `data-validate-*` markers** on those two fields, for the same reason: this screen re-arms `required` on Password and `required` + `equalTo` on Confirm Password, where the twin re-arms `equalTo` alone (CR-13.1 B1). The Email and Role markers are identical on both; * no Account Activity section — a user who does not exist yet has no account history; * no Deactivate/Activate button in the footer — same reason. ## Deliberate differences from the wireframe * **The Username field is gone** (client instruction, CR-13 §0.3; add-user.html:119-123). Login is by email and always has been — config/fortify.php:48 — and there is no username column to fill. Its slot in the Account Details row is taken by Password, and an empty `.form-group` keeps the row at three tracks so the two password fields stay a third wide rather than stretching to halves. * **The Permissions section is gone** (client, §0.4; lines 163-254). Its sixteen checkboxes were per-user direct permissions; access is by role here, and the profile screen still SHOWS what a role grants. * **The Additional Settings section is gone** (client, §0.5; lines 256-284). Max Login Attempts, Session Timeout, Two-Factor and Notification Preferences are four features this application does not have. * **The Role select lists the REAL spatie roles** (decision D3), narrowed by guard §4.6.2a — "Super Admin" is not offered to anybody who is not one — where v12 hardcodes eight fictional options. * **No Status control.** Unlike the broker form (CR-12 decision D1), a new user is always created `active` and their standing is changed by the status action afterwards, with its own confirmation and its own guards (CR-13 §4.5). A select here would have been a fourth way to reach a decision that has three guards on it. --}} @extends('layouts.app') @section('title', __('users.create_title')) @section('breadcrumbs') @endsection @section('content')
{{ __('users.sections.personal') }}
{{-- Email is one of the three fields jsvalidation cannot be handed: its rule set carries a `unique`, which proengsoft renders as a REMOTE check, so the whole FIELD sits on clientRuleExclusions() — and that dropped its `required` and `email` rules with it. CR-13.1 B1 puts those two back through nrems-client-rules.js, which reads the attributes below. The messages are the SERVER's own, from the lang layer with the same displayable attribute the FormRequest uses, so the two sides say one sentence. --}}
{{-- No asterisk on the wireframe, so the placeholder option is a real answer the user may leave selected. --}}
{{ __('users.sections.account') }}
{{-- v12's three-track row, with the Username slot removed (§0.3) and the two password fields moved up into it. No `:value` on either, and none is possible: Laravel's FormRequest never flashes `password` or `password_confirmation` back into old input, which is why a rejected submit clears them. Saying so here rather than letting a later reader "fix" it by passing a value. `autocomplete="new-password"` on both: without it a browser offers to fill the ADMINISTRATOR's own saved credentials into the account they are creating for somebody else. `password` is another excluded field — it is a rule OBJECT proengsoft cannot express — so its `required` is re-armed from the attributes below (CR-13.1 B1). The POLICY behind it (min 8, mixed case, uncompromised in production) is not re-armed and could not be: it is a network call. The server decides that one, as it decides all of them. `password_confirmation` is not excluded and renders natively, but it also carries an `equalTo` marker pointing at the password input, which is the client-side half of B2's `same:password` — a mismatch is reported HERE, under the field that is wrong, and not under Password. --}}
{{-- The Username slot, kept empty so the row stays three tracks wide and the two password fields keep their thirds. --}}
{{-- The last of the three excluded fields: `Rule::exists` is a remote rule too, so `required` comes back from the attributes below. --}}
{{-- v12's card footer: Cancel as the outline button, then the primary action, both right-aligned inside the card's bottom rule. `.card-footer` is already the flex row, so no wrapper of its own. --}}
{{-- Three keys are excluded from the client rule set: `email` and `role`, whose unique / exists rules proengsoft renders as REMOTE checks against an endpoint this application does not expose, and `password`, which is a rule OBJECT proengsoft cannot express at all — and whose policy (uncompromised, in production) is a network call rather than a client-side question. The server rules are what decide. See StoreUserRequest::clientRuleExclusions(). The list drops FIELDS, so all three lost their `required` too, and the `data-validate-*` attributes above are how CR-13.1 B1 gives it back — read by nrems-client-rules.js, pushed below. --}} @push('scripts') {{-- Re-arms the safe client rules the exclusion list dropped, from a closed list of rule names that can never include `remote`. Pushed AFTER above, which is what makes it work: jsvalidation's generated script registers its `.validate()` call on jQuery's ready queue first, so the callback this file registers runs after it and finds a validator to add to. --}} @endpush @endsection