This commit is contained in:
hamid zarghami
2024-12-25 18:09:15 +03:30
parent 6d5ce71b2f
commit 75d71e11fb
10 changed files with 233 additions and 4 deletions
+13 -2
View File
@@ -11,6 +11,7 @@
"@tanstack/react-query": "^5.62.8", "@tanstack/react-query": "^5.62.8",
"formik": "^2.4.6", "formik": "^2.4.6",
"i18next": "^24.2.0", "i18next": "^24.2.0",
"iconsax-react": "^0.0.8",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-i18next": "^15.2.0", "react-i18next": "^15.2.0",
@@ -2815,6 +2816,18 @@
} }
} }
}, },
"node_modules/iconsax-react": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/iconsax-react/-/iconsax-react-0.0.8.tgz",
"integrity": "sha512-l3dVk4zGtkkJHgvNYqAf0wDKqnKxXykee5/DoESGo2JvSYwaxajJUHSX2YrPRXSov8Hd8ClGFwJxCEaEjrFD1Q==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": "*"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3107,7 +3120,6 @@
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
}, },
@@ -3589,7 +3601,6 @@
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
+1
View File
@@ -13,6 +13,7 @@
"@tanstack/react-query": "^5.62.8", "@tanstack/react-query": "^5.62.8",
"formik": "^2.4.6", "formik": "^2.4.6",
"i18next": "^24.2.0", "i18next": "^24.2.0",
"iconsax-react": "^0.0.8",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-i18next": "^15.2.0", "react-i18next": "^15.2.0",
+14
View File
@@ -4,4 +4,18 @@ export const Pages = {
register: "/auth/register", register: "/auth/register",
forgotPassword: "/auth/forgot", forgotPassword: "/auth/forgot",
}, },
dashboard: "/dashboard",
services: {
mine: "/services",
other: "/other-service",
},
transactions: "/transactions",
receipts: "/receipts",
ticket: {
list: "/tickets",
},
announcement: "/announcement",
criticisms: "/criticisms",
learning: "/learning",
setting: "/setting",
}; };
+9
View File
@@ -53,3 +53,12 @@ textarea::placeholder {
.otp div { .otp div {
width: 100%; width: 100%;
} }
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
+15
View File
@@ -39,5 +39,20 @@
}, },
"errors": { "errors": {
"required": "این فیلد اجباری می باشد" "required": "این فیلد اجباری می باشد"
},
"sidebar": {
"menu": "منو",
"mainpage": "صفحه اصلی",
"myservice": "سرویس های من",
"other_service": "سایر سرویس ها",
"receipt_list": "لیست صورتحساب",
"transactions": "تراکنش ها",
"other": "سایر",
"ticket": "تیکت ها",
"announcement": "اطلاعیه ها",
"criticisms": "انتقادات",
"learning": "آموزش",
"setting": "تنظیمات",
"logout": "خروج"
} }
} }
+6 -1
View File
@@ -1,8 +1,13 @@
import { FC } from 'react' import { FC } from 'react'
import SideBar from '../shared/SideBar'
import Header from '../shared/Header'
const MainRouter: FC = () => { const MainRouter: FC = () => {
return ( return (
<div>Main</div> <div className='p-4'>
<SideBar />
<Header />
</div>
) )
} }
+11
View File
@@ -0,0 +1,11 @@
import { FC } from 'react'
const Header: FC = () => {
return (
<div className='fixed right-[280px] top-4 h-20 bg-white rounded-[32px] w-full'>
</div>
)
}
export default Header
+124
View File
@@ -0,0 +1,124 @@
import { FC } from 'react'
import LogoImage from '../assets/images/logo.svg'
import { useTranslation } from 'react-i18next'
import { Card, DocumentText, Element3, Element4, Home2, Logout, Messages3, NotificationStatus, Receipt21, Setting2, Teacher } from 'iconsax-react'
import SideBarItem from './SideBarItem'
import { useLocation } from 'react-router-dom'
import { Pages } from '../config/Pages'
const SideBar: FC = () => {
const { t } = useTranslation('global')
const location = useLocation()
const isActive = (name: string) => {
return location.pathname.includes(name)
}
const iconSizeSideBar = 20
return (
<div className='fixed right-4 top-4 bottom-4 rounded-[32px] w-[250px] bg-white flex flex-col py-12'>
<div className='flex justify-center'>
<img src={LogoImage} className='w-[140px]' />
</div>
<div className='flex-1 h-full overflow-y-auto no-scrollbar'>
<div className='mt-10 px-12 text-header text-sm font-normal'>
{t('sidebar.menu')}
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Home2 variant={isActive('dashboard') ? 'Bold' : 'Outline'} color={isActive('dashboard') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.mainpage')}
isActive={isActive('dashboard')}
link={Pages.dashboard}
/>
<SideBarItem
icon={<Element3 variant={isActive('services') ? 'Bold' : 'Outline'} color={isActive('services') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.myservice')}
isActive={isActive('services')}
link={Pages.services.mine}
/>
<SideBarItem
icon={<Element4 variant={isActive('other-service') ? 'Bold' : 'Outline'} color={isActive('other-service') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.other_service')}
isActive={isActive('other-service')}
link={Pages.services.other}
/>
<SideBarItem
icon={<Receipt21 variant={isActive('receipts') ? 'Bold' : 'Outline'} color={isActive('receipts') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.receipt_list')}
isActive={isActive('receipts')}
link={Pages.receipts}
/>
<SideBarItem
icon={<Card variant={isActive('transactions') ? 'Bold' : 'Outline'} color={isActive('transactions') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.transactions')}
isActive={isActive('transactions')}
link={Pages.transactions}
/>
</div>
<div className='mt-10 px-12 text-header text-sm font-normal'>
{t('sidebar.other')}
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Messages3 variant={isActive('tickets') ? 'Bold' : 'Outline'} color={isActive('tickets') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.ticket')}
isActive={isActive('tickets')}
link={Pages.ticket.list}
/>
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<NotificationStatus variant={isActive('announcement') ? 'Bold' : 'Outline'} color={isActive('announcement') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.announcement')}
isActive={isActive('announcement')}
link={Pages.announcement}
/>
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<DocumentText variant={isActive('criticisms') ? 'Bold' : 'Outline'} color={isActive('criticisms') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.criticisms')}
isActive={isActive('criticisms')}
link={Pages.criticisms}
/>
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Teacher variant={isActive('learning') ? 'Bold' : 'Outline'} color={isActive('learning') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.learning')}
isActive={isActive('learning')}
link={Pages.learning}
/>
</div>
<div className='flex-1 items-end mt-14'>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Setting2 variant={isActive('setting') ? 'Bold' : 'Outline'} color={isActive('setting') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.setting')}
isActive={isActive('setting')}
link={Pages.setting}
/>
</div>
<div className='text-xs text-[#8C90A3]'>
<SideBarItem
icon={<Logout variant={isActive('logout') ? 'Bold' : 'Outline'} color={isActive('logout') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.logout')}
isActive={isActive('logout')}
link={`#`}
/>
</div>
</div>
</div>
</div>
)
}
export default SideBar
+38
View File
@@ -0,0 +1,38 @@
import { FC, ReactNode } from 'react'
import { Link } from 'react-router-dom'
import { clx } from '../helpers/utils'
import { Pages } from '../config/Pages'
type Props = {
icon: ReactNode,
title: string,
isActive: boolean,
link: string,
isLogout?: boolean,
}
const SideBarItem: FC<Props> = (props: Props) => {
const handleLogout = () => {
localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME)
window.location.href = Pages.auth.login
}
return (
<Link onClick={props.isLogout ? handleLogout : undefined} to={props.link} className='flex text-xs gap-9 mt-4'>
<div className={clx(
'w-1 bg-black h-6',
!props.isActive && 'invisible'
)}></div>
<div className='flex gap-3 items-center'>
{props.icon}
<div className={props.isActive ? 'text-black' : ''}>
{props.title}
</div>
</div>
</Link>
)
}
export default SideBarItem
+2 -1
View File
@@ -10,7 +10,8 @@ export default {
primary: 'black', primary: 'black',
secondary: '#F4F5F9', secondary: '#F4F5F9',
border: '#D0D0D0', border: '#D0D0D0',
description: '#888888' description: '#888888',
header: "#C3C7DD"
}, },
borderRadius: { borderRadius: {
'2.5': '10px' '2.5': '10px'