(null);
+ // F23: when the rep picks a non-Enquiry stage without a yacht linked,
+ // we drop into a yacht-picker view in the popover. yachtPrereqTarget
+ // holds the stage they were trying to reach; submitting the picker
+ // PATCHes the yachtId, then chains the original stage move.
+ const [yachtPrereqTarget, setYachtPrereqTarget] = useState(null);
+ const [yachtPrereqId, setYachtPrereqId] = useState(null);
+ const [linkingYacht, setLinkingYacht] = useState(false);
// When a user picks a stage that isn't a legal next step (and has the
// override permission), the popover transitions into a confirm view
// that asks for a reason before committing. Reasons are not exposed
@@ -130,6 +148,15 @@ export function InlineStagePicker({
setOpen(false);
return;
}
+ // F23: leaving Enquiry without a yacht linked is the one hard prereq
+ // the service enforces. Instead of bouncing them to the validation
+ // error toast, drop into an inline yacht-picker so the rep can fix
+ // it in place. Same flow chains the stage move after the link.
+ if (stage === 'enquiry' && next !== 'enquiry' && !currentYachtId) {
+ setYachtPrereqTarget(next);
+ setYachtPrereqId(null);
+ return;
+ }
const isOverride = !canTransitionStage(stage, next);
if (isOverride && canOverride) {
// Switch into the confirm view rather than firing the mutation
@@ -143,6 +170,33 @@ export function InlineStagePicker({
mutation.mutate({ next, reason: null });
}
+ async function commitYachtPrereq() {
+ if (!yachtPrereqTarget || !yachtPrereqId) return;
+ setLinkingYacht(true);
+ try {
+ await apiFetch(`/api/v1/interests/${interestId}`, {
+ method: 'PATCH',
+ body: { yachtId: yachtPrereqId },
+ });
+ queryClient.invalidateQueries({ queryKey: ['interests', interestId] });
+ queryClient.invalidateQueries({ queryKey: ['interests'] });
+ const target = yachtPrereqTarget;
+ setYachtPrereqTarget(null);
+ setYachtPrereqId(null);
+ setPendingStage(target);
+ mutation.mutate({ next: target, reason: null });
+ } catch (err) {
+ toastError(err);
+ } finally {
+ setLinkingYacht(false);
+ }
+ }
+
+ function cancelYachtPrereq() {
+ setYachtPrereqTarget(null);
+ setYachtPrereqId(null);
+ }
+
async function unlinkAllAndOpen(target: PipelineStage) {
setUnlinking(true);
try {
@@ -196,9 +250,12 @@ export function InlineStagePicker({
{
- if (mutation.isPending) return;
+ if (mutation.isPending || linkingYacht) return;
setOpen(o);
- if (!o) cancelOverride();
+ if (!o) {
+ cancelOverride();
+ cancelYachtPrereq();
+ }
}}
>
@@ -228,7 +285,57 @@ export function InlineStagePicker({
className="w-72 p-0"
onClick={(e) => stopPropagation && e.stopPropagation()}
>
- {overrideTarget ? (
+ {yachtPrereqTarget ? (
+ // F23: inline yacht-prereq view — only reached when the rep
+ // picked a non-Enquiry stage without a yacht linked. Surfaces
+ // a yacht-picker right inside the popover so they can fix
+ // the prereq and move the stage in one flow.
+
+
+
+
+
Yacht required
+
+ A yacht must be linked before leaving Enquiry. Pick one below to move to{' '}
+ {STAGE_LABELS[yachtPrereqTarget]}.
+