refactor: organized components based on domain

This commit is contained in:
2026-02-23 14:38:30 +03:30
parent 1f763a33ac
commit e7f4c55bfe
103 changed files with 1066 additions and 1066 deletions

View File

@@ -0,0 +1,548 @@
import { Controller, useForm } from "react-hook-form";
import { Grid } from "../../../components/Grid/Grid";
import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import {
zValidateAutoComplete,
zValidateAutoCompleteOptional,
zValidateNumber,
zValidateString,
} from "../../../data/getFormTypeErrors";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { getMonthsList } from "../../../data/getMonths";
import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useEffect, useRef, useState } from "react";
import ToggleButton from "../../../components/ToggleButton/ToggleButton";
import Typography from "../../../components/Typography/Typography";
import Textfield from "../../../components/Textfeild/Textfeild";
import { useApiRequest } from "../../../utils/useApiRequest";
type Props = {
item: any;
getData: () => void;
onSubmit: (data: any) => void;
setFormRef: (ref: HTMLFormElement | null) => void;
visible: boolean;
};
const saleTypes = [
{ value: "gov", label: "دولتی" },
{ value: "free", label: "آزاد" },
];
const groupTypes = [
{ value: "روستایی", key: "rural", disabled: false },
{ value: "صنعتی", key: "industrial", disabled: false },
{ value: "عشایری", key: "nomadic", disabled: true },
];
const posSaleTypes = [
{ value: "هردو", key: "all", disabled: false },
{ value: "بر اساس وزن", key: "weight", disabled: false },
{ value: "بر اساس تعداد راس دام", key: "count", disabled: false },
];
export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const [hasDistributionLimit, setHasDistributionLimit] = useState(
item?.distribution_mode?.length ? true : false,
);
const internalRef = useRef<HTMLFormElement>(null);
const [livestockTypes, setLivestockTypes] = useState<
Array<{
livestock_group: string;
livestock_type: any;
weight_type: any;
quantity_kg: number;
fa: string;
}>
>([]);
useEffect(() => {
if (visible) {
setFormRef(internalRef.current);
}
}, [visible]);
const schema = z.object({
product: zValidateNumber("محصول"),
month_choices: zValidateAutoComplete("سهمیه ماه"),
distribution_mode: hasDistributionLimit
? zValidateAutoComplete("محدودیت توزیع دوره")
: zValidateAutoCompleteOptional(),
sale_license: zValidateAutoComplete("مجوز فروش"),
pos_sale_type: zValidateString("نوع فروش در دستگاه"),
sale_type: zValidateString("نوع فروش"),
group: zValidateAutoComplete("گروه"),
quota_weight: zValidateNumber("وزن سهمیه"),
sale_unit: zValidateNumber("نوع مولفه"),
});
type FormValues = z.infer<typeof schema>;
const {
control,
handleSubmit,
setValue,
getValues,
trigger,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
product: item?.product ? item?.product?.product_id : "",
month_choices: item?.month_choices || [],
distribution_mode: item?.distribution_mode || [],
sale_license: item?.sale_license || [],
sale_type: item?.sale_type || "gov",
group: item?.group || [],
pos_sale_type: item?.pos_sale_type || "all",
quota_weight: item?.quota_weight || 0,
sale_unit: item?.sale_unit ? item?.sale_unit?.id : "",
},
});
const { data: livestockData } = useApiRequest({
api: "/livestock/web/api/v1/livestock_type/",
method: "get",
params: { page: 1, page_size: 100 },
queryKey: ["livestockTypes"],
});
useEffect(() => {
if (livestockData?.results) {
const getQuantity = (allocate: any, group: string) => {
const result = item?.livestock_allocations?.find(
(option: any) =>
option?.livestock_type?.weight_type === allocate?.weight_type &&
option?.livestock_group === group &&
option?.livestock_type?.name === allocate?.name,
);
return result?.quantity_kg || 0;
};
const formattedData = livestockData.results.flatMap((item: any) => [
{
livestock_group: "rural",
livestock_type: item.id,
weight_type: item.weight_type,
quantity_kg: getQuantity(item, "rural"),
fa: item.name,
},
{
livestock_group: "industrial",
livestock_type: item.id,
weight_type: item.weight_type,
quantity_kg: getQuantity(item, "industrial"),
fa: item.name,
},
]);
setLivestockTypes(formattedData);
}
}, [livestockData, item]);
const handleQuantityChange = (index: number, value: number) => {
setLivestockTypes((prev) => {
const newTypes = [...prev];
newTypes[index] = { ...newTypes[index], quantity_kg: value };
return newTypes;
});
};
const findLivestockIndex = (
group: string,
weightType: string,
fa: string,
) => {
return livestockTypes.findIndex(
(item) =>
item.livestock_group === group &&
item.weight_type === weightType &&
item.fa === fa,
);
};
const handleSubmitForm = (data: FormValues) => {
onSubmit({ ...data, livestockTypes, hasDistributionLimit });
};
return (
<form ref={internalRef} onSubmit={handleSubmit(handleSubmitForm)}>
<Grid container column>
<Grid className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 mt-8 gap-4 items-start">
<Controller
name="product"
control={control}
render={() => (
<>
<FormApiBasedAutoComplete
defaultKey={item?.product?.product_id}
title="انتخاب محصول"
api={`product/web/api/v1/product`}
keyField="id"
valueField="name"
error={!!errors.product}
errorMessage={errors.product?.message}
onChange={(r) => {
setValue("product", r);
trigger("product");
}}
/>
</>
)}
/>
<Controller
name="quota_weight"
control={control}
render={({ field }) => (
<Textfield
formattedNumber
fullWidth
placeholder="وزن سهمیه"
value={field.value}
onChange={field.onChange}
error={!!errors.quota_weight}
helperText={errors.quota_weight?.message}
/>
)}
/>
<Controller
name="month_choices"
control={control}
render={({ field }) => (
<AutoComplete
data={getMonthsList}
selectField
selectedKeys={field.value}
multiselect
onChange={(keys: (string | number)[]) => {
setValue("month_choices", keys);
trigger("month_choices");
}}
error={!!errors.month_choices}
helperText={errors.month_choices?.message}
title="سهمیه ماه"
/>
)}
/>
<Controller
name="sale_license"
control={control}
render={({ field }) => (
<AutoComplete
selectField
data={getMonthsList}
selectedKeys={field.value}
multiselect
onChange={(keys: (string | number)[]) => {
setValue("sale_license", keys);
trigger("sale_license");
}}
error={!!errors.sale_license}
helperText={errors.sale_license?.message}
title="مجوز فروش"
/>
)}
/>
<Grid
container
className="p-2 border-1 rounded-lg items-center border-gray-200"
>
<Controller
name="sale_type"
control={control}
render={({ field }) => (
<RadioGroup
groupTitle="نوع فروش"
className="mr-2"
direction="row"
options={saleTypes}
value={field.value}
onChange={(e) => {
setValue("sale_type", e.target.value);
}}
/>
)}
/>
</Grid>
<Controller
name="group"
control={control}
render={({ field }) => (
<AutoComplete
selectField
data={groupTypes}
selectedKeys={field.value}
multiselect
onChange={(keys: (string | number)[]) => {
setValue("group", keys);
trigger("group");
}}
error={!!errors.group}
helperText={errors.group?.message}
title="گروه"
/>
)}
/>
<Controller
name="product"
control={control}
render={() => (
<>
<FormApiBasedAutoComplete
selectField
defaultKey={item?.sale_unit?.id}
title="نوع مولفه"
api={`product/web/api/v1/sale_unit`}
keyField="id"
valueField="unit"
error={!!errors.sale_unit}
errorMessage={errors.sale_unit?.message}
onChange={(r) => {
setValue("sale_unit", r);
trigger("sale_unit");
}}
/>
</>
)}
/>
<Grid
container
column
className="p-2 border-1 rounded-lg border-gray-200 items-start gap-2"
>
<ToggleButton
type="button"
isActive={hasDistributionLimit}
onClick={() => {
if (!hasDistributionLimit) {
setValue("distribution_mode", []);
trigger("distribution_mode");
}
setHasDistributionLimit(!hasDistributionLimit);
}}
title="محدودیت توزیع دوره"
aria-label="Toggle like"
/>
{hasDistributionLimit && (
<Controller
name="distribution_mode"
control={control}
render={({ field }) => (
<AutoComplete
data={getMonthsList}
selectedKeys={field.value || []}
multiselect
onChange={(keys: (string | number)[]) => {
setValue("distribution_mode", keys);
trigger("distribution_mode");
}}
error={!!errors.distribution_mode}
helperText={errors.distribution_mode?.message}
title="محدودیت توزیع دوره"
/>
)}
/>
)}
</Grid>
<Controller
name="pos_sale_type"
control={control}
render={({ field }) => (
<AutoComplete
selectField
data={posSaleTypes}
selectedKeys={[field.value]}
onChange={(keys: (string | number)[]) => {
setValue("pos_sale_type", keys.toString());
trigger("pos_sale_type");
}}
error={!!errors.pos_sale_type}
helperText={errors.pos_sale_type?.message}
title="نوع فروش در دستگاه"
/>
)}
/>
</Grid>
{getValues("group")?.includes("rural") && (
<>
<Typography
color="text-gray-600 dark:text-dark-100 my-4"
variant="body2"
>
سهمیه دام روستایی
</Typography>
<Grid className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start">
<Grid className="grid gap-2 p-2 border-1 border-gray-200 rounded-xl">
<Typography
color="text-gray-600 dark:text-dark-100"
className="flex justify-center select-none"
variant="caption"
>
سنگین
</Typography>
{livestockTypes
?.filter(
(option) =>
option.livestock_group === "rural" &&
option?.weight_type === "H",
)
.map((item, i) => {
const index = findLivestockIndex("rural", "H", item.fa);
return (
<Textfield
value={item.quantity_kg}
key={i}
start={item.fa}
end="کیلوگرم"
formattedNumber
onChange={(e) => {
const value =
parseInt(e.target.value.replace(/,/g, "")) || 0;
if (index !== -1) {
handleQuantityChange(index, value);
}
}}
/>
);
})}
</Grid>
<Grid className="grid gap-2 p-2 border-1 border-gray-200 rounded-xl">
<Typography
color="text-gray-600 dark:text-dark-100"
className="flex justify-center select-none"
variant="caption"
>
سبک
</Typography>
{livestockTypes
?.filter(
(option) =>
option.livestock_group === "rural" &&
option?.weight_type === "L",
)
.map((item, i) => {
const index = findLivestockIndex("rural", "L", item.fa);
return (
<Textfield
key={i}
start={item.fa}
end="کیلوگرم"
formattedNumber
value={item.quantity_kg}
onChange={(e) => {
const value =
parseInt(e.target.value.replace(/,/g, "")) || 0;
if (index !== -1) {
handleQuantityChange(index, value);
}
}}
/>
);
})}
</Grid>
</Grid>
</>
)}
{getValues("group")?.includes("industrial") && (
<>
<Typography
color="text-gray-600 dark:text-dark-100 my-4"
variant="body2"
>
سهمیه دام صنعتی
</Typography>
<Grid className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-4 items-start">
<Grid className="grid gap-2 p-2 border-1 border-gray-200 rounded-xl">
<Typography
color="text-gray-600 dark:text-dark-100"
className="flex justify-center select-none"
variant="caption"
>
سنگین
</Typography>
{livestockTypes
?.filter(
(option) =>
option.livestock_group === "industrial" &&
option?.weight_type === "H",
)
.map((item, i) => {
const index = findLivestockIndex(
"industrial",
"H",
item.fa,
);
return (
<Textfield
value={item.quantity_kg}
key={i}
start={item.fa}
end="کیلوگرم"
formattedNumber
onChange={(e) => {
const value =
parseInt(e.target.value.replace(/,/g, "")) || 0;
if (index !== -1) {
handleQuantityChange(index, value);
}
}}
/>
);
})}
</Grid>
<Grid className="grid gap-2 p-2 border-1 border-gray-200 rounded-xl">
<Typography
color="text-gray-600 dark:text-dark-100"
className="flex justify-center select-none"
variant="caption"
>
سبک
</Typography>
{livestockTypes
?.filter(
(option) =>
option.livestock_group === "industrial" &&
option?.weight_type === "L",
)
.map((item, i) => {
const index = findLivestockIndex(
"industrial",
"L",
item.fa,
);
return (
<Textfield
key={i}
start={item.fa}
end="کیلوگرم"
formattedNumber
value={item.quantity_kg}
onChange={(e) => {
const value =
parseInt(e.target.value.replace(/,/g, "")) || 0;
if (index !== -1) {
handleQuantityChange(index, value);
}
}}
/>
);
})}
</Grid>
</Grid>
</>
)}
</Grid>
</form>
);
};