add adminns

This commit is contained in:
hamid zarghami
2025-12-30 10:32:28 +03:30
parent 342326432d
commit bc61ea6a20
6 changed files with 307 additions and 38 deletions
+30 -1
View File
@@ -1,6 +1,10 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import * as api from "../service/IconService";
import { GroupIconsResponse, ReportsResponse } from "../types/Types";
import {
GroupIconsResponse,
ReportsResponse,
SystemRolesResponse,
} from "../types/Types";
export const useGetIcons = () => {
return useQuery({
@@ -68,3 +72,28 @@ export const useGetRestaurantSmsCount = () => {
queryFn: api.getRestaurantSmsCount,
});
};
export const useDeleteRestaurantAdmin = () => {
return useMutation({
mutationFn: ({
restaurantId,
adminId,
}: {
restaurantId: string;
adminId: string;
}) => api.deleteRestaurantAdmin(restaurantId, adminId),
});
};
export const useAddAdminRestaurant = () => {
return useMutation({
mutationFn: api.addAdminRestaurant,
});
};
export const useGetSystemRoles = () => {
return useQuery<SystemRolesResponse>({
queryKey: ["system-roles"],
queryFn: api.getSystemRoles,
});
};
@@ -0,0 +1,125 @@
import { FC } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useTranslation } from 'react-i18next'
import { AddAdminRestaurantType } from '../../types/Types'
import Input from '../../../../components/Input'
import Select from '../../../../components/Select'
import Button from '../../../../components/Button'
import { useAddAdminRestaurant, useGetSystemRoles } from '../../hooks/useIconData'
import { SystemRoleType } from '../../types/Types'
import { ErrorType } from '../../../../helpers/types'
import { toast } from 'react-toastify'
import { TickCircle } from 'iconsax-react'
interface AddRestaurantAdminFormProps {
restaurantId: string
onSuccess: () => void
onClose: () => void
}
const AddRestaurantAdminForm: FC<AddRestaurantAdminFormProps> = ({
restaurantId,
onSuccess,
onClose
}) => {
const { t } = useTranslation('global')
const { mutate: addAdmin, isPending } = useAddAdminRestaurant()
const { data: rolesData } = useGetSystemRoles()
const rolesList = rolesData?.data || []
const formik = useFormik<AddAdminRestaurantType>({
initialValues: {
restaurantId: restaurantId,
phone: '',
firstName: '',
lastName: '',
roleId: ''
},
validationSchema: Yup.object({
phone: Yup.string().required(t('errors.required')),
firstName: Yup.string().required(t('errors.required')),
lastName: Yup.string().required(t('errors.required')),
roleId: Yup.string().required(t('errors.required'))
}),
onSubmit: (values) => {
addAdmin(values, {
onSuccess: () => {
toast.success(t('success'))
formik.resetForm()
onSuccess()
onClose()
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
}
})
return (
<form onSubmit={formik.handleSubmit} className='mt-6'>
<div className='flex flex-col gap-6'>
<div className='rowTwoInput'>
<Input
label={t('user.name')}
{...formik.getFieldProps('firstName')}
error_text={formik.touched.firstName && formik.errors.firstName ? formik.errors.firstName : ''}
/>
<Input
label={t('user.family')}
{...formik.getFieldProps('lastName')}
error_text={formik.touched.lastName && formik.errors.lastName ? formik.errors.lastName : ''}
/>
</div>
<div className='rowTwoInput'>
<Input
label={t('restaurant.phone')}
{...formik.getFieldProps('phone')}
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
/>
<Select
label={t('user.role')}
placeholder={t('select')}
items={rolesList.map((item: SystemRoleType) => ({
value: item.id,
label: item.name
}))}
{...formik.getFieldProps('roleId')}
error_text={formik.touched.roleId && formik.errors.roleId ? formik.errors.roleId : ''}
/>
</div>
<div className='flex gap-4 justify-end mt-4'>
<Button
type='button'
onClick={onClose}
className='px-6 bg-gray-200 text-gray-700 hover:bg-gray-300'
>
{t('cancel')}
</Button>
<Button
type='submit'
isLoading={isPending}
className='px-6'
>
<div className='flex gap-2'>
<TickCircle
className='size-5'
color='white'
/>
<div className='text-sm'>
{t('restaurant.add_admin')}
</div>
</div>
</Button>
</div>
</div>
</form>
)
}
export default AddRestaurantAdminForm
@@ -1,11 +1,16 @@
import { FC } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import DefaulModal from '../../../../components/DefaulModal'
import { useGetRestaurantAdmins } from '../../hooks/useIconData'
import { useDeleteRestaurantAdmin, useGetRestaurantAdmins } from '../../hooks/useIconData'
import PageLoading from '../../../../components/PageLoading'
import Td from '../../../../components/Td'
import { RestaurantAdminType } from '../../types/Types'
import moment from 'moment-jalaali'
import TrashWithConfrim from '../../../../components/TrashWithConfrim'
import { toast } from 'react-toastify'
import { ErrorType } from '../../../../helpers/types'
import Button from '../../../../components/Button'
import AddRestaurantAdminForm from './AddRestaurantAdminForm'
interface RestaurantAdminsModalProps {
open: boolean
@@ -15,10 +20,37 @@ interface RestaurantAdminsModalProps {
const RestaurantAdminsModal: FC<RestaurantAdminsModalProps> = ({ open, close, restaurantId }) => {
const { t } = useTranslation('global')
const { data: adminsData, isLoading } = useGetRestaurantAdmins(restaurantId)
const { data: adminsData, isLoading, refetch } = useGetRestaurantAdmins(restaurantId)
const [showAddForm, setShowAddForm] = useState(false)
const adminsList = (adminsData as { data?: RestaurantAdminType[] })?.data || []
const { mutate: deleteAdmin, isPending } = useDeleteRestaurantAdmin()
const handleDeleteAdmin = (restaurantId: string, adminId: string) => {
deleteAdmin({ restaurantId, adminId }, {
onSuccess: () => {
refetch()
toast.success(t('success'))
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error?.message[0])
}
})
}
const handleAddAdmin = () => {
setShowAddForm(true)
}
const handleFormSuccess = () => {
refetch()
setShowAddForm(false)
}
const handleFormClose = () => {
setShowAddForm(false)
}
return (
<DefaulModal
open={open}
@@ -27,41 +59,66 @@ const RestaurantAdminsModal: FC<RestaurantAdminsModalProps> = ({ open, close, re
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>
{showAddForm ? (
<AddRestaurantAdminForm
restaurantId={restaurantId}
onSuccess={handleFormSuccess}
onClose={handleFormClose}
/>
) : (
<>
<div className='flex justify-end'>
<Button
onClick={handleAddAdmin}
className='w-fit px-5'
>
{t('restaurant.add_admin')}
</Button>
</div>
}
{
isLoading ?
<PageLoading />
:
<div className='relative overflow-x-auto rounded-3xl w-full mt-6'>
<table className='w-full text-sm'>
<thead className='thead'>
<tr>
<Td text={t('user.name')} />
<Td text={t('restaurant.phone')} />
<Td text={t('restaurant.created_at')} />
<Td text={''} />
</tr>
</thead>
<tbody>
{
adminsList.map((admin: RestaurantAdminType) => {
return (
<tr key={admin.id} className='tr'>
<Td text={`${admin.firstName} ${admin.lastName}`} />
<Td text={admin.phone || '-'} />
<Td text={''}>
<div className='dltr text-right'>
{moment(admin.createdAt).format('jYYYY-jMM-jDD HH:mm')}
</div>
</Td>
<Td text=''>
<div className='flex gap-2'>
<TrashWithConfrim
onDelete={() => handleDeleteAdmin(restaurantId, admin.id)}
isLoading={isPending}
/>
</div>
</Td>
</tr>
)
})
}
</tbody>
</table>
</div>
}
</>
)}
</div>
</DefaulModal>
)
+25
View File
@@ -7,6 +7,8 @@ import {
ReportsResponse,
RestaurantsResponse,
RestaurantSmsCountResponse,
AddAdminRestaurantType,
SystemRolesResponse,
} from "../types/Types";
export const getIcons = async (): Promise<IconsResponse> => {
@@ -62,3 +64,26 @@ export const getRestaurantSmsCount = async (): Promise<
const { data } = await axios.get(`/admin/dmenu/restaurants/sms-count`);
return data;
};
export const deleteRestaurantAdmin = async (
restaurantId: string,
adminId: string
) => {
const { data } = await axios.delete(
`/admin/dmenu/restaurants/${restaurantId}/admins/${adminId}`
);
return data;
};
export const addAdminRestaurant = async (params: AddAdminRestaurantType) => {
const { data } = await axios.post(
`/admin/dmenu/restaurants/${params.restaurantId}/admins`,
params
);
return data;
};
export const getSystemRoles = async (): Promise<SystemRolesResponse> => {
const { data } = await axios.get(`/admin/dmenu/system-roles`);
return data;
};
+32
View File
@@ -132,3 +132,35 @@ export interface RestaurantSmsCountResponse
extends IResponse<RestaurantSmsCountItem[]> {
statusCode: number;
}
export type AddAdminRestaurantType = {
restaurantId: string;
phone: string;
firstName: string;
lastName: string;
roleId: string;
};
export type PermissionType = {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
name: string;
title: string;
};
export type SystemRoleType = {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
name: string;
isSystem: boolean;
restaurant: string | null;
permissions: PermissionType[];
};
export interface SystemRolesResponse extends IResponse<SystemRoleType[]> {
statusCode: number;
}