Files
frontend/components/organisms/modals/CashPaymentModal.tsx
T
Thanh Quy - wolf dae381d182 Enhance Atoms and Layout Documentation
- Updated ATOMS.md to include new LoginInput and ErrorMessageLogin components with detailed usage examples and props descriptions.
- Adjusted the status and created date in ATOMS.md.
- Refined the layout and styling descriptions in LAYOUTS.md for Header and Footer components, including responsive behavior and accessibility improvements.
- Added new sections for WiFi card and social links in the Footer documentation.
- Improved code formatting and consistency across various components in the codebase.
2026-04-18 11:13:53 +07:00

145 lines
4.8 KiB
TypeScript

"use client";
import Button from "@/components/atoms/buttons/Button";
import { useEffect, useState } from "react";
import type { CashPaymentModalProps } from "./Modal.types";
const formatPrice = (value: number) =>
value.toLocaleString("vi-VN", { style: "currency", currency: "VND" });
export default function CashPaymentModal({
isOpen,
onClose,
onConfirm,
items,
totalAmount,
}: CashPaymentModalProps) {
const [cashReceived, setCashReceived] = useState("");
useEffect(() => {
if (isOpen) setCashReceived("");
}, [isOpen]);
if (!isOpen) return null;
const parsedCash = cashReceived === "" ? 0 : Number(cashReceived);
const isInsufficient = cashReceived !== "" && parsedCash < totalAmount;
const changeAmount = parsedCash - totalAmount;
const canConfirm = cashReceived !== "" && !isInsufficient && parsedCash >= 0;
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center p-4"
role="dialog"
aria-modal="true"
aria-labelledby="cash-modal-title"
>
<div
className="absolute inset-0 bg-black/50 backdrop-blur-sm"
aria-hidden="true"
/>
<div className="relative w-full max-w-lg rounded-2xl border border-(--color-border-light) bg-white p-6 shadow-xl">
<h2
id="cash-modal-title"
className="text-foreground mb-4 text-xl font-bold"
>
Thanh toán tiền mặt
</h2>
{/* Invoice list */}
<div className="mb-4">
<h3 className="mb-2 text-sm font-semibold tracking-wide text-(--color-text-secondary) uppercase">
Hóa đơn
</h3>
<div className="overflow-hidden rounded-xl border border-(--color-border-light)">
{items.map((item, index) => (
<div
key={index}
className="flex items-center justify-between border-b border-(--color-border-light) px-4 py-2.5 last:border-b-0"
>
<span className="text-foreground font-medium">{item.name}</span>
<div className="flex items-center gap-3">
<span className="text-sm text-(--color-text-muted)">
x{item.quantity}
</span>
<span className="font-semibold text-(--color-primary)">
{formatPrice(item.price * item.quantity)}
</span>
</div>
</div>
))}
<div className="flex items-center justify-between bg-(--color-border-light)/30 px-4 py-3">
<span className="text-foreground font-bold">Tổng cộng</span>
<span className="text-lg font-bold text-(--color-primary)">
{formatPrice(totalAmount)}
</span>
</div>
</div>
</div>
{/* Cash input section */}
<div className="mb-5">
<label
htmlFor="cash-input"
className="mb-1.5 block text-sm font-medium text-(--color-text-secondary)"
>
Số tiền nhận
</label>
<input
id="cash-input"
type="number"
min="0"
step={1000}
value={cashReceived}
onChange={(e) => setCashReceived(e.target.value)}
placeholder="0"
className="w-full rounded-lg border border-(--color-border) bg-transparent px-3 py-2 text-right text-lg font-semibold focus:ring-2 focus:ring-(--color-primary) focus:outline-none"
/>
{isInsufficient && (
<p className="mt-1.5 text-sm text-red-500">
<i className="fa-solid fa-triangle-exclamation mr-1"></i>
Số tiền không đủ
</p>
)}
{cashReceived !== "" && !isInsufficient && (
<div className="mt-2 flex items-center justify-between rounded-lg bg-(--color-border-light)/40 px-3 py-2">
<span className="text-sm text-(--color-text-secondary)">
Tiền thối
</span>
<span className="font-semibold text-(--color-primary)">
{formatPrice(changeAmount)}
</span>
</div>
)}
</div>
{/* Footer buttons */}
<div className="flex gap-3">
<Button
type="button"
variant="secondary"
size="md"
icon="fa-solid fa-arrow-left"
className="flex-1"
onClick={onClose}
>
Quay lại
</Button>
<Button
type="button"
variant="primary"
size="md"
icon="fa-solid fa-check"
className="flex-1"
onClick={onConfirm}
disabled={!canConfirm}
>
Xác nhận
</Button>
</div>
</div>
</div>
);
}