Compare commits

...

66 Commits

Author SHA1 Message Date
4138ea652a version changed to 02.79 2026-02-25 15:49:02 +03:30
313364a304 add: inquiry tracking code 2026-02-25 11:43:16 +03:30
472d0e66cc feat: check tracking code component 2026-02-25 11:43:04 +03:30
5572ed070e feat: units inventory 2026-02-25 10:59:40 +03:30
11bb774b9c add: domain key 2026-02-25 10:09:08 +03:30
88ec9ed3b6 feat: value field function 2026-02-25 09:33:59 +03:30
ff6089bfb0 update: dashboard now calculate domains 2026-02-25 08:59:06 +03:30
25c8171f87 version changed to 02.78 2026-02-24 17:06:08 +03:30
9ab4977b50 add: domain on paths 2026-02-24 17:05:59 +03:30
defa1e3e08 update: placement of elements 2026-02-24 17:05:46 +03:30
e6ff022335 feat: domains in menu and sidebar 2026-02-24 15:36:18 +03:30
043ad77b83 fix: icon issues 2026-02-24 15:35:23 +03:30
0743d6c86f add: domain in access and page 2026-02-24 12:12:40 +03:30
2829f68831 version changed to 02.77 2026-02-23 16:33:40 +03:30
d7d4848f2a feat: domains management 2026-02-23 16:33:34 +03:30
b8ae9757d4 feat: admin category 2026-02-23 15:32:16 +03:30
e7f4c55bfe refactor: organized components based on domain 2026-02-23 14:38:30 +03:30
1f763a33ac refactor: organized pages based on domain 2026-02-23 12:30:27 +03:30
32246fd0cc version changed to 02.76 2026-02-22 14:05:09 +03:30
78c7bb22e3 add: livestock active state filter 2026-02-22 08:47:24 +03:30
a3c9163787 version changed to 02.75 2026-02-21 11:29:45 +03:30
741385e282 add: manual entering sms receiver user 2026-02-21 11:29:34 +03:30
9d87907f81 version changed to 02.74 2026-02-21 10:28:59 +03:30
561ecafe0f version changed to 02.73 2026-02-21 10:06:14 +03:30
26c13018e7 version changed to 02.72 2026-02-21 09:58:43 +03:30
b3892b6a8a add: page and access for send sms 2026-02-21 09:58:34 +03:30
44037758ed version changed to 02.71 2026-02-18 16:03:44 +03:30
c55e1f907a add: two new keys 2026-02-18 15:45:51 +03:30
b3dde3c0ec add: active state 2026-02-18 14:01:17 +03:30
4245dce136 feat: tag batch otp auth 2026-02-18 12:25:09 +03:30
d9aed4d215 version changed to 02.70 2026-02-17 16:40:56 +03:30
4a5bdd7f89 feat: opt auth established 2026-02-17 16:40:45 +03:30
21dcc7dca8 add: otp status 2026-02-17 15:47:31 +03:30
e507d08835 add: page and access filter to tags tab 2026-02-16 10:59:31 +03:30
225b2d874d update: move tagging pages in one page 2026-02-16 10:57:25 +03:30
c7b3eb80ec chore: rename permission 2026-02-16 10:40:11 +03:30
33233cf9e0 version changed to 02.69 2026-02-15 10:17:54 +03:30
abe53b885a add: one time purchase limit 2026-02-15 10:17:42 +03:30
c0b8472c72 chore: rename tagging names 2026-02-15 08:49:05 +03:30
f473c729fe version changed to 02.68 2026-02-14 14:20:25 +03:30
e8a75fcc74 feat: allocate incentive plan to rancher 2026-02-14 13:20:21 +03:30
ec087e59bc feat: allocate incentive plan to rancher 2026-02-14 13:20:10 +03:30
468ad9079e version changed to 02.67 2026-02-10 16:50:13 +03:30
28f7d5c991 feat: inquiry unique id 2026-02-10 16:50:04 +03:30
763d5b79d3 add: quaternaryKey 2026-02-10 16:49:48 +03:30
f8e8e7f1f1 fix: unique unit id validation 2026-02-10 15:16:53 +03:30
63aed5572c fix: AutoComplete click event on open icon 2026-02-10 15:11:45 +03:30
59ae3b76d9 update: add address text color 2026-02-10 14:52:23 +03:30
93867ce7ee version changed to 02.66 2026-02-09 16:23:19 +03:30
4210dbd7e2 add: organization type filter 2026-02-09 14:38:32 +03:30
3624b3bc70 add: size prop 2026-02-09 13:53:43 +03:30
4d00b0d492 add: unique_unit_identity 2026-02-09 12:34:56 +03:30
ed7b257ed8 add: limit size for upload doc and removed validFiles limit 2026-02-09 10:23:35 +03:30
03136f5f30 add: limit size 2026-02-09 10:22:34 +03:30
908a69ce0e version changed to 02.65 2026-02-08 16:53:18 +03:30
a818683247 add: multiple addresses 2026-02-08 16:52:55 +03:30
95780cfbc9 add: document upload and sign 2026-02-08 16:52:26 +03:30
071c3e159b feat: document operation 2026-02-08 16:52:00 +03:30
90f51c6899 feat: edit distribution from distribution 2026-02-08 11:20:10 +03:30
e967329108 chore: fix grammar mistake 2026-02-03 10:24:28 +03:30
f8d2da4f28 version changed to 02.64 2026-02-02 16:34:27 +03:30
bb1d5b3315 update: tag distribution details 2026-02-02 16:34:21 +03:30
d8d415a8f5 fix: remaining number 2026-02-02 14:50:44 +03:30
f58c8e6c58 add: new keys 2026-02-02 14:49:12 +03:30
3a17fcb448 version changed to 02.63 2026-02-02 12:14:52 +03:30
6e219aca1a feat: distribute from distribution 2026-02-02 12:14:48 +03:30
126 changed files with 4611 additions and 1750 deletions

View File

@@ -32,7 +32,7 @@ export default function Dashboard() {
useDashboardTabStore(); useDashboardTabStore();
const menuItems: ItemWithSubItems[] = getUserPermissions( const menuItems: ItemWithSubItems[] = getUserPermissions(
profile?.permissions profile?.permissions,
); );
const [tabs, setTabs] = useState<Tab[]>(dashboarTabs || []); const [tabs, setTabs] = useState<Tab[]>(dashboarTabs || []);
@@ -55,7 +55,7 @@ export default function Dashboard() {
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: false, hour12: false,
}) }),
); );
useEffect(() => { useEffect(() => {
@@ -65,7 +65,7 @@ export default function Dashboard() {
hour: "2-digit", hour: "2-digit",
minute: "2-digit", minute: "2-digit",
hour12: false, hour12: false,
}) }),
); );
}, 60000); }, 60000);
return () => clearInterval(interval); return () => clearInterval(interval);
@@ -96,7 +96,7 @@ export default function Dashboard() {
if (activeTabId === id) { if (activeTabId === id) {
setActiveTabId( setActiveTabId(
newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null,
); );
} }
}; };
@@ -113,14 +113,49 @@ export default function Dashboard() {
(subItem) => (subItem) =>
!subItem.path.includes("$") && !subItem.path.includes("$") &&
(search.trim() === "" || (search.trim() === "" ||
getFaPermissions(subItem.name).includes(search.trim())) getFaPermissions(subItem.name).includes(search.trim())),
), ),
})) }))
.filter((item) => item.subItems.length > 0); .filter((item) => item.subItems.length > 0);
const adminFilteredItems = filteredMenuItems.filter(
(item) => item.en === "admin",
);
const nonAdminFilteredItems = filteredMenuItems.filter(
(item) => item.en !== "admin",
);
const permissionDomainMap = new Map<string, string>();
(profile?.permissions || []).forEach((permission: any) => {
if (permission?.page_name) {
permissionDomainMap.set(
permission.page_name,
permission?.domain_fa_name || "سایر حوزه ها",
);
}
});
const groupedFilteredItems = nonAdminFilteredItems.reduce(
(acc, item) => {
const firstSubItem = item.subItems?.find((sub) =>
permissionDomainMap.has(sub.name),
);
const domainTitle = firstSubItem
? permissionDomainMap.get(firstSubItem.name) || "سایر حوزه ها"
: "سایر حوزه ها";
if (!acc[domainTitle]) {
acc[domainTitle] = [];
}
acc[domainTitle].push(item);
return acc;
},
{} as Record<string, ItemWithSubItems[]>,
);
const showDomainGrouping = Object.keys(groupedFilteredItems).length > 1;
function findSubItemByPath( function findSubItemByPath(
items: ItemWithSubItems[], items: ItemWithSubItems[],
path: string path: string,
): ItemWithSubItems["subItems"][0] | null { ): ItemWithSubItems["subItems"][0] | null {
for (const item of items) { for (const item of items) {
for (const subItem of item.subItems) { for (const subItem of item.subItems) {
@@ -243,18 +278,11 @@ export default function Dashboard() {
}`} }`}
> >
{checkIsMobile() {checkIsMobile()
? filteredMenuItems.map(({ fa, icon: Icon, subItems }, index) => { ? (
const filteredSubItems = subItems.filter( <>
(item) => {adminFilteredItems.map(({ fa, icon: Icon, subItems }, index) => (
!item.path.includes("$") &&
getFaPermissions(item.name).includes(search.trim())
);
if (filteredSubItems.length === 0) return null;
return (
<section <section
key={index} key={`admin-mobile-${index}`}
className="w-full space-y-5 border border-gray-200 dark:border-dark-600 bg-white dark:bg-dark-800 rounded-2xl p-6 shadow-sm" className="w-full space-y-5 border border-gray-200 dark:border-dark-600 bg-white dark:bg-dark-800 rounded-2xl p-6 shadow-sm"
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@@ -263,9 +291,8 @@ export default function Dashboard() {
{fa} {fa}
</h2> </h2>
</div> </div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-4"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-4">
{filteredSubItems.map((sub, subIndex) => ( {subItems.map((sub, subIndex) => (
<motion.button <motion.button
key={subIndex} key={subIndex}
onClick={() => navigate({ to: sub.path })} onClick={() => navigate({ to: sub.path })}
@@ -281,11 +308,88 @@ export default function Dashboard() {
))} ))}
</div> </div>
</section> </section>
); ))}
}) {showDomainGrouping
: filteredMenuItems.map(({ fa, icon: Icon, subItems }, index) => ( ? Object.entries(groupedFilteredItems).map(
([domainTitle, domainItems], domainIndex) => (
<section
key={`domain-mobile-${domainTitle}-${domainIndex}`}
className="w-full space-y-3 border border-gray-200 dark:border-dark-600 bg-white dark:bg-dark-800 rounded-2xl p-4 shadow-sm"
>
<div className="flex items-center gap-2 pb-2 border-b border-gray-200 dark:border-dark-600">
<Squares2X2Icon className="w-4 h-4 text-primary-500" />
<h2 className="text-sm font-bold text-primary-700 dark:text-primary-300">
{domainTitle}
</h2>
</div>
{domainItems.map(({ fa, icon: Icon, subItems }, index) => (
<section
key={`domain-item-mobile-${domainTitle}-${index}`}
className="w-full space-y-3 border border-gray-200 dark:border-dark-600 bg-gray-50 dark:bg-dark-700 rounded-xl p-4"
>
<div className="flex items-center gap-2">
<Icon className="w-5 h-5 text-primary-600 dark:text-primary-400" />
<h3 className="text-base font-bold text-dark-900 dark:text-white">
{fa}
</h3>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{subItems.map((sub, subIndex) => (
<motion.button
key={subIndex}
onClick={() => navigate({ to: sub.path })}
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className="flex items-center gap-2 cursor-pointer bg-white dark:bg-dark-800 text-right border border-gray-200 dark:border-dark-600 hover:border-primary-500 dark:hover:border-primary-400 shadow-sm rounded-xl p-3 transition-all duration-200"
>
<ArrowRightCircleIcon className="w-5 h-5 text-primary-500 dark:text-primary-400" />
<span className="text-sm font-medium text-dark-800 dark:text-white">
{getFaPermissions(sub.name)}
</span>
</motion.button>
))}
</div>
</section>
))}
</section>
),
)
: nonAdminFilteredItems.map(({ fa, icon: Icon, subItems }, index) => (
<section
key={`plain-mobile-${index}`}
className="w-full space-y-5 border border-gray-200 dark:border-dark-600 bg-white dark:bg-dark-800 rounded-2xl p-6 shadow-sm"
>
<div className="flex items-center gap-3">
<Icon className="w-6 h-6 text-primary-600 dark:text-primary-400" />
<h2 className="text-xl font-bold text-dark-900 dark:text-white">
{fa}
</h2>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-5 gap-4">
{subItems.map((sub, subIndex) => (
<motion.button
key={subIndex}
onClick={() => navigate({ to: sub.path })}
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className="flex items-center gap-2 cursor-pointer bg-gray-50 dark:bg-dark-700 text-right border border-gray-200 dark:border-dark-600 hover:border-primary-500 dark:hover:border-primary-400 shadow-sm rounded-xl p-3 transition-all duration-200"
>
<ArrowRightCircleIcon className="w-5 h-5 text-primary-500 dark:text-primary-400" />
<span className="text-sm font-medium text-dark-800 dark:text-white">
{getFaPermissions(sub.name)}
</span>
</motion.button>
))}
</div>
</section>
))}
</>
)
: (
<>
{adminFilteredItems.map(({ fa, icon: Icon, subItems }, index) => (
<motion.div <motion.div
key={index} key={`admin-desktop-${index}`}
initial={{ opacity: 0, x: -10 }} initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }} animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 }} transition={{ delay: index * 0.05 }}
@@ -301,12 +405,86 @@ export default function Dashboard() {
</span> </span>
</div> </div>
</div> </div>
<div className="p-1.5 space-y-0.5">
{subItems.map((sub, subIndex) => {
const isActive = tabs.some(
(tab) => tab.path === sub.path && activeTabId === tab.id,
);
return (
<motion.div
key={subIndex}
initial={{ opacity: 0, x: -5 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 + subIndex * 0.02 }}
whileHover={{ x: 1 }}
whileTap={{ scale: 0.98 }}
onClick={() => openTab(sub)}
className={`flex items-center gap-1.5 px-2 py-1.5 text-xs rounded-md cursor-pointer transition-all duration-200 focus:outline-none ${
isActive
? "backdrop-blur-sm bg-primary-500/20 dark:bg-primary-400/20 text-primary-700 dark:text-primary-300 "
: "hover:backdrop-blur-sm hover:bg-white/30 dark:hover:bg-dark-600/30 border-none"
}`}
>
<div
className={`w-1.5 h-1.5 rounded-full ${
isActive
? "bg-primary-600 dark:bg-primary-400"
: "bg-dark-400 dark:bg-dark-500"
}`}
/>
<span
className={`truncate ${
isActive
? "text-primary-700 dark:text-white font-medium"
: "text-dark-600 dark:text-dark-200/80"
}`}
>
{getFaPermissions(sub.name)}
</span>
</motion.div>
);
})}
</div>
</motion.div>
))}
{showDomainGrouping
? Object.entries(groupedFilteredItems).map(
([domainTitle, domainItems], domainIndex) => (
<div
key={`domain-desktop-${domainTitle}-${domainIndex}`}
className="flex-none w-56 space-y-2"
>
<div className="flex items-center gap-2 px-1">
<Squares2X2Icon className="w-4 h-4 text-primary-500" />
<span className="text-xs font-bold text-primary-700 dark:text-primary-300">
{domainTitle}
</span>
</div>
<div className="space-y-2">
{domainItems.map(({ fa, icon: Icon, subItems }, index) => (
<motion.div
key={`domain-item-desktop-${domainTitle}-${index}`}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 }}
className="w-full backdrop-blur-xl bg-white/20 dark:bg-dark-800/80 rounded-xl shadow-lg border border-white/30 dark:border-dark-700/30 overflow-hidden"
>
<div className="backdrop-blur-sm bg-white/30 dark:bg-dark-700/30 px-3 py-2 border-b border-white/20 dark:border-dark-600/20">
<div className="flex items-center gap-2">
<div className="p-1.5 rounded-md backdrop-blur-sm bg-primary-500/20 dark:bg-primary-400/20">
<Icon className="w-3.5 h-3.5 text-primary-600 dark:text-primary-400" />
</div>
<span className="text-xs font-semibold text-dark-800 dark:text-dark-100 truncate">
{fa}
</span>
</div>
</div>
<div className="p-1.5 space-y-0.5"> <div className="p-1.5 space-y-0.5">
{subItems.map((sub, subIndex) => { {subItems.map((sub, subIndex) => {
const isActive = tabs.some( const isActive = tabs.some(
(tab) => (tab) =>
tab.path === sub.path && activeTabId === tab.id tab.path === sub.path &&
activeTabId === tab.id,
); );
return ( return (
<motion.div <motion.div
@@ -348,6 +526,75 @@ export default function Dashboard() {
</motion.div> </motion.div>
))} ))}
</div> </div>
</div>
),
)
: nonAdminFilteredItems.map(({ fa, icon: Icon, subItems }, index) => (
<motion.div
key={`plain-desktop-${index}`}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.05 }}
className="flex-none w-48 backdrop-blur-xl bg-white/20 dark:bg-dark-800/80 rounded-xl shadow-lg border border-white/30 dark:border-dark-700/30 overflow-hidden"
>
<div className="backdrop-blur-sm bg-white/30 dark:bg-dark-700/30 px-3 py-2 border-b border-white/20 dark:border-dark-600/20">
<div className="flex items-center gap-2">
<div className="p-1.5 rounded-md backdrop-blur-sm bg-primary-500/20 dark:bg-primary-400/20">
<Icon className="w-3.5 h-3.5 text-primary-600 dark:text-primary-400" />
</div>
<span className="text-xs font-semibold text-dark-800 dark:text-dark-100 truncate">
{fa}
</span>
</div>
</div>
<div className="p-1.5 space-y-0.5">
{subItems.map((sub, subIndex) => {
const isActive = tabs.some(
(tab) =>
tab.path === sub.path && activeTabId === tab.id,
);
return (
<motion.div
key={subIndex}
initial={{ opacity: 0, x: -5 }}
animate={{ opacity: 1, x: 0 }}
transition={{
delay: index * 0.05 + subIndex * 0.02,
}}
whileHover={{ x: 1 }}
whileTap={{ scale: 0.98 }}
onClick={() => openTab(sub)}
className={`flex items-center gap-1.5 px-2 py-1.5 text-xs rounded-md cursor-pointer transition-all duration-200 focus:outline-none ${
isActive
? "backdrop-blur-sm bg-primary-500/20 dark:bg-primary-400/20 text-primary-700 dark:text-primary-300 "
: "hover:backdrop-blur-sm hover:bg-white/30 dark:hover:bg-dark-600/30 border-none"
}`}
>
<div
className={`w-1.5 h-1.5 rounded-full ${
isActive
? "bg-primary-600 dark:bg-primary-400"
: "bg-dark-400 dark:bg-dark-500"
}`}
/>
<span
className={`truncate ${
isActive
? "text-primary-700 dark:text-white font-medium"
: "text-dark-600 dark:text-dark-200/80"
}`}
>
{getFaPermissions(sub.name)}
</span>
</motion.div>
);
})}
</div>
</motion.div>
))}
</>
)}
</div>
)} )}
</div> </div>

84
src/Pages/Domains.tsx Normal file
View File

@@ -0,0 +1,84 @@
import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest";
import { useModalStore } from "../context/zustand-store/appStore";
import Table from "../components/Table/Table";
import { Grid } from "../components/Grid/Grid";
import Button from "../components/Button/Button";
import { AddDomain } from "../partials/Admin/AddDomain";
import { Popover } from "../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons";
export default function Domains() {
const { openModal } = useModalStore();
const [domainsInfo, setDomainsInfo] = useState({ page: 1, page_size: 10 });
const [domainsTableData, setDomainsTableData] = useState([]);
const { data: domainsData, refetch } = useApiRequest({
api: "/core/domain/",
method: "get",
params: domainsInfo,
queryKey: ["domains", domainsInfo],
});
useEffect(() => {
if (domainsData?.results) {
const formattedData = domainsData.results.map((item: any, i: number) => {
return [
domainsInfo.page === 1
? i + 1
: i + domainsInfo.page_size * (domainsInfo.page - 1) + 1,
item?.fa_name,
item?.code,
item?.name,
<Popover key={i}>
<Tooltip title="ویرایش" position="right">
<Button
variant="edit"
onClick={() => {
openModal({
title: "ویرایش حوزه",
content: <AddDomain item={item} getData={refetch} />,
});
}}
/>
</Tooltip>
<DeleteButtonForPopOver
api={`core/domain/${item?.id}/`}
getData={refetch}
/>
</Popover>,
];
});
setDomainsTableData(formattedData);
}
}, [domainsData, domainsInfo]);
return (
<Grid container column>
<Grid container className="items-center gap-2">
<Button
size="small"
variant="submit"
onClick={() => {
openModal({
title: "ایجاد حوزه",
content: <AddDomain getData={refetch} />,
});
}}
>
ایجاد حوزه
</Button>
</Grid>
<Table
className="mt-2"
onChange={setDomainsInfo}
count={domainsData?.count || 10}
isPaginated
title="حوزه ها"
columns={["ردیف", "نام حوزه", "کد حوزه", "نام انگلیسی", "عملیات"]}
rows={domainsTableData}
/>
</Grid>
);
}

View File

@@ -1,16 +1,16 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { LIVESTOCK_FARMERS } from "../routes/paths"; import { LIVESTOCK_FARMERS } from "../../routes/paths";
import { TableButton } from "../components/TableButton/TableButton"; import { TableButton } from "../../components/TableButton/TableButton";
import { CooperativesDashboardDetails } from "../partials/cooperatives/CooperativesDashboardDetails"; import { CooperativesDashboardDetails } from "../../partials/LiveStock/cooperatives/CooperativesDashboardDetails";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
export default function CooperativeRanchers() { export default function CooperativeRanchers() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
@@ -98,7 +98,7 @@ export default function CooperativeRanchers() {
/> />
</Popover>, </Popover>,
]; ];
} },
); );
setCooperativesTableData(tableData); setCooperativesTableData(tableData);
} }

View File

@@ -1,19 +1,19 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { ChildOrganizations } from "../partials/cooperatives/ChildOrganizations"; import { ChildOrganizations } from "../../partials/LiveStock/cooperatives/ChildOrganizations";
import { COOPERATIVE_LIST } from "../routes/paths"; import { COOPERATIVE_LIST } from "../../routes/paths";
import { TableButton } from "../components/TableButton/TableButton"; import { TableButton } from "../../components/TableButton/TableButton";
import { CooperativesDashboardDetails } from "../partials/cooperatives/CooperativesDashboardDetails"; import { CooperativesDashboardDetails } from "../../partials/LiveStock/cooperatives/CooperativesDashboardDetails";
import { AddActivityType } from "../partials/cooperatives/AddActivityType"; import { AddActivityType } from "../../partials/LiveStock/cooperatives/AddActivityType";
import ShowMoreInfo from "../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
import ShowStringList from "../components/ShowStringList/ShowStringList"; import ShowStringList from "../../components/ShowStringList/ShowStringList";
export default function Cooperatives() { export default function Cooperatives() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
@@ -64,7 +64,7 @@ export default function Cooperatives() {
<ShowStringList <ShowStringList
showSearch={false} showSearch={false}
strings={item.org_service_area.map( strings={item.org_service_area.map(
(city: any) => city.name (city: any) => city.name,
)} )}
/> />
</Grid> </Grid>
@@ -131,7 +131,7 @@ export default function Cooperatives() {
/> />
</Tooltip>, </Tooltip>,
]; ];
} },
); );
setCooperativesTableData(formattedData); setCooperativesTableData(formattedData);
} }

View File

