report category list
This commit is contained in:
@@ -191,4 +191,7 @@ export const Pages = {
|
|||||||
list: "/jobs/list",
|
list: "/jobs/list",
|
||||||
resumes: "/jobs/resumes",
|
resumes: "/jobs/resumes",
|
||||||
},
|
},
|
||||||
|
contactUs: {
|
||||||
|
list: "/contact-us/list",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { type FC, useState } from 'react'
|
||||||
|
import { useGetContactUs } from './hooks/useContactUsData';
|
||||||
|
import { type ContactUsItem } from './types';
|
||||||
|
import PageLoading from '../../components/PageLoading';
|
||||||
|
import Error from '../../components/Error';
|
||||||
|
import PaginationUi from '../../components/PaginationUi';
|
||||||
|
import Td from '../../components/Td';
|
||||||
|
|
||||||
|
const ContactUsList: FC = () => {
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const { data: contactUsData, isLoading, error } = useGetContactUs(currentPage);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center min-h-[400px]">
|
||||||
|
<PageLoading />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center min-h-[400px]">
|
||||||
|
<Error errorText="خطا در بارگذاری پیامهای تماس با ما" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contactUs = contactUsData?.results?.contactUs || [];
|
||||||
|
const pager = contactUsData?.results?.pager;
|
||||||
|
|
||||||
|
const truncateText = (text: string, maxLength: number = 100) => {
|
||||||
|
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
|
||||||
|
<table className='w-full text-sm'>
|
||||||
|
<thead className='thead'>
|
||||||
|
<tr>
|
||||||
|
<Td text={'نام کامل'} />
|
||||||
|
<Td text={'ایمیل/تلفن'} />
|
||||||
|
<Td text={'پیام'} />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{contactUs.length === 0 ? (
|
||||||
|
<tr className='tr'>
|
||||||
|
<td colSpan={3} className="text-center py-8 text-gray-500">
|
||||||
|
هیچ پیامی یافت نشد
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
contactUs.map((contact: ContactUsItem, index: number) => (
|
||||||
|
<tr key={index} className='tr'>
|
||||||
|
<Td text={contact.fullName} />
|
||||||
|
<Td text={contact.email_phone} />
|
||||||
|
<Td text={truncateText(contact.message)} />
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PaginationUi
|
||||||
|
pager={pager}
|
||||||
|
onPageChange={(page) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContactUsList
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/ContactService";
|
||||||
|
|
||||||
|
export const useGetContactUs = (page: number = 1, limit: number = 10) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["contactUs", page, limit],
|
||||||
|
queryFn: () => api.getContactUs(page, limit),
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
import type { ContactUsResponse } from "../types";
|
||||||
|
|
||||||
|
export const getContactUs = async (
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = 10
|
||||||
|
): Promise<ContactUsResponse> => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`/admin/settings/contact-us?page=${page}&limit=${limit}`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { type PagerType } from '../../../shared/types/SharedTypes';
|
||||||
|
|
||||||
|
export interface ContactUsItem {
|
||||||
|
fullName: string;
|
||||||
|
email_phone: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContactUsResponse {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
pager: PagerType;
|
||||||
|
contactUs: ContactUsItem[];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,10 +1,44 @@
|
|||||||
import { type FC, useState } from 'react'
|
import { type FC, useState } from 'react'
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import CreateReportCategoryModal from './components/CreateReportCategoryModal.tsx';
|
import CreateReportCategoryModal from './components/CreateReportCategoryModal.tsx';
|
||||||
|
import { useGetReportCategories } from './hooks/useReportData.ts';
|
||||||
|
import { type Reason } from './types/Types';
|
||||||
|
import PageLoading from '../../components/PageLoading';
|
||||||
|
import Error from '../../components/Error';
|
||||||
|
import Td from '../../components/Td';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
const CategoryReport: FC = () => {
|
const CategoryReport: FC = () => {
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { data: reportCategoriesResponse, isLoading, error } = useGetReportCategories();
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['reportCategories'] });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center min-h-[400px]">
|
||||||
|
<PageLoading />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center min-h-[400px]">
|
||||||
|
<Error errorText="خطا در بارگذاری دستهبندیهای گزارش" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reportCategories = Array.isArray(reportCategoriesResponse?.results?.questions)
|
||||||
|
? reportCategoriesResponse.results.questions
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='flex justify-end mt-5'>
|
<div className='flex justify-end mt-5'>
|
||||||
@@ -14,11 +48,38 @@ const CategoryReport: FC = () => {
|
|||||||
className='w-fit'
|
className='w-fit'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'></div>
|
<div className='relative overflow-x-auto rounded-3xl mt-5 w-full'>
|
||||||
|
<table className='w-full text-sm'>
|
||||||
|
<thead className='thead'>
|
||||||
|
<tr>
|
||||||
|
<Td text={'عنوان'} />
|
||||||
|
<Td text={'تاریخ ایجاد'} />
|
||||||
|
<Td text={'نوع'} />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{reportCategories.length === 0 ? (
|
||||||
|
<tr className='tr'>
|
||||||
|
<td colSpan={3} className="text-center py-8 text-gray-500">
|
||||||
|
هیچ دستهبندی یافت نشد
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
reportCategories.map((category: Reason) => (
|
||||||
|
<tr key={category._id} className='tr'>
|
||||||
|
<Td text={category.title} />
|
||||||
|
<Td text={new Date(category.createdAt).toLocaleDateString('fa-IR')} />
|
||||||
|
<Td text={category.type} />
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<CreateReportCategoryModal
|
<CreateReportCategoryModal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={handleCloseModal}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,3 +13,10 @@ export const useCreateReportCategory = () => {
|
|||||||
mutationFn: api.createReportCategory,
|
mutationFn: api.createReportCategory,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetReportCategories = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["reportCategories"],
|
||||||
|
queryFn: api.getReportCategories,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import axios from "../../../config/axios";
|
import axios from "../../../config/axios";
|
||||||
import type { CreateReportCategoryType, ReportResponse } from "../types/Types";
|
import type {
|
||||||
|
CreateReportCategoryType,
|
||||||
|
ReportResponse,
|
||||||
|
ReportCategoriesResponse,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const getProductReport = async (): Promise<ReportResponse> => {
|
export const getProductReport = async (): Promise<ReportResponse> => {
|
||||||
const { data } = await axios.get("/admin/products/user-reports");
|
const { data } = await axios.get("/admin/products/user-reports");
|
||||||
@@ -13,7 +17,9 @@ export const createReportCategory = async (
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getReportQuestions = async () => {
|
export const getReportCategories = async (): Promise<
|
||||||
|
ReportCategoriesResponse
|
||||||
|
> => {
|
||||||
const { data } = await axios.get("/admin/products/report-questions");
|
const { data } = await axios.get("/admin/products/report-questions");
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,3 +85,11 @@ export interface ReportResponse {
|
|||||||
export interface CreateReportCategoryType {
|
export interface CreateReportCategoryType {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ReportCategoriesResponse {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: {
|
||||||
|
questions: Reason[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ import Banners from '@/pages/setting/Banners'
|
|||||||
import SiteSetting from '@/pages/setting/SiteSetting'
|
import SiteSetting from '@/pages/setting/SiteSetting'
|
||||||
import JobsList from '@/pages/jobs/List'
|
import JobsList from '@/pages/jobs/List'
|
||||||
import Resumes from '@/pages/jobs/Resume'
|
import Resumes from '@/pages/jobs/Resume'
|
||||||
|
import ContactUsList from '@/pages/contactUs/List'
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
@@ -146,6 +147,8 @@ const MainRouter: FC = () => {
|
|||||||
|
|
||||||
<Route path={Pages.jobs.list} element={<JobsList />} />
|
<Route path={Pages.jobs.list} element={<JobsList />} />
|
||||||
<Route path={Pages.jobs.resumes} element={<Resumes />} />
|
<Route path={Pages.jobs.resumes} element={<Resumes />} />
|
||||||
|
|
||||||
|
<Route path={Pages.contactUs.list} element={<ContactUsList />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ const SideBar: FC = () => {
|
|||||||
icon={<Headphone variant={isActive('contact') ? 'Bold' : 'Outline'} color={isActive('contact') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
icon={<Headphone variant={isActive('contact') ? 'Bold' : 'Outline'} color={isActive('contact') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||||
title="ارتباط با ما"
|
title="ارتباط با ما"
|
||||||
isActive={isActive('contact')}
|
isActive={isActive('contact')}
|
||||||
link="/contact"
|
link={Pages.contactUs.list}
|
||||||
activeName='contact'
|
activeName='contact'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ const SideBar: FC = () => {
|
|||||||
: subMenuName === 'tickets' ? <TicketsSubMenu />
|
: subMenuName === 'tickets' ? <TicketsSubMenu />
|
||||||
: subMenuName === 'reports' ? <ReportsSubMenu />
|
: subMenuName === 'reports' ? <ReportsSubMenu />
|
||||||
: subMenuName === 'chat' ? <ChatSubMenu />
|
: subMenuName === 'chat' ? <ChatSubMenu />
|
||||||
: subMenuName === 'contact' ? <ContactSubMenu />
|
: subMenuName === 'contact-us' ? <ContactSubMenu />
|
||||||
: subMenuName === 'settings' ? <SettingsSubMenu />
|
: subMenuName === 'settings' ? <SettingsSubMenu />
|
||||||
: subMenuName === 'pages' ? <PagesSubMenu />
|
: subMenuName === 'pages' ? <PagesSubMenu />
|
||||||
: subMenuName === 'jobs' ? <JobsSubMenu />
|
: subMenuName === 'jobs' ? <JobsSubMenu />
|
||||||
@@ -732,8 +732,8 @@ const ContactSubMenu: FC = () => {
|
|||||||
<div className='flex flex-col mt-10 gap-4'>
|
<div className='flex flex-col mt-10 gap-4'>
|
||||||
<SubMenuItem
|
<SubMenuItem
|
||||||
title={'پیام های دریافتی'}
|
title={'پیام های دریافتی'}
|
||||||
isActive={isActive('messages')}
|
isActive={isActive('contact')}
|
||||||
link="/contact/messages"
|
link={Pages.contactUs.list}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,3 +6,12 @@ export type SharedStoreType = {
|
|||||||
subMenuName: string;
|
subMenuName: string;
|
||||||
setSubMenuName: (value: string) => void;
|
setSubMenuName: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PagerType = {
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
totalItems: number;
|
||||||
|
totalPages: number;
|
||||||
|
prevPage: boolean | null;
|
||||||
|
nextPage: string | null;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user