From cf3e2b7d4bfbe973e79b1bd0df8557db93fbf866 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Mon, 27 Oct 2025 11:14:13 +0330 Subject: [PATCH] feat: add shipment management modal for admin - Add ShipmentManagementModal component to manage shop shipment methods - Add button to Shop settings page to open shipment management modal - Update useShipmentData hooks to invalidate queries after add/remove operations - Modal shows active and available shipment methods for admin to manage --- src/pages/setting/Shop.tsx | 21 ++- .../components/ShipmentManagementModal.tsx | 154 ++++++++++++++++++ src/pages/shipment/List.tsx | 4 +- src/pages/shipment/hooks/useShipmentData.ts | 27 +++ src/pages/shipment/servicce/Service.ts | 20 +++ src/pages/shipment/types/Types.ts | 37 +++++ 6 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 src/pages/setting/components/ShipmentManagementModal.tsx diff --git a/src/pages/setting/Shop.tsx b/src/pages/setting/Shop.tsx index 9b13b20..3210d18 100644 --- a/src/pages/setting/Shop.tsx +++ b/src/pages/setting/Shop.tsx @@ -8,10 +8,11 @@ import Button from '../../components/Button' import Input from '../../components/Input' import Textarea from '../../components/Textarea' import UploadBox from '../../components/UploadBox' -import { TickCircle } from 'iconsax-react' +import { TickCircle, Truck } from 'iconsax-react' import { toast } from 'react-toastify' import { extractErrorMessage } from '@/helpers/utils' import { useSingleUpload } from '../category' +import ShipmentManagementModal from './components/ShipmentManagementModal' // Validation schema برای UpdateShopType const validationSchema = Yup.object().shape({ @@ -26,6 +27,7 @@ const Shop: FC = () => { const updateShopMutation = useUpdateShop() const uploadSingle = useSingleUpload() const [logoFile, setLogoFile] = useState([]) + const [isShipmentModalOpen, setIsShipmentModalOpen] = useState(false) const formik = useFormik({ initialValues: { @@ -91,7 +93,17 @@ const Shop: FC = () => {

ویرایش اطلاعات شاپ

