diff --git a/.env b/.env
index b5e315f..e68eac3 100644
--- a/.env
+++ b/.env
@@ -3,6 +3,7 @@ VITE_REFRESH_TOKEN_NAME = 'dsc_refresh_token'
# VITE_BASE_URL = 'http://192.168.1.113:4001'
VITE_BASE_URL = 'https://api-dzone.danakcorp.com'
# VITE_DANAK_BASE_URL ='http://192.168.1.113:4000'
+VITE_DANAK_BASE_URL ='https://api.danakcorp.com'
VITE_SERVICE_ID = '7e3c2f08-b7e7-4402-ae5f-fea99cff56bd'
VITE_WORKSPACE_ID = 'workspace_id'
VITE_LOGIN_URL = 'https://console.danakcorp.com/auth/login'
diff --git a/src/config/Pages.ts b/src/config/Pages.ts
index 908648f..88848e8 100644
--- a/src/config/Pages.ts
+++ b/src/config/Pages.ts
@@ -6,6 +6,7 @@ export const Pages = {
},
requests: {
list: "/requests/list",
+ detail: "/requests/detail/",
},
dashboard: "/dashboard",
services: {
diff --git a/src/langs/fa.json b/src/langs/fa.json
index 6d36db3..d562a52 100644
--- a/src/langs/fa.json
+++ b/src/langs/fa.json
@@ -843,5 +843,66 @@
"select_company": "انتخاب شرکت",
"register_date": "تاریخ ثبت نام",
"company": "شرکت"
+ },
+ "request": {
+ "request_list": "لیست درخواست ها",
+ "details": "جزئیات درخواست",
+ "basic_info": "اطلاعات پایه",
+ "company_info": "اطلاعات شرکت",
+ "user_info": "اطلاعات کاربر",
+ "additional_info": "اطلاعات تکمیلی",
+ "id": "شناسه",
+ "created_at": "تاریخ ایجاد",
+ "updated_at": "تاریخ بروزرسانی",
+ "status": "وضعیت",
+ "reject_reason": "دلیل رد",
+ "company_name": "نام شرکت",
+ "ceo": "مدیرعامل",
+ "email": "ایمیل",
+ "phone": "شماره تماس",
+ "address": "آدرس",
+ "identification_number": "شماره شناسایی",
+ "date_of_establishment": "تاریخ تاسیس",
+ "description": "توضیحات",
+ "company_status": "وضعیت شرکت",
+ "business": "کسب و کار",
+ "invoice_count": "تعداد فاکتور",
+ "website": "وبسایت",
+ "map_location": "موقعیت مکانی",
+ "view_on_map": "مشاهده روی نقشه",
+ "profile_image": "تصویر پروفایل",
+ "cover_image": "تصویر کاور",
+ "view_image": "مشاهده تصویر",
+ "products": "محصولات",
+ "services": "خدمات",
+ "actions": "عملیات",
+ "approve": "تایید",
+ "reject": "رد",
+ "approving": "در حال تایید",
+ "rejecting": "در حال رد",
+ "enter_reject_reason": "دلیل رد درخواست را وارد کنید",
+ "approve_success": "درخواست با موفقیت تایید شد",
+ "reject_success": "درخواست با موفقیت رد شد",
+ "approve_error": "خطا در تایید درخواست",
+ "reject_error": "خطا در رد درخواست",
+ "pending": "در انتظار بررسی",
+ "approved": "تایید شده",
+ "rejected": "رد شده",
+ "industry": "صنعت",
+ "full_name": "نام و نام خانوادگی",
+ "national_code": "کد ملی",
+ "role": "نقش",
+ "customer": "مشتری",
+ "date_receipt": "تاریخ درخواست",
+ "last_date_receipt": "آخرین تاریخ درخواست",
+ "total": "مبلغ کل",
+ "lateFee": "جریمه دیرکرد",
+ "service": "سرویس",
+ "status_paid": "وضعیت پرداخت",
+ "recurring": "تکرار شونده",
+ "maxRecurringCycles": "حداکثر تعداد تکرار",
+ "paid": "پرداخت شده",
+ "recurringPeriod": "دوره تکرار",
+ "user": "کاربر"
}
}
diff --git a/src/pages/requests/Detail.tsx b/src/pages/requests/Detail.tsx
new file mode 100644
index 0000000..bf27bea
--- /dev/null
+++ b/src/pages/requests/Detail.tsx
@@ -0,0 +1,362 @@
+import { FC, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useParams } from 'react-router-dom'
+import moment from 'moment-jalaali'
+import PageLoading from '../../components/PageLoading'
+import { useChangeRequestStatus, useGetRequestDetail } from './hooks/useRequestData'
+import ModalConfrim from '../../components/ModalConfrim'
+import { toast } from 'react-toastify'
+import { ErrorType } from '../../helpers/types'
+interface Product {
+ id: string
+ title: string
+ imageUrl: string
+}
+
+interface Service {
+ id: string
+ title: string
+ imageUrl: string
+}
+
+interface Business {
+ id: string
+ name: string
+ slug: string
+}
+
+interface Company {
+ id: string
+ name: string
+ chiefExecutiveOfficer: string
+ email: string
+ phone: string
+ identificationNumber: string
+ dateOfEstablishment: string
+ address: string
+ mapAddressLink: string
+ description: string
+ websiteUrl: string
+ profileImageUrl: string
+ coverImageUrl: string
+ status: string
+ industry: string
+ business: string
+ products: Product[]
+ services: Service[]
+}
+
+interface Request {
+ id: string
+ createdAt: string
+ updatedAt: string
+ rejectReason: string | null
+ requestStatus: string
+ company: Company
+ business: Business
+}
+
+const RequestDetail: FC = () => {
+ const { t } = useTranslation('global')
+ const { id } = useParams()
+ const { data: response, isLoading } = useGetRequestDetail(id || '')
+ const [rejectReason, setRejectReason] = useState('')
+ const [showRejectModal, setShowRejectModal] = useState(false)
+ const { mutate: changeRequestStatus, isPending } = useChangeRequestStatus()
+
+ if (isLoading) {
+ return (
+
+ )
+ }
+
+ if (!response?.data?.request) {
+ return Request not found
+ }
+
+ const request: Request = response.data.request
+
+ const handleApprove = () => {
+ if (id) {
+ changeRequestStatus({
+ id,
+ status: 'APPROVED',
+ rejectReason: ''
+ })
+ }
+ }
+
+ const handleReject = (rejectReason: string) => {
+ if (id) {
+ changeRequestStatus({
+ id,
+ status: 'REJECTED',
+ rejectReason: rejectReason
+ }, {
+ onSuccess: () => {
+ toast.success(t('success'))
+ window.location.reload()
+ },
+ onError: (error: ErrorType) => {
+ toast.error(error.response?.data?.error?.message[0])
+ }
+ })
+ }
+ }
+
+ return (
+
+
+
{t('request.details')}
+
+
+
+
+
+
+
{t('request.basic_info')}
+
+
+ {t('request.id')}:
+ {request.id}
+
+
+ {t('request.created_at')}:
+ {moment(request.createdAt).format('jYYYY/jMM/jDD HH:mm')}
+
+
+ {t('request.updated_at')}:
+ {moment(request.updatedAt).format('jYYYY/jMM/jDD HH:mm')}
+
+
+ {t('request.status')}:
+ {request.requestStatus ? t(`request.${request.requestStatus.toLowerCase()}`) : '-'}
+
+ {request.rejectReason && (
+
+ {t('request.reject_reason')}:
+ {request.rejectReason}
+
+ )}
+
+
+
+
+
{t('request.company_info')}
+
+
+ {t('request.company_name')}:
+ {request.company.name || '-'}
+
+
+ {t('request.ceo')}:
+ {request.company.chiefExecutiveOfficer || '-'}
+
+
+ {t('request.email')}:
+ {request.company.email || '-'}
+
+
+ {t('request.phone')}:
+ {request.company.phone || '-'}
+
+
+ {t('request.address')}:
+ {request.company.address || '-'}
+
+
+ {t('request.identification_number')}:
+ {request.company.identificationNumber || '-'}
+
+
+ {t('request.date_of_establishment')}:
+ {request.company.dateOfEstablishment ? moment(request.company.dateOfEstablishment).format('jYYYY/jMM/jDD') : '-'}
+
+
+ {t('request.description')}:
+ {request.company.description || '-'}
+
+
+ {t('request.company_status')}:
+ {request.company.status ? t(`request.${request.company.status.toLowerCase()}`) : '-'}
+
+
+ {t('request.business')}:
+ {request.business.name || '-'}
+
+
+ {t('request.invoice_count')}:
+ 0
+
+
+
+
+
+
+
+
{t('request.additional_info')}
+
+ {request.company.websiteUrl && (
+
+ )}
+ {request.company.mapAddressLink && (
+
+ )}
+ {request.company.profileImageUrl && (
+
+ )}
+ {request.company.coverImageUrl && (
+
+ )}
+
+
+
+ {request.company.products && request.company.products.length > 0 && (
+
+
{t('request.products')}
+
+ {request.company.products.map((product: Product) => (
+
+
+

+
+
{product.title}
+
+ ))}
+
+
+ )}
+
+ {request.company.services && request.company.services.length > 0 && (
+
+
{t('request.services')}
+
+ {request.company.services.map((service: Service) => (
+
+
+

+
+
{service.title}
+
+ ))}
+
+
+ )}
+
+ {request.requestStatus === 'PENDING' && (
+
+
{t('request.actions')}
+
+
+
+
+
+ )}
+
+
+
+
+
setShowRejectModal(false)}
+ label={t('request.reject_reason')}
+ onConfrim={(text) => handleReject(text || '')}
+ />
+
+ {showRejectModal && (
+
+
+
{t('request.reject_reason')}
+
+
+ )}
+
+ )
+}
+
+export default RequestDetail
\ No newline at end of file
diff --git a/src/pages/requests/List.tsx b/src/pages/requests/List.tsx
new file mode 100644
index 0000000..fb9686e
--- /dev/null
+++ b/src/pages/requests/List.tsx
@@ -0,0 +1,88 @@
+import { FC, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useGetRequests } from './hooks/useRequestData'
+import Pagination from '../../components/Pagination'
+import Td from '../../components/Td'
+import PageLoading from '../../components/PageLoading'
+import { RequestItemType } from './types/Types'
+import moment from 'moment-jalaali'
+import { useNavigate } from 'react-router-dom'
+import { Eye } from 'iconsax-react'
+import { Pages } from '../../config/Pages'
+
+const RequestsList: FC = () => {
+ const { t } = useTranslation('global')
+ const [page, setPage] = useState(1)
+ const { data, isLoading } = useGetRequests(page)
+ const navigate = useNavigate()
+
+ const handleRowClick = (id: string) => {
+ navigate(Pages.requests.detail + id)
+ }
+
+ return (
+
+
+ {t('request.request_list')}
+
+
+ {
+ isLoading ?
+
+ :
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+ {
+ data?.data?.requests?.map((item: RequestItemType, index: number) => {
+ return (
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+ handleRowClick(item.id)}
+ />
+ |
+
+ )
+ })
+ }
+
+
+
+
+
+ }
+
+ )
+}
+
+export default RequestsList
\ No newline at end of file
diff --git a/src/pages/requests/hooks/useRequestData.ts b/src/pages/requests/hooks/useRequestData.ts
new file mode 100644
index 0000000..ca5bde1
--- /dev/null
+++ b/src/pages/requests/hooks/useRequestData.ts
@@ -0,0 +1,47 @@
+import { useMutation, useQuery } from "@tanstack/react-query";
+import {
+ approveRequest,
+ getRequestDetail,
+ getRequests,
+ rejectRequest,
+} from "../service/RequestService";
+
+export const useGetRequests = (page: number = 1) => {
+ return useQuery({
+ queryKey: ["requests", page],
+ queryFn: () => getRequests(page),
+ });
+};
+
+export const useGetRequestDetail = (id: string) => {
+ return useQuery({
+ queryKey: ["request", id],
+ queryFn: () => getRequestDetail(id),
+ });
+};
+
+export const useApproveRequest = () => {
+ return useMutation({
+ mutationFn: (id: string) => approveRequest(id),
+ });
+};
+
+export const useChangeRequestStatus = () => {
+ return useMutation({
+ mutationFn: ({
+ id,
+ status,
+ rejectReason,
+ }: {
+ id: string;
+ status: string;
+ rejectReason: string;
+ }) => {
+ if (status === "APPROVED") {
+ return approveRequest(id);
+ } else {
+ return rejectRequest(id, status, rejectReason);
+ }
+ },
+ });
+};
diff --git a/src/pages/requests/service/RequestService.ts b/src/pages/requests/service/RequestService.ts
new file mode 100644
index 0000000..bed3323
--- /dev/null
+++ b/src/pages/requests/service/RequestService.ts
@@ -0,0 +1,31 @@
+import axios from "../../../config/axios";
+
+export const getRequests = async (page: number = 1) => {
+ const { data } = await axios.get(`/companies/requests?page=${page}`);
+ return data;
+};
+
+export const getRequestDetail = async (id: string) => {
+ const { data } = await axios.get(`/companies/requests/${id}`);
+ return data;
+};
+
+export const approveRequest = async (id: string) => {
+ const { data } = await axios.patch(`/companies/requests/${id}/approve`);
+ return data;
+};
+
+export const rejectRequest = async (
+ id: string,
+ status: string,
+ rejectReason: string
+) => {
+ const { data } = await axios.patch(
+ `/companies/requests/${id}/change-status`,
+ {
+ requestStatus: status,
+ rejectReason,
+ }
+ );
+ return data;
+};
diff --git a/src/pages/requests/types/Types.ts b/src/pages/requests/types/Types.ts
new file mode 100644
index 0000000..ea4c3ed
--- /dev/null
+++ b/src/pages/requests/types/Types.ts
@@ -0,0 +1,48 @@
+export type ApiResponse = {
+ statusCode: number;
+ success: boolean;
+ data: T;
+};
+
+export type RequestResponse = {
+ request: RequestItemType;
+};
+
+export type RequestItemType = {
+ id: string;
+ createdAt: string;
+ updatedAt: string;
+ deletedAt: string | null;
+ rejectReason: string | null;
+ requestStatus: string;
+ company: {
+ id: string;
+ createdAt: string;
+ updatedAt: string;
+ deletedAt: string | null;
+ name: string;
+ chiefExecutiveOfficer: string;
+ email: string;
+ phone: string;
+ identificationNumber: string;
+ dateOfEstablishment: string;
+ address: string;
+ mapAddressLink: string;
+ description: string;
+ websiteUrl: string;
+ profileImageUrl: string;
+ coverImageUrl: string;
+ isActive: boolean;
+ status: string;
+ industry: string;
+ business: string;
+ user: string;
+ invoiceCount: number;
+ };
+ business: string;
+ user: {
+ id: string;
+ firstName: string;
+ lastName: string;
+ };
+};
diff --git a/src/pages/ticket/Detail.tsx b/src/pages/ticket/Detail.tsx
index 8b3db3a..30efe91 100644
--- a/src/pages/ticket/Detail.tsx
+++ b/src/pages/ticket/Detail.tsx
@@ -17,7 +17,6 @@ import { ErrorType } from '../../helpers/types'
import { clx } from '../../helpers/utils'
import { Pages } from '../../config/Pages'
import { useMultiUpload } from '../service/hooks/useServiceData'
-import ReferTicket from './components/ReferTicket'
const TicketDetail: FC = () => {
@@ -99,22 +98,11 @@ const TicketDetail: FC = () => {
}
diff --git a/src/pages/ticket/types/TicketTypes.ts b/src/pages/ticket/types/TicketTypes.ts
index 0c21fbc..0d34b55 100644
--- a/src/pages/ticket/types/TicketTypes.ts
+++ b/src/pages/ticket/types/TicketTypes.ts
@@ -54,9 +54,9 @@ export type TicketMessageType = {
id: string;
firstName: string;
lastName: string;
- roles: {
+ role: {
name: string;
- }[];
+ };
};
attachments: {
attachmentUrl: string;
diff --git a/src/router/Main.tsx b/src/router/Main.tsx
index d328031..34ce170 100644
--- a/src/router/Main.tsx
+++ b/src/router/Main.tsx
@@ -50,6 +50,8 @@ import CreateCompany from '../pages/company/Create'
import Industries from '../pages/company/components/Industries'
import CompanyList from '../pages/company/List'
import UpdateCompany from '../pages/company/Update'
+import RequestsList from '../pages/requests/List'
+import RequestDetail from '../pages/requests/Detail'
const MainRouter: FC = () => {
const { hasSubMenu } = useSharedStore()
@@ -112,6 +114,8 @@ const MainRouter: FC = () => {
} />
} />
} />
+ } />
+ } />
diff --git a/src/shared/Header.tsx b/src/shared/Header.tsx
index e60eb07..5aeafc3 100644
--- a/src/shared/Header.tsx
+++ b/src/shared/Header.tsx
@@ -10,13 +10,14 @@ import { useSharedStore } from './store/sharedStore'
import { useGetProfile } from '../pages/profile/hooks/useProfileData'
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'
import { removeRefreshToken, removeToken } from '../config/func'
+import { clx } from '../helpers/utils'
const Header: FC = () => {
const location = useLocation();
const [popoverKey, setPopoverKey] = useState(0);
const { t } = useTranslation('global')
- const { setOpenSidebar, openSidebar } = useSharedStore()
+ const { setOpenSidebar, openSidebar, hasSubMenu } = useSharedStore()
const { data } = useGetProfile()
useEffect(() => {
@@ -29,8 +30,13 @@ const Header: FC = () => {
window.location.href = Pages.auth.login
}
+ const hasSubMenuOpen = hasSubMenu && (openSidebar || window.innerWidth > 1139)
+
return (
-