{{-- Follow Up — docs/wireframes-v12/follow-up.html. v12's layout, top to bottom: 1. the inquiry info banner: code, customer (name · whatsapp), project interest (project · property type), and the current status pill on the right; 2. ONE card holding two `.form-section`s — the eleven Follow Up Details options, then the Follow Up History timeline; 3. the card footer: Cancel, the inert Proceed to Booking, Save Follow Up. Its page-local CSS is in partials/styles.blade.php, which records rule by rule what was copied and where the copy is deliberate rather than literal. ## The eleven options Ten are App\Enums\FollowUpType, in FollowUpType::followUpPageCases()' order — which is the wireframe's, and is NOT conclusionCases() plus one: the page puts Not Interested BEFORE Follow Up Call. The eleventh is **Tentative Booking**, which is not a follow-up type at all. It writes the inquiry's own `tentative_unit_id` / `hold_until` and creates no `follow_ups` row (CR-6 §5.3, §5.4), so it posts three keys of its own — `tentative_selected`, `tentative_unit_id`, `hold_until` — and is drawn between Site Visit and Auto Cad Plan where the wireframe has it. The enum names that position (FollowUpType::TENTATIVE_AFTER) so the ordering decision lives with the order. ## What is server-rendered, and why Every option's open/closed state, and every value in it. No `change` event fires on a freshly drawn page, so the module's JS never runs there — the lesson CR-5 §15.4 recorded about the unit-import replace warning, and the same fix: a panel holding a validation error is rendered open, and old input is rendered back into every control, without the script being involved. ## This is not a twin There is one Follow Up screen, not a create/edit pair, so the twin rule does not apply and there is nothing for TwinViewParityTest to compare. The section is a `@foreach` over the enum rather than eleven hand-written blocks, which is also what keeps it and the add form's Conclusion section from drifting: both iterate the same enum and read the same requiredFields(). ## Escaping Every value here is user-supplied text and every one goes out through `{{ }}`. There is no `{!! !!}` in this file. The one thing that reaches a script is the module config, and it goes as a JSON `data-` attribute. --}} @extends('layouts.app') @section('title', __('inquiries.follow_up_title')) @section('breadcrumbs') @endsection @section('content') @php /** @var array $conclusionOld */ $conclusionOld = is_array(old('conclusion')) ? old('conclusion') : []; // The closed list is FollowUpRecorder::isSelected()'s, so the screen and // the server agree about what a ticked box looks like. $isTicked = static fn (\App\Enums\FollowUpType $type): bool => in_array( data_get($conclusionOld, $type->value.'.selected'), [true, 1, '1', 'on', 'true'], true, ); $hasError = static fn (\App\Enums\FollowUpType $type): bool => $errors->hasAny(array_map( static fn (string $field): string => 'conclusion.'.$type->value.'.'.$field, $type->fields(), )); $tentativeTicked = in_array(old('tentative_selected'), [true, 1, '1', 'on', 'true'], true); $tentativeOpen = $tentativeTicked || $errors->hasAny(['tentative_unit_id', 'hold_until']); @endphp
{{ __('inquiries.banner.inquiry') }}
{{ $inquiry->code }}
{{ __('inquiries.banner.customer') }}
{{ $inquiry->full_name }} · {{ $inquiry->whatsapp_number }}
{{ __('inquiries.banner.project_interest') }}
{{-- v12 shows "Navratna Gulmohar Park · 3 BHK". The second half is a configuration the inquiry does not record; the property type is what it does record about what the customer wants, so that is the second half here. Both halves fall back to a dash rather than leaving a dangling separator. --}}
{{ $inquiry->project?->name ?? '—' }} · {{ $inquiry->property_type?->label() ?? '—' }}
{{-- ===== Follow Up Details ===== --}}
{{ __('inquiries.sections.follow_up_details') }}
{{-- "Choose at least one" is a whole-section rule, so its message renders here rather than against any one control. --}} @error('conclusion')
{{ $message }}
@enderror
@foreach (\App\Enums\FollowUpType::followUpPageCases() as $type) @php $ticked = $isTicked($type); $open = $ticked || $hasError($type); @endphp {{-- The two rows v12 gives a border colour of its own (follow-up.html:297 and 309), as classes rather than as the wireframe's inline styles — see the styles partial on why both are declared after `.is-checked`. --}} {{-- The eleventh option, which is not a type: the wireframe puts Tentative Booking directly after Site Visit. --}} @if ($type === \App\Enums\FollowUpType::TENTATIVE_AFTER) @endif @endforeach
{{-- ===== Follow Up History ===== --}}
{{ __('inquiries.sections.follow_up_history') }}
@include('inquiries.partials.complete-task-alert') @include('inquiries.partials.history', ['entries' => $inquiry->followUps])
{{-- The whole payload is excluded from the client rule set: every rule on this screen is conditional on a checkbox in the same row, and proengsoft has no vocabulary for that — it would mark an untouched Personal Meeting panel invalid. The server validates all of it. See StoreFollowUpRequest::clientRuleExclusions(). --}} {{-- The shared conclusion dialog and the document tasks' hoisted forms (CR-10 §6.3). It has to be OUT HERE, after : the timeline above sits inside `#nrems_follow_up_form`, and a form nested in a form is dropped by the parser — which is the whole reason partials/task-action.blade.php pushes its forms onto a stack instead of writing them beside their buttons. This dialog's own form has the same problem and the same answer: it is rendered here, where nothing contains it. The jsvalidation component above binds to `#nrems_follow_up_form` by selector, so it never reaches `#nrems_complete_task_form` and the two forms' rules cannot cross. --}} @include('inquiries.partials.complete-task-modal') @endsection @push('styles') @include('inquiries.partials.styles') @endpush @push('scripts') {{-- The same module the two form twins push. It does nothing at all on a page without one of its two roots, so one file covers both screens without either carrying the other's code. --}} @endpush