修正日期時區偏移導致顯示少一天的問題
This commit is contained in:
@@ -15,6 +15,7 @@ import { SearchableSelect } from "@/Components/ui/searchable-select";
|
|||||||
import { useForm } from "@inertiajs/react";
|
import { useForm } from "@inertiajs/react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Calendar } from "lucide-react";
|
import { Calendar } from "lucide-react";
|
||||||
|
import { getCurrentDate } from "@/utils/format";
|
||||||
|
|
||||||
export interface UtilityFee {
|
export interface UtilityFee {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -51,7 +52,7 @@ export default function UtilityFeeDialog({
|
|||||||
availableCategories,
|
availableCategories,
|
||||||
}: UtilityFeeDialogProps) {
|
}: UtilityFeeDialogProps) {
|
||||||
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
|
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
|
||||||
transaction_date: new Date().toISOString().split("T")[0],
|
transaction_date: getCurrentDate(),
|
||||||
category: "",
|
category: "",
|
||||||
amount: "",
|
amount: "",
|
||||||
invoice_number: "",
|
invoice_number: "",
|
||||||
@@ -74,7 +75,7 @@ export default function UtilityFeeDialog({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reset();
|
reset();
|
||||||
setData("transaction_date", new Date().toISOString().split("T")[0]);
|
setData("transaction_date", getCurrentDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [open, fee]);
|
}, [open, fee]);
|
||||||
@@ -83,6 +84,12 @@ export default function UtilityFeeDialog({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (fee) {
|
if (fee) {
|
||||||
|
// Validate invoice number format if present
|
||||||
|
if (data.invoice_number && !/^[A-Z]{2}-\d{8}$/.test(data.invoice_number)) {
|
||||||
|
toast.error("發票號碼格式錯誤,應為:AB-12345678");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
put(route("utility-fees.update", fee.id), {
|
put(route("utility-fees.update", fee.id), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("紀錄已更新");
|
toast.success("紀錄已更新");
|
||||||
@@ -94,6 +101,12 @@ export default function UtilityFeeDialog({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Validate invoice number format if present
|
||||||
|
if (data.invoice_number && !/^[A-Z]{2}-\d{8}$/.test(data.invoice_number)) {
|
||||||
|
toast.error("發票號碼格式錯誤,應為:AB-12345678");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
post(route("utility-fees.store"), {
|
post(route("utility-fees.store"), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast.success("公共事業費已記錄");
|
toast.success("公共事業費已記錄");
|
||||||
@@ -124,13 +137,13 @@ export default function UtilityFeeDialog({
|
|||||||
費用日期 <span className="text-red-500">*</span>
|
費用日期 <span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400" />
|
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
|
||||||
<Input
|
<Input
|
||||||
id="transaction_date"
|
id="transaction_date"
|
||||||
type="date"
|
type="date"
|
||||||
value={data.transaction_date}
|
value={data.transaction_date}
|
||||||
onChange={(e) => setData("transaction_date", e.target.value)}
|
onChange={(e) => setData("transaction_date", e.target.value)}
|
||||||
className={`pl-9 ${errors.transaction_date ? "border-red-500" : ""}`}
|
className={`pl-9 block w-full ${errors.transaction_date ? "border-red-500" : ""}`}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -176,26 +189,10 @@ export default function UtilityFeeDialog({
|
|||||||
<Input
|
<Input
|
||||||
id="invoice_number"
|
id="invoice_number"
|
||||||
value={data.invoice_number}
|
value={data.invoice_number}
|
||||||
onChange={(e) => {
|
onChange={(e) => setData("invoice_number", e.target.value)}
|
||||||
let value = e.target.value.toUpperCase();
|
|
||||||
// Remove non-alphanumeric chars
|
|
||||||
const raw = value.replace(/[^A-Z0-9]/g, '');
|
|
||||||
|
|
||||||
// Auto-insert hyphen after 2 chars if we have length > 2
|
|
||||||
if (raw.length > 2) {
|
|
||||||
value = `${raw.slice(0, 2)}-${raw.slice(2)}`;
|
|
||||||
} else {
|
|
||||||
value = raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit max length (2 letters + 8 digits + 1 hyphen = 11 chars)
|
|
||||||
if (value.length > 11) value = value.slice(0, 11);
|
|
||||||
|
|
||||||
setData("invoice_number", value);
|
|
||||||
}}
|
|
||||||
placeholder="例:AB-12345678"
|
placeholder="例:AB-12345678"
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-gray-500">格式:AB-12345678 (系統自動格式化)</p>
|
<p className="text-xs text-gray-500">格式範例:AB-12345678</p>
|
||||||
{errors.invoice_number && <p className="text-sm text-red-500">{errors.invoice_number}</p>}
|
{errors.invoice_number && <p className="text-sm text-red-500">{errors.invoice_number}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export default function UtilityFeeIndex({ fees, availableCategories, filters }:
|
|||||||
|
|
||||||
{/* Date Range Start */}
|
{/* Date Range Start */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400" />
|
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
|
||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
value={dateStart}
|
value={dateStart}
|
||||||
@@ -244,7 +244,7 @@ export default function UtilityFeeIndex({ fees, availableCategories, filters }:
|
|||||||
|
|
||||||
{/* Date Range End */}
|
{/* Date Range End */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400" />
|
<Calendar className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-400 pointer-events-none" />
|
||||||
<Input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
value={dateEnd}
|
value={dateEnd}
|
||||||
|
|||||||
@@ -21,7 +21,17 @@ export const formatCurrency = (num: number): string => {
|
|||||||
*/
|
*/
|
||||||
export const formatDate = (date: string): string => {
|
export const formatDate = (date: string): string => {
|
||||||
if (!date) return "-";
|
if (!date) return "-";
|
||||||
return new Date(date).toLocaleDateString("zh-TW");
|
const datePart = date.split("T")[0].split(" ")[0];
|
||||||
|
const parts = datePart.split("-").map(Number);
|
||||||
|
if (parts.length < 3 || parts.some(isNaN)) return date;
|
||||||
|
|
||||||
|
const [y, m, d] = parts;
|
||||||
|
const dt = new Date(y, m - 1, d, 12, 0, 0);
|
||||||
|
const year = dt.getFullYear();
|
||||||
|
const month = String(dt.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(dt.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}/${month}/${day}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,12 +39,19 @@ export const formatDate = (date: string): string => {
|
|||||||
*/
|
*/
|
||||||
export const formatDateWithDayOfWeek = (date: string): string => {
|
export const formatDateWithDayOfWeek = (date: string): string => {
|
||||||
if (!date) return "-";
|
if (!date) return "-";
|
||||||
return new Date(date).toLocaleDateString("zh-TW", {
|
const datePart = date.split("T")[0].split(" ")[0];
|
||||||
year: "numeric",
|
const parts = datePart.split("-").map(Number);
|
||||||
month: "2-digit",
|
if (parts.length < 3 || parts.some(isNaN)) return date;
|
||||||
day: "2-digit",
|
|
||||||
weekday: "short",
|
const [y, m, d] = parts;
|
||||||
});
|
const dt = new Date(y, m - 1, d, 12, 0, 0);
|
||||||
|
|
||||||
|
const year = dt.getFullYear();
|
||||||
|
const month = String(dt.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(dt.getDate()).padStart(2, '0');
|
||||||
|
const weekDay = dt.toLocaleDateString("zh-TW", { weekday: "short" });
|
||||||
|
|
||||||
|
return `${year}/${month}/${day} (${weekDay})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +71,11 @@ export const formatInvoiceNumber = (invoice: string | null | undefined): string
|
|||||||
* 獲取當前日期(YYYY-MM-DD 格式)
|
* 獲取當前日期(YYYY-MM-DD 格式)
|
||||||
*/
|
*/
|
||||||
export const getCurrentDate = (): string => {
|
export const getCurrentDate = (): string => {
|
||||||
return new Date().toISOString().split("T")[0];
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(now.getDate()).padStart(2, "0");
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user