review
This commit is contained in:
@@ -38,7 +38,7 @@ const Notifications: FC = () => {
|
||||
<div ref={ref}>
|
||||
<div onClick={() => setShowModal(!showModal)} className='relative cursor-pointer'>
|
||||
<Notification size={18} color="black" />
|
||||
<div className="absolute top-0 right-0 -mt-1 -mr-1 rounded-full bg-red-500 text-white text-[8px] size-3 flex justify-center items-center">
|
||||
<div className="absolute top-0 right-0 -mt-1 -mr-1 rounded-full bg-red-500 text-white text-[8px] size-3 flex pt-0.5 pl-[1px] justify-center items-center">
|
||||
{getDashboard.data?.data?.notificationCount}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -93,11 +93,18 @@ const CallBack: FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
label={t('callback.back')}
|
||||
className='xl:max-w-[560px] mt-8'
|
||||
onClick={() => navigate(Pages.transactions)}
|
||||
/>
|
||||
<div className='flex xl:max-w-[560px] w-full gap-2.5'>
|
||||
<Button
|
||||
label={t('callback.home')}
|
||||
className='w-full mt-8 bg-transparent border border-black text-black'
|
||||
onClick={() => navigate(Pages.dashboard)}
|
||||
/>
|
||||
<Button
|
||||
label={t('callback.back')}
|
||||
className='w-full mt-8'
|
||||
onClick={() => navigate(Pages.transactions)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { FC, Fragment } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../../components/Button'
|
||||
import Rate from 'rc-rate'
|
||||
import ServiceImages from './components/ServiceImages'
|
||||
import Input from '../../components/Input'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import AvatarImage from '../../assets/images/Avatar.png'
|
||||
import AvatarImage from '../../assets/images/avatar_image.png'
|
||||
import ServiceHeader from './components/ServiceHeader'
|
||||
import { useGetDetailService } from './hooks/useServiceData'
|
||||
import { useParams } from 'react-router-dom'
|
||||
@@ -13,6 +10,8 @@ import PageLoading from '../../components/PageLoading'
|
||||
import moment from 'moment-jalaali'
|
||||
import { useGetAds } from '../ads/hooks/useAdsData'
|
||||
import { AdsDisplayLocation } from '../ads/types/AdsTypes'
|
||||
import CreateReview from './components/CreateReview'
|
||||
import { ReviewItemType } from './types/ServiecTypes'
|
||||
|
||||
const DetailService: FC = () => {
|
||||
|
||||
@@ -74,8 +73,9 @@ const DetailService: FC = () => {
|
||||
<Rate
|
||||
count={5}
|
||||
style={{ fontSize: 15 }}
|
||||
value={4}
|
||||
value={getDetailService?.data?.data?.averageRating}
|
||||
disabled
|
||||
allowHalf
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,111 +92,51 @@ const DetailService: FC = () => {
|
||||
</div>
|
||||
|
||||
<div className='mt-8 flex xl:flex-row flex-col gap-8 items-start'>
|
||||
<div className='bg-white p-6 rounded-3xl xl:w-[360px] w-full'>
|
||||
<div>
|
||||
{t('service.submit_comment')}
|
||||
</div>
|
||||
<div className='text-xs mt-4'>
|
||||
{t('service.hint_comment')}
|
||||
</div>
|
||||
|
||||
<div className='mt-6 text-description text-sm'>
|
||||
<div>
|
||||
{t('service.score')}
|
||||
</div>
|
||||
<div className='mt-2 dltr flex justify-end'>
|
||||
<Rate
|
||||
count={5}
|
||||
style={{ fontSize: 22 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
label={t('service.title')}
|
||||
placeholder={t('service.enter_title_comment')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Textarea
|
||||
label={t('service.your_comment')}
|
||||
placeholder={t('service.write_your_comment')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Button
|
||||
label={t('service.submit_comment')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
getDetailService.data?.data?.purchased &&
|
||||
<CreateReview refetch={() => getDetailService.refetch()} />
|
||||
}
|
||||
<div className='flex-1 max-w-full bg-white p-6 rounded-3xl'>
|
||||
<div className='text-sm'>
|
||||
{t('service.users_comments')}
|
||||
</div>
|
||||
|
||||
<div className='mt-5 border border-border rounded-3xl p-6'>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<img src={AvatarImage} className='size-6 rounded-full object-cover' />
|
||||
<div className='text-description text-xs'>
|
||||
فروغ براتی
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
getDetailService.data?.data?.danakService?.reviews?.map((item: ReviewItemType) => {
|
||||
return (
|
||||
<div className='mt-5 border border-border rounded-3xl p-6'>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<img src={item.user.profilePic || AvatarImage} className='size-6 rounded-full object-cover' />
|
||||
<div className='text-description text-xs'>
|
||||
{item.user?.firstName + ' ' + item.user?.lastName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 flex justify-between'>
|
||||
<div className='text-sm max-w-[70%] truncate'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
||||
</div>
|
||||
<div className='text-xs text-description'>
|
||||
۳ روز پیش
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-4 flex justify-between'>
|
||||
<div className='text-sm max-w-[70%] truncate'>
|
||||
{item.title}
|
||||
</div>
|
||||
<div className='text-xs text-description'>
|
||||
{moment(item.createdAt).fromNow()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-2'>
|
||||
<Rate
|
||||
disabled
|
||||
value={5}
|
||||
count={5}
|
||||
style={{ fontSize: 14 }}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-2'>
|
||||
<Rate
|
||||
disabled
|
||||
value={item.rating}
|
||||
count={5}
|
||||
style={{ fontSize: 14 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 text-[11px] leading-6'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را ...
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-5 border border-border rounded-3xl p-6'>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<img src={AvatarImage} className='size-6 rounded-full object-cover' />
|
||||
<div className='text-description text-xs'>
|
||||
فروغ براتی
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 flex justify-between'>
|
||||
<div className='text-sm max-w-[70%] truncate'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
||||
</div>
|
||||
<div className='text-xs text-description'>
|
||||
۳ روز پیش
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-2'>
|
||||
<Rate
|
||||
disabled
|
||||
value={5}
|
||||
count={5}
|
||||
style={{ fontSize: 14 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 text-[11px] leading-6'>
|
||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را ...
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-4 text-[11px] leading-6'>
|
||||
{item.comment}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import Rate from 'rc-rate'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '../../../components/Input'
|
||||
import Textarea from '../../../components/Textarea'
|
||||
import Button from '../../../components/Button'
|
||||
import { useFormik } from 'formik'
|
||||
import { CreateReviewType } from '../types/ServiecTypes'
|
||||
import * as Yup from 'yup'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useCreateReview } from '../hooks/useServiceData'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import { toast } from 'react-toastify'
|
||||
import Error from '../../../components/Error'
|
||||
|
||||
type Props = {
|
||||
refetch: () => void
|
||||
}
|
||||
|
||||
const CreateReview: FC<Props> = ({ refetch }) => {
|
||||
|
||||
const { id } = useParams()
|
||||
const { t } = useTranslation('global')
|
||||
|
||||
const createReview = useCreateReview()
|
||||
|
||||
const formik = useFormik<CreateReviewType>({
|
||||
initialValues: {
|
||||
title: '',
|
||||
comment: '',
|
||||
rating: undefined,
|
||||
id: id ? id : ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
title: Yup.string().required(t('errors.required')),
|
||||
comment: Yup.string().required(t('errors.required')),
|
||||
rating: Yup.string().required(t('errors.required')),
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
createReview.mutate(values, {
|
||||
onSuccess: () => {
|
||||
formik.resetForm()
|
||||
refetch()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast.error(error.response?.data?.error.message[0])
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='bg-white p-6 rounded-3xl xl:w-[360px] w-full'>
|
||||
<div>
|
||||
{t('service.submit_comment')}
|
||||
</div>
|
||||
<div className='text-xs mt-4'>
|
||||
{t('service.hint_comment')}
|
||||
</div>
|
||||
|
||||
<div className='mt-6 text-description text-sm'>
|
||||
<div>
|
||||
{t('service.score')}
|
||||
</div>
|
||||
<div className='mt-2 dltr flex justify-end'>
|
||||
<Rate
|
||||
count={5}
|
||||
style={{ fontSize: 22 }}
|
||||
value={formik.values.rating}
|
||||
onChange={(value) => formik.setFieldValue('rating', value)}
|
||||
/>
|
||||
</div>
|
||||
{formik.errors.rating && formik.touched.rating && <Error errorText={formik.errors.rating} />}
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
label={t('service.title')}
|
||||
placeholder={t('service.enter_title_comment')}
|
||||
{...formik.getFieldProps('title')}
|
||||
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Textarea
|
||||
label={t('service.your_comment')}
|
||||
placeholder={t('service.write_your_comment')}
|
||||
{...formik.getFieldProps('comment')}
|
||||
error_text={formik.touched.comment && formik.errors.comment ? formik.errors.comment : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Button
|
||||
label={t('service.submit_comment')}
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={createReview.isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateReview
|
||||
@@ -82,13 +82,13 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
||||
<div className='flex gap-4 mt-5 xl:mt-0 items-center justify-between xl:justify-normal'>
|
||||
<div className='text-xs whitespace-nowrap flex gap-1 items-center'>
|
||||
<div className='flex gap-2'>
|
||||
{
|
||||
{/* {
|
||||
finalPrice !== price &&
|
||||
<div className='line-through text-xs text-description'>
|
||||
{NumberFormat(Number(price))}
|
||||
</div>
|
||||
}
|
||||
<div>{NumberFormat(Number(finalPrice))}</div>
|
||||
} */}
|
||||
<div>{NumberFormat(Number(price))}</div>
|
||||
</div>
|
||||
<span>{planText}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import * as api from "../service/ServiceService";
|
||||
import { BuyServiceType } from "../types/ServiecTypes";
|
||||
import { BuyServiceType, CreateReviewType } from "../types/ServiecTypes";
|
||||
|
||||
export const useGetSuggestedServices = () => {
|
||||
return useQuery({
|
||||
@@ -44,3 +44,9 @@ export const useGetMyServices = (search: string) => {
|
||||
queryFn: () => api.getMyServices(search),
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateReview = () => {
|
||||
return useMutation({
|
||||
mutationFn: (variables: CreateReviewType) => api.createReview(variables),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from "../../../config/axios";
|
||||
import { BuyServiceType } from "../types/ServiecTypes";
|
||||
import { BuyServiceType, CreateReviewType } from "../types/ServiecTypes";
|
||||
|
||||
export const getServiceSuggestedDanak = async () => {
|
||||
const { data } = await axios.get(`/danak-services/suggested`);
|
||||
@@ -32,3 +32,11 @@ export const getMyServices = async (search: string) => {
|
||||
const { data } = await axios.get(`/subscriptions/user?q=${search}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createReview = async (params: CreateReviewType) => {
|
||||
const { data } = await axios.post(
|
||||
`/danak-services/${params.id}/reviews`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -119,3 +119,25 @@ export type BuyServiceType = {
|
||||
description: string;
|
||||
serviceId: string;
|
||||
};
|
||||
|
||||
export type CreateReviewType = {
|
||||
id: string;
|
||||
comment: string;
|
||||
rating?: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type ReviewItemType = {
|
||||
comment: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
rating: number;
|
||||
status: "APPROVED" | "PENDING" | "REJECTED";
|
||||
title: string;
|
||||
user: {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
profilePic: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -81,10 +81,16 @@ const ChangePassword: FC = () => {
|
||||
|
||||
<div className='mt-9 xl:block hidden'>
|
||||
<Button
|
||||
label={t('setting.save_change')}
|
||||
className='w-32'
|
||||
className='w-fit px-4'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
/>
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<TickCircle size={20} color='white' />
|
||||
<div>
|
||||
{t('setting.save_change')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,10 +116,16 @@ const ChangePassword: FC = () => {
|
||||
|
||||
<div className=' xl:hidden block'>
|
||||
<Button
|
||||
label={t('setting.save_change')}
|
||||
className='w-32'
|
||||
className='w-32 hidden xl:block'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
/>
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<TickCircle size={20} color='white' />
|
||||
<div>
|
||||
{t('setting.save_change')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='justify-end xl:hidden flex'>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Input from '../../components/Input'
|
||||
import Textarea from '../../components/Textarea'
|
||||
import UploadBox from '../../components/UploadBox'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, InfoCircle, Paperclip2 } from 'iconsax-react'
|
||||
import { CloseCircle, InfoCircle, Paperclip2, Send2 } from 'iconsax-react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useAddMessageTicket, useCloseTicket, useGetMessages, useMultiUpload } from './hooks/useTicketData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
@@ -212,11 +212,17 @@ const TicketDetail: FC = () => {
|
||||
|
||||
<div className='flex justify-end mt-6'>
|
||||
<Button
|
||||
label={t('ticket.send')}
|
||||
className='max-w-[100px]'
|
||||
className='xl:max-w-[100px]'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
isLoading={addMessage.isPending || multiUpload.isPending}
|
||||
/>
|
||||
>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<Send2 size={20} color='white' />
|
||||
<div>
|
||||
{t('ticket.send')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ const CardtoCard: FC = () => {
|
||||
name='amount'
|
||||
onChange={formik.handleChange}
|
||||
error_text={formik.touched.amount && formik.errors.amount ? formik.errors.amount : ''}
|
||||
seprator
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ const Online: FC = () => {
|
||||
placeholder={t('wallet.enter_your_price')}
|
||||
onChange={(e) => setDesiredAmount(Number(e.target.value))}
|
||||
value={desiredAmount}
|
||||
seprator
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ const CardtoCard: FC = () => {
|
||||
name='amount'
|
||||
onChange={formik.handleChange}
|
||||
error_text={formik.touched.amount && formik.errors.amount ? formik.errors.amount : ''}
|
||||
seprator
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user