Files
Krow-workspace/frontend-web/src/components/orders/CancellationFeeModal.jsx
2025-11-21 09:13:05 -05:00

161 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { AlertTriangle, Clock, DollarSign, Calendar, Users } from "lucide-react";
import { format, differenceInHours } from "date-fns";
// Calculate if cancellation fee applies
export const calculateCancellationFee = (eventDate, eventStartTime, assignedCount) => {
const now = new Date();
// Combine event date and start time
const eventDateTime = new Date(`${eventDate}T${eventStartTime || '00:00'}`);
const hoursUntilEvent = differenceInHours(eventDateTime, now);
// Rule: 24+ hours = no fee, < 24 hours = 4-hour fee per worker
const feeApplies = hoursUntilEvent < 24;
const feeAmount = feeApplies ? assignedCount * 4 * 50 : 0; // Assuming $50/hour average
return {
feeApplies,
hoursUntilEvent,
feeAmount,
assignedCount
};
};
export default function CancellationFeeModal({
open,
onClose,
onConfirm,
event,
isSubmitting
}) {
if (!event) return null;
const eventStartTime = event.shifts?.[0]?.roles?.[0]?.start_time || '09:00';
const assignedCount = event.assigned_staff?.length || 0;
const feeData = calculateCancellationFee(event.date, eventStartTime, assignedCount);
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent className="max-w-xl">
<DialogHeader>
<div className="flex items-center gap-3 mb-2">
<div className="w-12 h-12 bg-red-100 rounded-xl flex items-center justify-center">
<AlertTriangle className="w-6 h-6 text-red-600" />
</div>
<div>
<DialogTitle className="text-2xl font-bold text-red-700">
Confirm Order Cancellation
</DialogTitle>
<DialogDescription className="text-slate-600 mt-1">
{feeData.feeApplies
? "⚠️ Cancellation fee will apply"
: "✅ No cancellation fee"
}
</DialogDescription>
</div>
</div>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Event Summary */}
<div className="bg-slate-50 border border-slate-200 rounded-xl p-4">
<h4 className="font-bold text-slate-900 mb-3">{event.event_name}</h4>
<div className="grid grid-cols-2 gap-3 text-sm">
<div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-blue-600" />
<span>{format(new Date(event.date), 'MMM d, yyyy')}</span>
</div>
<div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-blue-600" />
<span>{eventStartTime}</span>
</div>
<div className="flex items-center gap-2">
<Users className="w-4 h-4 text-blue-600" />
<span>{assignedCount} Staff Assigned</span>
</div>
</div>
</div>
{/* Time Until Event */}
<Alert className={feeData.feeApplies ? "bg-red-50 border-red-300" : "bg-green-50 border-green-300"}>
<AlertDescription>
<div className="flex items-center gap-2 mb-2">
<Clock className={`w-5 h-5 ${feeData.feeApplies ? 'text-red-600' : 'text-green-600'}`} />
<span className="font-bold text-slate-900">
{feeData.hoursUntilEvent} hours until event
</span>
</div>
<p className="text-sm text-slate-700">
{feeData.feeApplies
? "Canceling within 24 hours triggers a 4-hour minimum fee per assigned worker."
: "You're canceling more than 24 hours in advance - no penalty applies."
}
</p>
</AlertDescription>
</Alert>
{/* Fee Breakdown */}
{feeData.feeApplies && (
<div className="bg-gradient-to-r from-red-50 to-orange-50 border-2 border-red-300 rounded-xl p-5">
<div className="flex items-center gap-2 mb-4">
<DollarSign className="w-5 h-5 text-red-600" />
<h4 className="font-bold text-red-900">Cancellation Fee Breakdown</h4>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-white rounded-lg">
<span className="text-sm text-slate-700">Assigned Staff</span>
<span className="font-bold text-slate-900">{assignedCount} workers</span>
</div>
<div className="flex items-center justify-between p-3 bg-white rounded-lg">
<span className="text-sm text-slate-700">Minimum Charge</span>
<span className="font-bold text-slate-900">4 hours each</span>
</div>
<div className="flex items-center justify-between p-4 bg-red-100 rounded-lg border-2 border-red-300">
<span className="font-bold text-red-900">Total Cancellation Fee</span>
<span className="text-2xl font-bold text-red-700">
${feeData.feeAmount.toLocaleString()}
</span>
</div>
</div>
</div>
)}
{/* Warning Text */}
<Alert className="bg-yellow-50 border-yellow-300">
<AlertDescription className="text-sm text-yellow-900">
<strong> This action cannot be undone.</strong> The vendor will be notified immediately,
and all assigned staff will be released from this event.
</AlertDescription>
</Alert>
</div>
<DialogFooter>
<Button variant="outline" onClick={onClose}>
Keep Order
</Button>
<Button
variant="destructive"
onClick={onConfirm}
disabled={isSubmitting}
className="bg-red-600 hover:bg-red-700"
>
{isSubmitting ? "Canceling..." : `Confirm Cancellation${feeData.feeApplies ? ` ($${feeData.feeAmount})` : ''}`}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}