{{-- User profile — docs/wireframes-v12/view-user.html, content region for content region, minus everything CR-13 §0 removes. v12's order, top to bottom: a profile header card (64px initials circle, the name, a badge row, and the action buttons on the right), a `.grid-2` of two detail cards, an Assigned Permissions card, and a `.grid-2` holding Recent Activity and Performance Summary. ## Deliberate differences from the wireframe * **The "Reset Password" button is gone** (decision D2; line 92). The client's edit-password rule makes the edit form's optional password fields the one mechanism, and a second writer of the same column is exactly what that decision avoids. * **Delete is NOT on this page**, because v12 does not draw it here. It lives on the listing row, where the wireframe's own action group is. * **The Account Details card loses two rows** — Username (client, §0.3; line 143) and Two-Factor Auth (client, §0.5; line 167) — and **Failed Login Attempts** goes with them (decision D4; line 171), because nothing counts them. Five rows are left and every one of them is real. * **The Assigned Permissions grid is this application's own permissions** (decision D5): all thirty-two entries of RolePermissionSeeder::PERMISSIONS, in the seeder's order, each ticked or crossed by asking `$user->can()`. v12's sixteen checkboxes name features NREMS does not have ("Manage Visits", "Export Data"), and a grid of labels nothing could ever grant would be furniture. Asking the GATE rather than reading the pivot is what makes a Super Admin show every box ticked while holding no permission at all — which is the truth about what they can do. * **Recent Activity is real spatie rows** the user CAUSED, newest first, capped at ten. v12's six entries are mock-ups, and they are prose over a log that stores an event, a subject and a properties bag — so this renders those three things and fabricates no sentence. * **Performance Summary draws THREE tiles, not four.** "Revenue Generated" is removed (financials are excluded by a standing client rule) and the fourth cell is left empty rather than filled with a metric invented to occupy it. The "This month" / "Above avg" trend lines go too: they name periods nothing computes, and these values are all-time. Every blank is an em-dash, which is this application's blank. --}} @extends('layouts.app') @section('title', __('users.show_title', ['name' => $user->name])) @section('breadcrumbs') @endsection @section('content') @php $dateFormat = (string) config('nrems.date_format'); $actor = auth()->user(); $actor = $actor instanceof \App\Models\User ? $actor : null; // The same two predicates the listing's row actions use — see // users/partials/actions.blade.php. Hiding is the visible half of // guard §4.6; UserController answers 403 on the same conditions. $canEdit = $user->isManageableBy($actor); $canToggleStatus = $user->isDestructibleBy($actor); $activating = $user->status === \App\Enums\UserStatus::Inactive; @endphp {{-- The profile header. v12 puts this inside a `.card-body` rather than using the `.card-header` idiom, because the row is a portrait and not a heading. --}}

{{ $user->name }}

@include('users.partials.status', ['model' => $user]) @include('users.partials.role', ['model' => $user]) {{ $user->code ?? '—' }}
@can('users.edit') @if ($canEdit) {{ __('users.actions.edit_user') }} @endif @endcan @can('users.edit') @if ($canToggleStatus) {{-- A real POST form and never a link: a GET that changes state would be triggered by any crawler or prefetch. The confirmation goes through Js::from(), which emits the name as a JSON string literal — `{{ }}` escapes for HTML and not for Javascript. Both directions are confirmed (decision D1). --}} @endif @endcan
{{ __('users.show.full_name') }} {{ $user->name }}
{{ __('users.fields.email') }} {{ $user->email }}
{{ __('users.fields.phone') }} {{ $user->phone ?? '—' }}
{{ __('users.fields.gender') }} {{ $user->gender?->label() ?? '—' }}
{{ __('users.fields.date_of_joining') }} {{ $user->date_of_joining?->format($dateFormat) ?? '—' }}
{{ __('users.fields.department') }} {{ $user->department?->label() ?? '—' }}
{{ __('users.fields.role') }} {{ $roleName ?? '—' }}
{{ __('users.activity.created_at') }} {{ $user->created_at?->format($dateFormat) ?? '—' }}
{{ __('users.activity.last_login') }} {{ $user->last_login_at?->format($dateFormat.', g:i A') ?? '—' }}
{{ __('users.activity.total_logins') }} {{ $totalLogins }}
{{ __('users.activity.password_changed_at') }} {{ $user->password_changed_at?->format($dateFormat) ?? '—' }}
@foreach ($permissions as $permission) {{-- The lang key is built from the permission NAME, which is why lang/en/users.php nests its labels by resource: __() splits its key on dots, so 'users.permissions.projects.view' resolves through ['permissions']['projects']['view']. A permission added to the seeder's constant therefore appears here with nothing to change but its label. --}}
{{ __('users.permissions.'.$permission['name']) }}
@endforeach
@if ($activities === [])

{{ __('users.show.no_activity') }}

@else {{-- Composed here rather than in the controller because it is *wording*, and the controller hands over four facts per row: the formatted time, the log's event name, the description spatie wrote, and the subject's code where the subject has one. No sentence is invented. An event this application names — the three HasAudit writes plus the module events CR-9, CR-12 and CR-13 log by hand — reads as its lang string; anything else degrades to the raw word, and to the raw DESCRIPTION when even that is empty, because a timeline entry with no sentence is a dot with nothing beside it. Both are escaped like everything else here. --}}
@foreach ($activities as $entry) @php $word = $entry['event'] !== '' ? $entry['event'] : $entry['description']; $sentence = ucfirst(str_replace('_', ' ', $word)); @endphp
{{ $entry['time'] }}
{{ $sentence }}@if ($entry['subject'] !== null) {{ $entry['subject'] }}@endif
@endforeach
@endif
{{-- v12's inner `.grid-2` at a 20px gap, with the wireframe's own bordered stat tiles. THREE of them: Revenue Generated is removed and its cell is simply not drawn, so the two rows are 2 + 1 rather than 2 + 2. --}}
{{ $performance['inquiries'] }}
{{ __('users.show.inquiries_handled') }}
{{ $performance['bookings'] }}
{{ __('users.show.bookings_closed') }}
{{-- An em-dash and not "0%" when they have handled no inquiries: a rate over zero is not zero, it is nothing. --}}
{{ $performance['conversion'] === null ? '—' : $performance['conversion'].'%' }}
{{ __('users.show.conversion_rate') }}
@endsection