-
+
+
+ + setIsShipmentModalOpen(false)} + /> ) } diff --git a/src/pages/setting/components/ShipmentManagementModal.tsx b/src/pages/setting/components/ShipmentManagementModal.tsx new file mode 100644 index 0000000..d3b4879 --- /dev/null +++ b/src/pages/setting/components/ShipmentManagementModal.tsx @@ -0,0 +1,154 @@ +import { type FC, useState } from 'react' +import DefaulModal from '@/components/DefaulModal' +import Button from '@/components/Button' +import { useGetShipment } from '../../shipment/hooks/useShipmentData' +import { useGetAdminShop, useAddShipmentToAdmin, useRemoveShipmentFromAdmin } from '../../shipment/hooks/useShipmentData' +import PageLoading from '@/components/PageLoading' +import Error from '@/components/Error' +import { Add, Trash } from 'iconsax-react' +import { toast } from 'react-toastify' + +interface Props { + open: boolean + close: () => void +} + +const ShipmentManagementModal: FC = ({ open, close }) => { + const [currentPage] = useState(1) + + const { data: shipmentsData, isLoading: shipmentsLoading, error: shipmentsError } = useGetShipment(currentPage) + const { data: adminShopData, isLoading: adminShopLoading, error: adminShopError, refetch: refetchAdminShop } = useGetAdminShop() + const addShipmentMutation = useAddShipmentToAdmin() + const removeShipmentMutation = useRemoveShipmentFromAdmin() + + if (shipmentsLoading || adminShopLoading) { + return ( + +
+ +
+
+ ) + } + + if (shipmentsError || adminShopError) { + return ( + +
+ +
+
+ ) + } + + const allShipments = shipmentsData?.results?.shipper || [] + const shopShipments = adminShopData?.results?.data?.shipmentMethod || [] + const shopShipmentIds = shopShipments.map(s => s._id) + + const availableShipments = allShipments.filter(shipment => !shopShipmentIds.includes(shipment._id)) + + const getDeliveryTypeText = (type: string) => { + switch (type) { + case 'Standard': + return 'معمولی' + case 'SameDay': + return 'همان روز' + case 'Express': + return 'اکسپرس' + default: + return type + } + } + + const handleAddShipment = async (shipmentId: number) => { + try { + await addShipmentMutation.mutateAsync(shipmentId.toString()) + toast.success('روش ارسال با موفقیت اضافه شد') + refetchAdminShop() + } catch { + toast.error('خطا در اضافه کردن روش ارسال') + } + } + + const handleRemoveShipment = async (shipmentId: number) => { + try { + await removeShipmentMutation.mutateAsync(shipmentId.toString()) + toast.success('روش ارسال با موفقیت حذف شد') + refetchAdminShop() + } catch { + toast.error('خطا در حذف روش ارسال') + } + } + + return ( + +
+ {/* روش‌های ارسال فروشگاه */} +
+

روش‌های ارسال فعال فروشگاه

+ {shopShipments.length === 0 ? ( +
+ هیچ روش ارسالی فعالی وجود ندارد +
+ ) : ( +
+ {shopShipments.map((shipment) => ( +
+
+
{shipment.name}
+
+ {getDeliveryTypeText(shipment.deliveryType)} - {shipment.deliveryTime} روز +
+
+ +
+ ))} +
+ )} +
+ + {/* روش‌های ارسال موجود برای اضافه کردن */} +
+

روش‌های ارسال قابل اضافه کردن

+ {availableShipments.length === 0 ? ( +
+ همه روش‌های ارسال اضافه شده‌اند +
+ ) : ( +
+ {availableShipments.map((shipment) => ( +
+
+
{shipment.name}
+
+ {getDeliveryTypeText(shipment.deliveryType)} - {shipment.deliveryTime} روز +
+
+ +
+ ))} +
+ )} +
+
+
+ ) +} + +export default ShipmentManagementModal diff --git a/src/pages/shipment/List.tsx b/src/pages/shipment/List.tsx index 72a4e98..a97cae7 100644 --- a/src/pages/shipment/List.tsx +++ b/src/pages/shipment/List.tsx @@ -1,6 +1,6 @@ import { type FC, useState } from 'react' import PageTitle from '../../components/PageTitle' -import { useGetShipment, useDeleteShipment } from './hooks/useShipmentData' +import { useGetShipment, useDeleteShipment, useGetAdminShop } from './hooks/useShipmentData' import { type Shipper } from './types/Types' import PageLoading from '@/components/PageLoading'; import Error from '@/components/Error'; @@ -14,7 +14,9 @@ import { Pages } from '@/config/Pages'; import { useNavigate } from 'react-router-dom'; const ShippingList: FC = () => { + const [currentPage, setCurrentPage] = useState(1); + useGetAdminShop() const navigate = useNavigate(); const { data, isLoading, error, refetch } = useGetShipment(currentPage); const deleteShipmentMutation = useDeleteShipment(); diff --git a/src/pages/shipment/hooks/useShipmentData.ts b/src/pages/shipment/hooks/useShipmentData.ts index 69594d5..babf62b 100644 --- a/src/pages/shipment/hooks/useShipmentData.ts +++ b/src/pages/shipment/hooks/useShipmentData.ts @@ -46,3 +46,30 @@ export const useUpdateShipment = () => { }, }); }; + +export const useRemoveShipmentFromAdmin = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (id: string) => api.removeShipmentFromAdmin(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["admin-shop"] }); + }, + }); +}; + +export const useAddShipmentToAdmin = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (id: string) => api.addShipmentToAdmin(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["admin-shop"] }); + }, + }); +}; + +export const useGetAdminShop = () => { + return useQuery({ + queryKey: ["admin-shop"], + queryFn: () => api.getAdminShop(), + }); +}; diff --git a/src/pages/shipment/servicce/Service.ts b/src/pages/shipment/servicce/Service.ts index 3a7927d..f60e68d 100644 --- a/src/pages/shipment/servicce/Service.ts +++ b/src/pages/shipment/servicce/Service.ts @@ -4,6 +4,7 @@ import type { CreateShipper, Shipper, ShipmentByIdResponse, + GetAdminShopResponse, } from "../types/Types"; export const getShipment = async ( @@ -37,3 +38,22 @@ export const updateShipment = async ( const { data } = await axios.patch(`/admin/shipment/${id}`, params); return data; }; + +export const removeShipmentFromAdmin = async (id: string): Promise => { + const { data } = await axios.post(`/admin/shipment/shipment/deactivate`, { + shipmentId: id, + }); + return data; +}; + +export const addShipmentToAdmin = async (id: string): Promise => { + const { data } = await axios.post(`/admin/shipment/shipment/activate`, { + shipmentId: id, + }); + return data; +}; + +export const getAdminShop = async (): Promise => { + const { data } = await axios.get(`/admin/shop`); + return data; +}; diff --git a/src/pages/shipment/types/Types.ts b/src/pages/shipment/types/Types.ts index ce297ef..3bd0dbd 100644 --- a/src/pages/shipment/types/Types.ts +++ b/src/pages/shipment/types/Types.ts @@ -54,3 +54,40 @@ export interface ShipmentByIdResponse { shipper: Shipper; }; } + +export interface ShipmentMethod { + _id: number; + name: string; + description: string; + costs: Cost[]; + deliveryType: string; + deliveryTime: number; + deleted: boolean; + createdAt: string; + updatedAt: string; +} + +export interface ShopData { + _id: string; + shopName: string; + shopDescription: string; + telephoneNumber: string; + shopPostalCode: string; + address: string | null; + logo: string; + shipmentMethod: ShipmentMethod[]; + isChatActive: boolean; + owner: string; + ownerRef: string; + createdAt: string; + updatedAt: string; + shopCode: number; +} + +export interface GetAdminShopResponse { + status: number; + success: boolean; + results: { + data: ShopData; + }; +}