Files
ERP/frontend/src/pages/Dashboard/ArriveModal.tsx
T

133 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useMemo, useState } from "react";
import { toast } from "sonner";
import { Package } from "lucide-react";
import type { Quote } from "../../lib/types";
import { useMaterials, useQuotes, useSuppliers, useUpdateQuote } from "../../hooks";
import { activeItems, materialById, materialSpecs, supplierById } from "../../lib/quoteUtils";
import { fmtNum } from "../../lib/formatters";
import { groupCommas } from "../../lib/numutil";
import { Button } from "../../components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../../components/ui/dialog";
import { JalaliDatePicker } from "../../components/JalaliDatePicker";
export interface ArriveModalProps {
quoteId: string | null;
onClose: () => void;
}
// مودال رسید: تیک بزنید کدام قلم‌ها رسیده‌اند؛ تا همه نرسیده باشند quote پذیرفته می‌ماند
export function ArriveModal({ quoteId, onClose }: ArriveModalProps) {
const quotes = useQuotes();
const suppliers = useSuppliers();
const materials = useMaterials();
const updateQuote = useUpdateQuote();
const q = useMemo(() => quotes.find((x) => x.id === quoteId) || null, [quotes, quoteId]);
const [checked, setChecked] = useState<Set<string>>(new Set());
// تاریخ و زمان رسید — پیش‌فرض الان
const [arriveDate, setArriveDate] = useState<string | null>(() => new Date().toISOString());
const [ready, setReady] = useState(false);
useMemo(() => {
if (!q) return;
const set = new Set<string>();
activeItems(q).forEach((li) => {
if (li.arrived_date) set.add(li.id);
});
setChecked(set);
setReady(true);
}, [q]);
if (!q || !ready) return null;
const sup = supplierById(suppliers, q.supplier_id);
const items = activeItems(q);
const confirm = () => {
if (!checked.size) {
toast.error("حداقل یک قلم را برای رسید انتخاب کنید.");
return;
}
const next: Quote = JSON.parse(JSON.stringify(q));
const arrivedIso = arriveDate || new Date().toISOString();
const now = new Date().toISOString();
next.line_items.forEach((li) => {
li.arrived_date = checked.has(li.id) ? arrivedIso : null;
});
if (activeItems(next).every((li) => li.arrived_date)) {
next.status = "arrived";
next.arrived_date = arrivedIso;
next.events.push({ type: "arrived", occurred_at: now });
} else {
next.arrived_date = null;
}
void updateQuote.mutateAsync({ id: q.id, patch: next });
setReady(false);
onClose();
};
return (
<Dialog open={!!quoteId} onOpenChange={(o) => !o && onClose()}>
<DialogContent>
<DialogHeader>
<DialogTitle>ثبت رسید — {sup.name}</DialogTitle>
</DialogHeader>
<div className="text-[12px] text-muted">
کدام قلم‌ها رسیده‌اند؟ تیک بزنید و ثبت کنید. تا وقتی همه‌ی قلم‌ها نرسیده‌اند، پیشنهاد
«پذیرفته شده» می‌ماند.
</div>
<div className="flex flex-col gap-1.5">
{items.map((li) => {
const m = materialById(materials, li.material_id);
const specText = materialSpecs(m).join(" · ");
return (
<label key={li.id} className={`arrive-row${li.arrived_date ? " arrived" : ""}`}>
<input
type="checkbox"
className="accent-green"
checked={checked.has(li.id)}
onChange={(e) => {
const next = new Set(checked);
if (e.target.checked) next.add(li.id);
else next.delete(li.id);
setChecked(next);
}}
/>
<span>
<Package className="me-1 inline h-3.5 w-3.5 text-muted" />
{m.category} {m.brand}
{specText ? ` · ${specText}` : ""}
</span>
<span className="arrive-meta">
{fmtNum(li.quantity)} {li.unit || ""} × {groupCommas(li.unit_price, true)}
</span>
</label>
);
})}
</div>
{/* تاریخ و زمان رسید */}
<div className="field max-w-xs">
<label>تاریخ و زمان رسید</label>
<JalaliDatePicker value={arriveDate} onChange={setArriveDate} placeholder="تاریخ و ساعت" withTime />
</div>
<DialogFooter>
<Button variant="slate" onClick={onClose}>
انصراف
</Button>
<Button variant="green" onClick={confirm}>
ثبت رسید
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}