diff --git a/src/langs/fa.json b/src/langs/fa.json index 3ab1a12..d4d1778 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -264,7 +264,14 @@ "status": "وضعیت", "enter_your_title": "عنوان تبلیغ را وارد کنید", "link": "لینک", - "enter_your_link": "لینک تبلیغ را وارد کنید" + "enter_your_link": "لینک تبلیغ را وارد کنید", + "HOMEPAGE_TOP": "بالای صفحه اصلی", + "SINGLE_SERVICE_PAGE": "صفحه سرویس تکی", + "MY_SERVICES_PAGE": "صفحه سرویس های من", + "OTHER_SERVICES_TOP": "بالای سایر سرویس ها", + "OTHER_SERVICES_LEFT": "سمت چپ سایر سرویس ها", + "SERVICE": "سرویس", + "file_error": "فایل مورد نظر باید انتخاب شود" }, "discount": { "list": "لیست تخفیفات", diff --git a/src/pages/ads/AdsList.tsx b/src/pages/ads/AdsList.tsx index b7a34ba..0a29f84 100644 --- a/src/pages/ads/AdsList.tsx +++ b/src/pages/ads/AdsList.tsx @@ -1,16 +1,28 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '../../components/Button' import { Add, Eye } from 'iconsax-react' import Td from '../../components/Td' import { Link } from 'react-router-dom' import { Pages } from '../../config/Pages' -import SwitchComponent from '../../components/Switch' import DatePickerComponent from '../../components/DatePicker' import Select from '../../components/Select' import Input from '../../components/Input' +import { useGetAdsList } from './hooks/useAdsData' +import { AdsItemType } from './types/AdsTypes' +import moment from 'moment-jalaali' +import ToggleStatusAds from './components/ToggleStatus' +import PageLoading from '../../components/PageLoading' +import Pagination from '../../components/Pagination' const AddList: FC = () => { + const { t } = useTranslation('global') + const [search, setSearch] = useState('') + const [isActive, setIsActive] = useState(false) + const [since, setSince] = useState('') + const [page, setPage] = useState(1) + const getAds = useGetAdsList(page, search, isActive, since) + return (
@@ -37,9 +49,8 @@ const AddList: FC = () => {
{ }} + onChange={(date) => setSince(moment(date, 'jYYYY-jMM-jDD').format('YYYY-MM-DD'))} placeholder='' - defaulValue='1400-01-01' />
@@ -48,10 +59,15 @@ const AddList: FC = () => { items={[ { label: 'فعال', - value: 'active' - } + value: 'true' + }, + { + label: 'غیر فعال', + value: 'false' + }, ]} className='bg-white border' + onChange={(e) => setIsActive(e.target.value === 'true' ? true : false)} />
@@ -61,65 +77,67 @@ const AddList: FC = () => { variant='search' placeholder={t('ads.search')} className='bg-white w-full' + onChangeSearchFinal={(value) => setSearch(value)} />
-
- - - - - - - - - - - - - - - -
- - - - - - -
- - - - - - { }} - /> - - - - -
- - - - - - { }} - /> - - - - -
-
+ { + getAds.isPending ? +
+ +
+ : +
+ + + + + + + { + getAds.data?.data?.ads?.map((item: AdsItemType, index: number) => { + return ( + + + + + ) + }) + } + +
+ + + + + + +
+ + + + + + + + + + +
+
+ } + + setPage(page)} + totalPages={getAds.data?.data?.pager?.totalPages} + /> ) } diff --git a/src/pages/ads/CreateAds.tsx b/src/pages/ads/CreateAds.tsx index bc5b980..5908803 100644 --- a/src/pages/ads/CreateAds.tsx +++ b/src/pages/ads/CreateAds.tsx @@ -1,13 +1,78 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '../../components/Input' import Button from '../../components/Button' import SwitchComponent from '../../components/Switch' -import { TickCircle, TickSquare, Link2 } from 'iconsax-react' +import { TickCircle, Link2 } from 'iconsax-react' import DatePickerComponent from '../../components/DatePicker' -import ImageUploader from './components/ImageUploader' +import { useFormik } from 'formik' +import { AdsDisplayLocation, CreateAdsType } from './types/AdsTypes' +import * as Yup from 'yup' +import UploadBoxDraggble from '../../components/UploadBoxDraggble' +import CheckBoxComponent from '../../components/CheckBoxComponent' +import { useSingleUpload } from '../service/hooks/useServiceData' +import { useCreateAds } from './hooks/useAdsData' +import { ErrorType } from '../../helpers/types' +import { toast } from 'react-toastify' +import { clx } from '../../helpers/utils' +import { useNavigate } from 'react-router-dom' +import { Pages } from '../../config/Pages' +import moment from 'moment-jalaali' + const CreateAd: FC = () => { + + const navigate = useNavigate() const { t } = useTranslation('global') + const [file, setFile] = useState() + const singleUpload = useSingleUpload() + const createAds = useCreateAds() + + const formik = useFormik({ + initialValues: { + displayLocation: '', + endDate: '', + imageUrl: '', + isActive: true, + link: '', + serviceId: undefined, + startDate: '', + title: '' + }, + validationSchema: Yup.object().shape({ + title: Yup.string().required(t('errors.required')), + displayLocation: Yup.string().required(t('errors.required')), + endDate: Yup.string().required(t('errors.required')), + link: Yup.string().required(t('errors.required')), + startDate: Yup.string().required(t('errors.required')) + }), + onSubmit: async (values) => { + if (file) { + const formData = new FormData() + formData.append('file', file) + await singleUpload.mutateAsync(formData, { + onSuccess: (data) => { + values.imageUrl = data?.data?.url + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + + createAds.mutate(values, { + onSuccess: () => { + toast.success(t('success')) + navigate(Pages.ads.list) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + } else { + toast.error(t('ads.file_error')) + } + } + }) + return (
@@ -16,6 +81,8 @@ const CreateAd: FC = () => {
@@ -65,39 +139,88 @@ const CreateAd: FC = () => { {t('ads.deactive')}
{ }} + active={formik.values.isActive} + onChange={(value) => formik.setFieldValue('isActive', value)} />

{t('ads.location')}

+
-
+
- + formik.setFieldValue('displayLocation', AdsDisplayLocation.HOMEPAGE_TOP)} + />
- لورم ایپسوم + {t(`ads.${AdsDisplayLocation.HOMEPAGE_TOP}`)}
-
+
- + formik.setFieldValue('displayLocation', AdsDisplayLocation.SINGLE_SERVICE_PAGE)} + />
- لورم ایپسوم + {t(`ads.${AdsDisplayLocation.SINGLE_SERVICE_PAGE}`)}
-
+
- + formik.setFieldValue('displayLocation', AdsDisplayLocation.MY_SERVICES_PAGE)} + />
- لورم ایپسوم + {t(`ads.${AdsDisplayLocation.MY_SERVICES_PAGE}`)}
+
+
+ formik.setFieldValue('displayLocation', AdsDisplayLocation.OTHER_SERVICES_TOP)} + /> +
+
+ {t(`ads.${AdsDisplayLocation.OTHER_SERVICES_TOP}`)} +
+
+
+
+ formik.setFieldValue('displayLocation', AdsDisplayLocation.OTHER_SERVICES_LEFT)} + /> +
+
+ {t(`ads.${AdsDisplayLocation.OTHER_SERVICES_LEFT}`)} +
+
+
+
+ formik.setFieldValue('displayLocation', AdsDisplayLocation.SERVICE)} + /> +
+
+ {t(`ads.${AdsDisplayLocation.SERVICE}`)} +
+
+ +
+
+ setFile(value[0])} + />
-
diff --git a/src/pages/ads/components/ToggleStatus.tsx b/src/pages/ads/components/ToggleStatus.tsx new file mode 100644 index 0000000..1debde1 --- /dev/null +++ b/src/pages/ads/components/ToggleStatus.tsx @@ -0,0 +1,28 @@ +import { FC, useState } from 'react' +import SwitchComponent from '../../../components/Switch' +import { useToggleStatusAds } from '../hooks/useAdsData' + +type Props = { + defaultActive: boolean, + id: string +} + +const ToggleStatusAds: FC = (props: Props) => { + + const [isActive, setIsActive] = useState(props.defaultActive) + const toggleStatus = useToggleStatusAds() + + const handleChange = (value: boolean) => { + setIsActive(value) + toggleStatus.mutate(props.id) + } + + return ( + + ) +} + +export default ToggleStatusAds \ No newline at end of file diff --git a/src/pages/ads/hooks/useAdsData.ts b/src/pages/ads/hooks/useAdsData.ts new file mode 100644 index 0000000..3b2d8e5 --- /dev/null +++ b/src/pages/ads/hooks/useAdsData.ts @@ -0,0 +1,27 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import * as api from "../service/AdsService"; +import { CreateAdsType } from "../types/AdsTypes"; + +export const useGetAdsList = ( + page: number, + search: string, + isActive: boolean, + since: string +) => { + return useQuery({ + queryKey: ["ads", page, search, isActive, since], + queryFn: () => api.getAdsList({ page, search, isActive, since }), + }); +}; + +export const useCreateAds = () => { + return useMutation({ + mutationFn: (variables: CreateAdsType) => api.createAds(variables), + }); +}; + +export const useToggleStatusAds = () => { + return useMutation({ + mutationFn: (variables: string) => api.toggleStatusAds(variables), + }); +}; diff --git a/src/pages/ads/service/AdsService.ts b/src/pages/ads/service/AdsService.ts new file mode 100644 index 0000000..576d329 --- /dev/null +++ b/src/pages/ads/service/AdsService.ts @@ -0,0 +1,33 @@ +import axios from "../../../config/axios"; +import { CreateAdsType } from "../types/AdsTypes"; + +interface GetAdsListParams { + page: number; + search?: string; + isActive?: boolean; + since?: string; +} + +export const getAdsList = async ({ + page, + search, + isActive, + since, +}: GetAdsListParams) => { + let query = `?page=${page}&q=${search}&isActive=${isActive}`; + if (since) { + query += `&since=${since}`; + } + const { data } = await axios.get(`/advertise/list${query}`); + return data; +}; + +export const createAds = async (params: CreateAdsType) => { + const { data } = await axios.post(`/advertise`, params); + return data; +}; + +export const toggleStatusAds = async (id: string) => { + const { data } = await axios.patch(`/advertise/toggle-status/${id}`); + return data; +}; diff --git a/src/pages/ads/types/AdsTypes.ts b/src/pages/ads/types/AdsTypes.ts new file mode 100644 index 0000000..52448f2 --- /dev/null +++ b/src/pages/ads/types/AdsTypes.ts @@ -0,0 +1,33 @@ +export type CreateAdsType = { + title: string; + displayLocation: string; + startDate: string; + endDate: string; + isActive: boolean; + imageUrl: string; + link: string; + serviceId?: string; +}; + +export enum AdsDisplayLocation { + HOMEPAGE_TOP = "HOMEPAGE_TOP", + SINGLE_SERVICE_PAGE = "SINGLE_SERVICE_PAGE", + MY_SERVICES_PAGE = "MY_SERVICES_PAGE", + OTHER_SERVICES_TOP = "OTHER_SERVICES_TOP", + OTHER_SERVICES_LEFT = "OTHER_SERVICES_LEFT", + SERVICE = "SERVICE", +} + +export type AdsItemType = { + id: string; + title: string; + displayLocation: AdsDisplayLocation; + startDate: string; + endDate: string; + isActive: boolean; + imageUrl: string; + link: string; + service?: string | null; + createdAt: string; + updatedAt: string; +};