This commit is contained in:
2026-05-29 16:21:46 +03:30
parent 1140fbae3c
commit 9db796b8e1
12 changed files with 285 additions and 71 deletions
+72 -12
View File
@@ -1,14 +1,25 @@
import { type FC, type ChangeEvent } from "react";
import Input from "@/components/Input";
import SwitchComponent from "@/components/Switch";
import ProductsSelect from "@/pages/order/components/ProductsSelect";
import type { CreatePreInvoiceItemType } from "../types/Types";
import {
clampDiscountPercent,
clampDiscountValue,
getItemLineTotal,
getItemSubTotal,
} from "../utils/invoiceItem";
import { Add, Trash } from "iconsax-react";
import { Link } from "react-router-dom";
import { Paths } from "@/config/Paths";
type Props = {
item: CreatePreInvoiceItemType;
index: number;
onChange: (index: number, field: keyof CreatePreInvoiceItemType, value: string | number) => void;
onChange: (
index: number,
field: keyof CreatePreInvoiceItemType,
value: string | number | null,
) => void;
onAdd: () => void;
onRemove: () => void;
canRemove: boolean;
@@ -26,12 +37,25 @@ const InvoiceItemRow: FC<Props> = ({
onConvertToOrder,
error,
}) => {
const total = (item.quantity || 0) * (item.unitPrice || 0) - (item.discount || 0);
const total = getItemLineTotal(item);
const subTotal = getItemSubTotal(item);
const isPercentDiscount = item.discountPercent != null;
const handleProductChange = (e: ChangeEvent<HTMLSelectElement>) => {
onChange(index, "productId", e.target.value);
};
const handleDiscountTypeChange = (isPercent: boolean) => {
if (isPercent) {
onChange(index, "discount", 0);
onChange(index, "discountPercent", 0);
return;
}
onChange(index, "discountPercent", null);
onChange(index, "discount", 0);
};
return (
<div className="flex flex-wrap items-end gap-4 mt-6">
@@ -58,9 +82,14 @@ const InvoiceItemRow: FC<Props> = ({
type="number"
min={1}
value={item.quantity || ""}
onChange={(e) =>
onChange(index, "quantity", e.target.value ? Number(e.target.value) : 0)
}
onChange={(e) => {
const nextQuantity = e.target.value ? Number(e.target.value) : 0;
const nextSubTotal = nextQuantity * (item.unitPrice || 0);
onChange(index, "quantity", nextQuantity);
if (!isPercentDiscount) {
onChange(index, "discount", clampDiscountValue(item.discount, nextSubTotal));
}
}}
error_text={error?.quantity}
/>
</div>
@@ -74,23 +103,54 @@ const InvoiceItemRow: FC<Props> = ({
seprator
onChange={(e) => {
const raw = String(e.target.value).replace(/,/g, "");
onChange(index, "unitPrice", raw ? Number(raw) : 0);
const nextUnitPrice = raw ? Number(raw) : 0;
const nextSubTotal = (item.quantity || 0) * nextUnitPrice;
onChange(index, "unitPrice", nextUnitPrice);
if (!isPercentDiscount) {
onChange(index, "discount", clampDiscountValue(item.discount, nextSubTotal));
}
}}
error_text={error?.unitPrice}
/>
</div>
<div className="w-[120px]">
<div className="w-[150px]">
<div className="mb-1 flex items-center justify-between gap-2 text-sm">
<span>تخفیف </span>
<div className="flex items-center gap-1">
<span className={isPercentDiscount ? "text-[11px] text-black" : "text-[11px] text-[#888888]"}>
درصد
</span>
<SwitchComponent active={isPercentDiscount} onChange={handleDiscountTypeChange} />
<span className={!isPercentDiscount ? "text-[11px] text-black" : "text-[11px] text-[#888888]"}>
مبلغ
</span>
</div>
</div>
<Input
label="تخفیف"
type="text"
type={isPercentDiscount ? "number" : "text"}
inputMode="numeric"
value={item.discount ? String(item.discount) : ""}
seprator
min={0}
max={isPercentDiscount ? 100 : subTotal}
value={
isPercentDiscount
? item.discountPercent ? String(item.discountPercent) : ""
: item.discount ? String(item.discount) : ""
}
seprator={!isPercentDiscount}
onChange={(e) => {
const raw = String(e.target.value).replace(/,/g, "");
onChange(index, "discount", raw ? Number(raw) : 0);
const value = raw ? Number(raw) : 0;
if (isPercentDiscount) {
onChange(index, "discountPercent", clampDiscountPercent(value));
onChange(index, "discount", 0);
return;
}
onChange(index, "discount", clampDiscountValue(value, subTotal));
onChange(index, "discountPercent", null);
}}
unit={isPercentDiscount ? "٪" : undefined}
/>
</div>