|
|
|
|
@ -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<string | null>(null);
|
|
|
|
|
const [countdown, setCountdown] = useState(0);
|
|
|
|
|
const [selectedPolicy, setSelectedPolicy] = useState<MaterialisationPolicyProfile>('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
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Policy Selector */}
|
|
|
|
|
<div className="mt-6 space-y-3">
|
|
|
|
|
<label className="block text-sm font-semibold text-slate-700">
|
|
|
|
|
Materialization Policy
|
|
|
|
|
</label>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{MATERIALISATION_POLICIES.map((policy) => (
|
|
|
|
|
<label
|
|
|
|
|
key={policy.name}
|
|
|
|
|
className={`flex items-start p-4 border-2 rounded-lg cursor-pointer transition-all ${
|
|
|
|
|
selectedPolicy === policy.name
|
|
|
|
|
? policy.color === 'blue'
|
|
|
|
|
? 'border-blue-600 bg-blue-50'
|
|
|
|
|
: 'border-orange-600 bg-orange-50'
|
|
|
|
|
: 'border-slate-200 bg-white hover:border-slate-300'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
type="radio"
|
|
|
|
|
name="policy"
|
|
|
|
|
value={policy.name}
|
|
|
|
|
checked={selectedPolicy === policy.name}
|
|
|
|
|
onChange={(e) => setSelectedPolicy(e.target.value as MaterialisationPolicyProfile)}
|
|
|
|
|
className="mt-1 w-4 h-4"
|
|
|
|
|
disabled={!canTrigger}
|
|
|
|
|
/>
|
|
|
|
|
<div className="ml-3 flex-1">
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<span className="text-lg">{policy.icon}</span>
|
|
|
|
|
<span className="font-semibold text-slate-900">
|
|
|
|
|
{policy.name === 'SAFE' ? 'Safe Mode (Default)' : 'Cleanup Mode'}
|
|
|
|
|
</span>
|
|
|
|
|
{policy.badge && (
|
|
|
|
|
<span className="text-xs px-2 py-0.5 bg-orange-600 text-white rounded-full font-medium">
|
|
|
|
|
{policy.badge}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-sm text-slate-700 mt-1">{policy.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</label>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Trigger button */}
|
|
|
|
|
<div className="mt-6">
|
|
|
|
|
<button
|
|
|
|
|
|