announcement
This commit is contained in:
@@ -121,7 +121,7 @@ const MultiSelectSearchable: React.FC<MultiSelectSearchableProps> = ({
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-[var(--radix-popover-trigger-width)] max-w-[400px] p-0"
|
||||
className="w-[var(--radix-popover-trigger-width)] max-w-[400px] p-0 z-[70]"
|
||||
align="start"
|
||||
onKeyDown={(e) => {
|
||||
// Prevent event bubbling that might cause navigation
|
||||
@@ -154,7 +154,13 @@ const MultiSelectSearchable: React.FC<MultiSelectSearchableProps> = ({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<CommandList>
|
||||
<CommandList className="max-h-[200px]">
|
||||
{loading && options.length === 0 && (
|
||||
<div className="flex items-center justify-center py-6">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-900"></div>
|
||||
<span className="ml-2 text-sm text-muted-foreground">در حال بارگذاری...</span>
|
||||
</div>
|
||||
)}
|
||||
<CommandEmpty>موردی یافت نشد.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => {
|
||||
|
||||
@@ -198,5 +198,6 @@ export const Pages = {
|
||||
list: "/sellers/list",
|
||||
wholesaleRequests: "/sellers/wholesale-requests",
|
||||
withdrawalRequests: "/sellers/withdrawal-requests",
|
||||
announcement: "/sellers/announcement",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -196,3 +196,11 @@ textarea::placeholder {
|
||||
.rmdp-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { type FC, useState } from 'react'
|
||||
import { useGetAnnouncementSeller } from './hooks/useSellerData'
|
||||
import { type Notification } from './types/Types'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import Error from '../../components/Error'
|
||||
import PaginationUi from '../../components/PaginationUi'
|
||||
import Td from '../../components/Td'
|
||||
import AnnouncementTableRow from './components/AnnouncementTableRow'
|
||||
import Button from '@/components/Button'
|
||||
import CreateAnnouncementModal from './components/CreateAnnouncementModal'
|
||||
|
||||
const Annoncement: FC = () => {
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const { data: announcementsData, isLoading, error, refetch } = useGetAnnouncementSeller(currentPage)
|
||||
|
||||
const handleModalClose = () => {
|
||||
setIsModalOpen(false)
|
||||
}
|
||||
|
||||
const handleModalSuccess = () => {
|
||||
refetch()
|
||||
setCurrentPage(1) // Reset to first page after creating new announcement
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<PageLoading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<Error errorText="خطا در بارگذاری اطلاعیهها" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const notifications = announcementsData?.results?.notifications || []
|
||||
const notificationPager = announcementsData?.results?.pager
|
||||
|
||||
// Convert NotificationPager to PagerType expected by PaginationUi
|
||||
const pager = notificationPager ? {
|
||||
...notificationPager,
|
||||
nextPage: notificationPager.nextPage ? `${notificationPager.page + 1}` : null,
|
||||
prevPage: notificationPager.prevPage
|
||||
} : undefined
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='flex justify-end mt-5'>
|
||||
<Button
|
||||
label="ایجاد اطلاعیه جدید"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="w-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
|
||||
<table className='w-full text-sm'>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={'عنوان'} />
|
||||
<Td text={'متن اطلاعیه'} />
|
||||
<Td text={'نوع'} />
|
||||
<Td text={'تاریخ ایجاد'} />
|
||||
<Td text={'وضعیت'} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{notifications.length === 0 ? (
|
||||
<tr className='tr'>
|
||||
<td colSpan={5} className="text-center py-8 text-gray-500">
|
||||
هیچ اطلاعیهای یافت نشد
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
notifications.map((notification: Notification) => (
|
||||
<AnnouncementTableRow
|
||||
key={notification._id}
|
||||
notification={notification}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<PaginationUi
|
||||
pager={pager}
|
||||
onPageChange={(page) => {
|
||||
setCurrentPage(page)
|
||||
}}
|
||||
/>
|
||||
|
||||
<CreateAnnouncementModal
|
||||
open={isModalOpen}
|
||||
close={handleModalClose}
|
||||
onSuccess={handleModalSuccess}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Annoncement
|
||||
@@ -7,15 +7,25 @@ import Td from '../../components/Td'
|
||||
import WithdrawalRequestTableRow from './components/WithdrawalRequestTableRow'
|
||||
import WithdrawalRequestModal from './components/WithdrawalRequestModal'
|
||||
|
||||
interface WithdrawalRequestData {
|
||||
export interface WithdrawalRequestData {
|
||||
wallet: {
|
||||
_id: string;
|
||||
seller: {
|
||||
_id: string;
|
||||
fullName: string;
|
||||
email?: string;
|
||||
dateOfBirth: string | null;
|
||||
phoneNumber: string;
|
||||
accountStatus: "Pending" | "Approved";
|
||||
isRegisterCompleted: boolean;
|
||||
businessType: string | null;
|
||||
isWholesaler: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
email?: string;
|
||||
};
|
||||
balance: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
withdrawals: unknown[];
|
||||
lastPaidAmount: number | null;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { type FC } from 'react'
|
||||
import Td from '../../../components/Td'
|
||||
import { Calendar, MessageText } from 'iconsax-react'
|
||||
import StatusWithText from '../../../components/StatusWithText'
|
||||
import type { Notification } from '../types/Types'
|
||||
import { AnnouncementType } from '../types/Types'
|
||||
|
||||
interface Props {
|
||||
notification: Notification
|
||||
}
|
||||
|
||||
const AnnouncementTableRow: FC<Props> = ({ notification }) => {
|
||||
const getTypeLabel = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
[AnnouncementType.Announcement]: 'اطلاعیه عمومی',
|
||||
[AnnouncementType.Order]: 'سفارش',
|
||||
[AnnouncementType.Product]: 'محصول',
|
||||
[AnnouncementType.Shipment]: 'ارسال',
|
||||
[AnnouncementType.Fine]: 'جریمه',
|
||||
[AnnouncementType.Wallet]: 'کیف پول',
|
||||
[AnnouncementType.Ticket]: 'تیکت',
|
||||
[AnnouncementType.Chat]: 'چت',
|
||||
[AnnouncementType.Contract]: 'قرارداد'
|
||||
}
|
||||
return typeMap[type] || type
|
||||
}
|
||||
|
||||
const getTypeVariant = (type: string) => {
|
||||
switch (type) {
|
||||
case AnnouncementType.Announcement: return 'warning'
|
||||
case AnnouncementType.Order: return 'success'
|
||||
case AnnouncementType.Product: return 'warning'
|
||||
case AnnouncementType.Shipment: return 'success'
|
||||
case AnnouncementType.Fine: return 'error'
|
||||
case AnnouncementType.Wallet: return 'success'
|
||||
case AnnouncementType.Ticket: return 'warning'
|
||||
case AnnouncementType.Chat: return 'warning'
|
||||
case AnnouncementType.Contract: return 'success'
|
||||
default: return 'warning'
|
||||
}
|
||||
}
|
||||
|
||||
const truncateMessage = (message: string, maxLength: number = 100) => {
|
||||
if (message.length <= maxLength) return message
|
||||
return message.substring(0, maxLength) + '...'
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('fa-IR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className='tr'>
|
||||
<Td text="">
|
||||
<div className="max-w-xs">
|
||||
<div className="font-medium text-gray-900 truncate">
|
||||
{notification.title}
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div className="max-w-sm">
|
||||
<div className="flex items-start gap-2">
|
||||
<MessageText size={16} color="#8C90A3" className="mt-0.5 flex-shrink-0" />
|
||||
<span className="text-gray-600 text-sm leading-relaxed">
|
||||
{truncateMessage(notification.message)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<StatusWithText
|
||||
variant={getTypeVariant(notification.type)}
|
||||
text={getTypeLabel(notification.type)}
|
||||
/>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar size={16} color="#8C90A3" />
|
||||
<span className="text-gray-600 text-sm">
|
||||
{formatDate(notification.createdAt)}
|
||||
</span>
|
||||
</div>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<StatusWithText
|
||||
variant={notification.read ? 'success' : 'warning'}
|
||||
text={notification.read ? 'خوانده شده' : 'خوانده نشده'}
|
||||
/>
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementTableRow
|
||||
@@ -0,0 +1,190 @@
|
||||
import { type FC, useState, useMemo } from 'react'
|
||||
import DefaulModal from '@/components/DefaulModal'
|
||||
import Input from '@/components/Input'
|
||||
import Textarea from '@/components/Textarea'
|
||||
import Select, { type ItemsSelectType } from '@/components/Select'
|
||||
import Button from '@/components/Button'
|
||||
import MultiSelectSearchable, { type MultiSelectOption } from '@/components/MultiSelectSearchable'
|
||||
import RadioGroup from '@/components/RadioGroup'
|
||||
import { AnnouncementType, type CreateAnnouncementSeller, type CreateAnnouncementSpecificSeller } from '../types/Types'
|
||||
import { useCreateAnnouncementSeller, useCreateAnnouncementSpecificSeller, useGetSellers } from '../hooks/useSellerData'
|
||||
import { useFormik } from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import { toast } from 'react-toastify'
|
||||
import { extractErrorMessage } from '@/helpers/utils'
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
close: () => void
|
||||
onSuccess?: () => void
|
||||
}
|
||||
|
||||
const CreateAnnouncementModal: FC<Props> = ({ open, close, onSuccess }) => {
|
||||
const createMutation = useCreateAnnouncementSeller()
|
||||
const createSpecificMutation = useCreateAnnouncementSpecificSeller()
|
||||
const [selectedSellers, setSelectedSellers] = useState<string[]>([])
|
||||
const [sendToAll, setSendToAll] = useState(true)
|
||||
|
||||
// Get sellers for selection when modal is open
|
||||
const { data: sellersData, isLoading: sellersLoading, error: sellersError } = useGetSellers(1)
|
||||
|
||||
const announcementTypes: ItemsSelectType[] = [
|
||||
{ value: AnnouncementType.Announcement, label: 'اطلاعیه عمومی' },
|
||||
{ value: AnnouncementType.Order, label: 'سفارش' },
|
||||
{ value: AnnouncementType.Product, label: 'محصول' },
|
||||
{ value: AnnouncementType.Shipment, label: 'ارسال' },
|
||||
{ value: AnnouncementType.Fine, label: 'جریمه' },
|
||||
{ value: AnnouncementType.Wallet, label: 'کیف پول' },
|
||||
{ value: AnnouncementType.Ticket, label: 'تیکت' },
|
||||
{ value: AnnouncementType.Chat, label: 'چت' },
|
||||
{ value: AnnouncementType.Contract, label: 'قرارداد' }
|
||||
]
|
||||
|
||||
// Convert sellers to MultiSelectOption format
|
||||
const sellerOptions: MultiSelectOption[] = useMemo(() => {
|
||||
const sellers = sellersData?.results?.sellers || []
|
||||
return sellers
|
||||
.filter(seller => seller.fullName && seller.phoneNumber) // Filter out sellers with missing data
|
||||
.map(seller => ({
|
||||
value: seller._id,
|
||||
label: `${seller.fullName} (${seller.phoneNumber})`
|
||||
}))
|
||||
}, [sellersData])
|
||||
|
||||
const formik = useFormik<CreateAnnouncementSeller>({
|
||||
initialValues: {
|
||||
title: '',
|
||||
message: '',
|
||||
type: AnnouncementType.Announcement
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
title: Yup.string().required('عنوان اطلاعیه الزامی است'),
|
||||
message: Yup.string().required('متن اطلاعیه الزامی است'),
|
||||
type: Yup.string().required('نوع اطلاعیه الزامی است')
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
if (sendToAll) {
|
||||
// Send to all sellers
|
||||
createMutation.mutate(values, {
|
||||
onSuccess: () => {
|
||||
toast.success('اطلاعیه برای همه فروشندگان ارسال شد')
|
||||
close()
|
||||
onSuccess?.()
|
||||
formik.resetForm()
|
||||
setSelectedSellers([])
|
||||
setSendToAll(true)
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(extractErrorMessage(error))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Send to specific sellers
|
||||
if (selectedSellers.length === 0) {
|
||||
toast.error('لطفا حداقل یک فروشنده انتخاب کنید')
|
||||
return
|
||||
}
|
||||
|
||||
const specificValues: CreateAnnouncementSpecificSeller = {
|
||||
...values,
|
||||
sellerIds: selectedSellers
|
||||
}
|
||||
|
||||
createSpecificMutation.mutate(specificValues, {
|
||||
onSuccess: () => {
|
||||
toast.success(`اطلاعیه برای ${selectedSellers.length} فروشنده ارسال شد`)
|
||||
close()
|
||||
onSuccess?.()
|
||||
formik.resetForm()
|
||||
setSelectedSellers([])
|
||||
setSendToAll(true)
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(extractErrorMessage(error))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={open}
|
||||
close={close}
|
||||
isHeader={true}
|
||||
title_header="ایجاد اطلاعیه جدید"
|
||||
width={600}
|
||||
>
|
||||
<div className="space-y-6 w-[450px] mt-4">
|
||||
<Input
|
||||
label="عنوان اطلاعیه"
|
||||
placeholder="عنوان اطلاعیه را وارد کنید"
|
||||
{...formik.getFieldProps('title')}
|
||||
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
label="متن اطلاعیه"
|
||||
placeholder="متن اطلاعیه را وارد کنید"
|
||||
rows={4}
|
||||
{...formik.getFieldProps('message')}
|
||||
error_text={formik.touched.message && formik.errors.message ? formik.errors.message : ''}
|
||||
/>
|
||||
|
||||
<Select
|
||||
label="نوع اطلاعیه"
|
||||
items={announcementTypes}
|
||||
placeholder="نوع اطلاعیه را انتخاب کنید"
|
||||
{...formik.getFieldProps('type')}
|
||||
error_text={formik.touched.type && formik.errors.type ? formik.errors.type : ''}
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sm font-medium text-gray-700">گیرندگان اطلاعیه</label>
|
||||
<RadioGroup
|
||||
items={[
|
||||
{ label: 'ارسال به همه فروشندگان', value: true },
|
||||
{ label: 'ارسال به فروشندگان خاص', value: false }
|
||||
]}
|
||||
selected={sendToAll}
|
||||
onChange={(value) => {
|
||||
setSendToAll(value as boolean)
|
||||
setSelectedSellers([])
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!sendToAll && (
|
||||
<MultiSelectSearchable
|
||||
label="انتخاب فروشندگان"
|
||||
placeholder={sellersLoading ? "در حال بارگذاری فروشندگان..." : sellersError ? "خطا در بارگذاری فروشندگان" : sellerOptions.length === 0 ? "فروشندهای یافت نشد" : "فروشندگان مورد نظر را انتخاب کنید"}
|
||||
options={sellerOptions.length > 0 ? sellerOptions : []}
|
||||
value={selectedSellers}
|
||||
onChange={setSelectedSellers}
|
||||
error_text={sellersError ? 'خطا در بارگذاری فروشندگان' : selectedSellers.length === 0 && !sendToAll ? 'انتخاب حداقل یک فروشنده الزامی است' : ''}
|
||||
loading={sellersLoading && sellerOptions.length === 0}
|
||||
disabled={!!sellersError}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button
|
||||
label="لغو"
|
||||
variant="outline"
|
||||
onClick={close}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
label="ایجاد اطلاعیه"
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={createMutation.isPending || createSpecificMutation.isPending}
|
||||
disabled={!formik.isValid || createMutation.isPending || createSpecificMutation.isPending || (!sendToAll && selectedSellers.length === 0)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateAnnouncementModal
|
||||
@@ -1,21 +1,7 @@
|
||||
import { type FC } from 'react'
|
||||
import Td from '../../../components/Td'
|
||||
import { Call, Card, User as UserIcon, Money, Eye } from 'iconsax-react'
|
||||
|
||||
interface WithdrawalRequestData {
|
||||
wallet: {
|
||||
_id: string;
|
||||
seller: {
|
||||
fullName: string;
|
||||
email?: string;
|
||||
phoneNumber: string;
|
||||
};
|
||||
balance: number;
|
||||
};
|
||||
withdrawals: unknown[];
|
||||
lastPaidAmount: number | null;
|
||||
lastPaidDate: string | null;
|
||||
}
|
||||
import type { WithdrawalRequestData } from '../WithdrawalRequest'
|
||||
|
||||
interface Props {
|
||||
request: WithdrawalRequestData
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import * as api from "../service/SellerService";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useGetSellers = (page: number = 1) => {
|
||||
export const useGetSellers = (page: number = 1, enabled: boolean = true) => {
|
||||
return useQuery({
|
||||
queryKey: ["sellers", page],
|
||||
queryFn: () => api.getSellers(page),
|
||||
enabled,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -39,3 +40,22 @@ export const useGetWithdrawalRequests = (page: number = 1) => {
|
||||
queryFn: () => api.getWithdrawalRequests(page),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAnnouncementSeller = (page: number = 1) => {
|
||||
return useQuery({
|
||||
queryKey: ["announcement-seller", page],
|
||||
queryFn: () => api.getAnnouncementSeller(page),
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateAnnouncementSeller = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.createAnnouncementSeller,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateAnnouncementSpecificSeller = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.createAnnouncementToSpecificSellers,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,6 +3,9 @@ import type {
|
||||
SellersResponse,
|
||||
WholesaleRequestsResponse,
|
||||
WithdrawalRequestsResponse,
|
||||
CreateAnnouncementSeller,
|
||||
NotificationsResponse,
|
||||
CreateAnnouncementSpecificSeller,
|
||||
} from "../types/Types";
|
||||
|
||||
export const getSellers = async (
|
||||
@@ -48,3 +51,29 @@ export const getWithdrawalRequests = async (
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getAnnouncementSeller = async (
|
||||
page: number = 1
|
||||
): Promise<NotificationsResponse> => {
|
||||
const { data } = await axios.get(
|
||||
`/admin/notification/allSeller?page=${page}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createAnnouncementSeller = async (
|
||||
params: CreateAnnouncementSeller
|
||||
) => {
|
||||
const { data } = await axios.post(`/admin/notification/allSeller`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createAnnouncementToSpecificSellers = async (
|
||||
params: CreateAnnouncementSpecificSeller
|
||||
) => {
|
||||
const { data } = await axios.post(
|
||||
`/admin/notification/specific-sellers`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -159,3 +159,56 @@ export interface WithdrawalRequestsResponse {
|
||||
walletData: WithdrawalRequest[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface CreateAnnouncementSeller {
|
||||
title: string;
|
||||
message: string;
|
||||
type: AnnouncementType;
|
||||
}
|
||||
|
||||
export interface CreateAnnouncementSpecificSeller
|
||||
extends CreateAnnouncementSeller {
|
||||
sellerIds: string[];
|
||||
}
|
||||
|
||||
export enum AnnouncementType {
|
||||
Announcement = "announcement",
|
||||
Order = "order",
|
||||
Product = "product",
|
||||
Shipment = "shipment",
|
||||
Fine = "fine",
|
||||
Wallet = "wallet",
|
||||
Ticket = "ticket",
|
||||
Chat = "chat",
|
||||
Contract = "contract",
|
||||
}
|
||||
|
||||
export interface NotificationPager {
|
||||
page: number;
|
||||
limit: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
prevPage: boolean;
|
||||
nextPage: boolean;
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
_id: string;
|
||||
recipient: string;
|
||||
type: string;
|
||||
title: string;
|
||||
message: string;
|
||||
meta: unknown | null;
|
||||
read: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface NotificationsResponse {
|
||||
status: number;
|
||||
success: boolean;
|
||||
results: {
|
||||
pager: NotificationPager;
|
||||
notifications: Notification[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import Dashboard from '@/pages/dashboard/Dashboard'
|
||||
import SellerList from '@/pages/seller/List'
|
||||
import WholeSaleRequest from '@/pages/seller/WholeSaleRequest'
|
||||
import WithdrawalRequestPage from '@/pages/seller/WithdrawalRequest'
|
||||
import Annoncement from '@/pages/seller/Annoncement'
|
||||
const MainRouter: FC = () => {
|
||||
|
||||
const { hasSubMenu } = useSharedStore()
|
||||
@@ -163,6 +164,7 @@ const MainRouter: FC = () => {
|
||||
<Route path={Pages.sellers.list} element={<SellerList />} />
|
||||
<Route path={Pages.sellers.wholesaleRequests} element={<WholeSaleRequest />} />
|
||||
<Route path={Pages.sellers.withdrawalRequests} element={<WithdrawalRequestPage />} />
|
||||
<Route path={Pages.sellers.announcement} element={<Annoncement />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -513,8 +513,8 @@ const SellersSubMenu: FC = () => {
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={'ارسال اطلاعیه'}
|
||||
isActive={isActive('notifications')}
|
||||
link="/sellers/notifications"
|
||||
isActive={isActive('announcement')}
|
||||
link={Pages.sellers.announcement}
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={'درخواست های ثبت محصول'}
|
||||
|
||||
Reference in New Issue
Block a user