diff --git a/.env b/.env index 3c65636..3fbd3a5 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ VITE_TOKEN_NAME = 'admin_token' VITE_REFRESH_TOKEN_NAME = 'admin_refresh_token' -VITE_BASE_URL = 'https://danak-dsc-api.run.danakcorp.com' -# VITE_BASE_URL = 'http://192.168.0.221:3500' \ No newline at end of file +# VITE_BASE_URL = 'https://danak-dsc-api.run.danakcorp.com' +VITE_BASE_URL = 'http://192.168.0.207:3500' \ No newline at end of file diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 9446906..a3fb20d 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -27,7 +27,8 @@ export const Pages = { }, announcement: { list: "/announcement", - detail: "/announcement/", + detail: "/announcement/detail/", + create: "/announcement/create", }, criticisms: { list: "/criticisms/list", diff --git a/src/langs/fa.json b/src/langs/fa.json index 77a9777..e07980c 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -369,7 +369,18 @@ "search": "جستجو در اطلاعیه ها", "new": "جدید", "important": "مهم", - "text_announcement": "متن اطلاعیه" + "text_announcement": "متن اطلاعیه", + "add_announcement": "افزودن اطلاعیه", + "submit_announcement": "ثبت اطلاعیه", + "title": "عنوان اطلاعیه", + "text": "متن اطلاعیه", + "publish_date": "زمان انتشار", + "info_publishdata": "اگر مایلید اطلاعیه در زمان دیگری منتشر شود تاریخ انتشار مورد نظر را انتخاب کنید", + "label_importance": "لیبل “مهم” در اطلاعیه درج شود", + "select_service": "انتخاب سرویس", + "select_customer": "انتخاب مشتری", + "contact_announcement": "مخاطب این اطلاعیه", + "date_created": "تاریخ ثبت" }, "back": "بازگشت", "criticisms": { diff --git a/src/pages/annoncement/Create.tsx b/src/pages/annoncement/Create.tsx new file mode 100644 index 0000000..6d1bdce --- /dev/null +++ b/src/pages/annoncement/Create.tsx @@ -0,0 +1,174 @@ +import { FC, useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '../../components/Button' +import { InfoCircle, TickCircle } from 'iconsax-react' +import Input from '../../components/Input' +import Quill from 'quill' +import DatePickerComponent from '../../components/DatePicker' +import CheckBoxComponent from '../../components/CheckBoxComponent' +import Select from '../../components/Select' +import { useGetAllServices } from '../service/hooks/useServiceData' +import { useFormik } from 'formik' +import { AnnoncementCreateType } from './types/AnnoncementTypes' +import * as Yup from 'yup' +import { ServiceItemType } from '../service/types/ServiceTypes' +import { useCreateAnnoncement } from './hooks/useAnnoncementData' +import { ErrorType } from '../../helpers/types' +import { toast } from 'react-toastify' + +const Create: FC = () => { + + const { t } = useTranslation('global') + const editorRef = useRef(null); + const getServices = useGetAllServices('', 1, '', '', false) + const createAnnoncement = useCreateAnnoncement() + + const formik = useFormik({ + initialValues: { + title: '', + content: '', + publishAt: '', + isPublic: false, + isImportant: false, + serviceId: '', + groupIds: [] + }, + validationSchema: Yup.object({ + title: Yup.string().required(t('errors.required')), + content: Yup.string().required(t('errors.required')), + publishAt: Yup.string().required(t('errors.required')), + serviceId: Yup.string().required(t('errors.required')), + groupIds: Yup.array().required(t('errors.required')) + }), + onSubmit: (values) => { + createAnnoncement.mutate(values, { + onSuccess: () => { + formik.resetForm() + toast.success(t('success')) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + } + }) + + + useEffect(() => { + const quill = new Quill('#editor', { + theme: 'snow', + modules: { + toolbar: [ + [{ header: '1' }, { header: '2' }, { font: [] }], + [{ list: 'ordered' }, { list: 'bullet' }], + ['bold', 'italic', 'underline'], + ['link', 'image'], + [{ align: [] }], + ['clean'] + ] + } + }); + quill.on('text-change', () => { + const html = editorRef.current?.querySelector('.ql-editor')?.innerHTML || ''; + formik.setFieldValue('content', html); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
+
+ {t('announcement.add_announcement')} +
+
+ +
+
+ +
+
+
+ + +
{t('announcement.text')}
+ +
+
+
+
+
+ +
+ formik.setFieldValue('publishAt', d)} + placeholder={t('select')} + /> + +
+ +

