{{--
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
{{-- 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. --}}
{{-- "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`. --}}
@break
@case(\App\Enums\FollowUpType::NotInterested)
@break
@default
{{-- The six date-only types (1, 2, 5, 6, 7, 8). Their
date is optional — see FollowUpType::requiredFields(). --}}
@endswitch
{{-- The eleventh option, which is not a type: the wireframe
puts Tentative Booking directly after Site Visit. --}}
@if ($type === \App\Enums\FollowUpType::TENTATIVE_AFTER)
{{-- Server-rendered with the project's units,
this inquiry's own hold ignored (CR-6 §3),
and prefilled with the hold it already has
so "extend by a week" is one field. --}}
{{-- 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