[FEAT] 實作公共事業費逾期提醒、租戶自訂通知設定及發送測試信功能
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 56s

This commit is contained in:
2026-03-05 16:01:00 +08:00
parent 016366407c
commit 07b7d9b327
15 changed files with 519 additions and 44 deletions

View File

@@ -20,9 +20,11 @@ import { validateInvoiceNumber } from "@/utils/validation";
export interface UtilityFee {
id: number;
transaction_date: string;
transaction_date: string | null;
due_date: string;
category: string;
amount: number | string;
payment_status: 'pending' | 'paid' | 'overdue';
invoice_number?: string;
description?: string;
created_at: string;
@@ -53,7 +55,8 @@ export default function UtilityFeeDialog({
availableCategories,
}: UtilityFeeDialogProps) {
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
transaction_date: getCurrentDate(),
transaction_date: "",
due_date: getCurrentDate(),
category: "",
amount: "",
invoice_number: "",
@@ -68,7 +71,8 @@ export default function UtilityFeeDialog({
clearErrors();
if (fee) {
setData({
transaction_date: fee.transaction_date,
transaction_date: fee.transaction_date || "",
due_date: fee.due_date,
category: fee.category,
amount: fee.amount.toString(),
invoice_number: fee.invoice_number || "",
@@ -76,7 +80,14 @@ export default function UtilityFeeDialog({
});
} else {
reset();
setData("transaction_date", getCurrentDate());
setData({
transaction_date: "",
due_date: getCurrentDate(),
category: "",
amount: "",
invoice_number: "",
description: "",
});
}
}
}, [open, fee]);
@@ -131,22 +142,41 @@ export default function UtilityFeeDialog({
<form onSubmit={handleSubmit} className="space-y-4 py-2">
<div className="grid grid-cols-1 gap-4">
<div className="space-y-2">
<Label htmlFor="transaction_date">
<span className="text-red-500">*</span>
</Label>
<div className="relative">
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
<Input
id="transaction_date"
type="date"
value={data.transaction_date}
onChange={(e) => setData("transaction_date", e.target.value)}
className={`pl-9 block w-full ${errors.transaction_date ? "border-red-500" : ""}`}
required
/>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="due_date">
<span className="text-red-500">*</span>
</Label>
<div className="relative">
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
<Input
id="due_date"
type="date"
value={data.due_date}
onChange={(e) => setData("due_date", e.target.value)}
className={`pl-9 block w-full ${errors.due_date ? "border-red-500" : ""}`}
required
/>
</div>
{errors.due_date && <p className="text-sm text-red-500">{errors.due_date}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="transaction_date">
</Label>
<div className="relative">
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
<Input
id="transaction_date"
type="date"
value={data.transaction_date}
onChange={(e) => setData("transaction_date", e.target.value)}
className={`pl-9 block w-full ${errors.transaction_date ? "border-red-500" : ""}`}
/>
</div>
{errors.transaction_date && <p className="text-sm text-red-500">{errors.transaction_date}</p>}
</div>
{errors.transaction_date && <p className="text-sm text-red-500">{errors.transaction_date}</p>}
</div>
<div className="grid grid-cols-2 gap-4">