announcement
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { type FC } from 'react'
|
||||
import StatusWithText from '../../components/StatusWithText'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { ArrowLeft2 } from 'iconsax-react'
|
||||
import { useGetAnnoncementDetail } from './hooks/useAnnoncementData'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import { t } from '@/locale'
|
||||
|
||||
const AnnouncementDetail: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const getAnnoncementDetail = useGetAnnoncementDetail(id ? id : '')
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='flex justify-between'>
|
||||
<div>
|
||||
{t('announcement.text_announcement')}
|
||||
</div>
|
||||
<div onClick={() => navigate(-1)} className='text-xs flex gap-0.5 items-center font-extralight'>
|
||||
{t('back')}
|
||||
<ArrowLeft2 size={12} color='black' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
getAnnoncementDetail.isPending ?
|
||||
<PageLoading />
|
||||
:
|
||||
<div className='flex-1 mt-4 bg-white py-6 px-4 rounded-2xl'>
|
||||
<div className='flex justify-between items-start'>
|
||||
<div className='text-sm max-w-[70%] leading-67'>
|
||||
{getAnnoncementDetail.data?.data?.announcement?.title}
|
||||
</div>
|
||||
{
|
||||
getAnnoncementDetail.data?.data?.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div dangerouslySetInnerHTML={{ __html: getAnnoncementDetail.data?.data?.announcement?.content || '' }} className='mt-5 leading-7 text-xs font-light'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementDetail
|
||||
@@ -0,0 +1,146 @@
|
||||
import { type FC, Fragment, useState } from 'react'
|
||||
import Input from '@/components/Input'
|
||||
import StatusWithText from '@/components/StatusWithText'
|
||||
import { clx } from '@/helpers/utils'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Paths } from '@/config/Paths'
|
||||
import { useGetAnnoncement, useGetAnnoncementDetail } from './hooks/useAnnoncementData'
|
||||
import type { AnnoncementItemType } from './types/AnnoncementTypes'
|
||||
import moment from 'moment-jalaali'
|
||||
import AnouncementSkeleton from './components/AnouncementSkeleton'
|
||||
import { t } from '@/locale'
|
||||
|
||||
const AnnouncementtList: FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const [itemSelected, setItemSelected] = useState<AnnoncementItemType>()
|
||||
const getAnnoncements = useGetAnnoncement(search)
|
||||
useGetAnnoncementDetail(itemSelected ? itemSelected.announcement.id : '')
|
||||
|
||||
|
||||
const handleChange = (item: AnnoncementItemType) => {
|
||||
if (window.innerWidth < 1280) {
|
||||
navigate(Paths.announcement.detail + item.announcement.id)
|
||||
} else {
|
||||
setItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4 text-sm'>
|
||||
<div className='text-base'>
|
||||
{t('announcement.announcements')}
|
||||
</div>
|
||||
|
||||
<div className='mt-10 flex gap-8'>
|
||||
<div className='bg-white xl:w-[400px] w-full rounded-2xl p-6'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<div className='flex-1'>
|
||||
<Input
|
||||
variant='search'
|
||||
placeholder={t('announcement.search')}
|
||||
className='-mt-1'
|
||||
onChangeSearchFinal={(value) => setSearch(value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* <div>
|
||||
<Select
|
||||
items={[
|
||||
{ value: '1', label: 'همه' },
|
||||
{ value: '2', label: 'فعال' },
|
||||
{ value: '3', label: 'غیر فعال' },
|
||||
]}
|
||||
className='bg-[#EEF0F7] w-[68px]'
|
||||
/>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<Fragment>
|
||||
<div className='mt-8'>
|
||||
{
|
||||
getAnnoncements.isPending ?
|
||||
<Fragment>
|
||||
<AnouncementSkeleton />
|
||||
<AnouncementSkeleton />
|
||||
<AnouncementSkeleton />
|
||||
</Fragment>
|
||||
:
|
||||
getAnnoncements.data?.data?.announcements?.map((item: AnnoncementItemType) => {
|
||||
return (
|
||||
<div onClick={() => handleChange(item)} className={clx(
|
||||
'p-4 border-b border-border cursor-pointer',
|
||||
item.id === itemSelected?.id && 'bg-[#F8F9FB]'
|
||||
)}>
|
||||
<div className='flex justify-between'>
|
||||
<div className='leading-6 text-xs xl:text-[12.5px] max-w-[70%]'>
|
||||
{item.announcement.title}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-1'>
|
||||
{
|
||||
item.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
{
|
||||
!item.isRead &&
|
||||
<StatusWithText
|
||||
variant='success'
|
||||
text={t('announcement.new')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
||||
<div dangerouslySetInnerHTML={{ __html: item.announcement.content || '' }} className='max-w-[70%] line-clamp-1 truncate text-description'>
|
||||
</div>
|
||||
<div className='text-description'>
|
||||
{moment(item.announcement.publishAt).format('jYYYY/jMM/jDD')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
|
||||
{
|
||||
itemSelected &&
|
||||
<div className='flex-1 hidden xl:block bg-white py-7 px-5 rounded-2xl'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='text-lg'>
|
||||
متن اطلاعیه
|
||||
</div>
|
||||
{
|
||||
itemSelected.announcement.isImportant &&
|
||||
<StatusWithText
|
||||
variant='error'
|
||||
text={t('announcement.important')}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='mt-8'>
|
||||
{itemSelected.announcement.title}
|
||||
</div>
|
||||
|
||||
<div dangerouslySetInnerHTML={{ __html: itemSelected.announcement.content || '' }} className='mt-5 leading-7 text-xs font-light'>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnnouncementtList
|
||||
@@ -0,0 +1,41 @@
|
||||
import { type FC } from 'react'
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
|
||||
const AnouncementSkeleton: FC = () => {
|
||||
return (
|
||||
<div className={'p-4 border-b'}>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='leading-6 text-xs xl:text-[12.5px] max-w-[70%]'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={100}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-1'>
|
||||
<Skeleton
|
||||
className='!w-10 !h-6 !rounded-full'
|
||||
/>
|
||||
<Skeleton
|
||||
className='!w-10 !h-6 !rounded-full'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='m-2 flex justify-between items-center text-[11px]'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={150}
|
||||
/>
|
||||
<div className='text-description'>
|
||||
<Skeleton
|
||||
height={7}
|
||||
width={40}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AnouncementSkeleton
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as api from "../service/AnnoncementService";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useGetAnnoncement = (search: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["announcements", search],
|
||||
queryFn: () => api.getAnnoncements(search),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAnnoncementDetail = (id: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["announcement", id],
|
||||
queryFn: () => api.getAnnoncementDetail(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
// import axios from "../../../config/axios";
|
||||
|
||||
// دادههای استاتیک برای نمایش
|
||||
const mockAnnoncementsData = {
|
||||
data: {
|
||||
announcements: [
|
||||
{
|
||||
id: "1",
|
||||
createdAt: "2024-01-15T10:00:00Z",
|
||||
updatedAt: "2024-01-15T10:00:00Z",
|
||||
announcement: {
|
||||
id: "ann1",
|
||||
createdAt: "2024-01-15T10:00:00Z",
|
||||
title: "بهروزرسانی سیستم اطلاعرسانی",
|
||||
content:
|
||||
"سیستم اطلاعرسانی با ویژگیهای جدید بهروزرسانی شد. لطفاً از طریق بخش تنظیمات، تنظیمات خود را بررسی کنید.",
|
||||
publishAt: "2024-01-15T10:00:00Z",
|
||||
targetService: {
|
||||
id: "1",
|
||||
name: "سرویس عمومی",
|
||||
},
|
||||
isImportant: true,
|
||||
},
|
||||
isRead: false,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
createdAt: "2024-01-14T08:00:00Z",
|
||||
updatedAt: "2024-01-14T08:00:00Z",
|
||||
announcement: {
|
||||
id: "ann2",
|
||||
createdAt: "2024-01-14T08:00:00Z",
|
||||
title: "نگهداری برنامهریزی شده",
|
||||
content:
|
||||
"در تاریخ ۲۰ دی ماه، بهدلیل انجام نگهداری برنامهریزی شده، سرویسها به مدت ۲ ساعت در دسترس نخواهند بود.",
|
||||
publishAt: "2024-01-14T08:00:00Z",
|
||||
targetService: {
|
||||
id: "1",
|
||||
name: "سرویس عمومی",
|
||||
},
|
||||
isImportant: false,
|
||||
},
|
||||
isRead: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
createdAt: "2024-01-13T15:30:00Z",
|
||||
updatedAt: "2024-01-13T15:30:00Z",
|
||||
announcement: {
|
||||
id: "ann3",
|
||||
createdAt: "2024-01-13T15:30:00Z",
|
||||
title: "ویژگی جدید: پشتیبانی از پرداخت آنلاین",
|
||||
content:
|
||||
"ویژگی پرداخت آنلاین به سیستم اضافه شد. از این پس میتوانید بهراحتی از طریق درگاههای بانکی پرداخت خود را انجام دهید.",
|
||||
publishAt: "2024-01-13T15:30:00Z",
|
||||
targetService: {
|
||||
id: "2",
|
||||
name: "سرویس پرداخت",
|
||||
},
|
||||
isImportant: false,
|
||||
},
|
||||
isRead: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const mockAnnoncementDetailData = (id: string) => {
|
||||
const announcement = mockAnnoncementsData.data.announcements.find(
|
||||
(item) => item.announcement.id === id
|
||||
);
|
||||
|
||||
if (!announcement) {
|
||||
return {
|
||||
data: {
|
||||
announcement: {
|
||||
id: id,
|
||||
createdAt: "2024-01-15T10:00:00Z",
|
||||
title: "اطلاعیه نمونه",
|
||||
content:
|
||||
"<p>این یک اطلاعیه نمونه است. محتوای این اطلاعیه برای نمایش داده شده است.</p>",
|
||||
publishAt: "2024-01-15T10:00:00Z",
|
||||
targetService: {
|
||||
id: "1",
|
||||
name: "سرویس عمومی",
|
||||
},
|
||||
isImportant: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
announcement: announcement.announcement,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getAnnoncements = async (search: string) => {
|
||||
// ارتباط با سرور کامنت شده - از داده استاتیک استفاده میشود
|
||||
// const { data } = await axios.get(`/announcements/user?q=${search}`);
|
||||
// return data;
|
||||
|
||||
// شبیهسازی تاخیر شبکه
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
// فیلتر بر اساس جستجو
|
||||
let filteredData = { ...mockAnnoncementsData };
|
||||
if (search && search.trim()) {
|
||||
filteredData.data.announcements = mockAnnoncementsData.data.announcements.filter(
|
||||
(item) =>
|
||||
item.announcement.title.includes(search) ||
|
||||
item.announcement.content.includes(search)
|
||||
);
|
||||
}
|
||||
|
||||
return filteredData;
|
||||
};
|
||||
|
||||
export const getAnnoncementDetail = async (id: string) => {
|
||||
// ارتباط با سرور کامنت شده - از داده استاتیک استفاده میشود
|
||||
// const { data } = await axios.get(`/announcements/${id}`);
|
||||
// return data;
|
||||
|
||||
// شبیهسازی تاخیر شبکه
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
return mockAnnoncementDetailData(id);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
export type AnnoncementItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
announcement: {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
title: string;
|
||||
content: string;
|
||||
publishAt: string;
|
||||
targetService: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
isImportant: boolean;
|
||||
};
|
||||
isRead: boolean;
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import { staticTicketsData } from './data/staticData'
|
||||
import { t } from '@/locale'
|
||||
import moment from 'moment-jalaali'
|
||||
import type { ColumnType } from '@/components/types/TableTypes'
|
||||
import { Paths } from '@/config/Paths'
|
||||
|
||||
// تعریف type برای تیکت
|
||||
interface TicketItem {
|
||||
@@ -89,7 +90,7 @@ const TicketList: FC = () => {
|
||||
title: t('ticket.detail'),
|
||||
key: 'actions',
|
||||
render: (item) => (
|
||||
<Link to={`/tickets/${item.id}`}>
|
||||
<Link to={Paths.tickets.detail + item.id}>
|
||||
<Eye size={20} color='#8C90A3' />
|
||||
</Link>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user