review admin
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
VITE_TOKEN_NAME = 'admin_token'
|
||||
VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token'
|
||||
VITE_BASE_URL = 'https://api.danakcorp.com'
|
||||
# VITE_BASE_URL = 'http://192.168.0.245:4000'
|
||||
# VITE_BASE_URL = 'http://192.168.1.124:4000'
|
||||
@@ -13,6 +13,7 @@ export const Pages = {
|
||||
list: "/services/list",
|
||||
category: "/services/category",
|
||||
plan: "/services/plan/",
|
||||
review: "/services/reviews",
|
||||
},
|
||||
transactions: "/transactions",
|
||||
receipts: {
|
||||
|
||||
+14
-1
@@ -83,7 +83,20 @@
|
||||
"user_list": "لیست کاربران",
|
||||
"add_user": "افزودن کاربر",
|
||||
"role_list": "لیست نقش ها",
|
||||
"role_create": "افزودن نقش"
|
||||
"role_create": "افزودن نقش",
|
||||
"reviews": "نظرات"
|
||||
},
|
||||
"review": {
|
||||
"title": "عنوان",
|
||||
"comment": "نظر",
|
||||
"user": "کاربر",
|
||||
"service": "سرویس",
|
||||
"date": "تاریخ",
|
||||
"status": "وضعیت",
|
||||
"approved": "تایید شده",
|
||||
"rejected": "رد شده",
|
||||
"approve": "تایید",
|
||||
"reject": "رد"
|
||||
},
|
||||
"header": {
|
||||
"search": "جستجو"
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useChangeStatusReview, useGetReviews } from './hooks/useServiceData'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Td from '../../components/Td'
|
||||
import { ReviewItemType } from './types/ServiceTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, TickCircle } from 'iconsax-react'
|
||||
|
||||
enum ReviewStatusEnum {
|
||||
PENDING = 'PENDING',
|
||||
APPROVED = 'APPROVED',
|
||||
REJECTED = 'REJECTED'
|
||||
}
|
||||
|
||||
const Reviews: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [loadingType, setLoadingType] = useState<ReviewStatusEnum>(ReviewStatusEnum.PENDING)
|
||||
const [loadingId, setLoadingId] = useState<string>('')
|
||||
const getReviews = useGetReviews()
|
||||
const changeStatusReview = useChangeStatusReview()
|
||||
|
||||
const handleChange = (status: ReviewStatusEnum, id: string) => {
|
||||
setLoadingType(status)
|
||||
setLoadingId(id)
|
||||
changeStatusReview.mutate({ status, id }, {
|
||||
onSuccess: () => {
|
||||
getReviews.refetch()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<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('review.title')} />
|
||||
<Td text={t('review.comment')} />
|
||||
<Td text={t('review.user')} />
|
||||
<Td text={t('review.service')} />
|
||||
<Td text={t('review.date')} />
|
||||
<Td text={t('review.status')} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
getReviews.data?.data?.reviews?.map((item: ReviewItemType) => (
|
||||
<tr className='tr'>
|
||||
<Td text={item.title} />
|
||||
<Td text={item.comment} />
|
||||
<Td text={item.user?.firstName + ' ' + item.user.lastName} />
|
||||
<Td text={item.service?.name} />
|
||||
<Td text={''}>
|
||||
<div className='dltr text-right'>
|
||||
{moment(item.createdAt).format('jYYYY-jMM-jDD HH:mm')}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text={''}>
|
||||
{
|
||||
item.status === 'PENDING' ?
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
className='bg-green-500 text-xs h-7 w-fit px-3 text-white'
|
||||
onClick={() => handleChange(ReviewStatusEnum.APPROVED, item.id)}
|
||||
isLoading={changeStatusReview.isPending && loadingType === ReviewStatusEnum.APPROVED && item.id === loadingId}
|
||||
>
|
||||
<div className='flex gap-1.5 items-center'>
|
||||
<TickCircle size={16} color='white' />
|
||||
<div>
|
||||
{t('review.approve')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
className='bg-red-500 text-xs h-7 w-fit px-3 text-white'
|
||||
onClick={() => handleChange(ReviewStatusEnum.REJECTED, item.id)}
|
||||
isLoading={changeStatusReview.isPending && loadingType === ReviewStatusEnum.REJECTED && item.id === loadingId}
|
||||
>
|
||||
<div className='flex gap-1.5 items-center'>
|
||||
<CloseCircle size={16} color='white' />
|
||||
<div>
|
||||
{t('review.reject')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
:
|
||||
t('review.' + item.status.toLowerCase())
|
||||
}
|
||||
</Td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Reviews
|
||||
@@ -132,3 +132,17 @@ export const useUpdatePlan = (id: string) => {
|
||||
mutationFn: (variables: UpdatePlanType) => api.updatePlan(id, variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetReviews = (page: number = 1) => {
|
||||
return useQuery({
|
||||
queryKey: ["reviews", page],
|
||||
queryFn: () => api.getReviews(page),
|
||||
});
|
||||
};
|
||||
|
||||
export const useChangeStatusReview = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: { status: string; id: string }) =>
|
||||
api.changeStatusReview(variables),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -115,3 +115,18 @@ export const updatePlan = async (id: string, params: UpdatePlanType) => {
|
||||
const { data } = await axios.patch(`/subscriptions/${id}`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getReviews = async (page: number) => {
|
||||
const { data } = await axios.get(`/danak-services/reviews?page=${page}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const changeStatusReview = async (params: {
|
||||
status: string;
|
||||
id: string;
|
||||
}) => {
|
||||
const { data } = await axios.patch(
|
||||
`/danak-services/reviews/${params.id}/${params.status}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -82,3 +82,22 @@ export type UpdatePlanType = {
|
||||
isActive?: boolean;
|
||||
serviceId?: string;
|
||||
};
|
||||
|
||||
export type ReviewItemType = {
|
||||
id: string;
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
rating: number;
|
||||
service: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
status: "APPROVED" | "PENDING" | "REJECTED";
|
||||
title: string;
|
||||
user: {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
profilePic: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ import CreateLearning from '../pages/learning/Create'
|
||||
import Plans from '../pages/service/Plans'
|
||||
import GroupCreate from '../pages/users/GroupCreate'
|
||||
import GroupList from '../pages/users/GroupList'
|
||||
import Reviews from '../pages/service/Reviews'
|
||||
|
||||
const MainRouter: FC = () => {
|
||||
|
||||
@@ -80,6 +81,7 @@ const MainRouter: FC = () => {
|
||||
<Route path={Pages.services.list} element={<ListService />} />
|
||||
<Route path={Pages.services.category} element={<Category />} />
|
||||
<Route path={Pages.services.plan + ':id'} element={<Plans />} />
|
||||
<Route path={Pages.services.review} element={<Reviews />} />
|
||||
<Route path={Pages.ticket.list} element={<TicketList />} />
|
||||
<Route path={Pages.ticket.create} element={<CreateTicket />} />
|
||||
<Route path={Pages.ticket.category} element={<TicketCategory />} />
|
||||
|
||||
@@ -44,6 +44,11 @@ const ServicesSubMenu: FC = () => {
|
||||
isActive={isActive('category')}
|
||||
link={Pages.services.category}
|
||||
/>
|
||||
<SubMenuItem
|
||||
title={t('submenu.reviews')}
|
||||
isActive={isActive('reviews')}
|
||||
link={Pages.services.review}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user