added gst non gst lable hided hsn and gst related fields for the non-gst claims
This commit is contained in:
parent
170f9a1788
commit
b04776a5f8
@ -12,6 +12,13 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
import {
|
import {
|
||||||
FileText,
|
FileText,
|
||||||
Plus,
|
Plus,
|
||||||
@ -89,16 +96,17 @@ export function ActivityTypeManager() {
|
|||||||
try {
|
try {
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
if (!formData.title.trim()) {
|
if (!formData.title.trim() || !formData.taxationType.trim() || !formData.sapRefNo.trim()) {
|
||||||
setError('Activity type title is required');
|
setError('Title, Taxation Type, and Claim Document Type (SAP Ref) are required');
|
||||||
|
toast.error('Please fill in all mandatory fields');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload: Partial<ActivityType> = {
|
const payload: Partial<ActivityType> = {
|
||||||
title: formData.title.trim(),
|
title: formData.title.trim(),
|
||||||
itemCode: formData.itemCode.trim() || null,
|
itemCode: formData.itemCode.trim() || null,
|
||||||
taxationType: formData.taxationType.trim() || null,
|
taxationType: formData.taxationType.trim(),
|
||||||
sapRefNo: formData.sapRefNo.trim() || null
|
sapRefNo: formData.sapRefNo.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (editingActivityType) {
|
if (editingActivityType) {
|
||||||
@ -397,32 +405,37 @@ export function ActivityTypeManager() {
|
|||||||
|
|
||||||
{/* Taxation Type Field */}
|
{/* Taxation Type Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="taxationType" className="text-sm font-semibold text-slate-900">
|
<Label htmlFor="taxationType" className="text-sm font-semibold text-slate-900 flex items-center gap-1">
|
||||||
Taxation Type <span className="text-slate-400 font-normal text-xs">(Optional)</span>
|
Taxation Type <span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Select
|
||||||
id="taxationType"
|
|
||||||
placeholder="e.g., GST, VAT, Exempt"
|
|
||||||
value={formData.taxationType}
|
value={formData.taxationType}
|
||||||
onChange={(e) => setFormData({ ...formData, taxationType: e.target.value })}
|
onValueChange={(value) => setFormData({ ...formData, taxationType: value })}
|
||||||
className="h-11 border-slate-300 focus:border-re-green focus:ring-2 focus:ring-re-green/20 rounded-lg transition-all shadow-sm"
|
>
|
||||||
/>
|
<SelectTrigger id="taxationType" className="h-11 border-slate-300 focus:border-re-green focus:ring-2 focus:ring-re-green/20 rounded-lg transition-all shadow-sm">
|
||||||
<p className="text-xs text-slate-500">Optional taxation type for the activity</p>
|
<SelectValue placeholder="Select Taxation Type" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="rounded-lg">
|
||||||
|
<SelectItem value="GST" className="p-3">GST</SelectItem>
|
||||||
|
<SelectItem value="Non GST" className="p-3">Non GST</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-xs text-slate-500">Select whether the activity is GST or Non-GST</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SAP Reference Number Field */}
|
{/* SAP Reference Number Field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="sapRefNo" className="text-sm font-semibold text-slate-900">
|
<Label htmlFor="sapRefNo" className="text-sm font-semibold text-slate-900 flex items-center gap-1">
|
||||||
SAP Reference Number <span className="text-slate-400 font-normal text-xs">(Optional)</span>
|
Claim Document Type (SAP Ref) <span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="sapRefNo"
|
id="sapRefNo"
|
||||||
placeholder="e.g., SAP-12345"
|
placeholder="e.g., ZCNS, ZRE"
|
||||||
value={formData.sapRefNo}
|
value={formData.sapRefNo}
|
||||||
onChange={(e) => setFormData({ ...formData, sapRefNo: e.target.value })}
|
onChange={(e) => setFormData({ ...formData, sapRefNo: e.target.value })}
|
||||||
className="h-11 border-slate-300 focus:border-re-green focus:ring-2 focus:ring-re-green/20 rounded-lg transition-all shadow-sm"
|
className="h-11 border-slate-300 focus:border-re-green focus:ring-2 focus:ring-re-green/20 rounded-lg transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-slate-500">Optional SAP reference number</p>
|
<p className="text-xs text-slate-500">Required SAP reference number for CSV generation</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -436,7 +449,7 @@ export function ActivityTypeManager() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={!formData.title.trim()}
|
disabled={!formData.title.trim() || !formData.taxationType || !formData.sapRefNo.trim()}
|
||||||
className="h-11 bg-re-green hover:bg-re-green/90 text-white shadow-md hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
className="h-11 bg-re-green hover:bg-re-green/90 text-white shadow-md hover:shadow-lg transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
<FileText className="w-4 h-4 mr-2" />
|
<FileText className="w-4 h-4 mr-2" />
|
||||||
|
|||||||
@ -2525,6 +2525,7 @@ export function DealerClaimWorkflowTab({
|
|||||||
preFilledIONumber={request?.internalOrder?.ioNumber || request?.internalOrder?.io_number || request?.internal_order?.ioNumber || request?.internal_order?.io_number || undefined}
|
preFilledIONumber={request?.internalOrder?.ioNumber || request?.internalOrder?.io_number || request?.internal_order?.ioNumber || request?.internal_order?.io_number || undefined}
|
||||||
preFilledBlockedAmount={request?.internalOrder?.ioBlockedAmount || request?.internalOrder?.io_blocked_amount || request?.internal_order?.ioBlockedAmount || request?.internal_order?.io_blocked_amount || undefined}
|
preFilledBlockedAmount={request?.internalOrder?.ioBlockedAmount || request?.internalOrder?.io_blocked_amount || request?.internal_order?.ioBlockedAmount || request?.internal_order?.io_blocked_amount || undefined}
|
||||||
preFilledRemainingBalance={request?.internalOrder?.ioRemainingBalance || request?.internalOrder?.io_remaining_balance || request?.internal_order?.ioRemainingBalance || request?.internal_order?.io_remaining_balance || undefined}
|
preFilledRemainingBalance={request?.internalOrder?.ioRemainingBalance || request?.internalOrder?.io_remaining_balance || request?.internal_order?.ioRemainingBalance || request?.internal_order?.io_remaining_balance || undefined}
|
||||||
|
taxationType={request?.claimDetails?.taxationType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Dealer Completion Documents Modal */}
|
{/* Dealer Completion Documents Modal */}
|
||||||
@ -2562,12 +2563,14 @@ export function DealerClaimWorkflowTab({
|
|||||||
completionDocuments={completionDocumentsData}
|
completionDocuments={completionDocumentsData}
|
||||||
requestTitle={request?.title}
|
requestTitle={request?.title}
|
||||||
requestNumber={request?.requestNumber || request?.request_number || request?.id}
|
requestNumber={request?.requestNumber || request?.request_number || request?.id}
|
||||||
|
taxationType={request?.claimDetails?.taxationType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Credit Note from SAP Modal (Step 8) */}
|
{/* Credit Note from SAP Modal (Step 8) */}
|
||||||
<CreditNoteSAPModal
|
<CreditNoteSAPModal
|
||||||
isOpen={showCreditNoteModal}
|
isOpen={showCreditNoteModal}
|
||||||
onClose={() => setShowCreditNoteModal(false)}
|
onClose={() => setShowCreditNoteModal(false)}
|
||||||
|
taxationType={request?.claimDetails?.taxationType}
|
||||||
onDownload={async () => {
|
onDownload={async () => {
|
||||||
toast.info('Download functionality will be implemented');
|
toast.info('Download functionality will be implemented');
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ interface CreditNoteSAPModalProps {
|
|||||||
requestNumber?: string;
|
requestNumber?: string;
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
dueDate?: string;
|
dueDate?: string;
|
||||||
|
taxationType?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreditNoteSAPModal({
|
export function CreditNoteSAPModal({
|
||||||
@ -53,10 +54,13 @@ export function CreditNoteSAPModal({
|
|||||||
requestNumber,
|
requestNumber,
|
||||||
requestId: _requestId,
|
requestId: _requestId,
|
||||||
dueDate,
|
dueDate,
|
||||||
|
taxationType,
|
||||||
}: CreditNoteSAPModalProps) {
|
}: CreditNoteSAPModalProps) {
|
||||||
const [downloading, setDownloading] = useState(false);
|
const [downloading, setDownloading] = useState(false);
|
||||||
const [sending, setSending] = useState(false);
|
const [sending, setSending] = useState(false);
|
||||||
|
|
||||||
|
const isNonGst = taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
|
||||||
const hasCreditNote = creditNoteData?.creditNoteNumber && creditNoteData?.creditNoteNumber !== '';
|
const hasCreditNote = creditNoteData?.creditNoteNumber && creditNoteData?.creditNoteNumber !== '';
|
||||||
const creditNoteNumber = creditNoteData?.creditNoteNumber || '';
|
const creditNoteNumber = creditNoteData?.creditNoteNumber || '';
|
||||||
const creditNoteDate = creditNoteData?.creditNoteDate
|
const creditNoteDate = creditNoteData?.creditNoteDate
|
||||||
@ -118,9 +122,16 @@ export function CreditNoteSAPModal({
|
|||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||||
<DialogContent className="sm:max-w-lg lg:max-w-[1000px] max-w-4xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="sm:max-w-lg lg:max-w-[1000px] max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="font-semibold flex items-center gap-2 text-2xl">
|
<DialogTitle className="font-semibold flex items-center gap-2 text-2xl flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Receipt className="w-6 h-6 text-[--re-green]" />
|
<Receipt className="w-6 h-6 text-[--re-green]" />
|
||||||
Credit Note from SAP
|
Credit Note from SAP
|
||||||
|
</div>
|
||||||
|
{taxationType && (
|
||||||
|
<Badge className={`ml-2 border-none shadow-sm ${!isNonGst ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
|
{!isNonGst ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-base">
|
<DialogDescription className="text-base">
|
||||||
Review and send credit note to dealer
|
Review and send credit note to dealer
|
||||||
|
|||||||
@ -85,6 +85,7 @@ interface DMSPushModalProps {
|
|||||||
completionDocuments?: CompletionDocuments | null;
|
completionDocuments?: CompletionDocuments | null;
|
||||||
requestTitle?: string;
|
requestTitle?: string;
|
||||||
requestNumber?: string;
|
requestNumber?: string;
|
||||||
|
taxationType?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DMSPushModal({
|
export function DMSPushModal({
|
||||||
@ -96,7 +97,12 @@ export function DMSPushModal({
|
|||||||
completionDocuments,
|
completionDocuments,
|
||||||
requestTitle,
|
requestTitle,
|
||||||
requestNumber,
|
requestNumber,
|
||||||
|
taxationType,
|
||||||
}: DMSPushModalProps) {
|
}: DMSPushModalProps) {
|
||||||
|
const isNonGst = useMemo(() => {
|
||||||
|
return taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
}, [taxationType]);
|
||||||
|
|
||||||
const [comments, setComments] = useState('');
|
const [comments, setComments] = useState('');
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [previewDocument, setPreviewDocument] = useState<{
|
const [previewDocument, setPreviewDocument] = useState<{
|
||||||
@ -268,8 +274,13 @@ export function DMSPushModal({
|
|||||||
<Activity className="w-4 h-4 sm:w-5 sm:h-5 sm:w-6 sm:h-6 text-indigo-600" />
|
<Activity className="w-4 h-4 sm:w-5 sm:h-5 sm:w-6 sm:h-6 text-indigo-600" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<DialogTitle className="font-semibold text-lg sm:text-xl">
|
<DialogTitle className="font-semibold text-lg sm:text-xl flex items-center gap-2 flex-wrap">
|
||||||
E-Invoice Generation & Sync
|
E-Invoice Generation & Sync
|
||||||
|
{taxationType && (
|
||||||
|
<Badge className={`ml-2 border-none shadow-sm ${!isNonGst ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
|
{!isNonGst ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-xs sm:text-sm mt-1">
|
<DialogDescription className="text-xs sm:text-sm mt-1">
|
||||||
Review completion details and expenses before generating e-invoice and initiating SAP settlement
|
Review completion details and expenses before generating e-invoice and initiating SAP settlement
|
||||||
@ -402,10 +413,14 @@ export function DMSPushModal({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
{/* Table Header */}
|
{/* Table Header */}
|
||||||
<div className="grid grid-cols-12 gap-2 mb-2 px-3 text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:grid">
|
<div className="grid grid-cols-12 gap-2 mb-2 px-3 text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:grid">
|
||||||
<div className="col-span-4">Description</div>
|
<div className={`${isNonGst ? 'col-span-8' : 'col-span-4'}`}>Description</div>
|
||||||
<div className="col-span-2 text-right">Base</div>
|
<div className="col-span-2 text-right">Base</div>
|
||||||
|
{!isNonGst && (
|
||||||
|
<>
|
||||||
<div className="col-span-2 text-right">GST Rate</div>
|
<div className="col-span-2 text-right">GST Rate</div>
|
||||||
<div className="col-span-2 text-right">GST Amt</div>
|
<div className="col-span-2 text-right">GST Amt</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className="col-span-2 text-right">Total</div>
|
<div className="col-span-2 text-right">Total</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -428,11 +443,13 @@ export function DMSPushModal({
|
|||||||
</div>
|
</div>
|
||||||
<div className="sm:hidden flex justify-between w-full text-xs text-gray-500">
|
<div className="sm:hidden flex justify-between w-full text-xs text-gray-500">
|
||||||
<span>Base: {formatCurrency(amount)}</span>
|
<span>Base: {formatCurrency(amount)}</span>
|
||||||
|
{!isNonGst && (
|
||||||
<span>GST: {gstRate}% ({formatCurrency(gstAmt)})</span>
|
<span>GST: {gstRate}% ({formatCurrency(gstAmt)})</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Desktop View: Grid */}
|
{/* Desktop View: Grid */}
|
||||||
<div className="hidden sm:block col-span-4 min-w-0">
|
<div className={`hidden sm:block ${isNonGst ? 'col-span-8' : 'col-span-4'} min-w-0`}>
|
||||||
<p className="font-medium text-gray-900 truncate" title={expense.description}>
|
<p className="font-medium text-gray-900 truncate" title={expense.description}>
|
||||||
{expense.description || `Expense ${index + 1}`}
|
{expense.description || `Expense ${index + 1}`}
|
||||||
</p>
|
</p>
|
||||||
@ -440,12 +457,16 @@ export function DMSPushModal({
|
|||||||
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
||||||
{formatCurrency(amount)}
|
{formatCurrency(amount)}
|
||||||
</div>
|
</div>
|
||||||
|
{!isNonGst && (
|
||||||
|
<>
|
||||||
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
||||||
{gstRate}%
|
{gstRate}%
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
<div className="hidden sm:block col-span-2 text-right text-gray-600">
|
||||||
{formatCurrency(gstAmt)}
|
{formatCurrency(gstAmt)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<div className="hidden sm:block col-span-2 text-right font-semibold text-gray-900">
|
<div className="hidden sm:block col-span-2 text-right font-semibold text-gray-900">
|
||||||
{formatCurrency(total)}
|
{formatCurrency(total)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -96,6 +96,10 @@ export function DealerCompletionDocumentsModal({
|
|||||||
return getActiveTaxComponents(stateCode);
|
return getActiveTaxComponents(stateCode);
|
||||||
}, [dealerGSTIN]);
|
}, [dealerGSTIN]);
|
||||||
|
|
||||||
|
const isNonGst = useMemo(() => {
|
||||||
|
return taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
}, [taxationType]);
|
||||||
|
|
||||||
const [expenseItems, setExpenseItems] = useState<ExpenseItem[]>([]);
|
const [expenseItems, setExpenseItems] = useState<ExpenseItem[]>([]);
|
||||||
const [completionDocuments, setCompletionDocuments] = useState<File[]>([]);
|
const [completionDocuments, setCompletionDocuments] = useState<File[]>([]);
|
||||||
const [activityPhotos, setActivityPhotos] = useState<File[]>([]);
|
const [activityPhotos, setActivityPhotos] = useState<File[]>([]);
|
||||||
@ -225,13 +229,13 @@ export function DealerCompletionDocumentsModal({
|
|||||||
const hasPhotos = activityPhotos.length > 0;
|
const hasPhotos = activityPhotos.length > 0;
|
||||||
const hasDescription = completionDescription.trim().length > 0;
|
const hasDescription = completionDescription.trim().length > 0;
|
||||||
|
|
||||||
const hasHSNSACErrors = expenseItems.some(item => {
|
const hasHSNSACErrors = isNonGst ? false : expenseItems.some(item => {
|
||||||
const { isValid } = validateHSNSAC(item.hsnCode, item.isService);
|
const { isValid } = validateHSNSAC(item.hsnCode, item.isService);
|
||||||
return !isValid;
|
return !isValid;
|
||||||
});
|
});
|
||||||
|
|
||||||
return hasCompletionDate && hasDocuments && hasPhotos && hasDescription && !hasHSNSACErrors;
|
return hasCompletionDate && hasDocuments && hasPhotos && hasDescription && !hasHSNSACErrors;
|
||||||
}, [activityCompletionDate, completionDocuments, activityPhotos, completionDescription]);
|
}, [activityCompletionDate, completionDocuments, activityPhotos, completionDescription, isNonGst, expenseItems]);
|
||||||
|
|
||||||
// Get today's date in YYYY-MM-DD format for max date
|
// Get today's date in YYYY-MM-DD format for max date
|
||||||
const maxDate = new Date().toISOString().split('T')[0];
|
const maxDate = new Date().toISOString().split('T')[0];
|
||||||
@ -524,8 +528,8 @@ export function DealerCompletionDocumentsModal({
|
|||||||
(item) => item.description.trim() !== '' && item.amount > 0
|
(item) => item.description.trim() !== '' && item.amount > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validation: Alert for 0% GST on taxable items
|
// Validation: Alert for 0% GST on taxable items (Skip for Non-GST)
|
||||||
const hasZeroGstItems = validExpenses.some(item =>
|
const hasZeroGstItems = !isNonGst && validExpenses.some(item =>
|
||||||
item.description.trim() !== '' && item.amount > 0 && (item.gstRate === 0 || !item.gstRate)
|
item.description.trim() !== '' && item.amount > 0 && (item.gstRate === 0 || !item.gstRate)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -616,15 +620,17 @@ export function DealerCompletionDocumentsModal({
|
|||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent className="dealer-completion-documents-modal overflow-hidden flex flex-col">
|
<DialogContent className="dealer-completion-documents-modal overflow-hidden flex flex-col">
|
||||||
<DialogHeader className="px-6 pt-6 pb-3 flex-shrink-0">
|
<DialogHeader className="px-6 pt-6 pb-3 flex-shrink-0">
|
||||||
<DialogTitle className="font-semibold flex items-center gap-2 text-xl sm:text-2xl">
|
<DialogTitle className="font-semibold flex items-center gap-2 text-xl sm:text-2xl flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Upload className="w-5 h-5 sm:w-6 sm:h-6 text-[--re-green]" />
|
<Upload className="w-5 h-5 sm:w-6 sm:h-6 text-[--re-green]" />
|
||||||
Activity Completion Documents
|
Activity Completion Documents
|
||||||
</DialogTitle>
|
</div>
|
||||||
{taxationType && (
|
{taxationType && (
|
||||||
<Badge className={`mt-1 mb-1 w-fit ${taxationType === 'GST' ? 'bg-[#2d4a3e] text-white hover:bg-[#2d4a3e]/90' : 'bg-slate-200 text-slate-800 hover:bg-slate-200/90'}`}>
|
<Badge className={`ml-2 border-none shadow-sm ${taxationType === 'GST' ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
{taxationType === 'GST' ? 'GST Claim' : 'Non-GST Claim'}
|
{taxationType === 'GST' ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription className="text-sm sm:text-base">
|
<DialogDescription className="text-sm sm:text-base">
|
||||||
Step 5: Upload completion proof and final documents
|
Step 5: Upload completion proof and final documents
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
@ -666,9 +672,11 @@ export function DealerCompletionDocumentsModal({
|
|||||||
<h3 className="font-semibold text-base sm:text-lg">Closed Expenses</h3>
|
<h3 className="font-semibold text-base sm:text-lg">Closed Expenses</h3>
|
||||||
<Badge className="bg-secondary text-secondary-foreground text-xs">Optional</Badge>
|
<Badge className="bg-secondary text-secondary-foreground text-xs">Optional</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
{!isNonGst && (
|
||||||
<div className="text-[10px] text-gray-500 italic mt-0.5">
|
<div className="text-[10px] text-gray-500 italic mt-0.5">
|
||||||
Tax fields are automatically toggled based on the dealer's state (Inter-state vs Intra-state).
|
Tax fields are automatically toggled based on the dealer's state (Inter-state vs Intra-state).
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddExpense}
|
onClick={handleAddExpense}
|
||||||
@ -683,7 +691,7 @@ export function DealerCompletionDocumentsModal({
|
|||||||
{expenseItems.map((item) => (
|
{expenseItems.map((item) => (
|
||||||
<div key={item.id} className="p-4 border rounded-lg bg-gray-50/50 space-y-4 relative group">
|
<div key={item.id} className="p-4 border rounded-lg bg-gray-50/50 space-y-4 relative group">
|
||||||
<div className="flex gap-3 items-start w-full">
|
<div className="flex gap-3 items-start w-full">
|
||||||
<div className="flex-1 min-w-0">
|
<div className={`${isNonGst ? 'flex-[3]' : 'flex-1'} min-w-0`}>
|
||||||
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Item description</Label>
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Item description</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="e.g., Venue rental, Refreshments"
|
placeholder="e.g., Venue rental, Refreshments"
|
||||||
@ -694,6 +702,26 @@ export function DealerCompletionDocumentsModal({
|
|||||||
className="w-full bg-white text-sm"
|
className="w-full bg-white text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{isNonGst && (
|
||||||
|
<div className="w-28 sm:w-36 flex-shrink-0">
|
||||||
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<IndianRupee className="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400" />
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="0.00"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
value={item.amount || ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleExpenseChange(item.id, 'amount', e.target.value)
|
||||||
|
}
|
||||||
|
className="w-full bg-white text-sm pl-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isNonGst && (
|
||||||
<div className="w-28 sm:w-36 flex-shrink-0">
|
<div className="w-28 sm:w-36 flex-shrink-0">
|
||||||
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount (Base)</Label>
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount (Base)</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -711,6 +739,9 @@ export function DealerCompletionDocumentsModal({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{!isNonGst && (
|
||||||
|
<>
|
||||||
<div className="w-20 sm:w-24 flex-shrink-0">
|
<div className="w-20 sm:w-24 flex-shrink-0">
|
||||||
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">HSN Code</Label>
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">HSN Code</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -804,6 +835,8 @@ export function DealerCompletionDocumentsModal({
|
|||||||
className="w-full bg-white text-xs px-1 text-center disabled:bg-gray-100 disabled:text-gray-400"
|
className="w-full bg-white text-xs px-1 text-center disabled:bg-gray-100 disabled:text-gray-400"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -815,34 +848,26 @@ export function DealerCompletionDocumentsModal({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-5 gap-3 pt-3 border-t border-dashed border-gray-200">
|
<div className={`grid grid-cols-2 sm:grid-cols-5 gap-3 pt-3 border-t border-dashed border-gray-200 ${isNonGst ? 'items-center' : ''}`}>
|
||||||
<div className="flex flex-col">
|
{!isNonGst ? (
|
||||||
<span className="text-[10px] text-gray-500 uppercase">CGST</span>
|
<>
|
||||||
<span className="text-xs font-semibold">₹{item.cgstAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
<div className="flex flex-wrap gap-4 text-gray-500 font-medium">
|
||||||
|
<span>CGST: <span className="text-gray-900 font-semibold">₹{(item.cgstAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>
|
||||||
|
{item.sgstAmt > 0 && <span>SGST: <span className="text-gray-900 font-semibold">₹{(item.sgstAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>}
|
||||||
|
{item.utgstAmt > 0 && <span>UTGST: <span className="text-gray-900 font-semibold">₹{(item.utgstAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>}
|
||||||
|
<span>IGST: <span className="text-gray-900 font-semibold">₹{(item.igstAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>
|
||||||
</div>
|
</div>
|
||||||
{item.sgstAmt > 0 && (
|
<div className="flex flex-wrap gap-4 items-center sm:justify-end">
|
||||||
<div className="flex flex-col">
|
<span className="text-gray-500">GST Total: <span className="text-gray-900 font-bold">₹{(item.gstAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>
|
||||||
<span className="text-[10px] text-gray-500 uppercase">SGST</span>
|
<Badge className="bg-[#2d4a3e] text-white px-3 py-1 text-xs">Item Total: ₹{(item.totalAmt || 0).toLocaleString('en-IN', { minimumFractionDigits: 1 })}</Badge>
|
||||||
<span className="text-xs font-semibold">₹{item.sgstAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="col-span-4 invisible"></div>
|
||||||
)}
|
)}
|
||||||
{item.utgstAmt > 0 && (
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-[10px] text-gray-500 uppercase">UTGST</span>
|
|
||||||
<span className="text-xs font-semibold">₹{item.utgstAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-[10px] text-gray-500 uppercase">IGST</span>
|
|
||||||
<span className="text-xs font-semibold">₹{item.igstAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-[10px] text-gray-500 uppercase">GST Total</span>
|
|
||||||
<span className="text-xs font-semibold">₹{item.gstAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-end">
|
<div className="flex flex-col items-end">
|
||||||
<span className="text-[10px] text-gray-500 uppercase">Item Total</span>
|
<span className="text-[10px] text-gray-500 uppercase">Item Total</span>
|
||||||
<span className="text-sm font-bold text-[#2d4a3e]">₹{item.totalAmt.toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
<span className="text-sm font-bold text-[#2d4a3e]">₹{(item.amount || 0).toLocaleString('en-IN', { minimumFractionDigits: 2 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1348,7 +1373,7 @@ export function DealerCompletionDocumentsModal({
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog >
|
||||||
|
|
||||||
{/* File Preview Modal - Matching DocumentsTab style */}
|
{/* File Preview Modal - Matching DocumentsTab style */}
|
||||||
{
|
{
|
||||||
|
|||||||
@ -96,6 +96,10 @@ export function DealerProposalSubmissionModal({
|
|||||||
return getActiveTaxComponents(stateCode);
|
return getActiveTaxComponents(stateCode);
|
||||||
}, [dealerGSTIN]);
|
}, [dealerGSTIN]);
|
||||||
|
|
||||||
|
const isNonGst = useMemo(() => {
|
||||||
|
return taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
}, [taxationType]);
|
||||||
|
|
||||||
const [costItems, setCostItems] = useState<CostItem[]>([
|
const [costItems, setCostItems] = useState<CostItem[]>([
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
@ -260,13 +264,13 @@ export function DealerProposalSubmissionModal({
|
|||||||
: numberOfDays !== '' && parseInt(numberOfDays) > 0;
|
: numberOfDays !== '' && parseInt(numberOfDays) > 0;
|
||||||
const hasValidComments = dealerComments.trim().length > 0;
|
const hasValidComments = dealerComments.trim().length > 0;
|
||||||
|
|
||||||
const hasHSNSACErrors = costItems.some(item => {
|
const hasHSNSACErrors = isNonGst ? false : costItems.some(item => {
|
||||||
const { isValid } = validateHSNSAC(item.hsnCode, item.isService);
|
const { isValid } = validateHSNSAC(item.hsnCode, item.isService);
|
||||||
return !isValid;
|
return !isValid;
|
||||||
});
|
});
|
||||||
|
|
||||||
return hasProposalDoc && hasValidCostItems && hasTimeline && hasValidComments && !hasHSNSACErrors;
|
return hasProposalDoc && hasValidCostItems && hasTimeline && hasValidComments && !hasHSNSACErrors;
|
||||||
}, [proposalDocument, costItems, timelineMode, expectedCompletionDate, numberOfDays, dealerComments]);
|
}, [proposalDocument, costItems, timelineMode, expectedCompletionDate, numberOfDays, dealerComments, isNonGst]);
|
||||||
|
|
||||||
const handleProposalDocChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleProposalDocChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
@ -462,8 +466,8 @@ export function DealerProposalSubmissionModal({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
// Validation: Alert for 0% GST on taxable items
|
// Validation: Alert for 0% GST on taxable items (Skip for Non-GST)
|
||||||
const hasZeroGstItems = costItems.some(item =>
|
const hasZeroGstItems = !isNonGst && costItems.some(item =>
|
||||||
item.description.trim() !== '' && item.amount > 0 && (item.gstRate === 0 || !item.gstRate)
|
item.description.trim() !== '' && item.amount > 0 && (item.gstRate === 0 || !item.gstRate)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -546,15 +550,17 @@ export function DealerProposalSubmissionModal({
|
|||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent className="dealer-proposal-modal overflow-hidden flex flex-col">
|
<DialogContent className="dealer-proposal-modal overflow-hidden flex flex-col">
|
||||||
<DialogHeader className="flex-shrink-0 pb-3 lg:pb-4">
|
<DialogHeader className="flex-shrink-0 pb-3 lg:pb-4">
|
||||||
<DialogTitle className="flex items-center gap-2 text-xl lg:text-2xl">
|
<DialogTitle className="flex items-center gap-2 text-xl lg:text-2xl flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Upload className="w-5 h-5 lg:w-6 lg:h-6 text-[--re-green]" />
|
<Upload className="w-5 h-5 lg:w-6 lg:h-6 text-[--re-green]" />
|
||||||
Dealer Proposal Submission
|
Dealer Proposal Submission
|
||||||
</DialogTitle>
|
</div>
|
||||||
{taxationType && (
|
{taxationType && (
|
||||||
<Badge className={`mt-1 mb-1 w-fit ${taxationType === 'GST' ? 'bg-[#2d4a3e] text-white hover:bg-[#2d4a3e]/90' : 'bg-slate-200 text-slate-800 hover:bg-slate-200/90'}`}>
|
<Badge className={`ml-2 border-none shadow-sm ${taxationType === 'GST' ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
{taxationType === 'GST' ? 'GST Claim' : 'Non-GST Claim'}
|
{taxationType === 'GST' ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription className="text-sm lg:text-base">
|
<DialogDescription className="text-sm lg:text-base">
|
||||||
Step 1: Upload proposal and planning details
|
Step 1: Upload proposal and planning details
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
@ -824,9 +830,11 @@ export function DealerProposalSubmissionModal({
|
|||||||
<h3 className="font-semibold text-lg text-[#2d4a3e]">Cost Breakup</h3>
|
<h3 className="font-semibold text-lg text-[#2d4a3e]">Cost Breakup</h3>
|
||||||
<Badge variant="outline" className="text-xs border-[#2d4a3e] text-[#2d4a3e] bg-green-50 font-medium">Required</Badge>
|
<Badge variant="outline" className="text-xs border-[#2d4a3e] text-[#2d4a3e] bg-green-50 font-medium">Required</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
{!isNonGst && (
|
||||||
<div className="text-[10px] text-gray-500 italic mt-0.5">
|
<div className="text-[10px] text-gray-500 italic mt-0.5">
|
||||||
Tax fields are automatically toggled based on the dealer's state (Inter-state vs Intra-state).
|
Tax fields are automatically toggled based on the dealer's state (Inter-state vs Intra-state).
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddCostItem}
|
onClick={handleAddCostItem}
|
||||||
@ -844,7 +852,7 @@ export function DealerProposalSubmissionModal({
|
|||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{/* Row 1: Description and Close */}
|
{/* Row 1: Description and Close */}
|
||||||
<div className="flex gap-3 items-start">
|
<div className="flex gap-3 items-start">
|
||||||
<div className="flex-1">
|
<div className={`${isNonGst ? 'flex-[3]' : 'flex-1'}`}>
|
||||||
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Item Description</Label>
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Item Description</Label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="e.g., Venue branding, Logistics, etc."
|
placeholder="e.g., Venue branding, Logistics, etc."
|
||||||
@ -853,6 +861,20 @@ export function DealerProposalSubmissionModal({
|
|||||||
className="w-full bg-white shadow-sm"
|
className="w-full bg-white shadow-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{isNonGst && (
|
||||||
|
<div className="flex-1 min-w-[140px]">
|
||||||
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<IndianRupee className="absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400" />
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={item.amount || ''}
|
||||||
|
onChange={(e) => handleCostItemChange(item.id, 'amount', e.target.value)}
|
||||||
|
className="pl-8 bg-white shadow-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -865,7 +887,7 @@ export function DealerProposalSubmissionModal({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Row 2: Numeric Calculations - Optimization: Narrower widths for GST to save space */}
|
{!isNonGst && (
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-3 items-start">
|
<div className="flex flex-wrap gap-x-4 gap-y-3 items-start">
|
||||||
<div className="flex-1 min-w-[140px]">
|
<div className="flex-1 min-w-[140px]">
|
||||||
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount (Base)</Label>
|
<Label className="text-[10px] uppercase text-gray-500 font-bold mb-1 block">Amount (Base)</Label>
|
||||||
@ -939,8 +961,9 @@ export function DealerProposalSubmissionModal({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Item Summary Row */}
|
{!isNonGst && (
|
||||||
<div className="flex flex-wrap gap-4 pt-3 border-t border-dashed items-center justify-between text-xs">
|
<div className="flex flex-wrap gap-4 pt-3 border-t border-dashed items-center justify-between text-xs">
|
||||||
<div className="flex gap-4 text-gray-500 font-medium">
|
<div className="flex gap-4 text-gray-500 font-medium">
|
||||||
<span>CGST: <span className="text-gray-900">₹{item.cgstAmt.toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>
|
<span>CGST: <span className="text-gray-900">₹{item.cgstAmt.toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span></span>
|
||||||
@ -953,6 +976,7 @@ export function DealerProposalSubmissionModal({
|
|||||||
<span className="text-sm font-bold text-[#2d4a3e] bg-green-50 px-3 py-1 rounded-lg">Item Total: ₹{item.totalAmt.toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span>
|
<span className="text-sm font-bold text-[#2d4a3e] bg-green-50 px-3 py-1 rounded-lg">Item Total: ₹{item.totalAmt.toLocaleString('en-IN', { minimumFractionDigits: 1 })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -966,7 +990,7 @@ export function DealerProposalSubmissionModal({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-white/70 uppercase font-bold tracking-wider">Estimated Total Budget</p>
|
<p className="text-xs text-white/70 uppercase font-bold tracking-wider">Estimated Total Budget</p>
|
||||||
<p className="text-sm text-white/90">Inclusive of all applicable taxes</p>
|
{!isNonGst && <p className="text-sm text-white/90">Inclusive of all applicable taxes</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-3xl font-bold border-l border-white/20 pl-6">
|
<div className="text-3xl font-bold border-l border-white/20 pl-6">
|
||||||
|
|||||||
@ -37,6 +37,7 @@ interface DeptLeadIOApprovalModalProps {
|
|||||||
preFilledIONumber?: string;
|
preFilledIONumber?: string;
|
||||||
preFilledBlockedAmount?: number;
|
preFilledBlockedAmount?: number;
|
||||||
preFilledRemainingBalance?: number;
|
preFilledRemainingBalance?: number;
|
||||||
|
taxationType?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DeptLeadIOApprovalModal({
|
export function DeptLeadIOApprovalModal({
|
||||||
@ -49,11 +50,16 @@ export function DeptLeadIOApprovalModal({
|
|||||||
preFilledIONumber,
|
preFilledIONumber,
|
||||||
preFilledBlockedAmount,
|
preFilledBlockedAmount,
|
||||||
preFilledRemainingBalance,
|
preFilledRemainingBalance,
|
||||||
|
taxationType,
|
||||||
}: DeptLeadIOApprovalModalProps) {
|
}: DeptLeadIOApprovalModalProps) {
|
||||||
const [actionType, setActionType] = useState<'approve' | 'reject'>('approve');
|
const [actionType, setActionType] = useState<'approve' | 'reject'>('approve');
|
||||||
const [comments, setComments] = useState('');
|
const [comments, setComments] = useState('');
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const isNonGst = useMemo(() => {
|
||||||
|
return taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
}, [taxationType]);
|
||||||
|
|
||||||
// Get IO number from props (read-only, from IO table)
|
// Get IO number from props (read-only, from IO table)
|
||||||
const ioNumber = preFilledIONumber || '';
|
const ioNumber = preFilledIONumber || '';
|
||||||
|
|
||||||
@ -138,8 +144,13 @@ export function DeptLeadIOApprovalModal({
|
|||||||
<CircleCheckBig className="w-5 h-5 lg:w-6 lg:h-6 text-green-600" />
|
<CircleCheckBig className="w-5 h-5 lg:w-6 lg:h-6 text-green-600" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<DialogTitle className="font-semibold text-lg lg:text-xl">
|
<DialogTitle className="font-semibold text-lg lg:text-xl flex items-center gap-2 flex-wrap">
|
||||||
Review and Approve
|
Review and Approve
|
||||||
|
{taxationType && (
|
||||||
|
<Badge className={`ml-2 border-none shadow-sm ${!isNonGst ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
|
{!isNonGst ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-xs lg:text-sm mt-1">
|
<DialogDescription className="text-xs lg:text-sm mt-1">
|
||||||
Review IO details and provide your approval comments
|
Review IO details and provide your approval comments
|
||||||
@ -174,8 +185,7 @@ export function DeptLeadIOApprovalModal({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setActionType('approve')}
|
onClick={() => setActionType('approve')}
|
||||||
className={`flex-1 text-sm lg:text-base ${
|
className={`flex-1 text-sm lg:text-base ${actionType === 'approve'
|
||||||
actionType === 'approve'
|
|
||||||
? 'bg-green-600 text-white shadow-sm'
|
? 'bg-green-600 text-white shadow-sm'
|
||||||
: 'text-gray-700 hover:bg-gray-200'
|
: 'text-gray-700 hover:bg-gray-200'
|
||||||
}`}
|
}`}
|
||||||
@ -187,8 +197,7 @@ export function DeptLeadIOApprovalModal({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setActionType('reject')}
|
onClick={() => setActionType('reject')}
|
||||||
className={`flex-1 text-sm lg:text-base ${
|
className={`flex-1 text-sm lg:text-base ${actionType === 'reject'
|
||||||
actionType === 'reject'
|
|
||||||
? 'bg-red-600 text-white shadow-sm'
|
? 'bg-red-600 text-white shadow-sm'
|
||||||
: 'text-gray-700 hover:bg-gray-200'
|
: 'text-gray-700 hover:bg-gray-200'
|
||||||
}`}
|
}`}
|
||||||
@ -309,8 +318,7 @@ export function DeptLeadIOApprovalModal({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={!isFormValid || submitting}
|
disabled={!isFormValid || submitting}
|
||||||
className={`text-sm lg:text-base ${
|
className={`text-sm lg:text-base ${actionType === 'approve'
|
||||||
actionType === 'approve'
|
|
||||||
? 'bg-green-600 hover:bg-green-700'
|
? 'bg-green-600 hover:bg-green-700'
|
||||||
: 'bg-red-600 hover:bg-red-700'
|
: 'bg-red-600 hover:bg-red-700'
|
||||||
} text-white`}
|
} text-white`}
|
||||||
|
|||||||
@ -88,6 +88,10 @@ export function InitiatorProposalApprovalModal({
|
|||||||
previousProposalData,
|
previousProposalData,
|
||||||
taxationType,
|
taxationType,
|
||||||
}: InitiatorProposalApprovalModalProps) {
|
}: InitiatorProposalApprovalModalProps) {
|
||||||
|
const isNonGst = useMemo(() => {
|
||||||
|
return taxationType === 'Non GST' || taxationType === 'Non-GST';
|
||||||
|
}, [taxationType]);
|
||||||
|
|
||||||
const [comments, setComments] = useState('');
|
const [comments, setComments] = useState('');
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [actionType, setActionType] = useState<'approve' | 'reject' | 'revision' | null>(null);
|
const [actionType, setActionType] = useState<'approve' | 'reject' | 'revision' | null>(null);
|
||||||
@ -280,18 +284,20 @@ export function InitiatorProposalApprovalModal({
|
|||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent className="dealer-proposal-modal overflow-hidden flex flex-col">
|
<DialogContent className="dealer-proposal-modal overflow-hidden flex flex-col">
|
||||||
<DialogHeader className="flex-shrink-0 pb-3 lg:pb-4 px-6 pt-4 lg:pt-6 border-b">
|
<DialogHeader className="flex-shrink-0 pb-3 lg:pb-4 px-6 pt-4 lg:pt-6 border-b">
|
||||||
<DialogTitle className="flex items-center gap-2 text-lg lg:text-xl">
|
<DialogTitle className="flex items-center gap-2 text-lg lg:text-xl flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<CheckCircle className="w-4 h-4 lg:w-5 lg:h-5 text-green-600" />
|
<CheckCircle className="w-4 h-4 lg:w-5 lg:h-5 text-green-600" />
|
||||||
Requestor Evaluation & Confirmation
|
Requestor Evaluation & Confirmation
|
||||||
|
</div>
|
||||||
|
{taxationType && (
|
||||||
|
<Badge className={`ml-2 border-none shadow-sm ${!isNonGst ? 'bg-emerald-600 text-white hover:bg-emerald-700' : 'bg-indigo-600 text-white hover:bg-indigo-700'}`}>
|
||||||
|
{!isNonGst ? 'GST Claim' : 'Non-GST Claim'}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-xs lg:text-sm">
|
<DialogDescription className="text-xs lg:text-sm">
|
||||||
Step 2: Review dealer proposal and make a decision
|
Step 2: Review dealer proposal and make a decision
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
{taxationType && (
|
|
||||||
<Badge className={`mt-1 mb-1 w-fit ${taxationType === 'GST' ? 'bg-[#2d4a3e] text-white hover:bg-[#2d4a3e]/90' : 'bg-slate-200 text-slate-800 hover:bg-slate-200/90'}`}>
|
|
||||||
{taxationType === 'GST' ? 'GST Claim' : 'Non-GST Claim'}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
<div className="space-y-1 mt-2 text-xs text-gray-600">
|
<div className="space-y-1 mt-2 text-xs text-gray-600">
|
||||||
<div className="flex flex-wrap gap-x-4 gap-y-1">
|
<div className="flex flex-wrap gap-x-4 gap-y-1">
|
||||||
<div>
|
<div>
|
||||||
@ -615,26 +621,28 @@ export function InitiatorProposalApprovalModal({
|
|||||||
<>
|
<>
|
||||||
<div className="border rounded-lg overflow-hidden max-h-[200px] lg:max-h-[180px] overflow-y-auto">
|
<div className="border rounded-lg overflow-hidden max-h-[200px] lg:max-h-[180px] overflow-y-auto">
|
||||||
<div className="bg-gray-50 px-3 lg:px-4 py-2 border-b sticky top-0">
|
<div className="bg-gray-50 px-3 lg:px-4 py-2 border-b sticky top-0">
|
||||||
<div className="grid grid-cols-4 gap-4 text-xs lg:text-sm font-semibold text-gray-700">
|
<div className={`grid ${isNonGst ? 'grid-cols-3' : 'grid-cols-4'} gap-4 text-xs lg:text-sm font-semibold text-gray-700`}>
|
||||||
<div className="col-span-1">Item Description</div>
|
<div className="col-span-1">Item Description</div>
|
||||||
<div className="text-right">Base</div>
|
<div className="text-right">Base</div>
|
||||||
<div className="text-right">GST</div>
|
{!isNonGst && <div className="text-right">GST</div>}
|
||||||
<div className="text-right">Total</div>
|
<div className="text-right">Total</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="divide-y">
|
<div className="divide-y">
|
||||||
{costBreakup.map((item: any, index: number) => (
|
{costBreakup.map((item: any, index: number) => (
|
||||||
<div key={item?.id || item?.description || index} className="px-3 lg:px-4 py-2 lg:py-3 grid grid-cols-4 gap-4">
|
<div key={item?.id || item?.description || index} className={`px-3 lg:px-4 py-2 lg:py-3 grid ${isNonGst ? 'grid-cols-3' : 'grid-cols-4'} gap-4`}>
|
||||||
<div className="col-span-1 text-xs lg:text-sm text-gray-700">
|
<div className="col-span-1 text-xs lg:text-sm text-gray-700">
|
||||||
{item?.description || 'N/A'}
|
{item?.description || 'N/A'}
|
||||||
{item?.gstRate ? <span className="block text-[10px] text-gray-400">{item.gstRate}% GST</span> : null}
|
{!isNonGst && item?.gstRate ? <span className="block text-[10px] text-gray-400">{item.gstRate}% GST</span> : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs lg:text-sm text-gray-900 text-right">
|
<div className="text-xs lg:text-sm text-gray-900 text-right">
|
||||||
₹{(Number(item?.amount) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
₹{(Number(item?.amount) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||||
</div>
|
</div>
|
||||||
|
{!isNonGst && (
|
||||||
<div className="text-xs lg:text-sm text-gray-900 text-right">
|
<div className="text-xs lg:text-sm text-gray-900 text-right">
|
||||||
₹{(Number(item?.gstAmt) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
₹{(Number(item?.gstAmt) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="text-xs lg:text-sm font-semibold text-gray-900 text-right">
|
<div className="text-xs lg:text-sm font-semibold text-gray-900 text-right">
|
||||||
₹{(Number(item?.totalAmt || ((item?.amount || 0) * (item?.quantity || 1) + (item?.gstAmt || 0))) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
₹{(Number(item?.totalAmt || ((item?.amount || 0) * (item?.quantity || 1) + (item?.gstAmt || 0))) || 0).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user