@@ -1,21 +1,21 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { import {
useDrawerStore, useDrawerStore,
useModalStore, useModalStore,
} from "../context/zustand-store/appStore"; } from "../../context/zustand-store/appStore";
import { LiveStockAddHerd } from "../partials/live-stock/LiveStockAddHerd"; import { LiveStockAddHerd } from "../../partials/LiveStock/live-stock/LiveStockAddHerd";
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { LIVESTOCKS } from "../routes/paths"; import { LIVESTOCKS } from "../../routes/paths";
import { LiveStockAddLiveStock } from "../partials/live-stock/LiveStockAddLiveStock"; import { LiveStockAddLiveStock } from "../../partials/LiveStock/live-stock/LiveStockAddLiveStock";
import { TableButton } from "../components/TableButton/TableButton"; import { TableButton } from "../../components/TableButton/TableButton";
import { LiveStockHerdDetails } from "../partials/live-stock/LiveStockHerdDetails"; import { LiveStockHerdDetails } from "../../partials/LiveStock/live-stock/LiveStockHerdDetails";
export default function LiveStocks() { export default function LiveStocks() {
const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 }); const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 });
@@ -38,7 +38,7 @@ export default function LiveStocks() {
? `/herd/web/api/v1/rancher/${farmid}/rancher_dashboard/` ? `/herd/web/api/v1/rancher/${farmid}/rancher_dashboard/`
: "/herd/web/api/v1/rancher/rancher_main_dashboard/", : "/herd/web/api/v1/rancher/rancher_main_dashboard/",
queryKey: ["HerdsDashboard"], queryKey: ["HerdsDashboard"],
} },
); );
const handleUpdate = () => { const handleUpdate = () => {
@@ -156,6 +156,8 @@ export default function LiveStocks() {
"تعداد دام سنگین", "تعداد دام سنگین",
"تعداد دام سبک", "تعداد دام سبک",
"مجموع وزن خرید از سهمیه ها", "مجموع وزن خرید از سهمیه ها",
"تعداد دام های فعال",
"تعداد دام های غیرفعال",
"جزئیات", "جزئیات",
] ]
: [ : [
@@ -186,6 +188,10 @@ export default function LiveStocks() {
"0", "0",
DashboardData?.total_purchase_weight?.toLocaleString() || DashboardData?.total_purchase_weight?.toLocaleString() ||
"0", "0",
DashboardData?.total_active_livestock_count?.toLocaleString() ||
"0",
DashboardData?.total_deactive_livestock_count?.toLocaleString() ||
"0",
<TableButton <TableButton
size="small" size="small"
key={DashboardData} key={DashboardData}

View File

@@ -1,15 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { formatJustDate } from "../utils/formatTime"; import { formatJustDate } from "../../utils/formatTime";
import { AddIncentivePlan } from "../partials/quota/AddIncentivePlan"; import { AddIncentivePlan } from "../../partials/LiveStock/quota/AddIncentivePlan";
import AutoComplete from "../components/AutoComplete/AutoComplete"; import AutoComplete from "../../components/AutoComplete/AutoComplete";
export default function IncentivePlans() { export default function IncentivePlans() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,11 +1,11 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import { useUserProfileStore } from "../context/zustand-store/userStore"; import { useUserProfileStore } from "../../context/zustand-store/userStore";
import { InventoryStakeHolderAllocations } from "../partials/inventory/InventoryStakeHolderAllocations"; import { InventoryStakeHolderAllocations } from "../../partials/LiveStock/inventory/InventoryStakeHolderAllocations";
import { InventoryWarehouseEntryTab } from "../partials/inventory/InventoryWarehouseEntryTab"; import { InventoryWarehouseEntryTab } from "../../partials/LiveStock/inventory/InventoryWarehouseEntryTab";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { InventoryEntriesList } from "../partials/inventory/InventoryEntriesList"; import { InventoryEntriesList } from "../../partials/LiveStock/inventory/InventoryEntriesList";
export default function Inventory() { export default function Inventory() {
const [selectedTab, setSelectedTab] = useState<number>(0); const [selectedTab, setSelectedTab] = useState<number>(0);

View File

@@ -1,21 +1,21 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { LiveStockAddRancher } from "../partials/live-stock/LiveStockAddRancher"; import { LiveStockAddRancher } from "../../partials/LiveStock/live-stock/LiveStockAddRancher";
import { import {
useDrawerStore, useDrawerStore,
useModalStore, useModalStore,
} from "../context/zustand-store/appStore"; } from "../../context/zustand-store/appStore";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { LiveStockAddHerd } from "../partials/live-stock/LiveStockAddHerd"; import { LiveStockAddHerd } from "../../partials/LiveStock/live-stock/LiveStockAddHerd";
import { LiveStockAllocateCooperative } from "../partials/live-stock/LiveStockAllocateCooperative"; import { LiveStockAllocateCooperative } from "../../partials/LiveStock/live-stock/LiveStockAllocateCooperative";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { LIVESTOCK_FARMERS } from "../routes/paths"; import { LIVESTOCK_FARMERS } from "../../routes/paths";
import { LiveStockFarmersDashboardResponse } from "../types/LiveStockFarmers"; import { LiveStockFarmersDashboardResponse } from "../../types/LiveStockFarmers";
export default function LiveStockFarmers() { export default function LiveStockFarmers() {
const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 }); const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 });
@@ -137,7 +137,7 @@ export default function LiveStockFarmers() {
"/plans/" + "/plans/" +
item.id + item.id +
"/" + "/" +
item?.ranching_farm; `${item?.first_name.replace(":", " ") || ""} ${item?.last_name || ""} ${item?.ranching_farm ? " (" + item?.ranching_farm + ") " : ""}`;
navigate({ to: path }); navigate({ to: path });
}} }}
/> />

View File

@@ -1,30 +1,47 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { formatAgeCalcuation, formatJustDate } from "../utils/formatTime"; import { formatAgeCalcuation, formatJustDate } from "../../utils/formatTime";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { LiveStockAddLiveStock } from "../partials/live-stock/LiveStockAddLiveStock"; import { LiveStockAddLiveStock } from "../../partials/LiveStock/live-stock/LiveStockAddLiveStock";
import { useDrawerStore } from "../context/zustand-store/appStore"; import { useDrawerStore } from "../../context/zustand-store/appStore";
import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
import AutoComplete from "../../components/AutoComplete/AutoComplete";
export default function LiveStocks() { export default function LiveStocks() {
const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 }); const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 });
const [pagesTableData, setPagesTableData] = useState([]); const [pagesTableData, setPagesTableData] = useState([]);
const [selectedStatusKeys, setSelectedStatusKeys] = useState<
(string | number)[]
>([]);
const { openDrawer } = useDrawerStore(); const { openDrawer } = useDrawerStore();
const statusItems = [
{ key: "", value: "همه" },
{ key: "true", value: "فعال" },
{ key: "false", value: "غیرفعال" },
];
const { herdid, name } = useParams({ strict: false }); const { herdid, name } = useParams({ strict: false });
const activeParam =
selectedStatusKeys.length && selectedStatusKeys[0] !== ""
? { active: selectedStatusKeys[0] }
: {};
const { data: pagesData, refetch } = useApiRequest({ const { data: pagesData, refetch } = useApiRequest({
api: herdid api: herdid
? `herd/web/api/v1/herd/${herdid}/live_stocks/` ? `herd/web/api/v1/herd/${herdid}/live_stocks/`
: "/livestock/web/api/v1/livestock/", : "/livestock/web/api/v1/livestock/",
method: "get", method: "get",
params: pagesInfo, params: { ...pagesInfo, ...activeParam },
queryKey: ["LiveStockFarmers", pagesInfo], queryKey: ["LiveStockFarmers", pagesInfo, selectedStatusKeys],
}); });
useEffect(() => { useEffect(() => {
@@ -42,6 +59,19 @@ export default function LiveStocks() {
item?.gender === 1 ? "نر" : "ماده", item?.gender === 1 ? "نر" : "ماده",
// item?.species?.name, // item?.species?.name,
item?.weight_type === "L" ? "سبک" : "سنگین", item?.weight_type === "L" ? "سبک" : "سنگین",
<span className="flex items-center gap-1">
{item?.active ? (
<>
<span className="text-green-500">فعال</span>
<CheckCircleIcon className="w-5 h-5 text-green-500" />
</>
) : (
<>
<span className="text-red-500">غیرفعال</span>
<XCircleIcon className="w-5 h-5 text-red-500" />
</>
)}
</span>,
<Popover key={i}> <Popover key={i}>
<Tooltip title="ویرایش دام" position="right"> <Tooltip title="ویرایش دام" position="right">
<Button <Button
@@ -77,6 +107,18 @@ export default function LiveStocks() {
return ( return (
<Grid container column> <Grid container column>
<Grid container className="items-center gap-2">
<Grid>
<AutoComplete
inPage
size="small"
data={statusItems}
selectedKeys={selectedStatusKeys}
onChange={setSelectedStatusKeys}
title="فیلتر وضعیت"
/>
</Grid>
</Grid>
<Table <Table
className="mt-2" className="mt-2"
onChange={(e) => { onChange={(e) => {
@@ -95,6 +137,7 @@ export default function LiveStocks() {
"جنسیت", "جنسیت",
// "گونه", // "گونه",
"دسته وزنی", "دسته وزنی",
"وضعیت",
"عملیات", "عملیات",
]} ]}
rows={pagesTableData} rows={pagesTableData}

View File

@@ -1,17 +1,17 @@
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import { useState } from "react"; import { useState } from "react";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import { OrganizationsList } from "../partials/management/OrganizationsList"; import { OrganizationsList } from "../../partials/LiveStock/management/OrganizationsList";
import { OrganizationsTypes } from "../partials/management/OrganizationsTypes"; import { OrganizationsTypes } from "../../partials/LiveStock/management/OrganizationsTypes";
export default function Organizations() { export default function Organizations() {
const tabItems = [ const tabItems = [
{ label: "سازمان ها" },
{ {
label: "نهاد", label: "نهاد",
page: "organizations", page: "organizations",
access: "Show-Organization-Type", access: "Show-Organization-Type",
}, },
{ label: "سازمان ها" },
]; ];
const [selectedTab, setSelectedTab] = useState<number>(0); const [selectedTab, setSelectedTab] = useState<number>(0);
@@ -22,7 +22,7 @@ export default function Organizations() {
return ( return (
<Grid container column className="gap-2"> <Grid container column className="gap-2">
<Tabs tabs={tabItems} onChange={handleTabChange} size="medium" /> <Tabs tabs={tabItems} onChange={handleTabChange} size="medium" />
{selectedTab === 0 ? <OrganizationsList /> : <OrganizationsTypes />} {selectedTab === 0 ? <OrganizationsTypes /> : <OrganizationsList />}
</Grid> </Grid>
); );
} }

View File

@@ -1,18 +1,18 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { AddPos } from "../partials/pos/AddPos"; import { AddPos } from "../../partials/LiveStock/pos/AddPos";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { useUserProfileStore } from "../context/zustand-store/userStore"; import { useUserProfileStore } from "../../context/zustand-store/userStore";
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { AllocatePos } from "../partials/pos/AllocatePos"; import { AllocatePos } from "../../partials/LiveStock/pos/AllocatePos";
import { CreditCardIcon } from "@heroicons/react/24/outline"; import { CreditCardIcon } from "@heroicons/react/24/outline";
import { POS_POS_LIST } from "../routes/paths"; import { POS_POS_LIST } from "../../routes/paths";
export default function Pos() { export default function Pos() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,18 +1,18 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { AddPos } from "../partials/pos/AddPos"; import { AddPos } from "../../partials/LiveStock/pos/AddPos";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { AllocatePos } from "../partials/pos/AllocatePos"; import { AllocatePos } from "../../partials/LiveStock/pos/AllocatePos";
import { PosAllocateOrganizationAccount } from "../partials/pos/PosAllocateOrganizationAccount"; import { PosAllocateOrganizationAccount } from "../../partials/LiveStock/pos/PosAllocateOrganizationAccount";
import { AllocateAccountToBroker } from "../partials/pos/AllocateAccountToBroker"; import { AllocateAccountToBroker } from "../../partials/LiveStock/pos/AllocateAccountToBroker";
import { BooleanQuestion } from "../components/BooleanQuestion/BooleanQuestion"; import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion";
export default function PosAccounts() { export default function PosAccounts() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
@@ -102,7 +102,7 @@ export default function PosAccounts() {
/> />
</Popover>, </Popover>,
]; ];
} },
); );
setAccountsTableData(formattedData); setAccountsTableData(formattedData);
} }

View File

@@ -1,12 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { POS_COMPANIES } from "../routes/paths"; import { POS_COMPANIES } from "../../routes/paths";
export default function PosCompanies() { export default function PosCompanies() {
const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 }); const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 });

View File

@@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import { Attributes } from "../partials/feed-input/Attributes"; import { Attributes } from "../../partials/LiveStock/feed-input/Attributes";
import { Brokers } from "../partials/feed-input/Brokers"; import { Brokers } from "../../partials/LiveStock/feed-input/Brokers";
import { SaleUnits } from "../partials/feed-input/SaleUnits"; import { SaleUnits } from "../../partials/LiveStock/feed-input/SaleUnits";
const tabItems = [ const tabItems = [
{ label: "مولفه" }, { label: "مولفه" },
{ label: "کارگزار" }, { label: "کارگزار" },

View File

@@ -1,21 +1,21 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import sabos from "../assets/images/products/saboos.png"; import sabos from "../../assets/images/products/saboos.png";
import jo from "../assets/images/products/jo.png"; import jo from "../../assets/images/products/jo.png";
import soya from "../assets/images/products/soya.png"; import soya from "../../assets/images/products/soya.png";
import zorat from "../assets/images/products/zorat.png"; import zorat from "../../assets/images/products/zorat.png";
import goosfandi from "../assets/images/products/constantre-goosfandi.png"; import goosfandi from "../../assets/images/products/constantre-goosfandi.png";
import parvari from "../assets/images/products/constantre-parvari.png"; import parvari from "../../assets/images/products/constantre-parvari.png";
import porTolid from "../assets/images/products/constantre-gave-shiri-por-tolid.png"; import porTolid from "../../assets/images/products/constantre-gave-shiri-por-tolid.png";
import shiriMotevaset from "../assets/images/products/constantre-gave-shiri-motevaset.png"; import shiriMotevaset from "../../assets/images/products/constantre-gave-shiri-motevaset.png";
import defaultImage from "../assets/images/products/default.png"; import defaultImage from "../../assets/images/products/default.png";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { AddProduct } from "../partials/feed-input/AddProduct"; import { AddProduct } from "../../partials/LiveStock/feed-input/AddProduct";
import { getAbleToSee } from "../utils/getAbleToSee"; import { getAbleToSee } from "../../utils/getAbleToSee";
import { DeleteProduct } from "../partials/feed-input/DeleteProduct"; import { DeleteProduct } from "../../partials/LiveStock/feed-input/DeleteProduct";
interface Category { interface Category {
id: number; id: number;
@@ -232,7 +232,7 @@ export default function Products() {
<button <button
className={`${getAbleToSee( className={`${getAbleToSee(
"feed_input_products", "feed_input_products",
"Edit-Product" "Edit-Product",
)} rounded-full text-primary-600 text-sm`} )} rounded-full text-primary-600 text-sm`}
onClick={() => { onClick={() => {
openModal({ openModal({
@@ -252,7 +252,7 @@ export default function Products() {
}} }}
className={`${getAbleToSee( className={`${getAbleToSee(
"feed_input_products", "feed_input_products",
"Delete-Product" "Delete-Product",
)} text-red-400 rounded-lg text-sm`} )} text-red-400 rounded-lg text-sm`}
> >
<TrashIcon className="w-5" /> <TrashIcon className="w-5" />

View File

@@ -1,13 +1,13 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { AddProductCategory } from "../partials/feed-input/AddProductCategory"; import { AddProductCategory } from "../../partials/LiveStock/feed-input/AddProductCategory";
export const ProductsCategories = () => { export const ProductsCategories = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,11 +1,11 @@
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import { useState } from "react"; import { useState } from "react";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import { QuotaActives } from "../partials/quota/QuotaActives"; import { QuotaActives } from "../../partials/LiveStock/quota/QuotaActives";
import { QuotaClosed } from "../partials/quota/QuotaClosed"; import { QuotaClosed } from "../../partials/LiveStock/quota/QuotaClosed";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { QuotaDistributions } from "../partials/quota/QuotaDistributions"; import { QuotaDistributions } from "../../partials/LiveStock/quota/QuotaDistributions";
import { QuotaAllDistributions } from "../partials/quota/QuotaAllDistributions"; import { QuotaAllDistributions } from "../../partials/LiveStock/quota/QuotaAllDistributions";
export default function Quota() { export default function Quota() {
const [selectedTab, setSelectedTab] = useState<number>(0); const [selectedTab, setSelectedTab] = useState<number>(0);

View File

@@ -1,13 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import Button from "../../components/Button/Button";
import Button from "../components/Button/Button"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { useModalStore } from "../../context/zustand-store/appStore";
import { useModalStore } from "../context/zustand-store/appStore"; import { useApiRequest } from "../../utils/useApiRequest";
import { useApiRequest } from "../utils/useApiRequest"; import { Grid } from "../../components/Grid/Grid";
import { Grid } from "../components/Grid/Grid"; import Table from "../../components/Table/Table";
import Table from "../components/Table/Table"; import { LiveStockRancherAllocateIncentivePlan } from "../../partials/LiveStock/live-stock/LiveStockRancherAllocateIncentivePlan";
import { AddIncentivePlan } from "../partials/quota/AddIncentivePlan";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
export default function RancherPlans() { export default function RancherPlans() {
@@ -34,25 +33,11 @@ export default function RancherPlans() {
item?.livestock_type_name, item?.livestock_type_name,
item?.allowed_quantity?.toLocaleString(), item?.allowed_quantity?.toLocaleString(),
<Popover key={i}> <Popover key={i}>
<Tooltip title="ویرایش طرح" position="right">
<Button
page="farmer_plans"
access="Edit-Rancher-Incentive-Plan"
variant="edit"
onClick={() => {
openModal({
title: "ویرایش طرح",
content: <AddIncentivePlan getData={refetch} item={item} />,
});
}}
/>
</Tooltip>
<DeleteButtonForPopOver <DeleteButtonForPopOver
page="farmer_plans" page="farmer_plans"
access="Delete-Rancher-Incentive-Plan" access="Delete-Rancher-Incentive-Plan"
title="از حذف طرح تشویقی اطمینان دارید؟" title="از حذف طرح تشویقی اطمینان دارید؟"
api={`/product/web/api/v1/incentive_plan/${item?.id}/`} api={`/product/web/api/v1/rancher_incentive_plan/${item?.id}/`}
getData={refetch} getData={refetch}
/> />
</Popover>, </Popover>,
@@ -67,7 +52,6 @@ export default function RancherPlans() {
<Grid container className="items-center gap-2"> <Grid container className="items-center gap-2">
<Grid> <Grid>
<Button <Button
className="hidden"
size="small" size="small"
page="farmer_plans" page="farmer_plans"
access="Post-Rancher-Incentive-Plan" access="Post-Rancher-Incentive-Plan"
@@ -75,7 +59,12 @@ export default function RancherPlans() {
onClick={() => onClick={() =>
openModal({ openModal({
title: "تخصیص طرح تشویقی", title: "تخصیص طرح تشویقی",
content: <AddIncentivePlan getData={refetch} />, content: (
<LiveStockRancherAllocateIncentivePlan
getData={refetch}
rancher={farmid}
/>
),
}) })
} }
> >
@@ -95,13 +84,7 @@ export default function RancherPlans() {
title={`طرح های تشویقی دامدار (${name})`} title={`طرح های تشویقی دامدار (${name})`}
isPaginated isPaginated
count={apiData?.count || 10} count={apiData?.count || 10}
columns={[ columns={["ردیف", "نام طرح", "نوع دام", "تعداد راس مجاز", "عملیات"]}
"ردیف",
"نام طرح",
"نوع دام",
"تعداد راس مجاز",
// , "عملیات"
]}
rows={tableData} rows={tableData}
/> />
</Grid> </Grid>

View File

@@ -1,10 +1,10 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import { QuotaReportingProducts } from "../partials/quota/QuotaReportingProducts"; import { QuotaReportingProducts } from "../../partials/LiveStock/quota/QuotaReportingProducts";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { QuotaReportingProductDetails } from "../partials/quota/QuotaReportingProductDetails"; import { QuotaReportingProductDetails } from "../../partials/LiveStock/quota/QuotaReportingProductDetails";
import { QuotaReportingQuotaDistributions } from "../partials/quota/QuotaReportingQuotaDistributions"; import { QuotaReportingQuotaDistributions } from "../../partials/LiveStock/quota/QuotaReportingQuotaDistributions";
const tabItems = [ const tabItems = [
{ label: "محصول" }, { label: "محصول" },

View File

@@ -1,17 +1,17 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { AddRole } from "../partials/management/AddRole"; import { AddRole } from "../../partials/LiveStock/management/AddRole";
import { getFaPermissions } from "../utils/getFaPermissions"; import { getFaPermissions } from "../../utils/getFaPermissions";
import ShowStringList from "../components/ShowStringList/ShowStringList"; import ShowStringList from "../../components/ShowStringList/ShowStringList";
import ShowMoreInfo from "../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
import Typography from "../components/Typography/Typography"; import Typography from "../../components/Typography/Typography";
export default function Roles() { export default function Roles() {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
@@ -40,7 +40,7 @@ export default function Roles() {
acc[item.page].names.push(item.name); acc[item.page].names.push(item.name);
acc[item.page].descriptions.push(item.description); acc[item.page].descriptions.push(item.description);
return acc; return acc;
}, {}) }, {}),
); );
return [ return [

View File

@@ -1,10 +1,10 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import SettingCard from "../components/SettingCard/SettingCard"; import SettingCard from "../../components/SettingCard/SettingCard";
import { ShieldExclamationIcon, MapPinIcon } from "@heroicons/react/24/outline"; import { ShieldExclamationIcon, MapPinIcon } from "@heroicons/react/24/outline";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { CooperativesSettingsTable } from "../partials/units/CooperativesSettingsTable"; import { CooperativesSettingsTable } from "../../partials/LiveStock/units/CooperativesSettingsTable";
const tabItems = [ const tabItems = [
{ label: "اتحادیه ها", visible: false }, { label: "اتحادیه ها", visible: false },

View File

@@ -1,8 +1,8 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../../components/Tab/Tab";
import TagActiveDistributions from "../partials/tagging/TagActiveDistributions"; import TagActiveDistributions from "../../partials/LiveStock/tagging/TagActiveDistributions";
import TagCanceledDistributions from "../partials/tagging/TagCanceledDistributions"; import TagCanceledDistributions from "../../partials/LiveStock/tagging/TagCanceledDistributions";
export default function TagDistribtution() { export default function TagDistribtution() {
const [selectedTab, setSelectedTab] = useState<number>(0); const [selectedTab, setSelectedTab] = useState<number>(0);

View File

@@ -0,0 +1,412 @@
import { useEffect, useState } from "react";
import { useParams } from "@tanstack/react-router";
import { Bars3Icon, CubeIcon, SparklesIcon } from "@heroicons/react/24/outline";
import { useApiRequest } from "../../utils/useApiRequest";
import { useModalStore } from "../../context/zustand-store/appStore";
import { formatJustDate, formatJustTime } from "../../utils/formatTime";
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
import { Grid } from "../../components/Grid/Grid";
import Typography from "../../components/Typography/Typography";
import Table from "../../components/Table/Table";
import { Popover } from "../../components/PopOver/PopOver";
import Button from "../../components/Button/Button";
import { Tooltip } from "../../components/Tooltip/Tooltip";
import { DistributeFromDistribution } from "../../partials/LiveStock/tagging/DistributeFromDistribution";
import { DocumentOperation } from "../../components/DocumentOperation/DocumentOperation";
import { DocumentDownloader } from "../../components/DocumentDownloader/DocumentDownloader";
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion";
import { useUserProfileStore } from "../../context/zustand-store/userStore";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
const speciesMap: Record<number, string> = {
1: "گاو",
2: "گاومیش",
3: "شتر",
4: "گوسفند",
5: "بز",
};
export default function TagDistribtutionDetails() {
const { id } = useParams({ strict: false });
const { openModal } = useModalStore();
const [childTableInfo, setChildTableInfo] = useState({
page: 1,
page_size: 10,
});
const [childTableData, setChildTableData] = useState([]);
const { data, refetch: refetchData } = useApiRequest({
api: `/tag/web/api/v1/tag_distribution_batch/${id}/`,
method: "get",
queryKey: ["tagBatchInnerDashboard", id],
enabled: !!id,
});
const { data: childData, refetch: refetchChildList } = useApiRequest({
api: `/tag/web/api/v1/tag_distribution_batch/${id}/child_list/`,
method: "get",
queryKey: ["tagDistributionChildList", id, childTableInfo],
params: {
...childTableInfo,
},
enabled: !!id,
});
const { profile } = useUserProfileStore();
const showAssignDocColumn =
childData?.results?.some(
(item: any) =>
profile?.role?.type?.key === "ADM" ||
profile?.organization?.id === item?.assigned_org?.id,
) ?? false;
const AbleToSeeAssignDoc = (item: any) => {
if (
profile?.role?.type?.key === "ADM" ||
profile?.organization?.id === item?.assigned_org?.id
) {
return (
<DocumentOperation
key={item?.id}
downloadLink={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/distribution_pdf_view/`}
payloadKey="dist_exit_document"
validFiles={["pdf"]}
page="tag_distribution"
access="Upload-Assign-Document"
uploadLink={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/assign_document/`}
onUploadSuccess={handleUpdate}
/>
);
} else {
return "-";
}
};
const handleUpdate = () => {
refetchData();
refetchChildList();
};
useEffect(() => {
if (childData?.results) {
const formattedData = childData.results.map(
(item: any, index: number) => {
const dist = item?.distributions;
return [
childTableInfo.page === 1
? index + 1
: index +
childTableInfo.page_size * (childTableInfo.page - 1) +
1,
item?.dist_batch_identity ?? "-",
`${formatJustDate(item?.create_date) ?? "-"} (${
formatJustDate(item?.create_date)
? (formatJustTime(item?.create_date) ?? "-")
: "-"
})`,
item?.assigner_org?.name ?? "-",
item?.assigned_org?.name ?? "-",
item?.total_tag_count?.toLocaleString() ?? "-",
item?.total_distributed_tag_count?.toLocaleString() ?? "-",
item?.remaining_tag_count?.toLocaleString() ?? "-",
item?.distribution_type === "batch"
? "توزیع گروهی"
: "توزیع تصادفی",
<ShowMoreInfo key={item?.id} title="جزئیات توزیع">
<Grid container column className="gap-4 w-full">
{dist?.map((opt: any, idx: number) => (
<Grid
key={idx}
container
column
className="gap-3 w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4"
>
<Grid container className="gap-2 items-center">
<SparklesIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
گونه:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{speciesMap[opt?.species_code] ?? "-"}
</Typography>
</Grid>
{item?.distribution_type === "batch" &&
opt?.serial_from != null && (
<Grid container className="gap-2 items-center">
<Bars3Icon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
بازه سریال:
</Typography>
<Typography
variant="body2"
className="text-gray-600 dark:text-gray-400"
>
از {opt?.serial_from ?? "-"} تا{" "}
{opt?.serial_to ?? "-"}
</Typography>
</Grid>
)}
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
تعداد پلاک:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.total_tag_count?.toLocaleString() ?? "-"}
</Typography>
</Grid>
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
پلاک های توزیع شده:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.distributed_number?.toLocaleString() ?? "-"}
</Typography>
</Grid>
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
پلاک های باقیمانده:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.remaining_number?.toLocaleString() ?? "-"}
</Typography>
</Grid>
</Grid>
))}
</Grid>
</ShowMoreInfo>,
...(showAssignDocColumn ? [AbleToSeeAssignDoc(item)] : []),
<DocumentDownloader
key={`doc-${item?.id}`}
link={item?.warehouse_exit_doc}
title="دانلود"
/>,
item?.exit_doc_status ? (
"تایید شده"
) : (
<Button
key={`btn-${item?.id}`}
page="tag_distribution"
access="Accept-Assign-Document"
size="small"
disabled={item?.exit_doc_status}
onClick={() => {
openModal({
title: "تایید سند خروج",
content: (
<BooleanQuestion
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/accept_exit_doc/`}
method="post"
getData={handleUpdate}
title="آیا از تایید سند خروج مطمئنید؟"
/>
),
});
}}
>
تایید سند خروج
</Button>
),
<Popover key={`popover-${item?.id}`}>
<Tooltip title="ویرایش توزیع" position="right">
<Button
variant="edit"
page="tag_distribution"
access="Submit-Tag-Distribution"
onClick={() => {
openModal({
title: "ویرایش توزیع",
content: (
<DistributeFromDistribution
getData={handleUpdate}
item={item}
isEdit
parentDistributions={data?.distributions}
/>
),
});
}}
/>
</Tooltip>
<DeleteButtonForPopOver
page="tag_distribution"
access="Delete-Tag-Distribution"
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/`}
getData={handleUpdate}
/>
</Popover>,
];
},
);
setChildTableData(formattedData);
} else {
setChildTableData([]);
}
}, [childData, childTableInfo]);
const dist = data?.distributions;
return (
<Grid container column className="gap-4 mt-2">
<Grid isDashboard>
<Table
isDashboard
title="مشخصات توزیع پلاک"
noSearch
noPagination
columns={[
"شناسه توزیع",
"تاریخ ثبت",
"توزیع کننده",
"دریافت کننده",
"تعداد کل پلاک",
"پلاک های توزیع شده",
"پلاک های باقیمانده",
"نوع توزیع",
"جزئیات توزیع",
]}
rows={[
[
data?.dist_batch_identity ?? "-",
`${formatJustDate(data?.create_date) ?? "-"} (${
formatJustDate(data?.create_date)
? (formatJustTime(data?.create_date) ?? "-")
: "-"
})`,
data?.assigner_org?.name ?? "-",
data?.assigned_org?.name ?? "-",
data?.total_tag_count?.toLocaleString() ?? "-",
data?.total_distributed_tag_count?.toLocaleString() ?? "-",
data?.remaining_tag_count?.toLocaleString() ?? "-",
data?.distribution_type === "batch"
? "توزیع گروهی"
: "توزیع تصادفی",
<ShowMoreInfo key={data?.id} title="جزئیات توزیع">
<Grid container column className="gap-4 w-full">
{dist?.map((opt: any, index: number) => (
<Grid
key={index}
container
column
className="gap-3 w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4"
>
<Grid container className="gap-2 items-center">
<SparklesIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
گونه:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{speciesMap[opt?.species_code] ?? "-"}
</Typography>
</Grid>
{data?.distribution_type === "batch" &&
opt?.serial_from != null && (
<Grid container className="gap-2 items-center">
<Bars3Icon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
بازه سریال:
</Typography>
<Typography
variant="body2"
className="text-gray-600 dark:text-gray-400"
>
از {opt?.serial_from ?? "-"} تا{" "}
{opt?.serial_to ?? "-"}
</Typography>
</Grid>
)}
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
تعداد پلاک:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.total_tag_count?.toLocaleString() ?? "-"}
</Typography>
</Grid>
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
پلاک های توزیع شده:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.distributed_number?.toLocaleString() ?? "-"}
</Typography>
</Grid>
<Grid container className="gap-2 items-center">
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
<Typography variant="body2" className="font-medium">
پلاک های باقیمانده:
</Typography>
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300"
>
{opt?.remaining_number?.toLocaleString() ?? "-"}
</Typography>
</Grid>
</Grid>
))}
</Grid>
</ShowMoreInfo>,
],
]}
/>
</Grid>
<Table
className="mt-2"
onChange={setChildTableInfo}
count={childData?.count || 0}
isPaginated
title="توزیع های مجدد"
columns={[
"ردیف",
"شناسه توزیع",
"تاریخ ثبت",
"توزیع کننده",
"دریافت کننده",
"تعداد کل پلاک",
"پلاک های توزیع شده",
"پلاک های باقیمانده",
"نوع توزیع",
"جزئیات توزیع",
...(showAssignDocColumn ? ["امضا سند خروج از انبار"] : []),
"سند خروج از انبار",
"تایید سند خروج",
"عملیات",
]}
rows={childTableData}
/>
</Grid>
);
}

View File

@@ -0,0 +1,27 @@
import { useState } from "react";
import { Grid } from "../../components/Grid/Grid";
import Tabs from "../../components/Tab/Tab";
import Taggings from "../../partials/LiveStock/tagging/Taggings";
import Tags from "../../partials/LiveStock/tagging/Tags";
const tabItems = [
{ label: "ثبت پلاک" },
{ label: "پلاک ها", page: "tagging_detail", access: "Show-Tagging-Detail" },
];
export default function Tagging() {
const [selectedTab, setSelectedTab] = useState<number>(0);
const handleTabChange = (index: number) => {
setSelectedTab(index);
};
return (
<Grid container column className="justify-center mt-2">
<Tabs tabs={tabItems} onChange={handleTabChange} size="medium" />
<Grid container column className="mt-2">
{selectedTab === 0 && <Taggings />}
{selectedTab === 1 && <Tags />}
</Grid>
</Grid>
);
}

View File

@@ -1,16 +1,19 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { formatJustDate, formatJustTime } from "../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../utils/formatTime";
import { TableButton } from "../components/TableButton/TableButton"; import { TableButton } from "../../components/TableButton/TableButton";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import TransactionDetails from "../partials/transactions/TransactionDetails"; import TransactionDetails from "../../partials/LiveStock/transactions/TransactionDetails";
import { DashboardResponse, ProductSummaryItem } from "../types/transactions"; import {
import { ProductSummaryModal } from "../partials/transactions/ProductSummaryModal"; DashboardResponse,
import { PaginationParameters } from "../components/PaginationParameters/PaginationParameters"; ProductSummaryItem,
import TransactionSharingDetails from "../partials/transactions/TransactionSharingDetails"; } from "../../types/transactions";
import { convertNumberToPersian } from "../utils/convertNumberToPersian"; import { ProductSummaryModal } from "../../partials/LiveStock/transactions/ProductSummaryModal";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters";
import TransactionSharingDetails from "../../partials/LiveStock/transactions/TransactionSharingDetails";
import { convertNumberToPersian } from "../../utils/convertNumberToPersian";
type TransactionResponse = { type TransactionResponse = {
results?: any[]; results?: any[];
@@ -72,7 +75,7 @@ export default function Transactions() {
.filter((unit: any) => unit); .filter((unit: any) => unit);
const totalWeight = items.reduce( const totalWeight = items.reduce(
(sum: number, p: any) => sum + (p?.weight || 0), (sum: number, p: any) => sum + (p?.weight || 0),
0 0,
); );
let weightNature; let weightNature;
@@ -210,7 +213,7 @@ export default function Transactions() {
DashboardData?.transaction_summary?.total_amount?.toLocaleString() || DashboardData?.transaction_summary?.total_amount?.toLocaleString() ||
0, 0,
convertNumberToPersian( convertNumberToPersian(
DashboardData?.transaction_summary?.total_amount || 0 DashboardData?.transaction_summary?.total_amount || 0,
), ),
DashboardData?.transaction_summary?.total_weight?.toLocaleString() || DashboardData?.transaction_summary?.total_weight?.toLocaleString() ||
0, 0,

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { COOPERATIVE_LIST } from "../routes/paths"; import { COOPERATIVE_LIST } from "../../routes/paths";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
export default function Unions() { export default function Unions() {

View File

@@ -0,0 +1,81 @@
import { useEffect, useState } from "react";
import { Grid } from "../../components/Grid/Grid";
import Table from "../../components/Table/Table";
import { useApiRequest } from "../../utils/useApiRequest";
import { formatJustDate } from "../../utils/formatTime";
import CheckTrackingCode from "../../components/CheckTrackingCode/CheckTrackingCode";
type UnitsInventoryResponse = {
results?: any[];
count?: number;
};
export default function UnitsInventory() {
const [params, setParams] = useState({ page: 1, page_size: 10 });
const [tableData, setTableData] = useState<any[]>([]);
const { data } = useApiRequest<UnitsInventoryResponse>({
api: "/rsi/web/api/v1/org_rsi_data/",
method: "get",
params,
queryKey: ["units_inventory", params],
});
useEffect(() => {
if (data?.results) {
const formattedData = data.results.map((item: any, i: number) => [
params.page === 1
? i + 1
: i + params.page_size * (params.page - 1) + 1,
<CheckTrackingCode key={`${item?.id}-tracking`} tracking={item?.tracking} />,
item?.record_id || "-",
formatJustDate(item?.data) || "-",
item?.product || "-",
item?.quantity ? Number(item.quantity).toLocaleString() : "-",
item?.unit || "-",
item?.origin_province || "-",
item?.destination_province || "-",
item?.origin_city || "-",
item?.destination_city || "-",
item?.origin || "-",
item?.destination || "-",
item?.driver_name || "-",
item?.car_tracking_code || "-",
]);
setTableData(formattedData);
} else {
setTableData([]);
}
}, [data]);
return (
<Grid container column>
<Table
className="mt-2"
onChange={setParams}
count={data?.count || 0}
isPaginated
title="انبار واحدها"
columns={[
"ردیف",
"کد رهگیری",
"شماره رکورد",
"تاریخ",
"محصول",
"مقدار",
"واحد",
"استان مبدا",
"استان مقصد",
"شهرستان مبدا",
"شهرستان مقصد",
"مبدا",
"مقصد",
"راننده",
"کد رهگیری خودرو",
]}
rows={tableData}
/>
</Grid>
);
}

View File

@@ -1,22 +1,22 @@
import Table from "../components/Table/Table"; import Table from "../../components/Table/Table";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import { useApiRequest } from "../utils/useApiRequest"; import { useApiRequest } from "../../utils/useApiRequest";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { getFaPermissions } from "../utils/getFaPermissions"; import { getFaPermissions } from "../../utils/getFaPermissions";
import { Popover } from "../components/PopOver/PopOver"; import { Popover } from "../../components/PopOver/PopOver";
import { Tooltip } from "../components/Tooltip/Tooltip"; import { Tooltip } from "../../components/Tooltip/Tooltip";
import Button from "../components/Button/Button"; import Button from "../../components/Button/Button";
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
import { import {
useDrawerStore, useDrawerStore,
useModalStore, useModalStore,
} from "../context/zustand-store/appStore"; } from "../../context/zustand-store/appStore";
import { EditAccess } from "../partials/management/EditAccess"; import { EditAccess } from "../../partials/Admin/EditAccess";
import { AddUser } from "../partials/management/AddUser"; import { AddUser } from "../../partials/LiveStock/management/AddUser";
import ShowStringList from "../components/ShowStringList/ShowStringList"; import ShowStringList from "../../components/ShowStringList/ShowStringList";
import ShowMoreInfo from "../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
import AutoComplete from "../components/AutoComplete/AutoComplete"; import AutoComplete from "../../components/AutoComplete/AutoComplete";
import { useUserProfileStore } from "../context/zustand-store/userStore"; import { useUserProfileStore } from "../../context/zustand-store/userStore";
type PermissionType = { type PermissionType = {
page_name: string; page_name: string;
@@ -95,7 +95,7 @@ export default function Users() {
> >
<ShowStringList <ShowStringList
strings={item?.permissions?.map((option: PermissionType) => strings={item?.permissions?.map((option: PermissionType) =>
getFaPermissions(option?.page_name) getFaPermissions(option?.page_name),
)} )}
/> />
</ShowMoreInfo>, </ShowMoreInfo>,

View File

@@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from "react";
import { Grid } from "../components/Grid/Grid"; import { Grid } from "../components/Grid/Grid";
import Tabs from "../components/Tab/Tab"; import Tabs from "../components/Tab/Tab";
import Pages from "../partials/management/Pages"; import Pages from "../partials/Admin/Pages";
import Access from "../partials/management/Access"; import Access from "../partials/Admin/Access";
import UnusedAccess from "../partials/management/UnusedAccess"; import UnusedAccess from "../partials/Admin/UnusedAccess";
const tabItems = [ const tabItems = [
{ label: "صفحات" }, { label: "صفحات" },

View File

@@ -7,7 +7,11 @@ import { useUserProfileStore } from "../context/zustand-store/userStore";
import { getUserPermissions } from "../utils/getUserAvalableItems"; import { getUserPermissions } from "../utils/getUserAvalableItems";
import { ItemWithSubItems } from "../types/userPermissions"; import { ItemWithSubItems } from "../types/userPermissions";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { Bars3Icon, QueueListIcon } from "@heroicons/react/24/outline"; import {
Bars3Icon,
QueueListIcon,
Squares2X2Icon,
} from "@heroicons/react/24/outline";
import { getFaPermissions } from "../utils/getFaPermissions"; import { getFaPermissions } from "../utils/getFaPermissions";
import SVGImage from "../components/SvgImage/SvgImage"; import SVGImage from "../components/SvgImage/SvgImage";
@@ -58,13 +62,13 @@ export const Menu = () => {
const { profile } = useUserProfileStore(); const { profile } = useUserProfileStore();
const menuItems: ItemWithSubItems[] = getUserPermissions( const menuItems: ItemWithSubItems[] = getUserPermissions(
profile?.permissions profile?.permissions,
); );
const getOpenedItem = () => { const getOpenedItem = () => {
if (window.location.pathname !== "/") { if (window.location.pathname !== "/") {
const matchedIndex = menuItems.findIndex((item) => const matchedIndex = menuItems.findIndex((item) =>
item.subItems.some((sub) => sub.path === window.location.pathname) item.subItems.some((sub) => sub.path === window.location.pathname),
); );
return matchedIndex; return matchedIndex;
} else { } else {
@@ -73,12 +77,62 @@ export const Menu = () => {
}; };
const [openIndex, setOpenIndex] = useState<number | null>(getOpenedItem()); const [openIndex, setOpenIndex] = useState<number | null>(getOpenedItem());
const [openDomains, setOpenDomains] = useState<Record<string, boolean>>({});
const navigate = useNavigate(); const navigate = useNavigate();
const adminItems = menuItems
.map((item, index) => ({ ...item, originalIndex: index }))
.filter((item) => item.en === "admin");
const indexedNonAdminItems = menuItems
.map((item, index) => ({ ...item, originalIndex: index }))
.filter((item) => item.en !== "admin");
const permissionDomainMap = new Map<string, string>();
(profile?.permissions || []).forEach((permission: any) => {
if (permission?.page_name) {
permissionDomainMap.set(
permission.page_name,
permission?.domain_fa_name || "سایر حوزه ها",
);
}
});
const groupedItems = indexedNonAdminItems.reduce(
(acc, item) => {
const firstSubItem = item.subItems?.find((sub) =>
permissionDomainMap.has(sub.name),
);
const domainTitle = firstSubItem
? permissionDomainMap.get(firstSubItem.name) || "سایر حوزه ها"
: "سایر حوزه ها";
if (!acc[domainTitle]) {
acc[domainTitle] = [];
}
acc[domainTitle].push(item);
return acc;
},
{} as Record<
string,
(ItemWithSubItems & { originalIndex: number })[]
>,
);
const showDomainGrouping = Object.keys(groupedItems).length > 1;
const toggleSubmenu = (index: number) => { const toggleSubmenu = (index: number) => {
setOpenIndex((prev) => (prev === index ? null : index)); setOpenIndex((prev) => (prev === index ? null : index));
}; };
const isDomainOpen = (domainTitle: string) =>
openDomains[domainTitle] ?? true;
const toggleDomain = (domainTitle: string) => {
setOpenDomains((prev) => ({
...prev,
[domainTitle]: !(prev[domainTitle] ?? true),
}));
};
return ( return (
<Grid <Grid
container container
@@ -99,14 +153,14 @@ export const Menu = () => {
animate="visible" animate="visible"
className="flex flex-col items-center gap-4 w-full" className="flex flex-col items-center gap-4 w-full"
> >
{menuItems.map(({ fa, icon: Icon, subItems }, index) => ( {adminItems.map(({ fa, icon: Icon, subItems, originalIndex }) => (
<motion.div <motion.div
key={index} key={`admin-${originalIndex}`}
variants={itemVariants} variants={itemVariants}
className="w-full max-w-sm" className="w-full max-w-sm"
> >
<motion.button <motion.button
onClick={() => toggleSubmenu(index)} onClick={() => toggleSubmenu(originalIndex)}
whileTap={{ scale: 0.97 }} whileTap={{ scale: 0.97 }}
className="w-full flex justify-between items-center gap-3 px-4 py-3 rounded-lg shadow-xs dark:shadow-sm shadow-dark-300 dark:shadow-dark-500 backdrop-blur-md bg-gradient-to-r from-transparent to-transparent dark:via-gray-800 via-gray-100 border border-dark-200 dark:border-dark-700 text-dark-800 dark:text-dark-100 transition-all duration-200" className="w-full flex justify-between items-center gap-3 px-4 py-3 rounded-lg shadow-xs dark:shadow-sm shadow-dark-300 dark:shadow-dark-500 backdrop-blur-md bg-gradient-to-r from-transparent to-transparent dark:via-gray-800 via-gray-100 border border-dark-200 dark:border-dark-700 text-dark-800 dark:text-dark-100 transition-all duration-200"
> >
@@ -120,13 +174,13 @@ export const Menu = () => {
</div> </div>
<ChevronDownIcon <ChevronDownIcon
className={`w-5 h-5 text-dark-500 dark:text-dark-300 transition-transform duration-300 ${ className={`w-5 h-5 text-dark-500 dark:text-dark-300 transition-transform duration-300 ${
openIndex === index ? "rotate-180" : "" openIndex === originalIndex ? "rotate-180" : ""
}`} }`}
/> />
</motion.button> </motion.button>
<AnimatePresence> <AnimatePresence>
{openIndex === index && ( {openIndex === originalIndex && (
<motion.div <motion.div
key="submenu" key="submenu"
variants={submenuVariants} variants={submenuVariants}
@@ -156,6 +210,146 @@ export const Menu = () => {
</AnimatePresence> </AnimatePresence>
</motion.div> </motion.div>
))} ))}
{showDomainGrouping
? Object.entries(groupedItems).map(([domainTitle, domainItems]) => (
<div key={domainTitle} className="w-full max-w-sm">
<button
onClick={() => toggleDomain(domainTitle)}
className="w-full px-1 py-1 text-primary-700 dark:text-primary-200 text-sm font-bold flex items-center justify-between cursor-pointer"
>
<div className="flex items-center gap-2">
<Squares2X2Icon className="w-4 h-4 text-primary-600 dark:text-primary-300" />
<span>{domainTitle}</span>
</div>
<ChevronDownIcon
className={`w-4 h-4 transition-transform duration-300 ${
isDomainOpen(domainTitle) ? "rotate-180" : ""
}`}
/>
</button>
{(isDomainOpen(domainTitle) || !domainTitle) && (
<div className="mt-1 mr-2 border-r-2 border-primary-200 dark:border-primary-500/40 pr-2 flex flex-col gap-3">
{domainItems.map(
({ fa, icon: Icon, subItems, originalIndex }) => (
<motion.div
key={`group-${domainTitle}-${originalIndex}`}
variants={itemVariants}
className="w-full"
>
<motion.button
onClick={() => toggleSubmenu(originalIndex)}
whileTap={{ scale: 0.97 }}
className="w-full flex justify-between items-center gap-3 px-4 py-3 rounded-lg shadow-xs dark:shadow-sm shadow-dark-300 dark:shadow-dark-500 backdrop-blur-md bg-gradient-to-r from-transparent to-transparent dark:via-gray-800 via-gray-100 border border-dark-200 dark:border-dark-700 text-dark-800 dark:text-dark-100 transition-all duration-200"
>
<div className="flex items-center gap-3">
<SVGImage
src={Icon}
className={` text-primary-800 dark:text-primary-100`}
/>
<span className="text-base font-medium">{fa}</span>
</div>
<ChevronDownIcon
className={`w-5 h-5 text-dark-500 dark:text-dark-300 transition-transform duration-300 ${
openIndex === originalIndex ? "rotate-180" : ""
}`}
/>
</motion.button>
<AnimatePresence>
{openIndex === originalIndex && (
<motion.div
key="submenu"
variants={submenuVariants}
initial="hidden"
animate="visible"
exit="exit"
className="mt-2 mr-4 border-r-2 border-primary-500 dark:border-primary-400 pr-4 flex flex-col gap-2"
>
{subItems
.filter((item) => !item?.path.includes("$"))
?.map((sub, subIndex) => (
<motion.button
onClick={() => {
navigate({ to: sub.path });
}}
key={subIndex}
whileTap={{ scale: 0.97 }}
className="text-sm flex items-center gap-2 text-dark-700 dark:text-dark-200 bg-white dark:bg-dark-700 shadow-sm px-3 py-2 rounded-lg w-full text-right"
>
<QueueListIcon className="w-3" />
{getFaPermissions(sub.name)}
</motion.button>
))}
</motion.div>
)}
</AnimatePresence>
</motion.div>
),
)}
</div>
)}
</div>
))
: indexedNonAdminItems.map(
({ fa, icon: Icon, subItems, originalIndex }) => (
<motion.div
key={`plain-${originalIndex}`}
variants={itemVariants}
className="w-full max-w-sm"
>
<motion.button
onClick={() => toggleSubmenu(originalIndex)}
whileTap={{ scale: 0.97 }}
className="w-full flex justify-between items-center gap-3 px-4 py-3 rounded-lg shadow-xs dark:shadow-sm shadow-dark-300 dark:shadow-dark-500 backdrop-blur-md bg-gradient-to-r from-transparent to-transparent dark:via-gray-800 via-gray-100 border border-dark-200 dark:border-dark-700 text-dark-800 dark:text-dark-100 transition-all duration-200"
>
<div className="flex items-center gap-3">
<SVGImage
src={Icon}
className={` text-primary-800 dark:text-primary-100`}
/>
<span className="text-base font-medium">{fa}</span>
</div>
<ChevronDownIcon
className={`w-5 h-5 text-dark-500 dark:text-dark-300 transition-transform duration-300 ${
openIndex === originalIndex ? "rotate-180" : ""
}`}
/>
</motion.button>
<AnimatePresence>
{openIndex === originalIndex && (
<motion.div
key="submenu"
variants={submenuVariants}
initial="hidden"
animate="visible"
exit="exit"
className="mt-2 mr-4 border-r-2 border-primary-500 dark:border-primary-400 pr-4 flex flex-col gap-2"
>
{subItems
.filter((item) => !item?.path.includes("$"))
?.map((sub, subIndex) => (
<motion.button
onClick={() => {
navigate({ to: sub.path });
}}
key={subIndex}
whileTap={{ scale: 0.97 }}
className="text-sm flex items-center gap-2 text-dark-700 dark:text-dark-200 bg-white dark:bg-dark-700 shadow-sm px-3 py-2 rounded-lg w-full text-right"
>
<QueueListIcon className="w-3" />
{getFaPermissions(sub.name)}
</motion.button>
))}
</motion.div>
)}
</AnimatePresence>
</motion.div>
),
)}
</motion.div> </motion.div>
</Grid> </Grid>
); );

View File

@@ -1,56 +0,0 @@
import { useEffect, useState } from "react";
import { useParams } from "@tanstack/react-router";
import { useApiRequest } from "../utils/useApiRequest";
import { Grid } from "../components/Grid/Grid";
import Table from "../components/Table/Table";
export default function TagDistribtutionDetails() {
const { id } = useParams({ strict: false });
const [tableData, setTableData] = useState([]);
const { data } = useApiRequest({
api: `/tag/web/api/v1/tag_distribution_batch/${id}/`,
method: "get",
queryKey: ["tagBatchInnerDashboard", id],
enabled: !!id,
});
useEffect(() => {
if (data?.distributions) {
const rows = data.distributions.map((item: any, index: number) => [
index + 1,
item?.dist_identity,
item?.batch_identity,
item?.distribution_type === "batch" ? "توزیع گروهی" : "توزیع تصادفی",
item?.species_code,
item?.total_tag_count,
item?.distributed_number,
item?.remaining_number,
`از ${item?.serial_from} تا ${item?.serial_to}`,
]);
setTableData(rows);
}
}, [data]);
return (
<Grid container column className="gap-4">
<Table
title="جزئیات توزیع پلاک"
noSearch
noPagination
columns={[
"ردیف",
"شناسه توزیع",
"شناسه پلاک",
"نوع توزیع",
"کد گونه",
"تعداد کل پلاک ها",
"تعداد توزیع شده",
"تعداد باقیمانده",
"بازه سریال",
]}
rows={tableData}
/>
</Grid>
);
}

View File

@@ -16,7 +16,7 @@ import { Grid } from "../components/Grid/Grid";
import { useDarkMode } from "../hooks/useDarkMode"; import { useDarkMode } from "../hooks/useDarkMode";
import clsx from "clsx"; import clsx from "clsx";
import { useModalStore } from "../context/zustand-store/appStore"; import { useModalStore } from "../context/zustand-store/appStore";
import { Logout } from "../partials/auth/Logout"; import { Logout } from "../partials/Auth/Logout";
import { useUserProfileStore } from "../context/zustand-store/userStore"; import { useUserProfileStore } from "../context/zustand-store/userStore";
import { formatJustDate } from "../utils/formatTime"; import { formatJustDate } from "../utils/formatTime";
import bg from "../assets/images/profile-bg.png"; import bg from "../assets/images/profile-bg.png";
@@ -46,14 +46,14 @@ const ProfileCard = ({
return ( return (
<Grid <Grid
className={clsx( className={clsx(
"group relative p-4 transition-all duration-500 hover:-translate-y-1 hover:scale-[1.01]" "group relative p-4 transition-all duration-500 hover:-translate-y-1 hover:scale-[1.01]",
)} )}
> >
<Grid className="relative flex items-start gap-4"> <Grid className="relative flex items-start gap-4">
<Grid <Grid
className={clsx( className={clsx(
"rounded-xl p-3 shadow-lg group-hover:shadow-xl transition-all duration-500 group-hover:scale-110", "rounded-xl p-3 shadow-lg group-hover:shadow-xl transition-all duration-500 group-hover:scale-110",
"bg-primary-600" "bg-primary-600",
)} )}
> >
<Icon className="h-5 w-5 text-white" /> <Icon className="h-5 w-5 text-white" />
@@ -128,12 +128,12 @@ export default function UserProfile() {
<button <button
onClick={() => setIsDark(!isDark)} onClick={() => setIsDark(!isDark)}
className={clsx( className={clsx(
"group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer" "group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer",
)} )}
> >
<Grid <Grid
className={clsx( className={clsx(
"rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent" "rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent",
)} )}
> >
{isDark ? ( {isDark ? (
@@ -153,12 +153,12 @@ export default function UserProfile() {
navigate({ to: path }); navigate({ to: path });
}} }}
className={clsx( className={clsx(
"group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer" "group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer",
)} )}
> >
<Grid <Grid
className={clsx( className={clsx(
"rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent" "rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent",
)} )}
> >
<BookOpenIcon className="h-5 w-5 text-green-500" /> <BookOpenIcon className="h-5 w-5 text-green-500" />
@@ -176,12 +176,12 @@ export default function UserProfile() {
}); });
}} }}
className={clsx( className={clsx(
"group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer" "group relative flex items-center gap-3 rounded-2xl transition-all duration-500 cursor-pointer",
)} )}
> >
<Grid <Grid
className={clsx( className={clsx(
"rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent" "rounded-xl transition-all duration-500 group-hover:scale-110 bg-transparent",
)} )}
> >
<ArrowLeftStartOnRectangleIcon className="h-5 w-5 text-red-700" /> <ArrowLeftStartOnRectangleIcon className="h-5 w-5 text-red-700" />

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="currentColor" width="800px" height="800px" viewBox="0 0 1920 1920" xmlns="http://www.w3.org/2000/svg">
<path d="M276.941 440.584v565.722c0 422.4 374.174 625.468 674.71 788.668l8.02 4.292 8.131-4.292c300.537-163.2 674.71-366.268 674.71-788.668V440.584l-682.84-321.657L276.94 440.584Zm682.73 1479.529c-9.262 0-18.523-2.372-26.993-6.89l-34.9-18.974C588.095 1726.08 164 1495.906 164 1006.306V404.78c0-21.91 12.65-41.788 32.414-51.162L935.727 5.42c15.134-7.228 32.866-7.228 48 0l739.313 348.2c19.765 9.374 32.414 29.252 32.414 51.162v601.525c0 489.6-424.207 719.774-733.779 887.943l-34.899 18.975c-8.47 4.517-17.731 6.889-27.105 6.889Zm467.158-547.652h-313.412l-91.595-91.482v-83.803H905.041v-116.78h-83.69l-58.503-58.504c-1.92.113-3.84.113-5.76.113-176.075 0-319.285-143.21-319.285-319.285 0-176.075 143.21-319.398 319.285-319.398 176.075 0 319.285 143.323 319.285 319.398 0 1.92 0 3.84-.113 5.647l350.57 350.682v313.412Zm-266.654-112.941h153.713v-153.713L958.462 750.155l3.953-37.27c1.017-123.897-91.595-216.621-205.327-216.621S550.744 588.988 550.744 702.72c0 113.845 92.612 206.344 206.344 206.344l47.21-5.309 63.811 63.7h149.873v116.78h116.781v149.986l25.412 25.299Zm-313.4-553.57c0 46.758-37.949 84.706-84.706 84.706-46.758 0-84.706-37.948-84.706-84.706s37.948-84.706 84.706-84.706c46.757 0 84.706 37.948 84.706 84.706" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -196,11 +196,11 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
if (window.visualViewport) { if (window.visualViewport) {
window.visualViewport.addEventListener( window.visualViewport.addEventListener(
"resize", "resize",
handleVisualViewportResize handleVisualViewportResize,
); );
window.visualViewport.addEventListener( window.visualViewport.addEventListener(
"scroll", "scroll",
handleVisualViewportScroll handleVisualViewportScroll,
); );
} }
const inputElement = inputRef.current; const inputElement = inputRef.current;
@@ -222,11 +222,11 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
if (window.visualViewport) { if (window.visualViewport) {
window.visualViewport.removeEventListener( window.visualViewport.removeEventListener(
"resize", "resize",
handleVisualViewportResize handleVisualViewportResize,
); );
window.visualViewport.removeEventListener( window.visualViewport.removeEventListener(
"scroll", "scroll",
handleVisualViewportScroll handleVisualViewportScroll,
); );
} }
const inputElement = inputRef.current; const inputElement = inputRef.current;
@@ -247,7 +247,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
target.closest(".select-group") && !clickedInsideCurrent; target.closest(".select-group") && !clickedInsideCurrent;
const clickedOnPortalDropdown = target.closest( const clickedOnPortalDropdown = target.closest(
`[data-autocomplete-portal="${uniqueId}"]` `[data-autocomplete-portal="${uniqueId}"]`,
); );
if (clickedOnAnotherAutocomplete) { if (clickedOnAnotherAutocomplete) {
@@ -318,7 +318,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
const preventTouchMove = (e: TouchEvent) => { const preventTouchMove = (e: TouchEvent) => {
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
const dropdown = document.querySelector( const dropdown = document.querySelector(
`[data-autocomplete-portal="${uniqueId}"]` `[data-autocomplete-portal="${uniqueId}"]`,
); );
if (dropdown) { if (dropdown) {
@@ -326,7 +326,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
if (touch) { if (touch) {
const elementAtPoint = document.elementFromPoint( const elementAtPoint = document.elementFromPoint(
touch.clientX, touch.clientX,
touch.clientY touch.clientY,
); );
if ( if (
elementAtPoint && elementAtPoint &&
@@ -375,12 +375,12 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
setLocalInputValue(value); setLocalInputValue(value);
setIsTyping(true); setIsTyping(true);
const filtered = data.filter((item) => const filtered = data.filter((item) =>
item.value.toLowerCase().includes(value.toLowerCase()) item.value.toLowerCase().includes(value.toLowerCase()),
); );
setFilteredData(filtered); setFilteredData(filtered);
setShowOptions(true); setShowOptions(true);
}, },
[data] [data],
); );
const handleChange = useCallback( const handleChange = useCallback(
@@ -390,7 +390,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
if (onChangeValue && newSelectedKeys.length > 0) { if (onChangeValue && newSelectedKeys.length > 0) {
const selectedItem = data.find( const selectedItem = data.find(
(item) => item.key === newSelectedKeys[0] (item) => item.key === newSelectedKeys[0],
); );
if (selectedItem) { if (selectedItem) {
onChangeValue({ onChangeValue({
@@ -400,7 +400,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
} }
} }
}, },
[onChange, onChangeValue, data] [onChange, onChangeValue, data],
); );
const handleOptionClick = useCallback( const handleOptionClick = useCallback(
@@ -430,7 +430,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
setShowOptions(false); setShowOptions(false);
} }
}, },
[multiselect, handleChange] [multiselect, handleChange],
); );
const handleInputClick = useCallback(() => { const handleInputClick = useCallback(() => {
@@ -602,7 +602,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
> >
{dropdownOptions} {dropdownOptions}
</motion.ul>, </motion.ul>,
document.body document.body,
); );
}, [ }, [
showOptions, showOptions,
@@ -638,6 +638,7 @@ const AutoComplete: React.FC<AutoCompleteProps> = ({
placeholder={title || "انتخاب کنید..."} placeholder={title || "انتخاب کنید..."}
/> />
<ChevronDownIcon <ChevronDownIcon
onClick={handleInputClick}
className={`absolute left-3 text-dark-400 dark:text-dark-100 transition-transform duration-200 ${ className={`absolute left-3 text-dark-400 dark:text-dark-100 transition-transform duration-200 ${
showOptions ? "transform rotate-180" : "" showOptions ? "transform rotate-180" : ""
} ${getSizeStyles(size).icon}`} } ${getSizeStyles(size).icon}`}

View File

@@ -7,6 +7,7 @@ import React, {
} from "react"; } from "react";
import clsx from "clsx"; import clsx from "clsx";
import { import {
ArrowUpCircleIcon,
ChartBarIcon, ChartBarIcon,
DocumentChartBarIcon, DocumentChartBarIcon,
EyeIcon, EyeIcon,
@@ -56,7 +57,8 @@ type ButtonProps = {
| "view" | "view"
| "info" | "info"
| "chart" | "chart"
| "set"; | "set"
| "share";
page?: string; page?: string;
access?: string; access?: string;
height?: string | number; height?: string | number;
@@ -161,6 +163,10 @@ const Button: React.FC<ButtonProps> = ({
return ( return (
<WrenchIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" /> <WrenchIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" />
); );
case "share":
return (
<ArrowUpCircleIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" />
);
default: default:
return null; return null;
} }
@@ -181,7 +187,7 @@ const Button: React.FC<ButtonProps> = ({
return true; return true;
} else { } else {
const finded = profile?.permissions?.find( const finded = profile?.permissions?.find(
(item: any) => item.page_name === page (item: any) => item.page_name === page,
); );
if (finded && finded.page_access.includes(access)) { if (finded && finded.page_access.includes(access)) {
return true; return true;
@@ -237,7 +243,7 @@ const Button: React.FC<ButtonProps> = ({
sizeStyles.padding, sizeStyles.padding,
sizeStyles.text, sizeStyles.text,
className, className,
checkIsMobile() && !icon && !variant && children && mobileBorders checkIsMobile() && !icon && !variant && children && mobileBorders,
)} )}
style={{ height }} style={{ height }}
> >
@@ -256,7 +262,7 @@ const Button: React.FC<ButtonProps> = ({
.then((response) => { .then((response) => {
closeBackdrop(); closeBackdrop();
const url = window.URL.createObjectURL( const url = window.URL.createObjectURL(
new Blob([response.data]) new Blob([response.data]),
); );
const link = document.createElement("a"); const link = document.createElement("a");

View File

@@ -0,0 +1,44 @@
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { useRef } from "react";
type CheckTrackingCodeProps = {
tracking?: string | number | null;
className?: string;
};
export default function CheckTrackingCode({
tracking,
className = "",
}: CheckTrackingCodeProps) {
const formRef = useRef<HTMLFormElement>(null);
const safeTracking = tracking ? String(tracking) : "";
const handleSubmit = () => {
if (!safeTracking) return;
formRef.current?.submit();
};
if (!safeTracking) {
return <span>-</span>;
}
return (
<form
action="https://e.ivo.ir/Rahgiri/Gidprnt.aspx"
method="post"
target="_blank"
ref={formRef}
className={`inline-flex ${className}`}
>
<input name="gid" type="hidden" value={safeTracking} />
<button
type="button"
onClick={handleSubmit}
className="group inline-flex cursor-pointer items-center gap-1.5 rounded-xl border border-primary-500/30 bg-gradient-to-r from-primary-500/10 to-primary-500/20 px-3 py-1.5 text-xs font-medium text-primary-700 transition-all duration-200 hover:from-primary-500/20 hover:to-primary-500/30 hover:shadow-sm dark:border-primary-200/20 dark:from-primary-100/10 dark:to-primary-100/20 dark:text-primary-200"
>
<span className="tracking-wide">{safeTracking}</span>
<ArrowTopRightOnSquareIcon className="h-3.5 w-3.5 transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5" />
</button>
</form>
);
}

View File

@@ -0,0 +1,236 @@
import React, { useRef, useState, useEffect, ChangeEvent } from "react";
import {
ArrowDownTrayIcon,
ArrowUpTrayIcon,
CheckCircleIcon,
} from "@heroicons/react/24/outline";
import api from "../../utils/axios";
import { useBackdropStore } from "../../context/zustand-store/appStore";
import { useToast } from "../../hooks/useToast";
import { useUserProfileStore } from "../../context/zustand-store/userStore";
import { RolesContextMenu } from "../Button/RolesContextMenu";
interface DocumentOperationProps {
downloadLink: string;
uploadLink: string;
validFiles?: string[];
payloadKey: string;
onUploadSuccess?: () => void;
page?: string;
access?: string;
limitSize?: number;
}
const buildAcceptString = (extensions: string[]): string => {
const mimeTypes: string[] = [];
extensions.forEach((ext) => {
const lower = ext.toLowerCase().replace(".", "");
if (lower === "img" || lower === "image") {
mimeTypes.push("image/*");
} else {
mimeTypes.push(`.${lower}`);
}
});
return mimeTypes.join(",");
};
export const DocumentOperation = ({
downloadLink,
uploadLink,
validFiles = [],
payloadKey,
onUploadSuccess,
page = "",
access = "",
limitSize,
}: DocumentOperationProps) => {
const { openBackdrop, closeBackdrop } = useBackdropStore();
const showToast = useToast();
const fileInputRef = useRef<HTMLInputElement>(null);
const [uploadedFileName, setUploadedFileName] = useState<string>("");
const { profile } = useUserProfileStore();
const [contextMenu, setContextMenu] = useState<{
x: number;
y: number;
} | null>(null);
const isAdmin = profile?.role?.type?.key === "ADM";
const ableToSee = () => {
if (!access || !page) {
return true;
}
const found = profile?.permissions?.find(
(item: any) => item.page_name === page,
);
if (found && found.page_access.includes(access)) {
return true;
}
return false;
};
const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>) => {
if (isAdmin && page && access) {
e.preventDefault();
e.stopPropagation();
setContextMenu({
x: e.clientX,
y: e.clientY,
});
}
};
useEffect(() => {
const handleClick = () => {
if (contextMenu) {
setContextMenu(null);
}
};
if (contextMenu) {
document.addEventListener("click", handleClick);
}
return () => {
document.removeEventListener("click", handleClick);
};
}, [contextMenu]);
const handleDownload = async () => {
if (!downloadLink) return;
openBackdrop();
try {
const response = await api.get(downloadLink, {
responseType: "blob",
});
const contentDisposition = response.headers["content-disposition"];
let fileName = "document";
if (contentDisposition) {
const match = contentDisposition.match(
/filename\*?=(?:UTF-8''|"?)([^";]+)/i,
);
if (match?.[1]) {
fileName = decodeURIComponent(match[1].replace(/"/g, ""));
}
} else {
const urlParts = downloadLink.split("/").filter(Boolean);
const lastPart = urlParts[urlParts.length - 1];
if (lastPart && lastPart.includes(".")) {
fileName = lastPart;
}
}
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
showToast("فایل با موفقیت دانلود شد", "success");
} catch {
showToast("خطا در دانلود فایل", "error");
} finally {
closeBackdrop();
}
};
const handleUploadClick = () => {
fileInputRef.current?.click();
};
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
if (limitSize && file.size > limitSize * 1024 * 1024) {
showToast(`حداکثر حجم فایل ${limitSize} مگابایت است`, "error");
return;
}
openBackdrop();
try {
const formData = new FormData();
formData.append(payloadKey, file);
await api.post(uploadLink, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
setUploadedFileName(file.name);
showToast("فایل با موفقیت آپلود شد", "success");
onUploadSuccess?.();
} catch {
showToast("خطا در آپلود فایل", "error");
} finally {
closeBackdrop();
}
};
const acceptString =
validFiles.length > 0 ? buildAcceptString(validFiles) : undefined;
return (
<>
<div
className="inline-flex items-center"
onContextMenu={handleContextMenu}
>
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
className="hidden"
accept={acceptString}
/>
<button
type="button"
onClick={handleDownload}
disabled={!downloadLink || !ableToSee()}
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-r-lg border border-l-0 border-primary-300 dark:border-dark-400 bg-primary-50 dark:bg-dark-600 text-primary-700 dark:text-primary-200 hover:bg-primary-100 dark:hover:bg-dark-500 transition-colors duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
<ArrowDownTrayIcon className="w-4 h-4" />
<span>دانلود</span>
</button>
<button
type="button"
onClick={handleUploadClick}
disabled={!uploadLink || !ableToSee()}
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-l-lg border border-primary-300 dark:border-dark-400 bg-primary-600 dark:bg-primary-700 text-white hover:bg-primary-500 dark:hover:bg-primary-800 transition-colors duration-200 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
{uploadedFileName ? (
<CheckCircleIcon className="w-4 h-4 text-green-300" />
) : (
<ArrowUpTrayIcon className="w-4 h-4" />
)}
<span>آپلود</span>
</button>
</div>
{contextMenu && page && access && (
<RolesContextMenu
page={page}
access={access}
position={contextMenu}
onClose={() => setContextMenu(null)}
/>
)}
</>
);
};

View File

@@ -7,6 +7,7 @@ import { getNestedValue } from "../../utils/getNestedValue";
type FormEnterLocationsProps = { type FormEnterLocationsProps = {
title: string; title: string;
api: string; api: string;
size?: "small" | "medium" | "large";
error?: boolean; error?: boolean;
errorMessage?: any; errorMessage?: any;
multiple?: boolean; multiple?: boolean;
@@ -15,6 +16,7 @@ type FormEnterLocationsProps = {
keyField?: string; keyField?: string;
secondaryKey?: string | string[]; secondaryKey?: string | string[];
tertiaryKey?: string | string[]; tertiaryKey?: string | string[];
quaternaryKey?: string | string[];
valueField?: string | string[]; valueField?: string | string[];
valueField2?: string | string[]; valueField2?: string | string[];
valueField3?: string | string[]; valueField3?: string | string[];
@@ -25,12 +27,14 @@ type FormEnterLocationsProps = {
valueTemplateProps?: Array<{ [key: string]: "string" | "number" }>; valueTemplateProps?: Array<{ [key: string]: "string" | "number" }>;
groupBy?: string | string[]; groupBy?: string | string[];
groupFunction?: (item: any) => string; groupFunction?: (item: any) => string;
valueFieldFunction?: (item: any) => any;
selectField?: boolean; selectField?: boolean;
}; };
export const FormApiBasedAutoComplete = ({ export const FormApiBasedAutoComplete = ({
title, title,
api, api,
size,
error, error,
errorMessage, errorMessage,
onChange, onChange,
@@ -38,6 +42,7 @@ export const FormApiBasedAutoComplete = ({
keyField = "id", keyField = "id",
secondaryKey, secondaryKey,
tertiaryKey, tertiaryKey,
quaternaryKey,
valueField = "name", valueField = "name",
valueField2, valueField2,
valueField3, valueField3,
@@ -49,6 +54,7 @@ export const FormApiBasedAutoComplete = ({
valueTemplateProps, valueTemplateProps,
groupBy, groupBy,
groupFunction, groupFunction,
valueFieldFunction,
selectField = false, selectField = false,
}: FormEnterLocationsProps) => { }: FormEnterLocationsProps) => {
const [data, setData] = useState<any>([]); const [data, setData] = useState<any>([]);
@@ -79,6 +85,18 @@ export const FormApiBasedAutoComplete = ({
return value; return value;
}; };
const getPrimaryValue = (option: any) => {
if (valueFieldFunction) {
return valueFieldFunction(option);
}
return valueField === "page"
? getFaPermissions(option[valueField])
: typeof valueField === "string"
? option[valueField]
: getNestedValue(option, valueField);
};
const { data: apiData } = useApiRequest({ const { data: apiData } = useApiRequest({
api: api, api: api,
method: "get", method: "get",
@@ -94,7 +112,7 @@ export const FormApiBasedAutoComplete = ({
if (filterAddress && filterValue) { if (filterAddress && filterValue) {
data = apiData.results?.filter( data = apiData.results?.filter(
(item: any) => (item: any) =>
!filterValue.includes(getNestedValue(item, filterAddress)) !filterValue.includes(getNestedValue(item, filterAddress)),
); );
} else { } else {
data = apiData.results; data = apiData.results;
@@ -111,19 +129,14 @@ export const FormApiBasedAutoComplete = ({
? option[tertiaryKey] ? option[tertiaryKey]
: getNestedValue(option, tertiaryKey) : getNestedValue(option, tertiaryKey)
: undefined, : undefined,
quaternaryKey: quaternaryKey
? typeof quaternaryKey === "string"
? option[quaternaryKey]
: getNestedValue(option, quaternaryKey)
: undefined,
value: valueTemplate value: valueTemplate
? valueTemplate ? valueTemplate
.replace( .replace(/v1/g, formatValue(getPrimaryValue(option), "v1"))
/v1/g,
formatValue(
valueField === "page"
? getFaPermissions(option[valueField])
: typeof valueField === "string"
? option[valueField]
: getNestedValue(option, valueField),
"v1"
)
)
.replace( .replace(
/v2/g, /v2/g,
formatValue( formatValue(
@@ -132,8 +145,8 @@ export const FormApiBasedAutoComplete = ({
? option[valueField2] ? option[valueField2]
: getNestedValue(option, valueField2) : getNestedValue(option, valueField2)
: "", : "",
"v2" "v2",
) ),
) )
.replace( .replace(
/v3/g, /v3/g,
@@ -143,16 +156,10 @@ export const FormApiBasedAutoComplete = ({
? option[valueField3] ? option[valueField3]
: getNestedValue(option, valueField3) : getNestedValue(option, valueField3)
: "", : "",
"v3" "v3",
),
) )
) : `${getPrimaryValue(option)} ${
: `${
valueField === "page"
? getFaPermissions(option[valueField])
: typeof valueField === "string"
? option[valueField]
: getNestedValue(option, valueField)
} ${
valueField2 valueField2
? " - " + ? " - " +
(typeof valueField2 === "string" (typeof valueField2 === "string"
@@ -208,7 +215,7 @@ export const FormApiBasedAutoComplete = ({
setData(finalData); setData(finalData);
const actualDataItems = finalData.filter( const actualDataItems = finalData.filter(
(item: any) => !item.isGroupHeader && !item.disabled (item: any) => !item.isGroupHeader && !item.disabled,
); );
if (defaultKey !== undefined && defaultKey !== null) { if (defaultKey !== undefined && defaultKey !== null) {
@@ -218,10 +225,10 @@ export const FormApiBasedAutoComplete = ({
setSelectedKeys([]); setSelectedKeys([]);
} else { } else {
const defaultIds = defaultKey.map((item: any) => const defaultIds = defaultKey.map((item: any) =>
typeof item === "object" ? item[keyField] : item typeof item === "object" ? item[keyField] : item,
); );
const defaultItems = actualDataItems.filter((item: any) => const defaultItems = actualDataItems.filter((item: any) =>
defaultIds.includes(item.key) defaultIds.includes(item.key),
); );
setSelectedKeys(defaultItems.map((item: any) => item.key)); setSelectedKeys(defaultItems.map((item: any) => item.key));
if (onChange) { if (onChange) {
@@ -237,12 +244,17 @@ export const FormApiBasedAutoComplete = ({
key3: item?.tertiaryKey, key3: item?.tertiaryKey,
} }
: {}), : {}),
...(quaternaryKey
? {
key4: item?.quaternaryKey,
}
: {}),
}; };
}) }),
); );
if (onChangeValue) { if (onChangeValue) {
onChangeValue( onChangeValue(
defaultItems.map((item: any) => item.value.trim()) defaultItems.map((item: any) => item.value.trim()),
); );
} }
} else { } else {
@@ -258,7 +270,7 @@ export const FormApiBasedAutoComplete = ({
? defaultKey[keyField] ? defaultKey[keyField]
: defaultKey; : defaultKey;
const defaultItem = actualDataItems.find( const defaultItem = actualDataItems.find(
(item: any) => item.key === keyToFind (item: any) => item.key === keyToFind,
); );
if (defaultItem) { if (defaultItem) {
setSelectedKeys([keyToFind]); setSelectedKeys([keyToFind]);
@@ -268,6 +280,9 @@ export const FormApiBasedAutoComplete = ({
key1: defaultItem.key, key1: defaultItem.key,
key2: defaultItem.secondaryKey, key2: defaultItem.secondaryKey,
...(tertiaryKey ? { key3: defaultItem.tertiaryKey } : {}), ...(tertiaryKey ? { key3: defaultItem.tertiaryKey } : {}),
...(quaternaryKey
? { key4: defaultItem.quaternaryKey }
: {}),
}); });
} else { } else {
onChange(keyToFind); onChange(keyToFind);
@@ -278,6 +293,7 @@ export const FormApiBasedAutoComplete = ({
key1: defaultItem.key, key1: defaultItem.key,
key2: defaultItem.secondaryKey, key2: defaultItem.secondaryKey,
...(tertiaryKey ? { key3: defaultItem.tertiaryKey } : {}), ...(tertiaryKey ? { key3: defaultItem.tertiaryKey } : {}),
...(quaternaryKey ? { key4: defaultItem.quaternaryKey } : {}),
value: defaultItem.value.trim(), value: defaultItem.value.trim(),
}); });
} }
@@ -296,18 +312,18 @@ export const FormApiBasedAutoComplete = ({
const groupItemKeys = groupItems.map((item: any) => item.key); const groupItemKeys = groupItems.map((item: any) => item.key);
const allGroupItemsSelected = groupItemKeys.every((key) => const allGroupItemsSelected = groupItemKeys.every((key) =>
selectedKeys.includes(key) selectedKeys.includes(key),
); );
let newSelectedKeys: (string | number)[]; let newSelectedKeys: (string | number)[];
if (allGroupItemsSelected) { if (allGroupItemsSelected) {
newSelectedKeys = selectedKeys.filter( newSelectedKeys = selectedKeys.filter(
(key) => !groupItemKeys.includes(key) (key) => !groupItemKeys.includes(key),
); );
} else { } else {
const newKeys = groupItemKeys.filter( const newKeys = groupItemKeys.filter(
(key) => !selectedKeys.includes(key) (key) => !selectedKeys.includes(key),
); );
newSelectedKeys = [...selectedKeys, ...newKeys]; newSelectedKeys = [...selectedKeys, ...newKeys];
} }
@@ -320,7 +336,7 @@ export const FormApiBasedAutoComplete = ({
(item: any) => (item: any) =>
newSelectedKeys.includes(item.key) && newSelectedKeys.includes(item.key) &&
!item.isGroupHeader && !item.isGroupHeader &&
!item.disabled !item.disabled,
); );
if (secondaryKey) { if (secondaryKey) {
@@ -329,7 +345,8 @@ export const FormApiBasedAutoComplete = ({
key1: item.key, key1: item.key,
key2: item.secondaryKey, key2: item.secondaryKey,
...(tertiaryKey ? { key3: item.tertiaryKey } : {}), ...(tertiaryKey ? { key3: item.tertiaryKey } : {}),
})) ...(quaternaryKey ? { key4: item.quaternaryKey } : {}),
})),
); );
if (onChangeValue) { if (onChangeValue) {
onChangeValue(selectedItems.map((item: any) => item.value.trim())); onChangeValue(selectedItems.map((item: any) => item.value.trim()));
@@ -344,6 +361,7 @@ export const FormApiBasedAutoComplete = ({
<AutoComplete <AutoComplete
multiselect={multiple} multiselect={multiple}
selectField={selectField} selectField={selectField}
size={size}
data={data} data={data}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
onChange={(newSelectedKeys) => { onChange={(newSelectedKeys) => {
@@ -357,7 +375,7 @@ export const FormApiBasedAutoComplete = ({
(item: any) => (item: any) =>
newSelectedKeys.includes(item.key) && newSelectedKeys.includes(item.key) &&
!item.isGroupHeader && !item.isGroupHeader &&
!item.disabled !item.disabled,
); );
onChange( onChange(
@@ -365,16 +383,17 @@ export const FormApiBasedAutoComplete = ({
key1: item.key, key1: item.key,
key2: item.secondaryKey, key2: item.secondaryKey,
...(tertiaryKey ? { key3: item.tertiaryKey } : {}), ...(tertiaryKey ? { key3: item.tertiaryKey } : {}),
})) ...(quaternaryKey ? { key4: item.quaternaryKey } : {}),
})),
); );
if (onChangeValue) { if (onChangeValue) {
onChangeValue( onChangeValue(
selectedItems.map((item: any) => item.value.trim()) selectedItems.map((item: any) => item.value.trim()),
); );
} }
} else { } else {
const validKeys = newSelectedKeys.filter( const validKeys = newSelectedKeys.filter(
(key) => !String(key).startsWith("__group__") (key) => !String(key).startsWith("__group__"),
); );
onChange(validKeys); onChange(validKeys);
} }
@@ -384,7 +403,7 @@ export const FormApiBasedAutoComplete = ({
(item: any) => (item: any) =>
item.key === newSelectedKeys[0] && item.key === newSelectedKeys[0] &&
!item.isGroupHeader && !item.isGroupHeader &&
!item.disabled !item.disabled,
); );
if (onChangeValue) { if (onChangeValue) {
onChangeValue({ onChangeValue({
@@ -394,6 +413,9 @@ export const FormApiBasedAutoComplete = ({
...(tertiaryKey ...(tertiaryKey
? { key3: selectedItem?.tertiaryKey ?? "" } ? { key3: selectedItem?.tertiaryKey ?? "" }
: {}), : {}),
...(quaternaryKey
? { key4: selectedItem?.quaternaryKey ?? "" }
: {}),
}); });
} }
@@ -402,6 +424,9 @@ export const FormApiBasedAutoComplete = ({
key1: selectedItem.key, key1: selectedItem.key,
key2: selectedItem.secondaryKey, key2: selectedItem.secondaryKey,
...(tertiaryKey ? { key3: selectedItem.tertiaryKey } : {}), ...(tertiaryKey ? { key3: selectedItem.tertiaryKey } : {}),
...(quaternaryKey
? { key4: selectedItem.quaternaryKey }
: {}),
}); });
} }
} else { } else {

View File

@@ -8,9 +8,16 @@ interface UseUserProfileStore {
clearProfile: () => void; clearProfile: () => void;
} }
type UserPermission = {
page_name: string;
domain_name?: string;
domain_fa_name?: string;
page_access: string[];
};
const arePermissionsEqual = ( const arePermissionsEqual = (
permissions1?: Array<{ page_name: string; page_access: string[] }>, permissions1?: UserPermission[],
permissions2?: Array<{ page_name: string; page_access: string[] }> permissions2?: UserPermission[]
): boolean => { ): boolean => {
if (!permissions1 && !permissions2) return true; if (!permissions1 && !permissions2) return true;
if (!permissions1 || !permissions2) return false; if (!permissions1 || !permissions2) return false;
@@ -20,11 +27,13 @@ const arePermissionsEqual = (
const map2 = new Map<string, Set<string>>(); const map2 = new Map<string, Set<string>>();
permissions1.forEach((perm) => { permissions1.forEach((perm) => {
map1.set(perm.page_name, new Set(perm.page_access.sort())); const key = `${perm.domain_name || ""}::${perm.domain_fa_name || ""}::${perm.page_name}`;
map1.set(key, new Set([...(perm.page_access || [])].sort()));
}); });
permissions2.forEach((perm) => { permissions2.forEach((perm) => {
map2.set(perm.page_name, new Set(perm.page_access.sort())); const key = `${perm.domain_name || ""}::${perm.domain_fa_name || ""}::${perm.page_name}`;
map2.set(key, new Set([...(perm.page_access || [])].sort()));
}); });
if (map1.size !== map2.size) return false; if (map1.size !== map2.size) return false;

View File

@@ -37,6 +37,7 @@ export default function Access() {
pagesInfo.page === 1 pagesInfo.page === 1
? i + 1 ? i + 1
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
item?.domain?.fa_name || item?.page_data?.domain?.fa_name || "-",
item?.name, item?.name,
item?.description, item?.description,
`${getFaPermissions(item?.page)} (${item?.page})`, `${getFaPermissions(item?.page)} (${item?.page})`,
@@ -119,6 +120,7 @@ export default function Access() {
title="مدیریت دسترسی ها" title="مدیریت دسترسی ها"
columns={[ columns={[
"ردیف", "ردیف",
"حوزه",
"دسترسی", "دسترسی",
"توضیحات", "توضیحات",
"صفحه", "صفحه",

View File

@@ -3,15 +3,14 @@ import Button from "../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { zValidateString } from "../../data/getFormTypeErrors"; import { zValidateNumber, zValidateString } from "../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import AutoComplete from "../../components/AutoComplete/AutoComplete";
import { useEffect, useState } from "react";
import { getFaPermissions } from "../../utils/getFaPermissions";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../components/CheckBox/CheckBox";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete";
import { getFaPermissions } from "../../utils/getFaPermissions";
type FormValues = z.infer<typeof schema>; type FormValues = z.infer<typeof schema>;
type AddAccessProps = { type AddAccessProps = {
@@ -22,9 +21,8 @@ type AddAccessProps = {
const schema = z.object({ const schema = z.object({
access: zValidateString("نام صفحه"), access: zValidateString("نام صفحه"),
description: zValidateString("توضیحات"), description: zValidateString("توضیحات"),
selectedPageId: z selectedPageId: zValidateNumber("صفحه"),
.array(z.union([z.string(), z.number()])) domain: z.union([z.string(), z.number()]).optional(),
.min(1, { message: "لطفاً یک صفحه انتخاب کنید." }),
modify_state: z.boolean(), modify_state: z.boolean(),
}); });
@@ -32,80 +30,28 @@ export const AddAccess = ({ getData, item }: AddAccessProps) => {
const showToast = useToast(); const showToast = useToast();
const { closeModal } = useModalStore(); const { closeModal } = useModalStore();
const [selectedKeys, setSelectedKeys] = useState<(string | number)[]>([]);
const [data, setData] = useState<any>([]);
const [pagesData, setPagesData] = useState<any>(null);
const { const {
control, control,
handleSubmit, handleSubmit,
setValue, setValue,
trigger,
formState: { errors }, formState: { errors },
} = useForm<FormValues>({ } = useForm<FormValues>({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
access: item?.name || "", access: item?.name || "",
description: item?.description || "", description: item?.description || "",
selectedPageId: [], selectedPageId: item?.page_data?.id || "",
domain: item?.domain?.id || item?.page_data?.domain?.id || "",
modify_state: item?.modify_state || false, modify_state: item?.modify_state || false,
}, },
}); });
useEffect(() => {
if (selectedKeys.length > 0) {
setValue("selectedPageId", selectedKeys);
}
}, [selectedKeys, setValue]);
useEffect(() => {
if (pagesData?.results && item?.page) {
const matchingPage = pagesData.results.find(
(page: any) => page.name === item.page
);
if (matchingPage) {
const keys = [matchingPage.id];
setSelectedKeys(keys);
setValue("selectedPageId", keys);
}
}
}, [pagesData, item, setValue]);
const handleChangeComplete = (newSelectedKeys: (number | string)[]) => {
setSelectedKeys(newSelectedKeys);
};
const mutationPages = useApiMutation({
api: "/auth/api/v1/page/",
method: "get",
});
const mutation = useApiMutation({ const mutation = useApiMutation({
api: `/auth/api/v1/permission/${item ? item?.id + "/" : ""}`, api: `/auth/api/v1/permission/${item ? item?.id + "/" : ""}`,
method: item ? "put" : "post", method: item ? "put" : "post",
}); });
const getPages = async () => {
const data = await mutationPages.mutateAsync({
page: 1,
page_size: 1000,
});
setPagesData(data);
};
useEffect(() => {
getPages();
}, []);
useEffect(() => {
if (pagesData?.results) {
const d = pagesData.results.map((page: any) => ({
key: page.id,
value: `${getFaPermissions(page.name)} (${page.name})`,
}));
setData(d);
}
}, [pagesData]);
const onSubmit = async (data: FormValues) => { const onSubmit = async (data: FormValues) => {
try { try {
await mutation.mutateAsync({ await mutation.mutateAsync({
@@ -113,7 +59,8 @@ export const AddAccess = ({ getData, item }: AddAccessProps) => {
description: data.description, description: data.description,
category: "api", category: "api",
meta: {}, meta: {},
page: selectedKeys[0], page: data.selectedPageId,
domain: data.domain,
modify_state: data?.modify_state, modify_state: data?.modify_state,
}); });
showToast("عملیات با موفقیت انجام شد", "success"); showToast("عملیات با موفقیت انجام شد", "success");
@@ -132,17 +79,21 @@ export const AddAccess = ({ getData, item }: AddAccessProps) => {
<Controller <Controller
name="selectedPageId" name="selectedPageId"
control={control} control={control}
render={({ field }) => ( render={() => (
<AutoComplete <FormApiBasedAutoComplete
data={data} defaultKey={item?.page_id}
selectedKeys={selectedKeys}
onChange={(newSelectedKeys) => {
handleChangeComplete(newSelectedKeys);
field.onChange(newSelectedKeys);
}}
title="انتخاب صفحه" title="انتخاب صفحه"
api="auth/api/v1/page/"
keyField="id"
valueFieldFunction={(item) => getFaPermissions(item?.name)}
secondaryKey={["domain", "id"]}
onChange={(selectedPage) => {
setValue("selectedPageId", selectedPage?.key1 || "");
setValue("domain", selectedPage?.key2 || "");
trigger("selectedPageId");
}}
error={!!errors.selectedPageId} error={!!errors.selectedPageId}
helperText={errors.selectedPageId?.message} errorMessage={errors.selectedPageId?.message}
/> />
)} )}
/> />

View File

@@ -0,0 +1,125 @@
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 {
zValidateString,
zValidateEnglishString,
} from "../../data/getFormTypeErrors";
import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse";
const schema = z.object({
code: zValidateString("کد حوزه"),
name: zValidateEnglishString("نام انگلیسی حوزه"),
fa_name: zValidateString("نام حوزه"),
});
type AddDomainProps = {
getData: () => void;
item?: any;
};
type FormValues = z.infer<typeof schema>;
export const AddDomain = ({ getData, item }: AddDomainProps) => {
const showToast = useToast();
const { closeModal } = useModalStore();
const {
control,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
code: item?.code || "",
name: item?.name || "",
fa_name: item?.fa_name || "",
},
});
const mutation = useApiMutation({
api: `/core/domain/${item ? item?.id + "/" : ""}`,
method: item ? "put" : "post",
});
const onSubmit = async (data: FormValues) => {
try {
await mutation.mutateAsync({
code: data.code,
name: data.name,
fa_name: data.fa_name,
});
showToast(getToastResponse(item, "حوزه"), "success");
closeModal();
getData();
} 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="code"
control={control}
render={({ field }) => (
<Textfield
fullWidth
placeholder="کد حوزه"
value={field.value}
onChange={field.onChange}
error={!!errors.code}
helperText={errors.code?.message}
/>
)}
/>
<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="fa_name"
control={control}
render={({ field }) => (
<Textfield
fullWidth
placeholder="نام حوزه"
value={field.value}
onChange={field.onChange}
error={!!errors.fa_name}
helperText={errors.fa_name?.message}
/>
)}
/>
<Button type="submit">ثبت</Button>
</Grid>
</form>
);
};

View File

@@ -3,14 +3,19 @@ import Button from "../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { zValidateEnglishString } from "../../data/getFormTypeErrors"; import {
zValidateEnglishString,
zValidateNumber,
} from "../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../context/zustand-store/appStore";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete";
const schema = z.object({ const schema = z.object({
page: zValidateEnglishString("نام صفحه"), page: zValidateEnglishString("نام صفحه"),
domain: zValidateNumber("حوزه"),
}); });
type AddPageProps = { type AddPageProps = {
@@ -27,11 +32,13 @@ export const AddPage = ({ getData, item }: AddPageProps) => {
const { const {
control, control,
handleSubmit, handleSubmit,
setValue,
formState: { errors }, formState: { errors },
} = useForm<FormValues>({ } = useForm<FormValues>({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
page: item?.name || "", page: item?.name || "",
domain: item?.domain?.id,
}, },
}); });
@@ -45,6 +52,7 @@ export const AddPage = ({ getData, item }: AddPageProps) => {
const payload = { const payload = {
name: data.page, name: data.page,
code: data.page + ".view", code: data.page + ".view",
domain: data.domain,
}; };
if (item) { if (item) {
@@ -69,6 +77,25 @@ export const AddPage = ({ getData, item }: AddPageProps) => {
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Grid container column className="gap-2"> <Grid container column className="gap-2">
<Controller
name="domain"
control={control}
render={() => (
<FormApiBasedAutoComplete
selectField
defaultKey={item?.domain?.id}
title="انتخاب حوزه"
api="/core/domain/"
keyField="id"
valueField="fa_name"
error={!!errors.domain}
errorMessage={errors.domain?.message}
onChange={(value) => {
setValue("domain", value);
}}
/>
)}
/>
<Controller <Controller
name="page" name="page"
control={control} control={control}
@@ -83,6 +110,7 @@ export const AddPage = ({ getData, item }: AddPageProps) => {
/> />
)} )}
/> />
<Button type="submit">ثبت</Button> <Button type="submit">ثبت</Button>
</Grid> </Grid>
</form> </form>

View File

@@ -52,11 +52,11 @@ export const EditAccess = ({ getData, item }: AddAccessProps) => {
useEffect(() => { useEffect(() => {
if (accessData?.results && item?.permissions) { if (accessData?.results && item?.permissions) {
const permissionPageAccesses = item.permissions.flatMap( const permissionPageAccesses = item.permissions.flatMap(
(option: any) => option.page_access || [] (option: any) => option.page_access || [],
); );
const matchingPages = accessData.results.filter((page: any) => const matchingPages = accessData.results.filter((page: any) =>
permissionPageAccesses.includes(page.name) permissionPageAccesses.includes(page.name),
); );
const matchingIds = matchingPages.map((page: any) => page.id); const matchingIds = matchingPages.map((page: any) => page.id);

View File

@@ -32,6 +32,7 @@ export default function Pages() {
? i + 1 ? i + 1
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
getFaPermissions(item?.name), getFaPermissions(item?.name),
item?.domain?.fa_name || "-",
item?.name, item?.name,
item?.permissions?.map((option: any) => option.name)?.join(" - "), item?.permissions?.map((option: any) => option.name)?.join(" - "),
<Popover key={i}> <Popover key={i}>
@@ -110,7 +111,7 @@ export default function Pages() {
count={pagesData?.count || 10} count={pagesData?.count || 10}
isPaginated isPaginated
title="صفحات سامانه" title="صفحات سامانه"
columns={["ردیف", "صفحه", "کلید", "دسترسی ها", "عملیات"]} columns={["ردیف", "صفحه", "حوزه", "کلید", "دسترسی ها", "عملیات"]}
rows={pagesTableData} rows={pagesTableData}
/> />
</Grid> </Grid>

View File

@@ -29,6 +29,7 @@ export default function UnusedAccess() {
pagesInfo.page === 1 pagesInfo.page === 1
? i + 1 ? i + 1
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
item?.domain?.fa_name || item?.page_data?.domain?.fa_name || "-",
item?.name, item?.name,
item?.description, item?.description,
`${getFaPermissions(item?.page)} (${item?.page})`, `${getFaPermissions(item?.page)} (${item?.page})`,
@@ -63,7 +64,7 @@ export default function UnusedAccess() {
count={pagesData?.count || 10} count={pagesData?.count || 10}
isPaginated isPaginated
title="دسترسی های غیر فعال" title="دسترسی های غیر فعال"
columns={["ردیف", "دسترسی", "توضیحات", "صفحه", "عملیات"]} columns={["ردیف", "حوزه", "دسترسی", "توضیحات", "صفحه", "عملیات"]}
rows={pagesTableData} rows={pagesTableData}
/> />
</Grid> </Grid>

View File

@@ -1,17 +1,17 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation, useApiRequest } from "../../utils/useApiRequest"; import { useApiMutation, useApiRequest } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
const schema = z.object({ const schema = z.object({
@@ -107,7 +107,7 @@ export const AddActivityType = ({ getData, item }: AddActivityTypeProps) => {
} catch (error: any) { } catch (error: any) {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
}; };

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
interface ChildOrganizationsProps { interface ChildOrganizationsProps {
orgId: number; orgId: number;
@@ -40,7 +40,7 @@ export const ChildOrganizations: React.FC<ChildOrganizationsProps> = ({
item?.national_unique_id || "-", item?.national_unique_id || "-",
item?.address || "-", item?.address || "-",
]; ];
} },
); );
setChildOrgsTableData(formattedData); setChildOrgsTableData(formattedData);
} }

View File

@@ -1,8 +1,8 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
interface QuotaDashboardByProduct { interface QuotaDashboardByProduct {
quotas_count: string; quotas_count: string;

View File

@@ -3,19 +3,19 @@ import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateAutoCompleteOptional, zValidateAutoCompleteOptional,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
type Props = { type Props = {
getData: () => void; getData: () => void;
@@ -82,7 +82,7 @@ export const AddAttribute = ({ getData, item }: Props) => {
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,7 +1,7 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
@@ -9,17 +9,17 @@ import {
zValidateNumber, zValidateNumber,
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import { checkAccess } from "../../utils/checkAccess"; import { checkAccess } from "../../../utils/checkAccess";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
type AddPageProps = { type AddPageProps = {
getData: () => void; getData: () => void;
@@ -47,7 +47,7 @@ export const AddBroker = ({ getData, item }: AddPageProps) => {
const { closeModal } = useModalStore(); const { closeModal } = useModalStore();
const [isGlobal, setIsGlobal] = useState( const [isGlobal, setIsGlobal] = useState(
item?.broker_type === "exclusive" ? false : true item?.broker_type === "exclusive" ? false : true,
); );
const [isRequired, setIsRequired] = useState(item ? item?.required : true); const [isRequired, setIsRequired] = useState(item ? item?.required : true);
@@ -114,12 +114,12 @@ export const AddBroker = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,21 +1,21 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateNumber, zValidateNumber,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { ImageUploader } from "../../components/ImageUploader/ImageUploader"; import { ImageUploader } from "../../../components/ImageUploader/ImageUploader";
import { useState } from "react"; import { useState } from "react";
const schema = z.object({ const schema = z.object({
@@ -74,12 +74,12 @@ export const AddProduct = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,14 +1,14 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { zValidateString } from "../../data/getFormTypeErrors"; import { zValidateString } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
const schema = z.object({ const schema = z.object({
name: zValidateString("نام "), name: zValidateString("نام "),
@@ -53,12 +53,12 @@ export const AddProductCategory = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,13 +1,13 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { zValidateString } from "../../data/getFormTypeErrors"; import { zValidateString } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
const schema = z.object({ const schema = z.object({
unit: zValidateString("نام واحد فروش"), unit: zValidateString("نام واحد فروش"),

View File

@@ -1,16 +1,16 @@
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import SVGImage from "../../components/SvgImage/SvgImage"; import SVGImage from "../../../components/SvgImage/SvgImage";
import editIcon from "../../assets/images/svg/edit.svg?react"; import editIcon from "../../../assets/images/svg/edit.svg?react";
import trashIcon from "../../assets/images/svg/trash.svg?react"; import trashIcon from "../../../assets/images/svg/trash.svg?react";
import { AddAttribute } from "./AddAttribute"; import { AddAttribute } from "./AddAttribute";
import { NoData } from "../../components/NoData/NoData"; import { NoData } from "../../../components/NoData/NoData";
import { PageTitle } from "../../components/PageTitle/PageTitle"; import { PageTitle } from "../../../components/PageTitle/PageTitle";
import { checkAccess } from "../../utils/checkAccess"; import { checkAccess } from "../../../utils/checkAccess";
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion"; import { BooleanQuestion } from "../../../components/BooleanQuestion/BooleanQuestion";
export const Attributes = () => { export const Attributes = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,16 +1,16 @@
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import SVGImage from "../../components/SvgImage/SvgImage"; import SVGImage from "../../../components/SvgImage/SvgImage";
import editIcon from "../../assets/images/svg/edit.svg?react"; import editIcon from "../../../assets/images/svg/edit.svg?react";
import { AddBroker } from "./AddBroker"; import { AddBroker } from "./AddBroker";
import { NoData } from "../../components/NoData/NoData"; import { NoData } from "../../../components/NoData/NoData";
import { PageTitle } from "../../components/PageTitle/PageTitle"; import { PageTitle } from "../../../components/PageTitle/PageTitle";
import { checkAccess } from "../../utils/checkAccess"; import { checkAccess } from "../../../utils/checkAccess";
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion"; import { BooleanQuestion } from "../../../components/BooleanQuestion/BooleanQuestion";
import trashIcon from "../../assets/images/svg/trash.svg?react"; import trashIcon from "../../../assets/images/svg/trash.svg?react";
export const Brokers = () => { export const Brokers = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,9 +1,9 @@
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
type Props = { type Props = {
item: any; item: any;

View File

@@ -1,16 +1,16 @@
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import SVGImage from "../../components/SvgImage/SvgImage"; import SVGImage from "../../../components/SvgImage/SvgImage";
import editIcon from "../../assets/images/svg/edit.svg?react"; import editIcon from "../../../assets/images/svg/edit.svg?react";
import { AddSaleUnit } from "./AddSaleUnit"; import { AddSaleUnit } from "./AddSaleUnit";
import { NoData } from "../../components/NoData/NoData"; import { NoData } from "../../../components/NoData/NoData";
import { PageTitle } from "../../components/PageTitle/PageTitle"; import { PageTitle } from "../../../components/PageTitle/PageTitle";
import { checkAccess } from "../../utils/checkAccess"; import { checkAccess } from "../../../utils/checkAccess";
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion"; import { BooleanQuestion } from "../../../components/BooleanQuestion/BooleanQuestion";
import trashIcon from "../../assets/images/svg/trash.svg?react"; import trashIcon from "../../../assets/images/svg/trash.svg?react";
export const SaleUnits = () => { export const SaleUnits = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,17 +1,17 @@
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { formatJustDate, formatJustTime } from "../../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../../utils/formatTime";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { QuotaDistributionEntryInventory } from "../quota/QuotaDistributionEntryInventory"; import { QuotaDistributionEntryInventory } from "../quota/QuotaDistributionEntryInventory";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { DocumentDownloader } from "../../components/DocumentDownloader/DocumentDownloader"; import { DocumentDownloader } from "../../../components/DocumentDownloader/DocumentDownloader";
const formatGroupNames = (groups?: any[]) => const formatGroupNames = (groups?: any[]) =>
groups groups
@@ -20,7 +20,7 @@ const formatGroupNames = (groups?: any[]) =>
? "روستایی" ? "روستایی"
: group === "industrial" : group === "industrial"
? "صنعتی" ? "صنعتی"
: "عشایری" : "عشایری",
) )
.join(", "); .join(", ");
@@ -73,7 +73,7 @@ export const InventoryEntriesList = () => {
? i + 1 ? i + 1
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
`${formatJustDate(item?.create_date)} (${formatJustTime( `${formatJustDate(item?.create_date)} (${formatJustTime(
item?.create_date item?.create_date,
)})`, )})`,
<ShowWeight <ShowWeight
key={i} key={i}

View File

@@ -1,16 +1,16 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { formatJustDate, formatJustTime } from "../../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../../utils/formatTime";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { BarsArrowUpIcon } from "@heroicons/react/24/outline"; import { BarsArrowUpIcon } from "@heroicons/react/24/outline";
import { QuotaAllocateToStakeHolders } from "../quota/QuotaAllocateToStakeHolders"; import { QuotaAllocateToStakeHolders } from "../quota/QuotaAllocateToStakeHolders";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
export const InventoryStakeHolderAllocations = () => { export const InventoryStakeHolderAllocations = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
@@ -34,7 +34,7 @@ export const InventoryStakeHolderAllocations = () => {
item?.quota_distribution?.distribution_id, item?.quota_distribution?.distribution_id,
item?.quota_distribution?.quota?.quota_id, item?.quota_distribution?.quota?.quota_id,
`${formatJustDate(item?.create_date)} (${formatJustTime( `${formatJustDate(item?.create_date)} (${formatJustTime(
item?.quota_distribution?.create_date item?.quota_distribution?.create_date,
)})`, )})`,
item?.quota_distribution?.assigner_organization?.organization, item?.quota_distribution?.assigner_organization?.organization,
item?.quota_distribution?.assigned_organization?.organization, item?.quota_distribution?.assigned_organization?.organization,

View File

@@ -1,15 +1,15 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { ListBulletIcon } from "@heroicons/react/24/outline"; import { ListBulletIcon } from "@heroicons/react/24/outline";
import { INVENTORY } from "../../routes/paths"; import { INVENTORY } from "../../../routes/paths";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters"; import { PaginationParameters } from "../../../components/PaginationParameters/PaginationParameters";
const formatDeviceSaleType = (value?: string) => const formatDeviceSaleType = (value?: string) =>
value === "all" value === "all"
@@ -63,7 +63,7 @@ export const InventoryWarehouseEntryTab = () => {
? "روستایی" ? "روستایی"
: group === "industrial" : group === "industrial"
? "صنعتی" ? "صنعتی"
: "عشایری" : "عشایری",
) )
.join(", "); .join(", ");
@@ -103,7 +103,7 @@ export const InventoryWarehouseEntryTab = () => {
</Tooltip> </Tooltip>
</Popover>, </Popover>,
]; ];
} },
); );
setPagesTableData(formattedData); setPagesTableData(formattedData);
} }

View File

@@ -1,23 +1,23 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateNumber, zValidateNumber,
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useDrawerStore } from "../../context/zustand-store/appStore"; import { useDrawerStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import { FormEnterLocations } from "../../components/FormItems/FormEnterLocation"; import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
type AddPageProps = { type AddPageProps = {
getData: () => void; getData: () => void;
@@ -71,10 +71,10 @@ export const LiveStockAddHerd = ({ getData, item, rancher }: AddPageProps) => {
const [activityType, setActivityType] = useState(item?.activity || "V"); const [activityType, setActivityType] = useState(item?.activity || "V");
const [activityState, setActivityState] = useState( const [activityState, setActivityState] = useState(
item ? item?.activity_state : true item ? item?.activity_state : true,
); );
const [operatingLicenseState, setOperatingLicenseState] = useState( const [operatingLicenseState, setOperatingLicenseState] = useState(
item ? item?.operating_license_state : true item ? item?.operating_license_state : true,
); );
const { const {
@@ -137,12 +137,12 @@ export const LiveStockAddHerd = ({ getData, item, rancher }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,21 +1,21 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateNumber, zValidateNumber,
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useDrawerStore } from "../../context/zustand-store/appStore"; import { useDrawerStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import DatePicker from "../../components/date-picker/DatePicker"; import DatePicker from "../../../components/date-picker/DatePicker";
type AddPageProps = { type AddPageProps = {
getData: () => void; getData: () => void;
@@ -51,7 +51,7 @@ export const LiveStockAddLiveStock = ({
const { closeDrawer } = useDrawerStore(); const { closeDrawer } = useDrawerStore();
const [gender, setGender] = useState(item?.gender || 1); const [gender, setGender] = useState(item?.gender || 1);
const [weightType, setWeightType] = useState( const [weightType, setWeightType] = useState(
item?.weight_type === "H" ? "H" : "L" item?.weight_type === "H" ? "H" : "L",
); );
const { const {
@@ -92,12 +92,12 @@ export const LiveStockAddLiveStock = ({
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,7 +1,7 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
@@ -10,16 +10,16 @@ import {
zValidateNumber, zValidateNumber,
zValidateString, zValidateString,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useDrawerStore } from "../../context/zustand-store/appStore"; import { useDrawerStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormEnterLocations } from "../../components/FormItems/FormEnterLocation"; import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
type AddPageProps = { type AddPageProps = {
getData: () => void; getData: () => void;
@@ -67,7 +67,7 @@ export const LiveStockAddRancher = ({ getData, item }: AddPageProps) => {
{ {
message: "نام واحد حقوقی نمیتواند خالی باشد", message: "نام واحد حقوقی نمیتواند خالی باشد",
path: ["union_name"], path: ["union_name"],
} },
) )
.refine( .refine(
(data) => { (data) => {
@@ -79,7 +79,7 @@ export const LiveStockAddRancher = ({ getData, item }: AddPageProps) => {
{ {
message: "شناسه ملی واحد حقوقی نمیتواند خالی باشد", message: "شناسه ملی واحد حقوقی نمیتواند خالی باشد",
path: ["union_code"], path: ["union_code"],
} },
); );
type FormValues = z.infer<typeof schema>; type FormValues = z.infer<typeof schema>;
@@ -90,7 +90,7 @@ export const LiveStockAddRancher = ({ getData, item }: AddPageProps) => {
const [activityType, setActivityType] = useState(item?.activity || "V"); const [activityType, setActivityType] = useState(item?.activity || "V");
const [rancherHerdType, setRancherHerdType] = useState( const [rancherHerdType, setRancherHerdType] = useState(
item ? item?.without_herd : false item ? item?.without_herd : false,
); );
const { const {
@@ -149,12 +149,12 @@ export const LiveStockAddRancher = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,14 +1,14 @@
import { z } from "zod"; import { z } from "zod";
import { zValidateAutoComplete } from "../../data/getFormTypeErrors"; import { zValidateAutoComplete } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
type Props = { type Props = {
getData: () => void; getData: () => void;
@@ -57,7 +57,7 @@ export const LiveStockAllocateCooperative = ({ getData, item }: Props) => {
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -10,7 +10,7 @@ import {
TruckIcon, TruckIcon,
UserIcon, UserIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
export const LiveStockHerdDetails = ({ export const LiveStockHerdDetails = ({
farmid, farmid,
@@ -23,7 +23,7 @@ export const LiveStockHerdDetails = ({
Record<number, boolean> Record<number, boolean>
>({}); >({});
const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>( const [expandedItems, setExpandedItems] = useState<Record<string, boolean>>(
{} {},
); );
const { data: herdData } = useApiRequest({ const { data: herdData } = useApiRequest({
@@ -237,12 +237,12 @@ export const LiveStockHerdDetails = ({
{item?.by_type?.length > 0 && {item?.by_type?.length > 0 &&
item?.by_type item?.by_type
.filter( .filter(
(animal: any) => animal.weight > 0 (animal: any) => animal.weight > 0,
) )
.map( .map(
( (
animal: any, animal: any,
animalIndex: number animalIndex: number,
) => ( ) => (
<motion.div <motion.div
key={animal.name} key={animal.name}
@@ -261,23 +261,23 @@ export const LiveStockHerdDetails = ({
</span> </span>
<span <span
className={`text-xs px-2 py-1 rounded-full ${getAnimalTypeColor( className={`text-xs px-2 py-1 rounded-full ${getAnimalTypeColor(
animal.type animal.type,
)}`} )}`}
> >
{getAnimalTypeText( {getAnimalTypeText(
animal.type animal.type,
)} )}
</span> </span>
</div> </div>
<p className="text-lg font-bold text-gray-800 dark:text-gray-200 mt-2"> <p className="text-lg font-bold text-gray-800 dark:text-gray-200 mt-2">
{formatWeight( {formatWeight(
animal.weight animal.weight,
)} )}
</p> </p>
</div> </div>
</div> </div>
</motion.div> </motion.div>
) ),
)} )}
</div> </div>
</div> </div>

View File

@@ -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>
);
};

View File

@@ -1,35 +1,35 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateBigNumber, zValidateBigNumber,
zValidateNumber, zValidateNumber,
zValidateNumberOptional, zValidateNumberOptional,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import { useEffect } from "react"; import { useEffect } from "react";
import ansar from "../../assets/images/banks/ansar.png"; import ansar from "../../../assets/images/banks/ansar.png";
import ayandeh from "../../assets/images/banks/ayandeh.png"; import ayandeh from "../../../assets/images/banks/ayandeh.png";
import eghtesadNovin from "../../assets/images/banks/eghtesad-novin.png"; import eghtesadNovin from "../../../assets/images/banks/eghtesad-novin.png";
import keshavarzi from "../../assets/images/banks/keshavarzi.png"; import keshavarzi from "../../../assets/images/banks/keshavarzi.png";
import maskan from "../../assets/images/banks/maskan.png"; import maskan from "../../../assets/images/banks/maskan.png";
import mehriran from "../../assets/images/banks/mehriran.png"; import mehriran from "../../../assets/images/banks/mehriran.png";
import meli from "../../assets/images/banks/meli.png"; import meli from "../../../assets/images/banks/meli.png";
import mellat from "../../assets/images/banks/mellat.png"; import mellat from "../../../assets/images/banks/mellat.png";
import pasargad from "../../assets/images/banks/pasargad.png"; import pasargad from "../../../assets/images/banks/pasargad.png";
import saderat from "../../assets/images/banks/saderat.png"; import saderat from "../../../assets/images/banks/saderat.png";
import saman from "../../assets/images/banks/saman.png"; import saman from "../../../assets/images/banks/saman.png";
import sina from "../../assets/images/banks/sina.png"; import sina from "../../../assets/images/banks/sina.png";
import tejarat from "../../assets/images/banks/tejarat.png"; import tejarat from "../../../assets/images/banks/tejarat.png";
import toseeTavon from "../../assets/images/banks/tosee-tavon.png"; import toseeTavon from "../../../assets/images/banks/tosee-tavon.png";
const schema = z.object({ const schema = z.object({
name: zValidateStringOptional("بانک"), name: zValidateStringOptional("بانک"),
@@ -112,7 +112,7 @@ export const AddCard = ({ getData, item, target }: AddPageProps) => {
const foundBank = const foundBank =
cardToBank[ cardToBank[
Object.keys(cardToBank).find((prefix) => Object.keys(cardToBank).find((prefix) =>
cardNumber.toString().startsWith(prefix) cardNumber.toString().startsWith(prefix),
) || "" ) || ""
]; ];
if (foundBank) setValue("name", foundBank); if (foundBank) setValue("name", foundBank);
@@ -145,7 +145,7 @@ export const AddCard = ({ getData, item, target }: AddPageProps) => {
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,36 +1,42 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateNumber, zValidateNumber,
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
zValidateStringOptional, } from "../../../data/getFormTypeErrors";
} from "../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { FormEnterLocations } from "../../components/FormItems/FormEnterLocation"; import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
import { useState } from "react"; import { useState } from "react";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
import {
ArrowPathIcon,
CheckBadgeIcon,
PlusIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import axios from "axios";
const schema = z.object({ const schema = z.object({
name: zValidateString("نام سازمان"), name: zValidateString("نام سازمان"),
national_unique_id: zValidateString("شناسه کشوری"), national_unique_id: zValidateString("شناسه کشوری"),
address: zValidateStringOptional("آدرس"),
field_of_activity: zValidateAutoComplete("حوزه فعالیت"), field_of_activity: zValidateAutoComplete("حوزه فعالیت"),
province: zValidateNumber("استان"), province: zValidateNumber("استان"),
city: zValidateNumber("شهر"), city: zValidateNumber("شهر"),
organization: zValidateNumberOptional("سازمان"), organization: zValidateNumberOptional("سازمان"),
organizationType: zValidateNumber("سازمان"), organizationType: zValidateNumber("سازمان"),
unique_unit_identity: zValidateNumberOptional("شناسه یکتا واحد"),
is_repeatable: z.boolean(), is_repeatable: z.boolean(),
free_visibility_by_scope: z.boolean(), free_visibility_by_scope: z.boolean(),
}); });
@@ -75,8 +81,8 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: item?.name || "", name: item?.name || "",
address: item?.address || "",
national_unique_id: item?.national_unique_id || "", national_unique_id: item?.national_unique_id || "",
unique_unit_identity: item?.unique_unit_identity || "",
free_visibility_by_scope: item?.free_visibility_by_scope || false, free_visibility_by_scope: item?.free_visibility_by_scope || false,
field_of_activity: field_of_activity:
item && item?.field_of_activity !== "EM" item && item?.field_of_activity !== "EM"
@@ -95,9 +101,78 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
city: string | any; city: string | any;
}>({ province: "", city: "" }); }>({ province: "", city: "" });
const [addresses, setAddresses] = useState<
{ postal_code: string; address: string }[]
>(
item?.addresses?.length
? item.addresses.map((a: any) => ({
postal_code: a.postal_code || "",
address: a.address || "",
}))
: [{ postal_code: "", address: "" }],
);
const [isInquiryRequired, setIsInquiryRequired] = useState(false);
const [inquiryPassed, setInquiryPassed] = useState(false);
const [inquiryLoading, setInquiryLoading] = useState(false);
const handleAddAddress = () => {
setAddresses((prev) => [...prev, { postal_code: "", address: "" }]);
};
const handleRemoveAddress = (index: number) => {
setAddresses((prev) => prev.filter((_, i) => i !== index));
};
const handleAddressChange = (
index: number,
field: "postal_code" | "address",
value: string,
) => {
setAddresses((prev) =>
prev.map((item, i) => (i === index ? { ...item, [field]: value } : item)),
);
};
const handleInquiry = async () => {
const code = getValues("unique_unit_identity");
if (!code) {
showToast("لطفاً شناسه یکتا واحد را وارد کنید!", "error");
return;
}
setInquiryLoading(true);
try {
await axios.get(
`https://rsibackend.rasadyar.com/app/has_code_in_db/?code=${code}`,
);
setInquiryPassed(true);
showToast("استعلام با موفقیت انجام شد!", "success");
} catch (error: any) {
if (error?.response?.status === 404) {
setInquiryPassed(false);
showToast("شناسه موجود نیست!", "error");
} else {
showToast("خطا در استعلام!", "error");
}
} finally {
setInquiryLoading(false);
}
};
const onSubmit = async (data: FormValues) => { const onSubmit = async (data: FormValues) => {
if (isInquiryRequired && !data.unique_unit_identity) {
showToast("شناسه یکتا واحد الزامی است!", "error");
return;
}
if (isInquiryRequired && !inquiryPassed) {
showToast("لطفاً ابتدا استعلام شناسه یکتا واحد را انجام دهید!", "error");
return;
}
try { try {
await mutation.mutateAsync({ await mutation.mutateAsync({
addresses: addresses.filter(
(a) => a.postal_code.trim() || a.address.trim(),
),
organization: { organization: {
name: `${data?.name} ${ name: `${data?.name} ${
data?.is_repeatable data?.is_repeatable
@@ -111,6 +186,9 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
type: data.organizationType, type: data.organizationType,
}), }),
national_unique_id: data?.national_unique_id, national_unique_id: data?.national_unique_id,
...(data?.unique_unit_identity && {
unique_unit_identity: data.unique_unit_identity,
}),
province: data?.province, province: data?.province,
city: data?.city, city: data?.city,
...(data.organization && { ...(data.organization && {
@@ -118,7 +196,6 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
}), }),
field_of_activity: data.field_of_activity[0], field_of_activity: data.field_of_activity[0],
free_visibility_by_scope: data.free_visibility_by_scope, free_visibility_by_scope: data.free_visibility_by_scope,
address: data.address,
}, },
}); });
showToast(getToastResponse(item, "سازمان"), "success"); showToast(getToastResponse(item, "سازمان"), "success");
@@ -128,12 +205,12 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این سازمان تکراری است!", error?.response?.data?.message || "این سازمان تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }
@@ -153,6 +230,7 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
keyField="id" keyField="id"
secondaryKey="is_repeatable" secondaryKey="is_repeatable"
tertiaryKey="org_type_field" tertiaryKey="org_type_field"
quaternaryKey="key"
valueField="name" valueField="name"
error={!!errors.organizationType} error={!!errors.organizationType}
errorMessage={errors.organizationType?.message} errorMessage={errors.organizationType?.message}
@@ -163,6 +241,12 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
trigger(["organizationType"]); trigger(["organizationType"]);
}} }}
onChangeValue={(r) => { onChangeValue={(r) => {
if (r.key4 === "U" || r.key4 === "CO") {
setIsInquiryRequired(true);
} else {
setIsInquiryRequired(false);
setInquiryPassed(false);
}
if (!r.key2) { if (!r.key2) {
setValue("name", r.value); setValue("name", r.value);
} else { } else {
@@ -223,6 +307,62 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
)} )}
/> />
<Controller
name="unique_unit_identity"
control={control}
render={({ field }) => (
<div className="flex items-start gap-2 w-full">
<Textfield
fullWidth
placeholder={
isInquiryRequired
? "شناسه یکتا واحد"
: "شناسه یکتا واحد (اختیاری)"
}
value={field.value}
onChange={(e) => {
field.onChange(e);
setInquiryPassed(false);
}}
error={
!!errors.unique_unit_identity ||
(isInquiryRequired && !inquiryPassed && !!field.value)
}
helperText={
errors.unique_unit_identity?.message ||
(isInquiryRequired && inquiryPassed
? "استعلام تایید شده"
: undefined)
}
/>
{isInquiryRequired && (
<button
type="button"
onClick={handleInquiry}
disabled={inquiryLoading || !field.value}
className={`shrink-0 flex items-center gap-1 mt-[2px] px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
inquiryPassed
? "bg-green-500 text-white hover:bg-green-600"
: "bg-blue-500 text-white hover:bg-blue-600"
} disabled:opacity-50 disabled:cursor-not-allowed`}
>
{inquiryLoading
? "..."
: inquiryPassed
? "تایید شده"
: "استعلام"}
{inquiryPassed && !inquiryLoading ? (
<CheckBadgeIcon className="w-4 h-4 text-white" />
) : (
<ArrowPathIcon className="w-4 h-4 text-white" />
)}
</button>
)}
</div>
)}
/>
<Controller <Controller
name="province" name="province"
control={control} control={control}
@@ -258,7 +398,7 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
defaultKey={item?.parent_organization?.id} defaultKey={item?.parent_organization?.id}
title="سازمان والد (اختیاری)" title="سازمان والد (اختیاری)"
api={`auth/api/v1/organization/organizations_by_province?province=${getValues( api={`auth/api/v1/organization/organizations_by_province?province=${getValues(
"province" "province",
)}`} )}`}
keyField="id" keyField="id"
valueField="name" valueField="name"
@@ -273,20 +413,53 @@ export const AddOrganization = ({ getData, item }: AddPageProps) => {
)} )}
/> />
<Controller <div className="flex flex-col gap-2 w-full">
name="address" <div className="flex items-center justify-between">
control={control} <span className="text-sm font-medium text-gray-700">آدرسها</span>
render={({ field }) => ( <button
type="button"
onClick={handleAddAddress}
className="flex items-center gap-1 text-sm text-blue-500 dark:text-blue-300 hover:text-blue-800 transition-colors"
>
<PlusIcon className="w-4 h-4" />
افزودن آدرس
</button>
</div>
{addresses.map((addr, index) => (
<div
key={index}
className="flex items-start gap-2 p-3 border border-gray-200 rounded-lg"
>
<div className="flex flex-col gap-2 flex-1">
<Textfield <Textfield
fullWidth fullWidth
placeholder="آدرس (اختیاری)" placeholder="کد پستی"
value={field.value} value={addr.postal_code}
onChange={field.onChange} onChange={(e) =>
error={!!errors.address} handleAddressChange(index, "postal_code", e.target.value)
helperText={errors.address?.message} }
/> />
<Textfield
fullWidth
placeholder="آدرس"
value={addr.address}
onChange={(e) =>
handleAddressChange(index, "address", e.target.value)
}
/>
</div>
{addresses.length > 1 && (
<button
type="button"
onClick={() => handleRemoveAddress(index)}
className="mt-2 text-red-500 hover:text-red-700 transition-colors shrink-0"
>
<TrashIcon className="w-5 h-5" />
</button>
)} )}
/> </div>
))}
</div>
<Controller <Controller
name="free_visibility_by_scope" name="free_visibility_by_scope"

View File

@@ -1,21 +1,21 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
const schema = z.object({ const schema = z.object({
name: zValidateString("نام نهاد "), name: zValidateString("نام نهاد "),
@@ -81,12 +81,12 @@ export const AddOrganizationType = ({ getData, item }: AddPageProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,7 +1,7 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
@@ -9,15 +9,15 @@ import {
zValidateNumberOptional, zValidateNumberOptional,
zValidateString, zValidateString,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useFetchProfile } from "../../hooks/useFetchProfile"; import { useFetchProfile } from "../../../hooks/useFetchProfile";
import { getFaPermissions } from "../../utils/getFaPermissions"; import { getFaPermissions } from "../../../utils/getFaPermissions";
const schema = z.object({ const schema = z.object({
name: zValidateString("نام سازمان"), name: zValidateString("نام سازمان"),
@@ -79,7 +79,7 @@ export const AddRole = ({ getData, item }: AddPageProps) => {
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -6,23 +6,23 @@ import {
zValidateNumber, zValidateNumber,
zValidateString, zValidateString,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import DatePicker from "../../components/date-picker/DatePicker"; import DatePicker from "../../../components/date-picker/DatePicker";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { FormEnterLocations } from "../../components/FormItems/FormEnterLocation"; import { FormEnterLocations } from "../../../components/FormItems/FormEnterLocation";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useDrawerStore } from "../../context/zustand-store/appStore"; import { useDrawerStore } from "../../../context/zustand-store/appStore";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import { useFetchProfile } from "../../hooks/useFetchProfile"; import { useFetchProfile } from "../../../hooks/useFetchProfile";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
type AddAccessProps = { type AddAccessProps = {
getData: () => void; getData: () => void;
@@ -71,7 +71,7 @@ export const AddUser = ({ getData, item }: AddAccessProps) => {
{ {
message: "نام واحد حقوقی نمیتواند خالی باشد", message: "نام واحد حقوقی نمیتواند خالی باشد",
path: ["unit_name"], path: ["unit_name"],
} },
) )
.refine( .refine(
(data) => { (data) => {
@@ -83,7 +83,7 @@ export const AddUser = ({ getData, item }: AddAccessProps) => {
{ {
message: "شناسه ملی واحد حقوقی نمیتواند خالی باشد", message: "شناسه ملی واحد حقوقی نمیتواند خالی باشد",
path: ["unit_national_id"], path: ["unit_national_id"],
} },
); );
type FormValues = z.infer<typeof schema>; type FormValues = z.infer<typeof schema>;
@@ -398,7 +398,7 @@ export const AddUser = ({ getData, item }: AddAccessProps) => {
defaultKey={item?.organization?.id} defaultKey={item?.organization?.id}
title="سازمان" title="سازمان"
api={`auth/api/v1/organization/organizations_by_province?province=${getValues( api={`auth/api/v1/organization/organizations_by_province?province=${getValues(
"province" "province",
)}`} )}`}
keyField="id" keyField="id"
valueField="name" valueField="name"

View File

@@ -1,24 +1,28 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { AddOrganization } from "./AddOrganization"; import { AddOrganization } from "./AddOrganization";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import Table from "../../components/Table/Table"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useModalStore } from "../../context/zustand-store/appStore"; import Table from "../../../components/Table/Table";
import { useApiRequest } from "../../utils/useApiRequest"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { useApiRequest } from "../../../utils/useApiRequest";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
import { Popover } from "../../components/PopOver/PopOver"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { Popover } from "../../../components/PopOver/PopOver";
import { AddCard } from "./AddCard"; import { AddCard } from "./AddCard";
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../../components/ShowMoreInfo/ShowMoreInfo";
import { ShowCardsStringList } from "../../components/ShowCardsStringList/ShowCardsStringList"; import { ShowCardsStringList } from "../../../components/ShowCardsStringList/ShowCardsStringList";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
export const OrganizationsList = () => { export const OrganizationsList = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();
const [selectedProvinceKeys, setSelectedProvinceKeys] = useState< const [selectedProvinceKeys, setSelectedProvinceKeys] = useState<
(string | number)[] (string | number)[]
>([]); >([]);
const [selectedOrganizationType, setSelectedOrganizationType] = useState<
string | number
>("");
const [params, setParams] = useState({ page: 1, page_size: 10 }); const [params, setParams] = useState({ page: 1, page_size: 10 });
const [tableData, setTableData] = useState([]); const [tableData, setTableData] = useState([]);
const { profile } = useUserProfileStore(); const { profile } = useUserProfileStore();
@@ -32,11 +36,16 @@ export const OrganizationsList = () => {
const { data: apiData, refetch } = useApiRequest({ const { data: apiData, refetch } = useApiRequest({
api: selectedProvinceKeys?.length api: selectedProvinceKeys?.length
? `/auth/api/v1/organization/organizations_by_province?province=${selectedProvinceKeys[0]}` ? `/auth/api/v1/organization/organizations_by_province?province=${selectedProvinceKeys[0]}${selectedOrganizationType ? `&org_type=${selectedOrganizationType}` : ""}`
: "/auth/api/v1/organization/", : `/auth/api/v1/organization/${selectedOrganizationType ? `?org_type=${selectedOrganizationType}` : ""}`,
method: "get", method: "get",
params: params, params: params,
queryKey: ["organizations", params, selectedProvinceKeys], queryKey: [
"organizations",
params,
selectedProvinceKeys,
selectedOrganizationType,
],
}); });
useEffect(() => { useEffect(() => {
@@ -50,6 +59,7 @@ export const OrganizationsList = () => {
`${item?.type?.name}`, `${item?.type?.name}`,
item?.parent_organization?.name, item?.parent_organization?.name,
item?.national_unique_id, item?.national_unique_id,
item?.unique_unit_identity || "-",
item?.field_of_activity === "CO" item?.field_of_activity === "CO"
? "کشور" ? "کشور"
: item?.field_of_activity === "PR" : item?.field_of_activity === "PR"
@@ -59,7 +69,14 @@ export const OrganizationsList = () => {
: "نامشخص", : "نامشخص",
item?.province?.name, item?.province?.name,
item?.city?.name, item?.city?.name,
item?.address || "-", <ShowMoreInfo
key={`address-${i}`}
title="آدرس‌ها"
disabled={!item?.addresses?.length}
data={item?.addresses}
columns={["کد پستی", "آدرس"]}
accessKeys={[["postal_code"], ["address"]]}
/>,
<ShowMoreInfo <ShowMoreInfo
key={i} key={i}
title="اطلاعات حساب" title="اطلاعات حساب"
@@ -151,6 +168,16 @@ export const OrganizationsList = () => {
ایجاد سازمان ایجاد سازمان
</Button> </Button>
</Grid> </Grid>
<Grid>
<FormApiBasedAutoComplete
size="small"
title="فیلتر نهاد"
api={`auth/api/v1/organization-type`}
keyField="id"
valueField="name"
onChange={(r) => setSelectedOrganizationType(r)}
/>
</Grid>
{profile?.organization?.type?.org_type_field === "CO" && ( {profile?.organization?.type?.org_type_field === "CO" && (
<Grid> <Grid>
<AutoComplete <AutoComplete
@@ -177,6 +204,7 @@ export const OrganizationsList = () => {
"نهاد", "نهاد",
"سازمان والد", "سازمان والد",
"شناسه کشوری", "شناسه کشوری",
"شناسه یکتا واحد",
"حوزه فعالیت", "حوزه فعالیت",
"استان", "استان",
"شهر", "شهر",

View File

@@ -1,14 +1,14 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { AddOrganizationType } from "./AddOrganizationType"; import { AddOrganizationType } from "./AddOrganizationType";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
export const OrganizationsTypes = () => { export const OrganizationsTypes = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,21 +1,21 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { z } from "zod"; import { z } from "zod";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateAutoCompleteOptional, zValidateAutoCompleteOptional,
zValidateEnglishString, zValidateEnglishString,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
type AddPosProps = { type AddPosProps = {
getData: () => void; getData: () => void;
@@ -80,12 +80,12 @@ export const AddPos = ({ getData, item }: AddPosProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,14 +1,14 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zValidateAutoComplete } from "../../data/getFormTypeErrors"; import { zValidateAutoComplete } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { z } from "zod"; import { z } from "zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
type Props = { type Props = {
getData: () => void; getData: () => void;
@@ -59,7 +59,7 @@ export const AllocateAccountToBroker = ({ getData, item }: Props) => {
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,22 +1,22 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { z } from "zod"; import { z } from "zod";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateString, zValidateString,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useState } from "react"; import { useState } from "react";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Divider from "../../components/Divider/Divider"; import Divider from "../../../components/Divider/Divider";
type AddPosProps = { type AddPosProps = {
getData: () => void; getData: () => void;
@@ -96,12 +96,12 @@ export const AllocatePos = ({ getData, item }: AddPosProps) => {
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,14 +1,14 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { z } from "zod"; import { z } from "zod";
import { zValidateAutoComplete } from "../../data/getFormTypeErrors"; import { zValidateAutoComplete } from "../../../data/getFormTypeErrors";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
type AddPosProps = { type AddPosProps = {
getData: () => void; getData: () => void;
@@ -66,12 +66,12 @@ export const PosAllocateOrganizationAccount = ({
if (error?.status === 403) { if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -0,0 +1,235 @@
import { z } from "zod";
import {
zValidateAutoComplete,
zValidateString,
zValidateStringOptional,
} from "../../../data/getFormTypeErrors";
import { useState } from "react";
import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../../context/zustand-store/appStore";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useApiMutation } from "../../../utils/useApiRequest";
import { getToastResponse } from "../../../data/getToastResponse";
import Button from "../../../components/Button/Button";
import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../../components/Textfeild/Textfeild";
import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import DatePicker from "../../../components/date-picker/DatePicker";
type Props = {
getData: () => void;
item?: any;
};
const groupTypes = [
{ key: "rural", value: "روستایی", disabled: false },
{ key: "industrial", value: "صنعتی", disabled: false },
{ key: "nomadic", value: "عشایری", disabled: false },
];
const planTypes = [
{ key: "ILQ", value: "افزایش سهمیه دام", disabled: false },
{ key: "SM", value: "آماری / پایشی", disabled: true },
];
const limitTimeTypes = [
{ label: "دارد", value: true },
{
label: "ندارد",
value: false,
},
];
export const AddIncentivePlan = ({ getData, item }: Props) => {
const showToast = useToast();
const { closeModal } = useModalStore();
const [isTimeUnlimited, setIsTimeUnlimited] = useState(
item ? item?.is_time_unlimited : false,
);
const schema = z.object({
name: zValidateString("نام طرح"),
description: zValidateStringOptional("توضیحات"),
plan_type: zValidateAutoComplete("نوع طرح"),
group: zValidateAutoComplete("گروه"),
// is_time_unlimited: zValidateNumber("شهر"),
start_date_limit: isTimeUnlimited
? zValidateString("تاریخ شروع محدودیت")
: zValidateStringOptional("تاریخ شروع محدودیت"),
end_date_limit: isTimeUnlimited
? zValidateString("تاریخ اتمام محدودیت")
: zValidateStringOptional("تاریخ اتمام محدودیت"),
});
type FormValues = z.infer<typeof schema>;
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
name: item?.name || "",
description: item?.description || "",
group: item?.group ? [item?.group] : [],
plan_type: item?.plan_type ? [item?.plan_type] : [],
start_date_limit: item?.start_date_limit,
end_date_limit: item?.end_date_limit,
},
});
const mutation = useApiMutation({
api: `/product/web/api/v1/incentive_plan/${item ? item?.id + "/" : ""}`,
method: item ? "put" : "post",
});
const onSubmit = async (data: FormValues) => {
try {
await mutation.mutateAsync({
name: data.name,
description: data.description,
plan_type: data.plan_type[0],
group: data.group[0],
is_time_unlimited: isTimeUnlimited,
...(isTimeUnlimited
? {
start_date_limit: data?.start_date_limit,
end_date_limit: data?.end_date_limit,
}
: {}),
...(item
? { registering_organization: item?.registering_organization }
: {}),
});
showToast(getToastResponse(item, ""), "success");
getData();
closeModal();
} 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="description"
control={control}
render={({ field }) => (
<Textfield
fullWidth
placeholder="توضیحات (اختیاری)"
value={field.value}
onChange={field.onChange}
error={!!errors.description}
helperText={errors.description?.message}
/>
)}
/>
<Controller
name="plan_type"
control={control}
render={({ field }) => (
<AutoComplete
data={planTypes}
selectedKeys={field.value}
onChange={(keys: (string | number)[]) => {
setValue("plan_type", keys);
}}
error={!!errors.plan_type}
helperText={errors.plan_type?.message}
title="نوع طرح"
/>
)}
/>
<Controller
name="group"
control={control}
render={({ field }) => (
<AutoComplete
data={groupTypes}
selectedKeys={field.value}
onChange={(keys: (string | number)[]) => {
setValue("group", keys);
}}
error={!!errors.group}
helperText={errors.group?.message}
title="گروه"
/>
)}
/>
<RadioGroup
groupTitle="محدودیت زمانی"
className="mr-2 mt-2"
direction="row"
options={limitTimeTypes}
name="دریافت تعرفه"
value={isTimeUnlimited}
onChange={(e) =>
e.target.value === "true"
? setIsTimeUnlimited(true)
: setIsTimeUnlimited(false)
}
/>
{isTimeUnlimited && (
<>
<DatePicker
value={item?.start_date_limit || ""}
label="تاریخ شروع طرح"
size="medium"
onChange={(r) => {
setValue("start_date_limit", r);
}}
/>
<DatePicker
value={item?.end_date_limit || ""}
label="تاریخ اتمام طرح"
size="medium"
onChange={(r) => {
setValue("end_date_limit", r);
}}
/>
</>
)}
<Button type="submit">ثبت</Button>
</Grid>
</form>
);
};

View File

@@ -1,15 +1,15 @@
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { Stepper } from "../../components/Stepper/Stepper"; import { Stepper } from "../../../components/Stepper/Stepper";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { QuotaLevel1 } from "./QuotaLevel1"; import { QuotaLevel1 } from "./QuotaLevel1";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { QuotaLevel2 } from "./QuotaLevel2"; import { QuotaLevel2 } from "./QuotaLevel2";
import { QuotaLevel3 } from "./QuotaLevel3"; import { QuotaLevel3 } from "./QuotaLevel3";
import { QuotaLevel4 } from "./QuotaLevel4"; import { QuotaLevel4 } from "./QuotaLevel4";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast, useConfirmToast } from "../../hooks/useToast"; import { useToast, useConfirmToast } from "../../../hooks/useToast";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
type Props = { type Props = {
item?: any; item?: any;
@@ -70,7 +70,7 @@ export const AddQuota = ({ item, getData }: Props) => {
const handleSubmitForm = async () => { const handleSubmitForm = async () => {
if (item && item?.quota_distributed > 0) { if (item && item?.quota_distributed > 0) {
const confirmed = await showConfirmToast( const confirmed = await showConfirmToast(
"اطلاعات ویرایش شده بر روی تمام توزیع های زیر مجموعه اعمال میشود!" "اطلاعات ویرایش شده بر روی تمام توزیع های زیر مجموعه اعمال میشود!",
); );
if (!confirmed) { if (!confirmed) {
return; return;
@@ -96,15 +96,16 @@ export const AddQuota = ({ item, getData }: Props) => {
broker_data: formData?.broker_data, broker_data: formData?.broker_data,
pos_sale_type: formData?.pos_sale_type, pos_sale_type: formData?.pos_sale_type,
livestock_allocation_data: formData?.livestockTypes?.filter( livestock_allocation_data: formData?.livestockTypes?.filter(
(opt: { quantity_kg: number }) => opt?.quantity_kg > 0 (opt: { quantity_kg: number }) => opt?.quantity_kg > 0,
), ),
livestock_age_limitations: formData?.livestock_age_limitations, livestock_age_limitations: formData?.livestock_age_limitations,
pre_sale: formData?.pre_sale, pre_sale: formData?.pre_sale,
free_sale: formData?.free_sale, free_sale: formData?.free_sale,
one_time_purchase_limit: formData?.one_time_purchase_limit,
}; };
let filterEmptyKeys = Object.fromEntries( let filterEmptyKeys = Object.fromEntries(
Object.entries(submitData).filter(([, v]) => v != null) Object.entries(submitData).filter(([, v]) => v != null),
); );
try { try {

View File

@@ -1,14 +1,14 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { AddQuota } from "./AddQuota"; import { AddQuota } from "./AddQuota";
import { QuotaView } from "./QuotaView"; import { QuotaView } from "./QuotaView";
import { PopoverCustomModalOperation } from "../../components/PopOverCustomModalOperation/PopoverCustomModalOperation"; import { PopoverCustomModalOperation } from "../../../components/PopOverCustomModalOperation/PopoverCustomModalOperation";
import { import {
ArrowDownOnSquareIcon, ArrowDownOnSquareIcon,
ArrowUpOnSquareIcon, ArrowUpOnSquareIcon,
@@ -16,14 +16,14 @@ import {
XMarkIcon, XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { QUOTAS } from "../../routes/paths"; import { QUOTAS } from "../../../routes/paths";
import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils"; import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils";
import { QuotaAllocateToStakeHolders } from "./QuotaAllocateToStakeHolders"; import { QuotaAllocateToStakeHolders } from "./QuotaAllocateToStakeHolders";
import { QuotaDistributionEntryInventory } from "./QuotaDistributionEntryInventory"; import { QuotaDistributionEntryInventory } from "./QuotaDistributionEntryInventory";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
import { TableButton } from "../../components/TableButton/TableButton"; import { TableButton } from "../../../components/TableButton/TableButton";
import { QuotaActivesDashboardDetails } from "./QuotaActivesDashboardDetails"; import { QuotaActivesDashboardDetails } from "./QuotaActivesDashboardDetails";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters"; import { PaginationParameters } from "../../../components/PaginationParameters/PaginationParameters";
export const QuotaActives = () => { export const QuotaActives = () => {
const { openModal } = useModalStore(); const { openModal } = useModalStore();

View File

@@ -1,8 +1,8 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
interface QuotaDashboardByProduct { interface QuotaDashboardByProduct {
quotas_count: string; quotas_count: string;

View File

@@ -1,16 +1,16 @@
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { formatJustDate, formatJustTime } from "../../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../../utils/formatTime";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { QuotaDistribution } from "./QuotaDistribution"; import { QuotaDistribution } from "./QuotaDistribution";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters"; import { PaginationParameters } from "../../../components/PaginationParameters/PaginationParameters";
export const QuotaAllDistributions = () => { export const QuotaAllDistributions = () => {
const [params, setParams] = useState({ page: 1, page_size: 10 }); const [params, setParams] = useState({ page: 1, page_size: 10 });
@@ -55,7 +55,7 @@ export const QuotaAllDistributions = () => {
item?.quota?.quota_id, item?.quota?.quota_id,
item?.quota?.product?.product, item?.quota?.product?.product,
`${formatJustDate(item?.create_date)} (${formatJustTime( `${formatJustDate(item?.create_date)} (${formatJustTime(
item?.create_date item?.create_date,
)})`, )})`,
`${item?.assigner_organization?.organization} (${item?.creator_info})`, `${item?.assigner_organization?.organization} (${item?.creator_info})`,
item?.assigned_organization?.organization, item?.assigned_organization?.organization,

View File

@@ -1,19 +1,19 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateNumber, zValidateNumber,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
type Props = { type Props = {
getData: () => void; getData: () => void;
@@ -31,16 +31,16 @@ export const QuotaAllocateToStakeHolders = ({
const cooperativeValue = isSubmit const cooperativeValue = isSubmit
? item?.quota?.brokers?.find( ? item?.quota?.brokers?.find(
(broker: any) => broker?.broker_name === "تعاونی" (broker: any) => broker?.broker_name === "تعاونی",
)?.value )?.value
: item?.quota_distribution?.quota?.brokers?.find( : item?.quota_distribution?.quota?.brokers?.find(
(broker: any) => broker?.broker_name === "تعاونی" (broker: any) => broker?.broker_name === "تعاونی",
)?.value; )?.value;
const schema = z.object({ const schema = z.object({
share_amount: zValidateNumber("سهم از تعرفه").max( share_amount: zValidateNumber("سهم از تعرفه").max(
cooperativeValue, cooperativeValue,
`سهم از تعرفه نمی‌تواند بیشتر از ${cooperativeValue?.toLocaleString()} باشد!` `سهم از تعرفه نمی‌تواند بیشتر از ${cooperativeValue?.toLocaleString()} باشد!`,
), ),
organization: zValidateNumber("سازمان"), organization: zValidateNumber("سازمان"),
assigned_organization: zValidateNumber("سازمان تخصیص دهنده"), assigned_organization: zValidateNumber("سازمان تخصیص دهنده"),
@@ -88,7 +88,7 @@ export const QuotaAllocateToStakeHolders = ({
await mutation.mutateAsync(payload as any); await mutation.mutateAsync(payload as any);
showToast( showToast(
getToastResponse(isSubmit ? false : true, "تخصیص به زیر مجموعه"), getToastResponse(isSubmit ? false : true, "تخصیص به زیر مجموعه"),
"success" "success",
); );
getData(); getData();
closeModal(); closeModal();
@@ -96,18 +96,18 @@ export const QuotaAllocateToStakeHolders = ({
if (error?.status === 400) { if (error?.status === 400) {
showToast( showToast(
error?.response?.data?.detail || error?.response?.data?.message, error?.response?.data?.detail || error?.response?.data?.message,
"error" "error",
); );
closeModal(); closeModal();
} else if (error?.status === 403) { } else if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,12 +1,12 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { PopoverCustomModalOperation } from "../../components/PopOverCustomModalOperation/PopoverCustomModalOperation"; import { PopoverCustomModalOperation } from "../../../components/PopOverCustomModalOperation/PopoverCustomModalOperation";
import { ArrowUturnDownIcon } from "@heroicons/react/24/outline"; import { ArrowUturnDownIcon } from "@heroicons/react/24/outline";
import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils"; import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters"; import { PaginationParameters } from "../../../components/PaginationParameters/PaginationParameters";
export const QuotaClosed = () => { export const QuotaClosed = () => {
const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 }); const [pagesInfo, setPagesInfo] = useState({ page: 1, page_size: 10 });

View File

@@ -1,21 +1,21 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateNumber, zValidateNumber,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
type Props = { type Props = {
@@ -100,17 +100,17 @@ export const QuotaDistribution = ({
if (editPriceComponents) { if (editPriceComponents) {
const currentAttributeSum = Object.values(attributeValues).reduce( const currentAttributeSum = Object.values(attributeValues).reduce(
(sum, val) => sum + val, (sum, val) => sum + val,
0 0,
); );
const currentBrokerSum = Object.values(brokerValues).reduce( const currentBrokerSum = Object.values(brokerValues).reduce(
(sum, val) => sum + val, (sum, val) => sum + val,
0 0,
); );
if (currentAttributeSum !== initialAttributeSum) { if (currentAttributeSum !== initialAttributeSum) {
showToast( showToast(
`مجموع قیمت مولفه های قیمت گذاری باید برابر ${initialAttributeSum.toLocaleString()} باشد. مجموع فعلی: ${currentAttributeSum.toLocaleString()}`, `مجموع قیمت مولفه های قیمت گذاری باید برابر ${initialAttributeSum.toLocaleString()} باشد. مجموع فعلی: ${currentAttributeSum.toLocaleString()}`,
"error" "error",
); );
return; return;
} }
@@ -118,7 +118,7 @@ export const QuotaDistribution = ({
if (currentBrokerSum !== initialBrokerSum) { if (currentBrokerSum !== initialBrokerSum) {
showToast( showToast(
`مجموع قیمت کارگزاران باید برابر ${initialBrokerSum.toLocaleString()} باشد. مجموع فعلی: ${currentBrokerSum.toLocaleString()}`, `مجموع قیمت کارگزاران باید برابر ${initialBrokerSum.toLocaleString()} باشد. مجموع فعلی: ${currentBrokerSum.toLocaleString()}`,
"error" "error",
); );
return; return;
} }
@@ -136,7 +136,7 @@ export const QuotaDistribution = ({
(key) => ({ (key) => ({
attribute: parseInt(key), attribute: parseInt(key),
value: attributeValues[parseInt(key)], value: attributeValues[parseInt(key)],
}) }),
); );
payload.broker_data = Object.keys(brokerValues).map((key) => ({ payload.broker_data = Object.keys(brokerValues).map((key) => ({
broker: parseInt(key), broker: parseInt(key),
@@ -152,18 +152,18 @@ export const QuotaDistribution = ({
if (error?.status === 400) { if (error?.status === 400) {
showToast( showToast(
error?.response?.data?.detail || error?.response?.data?.message, error?.response?.data?.detail || error?.response?.data?.message,
"error" "error",
); );
closeModal(); closeModal();
} else if (error?.status === 403) { } else if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }
@@ -273,7 +273,7 @@ export const QuotaDistribution = ({
className={ className={
Object.values(attributeValues).reduce( Object.values(attributeValues).reduce(
(sum, val) => sum + val, (sum, val) => sum + val,
0 0,
) === initialAttributeSum ) === initialAttributeSum
? "text-green-500" ? "text-green-500"
: "text-red-500" : "text-red-500"
@@ -320,7 +320,7 @@ export const QuotaDistribution = ({
className={ className={
Object.values(brokerValues).reduce( Object.values(brokerValues).reduce(
(sum, val) => sum + val, (sum, val) => sum + val,
0 0,
) === initialBrokerSum ) === initialBrokerSum
? "text-green-500" ? "text-green-500"
: "text-red-500" : "text-red-500"

View File

@@ -1,20 +1,20 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { import {
zValidateBase64Optional, zValidateBase64Optional,
zValidateNumber, zValidateNumber,
zValidateStringOptional, zValidateStringOptional,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { z } from "zod"; import { z } from "zod";
import { useApiMutation } from "../../utils/useApiRequest"; import { useApiMutation } from "../../../utils/useApiRequest";
import { useToast } from "../../hooks/useToast"; import { useToast } from "../../../hooks/useToast";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { getToastResponse } from "../../data/getToastResponse"; import { getToastResponse } from "../../../data/getToastResponse";
import FileUploader from "../../components/FIleUploader/FileUploader"; import FileUploader from "../../../components/FIleUploader/FileUploader";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
type Props = { type Props = {
getData: () => void; getData: () => void;
@@ -81,18 +81,18 @@ export const QuotaDistributionEntryInventory = ({
if (error?.status === 400) { if (error?.status === 400) {
showToast( showToast(
error?.response?.data?.detail || error?.response?.data?.message, error?.response?.data?.detail || error?.response?.data?.message,
"error" "error",
); );
closeModal(); closeModal();
} else if (error?.status === 403) { } else if (error?.status === 403) {
showToast( showToast(
error?.response?.data?.message || "این مورد تکراری است!", error?.response?.data?.message || "این مورد تکراری است!",
"error" "error",
); );
} else { } else {
showToast( showToast(
error?.response?.data?.message || "خطا در ثبت اطلاعات!", error?.response?.data?.message || "خطا در ثبت اطلاعات!",
"error" "error",
); );
} }
} }

View File

@@ -1,5 +1,5 @@
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react"; import { useState } from "react";
import { import {
@@ -10,7 +10,7 @@ import {
DocumentTextIcon, DocumentTextIcon,
TruckIcon, TruckIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { formatJustDate } from "../../utils/formatTime"; import { formatJustDate } from "../../../utils/formatTime";
const formatWeight = (value: number | string | undefined, unit?: string) => { const formatWeight = (value: number | string | undefined, unit?: string) => {
if (value === null || value === undefined || value === "") return "-"; if (value === null || value === undefined || value === "") return "-";
@@ -136,7 +136,7 @@ const DistributionNode = ({
مانده:{" "} مانده:{" "}
{formatWeight( {formatWeight(
item?.warehouse_balance, item?.warehouse_balance,
item?.sale_unit?.unit item?.sale_unit?.unit,
)} )}
</span> </span>
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-amber-50 text-amber-700 dark:bg-amber-900/30 dark:text-amber-200"> <span className="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-amber-50 text-amber-700 dark:bg-amber-900/30 dark:text-amber-200">
@@ -155,7 +155,7 @@ const DistributionNode = ({
<button <button
onClick={() => onClick={() =>
setIsWarehouseEntriesExpanded( setIsWarehouseEntriesExpanded(
!isWarehouseEntriesExpanded !isWarehouseEntriesExpanded,
) )
} }
className="w-full flex items-center justify-between px-1.5 py-1 rounded text-xs text-gray-600 bg-gray-50 hover:bg-gray-100 transition-colors dark:bg-gray-700/40 dark:text-gray-100 dark:hover:bg-gray-700/60" className="w-full flex items-center justify-between px-1.5 py-1 rounded text-xs text-gray-600 bg-gray-50 hover:bg-gray-100 transition-colors dark:bg-gray-700/40 dark:text-gray-100 dark:hover:bg-gray-700/60"
@@ -222,7 +222,7 @@ const DistributionNode = ({
وزن:{" "} وزن:{" "}
{formatWeight( {formatWeight(
entry?.weight, entry?.weight,
item?.sale_unit?.unit item?.sale_unit?.unit,
)} )}
</span> </span>
{entry?.lading_number && ( {entry?.lading_number && (
@@ -249,7 +249,7 @@ const DistributionNode = ({
)} )}
</div> </div>
</motion.div> </motion.div>
) ),
)} )}
{!isLoadingWarehouseEntries && {!isLoadingWarehouseEntries &&

View File

@@ -1,22 +1,22 @@
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { QuotaDistribution } from "./QuotaDistribution"; import { QuotaDistribution } from "./QuotaDistribution";
import { useModalStore } from "../../context/zustand-store/appStore"; import { useModalStore } from "../../../context/zustand-store/appStore";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { formatJustDate, formatJustTime } from "../../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../../utils/formatTime";
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons"; import { DeleteButtonForPopOver } from "../../../components/PopOverButtons/PopOverButtons";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { import {
getQuotaDashboardColumns, getQuotaDashboardColumns,
getQuotaDashboardRowData, getQuotaDashboardRowData,
} from "./quotaTableUtils"; } from "./quotaTableUtils";
import { QuotaDistributionOverview } from "./QuotaDistributionOverview"; import { QuotaDistributionOverview } from "./QuotaDistributionOverview";
import { useUserProfileStore } from "../../context/zustand-store/userStore"; import { useUserProfileStore } from "../../../context/zustand-store/userStore";
export const QuotaDistributions = () => { export const QuotaDistributions = () => {
const params = useParams({ strict: false }); const params = useParams({ strict: false });
@@ -49,7 +49,7 @@ export const QuotaDistributions = () => {
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
item?.distribution_id, item?.distribution_id,
`${formatJustDate(item?.create_date)} (${formatJustTime( `${formatJustDate(item?.create_date)} (${formatJustTime(
item?.create_date item?.create_date,
)})`, )})`,
item?.assigner_organization?.organization + item?.assigner_organization?.organization +
" (" + " (" +

View File

@@ -1,22 +1,22 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import { import {
zValidateAutoComplete, zValidateAutoComplete,
zValidateAutoCompleteOptional, zValidateAutoCompleteOptional,
zValidateNumber, zValidateNumber,
zValidateString, zValidateString,
} from "../../data/getFormTypeErrors"; } from "../../../data/getFormTypeErrors";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
import { getMonthsList } from "../../data/getMonths"; import { getMonthsList } from "../../../data/getMonths";
import { RadioGroup } from "../../components/RadioButton/RadioGroup"; import { RadioGroup } from "../../../components/RadioButton/RadioGroup";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import ToggleButton from "../../components/ToggleButton/ToggleButton"; import ToggleButton from "../../../components/ToggleButton/ToggleButton";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
type Props = { type Props = {
item: any; item: any;
@@ -45,7 +45,7 @@ const posSaleTypes = [
export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => { export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const [hasDistributionLimit, setHasDistributionLimit] = useState( const [hasDistributionLimit, setHasDistributionLimit] = useState(
item?.distribution_mode?.length ? true : false item?.distribution_mode?.length ? true : false,
); );
const internalRef = useRef<HTMLFormElement>(null); const internalRef = useRef<HTMLFormElement>(null);
const [livestockTypes, setLivestockTypes] = useState< const [livestockTypes, setLivestockTypes] = useState<
@@ -116,7 +116,7 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
(option: any) => (option: any) =>
option?.livestock_type?.weight_type === allocate?.weight_type && option?.livestock_type?.weight_type === allocate?.weight_type &&
option?.livestock_group === group && option?.livestock_group === group &&
option?.livestock_type?.name === allocate?.name option?.livestock_type?.name === allocate?.name,
); );
return result?.quantity_kg || 0; return result?.quantity_kg || 0;
}; };
@@ -153,13 +153,13 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const findLivestockIndex = ( const findLivestockIndex = (
group: string, group: string,
weightType: string, weightType: string,
fa: string fa: string,
) => { ) => {
return livestockTypes.findIndex( return livestockTypes.findIndex(
(item) => (item) =>
item.livestock_group === group && item.livestock_group === group &&
item.weight_type === weightType && item.weight_type === weightType &&
item.fa === fa item.fa === fa,
); );
}; };
@@ -394,7 +394,7 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
?.filter( ?.filter(
(option) => (option) =>
option.livestock_group === "rural" && option.livestock_group === "rural" &&
option?.weight_type === "H" option?.weight_type === "H",
) )
.map((item, i) => { .map((item, i) => {
const index = findLivestockIndex("rural", "H", item.fa); const index = findLivestockIndex("rural", "H", item.fa);
@@ -428,7 +428,7 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
?.filter( ?.filter(
(option) => (option) =>
option.livestock_group === "rural" && option.livestock_group === "rural" &&
option?.weight_type === "L" option?.weight_type === "L",
) )
.map((item, i) => { .map((item, i) => {
const index = findLivestockIndex("rural", "L", item.fa); const index = findLivestockIndex("rural", "L", item.fa);
@@ -475,13 +475,13 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
?.filter( ?.filter(
(option) => (option) =>
option.livestock_group === "industrial" && option.livestock_group === "industrial" &&
option?.weight_type === "H" option?.weight_type === "H",
) )
.map((item, i) => { .map((item, i) => {
const index = findLivestockIndex( const index = findLivestockIndex(
"industrial", "industrial",
"H", "H",
item.fa item.fa,
); );
return ( return (
<Textfield <Textfield
@@ -513,13 +513,13 @@ export const QuotaLevel1 = ({ item, onSubmit, setFormRef, visible }: Props) => {
?.filter( ?.filter(
(option) => (option) =>
option.livestock_group === "industrial" && option.livestock_group === "industrial" &&
option?.weight_type === "L" option?.weight_type === "L",
) )
.map((item, i) => { .map((item, i) => {
const index = findLivestockIndex( const index = findLivestockIndex(
"industrial", "industrial",
"L", "L",
item.fa item.fa,
); );
return ( return (
<Textfield <Textfield

View File

@@ -1,9 +1,9 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
type Props = { type Props = {
item: any; item: any;
@@ -48,7 +48,7 @@ export const QuotaLevel2 = ({ item, onSubmit, setFormRef, visible }: Props) => {
if (livestockData) { if (livestockData) {
const d = data?.results?.map((option: PlansProps) => { const d = data?.results?.map((option: PlansProps) => {
const founded = item?.incentive_plan?.find( const founded = item?.incentive_plan?.find(
(itm: PlansProps) => itm?.incentive_plan === option.id (itm: PlansProps) => itm?.incentive_plan === option.id,
); );
return { return {
name: option?.name, name: option?.name,
@@ -56,7 +56,7 @@ export const QuotaLevel2 = ({ item, onSubmit, setFormRef, visible }: Props) => {
active: founded ? true : false, active: founded ? true : false,
live_stocks: livestockData.results.flatMap((item: any) => { live_stocks: livestockData.results.flatMap((item: any) => {
const foundedLiveStock = founded?.live_stocks?.find( const foundedLiveStock = founded?.live_stocks?.find(
(option: any) => option?.id === item.id (option: any) => option?.id === item.id,
); );
return [ return [

View File

@@ -1,10 +1,10 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete"; import { FormApiBasedAutoComplete } from "../../../components/FormItems/FormApiBasedAutoComplete";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
type Props = { type Props = {
item: any; item: any;
@@ -24,13 +24,17 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const internalRef = useRef<HTMLFormElement>(null); const internalRef = useRef<HTMLFormElement>(null);
const [limitByHerdSize, setLimitByHerdSize] = useState( const [limitByHerdSize, setLimitByHerdSize] = useState(
item?.limit_by_herd_size || false item?.limit_by_herd_size || false,
); );
const [preSale, setPreSale] = useState(item?.pre_sale || false); const [preSale, setPreSale] = useState(item?.pre_sale || false);
const [freeSale, setFreeSale] = useState(item?.free_sale || false); const [freeSale, setFreeSale] = useState(item?.free_sale || false);
const [oneTimePurchase, setOneTimePurchase] = useState(
item?.one_time_purchase_limit || false,
);
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
setFormRef(internalRef.current); setFormRef(internalRef.current);
@@ -66,7 +70,7 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
(option: any) => (option: any) =>
option?.livestock_type?.weight_type === allocate?.weight_type && option?.livestock_type?.weight_type === allocate?.weight_type &&
option?.livestock_type?.id === allocate?.id && option?.livestock_type?.id === allocate?.id &&
option?.livestock_type?.name === allocate?.name option?.livestock_type?.name === allocate?.name,
); );
if (result) { if (result) {
return result.age_month; return result.age_month;
@@ -105,6 +109,7 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
}), }),
pre_sale: preSale, pre_sale: preSale,
free_sale: freeSale, free_sale: freeSale,
one_time_purchase_limit: oneTimePurchase,
}; };
if ( if (
@@ -234,7 +239,7 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const originalIndex = livestockTypes.findIndex( const originalIndex = livestockTypes.findIndex(
(type) => (type) =>
type.livestock_type === item.livestock_type && type.livestock_type === item.livestock_type &&
type.weight_type === item.weight_type type.weight_type === item.weight_type,
); );
if (originalIndex !== -1) { if (originalIndex !== -1) {
handleLivestockTypeChange(originalIndex, value); handleLivestockTypeChange(originalIndex, value);
@@ -268,7 +273,7 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
const originalIndex = livestockTypes.findIndex( const originalIndex = livestockTypes.findIndex(
(type) => (type) =>
type.livestock_type === item.livestock_type && type.livestock_type === item.livestock_type &&
type.weight_type === item.weight_type type.weight_type === item.weight_type,
); );
if (originalIndex !== -1) { if (originalIndex !== -1) {
handleLivestockTypeChange(originalIndex, value); handleLivestockTypeChange(originalIndex, value);
@@ -301,6 +306,16 @@ export const QuotaLevel3 = ({ item, onSubmit, setFormRef, visible }: Props) => {
}} }}
/> />
</Grid> </Grid>
<Grid className="flex gap-2 p-2 border-1 border-gray-200 rounded-xl items-center">
<Checkbox
label="محدودیت یکبار خرید از سهیمه"
checked={oneTimePurchase}
onChange={() => {
setOneTimePurchase(!oneTimePurchase);
}}
/>
</Grid>
</Grid> </Grid>
</Grid> </Grid>
</form> </form>

View File

@@ -1,10 +1,10 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import Checkbox from "../../components/CheckBox/CheckBox"; import Checkbox from "../../../components/CheckBox/CheckBox";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Textfield from "../../components/Textfeild/Textfeild"; import Textfield from "../../../components/Textfeild/Textfeild";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import AutoComplete from "../../components/AutoComplete/AutoComplete"; import AutoComplete from "../../../components/AutoComplete/AutoComplete";
type Props = { type Props = {
item: any; item: any;
@@ -64,7 +64,7 @@ export const QuotaLevel4 = ({
if (visible) { if (visible) {
const getQuatity = (allocate: any) => { const getQuatity = (allocate: any) => {
const result = item?.attribute_values?.find( const result = item?.attribute_values?.find(
(option: any) => option?.attribute === allocate?.id (option: any) => option?.attribute === allocate?.id,
); );
if (result) { if (result) {
return result.value; return result.value;
@@ -91,7 +91,7 @@ export const QuotaLevel4 = ({
if (visible) { if (visible) {
const getQuatity = (allocate: any) => { const getQuatity = (allocate: any) => {
const result = item?.brokers?.find( const result = item?.brokers?.find(
(option: any) => option?.broker === allocate?.id (option: any) => option?.broker === allocate?.id,
); );
if (result) { if (result) {
return result.value; return result.value;
@@ -169,7 +169,7 @@ export const QuotaLevel4 = ({
fixedBrokers.forEach((broker: any) => { fixedBrokers.forEach((broker: any) => {
const existingIndex = next.findIndex( const existingIndex = next.findIndex(
(selection) => (selection) =>
selection.pricing_type === pt?.id && selection.name === broker.fa selection.pricing_type === pt?.id && selection.name === broker.fa,
); );
if (existingIndex === -1) { if (existingIndex === -1) {
next.push({ next.push({
@@ -195,7 +195,7 @@ export const QuotaLevel4 = ({
return getPriceList() return getPriceList()
?.filter((opt) => { ?.filter((opt) => {
const isSelected = selectedItems.some( const isSelected = selectedItems.some(
(item) => item.name === opt.value (item) => item.name === opt.value,
); );
if (!isSelected) return false; if (!isSelected) return false;
const broker = brokersData?.find((b: any) => b.fa === opt.value); const broker = brokersData?.find((b: any) => b.fa === opt.value);
@@ -209,7 +209,7 @@ export const QuotaLevel4 = ({
const handleSubmitForm = () => { const handleSubmitForm = () => {
const allTypesSelected = priceTypesResponse?.results?.every((pt: any) => const allTypesSelected = priceTypesResponse?.results?.every((pt: any) =>
priceSelections?.some((item) => item.pricing_type === pt?.id) priceSelections?.some((item) => item.pricing_type === pt?.id),
); );
if ( if (
@@ -217,7 +217,7 @@ export const QuotaLevel4 = ({
!brokersData?.filter((opt: any) => opt.required && opt.value === 0).length !brokersData?.filter((opt: any) => opt.required && opt.value === 0).length
) { ) {
const activeBrokersData = brokersData?.filter( const activeBrokersData = brokersData?.filter(
(broker: any) => broker.active (broker: any) => broker.active,
); );
const activeBrokerNames = activeBrokersData?.map((b: any) => b.fa) || []; const activeBrokerNames = activeBrokersData?.map((b: any) => b.fa) || [];
const filteredPriceSelections = priceSelections?.filter((item) => { const filteredPriceSelections = priceSelections?.filter((item) => {
@@ -243,8 +243,8 @@ export const QuotaLevel4 = ({
...item, ...item,
value, value,
} }
: item : item,
) ),
); );
}; };
@@ -313,7 +313,7 @@ export const QuotaLevel4 = ({
}); });
if (!newActiveState) { if (!newActiveState) {
setPriceSelections((prev) => setPriceSelections((prev) =>
prev.filter((selection) => selection.name !== item.fa) prev.filter((selection) => selection.name !== item.fa),
); );
} }
}} }}
@@ -379,16 +379,16 @@ export const QuotaLevel4 = ({
onChange={(e: (string | number)[]) => { onChange={(e: (string | number)[]) => {
setPriceSelections((prev) => { setPriceSelections((prev) => {
const filtered = prev.filter( const filtered = prev.filter(
(item) => item.pricing_type !== pt?.id (item) => item.pricing_type !== pt?.id,
); );
const requiredBrokers = item const requiredBrokers = item
? [] ? []
: brokersData?.filter( : brokersData?.filter(
(broker: any) => broker?.fix_broker_price_state (broker: any) => broker?.fix_broker_price_state,
) || []; ) || [];
const newSelections = e.map((selectedKey) => { const newSelections = e.map((selectedKey) => {
const selectedItem = getPriceList().find( const selectedItem = getPriceList().find(
(item) => item.key === selectedKey (item) => item.key === selectedKey,
); );
return { return {
@@ -402,7 +402,7 @@ export const QuotaLevel4 = ({
const existingIndex = merged.findIndex( const existingIndex = merged.findIndex(
(selection) => (selection) =>
selection.pricing_type === pt?.id && selection.pricing_type === pt?.id &&
selection.name === broker.fa selection.name === broker.fa,
); );
const brokerValue = broker.value || 0; const brokerValue = broker.value || 0;
if (existingIndex === -1) { if (existingIndex === -1) {
@@ -424,11 +424,11 @@ export const QuotaLevel4 = ({
selectedKeys={(() => { selectedKeys={(() => {
const filtered = const filtered =
priceSelections?.filter( priceSelections?.filter(
(item) => item.pricing_type === pt?.id (item) => item.pricing_type === pt?.id,
) || []; ) || [];
const keys = filtered.map((item) => { const keys = filtered.map((item) => {
const priceItem = getPriceList().find( const priceItem = getPriceList().find(
(p) => p.value === item.name (p) => p.value === item.name,
); );
return priceItem?.key; return priceItem?.key;
}); });

View File

@@ -1,12 +1,12 @@
import { useNavigate, useParams } from "@tanstack/react-router"; import { useNavigate, useParams } from "@tanstack/react-router";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { REPORTING } from "../../routes/paths"; import { REPORTING } from "../../../routes/paths";
import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils"; import { getQuotaTableColumns, getQuotaTableRowData } from "./quotaTableUtils";
export const QuotaReportingProductDetails = () => { export const QuotaReportingProductDetails = () => {

View File

@@ -1,14 +1,14 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Button from "../../components/Button/Button"; import Button from "../../../components/Button/Button";
import { Popover } from "../../components/PopOver/PopOver"; import { Popover } from "../../../components/PopOver/PopOver";
import { Tooltip } from "../../components/Tooltip/Tooltip"; import { Tooltip } from "../../../components/Tooltip/Tooltip";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { REPORTING } from "../../routes/paths"; import { REPORTING } from "../../../routes/paths";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import { PaginationParameters } from "../../components/PaginationParameters/PaginationParameters"; import { PaginationParameters } from "../../../components/PaginationParameters/PaginationParameters";
interface QuotaDashboardByProduct { interface QuotaDashboardByProduct {
product_id: string; product_id: string;
@@ -42,7 +42,7 @@ export const QuotaReportingProducts = () => {
method: "get", method: "get",
params: { ...publicParams }, params: { ...publicParams },
queryKey: ["QuotaReportingAllProducts", publicParams], queryKey: ["QuotaReportingAllProducts", publicParams],
} },
); );
const navigate = useNavigate(); const navigate = useNavigate();
@@ -89,7 +89,7 @@ export const QuotaReportingProducts = () => {
</Tooltip> </Tooltip>
</Popover>, </Popover>,
]; ];
} },
); );
setPagesTableData(tableData); setPagesTableData(tableData);
} else { } else {

View File

@@ -1,15 +1,15 @@
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useApiRequest } from "../../utils/useApiRequest"; import { useApiRequest } from "../../../utils/useApiRequest";
import Table from "../../components/Table/Table"; import Table from "../../../components/Table/Table";
import { formatJustDate, formatJustTime } from "../../utils/formatTime"; import { formatJustDate, formatJustTime } from "../../../utils/formatTime";
import { getPersianMonths } from "../../utils/getPersianMonths"; import { getPersianMonths } from "../../../utils/getPersianMonths";
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../../components/ShowMoreInfo/ShowMoreInfo";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import ShowStringList from "../../components/ShowStringList/ShowStringList"; import ShowStringList from "../../../components/ShowStringList/ShowStringList";
import Divider from "../../components/Divider/Divider"; import Divider from "../../../components/Divider/Divider";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
export const QuotaReportingQuotaDistributions = () => { export const QuotaReportingQuotaDistributions = () => {
const params = useParams({ strict: false }); const params = useParams({ strict: false });
@@ -39,7 +39,7 @@ export const QuotaReportingQuotaDistributions = () => {
: i + pagesInfo.page_size * (pagesInfo.page - 1) + 1, : i + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
item?.distribution_id, item?.distribution_id,
`${formatJustDate(item?.create_date)} (${formatJustTime( `${formatJustDate(item?.create_date)} (${formatJustTime(
item?.create_date item?.create_date,
)})`, )})`,
item?.assigner_organization?.organization + item?.assigner_organization?.organization +
" (" + " (" +
@@ -172,7 +172,7 @@ export const QuotaReportingQuotaDistributions = () => {
</Typography> </Typography>
<ShowStringList <ShowStringList
strings={DashboardData?.limit_by_organizations?.map( strings={DashboardData?.limit_by_organizations?.map(
(opt: { name: string }) => opt?.name (opt: { name: string }) => opt?.name,
)} )}
/> />
@@ -190,7 +190,7 @@ export const QuotaReportingQuotaDistributions = () => {
opt?.livestock_type?.weight_type === "L" opt?.livestock_type?.weight_type === "L"
? "سبک" ? "سبک"
: "سنگین" : "سنگین"
}) با سن ${opt?.age_month}` }) با سن ${opt?.age_month}`,
)} )}
/> />
</Grid> </Grid>
@@ -250,12 +250,12 @@ export const QuotaReportingQuotaDistributions = () => {
</Typography> </Typography>
</div> </div>
</Grid> </Grid>
) ),
)} )}
</Grid> </Grid>
)} )}
</Grid> </Grid>
) ),
)} )}
</div> </div>
</ShowMoreInfo>, </ShowMoreInfo>,

View File

@@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
import Typography from "../../components/Typography/Typography"; import Typography from "../../../components/Typography/Typography";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import ShowStringList from "../../components/ShowStringList/ShowStringList"; import ShowStringList from "../../../components/ShowStringList/ShowStringList";
import Divider from "../../components/Divider/Divider"; import Divider from "../../../components/Divider/Divider";
import { formatJustDate } from "../../utils/formatTime"; import { formatJustDate } from "../../../utils/formatTime";
import { getPersianMonths } from "../../utils/getPersianMonths"; import { getPersianMonths } from "../../../utils/getPersianMonths";
interface QuotaViewProps { interface QuotaViewProps {
item: any; item: any;
@@ -130,7 +130,7 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
? "روستایی" ? "روستایی"
: group === "industrial" : group === "industrial"
? "صنعتی" ? "صنعتی"
: "عشایری" : "عشایری",
) )
.join(", ") || "-" .join(", ") || "-"
} }
@@ -202,8 +202,8 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
acc[group].push(allocation); acc[group].push(allocation);
return acc; return acc;
}, },
{} as Record<string, any[]> {} as Record<string, any[]>,
) ),
) as [string, any[]][] ) as [string, any[]][]
).map(([group, allocations]) => ( ).map(([group, allocations]) => (
<div key={group} className="space-y-2"> <div key={group} className="space-y-2">
@@ -296,6 +296,13 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
</Typography> </Typography>
</div> </div>
<Divider /> <Divider />
<div>
<Typography variant="body1" sign="info" className="mb-2">
محدودیت یکبار خرید از سهیمه:{" "}
{item?.one_time_purchase_limit ? "دارد" : "ندارد"}
</Typography>
</div>
<Divider />
<div> <div>
<Typography <Typography
variant="body2" variant="body2"
@@ -308,7 +315,7 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
<ShowStringList <ShowStringList
showSearch={false} showSearch={false}
strings={item?.limit_by_organizations?.map( strings={item?.limit_by_organizations?.map(
(opt: { name: string }) => opt?.name (opt: { name: string }) => opt?.name,
)} )}
/> />
) : ( ) : (
@@ -341,7 +348,7 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
opt?.livestock_type?.weight_type === "L" opt?.livestock_type?.weight_type === "L"
? "سبک" ? "سبک"
: "سنگین" : "سنگین"
}) با سن ${opt?.age_month}` }) با سن ${opt?.age_month}`,
)} )}
/> />
) : ( ) : (
@@ -423,7 +430,7 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
</Typography> </Typography>
</div> </div>
</div> </div>
) ),
)} )}
</div> </div>
)} )}
@@ -453,7 +460,7 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
pricing_type_name: string; pricing_type_name: string;
name: string; name: string;
value: number; value: number;
} },
) => { ) => {
const key = itm.pricing_type_name; const key = itm.pricing_type_name;
if (acc[key]) { if (acc[key]) {
@@ -463,8 +470,8 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
} }
return acc; return acc;
}, },
{} as Record<string, any> {} as Record<string, any>,
) ),
).map((priceItem: any, priceIndex: number) => ( ).map((priceItem: any, priceIndex: number) => (
<div <div
key={priceIndex} key={priceIndex}
@@ -499,8 +506,8 @@ export const QuotaView: React.FC<QuotaViewProps> = ({ item }) => {
acc[key].push(itm); acc[key].push(itm);
return acc; return acc;
}, },
{} as Record<string, any[]> {} as Record<string, any[]>,
) ),
) as [string, any[]][] ) as [string, any[]][]
).map(([pricingTypeName, items]) => ( ).map(([pricingTypeName, items]) => (
<div key={pricingTypeName} className="space-y-2"> <div key={pricingTypeName} className="space-y-2">

View File

@@ -1,10 +1,10 @@
import React from "react"; import React from "react";
import { formatJustDate } from "../../utils/formatTime"; import { formatJustDate } from "../../../utils/formatTime";
import { ShieldCheckIcon } from "@heroicons/react/24/solid"; import { ShieldCheckIcon } from "@heroicons/react/24/solid";
import { ShowWeight } from "../../components/ShowWeight/ShowWeight"; import { ShowWeight } from "../../../components/ShowWeight/ShowWeight";
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo"; import ShowMoreInfo from "../../../components/ShowMoreInfo/ShowMoreInfo";
import ShowStringList from "../../components/ShowStringList/ShowStringList"; import ShowStringList from "../../../components/ShowStringList/ShowStringList";
import { Grid } from "../../components/Grid/Grid"; import { Grid } from "../../../components/Grid/Grid";
export interface PriceCalculationItem { export interface PriceCalculationItem {
id: number; id: number;
@@ -114,7 +114,7 @@ const getFilteredPriceCalculationData = (
export const getQuotaTableRowData = ( export const getQuotaTableRowData = (
item: any, item: any,
index: number, index: number,
config: QuotaTableConfig config: QuotaTableConfig,
): any[] => { ): any[] => {
const rowData: any[] = []; const rowData: any[] = [];
const { const {
@@ -131,7 +131,7 @@ export const getQuotaTableRowData = (
rowData.push( rowData.push(
pagesInfo.page === 1 pagesInfo.page === 1
? index + 1 ? index + 1
: index + pagesInfo.page_size * (pagesInfo.page - 1) + 1 : index + pagesInfo.page_size * (pagesInfo.page - 1) + 1,
); );
} }
@@ -161,7 +161,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.quota_weight} weight={item?.quota_weight}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن توزیع شده // وزن توزیع شده
@@ -170,7 +170,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.quota_distributed} weight={item?.quota_distributed}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن باقیمانده سهمیه // وزن باقیمانده سهمیه
@@ -179,7 +179,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.remaining_weight} weight={item?.remaining_weight}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن فروش رفته // وزن فروش رفته
@@ -188,7 +188,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.been_sold} weight={item?.been_sold}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// ورود به انبار // ورود به انبار
@@ -197,7 +197,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.inventory_received} weight={item?.inventory_received}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// مانده انبار // مانده انبار
@@ -206,7 +206,7 @@ export const getQuotaTableRowData = (
key={index} key={index}
weight={item?.pre_sale_balance} weight={item?.pre_sale_balance}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// تاریخ بایگانی // تاریخ بایگانی
@@ -225,9 +225,9 @@ export const getQuotaTableRowData = (
? "روستایی" ? "روستایی"
: group === "industrial" : group === "industrial"
? "صنعتی" ? "صنعتی"
: "عشایری" : "عشایری",
) )
.join(", ") .join(", "),
); );
// مبدا // مبدا
@@ -270,7 +270,7 @@ export const getQuotaTableRowData = (
}, },
}, },
]} ]}
/> />,
); );
rowData.push( rowData.push(
@@ -286,7 +286,7 @@ export const getQuotaTableRowData = (
showSearch={false} showSearch={false}
/> />
</Grid> </Grid>
</ShowMoreInfo> </ShowMoreInfo>,
); );
// سهمیه و مجوز // سهمیه و مجوز
@@ -418,7 +418,7 @@ export const getQuotaTableRowData = (
? "بر اساس وزن" ? "بر اساس وزن"
: item?.pos_sale_type === "count" : item?.pos_sale_type === "count"
? "بر اساس تعداد راس دام" ? "بر اساس تعداد راس دام"
: "-" : "-",
); );
// طرح های تشویقی // طرح های تشویقی
@@ -555,7 +555,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.quota_weight} weight={item?.quota_weight}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن توزیع شده // وزن توزیع شده
@@ -564,7 +564,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.quota_distributed} weight={item?.quota_distributed}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن باقیمانده سهمیه // وزن باقیمانده سهمیه
@@ -573,7 +573,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.remaining_weight} weight={item?.remaining_weight}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// وزن فروش رفته // وزن فروش رفته
@@ -582,7 +582,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.been_sold} weight={item?.been_sold}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// ورود به انبار // ورود به انبار
@@ -591,7 +591,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.inventory_received} weight={item?.inventory_received}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// مانده انبار // مانده انبار
@@ -600,7 +600,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
key={item?.id} key={item?.id}
weight={item?.pre_sale_balance} weight={item?.pre_sale_balance}
type={item?.sale_unit?.unit} type={item?.sale_unit?.unit}
/> />,
); );
// واحد فروش // واحد فروش
@@ -614,9 +614,9 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
? "روستایی" ? "روستایی"
: group === "industrial" : group === "industrial"
? "صنعتی" ? "صنعتی"
: "عشایری" : "عشایری",
) )
.join(", ") .join(", "),
); );
// مبدا // مبدا
@@ -659,7 +659,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
}, },
}, },
]} ]}
/> />,
); );
// توزیع // توزیع
@@ -676,7 +676,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
showSearch={false} showSearch={false}
/> />
</Grid> </Grid>
</ShowMoreInfo> </ShowMoreInfo>,
); );
// سهمیه و مجوز // سهمیه و مجوز
@@ -808,7 +808,7 @@ export const getQuotaDashboardRowData = (item: any): any[] => {
? "بر اساس وزن" ? "بر اساس وزن"
: item?.pos_sale_type === "count" : item?.pos_sale_type === "count"
? "بر اساس تعداد راس دام" ? "بر اساس تعداد راس دام"
: "-" : "-",
); );
// طرح های تشویقی // طرح های تشویقی

Some files were not shown because too many files have changed in this diff Show More