announcement list and create announcement and ...
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://danak-dsc-api.run.danakcorp.com'
|
# VITE_BASE_URL = 'https://danak-dsc-api.run.danakcorp.com'
|
||||||
# VITE_BASE_URL = 'http://192.168.0.221:3500'
|
VITE_BASE_URL = 'http://192.168.0.207:3500'
|
||||||
+2
-1
@@ -27,7 +27,8 @@ export const Pages = {
|
|||||||
},
|
},
|
||||||
announcement: {
|
announcement: {
|
||||||
list: "/announcement",
|
list: "/announcement",
|
||||||
detail: "/announcement/",
|
detail: "/announcement/detail/",
|
||||||
|
create: "/announcement/create",
|
||||||
},
|
},
|
||||||
criticisms: {
|
criticisms: {
|
||||||
list: "/criticisms/list",
|
list: "/criticisms/list",
|
||||||
|
|||||||
+12
-1
@@ -369,7 +369,18 @@
|
|||||||
"search": "جستجو در اطلاعیه ها",
|
"search": "جستجو در اطلاعیه ها",
|
||||||
"new": "جدید",
|
"new": "جدید",
|
||||||
"important": "مهم",
|
"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": "بازگشت",
|
"back": "بازگشت",
|
||||||
"criticisms": {
|
"criticisms": {
|
||||||
|
|||||||
@@ -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<HTMLDivElement | null>(null);
|
||||||
|
const getServices = useGetAllServices('', 1, '', '', false)
|
||||||
|
const createAnnoncement = useCreateAnnoncement()
|
||||||
|
|
||||||
|
const formik = useFormik<AnnoncementCreateType>({
|
||||||
|
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 (
|
||||||
|
<div className='w-full mt-4'>
|
||||||
|
<div className='flex w-full justify-between items-center'>
|
||||||
|
<div>
|
||||||
|
{t('announcement.add_announcement')}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
className='px-5'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={createAnnoncement.isPending}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<TickCircle
|
||||||
|
className='size-5'
|
||||||
|
color='#fff'
|
||||||
|
/>
|
||||||
|
<div>{t('announcement.submit_announcement')}</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex gap-6'>
|
||||||
|
<div className='flex-1'>
|
||||||
|
<div className='flex-1 mt-10 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||||
|
<Input
|
||||||
|
label={t('announcement.title')}
|
||||||
|
name='title'
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='mt-7 text-sm'>{t('announcement.text')}</div>
|
||||||
|
|
||||||
|
<div className='mt-1'>
|
||||||
|
<div ref={editorRef} id='editor' style={{ minHeight: '120px' }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='bg-white mt-10 w-sidebar text-xs hidden 2xl:block h-fit px-5 py-7 rounded-3xl'>
|
||||||
|
<DatePickerComponent
|
||||||
|
label={t('announcement.publish_date')}
|
||||||
|
onChange={(d) => formik.setFieldValue('publishAt', d)}
|
||||||
|
placeholder={t('select')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='flex gap-1 mt-3'>
|
||||||
|
<InfoCircle
|
||||||
|
color='#8C90A3'
|
||||||
|
className='size-4 min-w-4'
|
||||||
|
/>
|
||||||
|
<p className='text-[10px] text-description'>
|
||||||
|
{t('announcement.info_publishdata')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex items-center mt-5'>
|
||||||
|
<CheckBoxComponent
|
||||||
|
checked={formik.values.isImportant}
|
||||||
|
onChange={(e) => formik.setFieldValue('isImportant', e.target.checked)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className='text-xs'>{t('announcement.label_importance')}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-5'>
|
||||||
|
<Select
|
||||||
|
label={t('announcement.contact_announcement')}
|
||||||
|
placeholder={t('announcement.select_service')}
|
||||||
|
items={getServices?.data?.data?.services?.map((item: ServiceItemType) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id
|
||||||
|
})) || []}
|
||||||
|
name='serviceId'
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='mt-5'>
|
||||||
|
<Select
|
||||||
|
label={t('announcement.select_customer')}
|
||||||
|
items={[{
|
||||||
|
label: 'Service 1',
|
||||||
|
value: '1'
|
||||||
|
}]}
|
||||||
|
onChange={() => null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Create
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import StatusWithText from '../../components/StatusWithText'
|
import StatusWithText from '../../components/StatusWithText'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import { ArrowLeft2 } from 'iconsax-react'
|
import { ArrowLeft2 } from 'iconsax-react'
|
||||||
|
import { useGetAnnoncementDetail } from './hooks/useAnnoncementData'
|
||||||
|
import PageLoading from '../../components/PageLoading'
|
||||||
|
|
||||||
const AnnouncementDetail: FC = () => {
|
const AnnouncementDetail: FC = () => {
|
||||||
|
|
||||||
|
const { id } = useParams()
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const getDetail = useGetAnnoncementDetail(id ? id : '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
@@ -21,6 +25,10 @@ const AnnouncementDetail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
getDetail.isPending ?
|
||||||
|
<PageLoading />
|
||||||
|
:
|
||||||
<div className='flex-1 mt-4 bg-white py-6 px-4 rounded-2xl'>
|
<div className='flex-1 mt-4 bg-white py-6 px-4 rounded-2xl'>
|
||||||
<div className='flex justify-between items-start'>
|
<div className='flex justify-between items-start'>
|
||||||
<div className='text-sm max-w-[70%] leading-67'>
|
<div className='text-sm max-w-[70%] leading-67'>
|
||||||
@@ -36,6 +44,7 @@ const AnnouncementDetail: FC = () => {
|
|||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد.
|
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
import { FC } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Input from '../../components/Input'
|
import Input from '../../components/Input'
|
||||||
import Select from '../../components/Select'
|
|
||||||
import StatusWithText from '../../components/StatusWithText'
|
import StatusWithText from '../../components/StatusWithText'
|
||||||
import { clx } from '../../helpers/utils'
|
import { clx } from '../../helpers/utils'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Pages } from '../../config/Pages'
|
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 AnnouncementtList: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const [openItem, setOpenItem] = useState<AnnoncementItemType>()
|
||||||
|
const [page, setPage] = useState<number>(1)
|
||||||
|
const [since, setSince] = useState<string>('')
|
||||||
|
const [search, setSearch] = useState<string>('')
|
||||||
|
const getAnnoncements = useGetAnnoncements(page, search, since)
|
||||||
|
|
||||||
const handleChange = () => {
|
const handleChange = (item: AnnoncementItemType) => {
|
||||||
if (window.innerWidth < 1280) {
|
if (window.innerWidth < 1280) {
|
||||||
navigate(Pages.announcement.detail + '1')
|
navigate(Pages.announcement.detail + item.id)
|
||||||
|
} else {
|
||||||
|
setOpenItem(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +36,10 @@ const AnnouncementtList: FC = () => {
|
|||||||
{t('announcement.announcements')}
|
{t('announcement.announcements')}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
getAnnoncements.isPending ?
|
||||||
|
<PageLoading />
|
||||||
|
:
|
||||||
<div className='mt-10 flex gap-8'>
|
<div className='mt-10 flex gap-8'>
|
||||||
<div className='bg-white xl:w-[400px] w-full rounded-2xl p-6'>
|
<div className='bg-white xl:w-[400px] w-full rounded-2xl p-6'>
|
||||||
<div className='flex gap-3 items-center'>
|
<div className='flex gap-3 items-center'>
|
||||||
@@ -32,36 +48,40 @@ const AnnouncementtList: FC = () => {
|
|||||||
variant='search'
|
variant='search'
|
||||||
placeholder={t('announcement.search')}
|
placeholder={t('announcement.search')}
|
||||||
className='-mt-1'
|
className='-mt-1'
|
||||||
|
onChangeSearchFinal={(value) => setSearch(value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Select
|
<DatePickerComponent
|
||||||
items={[
|
onChange={(d) => setSince(d)}
|
||||||
{ value: '1', label: 'همه' },
|
placeholder={t('announcement.date_created')}
|
||||||
{ value: '2', label: 'فعال' },
|
|
||||||
{ value: '3', label: 'غیر فعال' },
|
|
||||||
]}
|
|
||||||
className='bg-[#EEF0F7] w-[68px]'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
<div onClick={handleChange} className={clx(
|
{
|
||||||
'p-4 rounded-2xl',
|
getAnnoncements.data?.data?.announcements?.map((item: AnnoncementItemType) => {
|
||||||
'bg-[#F8F9FB]'
|
return (
|
||||||
|
<div key={item.id} onClick={() => handleChange(item)} className={clx(
|
||||||
|
'p-4 rounded-2xl cursor-pointer',
|
||||||
|
openItem?.id === item.id && 'bg-[#F5F6FA]'
|
||||||
)}>
|
)}>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className='leading-6 text-xs xl:text-sm max-w-[70%]'>
|
<div className='leading-6 text-xs xl:text-sm max-w-[70%]'>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
{item.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
|
{
|
||||||
|
item.isImportant &&
|
||||||
<StatusWithText
|
<StatusWithText
|
||||||
variant='error'
|
variant='error'
|
||||||
text={t('announcement.important')}
|
text={t('announcement.important')}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
<StatusWithText
|
<StatusWithText
|
||||||
variant='success'
|
variant='success'
|
||||||
text={t('announcement.new')}
|
text={t('announcement.new')}
|
||||||
@@ -70,66 +90,58 @@ const AnnouncementtList: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
<div className='m-2 flex justify-between items-center text-[11px]'>
|
||||||
<div className='max-w-[70%] truncate '>
|
<div dangerouslySetInnerHTML={{ __html: item.content }} className='max-w-[70%] truncate '>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
|
||||||
</div>
|
</div>
|
||||||
<div className='text-description'>
|
<div className='text-description'>
|
||||||
۱۴۰۳/۰۹/۲۴
|
{moment(item.publishAt, 'jYYYY-jMM-jDD').format('jYYYY/jMM/jDD')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={handleChange} className={clx(
|
)
|
||||||
'p-4 rounded-2xl',
|
})
|
||||||
)}>
|
}
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-end'>
|
||||||
<div className='leading-6 text-xs xl:text-sm max-w-[70%]'>
|
<Pagination
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
currentPage={page}
|
||||||
</div>
|
onPageChange={setPage}
|
||||||
|
totalPages={getAnnoncements.data?.data?.pager?.totalPages}
|
||||||
<div className='flex gap-1'>
|
|
||||||
<StatusWithText
|
|
||||||
variant='error'
|
|
||||||
text={t('announcement.important')}
|
|
||||||
/>
|
|
||||||
<StatusWithText
|
|
||||||
variant='success'
|
|
||||||
text={t('announcement.new')}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
|
||||||
<div className='max-w-[70%] truncate '>
|
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
|
||||||
</div>
|
|
||||||
<div className='text-description'>
|
|
||||||
۱۴۰۳/۰۹/۲۴
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
openItem &&
|
||||||
<div className='flex-1 hidden xl:block bg-white py-7 px-5 rounded-2xl'>
|
<div className='flex-1 hidden xl:block bg-white py-7 px-5 rounded-2xl'>
|
||||||
<div className='flex justify-between items-center'>
|
<div className='flex justify-between items-center'>
|
||||||
<div className='text-lg'>
|
<div className='text-lg'>
|
||||||
متن اطلاعیه
|
متن اطلاعیه
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
{
|
||||||
|
openItem.isImportant &&
|
||||||
|
<StatusWithText
|
||||||
|
variant='error'
|
||||||
|
text={t('announcement.important')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
<StatusWithText
|
<StatusWithText
|
||||||
variant='success'
|
variant='success'
|
||||||
text={t('announcement.new')}
|
text={t('announcement.new')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='mt-8'>
|
<div className='mt-8'>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ
|
{openItem.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-5 leading-7 text-xs font-light'>
|
<div dangerouslySetInnerHTML={{ __html: openItem.content }} className='mt-5 leading-7 text-xs font-light'>
|
||||||
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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[];
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export type CreateAnnouncementType = {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
publishAt: string;
|
||||||
|
isPublic: boolean;
|
||||||
|
isImportant: boolean;
|
||||||
|
serviceId: string;
|
||||||
|
groupIds: string[];
|
||||||
|
};
|
||||||
@@ -39,6 +39,7 @@ import TicketCategory from '../pages/ticket/Category'
|
|||||||
import MessagesList from '../pages/messages/List'
|
import MessagesList from '../pages/messages/List'
|
||||||
import CriticismsList from '../pages/criticisms/List'
|
import CriticismsList from '../pages/criticisms/List'
|
||||||
import CriticismsDetail from '../pages/criticisms/Detail'
|
import CriticismsDetail from '../pages/criticisms/Detail'
|
||||||
|
import Create from '../pages/annoncement/Create'
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.receipts.create} element={<CreateReceipt />} />
|
<Route path={Pages.receipts.create} element={<CreateReceipt />} />
|
||||||
<Route path={Pages.announcement.list} element={<AnnouncementtList />} />
|
<Route path={Pages.announcement.list} element={<AnnouncementtList />} />
|
||||||
<Route path={Pages.announcement.detail + ':id'} element={<AnnouncementDetail />} />
|
<Route path={Pages.announcement.detail + ':id'} element={<AnnouncementDetail />} />
|
||||||
|
<Route path={Pages.announcement.create} element={<Create />} />
|
||||||
<Route path={Pages.criticisms.list} element={<CriticismsList />} />
|
<Route path={Pages.criticisms.list} element={<CriticismsList />} />
|
||||||
<Route path={Pages.criticisms.detail + ':id'} element={<CriticismsDetail />} />
|
<Route path={Pages.criticisms.detail + ':id'} element={<CriticismsDetail />} />
|
||||||
<Route path={Pages.learning} element={<LearningList />} />
|
<Route path={Pages.learning} element={<LearningList />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user