create request company

This commit is contained in:
hamid zarghami
2025-05-19 14:59:49 +03:30
parent 2fbdf05a25
commit f7e4548c96
14 changed files with 677 additions and 24 deletions
+7 -3
View File
@@ -30,7 +30,7 @@ declare global {
function App() {
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: async (error: IApiErrorRepsonse) => {
onError: async (error: IApiErrorRepsonse, query) => {
if (error?.response?.status === 401) {
try {
// Use a flag to track if refresh is in progress
@@ -63,8 +63,12 @@ function App() {
await setRefreshToken(data.refreshToken?.token);
}
// Retry the failed request
queryClient.invalidateQueries();
// Retry the failed request, even if it was muted
if (query) {
queryClient.invalidateQueries({ queryKey: query.queryKey });
} else {
queryClient.invalidateQueries();
}
return;
} else {
throw new Error('Invalid token response');
+1 -2
View File
@@ -6,7 +6,7 @@ type Props = {
className?: string;
isLoading?: boolean,
} & ButtonHTMLAttributes<HTMLButtonElement> &
XOR<{ children: ReactNode }, { label: string, icon?: string, isLoading?: boolean }>;
XOR<{ children: ReactNode }, { label: string, icon?: string }>;
const Button: FC<Props> = memo((props: Props) => {
const buttonClass = clx(
'flex rounded-xl items-center justify-center text-center h-10 text-sm bg-primary text-white w-full',
@@ -15,7 +15,6 @@ const Button: FC<Props> = memo((props: Props) => {
);
return (
<button {...props} className={`${buttonClass} ${props.className}`} >
<img src={props.icon} alt="" />
{
props.isLoading ?
<ReactLoading type='spin' color={'white'} width={20} height={20} />
+56 -9
View File
@@ -1,22 +1,57 @@
import { FC, useCallback, useState } from 'react'
import { FC, useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useDropzone } from 'react-dropzone'
import { CloseCircle } from 'iconsax-react'
type Props = {
label: string,
isMultiple?: boolean,
onChange?: (file: File[]) => void;
isReset?: boolean;
}
const UploadBox: FC<Props> = (props: Props) => {
const { t } = useTranslation('global')
const [file, setFile] = useState<File>()
const [files, setFiles] = useState<File[]>([])
const onDrop = useCallback((acceptedFiles: File[]) => {
setFile(acceptedFiles[0])
}, [])
if (props.isMultiple) {
const array = [...files]
array.push(acceptedFiles[0])
setFiles(array)
if (props.onChange) {
props.onChange(array);
}
} else {
setFiles([acceptedFiles[0]])
if (props.onChange) {
props.onChange([acceptedFiles[0]])
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [files])
const { getRootProps, getInputProps } = useDropzone({ onDrop })
const handleRemove = (index: number) => {
const array = [...files]
array.splice(index, 1)
setFiles(array)
if (props.onChange) {
props.onChange(array)
}
}
useEffect(() => {
if (props.isReset) {
setFiles([])
}
}, [props.isReset])
return (
<div>
@@ -28,15 +63,27 @@ const UploadBox: FC<Props> = (props: Props) => {
{t('select_file')}
</button>
<div className='text-description text-xs'>
<div className='lg:flex gap-2 hidden'>
{
file ?
file.name
:
t('no_select_file')
files?.map((item, index) => (
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
<div className='text-xs'>{item.name}</div>
</div>
))
}
</div>
</div>
<div className='lg:hidden gap-2 flex flex-wrap mt-4'>
{
files?.map((item, index) => (
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
<div className='text-xs'>{item.name}</div>
</div>
))
}
</div>
</div>
)
}
+1
View File
@@ -41,4 +41,5 @@ export const Pages = {
reportCard: {
list: "report-card/list",
},
request: "request",
};
+54 -2
View File
@@ -1,5 +1,13 @@
import axios from "axios";
import { getToken } from "./func";
import {
getToken,
getRefreshToken,
setToken,
setRefreshToken,
removeToken,
removeRefreshToken,
} from "./func";
import { refreshToken } from "../pages/auth/service/AuthService";
// import { Pages } from "./Pages";
const axiosInstance = axios.create({
@@ -8,7 +16,51 @@ const axiosInstance = axios.create({
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
async (error) => {
const originalRequest = error.config;
if (error?.response?.status === 401 && !originalRequest._retry) {
if (window.isRefreshingToken) {
// Wait for the refresh to complete
await new Promise((resolve) => {
const checkComplete = setInterval(() => {
if (!window.isRefreshingToken) {
clearInterval(checkComplete);
resolve(true);
}
}, 100);
});
return axiosInstance(originalRequest);
}
originalRequest._retry = true;
window.isRefreshingToken = true;
try {
const refreshTokenValue = await getRefreshToken();
const { data } = await refreshToken({
refreshToken: refreshTokenValue || "",
});
if (data?.accessToken?.token) {
await setToken(data?.accessToken?.token);
if (data.refreshToken?.token) {
await setRefreshToken(data.refreshToken?.token);
}
originalRequest.headers.Authorization = `Bearer ${data.accessToken.token}`;
return axiosInstance(originalRequest);
}
} catch (refreshError) {
console.error("Token refresh failed:", refreshError);
await removeToken();
await removeRefreshToken();
window.location.href = "/auth/login";
return Promise.reject(refreshError);
} finally {
window.isRefreshingToken = false;
}
}
return Promise.reject(error);
}
);
-5
View File
@@ -3,12 +3,9 @@ import { useNavigate, useParams } from 'react-router-dom'
import { getToken } from '../config/func'
import { buildPath } from '../helpers/utils'
// تایپ برای کامپوننت‌های React
type ComponentType = FC<any>
// HOC برای محافظت از کامپوننت‌های نیازمند لاگین
const withAuth = (WrappedComponent: ComponentType): ComponentType => {
// کامپوننت جدید که لاگین را چک می‌کند
const WithAuthComponent: FC<any> = (props) => {
const navigate = useNavigate()
const { slug } = useParams()
@@ -25,11 +22,9 @@ const withAuth = (WrappedComponent: ComponentType): ComponentType => {
checkAuth()
}, [navigate, slug])
// کامپوننت اصلی را با props خودش رندر کن
return <WrappedComponent {...props} />
}
// نام نمایشی کامپوننت را برای دیباگ بهتر تنظیم کن
WithAuthComponent.displayName = `WithAuth(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`
return WithAuthComponent
+36 -1
View File
@@ -320,5 +320,40 @@
"title_course": "عنوان دوره"
},
"status": "وضعیت",
"download": "دانلود"
"download": "دانلود",
"company": {
"count_company": "تعداد شرکت ها",
"company_name": "نام شرکت",
"ceo_name": "نام مدیر عامل",
"ceo_first_name": "نام مدیر عامل",
"ceo_last_name": "نام خانوادگی مدیر عامل",
"phone_number": "شماره تماس",
"email": "ایمیل",
"national_id": "شناسه ملی",
"establishment_date": "تاریخ تاسیس",
"address": "آدرس",
"map_link": "لینک نقشه شرکت",
"short_description": "توضیح کوتاه شرکت",
"website_url": "آدرس وبسایت",
"profile_image_url": "تصویر پروفایل",
"cover_image_url": "تصویر پوشه",
"is_active": "وضعیت فعالیت",
"industry_id": "صنعت",
"industry": "صنعت",
"profile_image": "تصویر پروفایل",
"cover_image": "تصویر کاور",
"created_at": "تاریخ ثبت",
"status": "وضعیت",
"plans": "پلن ها",
"detail": "جزییات",
"slider": "اسلایدر",
"category": "دسته بندی",
"company_developed": "شرکت توسعه دهنده",
"slug": "اسلاگ",
"industries": "صنعت ها",
"invoice_count": "تعداد صورتحساب",
"select_company": "انتخاب شرکت",
"register_date": "تاریخ ثبت نام",
"company": "شرکت"
}
}
+263
View File
@@ -0,0 +1,263 @@
import { FC } from 'react'
import withAuth from '../../hoc/withAuth'
import Button from '../../components/Button'
import { TickCircle } from 'iconsax-react'
import Input from '../../components/Input'
import { useTranslation } from 'react-i18next'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useState } from 'react'
import { toast } from 'react-toastify'
import { ErrorType } from '../../helpers/types'
import { Pages } from '../../config/Pages'
import { useNavigate, useParams } from 'react-router-dom'
import DatePickerComponent from '../../components/DatePicker'
import moment from 'moment-jalaali'
import { useCreateCompany, useMultiUpload, useSingleUpload } from './hooks/useRequestData'
import { CreateCompanyType } from './types/Types'
import UploadBox from '../../components/UploadBox'
import CompanyServices from './components/CompanyServices'
import CompanyProducts from './components/CompanyProducts'
import { useGetIndustry } from '../home/hooks/useHomeData'
import Select from '../../components/Select'
import { IndustryItemType } from '../home/types/HomeTypes'
import { buildPath } from '../../helpers/utils'
const Request: FC = () => {
const createCompany = useCreateCompany()
const navigate = useNavigate()
const { t } = useTranslation('global')
const [profileImage, setProfileImage] = useState<File | null>(null)
const [coverImage, setCoverImage] = useState<File | null>(null)
const multiUpload = useMultiUpload()
const singleUpload = useSingleUpload()
const { data: industries } = useGetIndustry()
const { slug } = useParams()
const [services, setServices] = useState<{ title: string; image?: File }[]>([])
const [products, setProducts] = useState<{ title: string; image?: File }[]>([])
const formik = useFormik<CreateCompanyType>({
initialValues: {
phone: '',
name: '',
email: '',
identificationNumber: '',
dateOfEstablishment: '',
address: '',
mapAddressLink: '',
description: '',
websiteUrl: '',
profileImageUrl: '',
coverImageUrl: '',
industryId: '',
chiefExecutiveOfficer: '',
products: [],
services: [],
},
validationSchema: Yup.object({
phone: Yup.string().required(t('errors.required')),
name: Yup.string().required(t('errors.required')),
email: Yup.string().email(t('errors.invalid_email')).required(t('errors.required')),
identificationNumber: Yup.string().required(t('errors.required')),
dateOfEstablishment: Yup.string().required(t('errors.required')),
address: Yup.string().required(t('errors.required')),
mapAddressLink: Yup.string().url(t('errors.invalid_url')),
description: Yup.string().required(t('errors.required')),
websiteUrl: Yup.string().url(t('errors.invalid_url')).required(t('errors.required')),
industryId: Yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
if (!profileImage || !coverImage) {
toast.error('لطفا تصاویر را انتخاب کنید')
return
}
let serviceImages: string[] = []
if (services.length > 0) {
const formDataServices = new FormData()
services.forEach(s => s.image && formDataServices.append('files', s.image))
const serviceUploadRes = await multiUpload.mutateAsync(formDataServices)
serviceImages = serviceUploadRes.data.map((item: { url: string }) => item?.url)
}
// آپلود تصاویر محصولات
let productImages: string[] = []
if (products.length > 0) {
const formDataProducts = new FormData()
products.forEach(p => p.image && formDataProducts.append('files', p.image))
const productUploadRes = await multiUpload.mutateAsync(formDataProducts)
productImages = productUploadRes.data.map((item: { url: string }) => item?.url)
}
// مقداردهی به values
values.services = services.map((s, idx) => ({
title: s.title,
imageUrl: serviceImages[idx] ? serviceImages[idx] : ''
}))
values.products = products.map((p, idx) => ({
title: p.title,
imageUrl: productImages[idx] ? productImages[idx] : ''
}))
const formDataProfile = new FormData()
formDataProfile.append('file', profileImage)
const profileUploadRes = await singleUpload.mutateAsync(formDataProfile)
values.profileImageUrl = profileUploadRes.data.url
const formDataCover = new FormData()
formDataCover.append('file', coverImage)
const coverUploadRes = await singleUpload.mutateAsync(formDataCover)
values.coverImageUrl = coverUploadRes.data.url
createCompany.mutate(values, {
onSuccess: () => {
toast.success('درخواست با موفقیت ثبت شد')
navigate(buildPath(Pages.dashboard, slug))
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0])
}
})
toast.success('درخواست با موفقیت ثبت شد')
// navigate(Pages.request.list)
}
})
return (
<div className='mt-4'>
<div className='flex flex-col md:flex-row justify-between items-start md:items-center gap-4'>
<div className='text-base'>
ثبت درخواست
</div>
<div className='flex justify-end w-full'>
<Button
className='w-fit px-4'
onClick={() => formik.handleSubmit()}
isLoading={singleUpload.isPending || multiUpload.isPending}
>
<div className='flex items-center gap-2'>
<TickCircle size={20} color='white' />
<div>ثبت درخواست</div>
</div>
</Button>
</div>
</div>
<div className='mt-10 bg-white py-8 px-4 md:px-6 lg:px-10 rounded-3xl'>
<div className='text-lg font-medium mb-6'>اطلاعات درخواست</div>
<div className='space-y-6'>
<Input
label='نام شرکت'
name='name'
value={formik.values.name}
onChange={formik.handleChange}
error_text={formik.touched.name && formik.errors.name ? formik.errors.name : ''}
/>
<div className=''>
<Input
label='نام مدیر عامل'
name='chiefExecutiveOfficer'
value={formik.values.chiefExecutiveOfficer}
onChange={formik.handleChange}
error_text={formik.touched.chiefExecutiveOfficer && formik.errors.chiefExecutiveOfficer ? formik.errors.chiefExecutiveOfficer : ''}
/>
</div>
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<Input
label={t('company.phone_number')}
name='phone'
value={formik.values.phone}
onChange={formik.handleChange}
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
/>
<Input
label={t('company.email')}
name='email'
value={formik.values.email}
onChange={formik.handleChange}
error_text={formik.touched.email && formik.errors.email ? formik.errors.email : ''}
/>
</div>
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<Input
label={t('company.national_id')}
name='identificationNumber'
value={formik.values.identificationNumber}
onChange={formik.handleChange}
error_text={formik.touched.identificationNumber && formik.errors.identificationNumber ? formik.errors.identificationNumber : ''}
/>
<DatePickerComponent
label={t('company.establishment_date')}
placeholder='تاریخ تاسیس'
onChange={(date) => formik.setFieldValue('dateOfEstablishment', moment(date).format('YYYY-MM-DD'))}
error_text={formik.touched.dateOfEstablishment && formik.errors.dateOfEstablishment ? formik.errors.dateOfEstablishment : ''}
/>
</div>
<Select
label={t('company.industry')}
name='industryId'
items={industries?.data?.industries?.map((industry: IndustryItemType) => ({ label: industry.title, value: industry.id })) || []}
onChange={formik.handleChange}
error_text={formik.touched.industryId && formik.errors.industryId ? formik.errors.industryId : ''}
/>
<Input
label={t('company.map_link')}
name='mapAddressLink'
value={formik.values.mapAddressLink}
onChange={formik.handleChange}
error_text={formik.touched.mapAddressLink && formik.errors.mapAddressLink ? formik.errors.mapAddressLink : ''}
/>
<Input
label={t('company.address')}
name='address'
value={formik.values.address}
onChange={formik.handleChange}
error_text={formik.touched.address && formik.errors.address ? formik.errors.address : ''}
/>
<Input
label={t('company.short_description')}
name='description'
value={formik.values.description}
onChange={formik.handleChange}
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
/>
<Input
label={t('company.website_url')}
name='websiteUrl'
value={formik.values.websiteUrl}
onChange={formik.handleChange}
error_text={formik.touched.websiteUrl && formik.errors.websiteUrl ? formik.errors.websiteUrl : ''}
/>
<UploadBox
label='تصویر پروفایل شرکت'
onChange={(file) => setProfileImage(file[0])}
/>
<UploadBox
label='تصویر کاور'
onChange={(file) => setCoverImage(file[0])}
/>
<CompanyServices value={services} onChange={setServices} />
<CompanyProducts value={products} onChange={setProducts} />
</div>
</div>
</div>
)
}
export default withAuth(Request)
@@ -0,0 +1,92 @@
import { FC, useState } from 'react'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import { Add, CloseCircle } from 'iconsax-react'
import Button from '../../../components/Button'
type Product = { title: string; image?: File; existingImageUrl?: string }
type Props = {
value: Product[]
onChange: (val: Product[]) => void
}
const CompanyProducts: FC<Props> = ({ value, onChange }) => {
const [productTitle, setProductTitle] = useState('')
const [productImage, setProductImage] = useState<File | undefined>(undefined)
const [productError, setProductError] = useState('')
const handleAddProduct = () => {
if (!productTitle.trim()) {
setProductError('عنوان محصول الزامی است')
return
}
if (!productImage) {
setProductError('تصویر محصول الزامی است')
return
}
if (value.some(s => s.title.trim() === productTitle.trim())) {
setProductError('عنوان محصول تکراری است')
return
}
onChange([...value, { title: productTitle.trim(), image: productImage }])
setProductTitle('')
setProductImage(undefined)
setProductError('')
}
const handleRemoveProduct = (idx: number) => {
onChange(value.filter((_, i) => i !== idx))
}
return (
<div className='mt-8'>
<div className='mb-2'>محصولات شرکت</div>
<div className='rowTwoInput items-end gap-4'>
<div className='w-full'>
<Input
label='عنوان محصول'
name='productTitle'
value={productTitle}
onChange={e => setProductTitle(e.target.value)}
/>
</div>
<div className='w-full'>
<UploadBox
label='تصویر'
onChange={files => setProductImage(files[0])}
isMultiple={false}
isReset={!productImage && !productTitle}
/>
</div>
<Button
onClick={handleAddProduct}
className='w-fit px-4 whitespace-nowrap'
>
<div className='flex gap-2'>
<Add size={22} color='white' />
<div>افزودن خدمت</div>
</div>
</Button>
</div>
{productError && <div className='text-xs text-red-600 mt-2'>{productError}</div>}
{/* نمایش لیست محصولات */}
<div className='mt-4 flex gap-2'>
{value.map((prd, idx) => (
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
<div className='flex items-center gap-2'>
{prd.image && <img src={URL.createObjectURL(prd.image)} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
{prd.existingImageUrl && !prd.image && <img src={prd.existingImageUrl} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
</div>
<div className='w-full text-xs'>{prd.title}</div>
<button type='button' onClick={() => handleRemoveProduct(idx)} className='ml-auto'>
<CloseCircle size={16} color='red' />
</button>
</div>
))}
</div>
</div>
)
}
export default CompanyProducts
@@ -0,0 +1,92 @@
import { FC, useState } from 'react'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import { Add, CloseCircle } from 'iconsax-react'
import Button from '../../../components/Button'
type Service = { title: string; image?: File; existingImageUrl?: string }
type Props = {
value: Service[]
onChange: (val: Service[]) => void
}
const CompanyServices: FC<Props> = ({ value, onChange }) => {
const [serviceTitle, setServiceTitle] = useState('')
const [serviceImage, setServiceImage] = useState<File | undefined>(undefined)
const [serviceError, setServiceError] = useState('')
const handleAddService = () => {
if (!serviceTitle.trim()) {
setServiceError('عنوان خدمت الزامی است')
return
}
if (!serviceImage) {
setServiceError('تصویر خدمت الزامی است')
return
}
if (value.some(s => s.title.trim() === serviceTitle.trim())) {
setServiceError('عنوان خدمت تکراری است')
return
}
onChange([...value, { title: serviceTitle.trim(), image: serviceImage }])
setServiceTitle('')
setServiceImage(undefined)
setServiceError('')
}
const handleRemoveService = (idx: number) => {
onChange(value.filter((_, i) => i !== idx))
}
return (
<div className='mt-8'>
<div className='mb-2'>خدمات شرکت</div>
<div className='rowTwoInput items-end gap-4'>
<div className='w-full'>
<Input
label='عنوان خدمت'
name='serviceTitle'
value={serviceTitle}
onChange={e => setServiceTitle(e.target.value)}
/>
</div>
<div className='w-full'>
<UploadBox
label='تصویر'
onChange={files => setServiceImage(files[0])}
isMultiple={false}
isReset={!serviceImage && !serviceTitle}
/>
</div>
<Button
onClick={handleAddService}
className='w-fit px-4 whitespace-nowrap'
>
<div className='flex gap-2'>
<Add size={22} color='white' />
<div>افزودن خدمت</div>
</div>
</Button>
</div>
{serviceError && <div className='text-xs text-red-600 mt-2'>{serviceError}</div>}
{/* نمایش لیست خدمات */}
<div className='mt-4 flex gap-2'>
{value.map((srv, idx) => (
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
<div className='flex items-center gap-2'>
{srv.image && <img src={URL.createObjectURL(srv.image)} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
{srv.existingImageUrl && !srv.image && <img src={srv.existingImageUrl} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
</div>
<div className='w-full text-xs'>{srv.title}</div>
<button type='button' onClick={() => handleRemoveService(idx)} className='ml-auto'>
<CloseCircle size={16} color='red' />
</button>
</div>
))}
</div>
</div>
)
}
export default CompanyServices
+26
View File
@@ -0,0 +1,26 @@
import * as api from "../service/Service";
import { useMutation } from "@tanstack/react-query";
import { CreateCompanyType } from "../types/Types";
export const useCreateCompany = () => {
return useMutation({
mutationFn: (data: CreateCompanyType) => api.createCompany(data),
});
};
export const useSingleUpload = () => {
return useMutation({
mutationFn: (variables: FormData) => api.uploadSingle(variables),
});
};
export const useMultiUpload = () => {
return useMutation({
mutationFn: (variables: FormData) => api.uploadMultiple(variables),
});
};
export const useCreateService = () => {
return useMutation({
mutationFn: (variables: CreateCompanyType) => api.createCompany(variables),
});
};
+22
View File
@@ -0,0 +1,22 @@
import axios from "../../../config/axios";
import { CreateCompanyType } from "../types/Types";
export const createCompany = async (data: CreateCompanyType) => {
const response = await axios.post("/companies/requests", data);
return response.data;
};
export const uploadSingle = async (params: FormData) => {
const { data } = await axios.post(`/uploader/single-file`, params);
return data;
};
export const uploadMultiple = async (params: FormData) => {
const { data } = await axios.post(`/uploader/multi-file`, params);
return data;
};
export const createService = async (params: CreateCompanyType) => {
const { data } = await axios.post("/companies/company-request", params);
return data;
};
+23
View File
@@ -0,0 +1,23 @@
export type CreateCompanyType = {
phone: string;
name: string;
email: string;
identificationNumber: string;
dateOfEstablishment: string;
address: string;
mapAddressLink: string;
description: string;
websiteUrl: string;
profileImageUrl: string;
coverImageUrl: string;
industryId: string;
products: {
title: string;
imageUrl: string;
}[];
services: {
title: string;
imageUrl: string;
}[];
chiefExecutiveOfficer: string;
};
+4 -2
View File
@@ -21,6 +21,7 @@ import { useEffect } from "react"
import { useGetSlugId } from "../pages/home/hooks/useHomeData"
import { ErrorType } from "../helpers/types"
import { toast } from "react-toastify"
import Request from "../pages/request/Request"
const MainRouter = () => {
const { slug } = useParams()
const getSlugId = useGetSlugId()
@@ -47,12 +48,12 @@ const MainRouter = () => {
}, [slug])
return (
<div className='p-4 overflow-hidden'>
<div className='p-4 overflow-hidden '>
<SideBar />
<Header />
<div className='flex-1 xl:ms-[269px] mt-[68px] xl:mt-[81px]'>
<div className={`hide-scroll-bar overflow-auto w-[${window.innerWidth}] max-h-[calc(100vh-113px)]`}>
<div>
<div className="pb-20">
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Home />} />
@@ -69,6 +70,7 @@ const MainRouter = () => {
<Route path="wallet" element={<Wallet />} />
<Route path="reportCard/list" element={<ReportCardList />} />
<Route path="profile" element={<Profile />} />
<Route path="request" element={<Request />} />
</Routes>
</div>
</div>