Re_Figma_Code/components/modals/ApprovalActionModal.tsx
2025-10-22 10:27:06 +05:30

195 lines
6.8 KiB
TypeScript

import React, { useState } from 'react';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/dialog';
import { Button } from '../ui/button';
import { Textarea } from '../ui/textarea';
import { Label } from '../ui/label';
import { CheckCircle, XCircle, AlertTriangle } from 'lucide-react';
import { Badge } from '../ui/badge';
interface ApprovalActionModalProps {
isOpen: boolean;
onClose: () => void;
action: 'approve' | 'reject';
requestId: string;
requestTitle: string;
onSubmit: (action: 'approve' | 'reject', comment: string) => void;
}
export function ApprovalActionModal({
isOpen,
onClose,
action,
requestId,
requestTitle,
onSubmit
}: ApprovalActionModalProps) {
const [comment, setComment] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async () => {
if (!comment.trim() || comment.length > 500) {
return;
}
setIsSubmitting(true);
try {
await onSubmit(action, comment.trim());
setComment('');
onClose();
} catch (error) {
console.error('Error submitting approval action:', error);
} finally {
setIsSubmitting(false);
}
};
const handleClose = () => {
setComment('');
onClose();
};
const getActionConfig = () => {
if (action === 'approve') {
return {
title: 'Approve Request',
description: 'Please provide your approval comments and remarks',
icon: CheckCircle,
iconColor: 'text-green-600',
buttonColor: 'bg-green-600 hover:bg-green-700',
badgeColor: 'bg-green-100 text-green-800 border-green-200',
placeholder: 'Enter your approval comments and any conditions or notes...'
};
} else {
return {
title: 'Reject Request',
description: 'Please provide detailed reasons for rejection',
icon: XCircle,
iconColor: 'text-red-600',
buttonColor: 'bg-red-600 hover:bg-red-700',
badgeColor: 'bg-red-100 text-red-800 border-red-200',
placeholder: 'Enter detailed reasons for rejection and any suggestions for improvement...'
};
}
};
const config = getActionConfig();
const IconComponent = config.icon;
return (
<Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<div className="flex items-center gap-3 mb-2">
<div className={`p-2 rounded-lg ${action === 'approve' ? 'bg-green-100' : 'bg-red-100'}`}>
<IconComponent className={`w-6 h-6 ${config.iconColor}`} />
</div>
<div className="flex-1">
<DialogTitle className="text-xl">{config.title}</DialogTitle>
<DialogDescription className="mt-1">
{config.description}
</DialogDescription>
</div>
</div>
<div className="space-y-3 p-4 bg-gray-50 rounded-lg border">
<div className="flex items-center justify-between">
<span className="font-medium text-gray-900">Request ID:</span>
<Badge variant="outline" className="font-mono">
{requestId}
</Badge>
</div>
<div>
<span className="font-medium text-gray-900">Title:</span>
<p className="text-gray-700 mt-1">{requestTitle}</p>
</div>
<div className="flex items-center gap-2">
<span className="font-medium text-gray-900">Action:</span>
<Badge className={config.badgeColor} variant="outline">
<IconComponent className="w-3 h-3 mr-1" />
{action === 'approve' ? 'APPROVE' : 'REJECT'}
</Badge>
</div>
</div>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="comment" className="text-sm font-semibold text-gray-900">
Comments & Remarks *
</Label>
<Textarea
id="comment"
placeholder={config.placeholder}
value={comment}
onChange={(e) => setComment(e.target.value.slice(0, 500))}
className="min-h-[120px] resize-none"
rows={6}
/>
<div className="flex items-center justify-between text-xs text-gray-500">
<div className="flex items-center gap-1">
<AlertTriangle className="w-3 h-3" />
Comments are required and will be visible to all participants
</div>
<span>{comment.length}/500</span>
</div>
</div>
{action === 'reject' && (
<div className="p-3 bg-orange-50 border border-orange-200 rounded-lg">
<div className="flex items-start gap-2">
<AlertTriangle className="w-4 h-4 text-orange-600 mt-0.5 flex-shrink-0" />
<div className="text-sm">
<p className="font-medium text-orange-800">Rejection Guidelines</p>
<p className="text-orange-700 mt-1">
Please provide specific, actionable feedback to help the initiator improve their request.
</p>
</div>
</div>
</div>
)}
{action === 'approve' && (
<div className="p-3 bg-green-50 border border-green-200 rounded-lg">
<div className="flex items-start gap-2">
<CheckCircle className="w-4 h-4 text-green-600 mt-0.5 flex-shrink-0" />
<div className="text-sm">
<p className="font-medium text-green-800">Approval Confirmation</p>
<p className="text-green-700 mt-1">
This request will be forwarded to the next approver or completed if this is the final step.
</p>
</div>
</div>
</div>
)}
</div>
<DialogFooter className="gap-2">
<Button
variant="outline"
onClick={handleClose}
disabled={isSubmitting}
>
Cancel
</Button>
<Button
onClick={handleSubmit}
disabled={!comment.trim() || isSubmitting || comment.length > 500}
className={config.buttonColor}
>
{isSubmitting ? (
<>
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" />
Processing...
</>
) : (
<>
<IconComponent className="w-4 h-4 mr-2" />
{action === 'approve' ? 'Approve Request' : 'Reject Request'}
</>
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}