sms count
This commit is contained in:
@@ -132,6 +132,7 @@ export const Pages = {
|
|||||||
},
|
},
|
||||||
restaurants: {
|
restaurants: {
|
||||||
list: "/dmenu/restaurants/list",
|
list: "/dmenu/restaurants/list",
|
||||||
|
smsCountList: "/dmenu/sms-count/list",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-1
@@ -872,6 +872,7 @@
|
|||||||
},
|
},
|
||||||
"restaurant": {
|
"restaurant": {
|
||||||
"list_restaurant": "لیست رستورانها",
|
"list_restaurant": "لیست رستورانها",
|
||||||
|
"list_sms_count": "لیست تعداد پیامکهای رستورانها",
|
||||||
"name": "نام",
|
"name": "نام",
|
||||||
"phone": "شماره تماس",
|
"phone": "شماره تماس",
|
||||||
"address": "آدرس",
|
"address": "آدرس",
|
||||||
@@ -880,7 +881,9 @@
|
|||||||
"plan": "پلن",
|
"plan": "پلن",
|
||||||
"created_at": "تاریخ ایجاد",
|
"created_at": "تاریخ ایجاد",
|
||||||
"active": "فعال",
|
"active": "فعال",
|
||||||
"inactive": "غیرفعال"
|
"inactive": "غیرفعال",
|
||||||
|
"admins": "مدیران",
|
||||||
|
"sms_count": "تعداد پیامک"
|
||||||
},
|
},
|
||||||
"cancel": "لغو"
|
"cancel": "لغو"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,3 +53,18 @@ export const useGetRestaurants = () => {
|
|||||||
queryFn: api.getRestaurants,
|
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 { useTranslation } from 'react-i18next'
|
||||||
import { useGetRestaurants } from '../hooks/useIconData'
|
import { useGetRestaurants } from '../hooks/useIconData'
|
||||||
import PageLoading from '../../../components/PageLoading'
|
import PageLoading from '../../../components/PageLoading'
|
||||||
@@ -7,14 +7,28 @@ import { RestaurantType } from '../types/Types'
|
|||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import { Copy } from 'iconsax-react'
|
import { Copy } from 'iconsax-react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
import Button from '../../../components/Button'
|
||||||
|
import RestaurantAdminsModal from './components/RestaurantAdminsModal'
|
||||||
|
|
||||||
const RestaurantsList: FC = () => {
|
const RestaurantsList: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const { data: restaurants, isLoading } = useGetRestaurants()
|
const { data: restaurants, isLoading } = useGetRestaurants()
|
||||||
|
const [selectedRestaurantId, setSelectedRestaurantId] = useState<string>('')
|
||||||
|
const [isAdminsModalOpen, setIsAdminsModalOpen] = useState<boolean>(false)
|
||||||
|
|
||||||
const restaurantsList = (restaurants as { data?: RestaurantType[] })?.data || []
|
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) => {
|
const handleCopyAddress = async (address: string | null) => {
|
||||||
if (!address) return
|
if (!address) return
|
||||||
|
|
||||||
@@ -87,21 +101,30 @@ const RestaurantsList: FC = () => {
|
|||||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</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>
|
</tr>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{restaurantsList.length === 0 && (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<div className="text-gray-500 text-lg">هیچ رستورانی یافت نشد</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<RestaurantAdminsModal
|
||||||
|
open={isAdminsModalOpen}
|
||||||
|
close={handleCloseAdminsModal}
|
||||||
|
restaurantId={selectedRestaurantId}
|
||||||
|
/>
|
||||||
</div>
|
</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,
|
IconsResponse,
|
||||||
ReportsResponse,
|
ReportsResponse,
|
||||||
RestaurantsResponse,
|
RestaurantsResponse,
|
||||||
|
RestaurantSmsCountResponse,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
|
|
||||||
export const getIcons = async (): Promise<IconsResponse> => {
|
export const getIcons = async (): Promise<IconsResponse> => {
|
||||||
@@ -47,3 +48,17 @@ export const getRestaurants = async (): Promise<RestaurantsResponse> => {
|
|||||||
const { data } = await axios.get("/admin/dmenu/restaurants");
|
const { data } = await axios.get("/admin/dmenu/restaurants");
|
||||||
return data;
|
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[]> {
|
export interface RestaurantsResponse extends IResponse<RestaurantType[]> {
|
||||||
statusCode: number;
|
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 GroupIconList from '../pages/dmenu/icon/GroupList'
|
||||||
import WarningsList from '../pages/dmenu/report/List.tsx'
|
import WarningsList from '../pages/dmenu/report/List.tsx'
|
||||||
import RestaurantsList from '../pages/dmenu/restaurant/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
|
// import WarningsList from '../pages/dmenu/reports/List' // TODO: Create this component
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
@@ -175,6 +176,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.dmenu.icons.groupList} element={<GroupIconList />} />
|
<Route path={Pages.dmenu.icons.groupList} element={<GroupIconList />} />
|
||||||
<Route path={Pages.dmenu.warnings.list} element={<WarningsList />} />
|
<Route path={Pages.dmenu.warnings.list} element={<WarningsList />} />
|
||||||
<Route path={Pages.dmenu.restaurants.list} element={<RestaurantsList />} />
|
<Route path={Pages.dmenu.restaurants.list} element={<RestaurantsList />} />
|
||||||
|
<Route path={Pages.dmenu.restaurants.smsCountList} element={<SmsCountList />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+11
-1
@@ -2,7 +2,7 @@ import { FC, useEffect } from 'react'
|
|||||||
import LogoImage from '../assets/images/logo.svg'
|
import LogoImage from '../assets/images/logo.svg'
|
||||||
import LogoSmall from '../assets/images/logo-small.svg'
|
import LogoSmall from '../assets/images/logo-small.svg'
|
||||||
import { useTranslation } from 'react-i18next'
|
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 SideBarItem from './SideBarItem'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Pages } from '../config/Pages'
|
import { Pages } from '../config/Pages'
|
||||||
@@ -344,6 +344,16 @@ const SideBar: FC = () => {
|
|||||||
activeName='dmenu'
|
activeName='dmenu'
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user