From 2881721b18624f18a9d795310a5741f81ab08ec3 Mon Sep 17 00:00:00 2001 From: Guillermo Pages Date: Wed, 12 Nov 2025 00:16:27 +0100 Subject: [PATCH] feat(slot-definitions): add materialization policy selector (SAFE/CLEANUP) - Add MaterialisationPolicyProfile type and MATERIALISATION_POLICIES constants - Add policy selector UI with radio buttons for SAFE and CLEANUP modes - SAFE mode (default): skips overlapping slots to protect existing schedule - CLEANUP mode: removes conflicting slots to fix overlaps (keeps bookings safe) - Update MaterialisationTriggerRequest to include policy_profile field - Pass selected policy to triggerMaterialisation API call - Fixes issue where partial overlaps blocked slot materialization - Implements Backend Brooke's BUILD 352 policy preset feature --- .../MaterialisationStatusPanel.tsx | 54 ++++++++++++++++++- src/types/materialisation.ts | 28 ++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/app/[locale]/admin/clubs/[club_id]/slot-definitions/MaterialisationStatusPanel.tsx b/src/app/[locale]/admin/clubs/[club_id]/slot-definitions/MaterialisationStatusPanel.tsx index df004bb..2f68920 100644 --- a/src/app/[locale]/admin/clubs/[club_id]/slot-definitions/MaterialisationStatusPanel.tsx +++ b/src/app/[locale]/admin/clubs/[club_id]/slot-definitions/MaterialisationStatusPanel.tsx @@ -3,12 +3,13 @@ import { useState, useEffect } from 'react'; import { Calendar, Loader2, AlertCircle, CheckCircle, Clock, AlertTriangle } from 'lucide-react'; import { getMaterialisationStatus, triggerMaterialisation } from '@/src/lib/api/materialisation'; -import type { MaterialisationStatus } from '@/src/types/materialisation'; +import type { MaterialisationStatus, MaterialisationPolicyProfile } from '@/src/types/materialisation'; import { generateIdempotencyKey, calculateRemainingCooldown, formatCountdown, formatTimestamp, + MATERIALISATION_POLICIES, } from '@/src/types/materialisation'; interface MaterialisationStatusPanelProps { @@ -21,6 +22,7 @@ export default function MaterialisationStatusPanel({ clubId }: MaterialisationSt const [triggering, setTriggering] = useState(false); const [error, setError] = useState(null); const [countdown, setCountdown] = useState(0); + const [selectedPolicy, setSelectedPolicy] = useState('SAFE'); // Poll status on mount and when triggering completes useEffect(() => { @@ -85,7 +87,10 @@ export default function MaterialisationStatusPanel({ clubId }: MaterialisationSt const idempotencyKey = generateIdempotencyKey(); - const result = await triggerMaterialisation(clubId, { idempotency_key: idempotencyKey }); + const result = await triggerMaterialisation(clubId, { + idempotency_key: idempotencyKey, + policy_profile: selectedPolicy, + }); if (result.success) { // Start polling immediately @@ -275,6 +280,51 @@ export default function MaterialisationStatusPanel({ clubId }: MaterialisationSt )} + {/* Policy Selector */} +
+ +
+ {MATERIALISATION_POLICIES.map((policy) => ( + + ))} +
+
+ {/* Trigger button */}