refactor: organized components based on domain
This commit is contained in:
388
src/partials/LiveStock/live-stock/LiveStockAddHerd.tsx
Normal file
388
src/partials/LiveStock/live-stock/LiveStockAddHerd.tsx
Normal file
@@ -0,0 +1,388 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Button from "../../../components/Button/Button";
|
||||
import { Grid } from "../../../components/Grid/Grid";
|
||||
import Textfield from "../../../components/Textfeild/Textfeild";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import {
|
||||
zValidateNumber,
|
||||
zValidateNumberOptional,
|
||||
zValidateString,
|
||||
} from "../../../data/getFormTypeErrors";
|
||||
import { z } from "zod";
|
||||
import { useApiMutation } from "../../../utils/useApiRequest";
|
||||
import { useToast } from "../../../hooks/useToast";
|
||||
import { useDrawerStore } from "../../../context/zustand-store/appStore";
|
||||
import { getToastResponse } from "../../../data/getToastResponse";
|
||||
import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
|
||||
import { useState } from "react";
|
||||
import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
|
||||
import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
|
||||
import { useUserProfileStore } from "../../../context/zustand-store/userStore";
|
||||
|
||||
type AddPageProps = {
|
||||
getData: () => void;
|
||||
item?: any;
|
||||
rancher?: number | any;
|
||||
};
|
||||
|
||||
const activityTypes = [
|
||||
{ label: "روستایی", value: "V" },
|
||||
{ label: "صنعتی", value: "I" },
|
||||
{ label: "عشایری", value: "N" },
|
||||
];
|
||||
|
||||
const activityStateTypes = [
|
||||
{ label: "فعال", value: true },
|
||||
{ label: "غیر فعال", value: false },
|
||||
];
|
||||
|
||||
const operatingLicenseStateTypes = [
|
||||
{ label: "دارای مجوز", value: true },
|
||||
{ label: "بدون مجوز", value: false },
|
||||
];
|
||||
|
||||
export const LiveStockAddHerd = ({ getData, item, rancher }: AddPageProps) => {
|
||||
const { profile } = useUserProfileStore();
|
||||
|
||||
const schema = z.object({
|
||||
name: zValidateString("نام گله"),
|
||||
unit_unique_id: zValidateNumber("شناسه یکتا"),
|
||||
capacity: zValidateNumberOptional("ظرفیت"),
|
||||
code: zValidateNumber("کد گله"),
|
||||
heavy_livestock_number: zValidateNumberOptional("حجم دام سنگین"),
|
||||
light_livestock_number: zValidateNumberOptional("حجم دام سبک"),
|
||||
postal: zValidateNumberOptional("کد پستی"),
|
||||
institution: zValidateNumberOptional("کد موسسه"),
|
||||
epidemiologic: zValidateNumberOptional("کد اپیدمیولوژیک"),
|
||||
province: zValidateNumber("استان"),
|
||||
city: zValidateNumber("شهر"),
|
||||
cooperative:
|
||||
profile?.role?.type?.key === "J"
|
||||
? zValidateNumber("تعاونی")
|
||||
: zValidateNumberOptional("تعاونی"),
|
||||
contractor: zValidateNumberOptional("شرکت پیمانکار"),
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof schema>;
|
||||
|
||||
const showToast = useToast();
|
||||
const { closeDrawer } = useDrawerStore();
|
||||
|
||||
const [activityType, setActivityType] = useState(item?.activity || "V");
|
||||
|
||||
const [activityState, setActivityState] = useState(
|
||||
item ? item?.activity_state : true,
|
||||
);
|
||||
const [operatingLicenseState, setOperatingLicenseState] = useState(
|
||||
item ? item?.operating_license_state : true,
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
trigger,
|
||||
formState: { errors },
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: item?.name || "",
|
||||
capacity: item?.capacity || "",
|
||||
code: item?.code || "",
|
||||
unit_unique_id: item?.unit_unique_id || "",
|
||||
heavy_livestock_number: item?.heavy_livestock_number || "",
|
||||
light_livestock_number: item?.light_livestock_number || "",
|
||||
postal: item?.postal || "",
|
||||
institution: item?.institution || "",
|
||||
epidemiologic: item?.epidemiologic || "",
|
||||
province: item?.user?.province || "",
|
||||
city: item?.user?.city || "",
|
||||
},
|
||||
});
|
||||
|
||||
const mutation = useApiMutation({
|
||||
api: `/herd/web/api/v1/herd/${item ? item?.id + "/" : ""}`,
|
||||
method: item ? "put" : "post",
|
||||
});
|
||||
const onSubmit = async (data: FormValues) => {
|
||||
try {
|
||||
await mutation.mutateAsync({
|
||||
name: data?.name,
|
||||
capacity: data?.capacity,
|
||||
code: data?.code,
|
||||
unit_unique_id: data?.unit_unique_id,
|
||||
heavy_livestock_number: data?.heavy_livestock_number,
|
||||
light_livestock_number: data?.light_livestock_number,
|
||||
postal: data?.postal ?? "",
|
||||
institution: data?.institution ?? "",
|
||||
epidemiologic: data?.epidemiologic ?? "",
|
||||
province: data?.province,
|
||||
city: data?.city,
|
||||
|
||||
rancher: parseInt(rancher) ?? parseInt(rancher),
|
||||
operating_license_state: operatingLicenseState,
|
||||
activity: activityType,
|
||||
activity_state: activityState,
|
||||
...(data.contractor !== undefined && {
|
||||
contractor: data.contractor,
|
||||
}),
|
||||
...(data.cooperative !== undefined && {
|
||||
cooperative: data.cooperative,
|
||||
}),
|
||||
});
|
||||
showToast(getToastResponse(item, ""), "success");
|
||||
getData();
|
||||
closeDrawer();
|
||||
} catch (error: any) {
|
||||
if (error?.status === 403) {
|
||||
showToast(
|
||||
error?.response?.data?.message || "این مورد تکراری است!",
|
||||
"error",
|
||||
);
|
||||
} else {
|
||||
showToast(
|
||||
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container column className="gap-2">
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="نام گله"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="unit_unique_id"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="شناسه یکتا"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.unit_unique_id}
|
||||
helperText={errors.unit_unique_id?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{profile?.role?.type?.key === "J" && (
|
||||
<Controller
|
||||
name="cooperative"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
defaultKey={item?.cooperative?.id}
|
||||
title="تعاونی "
|
||||
api={`auth/api/v1/organization/child_organizations?search=تعاونی`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.cooperative}
|
||||
errorMessage={errors.cooperative?.message}
|
||||
onChange={(r) => {
|
||||
setValue("cooperative", r);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
name="contractor"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
defaultKey={item?.contractor?.id}
|
||||
title="شرکت پیمانکار (اختیاری)"
|
||||
api={`auth/api/v1/organization/child_organizations?search=شرکت`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.contractor}
|
||||
errorMessage={errors.contractor?.message}
|
||||
onChange={(r) => {
|
||||
setValue("contractor", r);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="province"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormEnterLocations
|
||||
cityValue={item?.city?.id}
|
||||
provinceValue={item?.province?.id}
|
||||
cityError={!!errors.city}
|
||||
provincError={!!errors.province}
|
||||
cityErrorMessage={errors.city?.message}
|
||||
provinceErrMessage={errors.province?.message}
|
||||
roleControlled
|
||||
onChange={async (locations) => {
|
||||
setValue("province", locations.province);
|
||||
setValue("city", locations.city);
|
||||
if (locations.province || locations.city)
|
||||
await trigger(["province", "city"]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="capacity"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
formattedNumber
|
||||
fullWidth
|
||||
placeholder="ظرفیت"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.capacity}
|
||||
helperText={errors.capacity?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="code"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="کد گله"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.code}
|
||||
helperText={errors.code?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="نوع فعالیت"
|
||||
direction="column"
|
||||
options={activityTypes}
|
||||
name="نوع فعالیت"
|
||||
value={activityType}
|
||||
onChange={(e) => setActivityType(e.target.value)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="heavy_livestock_number"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
formattedNumber
|
||||
fullWidth
|
||||
placeholder="حجم دام سنگین"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.heavy_livestock_number}
|
||||
helperText={errors.heavy_livestock_number?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="light_livestock_number"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
formattedNumber
|
||||
fullWidth
|
||||
placeholder="حجم دام سبک"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.light_livestock_number}
|
||||
helperText={errors.light_livestock_number?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="postal"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="کد پستی"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.postal}
|
||||
helperText={errors.postal?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="institution"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="کد موسسه"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.institution}
|
||||
helperText={errors.institution?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="epidemiologic"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="کد اپیدمیولوژیک"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.epidemiologic}
|
||||
helperText={errors.epidemiologic?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="وضعیت فعالیت"
|
||||
direction="row"
|
||||
options={activityStateTypes}
|
||||
name="وضعیت فعالیت"
|
||||
value={activityState}
|
||||
onChange={(e) =>
|
||||
e.target.value === "true"
|
||||
? setActivityState(true)
|
||||
: setActivityState(false)
|
||||
}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="وضعیت مجوز"
|
||||
direction="row"
|
||||
options={operatingLicenseStateTypes}
|
||||
name="وضعیت مجوز"
|
||||
value={operatingLicenseState}
|
||||
onChange={(e) =>
|
||||
e.target.value === "true"
|
||||
? setOperatingLicenseState(true)
|
||||
: setOperatingLicenseState(false)
|
||||
}
|
||||
/>
|
||||
|
||||
<Button type="submit">ثبت</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
201
src/partials/LiveStock/live-stock/LiveStockAddLiveStock.tsx
Normal file
201
src/partials/LiveStock/live-stock/LiveStockAddLiveStock.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Button from "../../../components/Button/Button";
|
||||
import { Grid } from "../../../components/Grid/Grid";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import {
|
||||
zValidateNumber,
|
||||
zValidateNumberOptional,
|
||||
zValidateString,
|
||||
} from "../../../data/getFormTypeErrors";
|
||||
import { z } from "zod";
|
||||
import { useApiMutation } from "../../../utils/useApiRequest";
|
||||
import { useToast } from "../../../hooks/useToast";
|
||||
import { useDrawerStore } from "../../../context/zustand-store/appStore";
|
||||
import { getToastResponse } from "../../../data/getToastResponse";
|
||||
import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
|
||||
import { useState } from "react";
|
||||
import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
|
||||
import DatePicker from "../../../components/date-picker/DatePicker";
|
||||
|
||||
type AddPageProps = {
|
||||
getData: () => void;
|
||||
item?: any;
|
||||
herdId?: number;
|
||||
};
|
||||
|
||||
const genderTypes = [
|
||||
{ label: "نر", value: 1 },
|
||||
{ label: "ماده", value: 2 },
|
||||
];
|
||||
|
||||
const weightTypes = [
|
||||
{ label: "سبک", value: "L" },
|
||||
{ label: "سنگین", value: "H" },
|
||||
];
|
||||
|
||||
export const LiveStockAddLiveStock = ({
|
||||
getData,
|
||||
item,
|
||||
herdId,
|
||||
}: AddPageProps) => {
|
||||
const schema = z.object({
|
||||
type: zValidateNumber("نوع دام "),
|
||||
species: zValidateNumberOptional("گونه"),
|
||||
use_type: zValidateNumberOptional("نوع دام "),
|
||||
birthdate: zValidateString("تاریخ تولد"),
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof schema>;
|
||||
|
||||
const showToast = useToast();
|
||||
const { closeDrawer } = useDrawerStore();
|
||||
const [gender, setGender] = useState(item?.gender || 1);
|
||||
const [weightType, setWeightType] = useState(
|
||||
item?.weight_type === "H" ? "H" : "L",
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
type: item?.type?.id || "",
|
||||
},
|
||||
});
|
||||
|
||||
const mutation = useApiMutation({
|
||||
api: `/livestock/web/api/v1/livestock/${item ? item?.id + "/" : ""}`,
|
||||
method: item ? "put" : "post",
|
||||
});
|
||||
const onSubmit = async (data: FormValues) => {
|
||||
try {
|
||||
await mutation.mutateAsync({
|
||||
herd: herdId,
|
||||
type: data?.type,
|
||||
...(data.use_type && {
|
||||
use_type: data.use_type,
|
||||
}),
|
||||
...(data.species && {
|
||||
species: data.species,
|
||||
}),
|
||||
birthdate: data?.birthdate,
|
||||
weight_type: weightType,
|
||||
gender: gender,
|
||||
});
|
||||
showToast(getToastResponse(item, ""), "success");
|
||||
getData();
|
||||
closeDrawer();
|
||||
} catch (error: any) {
|
||||
if (error?.status === 403) {
|
||||
showToast(
|
||||
error?.response?.data?.message || "این مورد تکراری است!",
|
||||
"error",
|
||||
);
|
||||
} else {
|
||||
showToast(
|
||||
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container column className="gap-2">
|
||||
<Controller
|
||||
name="type"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
defaultKey={item?.type?.id}
|
||||
title="نوع دام"
|
||||
api={`livestock/web/api/v1/livestock_type`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.type}
|
||||
errorMessage={errors.type?.message}
|
||||
onChange={(r) => {
|
||||
setValue("type", r);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="جنسیت"
|
||||
direction="row"
|
||||
options={genderTypes}
|
||||
name="جنسیت"
|
||||
value={gender}
|
||||
onChange={(e) => {
|
||||
setGender(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="species"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
defaultKey={item?.species?.id}
|
||||
title="گونه (اختیاری)"
|
||||
api={`livestock/web/api/v1/livestock_species`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.species}
|
||||
errorMessage={errors.species?.message}
|
||||
onChange={(r) => {
|
||||
setValue("species", r);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="use_type"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
defaultKey={item?.use_type?.id}
|
||||
title="نوع دام (اختیاری)"
|
||||
api={`livestock/web/api/v1/livestock_use_type`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.use_type}
|
||||
errorMessage={errors.use_type?.message}
|
||||
onChange={(r) => {
|
||||
setValue("use_type", r);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<DatePicker
|
||||
value={item?.birthdate || ""}
|
||||
minYear={1300}
|
||||
label="تاریخ تولد"
|
||||
size="medium"
|
||||
onChange={(r) => {
|
||||
setValue("birthdate", r);
|
||||
}}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="نوع وزن"
|
||||
direction="row"
|
||||
options={weightTypes}
|
||||
name="نوع وزن"
|
||||
value={weightType}
|
||||
onChange={(e) => {
|
||||
setWeightType(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button type="submit">ثبت</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
360
src/partials/LiveStock/live-stock/LiveStockAddRancher.tsx
Normal file
360
src/partials/LiveStock/live-stock/LiveStockAddRancher.tsx
Normal file
@@ -0,0 +1,360 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Button from "../../../components/Button/Button";
|
||||
import { Grid } from "../../../components/Grid/Grid";
|
||||
import Textfield from "../../../components/Textfeild/Textfeild";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import {
|
||||
zValidateAutoComplete,
|
||||
zValidateMobile,
|
||||
zValidateNationalCode,
|
||||
zValidateNumber,
|
||||
zValidateString,
|
||||
zValidateStringOptional,
|
||||
} from "../../../data/getFormTypeErrors";
|
||||
import { z } from "zod";
|
||||
import { useApiMutation } from "../../../utils/useApiRequest";
|
||||
import { useToast } from "../../../hooks/useToast";
|
||||
import { useDrawerStore } from "../../../context/zustand-store/appStore";
|
||||
import { getToastResponse } from "../../../data/getToastResponse";
|
||||
import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
|
||||
import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
|
||||
import { useState } from "react";
|
||||
import AutoComplete from "../../../components/AutoComplete/AutoComplete";
|
||||
|
||||
type AddPageProps = {
|
||||
getData: () => void;
|
||||
item?: any;
|
||||
};
|
||||
|
||||
export const LiveStockAddRancher = ({ getData, item }: AddPageProps) => {
|
||||
const activityTypes = [
|
||||
{ label: "روستایی", value: "V" },
|
||||
{ label: "صنعتی", value: "I" },
|
||||
{ label: "عشایری", value: "N" },
|
||||
];
|
||||
|
||||
const rancherHerdTypes = [
|
||||
{ label: "عادی", value: false },
|
||||
{ label: "بدون دام", value: true },
|
||||
];
|
||||
|
||||
const rancherTypes = [
|
||||
{ key: "N", value: "حقیقی", disabled: false },
|
||||
{ key: "L", value: "حقوقی", disabled: false },
|
||||
];
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
ranching_farm: zValidateString("نام صفحه"),
|
||||
first_name: zValidateString("نام"),
|
||||
last_name: zValidateString("نام خانوادگی"),
|
||||
mobile: zValidateMobile("موبایل"),
|
||||
national_code: zValidateNationalCode("کد ملی"),
|
||||
address: zValidateString("آدرس"),
|
||||
province: zValidateNumber("استان"),
|
||||
city: zValidateNumber("شهر"),
|
||||
rancher_type: zValidateAutoComplete("مالکیت"),
|
||||
union_name: zValidateStringOptional("نام واحد حقوقی"),
|
||||
union_code: zValidateStringOptional("شناسه ملی واحد حقوقی"),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.rancher_type?.[0] === "L") {
|
||||
return !!data.union_name;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "نام واحد حقوقی نمیتواند خالی باشد",
|
||||
path: ["union_name"],
|
||||
},
|
||||
)
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.rancher_type?.[0] === "L") {
|
||||
return !!data.union_code;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "شناسه ملی واحد حقوقی نمیتواند خالی باشد",
|
||||
path: ["union_code"],
|
||||
},
|
||||
);
|
||||
|
||||
type FormValues = z.infer<typeof schema>;
|
||||
|
||||
const showToast = useToast();
|
||||
const { closeDrawer } = useDrawerStore();
|
||||
|
||||
const [activityType, setActivityType] = useState(item?.activity || "V");
|
||||
|
||||
const [rancherHerdType, setRancherHerdType] = useState(
|
||||
item ? item?.without_herd : false,
|
||||
);
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
trigger,
|
||||
getValues,
|
||||
formState: { errors },
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
ranching_farm: item?.ranching_farm || "",
|
||||
first_name: item?.first_name || "",
|
||||
last_name: item?.last_name || "",
|
||||
mobile: item?.mobile || "",
|
||||
national_code: item?.national_code || "",
|
||||
address: item?.address || "",
|
||||
province: item?.user?.province || "",
|
||||
city: item?.user?.city || "",
|
||||
rancher_type: item ? [item?.rancher_type] : [],
|
||||
union_name: item?.union_name || "",
|
||||
union_code: item?.union_code || "",
|
||||
},
|
||||
});
|
||||
|
||||
const mutation = useApiMutation({
|
||||
api: `/herd/web/api/v1/rancher/${item ? item?.id + "/" : ""}`,
|
||||
method: item ? "put" : "post",
|
||||
});
|
||||
const onSubmit = async (data: FormValues) => {
|
||||
try {
|
||||
await mutation.mutateAsync({
|
||||
ranching_farm: data?.ranching_farm,
|
||||
first_name: data?.first_name,
|
||||
last_name: data?.last_name,
|
||||
mobile: data?.mobile,
|
||||
national_code: data?.national_code,
|
||||
address: data?.address,
|
||||
province: data?.province,
|
||||
city: data?.city,
|
||||
without_herd: rancherHerdType,
|
||||
activity: activityType,
|
||||
rancher_type: data?.rancher_type?.[0],
|
||||
...(data.rancher_type?.[0] === "L"
|
||||
? { union_name: data.union_name }
|
||||
: { union_name: "" }),
|
||||
...(data.rancher_type?.[0] === "L"
|
||||
? { union_code: data.union_code }
|
||||
: { union_code: "" }),
|
||||
});
|
||||
showToast(getToastResponse(item, ""), "success");
|
||||
getData();
|
||||
closeDrawer();
|
||||
} catch (error: any) {
|
||||
if (error?.status === 403) {
|
||||
showToast(
|
||||
error?.response?.data?.message || "این مورد تکراری است!",
|
||||
"error",
|
||||
);
|
||||
} else {
|
||||
showToast(
|
||||
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container column className="gap-2">
|
||||
<Controller
|
||||
name="rancher_type"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<AutoComplete
|
||||
data={rancherTypes}
|
||||
selectedKeys={field.value}
|
||||
onChange={(keys: (string | number)[]) => {
|
||||
setValue("rancher_type", keys);
|
||||
trigger(["rancher_type", "union_name", "union_code"]);
|
||||
}}
|
||||
error={!!errors.rancher_type}
|
||||
helperText={errors.rancher_type?.message}
|
||||
title="نوع دامدار"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!!getValues("rancher_type")?.length && (
|
||||
<Grid container column className="gap-2">
|
||||
<Controller
|
||||
name="ranching_farm"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="نام دامداری"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.ranching_farm}
|
||||
helperText={errors.ranching_farm?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="first_name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="نام"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.first_name}
|
||||
helperText={errors.first_name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="last_name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="نام خانوادگی"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.last_name}
|
||||
helperText={errors.last_name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="national_code"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="کد ملی"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.national_code}
|
||||
helperText={errors.national_code?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="mobile"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
isNumber
|
||||
fullWidth
|
||||
placeholder="موبایل"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.mobile}
|
||||
helperText={errors.mobile?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="نوع فعالیت"
|
||||
direction="column"
|
||||
options={activityTypes}
|
||||
name="نوع فعالیت"
|
||||
value={activityType}
|
||||
onChange={(e) => setActivityType(e.target.value)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="province"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormEnterLocations
|
||||
cityValue={item?.city?.id}
|
||||
provinceValue={item?.province?.id}
|
||||
cityError={!!errors.city}
|
||||
provincError={!!errors.province}
|
||||
cityErrorMessage={errors.city?.message}
|
||||
provinceErrMessage={errors.province?.message}
|
||||
roleControlled
|
||||
onChange={async (locations) => {
|
||||
setValue("province", locations.province);
|
||||
setValue("city", locations.city);
|
||||
if (locations.province || locations.city)
|
||||
await trigger(["province", "city"]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="address"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="آدرس"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.address}
|
||||
helperText={errors.address?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
groupTitle="وضعیت دامدار"
|
||||
direction="row"
|
||||
options={rancherHerdTypes}
|
||||
name="وضعیت"
|
||||
value={rancherHerdType}
|
||||
onChange={(e) =>
|
||||
e.target.value === "true"
|
||||
? setRancherHerdType(true)
|
||||
: setRancherHerdType(false)
|
||||
}
|
||||
/>
|
||||
|
||||
{getValues("rancher_type")?.[0] === "L" && (
|
||||
<>
|
||||
<Controller
|
||||
name="union_name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="نام واحد حقوقی"
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.union_name}
|
||||
helperText={errors.union_name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="union_code"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textfield
|
||||
fullWidth
|
||||
placeholder="شناسه ملی واحد حقوقی"
|
||||
isNumber
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
error={!!errors.union_code}
|
||||
helperText={errors.union_code?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button type="submit">ثبت</Button>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import { z } from "zod";
|
||||
import { zValidateAutoComplete } from "../../../data/getFormTypeErrors";
|
||||
import { useToast } from "../../../hooks/useToast";
|
||||
import { useModalStore } from "../../../context/zustand-store/appStore";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useApiMutation } from "../../../utils/useApiRequest";
|
||||
import { getToastResponse } from "../../../data/getToastResponse";
|
||||
import { Grid } from "../../../components/Grid/Grid";
|
||||
import Button from "../../../components/Button/Button";
|
||||
import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
|
||||
|
||||
type Props = {
|
||||
getData: () => void;
|
||||
item: any;
|
||||
};
|
||||
|
||||
export const LiveStockAllocateCooperative = ({ getData, item }: Props) => {
|
||||
const showToast = useToast();
|
||||
const { closeModal } = useModalStore();
|
||||
|
||||
const schema = z.object({
|
||||
organization: zValidateAutoComplete("تعاونی"),
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof schema>;
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
} = useForm<FormValues>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
organization: [],
|
||||
},
|
||||
});
|
||||
|
||||
const mutation = useApiMutation({
|
||||
api: `herd/web/api/v1/rancher_org_link/`,
|
||||
method: "post",
|
||||
});
|
||||
|
||||
const onSubmit = async (data: FormValues) => {
|
||||
try {
|
||||
await mutation.mutateAsync({
|
||||
rancher: item?.id,
|
||||
organization: data?.organization?.[0],
|
||||
});
|
||||
showToast(getToastResponse(null, "تخصیص با موفقیت انجام شد"), "success");
|
||||
getData();
|
||||
closeModal();
|
||||
} catch (error: any) {
|
||||
if (error?.status === 403) {
|
||||
showToast("این تخصیص تکراری است!", "error");
|
||||
} else {
|
||||
showToast(
|
||||
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container column className="gap-2 justify-center">
|
||||
<Controller
|
||||
name="organization"
|
||||
control={control}
|
||||
render={() => (
|
||||
<FormApiBasedAutoComplete
|
||||
title="انتخاب تعاونی"
|
||||
api={`herd/web/api/v1/rancher_org_link/org_linked_rancher_list/`}
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
error={!!errors.organization}
|
||||
errorMessage={errors.organization?.message}
|
||||
onChange={(r) => {
|
||||
setValue("organization", [r]);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">ثبت</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
320
src/partials/LiveStock/live-stock/LiveStockHerdDetails.tsx
Normal file
320
src/partials/LiveStock/live-stock/LiveStockHerdDetails.tsx
Normal file
@@ -0,0 +1,320 @@
|
||||
import { useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
ScaleIcon,
|
||||
CubeIcon,
|
||||
ShoppingBagIcon,
|
||||
GiftIcon,
|
||||
TruckIcon,
|
||||
UserIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { useApiRequest } from "../../../utils/useApiRequest";
|
||||
|
||||
export const LiveStockHerdDetails = ({
|
||||
farmid,
|
||||
name,
|
||||
}: {
|
||||
farmid: string;
|
||||
name: string;
|
||||
}) => {
|
||||
const [expandedProducts, setExpandedProducts] = useState<
|
||||
Record<number, boolean>
|
||||
>({});
|
||||
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const { data: herdData } = useApiRequest({
|
||||
api: `herd/web/api/v1/rancher/${farmid}/rancher_dashboard_by_product_usage/`,
|
||||
method: "get",
|
||||
queryKey: ["HerdDetails"],
|
||||
});
|
||||
|
||||
// Sample data structure based on your example
|
||||
const products = herdData || [];
|
||||
|
||||
const toggleProduct = (productId: number) => {
|
||||
setExpandedProducts((prev) => ({
|
||||
...prev,
|
||||
[productId]: !prev[productId],
|
||||
}));
|
||||
};
|
||||
|
||||
const toggleItem = (productId: number, itemIndex: number) => {
|
||||
const key = `${productId}-${itemIndex}`;
|
||||
setExpandedItems((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
const getAnimalTypeColor = (type: string) => {
|
||||
return type === "H"
|
||||
? "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
|
||||
: "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200";
|
||||
};
|
||||
|
||||
const getAnimalTypeText = (type: string) => {
|
||||
return type === "H" ? "دام سنگین" : "دام سبک";
|
||||
};
|
||||
|
||||
const formatWeight = (weight: number) => {
|
||||
return weight.toLocaleString("fa-IR") + " کیلوگرم";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto rtl">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="bg-gradient-to-r from-primary-600 to-primary-800 rounded-2xl shadow-2xl p-6 mb-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="bg-white/20 p-3 rounded-xl">
|
||||
<UserIcon className="h-8 w-8 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white">
|
||||
جزئیات دامدار: {name}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{products.map((product: any, index: number) => (
|
||||
<motion.div
|
||||
key={product.product_id}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.3, delay: index * 0.1 }}
|
||||
className="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div
|
||||
className="p-6 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-900/60 transition-colors"
|
||||
onClick={() => toggleProduct(product.product_id)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div
|
||||
className={`p-3 rounded-lg ${
|
||||
expandedProducts[product.product_id]
|
||||
? "bg-primary-100 dark:bg-primary-900"
|
||||
: "bg-gray-100 dark:bg-gray-700"
|
||||
}`}
|
||||
>
|
||||
<CubeIcon className="h-6 w-6 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{product.product}
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-4 mt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<ScaleIcon className="h-5 w-5 text-gray-500 dark:text-white" />
|
||||
<span className="text-sm text-gray-600 dark:text-white">
|
||||
وزن کل:{" "}
|
||||
<span className="font-medium">
|
||||
{formatWeight(product.total_weight)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CubeIcon className="h-5 w-5 text-gray-500 dark:text-white" />
|
||||
<span className="text-sm text-gray-600 dark:text-white">
|
||||
وزن باقیمانده:{" "}
|
||||
<span className="font-medium">
|
||||
{formatWeight(product.remaining_weight)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
{product.free_sale > 0 && (
|
||||
<div className="flex items-center gap-2 bg-yellow-50 dark:bg-yellow-900/30 px-4 py-2 rounded-lg">
|
||||
<ShoppingBagIcon className="h-5 w-5 text-yellow-600 dark:text-yellow-400" />
|
||||
<span className="text-yellow-700 dark:text-yellow-300 font-medium">
|
||||
خرید مازاد: {formatWeight(product.free_sale)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{product.total_purchase > 0 && (
|
||||
<div className="flex items-center gap-2 bg-purple-50 dark:bg-purple-900/30 px-4 py-2 rounded-lg">
|
||||
<GiftIcon className="h-5 w-5 text-purple-600 dark:text-purple-400" />
|
||||
<span className="text-purple-700 dark:text-purple-300 font-medium">
|
||||
خرید کل: {formatWeight(product.total_purchase)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-gray-500">
|
||||
{expandedProducts[product.product_id] ? (
|
||||
<ChevronDownIcon className="h-6 w-6" />
|
||||
) : (
|
||||
<ChevronRightIcon className="h-6 w-6" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product Details - Animated */}
|
||||
<AnimatePresence>
|
||||
{expandedProducts[product.product_id] && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="border-t border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="space-y-4">
|
||||
{product.items.map((item: any, itemIndex: number) => (
|
||||
<motion.div
|
||||
key={itemIndex}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: itemIndex * 0.05 }}
|
||||
className="bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div
|
||||
className="p-4 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors rounded-t-lg"
|
||||
onClick={() =>
|
||||
toggleItem(product.product_id, itemIndex)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<TruckIcon className="h-5 w-5 text-gray-500 dark:text-white" />
|
||||
<span className="font-medium text-gray-700 dark:text-white">
|
||||
سهمیه {item?.quota_id} :
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 dark:text-white">
|
||||
وزن کل: {formatWeight(item.total_weight)}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500 dark:text-white">
|
||||
باقیمانده:{" "}
|
||||
{formatWeight(item.remaining_weight)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{expandedItems[
|
||||
`${product.product_id}-${itemIndex}`
|
||||
] ? (
|
||||
<ChevronDownIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<ChevronRightIcon className="h-5 w-5" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{expandedItems[
|
||||
`${product.product_id}-${itemIndex}`
|
||||
] && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<div className="p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<h5 className="text-sm font-medium text-gray-600 dark:text-white mb-3">
|
||||
سهمیه بر اساس نوع دام
|
||||
</h5>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{item?.by_type?.length > 0 &&
|
||||
item?.by_type
|
||||
.filter(
|
||||
(animal: any) => animal.weight > 0,
|
||||
)
|
||||
.map(
|
||||
(
|
||||
animal: any,
|
||||
animalIndex: number,
|
||||
) => (
|
||||
<motion.div
|
||||
key={animal.name}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: animalIndex * 0.05,
|
||||
}}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg p-3 border border-gray-200 dark:border-gray-700 shadow-sm"
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-gray-900 dark:text-white">
|
||||
{animal.name_fa}
|
||||
</span>
|
||||
<span
|
||||
className={`text-xs px-2 py-1 rounded-full ${getAnimalTypeColor(
|
||||
animal.type,
|
||||
)}`}
|
||||
>
|
||||
{getAnimalTypeText(
|
||||
animal.type,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-lg font-bold text-gray-800 dark:text-gray-200 mt-2">
|
||||
{formatWeight(
|
||||
animal.weight,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Empty State */}
|
||||
{products.length === 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="text-center py-12"
|
||||
>
|
||||
<div className="max-w-md mx-auto">
|
||||
<div className="bg-gray-100 dark:bg-gray-800 w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<CubeIcon className="h-10 w-10 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||
اطلاعاتی یافت نشد
|
||||
</h3>
|
||||
<p className="text-gray-500 dark:text-white">
|
||||
هیچ محصولی برای این دامدار ثبت نشده است
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,207 @@
|
||||
import { useState } from "react";
|
||||
import { useToast } from "../../../hooks/useToast";
|
||||
import { useModalStore } from "../../../context/zustand-store/appStore";
|
||||
import { useApiMutation, useApiRequest } from "../../../utils/useApiRequest";
|
||||
import { Grid } from "../../../components/Grid/Grid";
|
||||
import Button from "../../../components/Button/Button";
|
||||
import Textfield from "../../../components/Textfeild/Textfeild";
|
||||
import AutoComplete from "../../../components/AutoComplete/AutoComplete";
|
||||
import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
|
||||
|
||||
type Props = {
|
||||
getData: () => void;
|
||||
rancher: string | number;
|
||||
};
|
||||
|
||||
type LivestockEntry = {
|
||||
livestock_type: number;
|
||||
allowed_quantity: number | "";
|
||||
};
|
||||
|
||||
type PlanAllocation = {
|
||||
plan: string | number;
|
||||
plan_name: string;
|
||||
livestock_entries: LivestockEntry[];
|
||||
};
|
||||
|
||||
export const LiveStockRancherAllocateIncentivePlan = ({
|
||||
getData,
|
||||
rancher,
|
||||
}: Props) => {
|
||||
const showToast = useToast();
|
||||
const { closeModal } = useModalStore();
|
||||
|
||||
const [planAllocations, setPlanAllocations] = useState<PlanAllocation[]>([]);
|
||||
|
||||
const { data: speciesData } = useApiRequest({
|
||||
api: "/livestock/web/api/v1/livestock_type",
|
||||
method: "get",
|
||||
params: { page: 1, page_size: 1000 },
|
||||
queryKey: ["livestock_species"],
|
||||
});
|
||||
|
||||
const speciesOptions = () => {
|
||||
return (
|
||||
speciesData?.results?.map((opt: any) => ({
|
||||
key: opt?.id,
|
||||
value: opt?.name,
|
||||
})) ?? []
|
||||
);
|
||||
};
|
||||
|
||||
const mutation = useApiMutation({
|
||||
api: "/product/web/api/v1/rancher_incentive_plan/",
|
||||
method: "post",
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const payload = planAllocations.flatMap((pa) =>
|
||||
pa.livestock_entries.map((entry) => ({
|
||||
plan: pa.plan,
|
||||
rancher: Number(rancher),
|
||||
livestock_type: entry.livestock_type,
|
||||
allowed_quantity: Number(entry.allowed_quantity),
|
||||
})),
|
||||
);
|
||||
|
||||
if (payload.length === 0) {
|
||||
showToast("لطفاً حداقل یک طرح و نوع دام انتخاب کنید!", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await mutation.mutateAsync({ data: payload });
|
||||
showToast("تخصیص طرح تشویقی با موفقیت انجام شد", "success");
|
||||
getData();
|
||||
closeModal();
|
||||
} catch (error: any) {
|
||||
showToast(
|
||||
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||
"error",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Grid container column className="gap-3">
|
||||
<FormApiBasedAutoComplete
|
||||
title="انتخاب طرح تشویقی"
|
||||
api="product/web/api/v1/incentive_plan/active_plans/"
|
||||
keyField="id"
|
||||
valueField="name"
|
||||
secondaryKey="name"
|
||||
multiple
|
||||
onChange={(items) => {
|
||||
const selectedItems = Array.isArray(items) ? items : [];
|
||||
setPlanAllocations((prev) =>
|
||||
selectedItems.map((item: any) => {
|
||||
const existing = prev.find((pa) => pa.plan === item.key1);
|
||||
return (
|
||||
existing || {
|
||||
plan: item.key1,
|
||||
plan_name: item.key2,
|
||||
livestock_entries: [],
|
||||
}
|
||||
);
|
||||
}),
|
||||
);
|
||||
}}
|
||||
onChangeValue={(names) => {
|
||||
setPlanAllocations((prev) =>
|
||||
prev.map((pa, i) => ({
|
||||
...pa,
|
||||
plan_name: names[i] || pa.plan_name,
|
||||
})),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{planAllocations.map((pa, planIndex) => (
|
||||
<Grid
|
||||
key={pa.plan}
|
||||
container
|
||||
column
|
||||
className="gap-2 border p-3 rounded-lg"
|
||||
>
|
||||
<span className="font-bold text-sm">{pa.plan_name}</span>
|
||||
|
||||
{speciesData?.results && (
|
||||
<AutoComplete
|
||||
data={speciesOptions()}
|
||||
multiselect
|
||||
selectedKeys={pa.livestock_entries.map((e) => e.livestock_type)}
|
||||
onChange={(keys: (string | number)[]) => {
|
||||
setPlanAllocations((prev) => {
|
||||
const next = [...prev];
|
||||
next[planIndex] = {
|
||||
...next[planIndex],
|
||||
livestock_entries: keys.map((k) => {
|
||||
const existing = next[planIndex].livestock_entries.find(
|
||||
(e) => e.livestock_type === k,
|
||||
);
|
||||
return {
|
||||
livestock_type: k as number,
|
||||
allowed_quantity: existing?.allowed_quantity ?? "",
|
||||
};
|
||||
}),
|
||||
};
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
title="نوع دام"
|
||||
/>
|
||||
)}
|
||||
|
||||
{pa.livestock_entries.map((entry, entryIndex) => (
|
||||
<Textfield
|
||||
key={entry.livestock_type}
|
||||
fullWidth
|
||||
formattedNumber
|
||||
placeholder={`تعداد مجاز ${
|
||||
speciesOptions().find(
|
||||
(s: any) => s.key === entry.livestock_type,
|
||||
)?.value || ""
|
||||
}`}
|
||||
value={entry.allowed_quantity}
|
||||
onChange={(e) => {
|
||||
setPlanAllocations((prev) => {
|
||||
const next = [...prev];
|
||||
const entries = [...next[planIndex].livestock_entries];
|
||||
entries[entryIndex] = {
|
||||
...entries[entryIndex],
|
||||
allowed_quantity: Number(e.target.value),
|
||||
};
|
||||
next[planIndex] = {
|
||||
...next[planIndex],
|
||||
livestock_entries: entries,
|
||||
};
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
))}
|
||||
|
||||
<Button
|
||||
disabled={
|
||||
planAllocations.length === 0 ||
|
||||
planAllocations.some((pa) => pa.livestock_entries.length === 0) ||
|
||||
planAllocations.some((pa) =>
|
||||
pa.livestock_entries.some(
|
||||
(e) =>
|
||||
e.allowed_quantity === "" || Number(e.allowed_quantity) <= 0,
|
||||
),
|
||||
)
|
||||
}
|
||||
type="submit"
|
||||
>
|
||||
ثبت
|
||||
</Button>
|
||||
</Grid>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user