@@ -48,6 +48,31 @@ textarea::placeholder {
|
|||||||
--color-desc: #8c90a3;
|
--color-desc: #8c90a3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* App shell — keep Header / Sidebar / AppLayout in sync */
|
||||||
|
--layout-frame: 1rem;
|
||||||
|
--layout-sidebar-width: 240px;
|
||||||
|
--layout-main-offset: calc(var(--layout-sidebar-width) + var(--layout-frame));
|
||||||
|
--layout-header-height: 3rem;
|
||||||
|
--layout-header-height-xl: 4rem;
|
||||||
|
--layout-header-stack: calc(var(--layout-header-height) + 0.75rem);
|
||||||
|
--layout-header-stack-xl: calc(var(--layout-header-height-xl) + 1rem);
|
||||||
|
--layout-content-pad-x: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-main {
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-scrollbar {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
tbody tr:nth-child(odd) {
|
tbody tr:nth-child(odd) {
|
||||||
background-color: #f5f7fc;
|
background-color: #f5f7fc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,12 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import LogoImage from '../../assets/images/logo.svg'
|
|
||||||
import LogoSmallImage from '../../assets/images/logo-small.svg'
|
|
||||||
import CompleteProfileForm from './components/CompleteProfileForm'
|
import CompleteProfileForm from './components/CompleteProfileForm'
|
||||||
|
import AuthLayout from '@/shared/AuthLayout'
|
||||||
|
|
||||||
const CompleteProfile: FC = () => {
|
const CompleteProfile: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
<AuthLayout>
|
||||||
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
<CompleteProfileForm />
|
||||||
<div className='flex-1 min-w-[50%] overflow-y-auto bg-white lg:px-9 px-4 py-7'>
|
</AuthLayout>
|
||||||
<div className='flex-1 h-full flex flex-col'>
|
|
||||||
<div className='flex relative flex-1 flex-col h-full justify-center'>
|
|
||||||
<img src={LogoSmallImage} className='w-8 hidden xl:block' />
|
|
||||||
<img src={LogoImage} className='w-44 right-0 left-0 mx-auto absolute top-10 xl:hidden' />
|
|
||||||
<CompleteProfileForm />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex-1 min-w-[50%] lg:flex hidden justify-center items-center'>
|
|
||||||
<img src={LogoImage} className='h-20' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-31
@@ -1,44 +1,25 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import LogoImage from '../../assets/images/logo.svg'
|
|
||||||
import LogoSmallImage from '../../assets/images/logo-small.svg'
|
|
||||||
import { useAuthStore } from './store/AuthStore'
|
import { useAuthStore } from './store/AuthStore'
|
||||||
import LoginStep1 from './components/LoginStep1'
|
import LoginStep1 from './components/LoginStep1'
|
||||||
import LoginStep2 from './components/LoginStep2'
|
import LoginStep2 from './components/LoginStep2'
|
||||||
|
import AuthLayout from '@/shared/AuthLayout'
|
||||||
|
|
||||||
const Login: FC = () => {
|
const Login: FC = () => {
|
||||||
|
|
||||||
const { stepLogin, phone } = useAuthStore()
|
const { stepLogin, phone } = useAuthStore()
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-full flex justify-center lg:py-[75px] py-4 lg:items-center lg:px-10 px-4'>
|
<AuthLayout>
|
||||||
<div className='flex w-full max-h-[812px] max-w-[1200px] bg-secondary h-full rounded-3xl overflow-hidden'>
|
{
|
||||||
<div className='flex-1 min-w-[50%] overflow-y-auto bg-white lg:px-9 px-4 py-7'>
|
stepLogin === 1 ?
|
||||||
<div className='flex-1 h-full flex flex-col'>
|
<LoginStep1 />
|
||||||
|
:
|
||||||
<div className='flex relative flex-1 flex-col h-full justify-center'>
|
stepLogin === 2 && !!phone ?
|
||||||
<img src={LogoSmallImage} className='w-8 hidden xl:block' />
|
<LoginStep2 />
|
||||||
<img src={LogoImage} className='w-44 right-0 left-0 mx-auto absolute top-10 xl:hidden' />
|
: null
|
||||||
{
|
}
|
||||||
stepLogin === 1 ?
|
</AuthLayout>
|
||||||
<LoginStep1 />
|
|
||||||
:
|
|
||||||
stepLogin === 2 && !!phone ?
|
|
||||||
<LoginStep2 />
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex-1 min-w-[50%] lg:flex hidden justify-center items-center'>
|
|
||||||
<img src={LogoImage} className='h-20' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Login
|
export default Login
|
||||||
|
|||||||
+24
-34
@@ -1,8 +1,7 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { Route, Routes } from 'react-router-dom'
|
import { Route, Routes } from 'react-router-dom'
|
||||||
import Home from '../pages/home/Home'
|
import Home from '../pages/home/Home'
|
||||||
import SideBar from '../shared/Sidebar'
|
import AppLayout from '../shared/AppLayout'
|
||||||
import Header from '../shared/Header'
|
|
||||||
import { Paths } from '@/config/Paths'
|
import { Paths } from '@/config/Paths'
|
||||||
import MyRequests from '@/pages/request/MyRequests'
|
import MyRequests from '@/pages/request/MyRequests'
|
||||||
import ProformaInvoice from '@/pages/invoice/ProformaInvoice'
|
import ProformaInvoice from '@/pages/invoice/ProformaInvoice'
|
||||||
@@ -23,38 +22,29 @@ import PayInvoice from '@/pages/payment/PayInvoice'
|
|||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className='p-4 overflow-hidden'>
|
<AppLayout>
|
||||||
<Header />
|
<Routes>
|
||||||
<SideBar />
|
<Route path="/" element={<Home />} />
|
||||||
<div className='flex-1 xl:ms-[269px] mt-[68px] xl:mt-[81px]'>
|
<Route path={Paths.home} element={<Home />} />
|
||||||
<div className='overflow-auto w-full max-h-[calc(100vh-113px)] scrollbar-stable'>
|
<Route path={Paths.myRequests} element={<MyRequests />} />
|
||||||
<div className='xl:pb-20 pb-32'>
|
<Route path={Paths.order.myOrders} element={<MyOrders />} />
|
||||||
|
<Route path={Paths.proformaInvoice} element={<ProformaInvoice />} />
|
||||||
<Routes>
|
<Route path={Paths.newRequest} element={<NewRequest />} />
|
||||||
<Route path="/" element={<Home />} />
|
<Route path={`${Paths.requestDetails}:id`} element={<RequestDetail />} />
|
||||||
<Route path={Paths.home} element={<Home />} />
|
<Route path={`${Paths.orderDetails}:id`} element={<OrderDetail />} />
|
||||||
<Route path={Paths.myRequests} element={<MyRequests />} />
|
<Route path={Paths.tickets.list} element={<TicketList />} />
|
||||||
<Route path={Paths.order.myOrders} element={<MyOrders />} />
|
<Route path={Paths.tickets.create} element={<CreateTicket />} />
|
||||||
<Route path={Paths.proformaInvoice} element={<ProformaInvoice />} />
|
<Route path={`${Paths.tickets.detail}:id`} element={<TicketDetail />} />
|
||||||
<Route path={Paths.newRequest} element={<NewRequest />} />
|
<Route path={Paths.announcement.list} element={<AnnouncementList />} />
|
||||||
<Route path={`${Paths.requestDetails}:id`} element={<RequestDetail />} />
|
<Route path={`${Paths.announcement.detail}:id`} element={<AnnouncementDetail />} />
|
||||||
<Route path={`${Paths.orderDetails}:id`} element={<OrderDetail />} />
|
<Route path={Paths.criticisms} element={<AddCriticisms />} />
|
||||||
<Route path={Paths.tickets.list} element={<TicketList />} />
|
<Route path={Paths.learning.list} element={<LearningList />} />
|
||||||
<Route path={Paths.tickets.create} element={<CreateTicket />} />
|
<Route path={`${Paths.learning.detail}:id`} element={<LearningDetail />} />
|
||||||
<Route path={`${Paths.tickets.detail}:id`} element={<TicketDetail />} />
|
<Route path={`${Paths.proformaInvoice}/:id`} element={<InvoiceDetail />} />
|
||||||
<Route path={Paths.announcement.list} element={<AnnouncementList />} />
|
<Route path={`${Paths.payment.payInvoice}:id`} element={<PayInvoice />} />
|
||||||
<Route path={`${Paths.announcement.detail}:id`} element={<AnnouncementDetail />} />
|
</Routes>
|
||||||
<Route path={Paths.criticisms} element={<AddCriticisms />} />
|
</AppLayout>
|
||||||
<Route path={Paths.learning.list} element={<LearningList />} />
|
|
||||||
<Route path={`${Paths.learning.detail}:id`} element={<LearningDetail />} />
|
|
||||||
<Route path={`${Paths.proformaInvoice}/:id`} element={<InvoiceDetail />} />
|
|
||||||
<Route path={`${Paths.payment.payInvoice}:id`} element={<PayInvoice />} />
|
|
||||||
</Routes>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MainRouter
|
export default MainRouter
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { type FC, type ReactNode } from 'react'
|
||||||
|
import Header from './Header'
|
||||||
|
import SideBar from './Sidebar'
|
||||||
|
|
||||||
|
type AppLayoutProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticated app shell: fixed header + sidebar, one scrollable main column.
|
||||||
|
* Horizontal padding on the scrollport keeps content clear of the RTL scrollbar.
|
||||||
|
*/
|
||||||
|
const AppLayout: FC<AppLayoutProps> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<div className="box-border flex h-dvh flex-col overflow-hidden p-[var(--layout-frame)]">
|
||||||
|
<Header />
|
||||||
|
<SideBar />
|
||||||
|
|
||||||
|
<div className="flex min-h-0 flex-1 flex-col pt-[var(--layout-header-stack)] xl:ms-[var(--layout-main-offset)] xl:pt-[var(--layout-header-stack-xl)]">
|
||||||
|
<main className="app-main min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
||||||
|
<div className="min-w-0 px-[var(--layout-content-pad-x)] pb-28 xl:pb-16">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppLayout
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { type FC, type ReactNode } from 'react'
|
||||||
|
import LogoImage from '@/assets/images/logo.svg'
|
||||||
|
import LogoSmallImage from '@/assets/images/logo-small.svg'
|
||||||
|
|
||||||
|
type AuthLayoutProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const AuthLayout: FC<AuthLayoutProps> = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full justify-center px-4 py-4 lg:items-center lg:px-10 lg:py-[75px]">
|
||||||
|
<div className="flex h-full max-h-[812px] w-full max-w-[1200px] overflow-hidden rounded-3xl bg-secondary">
|
||||||
|
<div className="flex min-w-[50%] flex-1 flex-col overflow-y-auto bg-white px-4 py-7 lg:px-9">
|
||||||
|
<div className="relative flex h-full flex-1 flex-col justify-center">
|
||||||
|
<img src={LogoSmallImage} className="hidden w-8 xl:block" alt="" />
|
||||||
|
<img
|
||||||
|
src={LogoImage}
|
||||||
|
className="absolute top-10 right-0 left-0 mx-auto w-44 xl:hidden"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden min-w-[50%] flex-1 items-center justify-center lg:flex">
|
||||||
|
<img src={LogoImage} className="h-20" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthLayout
|
||||||
@@ -17,7 +17,7 @@ const Header: FC = () => {
|
|||||||
const initials = `${String(data?.firstName ?? '').charAt(0)}${String(data?.lastName ?? '').charAt(0)}`
|
const initials = `${String(data?.firstName ?? '').charAt(0)}${String(data?.lastName ?? '').charAt(0)}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='fixed z-10 right-4 left-4 xl:right-[285px] top-4 xl:h-16 h-12 flex items-center px-6 bg-white justify-between rounded-[32px] xl:w-[calc(100%-305px)]'>
|
<div className='fixed z-10 left-[var(--layout-frame)] right-[var(--layout-frame)] top-[var(--layout-frame)] flex h-[var(--layout-header-height)] items-center justify-between rounded-[32px] bg-white px-6 xl:left-auto xl:right-[calc(var(--layout-frame)+var(--layout-main-offset))] xl:h-[var(--layout-header-height-xl)] xl:w-[calc(100%-(var(--layout-frame)*2)-var(--layout-main-offset))]'>
|
||||||
|
|
||||||
<div className='min-w-[270px] hidden xl:block'>
|
<div className='min-w-[270px] hidden xl:block'>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -29,19 +29,19 @@ const SideBar: FC = () => {
|
|||||||
}
|
}
|
||||||
<div
|
<div
|
||||||
className={clx(
|
className={clx(
|
||||||
'fixed xl:flex translate-x-[400px] xl:translate-x-0 transition-all ease-in-out opacity-0 invisible xl:visible xl:opacity-100 xl:right-4 right-0 xl:top-4 top-0 xl:bottom-4 bottom-0 xl:rounded-[32px] w-[240px] bg-white flex-col py-12',
|
'fixed right-0 top-0 bottom-0 flex w-[var(--layout-sidebar-width)] translate-x-[400px] flex-col overflow-hidden bg-white py-12 opacity-0 invisible transition-all ease-in-out xl:right-[var(--layout-frame)] xl:top-[var(--layout-frame)] xl:bottom-[var(--layout-frame)] xl:z-40 xl:translate-x-0 xl:rounded-[32px] xl:opacity-100 xl:visible',
|
||||||
openSidebar && 'opacity-100 visible translate-x-0 block z-40'
|
openSidebar && 'visible z-40 flex translate-x-0 opacity-100'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className='px-6'>
|
<div className="shrink-0 px-6">
|
||||||
<div className='flex border-b-2 border-[#f5f7fc] pb-10'>
|
<div className='flex border-b-2 border-[#f5f7fc] pb-10'>
|
||||||
<Link to={Paths.home}>
|
<Link to={Paths.home}>
|
||||||
<img src={LogoImage} className='w-[120px]' />
|
<img src={LogoImage} className='w-[120px]' alt="" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex-1 h-full overflow-y-auto no-scrollbar px-6'>
|
<div className='min-h-0 flex-1 overflow-y-auto no-scrollbar px-6'>
|
||||||
<div className='mt-10 text-description px-6 text-header text-sm font-bold'>
|
<div className='mt-10 text-description px-6 text-header text-sm font-bold'>
|
||||||
{t('sidebar.menu')}
|
{t('sidebar.menu')}
|
||||||
</div>
|
</div>
|
||||||
@@ -133,4 +133,4 @@ const SideBar: FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SideBar
|
export default SideBar
|
||||||
|
|||||||
Reference in New Issue
Block a user