review admin
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
VITE_TOKEN_NAME = 'admin_token'
|
VITE_TOKEN_NAME = 'admin_token'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token'
|
VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token'
|
||||||
VITE_BASE_URL = 'https://api.danakcorp.com'
|
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",
|
list: "/services/list",
|
||||||
category: "/services/category",
|
category: "/services/category",
|
||||||
plan: "/services/plan/",
|
plan: "/services/plan/",
|
||||||
|
review: "/services/reviews",
|
||||||
},
|
},
|
||||||
transactions: "/transactions",
|
transactions: "/transactions",
|
||||||
receipts: {
|
receipts: {
|
||||||
|
|||||||
+14
-1
@@ -83,7 +83,20 @@
|
|||||||
"user_list": "لیست کاربران",
|
"user_list": "لیست کاربران",
|
||||||
"add_user": "افزودن کاربر",
|
"add_user": "افزودن کاربر",
|
||||||
"role_list": "لیست نقش ها",
|
"role_list": "لیست نقش ها",
|
||||||
"role_create": "افزودن نقش"
|
"role_create": "افزودن نقش",
|
||||||
|
"reviews": "نظرات"
|
||||||
|
},
|
||||||
|
"review": {
|
||||||
|
"title": "عنوان",
|
||||||
|
"comment": "نظر",
|
||||||
|
"user": "کاربر",
|
||||||
|
"service": "سرویس",
|
||||||
|
"date": "تاریخ",
|
||||||
|
"status": "وضعیت",
|
||||||
|
"approved": "تایید شده",
|
||||||
|
"rejected": "رد شده",
|
||||||
|
"approve": "تایید",
|
||||||
|
"reject": "رد"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "جستجو"
|
"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),
|
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);
|
const { data } = await axios.patch(`/subscriptions/${id}`, params);
|
||||||
return data;
|
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;
|
isActive?: boolean;
|
||||||
serviceId?: string;
|
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 Plans from '../pages/service/Plans'
|
||||||
import GroupCreate from '../pages/users/GroupCreate'
|
import GroupCreate from '../pages/users/GroupCreate'
|
||||||
import GroupList from '../pages/users/GroupList'
|
import GroupList from '../pages/users/GroupList'
|
||||||
|
import Reviews from '../pages/service/Reviews'
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
@@ -80,6 +81,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.services.list} element={<ListService />} />
|
<Route path={Pages.services.list} element={<ListService />} />
|
||||||
<Route path={Pages.services.category} element={<Category />} />
|
<Route path={Pages.services.category} element={<Category />} />
|
||||||
<Route path={Pages.services.plan + ':id'} element={<Plans />} />
|
<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.list} element={<TicketList />} />
|
||||||
<Route path={Pages.ticket.create} element={<CreateTicket />} />
|
<Route path={Pages.ticket.create} element={<CreateTicket />} />
|
||||||
<Route path={Pages.ticket.category} element={<TicketCategory />} />
|
<Route path={Pages.ticket.category} element={<TicketCategory />} />
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ const ServicesSubMenu: FC = () => {
|
|||||||
isActive={isActive('category')}
|
isActive={isActive('category')}
|
||||||
link={Pages.services.category}
|
link={Pages.services.category}
|
||||||
/>
|
/>
|
||||||
|
<SubMenuItem
|
||||||
|
title={t('submenu.reviews')}
|
||||||
|
isActive={isActive('reviews')}
|
||||||
|
link={Pages.services.review}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user