+ {t('announcement.info_publishdata')} +

+
+ +
+ formik.setFieldValue('isImportant', e.target.checked)} + /> + +
{t('announcement.label_importance')}
+ +
+ +
+ null} + /> +
+
+
+
+ ) +} + +export default Create \ No newline at end of file diff --git a/src/pages/annoncement/Detail.tsx b/src/pages/annoncement/Detail.tsx index 60ea2e4..c8bcbb2 100644 --- a/src/pages/annoncement/Detail.tsx +++ b/src/pages/annoncement/Detail.tsx @@ -1,13 +1,17 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import StatusWithText from '../../components/StatusWithText' -import { useNavigate } from 'react-router-dom' +import { useNavigate, useParams } from 'react-router-dom' import { ArrowLeft2 } from 'iconsax-react' +import { useGetAnnoncementDetail } from './hooks/useAnnoncementData' +import PageLoading from '../../components/PageLoading' const AnnouncementDetail: FC = () => { + const { id } = useParams() const { t } = useTranslation('global') const navigate = useNavigate() + const getDetail = useGetAnnoncementDetail(id ? id : '') return (
@@ -21,21 +25,26 @@ const AnnouncementDetail: FC = () => {
-
-
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ -
- -
+ { + getDetail.isPending ? + + : +
+
+
+ لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ +
+ +
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. -
-
+
+ لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. +
+
+ } ) } diff --git a/src/pages/annoncement/List.tsx b/src/pages/annoncement/List.tsx index 98e2744..70c204d 100644 --- a/src/pages/annoncement/List.tsx +++ b/src/pages/annoncement/List.tsx @@ -1,20 +1,32 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '../../components/Input' -import Select from '../../components/Select' import StatusWithText from '../../components/StatusWithText' import { clx } from '../../helpers/utils' import { useNavigate } from 'react-router-dom' import { Pages } from '../../config/Pages' +import { useGetAnnoncements } from './hooks/useAnnoncementData' +import PageLoading from '../../components/PageLoading' +import { AnnoncementItemType } from './types/AnnoncementTypes' +import moment from 'moment-jalaali' +import Pagination from '../../components/Pagination' +import DatePickerComponent from '../../components/DatePicker' const AnnouncementtList: FC = () => { const { t } = useTranslation('global') const navigate = useNavigate() + const [openItem, setOpenItem] = useState() + const [page, setPage] = useState(1) + const [since, setSince] = useState('') + const [search, setSearch] = useState('') + const getAnnoncements = useGetAnnoncements(page, search, since) - const handleChange = () => { + const handleChange = (item: AnnoncementItemType) => { if (window.innerWidth < 1280) { - navigate(Pages.announcement.detail + '1') + navigate(Pages.announcement.detail + item.id) + } else { + setOpenItem(item) } } @@ -24,112 +36,112 @@ const AnnouncementtList: FC = () => { {t('announcement.announcements')} -
-
-
-
- -
- -
- setSearch(value)} + />
-
- - + setSince(d)} + placeholder={t('announcement.date_created')} />
-
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ -
-
- ۱۴۰۳/۰۹/۲۴ -
-
-
-
-
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ -
+
+ { + getAnnoncements.data?.data?.announcements?.map((item: AnnoncementItemType) => { + return ( +
handleChange(item)} className={clx( + 'p-4 rounded-2xl cursor-pointer', + openItem?.id === item.id && 'bg-[#F5F6FA]' + )}> +
+
+ {item.title} +
-
- - + { + item.isImportant && + + } + + +
+
+ +
+
+
+
+ {moment(item.publishAt, 'jYYYY-jMM-jDD').format('jYYYY/jMM/jDD')} +
+
+
+ ) + }) + } +
+
+
-
-
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ + { + openItem && +
+
+
+ متن اطلاعیه +
+
+ { + openItem.isImportant && + + } + +
-
- ۱۴۰۳/۰۹/۲۴ + +
+ {openItem.title} +
+ +
-
+ }
-
- -
-
-
- متن اطلاعیه -
- -
- -
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ -
- -
- لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. -
-
-
+ }
) diff --git a/src/pages/annoncement/hooks/useAnnoncementData.ts b/src/pages/annoncement/hooks/useAnnoncementData.ts new file mode 100644 index 0000000..1871791 --- /dev/null +++ b/src/pages/annoncement/hooks/useAnnoncementData.ts @@ -0,0 +1,28 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; +import { CreateAnnouncementType } from "../../types/AnnouncementTypes"; +import * as api from "../service/AnnoncementService"; + +export const useCreateAnnoncement = () => { + return useMutation({ + mutationFn: (variables: CreateAnnouncementType) => + api.createAnnoncement(variables), + }); +}; + +export const useGetAnnoncements = ( + page: number, + search: string, + since: string +) => { + return useQuery({ + queryKey: ["announcements", page, search, since], + queryFn: () => api.getAnnoncements(page, search, since), + }); +}; + +export const useGetAnnoncementDetail = (id: string) => { + return useQuery({ + queryKey: ["announcement", id], + queryFn: () => api.getAnnoncementDetail(id), + }); +}; diff --git a/src/pages/annoncement/service/AnnoncementService.ts b/src/pages/annoncement/service/AnnoncementService.ts new file mode 100644 index 0000000..3224682 --- /dev/null +++ b/src/pages/annoncement/service/AnnoncementService.ts @@ -0,0 +1,25 @@ +import axios from "../../../config/axios"; +import { CreateAnnouncementType } from "../../types/AnnouncementTypes"; + +export const createAnnoncement = async (params: CreateAnnouncementType) => { + const { data } = await axios.post(`/announcements`, params); + return data; +}; + +export const getAnnoncements = async ( + page: number, + search: string, + since: string +) => { + let query = `?page=${page}`; + if (search) query += `&q=${search}`; + if (since) query += `&since=${since}`; + + const { data } = await axios.get(`/announcements${query}`); + return data; +}; + +export const getAnnoncementDetail = async (id: string) => { + const { data } = await axios.get(`/announcements/${id}`); + return data; +}; diff --git a/src/pages/annoncement/types/AnnoncementTypes.ts b/src/pages/annoncement/types/AnnoncementTypes.ts new file mode 100644 index 0000000..aff954d --- /dev/null +++ b/src/pages/annoncement/types/AnnoncementTypes.ts @@ -0,0 +1,20 @@ +export type AnnoncementCreateType = { + title: string; + content: string; + publishAt: string; + isPublic: boolean; + isImportant: boolean; + serviceId: string; + groupIds: string[]; +}; + +export type AnnoncementItemType = { + id: string; + title: string; + content: string; + publishAt: string; + isPublic: boolean; + isImportant: boolean; + serviceId: string; + groupIds: string[]; +}; diff --git a/src/pages/types/AnnouncementTypes.ts b/src/pages/types/AnnouncementTypes.ts new file mode 100644 index 0000000..442539b --- /dev/null +++ b/src/pages/types/AnnouncementTypes.ts @@ -0,0 +1,9 @@ +export type CreateAnnouncementType = { + title: string; + content: string; + publishAt: string; + isPublic: boolean; + isImportant: boolean; + serviceId: string; + groupIds: string[]; +}; diff --git a/src/router/Main.tsx b/src/router/Main.tsx index b81a20e..d11b2d1 100644 --- a/src/router/Main.tsx +++ b/src/router/Main.tsx @@ -39,6 +39,7 @@ import TicketCategory from '../pages/ticket/Category' import MessagesList from '../pages/messages/List' import CriticismsList from '../pages/criticisms/List' import CriticismsDetail from '../pages/criticisms/Detail' +import Create from '../pages/annoncement/Create' const MainRouter: FC = () => { @@ -81,6 +82,7 @@ const MainRouter: FC = () => { } /> } /> } /> + } /> } /> } /> } />