sms count
This commit is contained in:
@@ -132,6 +132,7 @@ export const Pages = {
|
||||
},
|
||||
restaurants: {
|
||||
list: "/dmenu/restaurants/list",
|
||||
smsCountList: "/dmenu/sms-count/list",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+4
-1
@@ -872,6 +872,7 @@
|
||||
},
|
||||
"restaurant": {
|
||||
"list_restaurant": "لیست رستورانها",
|
||||
"list_sms_count": "لیست تعداد پیامکهای رستورانها",
|
||||
"name": "نام",
|
||||
"phone": "شماره تماس",
|
||||
"address": "آدرس",
|
||||
@@ -880,7 +881,9 @@
|
||||
"plan": "پلن",
|
||||
"created_at": "تاریخ ایجاد",
|
||||
"active": "فعال",
|
||||
"inactive": "غیرفعال"
|
||||
"inactive": "غیرفعال",
|
||||
"admins": "مدیران",
|
||||
"sms_count": "تعداد پیامک"
|
||||
},
|
||||
"cancel": "لغو"
|
||||
}
|
||||
|
||||
@@ -53,3 +53,18 @@ export const useGetRestaurants = () => {
|
||||
queryFn: api.getRestaurants,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetRestaurantAdmins = (restaurantId: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["restaurant-admins", restaurantId],
|
||||
queryFn: () => api.getRestaurantAdmins(restaurantId),
|
||||
enabled: !!restaurantId,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetRestaurantSmsCount = () => {
|
||||
return useQuery({
|
||||
queryKey: ["restaurant-sms-count"],
|
||||
queryFn: api.getRestaurantSmsCount,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC } from 'react'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGetRestaurants } from '../hooks/useIconData'
|
||||
import PageLoading from '../../../components/PageLoading'
|
||||
@@ -7,14 +7,28 @@ import { RestaurantType } from '../types/Types'
|
||||
import moment from 'moment-jalaali'
|
||||
import { Copy } from 'iconsax-react'
|
||||
import { toast } from 'react-toastify'
|
||||
import Button from '../../../components/Button'
|
||||
import RestaurantAdminsModal from './components/RestaurantAdminsModal'
|
||||
|
||||
const RestaurantsList: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { data: restaurants, isLoading } = useGetRestaurants()
|
||||
const [selectedRestaurantId, setSelectedRestaurantId] = useState<string>('')
|
||||
const [isAdminsModalOpen, setIsAdminsModalOpen] = useState<boolean>(false)
|
||||
|
||||
const restaurantsList = (restaurants as { data?: RestaurantType[] })?.data || []
|
||||
|
||||
const handleOpenAdminsModal = (restaurantId: string) => {
|
||||
setSelectedRestaurantId(restaurantId)
|
||||
setIsAdminsModalOpen(true)
|
||||
}
|
||||
|
||||
const handleCloseAdminsModal = () => {
|
||||
setIsAdminsModalOpen(false)
|
||||
setSelectedRestaurantId('')
|
||||
}
|
||||
|
||||
const handleCopyAddress = async (address: string | null) => {
|
||||
if (!address) return
|
||||
|
||||
@@ -87,21 +101,30 @@ const RestaurantsList: FC = () => {
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={''} />
|
||||
<Td text={''}>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button
|
||||
className='w-fit px-4'
|
||||
onClick={() => handleOpenAdminsModal(item.id)}
|
||||
>
|
||||
{t('restaurant.admins')}
|
||||
</Button>
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{restaurantsList.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-gray-500 text-lg">هیچ رستورانی یافت نشد</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
|
||||
<RestaurantAdminsModal
|
||||
open={isAdminsModalOpen}
|
||||
close={handleCloseAdminsModal}
|
||||
restaurantId={selectedRestaurantId}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useGetRestaurantSmsCount } from '../hooks/useIconData'
|
||||
import PageLoading from '../../../components/PageLoading'
|
||||
import Td from '../../../components/Td'
|
||||
import { RestaurantSmsCountItem } from '../types/Types'
|
||||
|
||||
const SmsCountList: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
const { data: smsCount, isLoading } = useGetRestaurantSmsCount()
|
||||
|
||||
const smsCountList = (smsCount as { data?: RestaurantSmsCountItem[] })?.data || []
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='flex w-full justify-between items-center'>
|
||||
<div>
|
||||
{t('restaurant.list_sms_count')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
isLoading ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='relative overflow-x-auto rounded-3xl mt-9 w-full'>
|
||||
<table className='w-full text-sm '>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={t('restaurant.name')} />
|
||||
<Td text={t('restaurant.sms_count')} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
smsCountList.map((item: RestaurantSmsCountItem) => {
|
||||
return (
|
||||
<tr key={item.restaurantId} className='tr'>
|
||||
<Td text={item.restaurantName} />
|
||||
<Td text={item.smsCount.toString()} />
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SmsCountList
|
||||
@@ -0,0 +1,71 @@
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import DefaulModal from '../../../../components/DefaulModal'
|
||||
import { useGetRestaurantAdmins } from '../../hooks/useIconData'
|
||||
import PageLoading from '../../../../components/PageLoading'
|
||||
import Td from '../../../../components/Td'
|
||||
import { RestaurantAdminType } from '../../types/Types'
|
||||
import moment from 'moment-jalaali'
|
||||
|
||||
interface RestaurantAdminsModalProps {
|
||||
open: boolean
|
||||
close: () => void
|
||||
restaurantId: string
|
||||
}
|
||||
|
||||
const RestaurantAdminsModal: FC<RestaurantAdminsModalProps> = ({ open, close, restaurantId }) => {
|
||||
const { t } = useTranslation('global')
|
||||
const { data: adminsData, isLoading } = useGetRestaurantAdmins(restaurantId)
|
||||
|
||||
const adminsList = (adminsData as { data?: RestaurantAdminType[] })?.data || []
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={open}
|
||||
close={close}
|
||||
title_header={t('restaurant.admins')}
|
||||
isHeader
|
||||
>
|
||||
<div className='mt-6'>
|
||||
{
|
||||
isLoading ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='relative overflow-x-auto rounded-3xl w-full'>
|
||||
<table className='w-full text-sm'>
|
||||
<thead className='thead'>
|
||||
<tr>
|
||||
<Td text={t('user.name')} />
|
||||
<Td text={t('user.email')} />
|
||||
<Td text={t('restaurant.phone')} />
|
||||
<Td text={t('restaurant.created_at')} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
adminsList.map((admin: RestaurantAdminType) => {
|
||||
return (
|
||||
<tr key={admin.id} className='tr'>
|
||||
<Td text={`${admin.firstName} ${admin.lastName}`} />
|
||||
<Td text={admin.email} />
|
||||
<Td text={admin.phone || '-'} />
|
||||
<Td text={''}>
|
||||
<div className='dltr text-right'>
|
||||
{moment(admin.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</Td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default RestaurantAdminsModal
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
IconsResponse,
|
||||
ReportsResponse,
|
||||
RestaurantsResponse,
|
||||
RestaurantSmsCountResponse,
|
||||
} from "../types/Types";
|
||||
|
||||
export const getIcons = async (): Promise<IconsResponse> => {
|
||||
@@ -47,3 +48,17 @@ export const getRestaurants = async (): Promise<RestaurantsResponse> => {
|
||||
const { data } = await axios.get("/admin/dmenu/restaurants");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getRestaurantAdmins = async (restaurantId: string) => {
|
||||
const { data } = await axios.get(
|
||||
`/admin/dmenu/restaurants/${restaurantId}/admins`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getRestaurantSmsCount = async (): Promise<
|
||||
RestaurantSmsCountResponse
|
||||
> => {
|
||||
const { data } = await axios.get(`/admin/dmenu/restaurants/sms-count`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -106,3 +106,29 @@ export type RestaurantType = {
|
||||
export interface RestaurantsResponse extends IResponse<RestaurantType[]> {
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export type RestaurantAdminType = {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export interface RestaurantAdminsResponse
|
||||
extends IResponse<RestaurantAdminType[]> {
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
export type RestaurantSmsCountItem = {
|
||||
restaurantId: string;
|
||||
restaurantName: string;
|
||||
smsCount: number;
|
||||
};
|
||||
|
||||
export interface RestaurantSmsCountResponse
|
||||
extends IResponse<RestaurantSmsCountItem[]> {
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ import IconsList from '../pages/dmenu/icon/List'
|
||||
import GroupIconList from '../pages/dmenu/icon/GroupList'
|
||||
import WarningsList from '../pages/dmenu/report/List.tsx'
|
||||
import RestaurantsList from '../pages/dmenu/restaurant/List.tsx'
|
||||
import SmsCountList from '../pages/dmenu/restaurant/SmsCountList.tsx'
|
||||
// import WarningsList from '../pages/dmenu/reports/List' // TODO: Create this component
|
||||
const MainRouter: FC = () => {
|
||||
|
||||
@@ -175,6 +176,7 @@ const MainRouter: FC = () => {
|
||||
<Route path={Pages.dmenu.icons.groupList} element={<GroupIconList />} />
|
||||
<Route path={Pages.dmenu.warnings.list} element={<WarningsList />} />
|
||||
<Route path={Pages.dmenu.restaurants.list} element={<RestaurantsList />} />
|
||||
<Route path={Pages.dmenu.restaurants.smsCountList} element={<SmsCountList />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+11
-1
@@ -2,7 +2,7 @@ import { FC, useEffect } from 'react'
|
||||
import LogoImage from '../assets/images/logo.svg'
|
||||
import LogoSmall from '../assets/images/logo-small.svg'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Card, Code, CodeCircle, DocumentLike, DocumentText, Element3, Gallery, Headphone, Home2, Logout, Message, Messages3, Money3, NotificationStatus, People, Profile, Receipt21, Setting2, SmsTracking, Teacher, TicketDiscount, UserSquare, Activity, Icon, Category, Warning2, Building } from 'iconsax-react'
|
||||
import { Card, Code, CodeCircle, DocumentLike, DocumentText, Element3, Gallery, Headphone, Home2, Logout, Message, Messages3, Money3, NotificationStatus, People, Profile, Receipt21, Setting2, SmsTracking, Teacher, TicketDiscount, UserSquare, Activity, Icon, Category, Warning2, Building, Sms } from 'iconsax-react'
|
||||
import SideBarItem from './SideBarItem'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Pages } from '../config/Pages'
|
||||
@@ -344,6 +344,16 @@ const SideBar: FC = () => {
|
||||
activeName='dmenu'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='text-xs text-[#8C90A3]'>
|
||||
<SideBarItem
|
||||
icon={<Sms variant={isActive('sms-count') ? 'Bold' : 'Outline'} color={isActive('sms-count') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||
title={t('sidebar.sms_count')}
|
||||
isActive={isActive('sms-count')}
|
||||
link={Pages.dmenu.restaurants.smsCountList}
|
||||
activeName='dmenu'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user