initial commit
This commit is contained in:
@@ -0,0 +1,378 @@
|
||||
// React Imports
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
import type { CSSProperties, MouseEvent, ReactNode } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Collapse from '@mui/material/Collapse'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
import {
|
||||
useFloating,
|
||||
useDismiss,
|
||||
useRole,
|
||||
useInteractions,
|
||||
useHover,
|
||||
offset,
|
||||
flip,
|
||||
size,
|
||||
autoUpdate,
|
||||
FloatingPortal,
|
||||
safePolygon,
|
||||
useTransitionStyles
|
||||
} from '@floating-ui/react'
|
||||
|
||||
// Type Imports
|
||||
import type { Mode } from '@core/types'
|
||||
|
||||
// Component Imports
|
||||
import Link from '@components/Link'
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
|
||||
type Props = {
|
||||
mode: Mode
|
||||
isBelowLgScreen: boolean
|
||||
isDrawerOpen: boolean
|
||||
setIsDrawerOpen: (open: boolean) => void
|
||||
}
|
||||
|
||||
type MenuWrapperProps = {
|
||||
children: ReactNode
|
||||
refs: any
|
||||
isBelowLgScreen: boolean
|
||||
isOpen: boolean
|
||||
getFloatingProps: any
|
||||
top: number
|
||||
floatingStyles: CSSProperties
|
||||
isMounted: boolean
|
||||
styles: CSSProperties
|
||||
}
|
||||
|
||||
// Constants
|
||||
const pageData = [
|
||||
{
|
||||
title: 'Pricing',
|
||||
href: '/pricing'
|
||||
},
|
||||
{
|
||||
title: 'Payment',
|
||||
href: '/payment'
|
||||
},
|
||||
{
|
||||
title: 'Checkout',
|
||||
href: '/checkout'
|
||||
},
|
||||
{
|
||||
title: 'Help Center',
|
||||
href: '/help-center'
|
||||
}
|
||||
]
|
||||
|
||||
const authData = [
|
||||
{
|
||||
title: 'Login (Basic)',
|
||||
href: '/login-v1'
|
||||
},
|
||||
{
|
||||
title: 'Login (Cover)',
|
||||
href: '/login-v2'
|
||||
},
|
||||
{
|
||||
title: 'Register (Basic)',
|
||||
href: '/register-v1'
|
||||
},
|
||||
{
|
||||
title: 'Register (Cover)',
|
||||
href: '/register-v2'
|
||||
},
|
||||
{
|
||||
title: 'Register (Multi-steps)',
|
||||
href: '/register-multi-steps'
|
||||
},
|
||||
{
|
||||
title: 'Forgot Password (Basic)',
|
||||
href: '/forgot-password-v1'
|
||||
},
|
||||
{
|
||||
title: 'Forgot Password (Cover)',
|
||||
href: '/forgot-password-v2'
|
||||
},
|
||||
{
|
||||
title: 'Reset Password (Basic)',
|
||||
href: '/reset-password-v1'
|
||||
},
|
||||
{
|
||||
title: 'Reset Password (Cover)',
|
||||
href: '/reset-password-v2'
|
||||
}
|
||||
]
|
||||
|
||||
const othersData = [
|
||||
{
|
||||
title: 'Under Maintenance',
|
||||
href: '/misc/under-maintenance'
|
||||
},
|
||||
{
|
||||
title: 'Coming Soon',
|
||||
href: '/misc/coming-soon'
|
||||
},
|
||||
{
|
||||
title: 'Not Authorized',
|
||||
href: '/misc/401-not-authorized'
|
||||
},
|
||||
{
|
||||
title: 'Verify Email (Basic)',
|
||||
href: '/auth/verify-email-v1'
|
||||
},
|
||||
{
|
||||
title: 'Verify Email (Cover)',
|
||||
href: '/auth/verify-email-v2'
|
||||
},
|
||||
{
|
||||
title: 'Two Steps (Basic)',
|
||||
href: '/auth/two-steps-v1'
|
||||
},
|
||||
{
|
||||
title: 'Two Steps (Cover)',
|
||||
href: '/auth/two-steps-v2'
|
||||
}
|
||||
]
|
||||
|
||||
const MenuWrapper = (props: MenuWrapperProps) => {
|
||||
// Props
|
||||
const { children, refs, isBelowLgScreen, isOpen, getFloatingProps, top, floatingStyles, isMounted, styles } = props
|
||||
|
||||
if (!isBelowLgScreen) {
|
||||
return (
|
||||
<FloatingPortal>
|
||||
{isMounted && (
|
||||
<div ref={refs.setFloating} className='z-[1201] lg:z-[11]' {...getFloatingProps()} style={floatingStyles}>
|
||||
<div
|
||||
className='flex gap-8 p-8'
|
||||
style={{
|
||||
...styles,
|
||||
overflowY: 'auto',
|
||||
background: 'var(--mui-palette-background-paper)',
|
||||
minWidth: 100,
|
||||
borderRadius: 'var(--mui-shape-borderRadius)',
|
||||
outline: 0,
|
||||
boxShadow: 'var(--mui-shadows-3)',
|
||||
maxBlockSize: `calc((var(--vh, 1vh) * 100) - ${top}px)`
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</FloatingPortal>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapse in={isOpen}>
|
||||
<div className='flex flex-col gap-6 mbs-3'>{children}</div>
|
||||
</Collapse>
|
||||
)
|
||||
}
|
||||
|
||||
const DropdownMenu = (props: Props) => {
|
||||
// Props
|
||||
const { isBelowLgScreen, isDrawerOpen, setIsDrawerOpen } = props
|
||||
|
||||
// states
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
// hooks
|
||||
const pathname = usePathname()
|
||||
|
||||
const { y, refs, floatingStyles, context } = useFloating<HTMLElement>({
|
||||
placement: 'bottom',
|
||||
open: isOpen,
|
||||
...(!isBelowLgScreen && { onOpenChange: setIsOpen }),
|
||||
whileElementsMounted: autoUpdate,
|
||||
middleware: [
|
||||
offset(14),
|
||||
flip({ padding: 10 }),
|
||||
size({
|
||||
apply({ rects, elements, availableHeight }) {
|
||||
Object.assign(elements.floating.style, {
|
||||
maxHeight: `${availableHeight}px`,
|
||||
minWidth: `${rects.reference.width}px`
|
||||
})
|
||||
},
|
||||
padding: 10
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
// Floating UI Transition Styles
|
||||
const { isMounted, styles } = useTransitionStyles(context, {
|
||||
// Configure both open and close durations:
|
||||
duration: 300,
|
||||
|
||||
initial: {
|
||||
opacity: 0,
|
||||
transform: 'translateY(10px)'
|
||||
},
|
||||
open: {
|
||||
opacity: 1,
|
||||
transform: 'translateY(0px)'
|
||||
},
|
||||
close: {
|
||||
opacity: 0,
|
||||
transform: 'translateY(10px)'
|
||||
}
|
||||
})
|
||||
|
||||
const hover = useHover(context, {
|
||||
handleClose: safePolygon({
|
||||
blockPointerEvents: true
|
||||
}),
|
||||
restMs: 25,
|
||||
delay: { open: 75 }
|
||||
})
|
||||
|
||||
const dismiss = useDismiss(context)
|
||||
const role = useRole(context, { role: 'menu' })
|
||||
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, role, hover])
|
||||
|
||||
const Tag = isBelowLgScreen ? 'div' : Fragment
|
||||
|
||||
const handleLinkClick = () => {
|
||||
if (isBelowLgScreen) {
|
||||
isDrawerOpen && setIsDrawerOpen(false)
|
||||
} else {
|
||||
setIsOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDrawerOpen && isOpen) {
|
||||
setIsOpen(false)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isDrawerOpen])
|
||||
|
||||
return (
|
||||
<Tag {...(isBelowLgScreen && { className: 'flex flex-col' })}>
|
||||
<Typography
|
||||
component={Link}
|
||||
color='text.primary'
|
||||
className={classnames('flex items-center gap-2 font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary':
|
||||
pathname === '/front-pages/payment' ||
|
||||
pathname === '/front-pages/pricing' ||
|
||||
pathname === '/front-pages/checkout' ||
|
||||
pathname === '/front-pages/help-center' ||
|
||||
pathname === '/front-pages/help-center/article/how-to-add-product-in-cart'
|
||||
})}
|
||||
{...(isBelowLgScreen
|
||||
? {
|
||||
onClick: (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
}
|
||||
: {
|
||||
ref: refs.setReference,
|
||||
...getReferenceProps()
|
||||
})}
|
||||
>
|
||||
<span>Pages</span>
|
||||
<i
|
||||
className={classnames(
|
||||
{
|
||||
'tabler-chevron-down': !isBelowLgScreen || (isBelowLgScreen && !isOpen),
|
||||
'tabler-chevron-up': isBelowLgScreen && isOpen
|
||||
},
|
||||
'text-xl'
|
||||
)}
|
||||
/>
|
||||
</Typography>
|
||||
<MenuWrapper
|
||||
refs={refs}
|
||||
isBelowLgScreen={isBelowLgScreen}
|
||||
isOpen={isOpen}
|
||||
getFloatingProps={getFloatingProps}
|
||||
top={y ? y - window.scrollY : 0}
|
||||
floatingStyles={floatingStyles}
|
||||
isMounted={isMounted}
|
||||
styles={styles}
|
||||
>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<CustomAvatar variant='rounded' color='primary' skin='light'>
|
||||
<i className='tabler-layout-grid' />
|
||||
</CustomAvatar>
|
||||
<Typography variant='h6'>Page</Typography>
|
||||
</div>
|
||||
{pageData.map((page, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={'/front-pages' + page.href}
|
||||
className={classnames('flex items-center gap-3 focus:outline-none hover:text-primary', {
|
||||
'text-primary': pathname.includes('/front-pages' + page.href)
|
||||
})}
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
<i className='tabler-circle text-[10px]' />
|
||||
<span>{page.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<CustomAvatar variant='rounded' color='primary' skin='light'>
|
||||
<i className='tabler-lock' />
|
||||
</CustomAvatar>
|
||||
<Typography variant='h6'>Auth Demo</Typography>
|
||||
</div>
|
||||
{authData.map((page, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={'/pages/auth' + page.href}
|
||||
target='_blank'
|
||||
className='flex items-center gap-3 focus:outline-none hover:text-primary'
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
<i className='tabler-circle text-[10px]' />
|
||||
<span>{page.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex items-center gap-3'>
|
||||
<CustomAvatar variant='rounded' color='primary' skin='light'>
|
||||
<i className='tabler-photo' />
|
||||
</CustomAvatar>
|
||||
<Typography variant='h6'>Auth Demo</Typography>
|
||||
</div>
|
||||
{othersData.map((page, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={'/pages' + page.href}
|
||||
target='_blank'
|
||||
className='flex items-center gap-3 focus:outline-none hover:text-primary'
|
||||
onClick={handleLinkClick}
|
||||
>
|
||||
<i className='tabler-circle text-[10px]' />
|
||||
<span>{page.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
{!isBelowLgScreen && (
|
||||
<div className='flex bg-backgroundDefault p-2 rounded'>
|
||||
<img src='/images/front-pages/dropdown-image.png' width='385' alt='dropdown image' className='rounded' />
|
||||
</div>
|
||||
)}
|
||||
</MenuWrapper>
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
|
||||
export default DropdownMenu
|
||||
@@ -0,0 +1,208 @@
|
||||
'use client'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Mode } from '@core/types'
|
||||
|
||||
// Component Imports
|
||||
import Link from '@components/Link'
|
||||
import Logo from '@components/layout/shared/Logo'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Hooks Imports
|
||||
import { useImageVariant } from '@core/hooks/useImageVariant'
|
||||
|
||||
// Util Imports
|
||||
import { frontLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
// Styles Imports
|
||||
import styles from './styles.module.css'
|
||||
import frontCommonStyles from '@views/front-pages/styles.module.css'
|
||||
|
||||
const Footer = ({ mode }: { mode: Mode }) => {
|
||||
// Vars
|
||||
const footerImageLight = '/images/front-pages/footer-bg-light.png'
|
||||
const footerImageDark = '/images/front-pages/footer-bg-dark.png'
|
||||
|
||||
// Hooks
|
||||
const dashboardImage = useImageVariant(mode, footerImageLight, footerImageDark)
|
||||
|
||||
return (
|
||||
<footer className={frontLayoutClasses.footer}>
|
||||
<div className='relative'>
|
||||
<img src={dashboardImage} alt='footer bg' className='absolute inset-0 is-full bs-full object-cover -z-[1]' />
|
||||
<div className={classnames('plb-12 text-white', frontCommonStyles.layoutSpacing)}>
|
||||
<Grid container rowSpacing={10} columnSpacing={12}>
|
||||
<Grid size={{ xs: 12, lg: 5 }}>
|
||||
<div className='flex flex-col items-start gap-6'>
|
||||
<Link href='/front-pages/landing-page'>
|
||||
<Logo color='var(--mui-palette-common-white)' />
|
||||
</Link>
|
||||
<Typography color='white' className='md:max-is-[390px] opacity-[0.78]'>
|
||||
Most Powerful & Comprehensive 🤩 React NextJS Admin Template with Elegant Material Design & Unique
|
||||
Layouts.
|
||||
</Typography>
|
||||
<div className='flex items-end'>
|
||||
<CustomTextField
|
||||
size='small'
|
||||
className={styles.inputBorder}
|
||||
label='Subscribe to newsletter'
|
||||
placeholder='Your email'
|
||||
sx={{
|
||||
'& .MuiInputBase-root': {
|
||||
borderStartEndRadius: '0 !important',
|
||||
borderEndEndRadius: '0 !important',
|
||||
'&:not(.Mui-focused)': {
|
||||
borderColor: 'rgb(var(--mui-mainColorChannels-dark) / 0.22)'
|
||||
},
|
||||
'&.MuiFilledInput-root:not(.Mui-focused):not(.Mui-disabled):hover': {
|
||||
borderColor: 'rgba(255 255 255 / 0.6) !important'
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant='contained'
|
||||
color='primary'
|
||||
sx={{
|
||||
borderStartStartRadius: 0,
|
||||
borderEndStartRadius: 0
|
||||
}}
|
||||
>
|
||||
Subscribe
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 3, lg: 2 }}>
|
||||
<Typography color='white' className='font-medium mbe-6 opacity-[0.92]'>
|
||||
Pages
|
||||
</Typography>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography component={Link} href='/front-pages/pricing' color='white' className='opacity-[0.78]'>
|
||||
Pricing
|
||||
</Typography>
|
||||
<Link href='/front-pages/payment' className='flex items-center gap-[10px]'>
|
||||
<Typography color='white' className='opacity-[0.78]'>
|
||||
Payment
|
||||
</Typography>
|
||||
<Chip label='New' color='primary' size='small' />
|
||||
</Link>
|
||||
<Typography
|
||||
component={Link}
|
||||
href='/pages/misc/under-maintenance'
|
||||
color='white'
|
||||
className='opacity-[0.78]'
|
||||
>
|
||||
Maintenance
|
||||
</Typography>
|
||||
<Typography component={Link} href='/pages/misc/coming-soon' color='white' className='opacity-[0.78]'>
|
||||
Coming Soon
|
||||
</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 3, lg: 2 }}>
|
||||
<Typography color='white' className='font-medium mbe-6 opacity-[0.92]'>
|
||||
Products
|
||||
</Typography>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography component={Link} href='/front-pages/landing-page' color='white' className='opacity-[0.78]'>
|
||||
Page builder
|
||||
</Typography>
|
||||
<Typography component={Link} href='/front-pages/landing-page' color='white' className='opacity-[0.78]'>
|
||||
Admin Dashboards
|
||||
</Typography>
|
||||
<Typography component={Link} href='/front-pages/landing-page' color='white' className='opacity-[0.78]'>
|
||||
UI Kits
|
||||
</Typography>
|
||||
<Typography component={Link} href='/front-pages/landing-page' color='white' className='opacity-[0.78]'>
|
||||
Illustrations
|
||||
</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 3 }}>
|
||||
<Typography color='white' className='font-medium mbe-6 opacity-[0.92]'>
|
||||
Download our App
|
||||
</Typography>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Link className='bg-[#282C3E] bs-[56px] is-[211px] rounded'>
|
||||
<div className='flex items-center pli-5 plb-[7px] gap-6'>
|
||||
<img src='/images/front-pages/apple-icon.png' alt='apple store' className='bs-[34px]' />
|
||||
<div className='flex flex-col items-start'>
|
||||
<Typography variant='body2' color='white' className='opacity-75'>
|
||||
Download on the
|
||||
</Typography>
|
||||
<Typography color='white' className='font-medium opacity-[0.92]'>
|
||||
App Store
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link className='bg-[#282C3E] bs-[56px] is-[211px] rounded'>
|
||||
<div className='flex items-center pli-5 plb-[7px] gap-6'>
|
||||
<img src='/images/front-pages/google-play-icon.png' alt='Google play' className='bs-[34px]' />
|
||||
<div className='flex flex-col items-start'>
|
||||
<Typography variant='body2' color='white' className='opacity-75'>
|
||||
Download on the
|
||||
</Typography>
|
||||
<Typography color='white' className='font-medium opacity-[0.92]'>
|
||||
Google Play
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
<div className='bg-[#211B2C]'>
|
||||
<div
|
||||
className={classnames(
|
||||
'flex flex-wrap items-center justify-center sm:justify-between gap-4 plb-[15px]',
|
||||
frontCommonStyles.layoutSpacing
|
||||
)}
|
||||
>
|
||||
<Typography className='text-white' variant='body2'>
|
||||
<span>{`© ${new Date().getFullYear()}, Made with `}</span>
|
||||
<span>{`❤️`}</span>
|
||||
<span>{` by `}</span>
|
||||
<Link href='https://pixinvent.com/' target='_blank' className='font-medium text-white'>
|
||||
Pixinvent
|
||||
</Link>
|
||||
</Typography>
|
||||
<div className='flex gap-1.5 items-center'>
|
||||
<IconButton component={Link} size='small' href='https://github.com/pixinvent' target='_blank'>
|
||||
<i className='tabler-brand-github-filled text-white text-lg' />
|
||||
</IconButton>
|
||||
<IconButton component={Link} size='small' href='https://www.facebook.com/pixinvents/' target='_blank'>
|
||||
<i className='tabler-brand-facebook-filled text-white text-lg' />
|
||||
</IconButton>
|
||||
<IconButton component={Link} size='small' href='https://x.com/pixinvents' target='_blank'>
|
||||
<i className='tabler-brand-twitter-filled text-white text-lg' />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
component={Link}
|
||||
size='small'
|
||||
href='https://www.youtube.com/channel/UClOcB3o1goJ293ri_Hxpklg'
|
||||
target='_blank'
|
||||
>
|
||||
<i className='tabler-brand-youtube-filled text-white text-lg' />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -0,0 +1,165 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import { usePathname } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
|
||||
// MUI Imports
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Drawer from '@mui/material/Drawer'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import type { Theme } from '@mui/material/styles'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Mode } from '@core/types'
|
||||
|
||||
// Hook Imports
|
||||
import { useIntersection } from '@/hooks/useIntersection'
|
||||
|
||||
// Component Imports
|
||||
import DropdownMenu from './DropdownMenu'
|
||||
|
||||
type Props = {
|
||||
mode: Mode
|
||||
isDrawerOpen: boolean
|
||||
setIsDrawerOpen: (open: boolean) => void
|
||||
}
|
||||
|
||||
type WrapperProps = {
|
||||
children: React.ReactNode
|
||||
isBelowLgScreen: boolean
|
||||
className?: string
|
||||
isDrawerOpen: boolean
|
||||
setIsDrawerOpen: (open: boolean) => void
|
||||
}
|
||||
|
||||
const Wrapper = (props: WrapperProps) => {
|
||||
// Props
|
||||
const { children, isBelowLgScreen, className, isDrawerOpen, setIsDrawerOpen } = props
|
||||
|
||||
if (isBelowLgScreen) {
|
||||
return (
|
||||
<Drawer
|
||||
variant='temporary'
|
||||
anchor='left'
|
||||
open={isDrawerOpen}
|
||||
onClose={() => setIsDrawerOpen(false)}
|
||||
ModalProps={{
|
||||
keepMounted: true
|
||||
}}
|
||||
sx={{ '& .MuiDrawer-paper': { width: ['100%', 300] } }}
|
||||
className={classnames('p-5', className)}
|
||||
>
|
||||
<div className='p-4 flex flex-col gap-x-3'>
|
||||
<IconButton onClick={() => setIsDrawerOpen(false)} className='absolute inline-end-4 block-start-2'>
|
||||
<i className='tabler-x' />
|
||||
</IconButton>
|
||||
{children}
|
||||
</div>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
return <div className={classnames('flex items-center flex-wrap gap-x-4 gap-y-3', className)}>{children}</div>
|
||||
}
|
||||
|
||||
const FrontMenu = (props: Props) => {
|
||||
// Props
|
||||
const { isDrawerOpen, setIsDrawerOpen, mode } = props
|
||||
|
||||
// Hooks
|
||||
const pathname = usePathname()
|
||||
const isBelowLgScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||
const { intersections } = useIntersection()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBelowLgScreen && isDrawerOpen) {
|
||||
setIsDrawerOpen(false)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isBelowLgScreen])
|
||||
|
||||
return (
|
||||
<Wrapper isBelowLgScreen={isBelowLgScreen} isDrawerOpen={isDrawerOpen} setIsDrawerOpen={setIsDrawerOpen}>
|
||||
<Typography
|
||||
color='text.primary'
|
||||
component={Link}
|
||||
href='/front-pages/landing-page'
|
||||
className={classnames('font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary':
|
||||
!intersections.features &&
|
||||
!intersections.team &&
|
||||
!intersections.faq &&
|
||||
!intersections['contact-us'] &&
|
||||
pathname === '/front-pages/landing-page'
|
||||
})}
|
||||
>
|
||||
Home
|
||||
</Typography>
|
||||
<Typography
|
||||
color='text.primary'
|
||||
component={Link}
|
||||
href='/front-pages/landing-page#features'
|
||||
className={classnames('font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary': intersections.features
|
||||
})}
|
||||
>
|
||||
Features
|
||||
</Typography>
|
||||
<Typography
|
||||
color='text.primary'
|
||||
component={Link}
|
||||
href='/front-pages/landing-page#team'
|
||||
className={classnames('font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary': intersections.team
|
||||
})}
|
||||
>
|
||||
Team
|
||||
</Typography>
|
||||
<Typography
|
||||
color='text.primary'
|
||||
component={Link}
|
||||
href='/front-pages/landing-page#faq'
|
||||
className={classnames('font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary': intersections.faq
|
||||
})}
|
||||
>
|
||||
FAQ
|
||||
</Typography>
|
||||
<Typography
|
||||
color='text.primary'
|
||||
component={Link}
|
||||
href='/front-pages/landing-page#contact-us'
|
||||
className={classnames('font-medium plb-3 pli-1.5 hover:text-primary', {
|
||||
'text-primary': intersections['contact-us']
|
||||
})}
|
||||
>
|
||||
Contact us
|
||||
</Typography>
|
||||
<DropdownMenu
|
||||
mode={mode}
|
||||
isBelowLgScreen={isBelowLgScreen}
|
||||
isDrawerOpen={isDrawerOpen}
|
||||
setIsDrawerOpen={setIsDrawerOpen}
|
||||
/>
|
||||
<Typography
|
||||
component={Link}
|
||||
color='text.primary'
|
||||
href='/'
|
||||
target='_blank'
|
||||
className='font-medium plb-3 pli-1.5 hover:text-primary'
|
||||
>
|
||||
Admin
|
||||
</Typography>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default FrontMenu
|
||||
@@ -0,0 +1,100 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
|
||||
// MUI Imports
|
||||
import Button from '@mui/material/Button'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import useScrollTrigger from '@mui/material/useScrollTrigger'
|
||||
import type { Theme } from '@mui/material/styles'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Mode } from '@core/types'
|
||||
|
||||
// Component Imports
|
||||
import Logo from '@components/layout/shared/Logo'
|
||||
import ModeDropdown from '@components/layout/shared/ModeDropdown'
|
||||
import FrontMenu from './FrontMenu'
|
||||
import CustomIconButton from '@core/components/mui/IconButton'
|
||||
|
||||
// Util Imports
|
||||
import { frontLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
// Styles Imports
|
||||
import styles from './styles.module.css'
|
||||
|
||||
const Header = ({ mode }: { mode: Mode }) => {
|
||||
// States
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
||||
|
||||
// Hooks
|
||||
const isBelowLgScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||
|
||||
// Detect window scroll
|
||||
const trigger = useScrollTrigger({
|
||||
threshold: 0,
|
||||
disableHysteresis: true
|
||||
})
|
||||
|
||||
return (
|
||||
<header className={classnames(frontLayoutClasses.header, styles.header)}>
|
||||
<div className={classnames(frontLayoutClasses.navbar, styles.navbar, { [styles.headerScrolled]: trigger })}>
|
||||
<div className={classnames(frontLayoutClasses.navbarContent, styles.navbarContent)}>
|
||||
{isBelowLgScreen ? (
|
||||
<div className='flex items-center gap-2 sm:gap-4'>
|
||||
<IconButton onClick={() => setIsDrawerOpen(true)} className='-mis-2'>
|
||||
<i className='tabler-menu-2 text-textPrimary' />
|
||||
</IconButton>
|
||||
<Link href='/front-pages/landing-page'>
|
||||
<Logo />
|
||||
</Link>
|
||||
<FrontMenu mode={mode} isDrawerOpen={isDrawerOpen} setIsDrawerOpen={setIsDrawerOpen} />
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex items-center gap-10'>
|
||||
<Link href='/front-pages/landing-page'>
|
||||
<Logo />
|
||||
</Link>
|
||||
<FrontMenu mode={mode} isDrawerOpen={isDrawerOpen} setIsDrawerOpen={setIsDrawerOpen} />
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center gap-2 sm:gap-4'>
|
||||
<ModeDropdown />
|
||||
{isBelowLgScreen ? (
|
||||
<CustomIconButton
|
||||
component={Link}
|
||||
variant='contained'
|
||||
href='https://1.envato.market/vuexy_admin'
|
||||
color='primary'
|
||||
target='_blank'
|
||||
>
|
||||
<i className='tabler-shopping-cart text-xl' />
|
||||
</CustomIconButton>
|
||||
) : (
|
||||
<Button
|
||||
component={Link}
|
||||
variant='contained'
|
||||
href='https://1.envato.market/vuexy_admin'
|
||||
startIcon={<i className='tabler-shopping-cart text-xl' />}
|
||||
className='whitespace-nowrap'
|
||||
target='_blank'
|
||||
>
|
||||
Purchase Now
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -0,0 +1,27 @@
|
||||
// Type Imports
|
||||
import type { ChildrenType } from '@core/types'
|
||||
|
||||
// Component Imports
|
||||
import Footer from '@components/layout/front-pages/Footer'
|
||||
import Header from '@components/layout/front-pages/Header'
|
||||
|
||||
// Server Action Imports
|
||||
import { getServerMode } from '@core/utils/serverHelpers'
|
||||
|
||||
// Util Imports
|
||||
import { frontLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
const FrontLayout = async ({ children }: ChildrenType) => {
|
||||
// Vars
|
||||
const mode = await getServerMode()
|
||||
|
||||
return (
|
||||
<div className={frontLayoutClasses.root}>
|
||||
<Header mode={mode} />
|
||||
{children}
|
||||
<Footer mode={mode} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FrontLayout
|
||||
@@ -0,0 +1,73 @@
|
||||
.header {
|
||||
position: sticky;
|
||||
inset-block-start: 0;
|
||||
z-index: var(--header-z-index);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
inline-size: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
inset-block-start: 0;
|
||||
inset-inline: 0;
|
||||
block-size: 100%;
|
||||
backdrop-filter: saturate(100%) blur(6px);
|
||||
}
|
||||
|
||||
.navbar {
|
||||
inline-size: calc(100% - 48px);
|
||||
padding-block: 0.438rem;
|
||||
padding-inline: 1.5rem;
|
||||
margin-inline: auto;
|
||||
margin-block-start: 1rem;
|
||||
pointer-events: auto;
|
||||
background-color: rgb(var(--mui-palette-background-paperChannel) / 0.38);
|
||||
border: 2px solid rgb(var(--mui-palette-background-paperChannel) / 0.68);
|
||||
border-radius: var(--mui-shape-borderRadius);
|
||||
|
||||
&.headerScrolled {
|
||||
background-color: var(--mui-palette-background-paper);
|
||||
border-color: var(--mui-palette-background-paper);
|
||||
box-shadow: var(--mui-customShadows-sm);
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
max-inline-size: calc(900px - 48px);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
max-inline-size: calc(1200px - 48px);
|
||||
}
|
||||
@media (min-width: 1920px) {
|
||||
max-inline-size: calc(1440px - 48px);
|
||||
}
|
||||
|
||||
.navbarContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inputBorder {
|
||||
fieldset {
|
||||
border-color: rgb(var(--mui-mainColorChannels-dark) / 0.22) !important;
|
||||
}
|
||||
label,
|
||||
input {
|
||||
color: rgb(var(--mui-mainColorChannels-dark) / 0.9) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.footerRadius {
|
||||
border-start-start-radius: 3.75rem;
|
||||
border-start-end-radius: 3.75rem;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Component Imports
|
||||
import LayoutFooter from '@layouts/components/horizontal/Footer'
|
||||
import FooterContent from './FooterContent'
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<LayoutFooter>
|
||||
<FooterContent />
|
||||
</LayoutFooter>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Hook Imports
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
// Util Imports
|
||||
import { horizontalLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
const FooterContent = () => {
|
||||
// Hooks
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(horizontalLayoutClasses.footerContent, 'flex items-center justify-between flex-wrap gap-4')}
|
||||
>
|
||||
<p>
|
||||
<span className='text-textSecondary'>{`© ${new Date().getFullYear()}, Made with `}</span>
|
||||
<span>{`❤️`}</span>
|
||||
<span className='text-textSecondary'>{` by `}</span>
|
||||
<Link href='https://pixinvent.com/' target='_blank' className='text-primary uppercase'>
|
||||
Pixinvent
|
||||
</Link>
|
||||
</p>
|
||||
{!isBreakpointReached && (
|
||||
<div className='flex items-center gap-4'>
|
||||
<Link href='https://themeforest.net/licenses/standard' target='_blank' className='text-primary'>
|
||||
License
|
||||
</Link>
|
||||
<Link href='https://themeforest.net/user/pixinvent/portfolio' target='_blank' className='text-primary'>
|
||||
More Themes
|
||||
</Link>
|
||||
<Link
|
||||
href='https://demos.pixinvent.com/vuexy-nextjs-admin-template/documentation'
|
||||
target='_blank'
|
||||
className='text-primary'
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
<Link href='https://pixinvent.ticksy.com' target='_blank' className='text-primary'>
|
||||
Support
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FooterContent
|
||||
@@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
// Type Imports
|
||||
import type { getDictionary } from '@/utils/getDictionary'
|
||||
|
||||
// Component Imports
|
||||
import Navigation from './Navigation'
|
||||
import NavbarContent from './NavbarContent'
|
||||
import Navbar from '@layouts/components/horizontal/Navbar'
|
||||
import LayoutHeader from '@layouts/components/horizontal/Header'
|
||||
|
||||
// Hook Imports
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
const Header = ({ dictionary }: { dictionary: Awaited<ReturnType<typeof getDictionary>> }) => {
|
||||
// Hooks
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
|
||||
return (
|
||||
<>
|
||||
<LayoutHeader>
|
||||
<Navbar>
|
||||
<NavbarContent />
|
||||
</Navbar>
|
||||
{!isBreakpointReached && <Navigation dictionary={dictionary} />}
|
||||
</LayoutHeader>
|
||||
{isBreakpointReached && <Navigation dictionary={dictionary} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -0,0 +1,443 @@
|
||||
// Next Imports
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
// Type Imports
|
||||
import type { getDictionary } from '@/utils/getDictionary'
|
||||
import type { VerticalMenuContextProps } from '@menu/components/vertical-menu/Menu'
|
||||
|
||||
// Component Imports
|
||||
import HorizontalNav, { Menu, SubMenu, MenuItem } from '@menu/horizontal-menu'
|
||||
import VerticalNavContent from './VerticalNavContent'
|
||||
import CustomChip from '@core/components/mui/Chip'
|
||||
|
||||
// import { GenerateHorizontalMenu } from '@components/GenerateMenu'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
|
||||
// Styled Component Imports
|
||||
import StyledHorizontalNavExpandIcon from '@menu/styles/horizontal/StyledHorizontalNavExpandIcon'
|
||||
import StyledVerticalNavExpandIcon from '@menu/styles/vertical/StyledVerticalNavExpandIcon'
|
||||
|
||||
// Style Imports
|
||||
import menuItemStyles from '@core/styles/horizontal/menuItemStyles'
|
||||
import menuRootStyles from '@core/styles/horizontal/menuRootStyles'
|
||||
import verticalNavigationCustomStyles from '@core/styles/vertical/navigationCustomStyles'
|
||||
import verticalMenuItemStyles from '@core/styles/vertical/menuItemStyles'
|
||||
import verticalMenuSectionStyles from '@core/styles/vertical/menuSectionStyles'
|
||||
|
||||
// Menu Data Imports
|
||||
// import menuData from '@/data/navigation/horizontalMenuData'
|
||||
|
||||
type RenderExpandIconProps = {
|
||||
level?: number
|
||||
}
|
||||
|
||||
type RenderVerticalExpandIconProps = {
|
||||
open?: boolean
|
||||
transitionDuration?: VerticalMenuContextProps['transitionDuration']
|
||||
}
|
||||
|
||||
const RenderExpandIcon = ({ level }: RenderExpandIconProps) => (
|
||||
<StyledHorizontalNavExpandIcon level={level}>
|
||||
<i className='tabler-chevron-right' />
|
||||
</StyledHorizontalNavExpandIcon>
|
||||
)
|
||||
|
||||
const RenderVerticalExpandIcon = ({ open, transitionDuration }: RenderVerticalExpandIconProps) => (
|
||||
<StyledVerticalNavExpandIcon open={open} transitionDuration={transitionDuration}>
|
||||
<i className='tabler-chevron-right' />
|
||||
</StyledVerticalNavExpandIcon>
|
||||
)
|
||||
|
||||
const HorizontalMenu = ({ dictionary }: { dictionary: Awaited<ReturnType<typeof getDictionary>> }) => {
|
||||
// Hooks
|
||||
const verticalNavOptions = useVerticalNav()
|
||||
const theme = useTheme()
|
||||
const params = useParams()
|
||||
|
||||
// Vars
|
||||
const { transitionDuration } = verticalNavOptions
|
||||
const { lang: locale } = params
|
||||
|
||||
return (
|
||||
<HorizontalNav
|
||||
switchToVertical
|
||||
verticalNavContent={VerticalNavContent}
|
||||
verticalNavProps={{
|
||||
customStyles: verticalNavigationCustomStyles(verticalNavOptions, theme),
|
||||
backgroundColor: 'var(--mui-palette-background-paper)'
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
rootStyles={menuRootStyles(theme)}
|
||||
renderExpandIcon={({ level }) => <RenderExpandIcon level={level} />}
|
||||
menuItemStyles={menuItemStyles(theme, 'tabler-circle')}
|
||||
renderExpandedMenuItemIcon={{ icon: <i className='tabler-circle text-xs' /> }}
|
||||
popoutMenuOffset={{
|
||||
mainAxis: ({ level }) => (level && level > 0 ? 14 : 12),
|
||||
alignmentAxis: 0
|
||||
}}
|
||||
verticalMenuProps={{
|
||||
menuItemStyles: verticalMenuItemStyles(verticalNavOptions, theme),
|
||||
renderExpandIcon: ({ open }) => (
|
||||
<RenderVerticalExpandIcon open={open} transitionDuration={transitionDuration} />
|
||||
),
|
||||
renderExpandedMenuItemIcon: { icon: <i className='tabler-circle text-xs' /> },
|
||||
menuSectionStyles: verticalMenuSectionStyles(verticalNavOptions, theme)
|
||||
}}
|
||||
>
|
||||
<SubMenu label={dictionary['navigation'].dashboards} icon={<i className='tabler-smart-home' />}>
|
||||
<MenuItem href={`/${locale}/dashboards/crm`} icon={<i className='tabler-chart-pie-2' />}>
|
||||
{dictionary['navigation'].crm}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/analytics`} icon={<i className='tabler-trending-up' />}>
|
||||
{dictionary['navigation'].analytics}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/ecommerce`} icon={<i className='tabler-shopping-cart' />}>
|
||||
{dictionary['navigation'].eCommerce}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/academy`} icon={<i className='tabler-school' />}>
|
||||
{dictionary['navigation'].academy}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/logistics`} icon={<i className='tabler-truck' />}>
|
||||
{dictionary['navigation'].logistics}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].apps} icon={<i className='tabler-mail' />}>
|
||||
<SubMenu label={dictionary['navigation'].eCommerce} icon={<i className='tabler-shopping-cart' />}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].products}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/add`}>{dictionary['navigation'].add}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/category`}>
|
||||
{dictionary['navigation'].category}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].orders}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/orders/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/ecommerce/orders/details/5434`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/ecommerce/orders/details'
|
||||
>
|
||||
{dictionary['navigation'].details}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].customers}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/customers/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/ecommerce/customers/details/879861`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/ecommerce/customers/details'
|
||||
>
|
||||
{dictionary['navigation'].details}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/manage-reviews`}>
|
||||
{dictionary['navigation'].manageReviews}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/referrals`}>{dictionary['navigation'].referrals}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/settings`}>{dictionary['navigation'].settings}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].academy} icon={<i className='tabler-school' />}>
|
||||
<MenuItem href={`/${locale}/apps/academy/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/academy/my-courses`}>{dictionary['navigation'].myCourses}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/academy/course-details`}>
|
||||
{dictionary['navigation'].courseDetails}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].logistics} icon={<i className='tabler-truck' />}>
|
||||
<MenuItem href={`/${locale}/apps/logistics/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/logistics/fleet`}>{dictionary['navigation'].fleet}</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/email`}
|
||||
icon={<i className='tabler-mail' />}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/email'
|
||||
>
|
||||
{dictionary['navigation'].email}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/chat`} icon={<i className='tabler-message-circle-2' />}>
|
||||
{dictionary['navigation'].chat}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/calendar`} icon={<i className='tabler-calendar' />}>
|
||||
{dictionary['navigation'].calendar}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/kanban`} icon={<i className='tabler-copy' />}>
|
||||
{dictionary['navigation'].kanban}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].invoice} icon={<i className='tabler-file-description' />}>
|
||||
<MenuItem href={`/${locale}/apps/invoice/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/invoice/preview/4987`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/invoice/preview'
|
||||
>
|
||||
{dictionary['navigation'].preview}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/invoice/edit/4987`} exactMatch={false} activeUrl='/apps/invoice/edit'>
|
||||
{dictionary['navigation'].edit}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/invoice/add`}>{dictionary['navigation'].add}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].user} icon={<i className='tabler-user' />}>
|
||||
<MenuItem href={`/${locale}/apps/user/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/user/view`}>{dictionary['navigation'].view}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].rolesPermissions} icon={<i className='tabler-lock' />}>
|
||||
<MenuItem href={`/${locale}/apps/roles`}>{dictionary['navigation'].roles}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/permissions`}>{dictionary['navigation'].permissions}</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].pages} icon={<i className='tabler-file' />}>
|
||||
<MenuItem href={`/${locale}/pages/user-profile`} icon={<i className='tabler-user-circle' />}>
|
||||
{dictionary['navigation'].userProfile}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/account-settings`} icon={<i className='tabler-settings' />}>
|
||||
{dictionary['navigation'].accountSettings}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/faq`} icon={<i className='tabler-help-circle' />}>
|
||||
{dictionary['navigation'].faq}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/pricing`} icon={<i className='tabler-currency-dollar' />}>
|
||||
{dictionary['navigation'].pricing}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].miscellaneous} icon={<i className='tabler-file-info' />}>
|
||||
<MenuItem href={`/${locale}/pages/misc/coming-soon`} target='_blank'>
|
||||
{dictionary['navigation'].comingSoon}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/under-maintenance`} target='_blank'>
|
||||
{dictionary['navigation'].underMaintenance}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/404-not-found`} target='_blank'>
|
||||
{dictionary['navigation'].pageNotFound404}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/401-not-authorized`} target='_blank'>
|
||||
{dictionary['navigation'].notAuthorized401}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].authPages} icon={<i className='tabler-shield-lock' />}>
|
||||
<SubMenu label={dictionary['navigation'].login}>
|
||||
<MenuItem href={`/${locale}/pages/auth/login-v1`} target='_blank'>
|
||||
{dictionary['navigation'].loginV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/login-v2`} target='_blank'>
|
||||
{dictionary['navigation'].loginV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].register}>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-v1`} target='_blank'>
|
||||
{dictionary['navigation'].registerV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-v2`} target='_blank'>
|
||||
{dictionary['navigation'].registerV2}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-multi-steps`} target='_blank'>
|
||||
{dictionary['navigation'].registerMultiSteps}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].verifyEmail}>
|
||||
<MenuItem href={`/${locale}/pages/auth/verify-email-v1`} target='_blank'>
|
||||
{dictionary['navigation'].verifyEmailV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/verify-email-v2`} target='_blank'>
|
||||
{dictionary['navigation'].verifyEmailV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].forgotPassword}>
|
||||
<MenuItem href={`/${locale}/pages/auth/forgot-password-v1`} target='_blank'>
|
||||
{dictionary['navigation'].forgotPasswordV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/forgot-password-v2`} target='_blank'>
|
||||
{dictionary['navigation'].forgotPasswordV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].resetPassword}>
|
||||
<MenuItem href={`/${locale}/pages/auth/reset-password-v1`} target='_blank'>
|
||||
{dictionary['navigation'].resetPasswordV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/reset-password-v2`} target='_blank'>
|
||||
{dictionary['navigation'].resetPasswordV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].twoSteps}>
|
||||
<MenuItem href={`/${locale}/pages/auth/two-steps-v1`} target='_blank'>
|
||||
{dictionary['navigation'].twoStepsV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/two-steps-v2`} target='_blank'>
|
||||
{dictionary['navigation'].twoStepsV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].wizardExamples} icon={<i className='tabler-dots' />}>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/checkout`}>{dictionary['navigation'].checkout}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/property-listing`}>
|
||||
{dictionary['navigation'].propertyListing}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/create-deal`}>
|
||||
{dictionary['navigation'].createDeal}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem href={`/${locale}/pages/dialog-examples`} icon={<i className='tabler-square' />}>
|
||||
{dictionary['navigation'].dialogExamples}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].widgetExamples} icon={<i className='tabler-chart-bar' />}>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/basic`}>{dictionary['navigation'].basic}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/advanced`}>{dictionary['navigation'].advanced}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/statistics`}>
|
||||
{dictionary['navigation'].statistics}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/charts`}>{dictionary['navigation'].charts}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/actions`}>{dictionary['navigation'].actions}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].frontPages} icon={<i className='tabler-files' />}>
|
||||
<MenuItem href='/front-pages/landing-page' target='_blank'>
|
||||
{dictionary['navigation'].landing}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/pricing' target='_blank'>
|
||||
{dictionary['navigation'].pricing}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/payment' target='_blank'>
|
||||
{dictionary['navigation'].payment}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/checkout' target='_blank'>
|
||||
{dictionary['navigation'].checkout}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/help-center' target='_blank'>
|
||||
{dictionary['navigation'].helpCenter}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].formsAndTables} icon={<i className='tabler-file-invoice' />}>
|
||||
<MenuItem href={`/${locale}/forms/form-layouts`} icon={<i className='tabler-layout' />}>
|
||||
{dictionary['navigation'].formLayouts}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/forms/form-validation`} icon={<i className='tabler-checkup-list' />}>
|
||||
{dictionary['navigation'].formValidation}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/forms/form-wizard`} icon={<i className='tabler-git-merge' />}>
|
||||
{dictionary['navigation'].formWizard}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/react-table`} icon={<i className='tabler-table' />}>
|
||||
{dictionary['navigation'].reactTable}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-checkbox' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/form-elements`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].formELements}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-layout-board-split' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/mui-table`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].muiTables}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].charts} icon={<i className='tabler-chart-donut-2' />}>
|
||||
<MenuItem href={`/${locale}/charts/apex-charts`} icon={<i className='tabler-chart-ppf' />}>
|
||||
{dictionary['navigation'].apex}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/charts/recharts`} icon={<i className='tabler-chart-sankey' />}>
|
||||
{dictionary['navigation'].recharts}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].others} icon={<i className='tabler-dots' />}>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-cards' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/foundation`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].foundation}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-atom' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/components`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].components}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-list-search' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/menu-examples/overview`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].menuExamples}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
href='https://pixinvent.ticksy.com'
|
||||
icon={<i className='tabler-lifebuoy' />}
|
||||
>
|
||||
{dictionary['navigation'].raiseSupport}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
icon={<i className='tabler-book-2' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}`}
|
||||
>
|
||||
{dictionary['navigation'].documentation}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
suffix={<CustomChip label='New' size='small' color='info' round='true' />}
|
||||
icon={<i className='tabler-notification' />}
|
||||
>
|
||||
{dictionary['navigation'].itemWithBadge}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-link' />}
|
||||
href='https://pixinvent.com'
|
||||
target='_blank'
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
>
|
||||
{dictionary['navigation'].externalLink}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].menuLevels} icon={<i className='tabler-menu-2' />}>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel2}</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].menuLevel2}>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel3}</MenuItem>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel3}</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<MenuItem disabled>{dictionary['navigation'].disabledMenu}</MenuItem>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
{/* <Menu
|
||||
rootStyles={menuRootStyles(theme)}
|
||||
renderExpandIcon={({ level }) => <RenderExpandIcon level={level} />}
|
||||
menuItemStyles={menuItemStyles(theme, 'tabler-circle')}
|
||||
renderExpandedMenuItemIcon={{ icon: <i className='tabler-circle text-xs' /> }}
|
||||
popoutMenuOffset={{
|
||||
mainAxis: ({ level }) => (level && level > 0 ? 14 : 12),
|
||||
alignmentAxis: 0
|
||||
}}
|
||||
verticalMenuProps={{
|
||||
menuItemStyles: verticalMenuItemStyles(verticalNavOptions, theme),
|
||||
renderExpandIcon: ({ open }) => (
|
||||
<RenderVerticalExpandIcon open={open} transitionDuration={transitionDuration} />
|
||||
),
|
||||
renderExpandedMenuItemIcon: { icon: <i className='tabler-circle text-xs' /> },
|
||||
menuSectionStyles: verticalMenuSectionStyles(verticalNavOptions, theme)
|
||||
}}
|
||||
>
|
||||
<GenerateHorizontalMenu menuData={menuData(dictionary)} />
|
||||
</Menu> */}
|
||||
</HorizontalNav>
|
||||
)
|
||||
}
|
||||
|
||||
export default HorizontalMenu
|
||||
@@ -0,0 +1,24 @@
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
const NavToggle = () => {
|
||||
// Hooks
|
||||
const { toggleVerticalNav } = useVerticalNav()
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
|
||||
// Toggle Vertical Nav
|
||||
const handleClick = () => {
|
||||
toggleVerticalNav()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <i className='tabler-menu-2 cursor-pointer' onClick={handleClick} /> */}
|
||||
{/* Comment following code and uncomment this code in order to toggle menu on desktop screens as well */}
|
||||
{isBreakpointReached && <i className='tabler-menu-2 cursor-pointer' onClick={handleClick} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavToggle
|
||||
@@ -0,0 +1,150 @@
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
import type { ShortcutsType } from '@components/layout/shared/ShortcutsDropdown'
|
||||
import type { NotificationsType } from '@components/layout/shared/NotificationsDropdown'
|
||||
|
||||
// Component Imports
|
||||
import NavToggle from './NavToggle'
|
||||
import Logo from '@components/layout/shared/Logo'
|
||||
import NavSearch from '@components/layout/shared/search'
|
||||
import LanguageDropdown from '@components/layout/shared/LanguageDropdown'
|
||||
import ModeDropdown from '@components/layout/shared/ModeDropdown'
|
||||
import ShortcutsDropdown from '@components/layout/shared/ShortcutsDropdown'
|
||||
import NotificationsDropdown from '@components/layout/shared/NotificationsDropdown'
|
||||
import UserDropdown from '@components/layout/shared/UserDropdown'
|
||||
|
||||
// Hook Imports
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
// Util Imports
|
||||
import { horizontalLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
// Vars
|
||||
const shortcuts: ShortcutsType[] = [
|
||||
{
|
||||
url: '/apps/calendar',
|
||||
icon: 'tabler-calendar',
|
||||
title: 'Calendar',
|
||||
subtitle: 'Appointments'
|
||||
},
|
||||
{
|
||||
url: '/apps/invoice/list',
|
||||
icon: 'tabler-file-dollar',
|
||||
title: 'Invoice App',
|
||||
subtitle: 'Manage Accounts'
|
||||
},
|
||||
{
|
||||
url: '/apps/user/list',
|
||||
icon: 'tabler-user',
|
||||
title: 'Users',
|
||||
subtitle: 'Manage Users'
|
||||
},
|
||||
{
|
||||
url: '/apps/roles',
|
||||
icon: 'tabler-users-group',
|
||||
title: 'Role Management',
|
||||
subtitle: 'Permissions'
|
||||
},
|
||||
{
|
||||
url: '/',
|
||||
icon: 'tabler-device-desktop-analytics',
|
||||
title: 'Dashboard',
|
||||
subtitle: 'User Dashboard'
|
||||
},
|
||||
{
|
||||
url: '/pages/account-settings',
|
||||
icon: 'tabler-settings',
|
||||
title: 'Settings',
|
||||
subtitle: 'Account Settings'
|
||||
}
|
||||
]
|
||||
|
||||
const notifications: NotificationsType[] = [
|
||||
{
|
||||
avatarImage: '/images/avatars/8.png',
|
||||
title: 'Congratulations Flora 🎉',
|
||||
subtitle: 'Won the monthly bestseller gold badge',
|
||||
time: '1h ago',
|
||||
read: false
|
||||
},
|
||||
{
|
||||
title: 'Cecilia Becker',
|
||||
avatarColor: 'secondary',
|
||||
subtitle: 'Accepted your connection',
|
||||
time: '12h ago',
|
||||
read: false
|
||||
},
|
||||
{
|
||||
avatarImage: '/images/avatars/3.png',
|
||||
title: 'Bernard Woods',
|
||||
subtitle: 'You have new message from Bernard Woods',
|
||||
time: 'May 18, 8:26 AM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarIcon: 'tabler-chart-bar',
|
||||
title: 'Monthly report generated',
|
||||
subtitle: 'July month financial report is generated',
|
||||
avatarColor: 'info',
|
||||
time: 'Apr 24, 10:30 AM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarText: 'MG',
|
||||
title: 'Application has been approved 🚀',
|
||||
subtitle: 'Your Meta Gadgets project application has been approved.',
|
||||
avatarColor: 'success',
|
||||
time: 'Feb 17, 12:17 PM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarIcon: 'tabler-mail',
|
||||
title: 'New message from Harry',
|
||||
subtitle: 'You have new message from Harry',
|
||||
avatarColor: 'error',
|
||||
time: 'Jan 6, 1:48 PM',
|
||||
read: true
|
||||
}
|
||||
]
|
||||
|
||||
const NavbarContent = () => {
|
||||
// Hooks
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(horizontalLayoutClasses.navbarContent, 'flex items-center justify-between gap-4 is-full')}
|
||||
>
|
||||
<div className='flex items-center gap-4'>
|
||||
<NavToggle />
|
||||
{/* Hide Logo on Smaller screens */}
|
||||
{!isBreakpointReached && (
|
||||
<Link href={getLocalizedUrl('/', locale as Locale)}>
|
||||
<Logo />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex items-center'>
|
||||
<NavSearch />
|
||||
<LanguageDropdown />
|
||||
<ModeDropdown />
|
||||
<ShortcutsDropdown shortcuts={shortcuts} />
|
||||
<NotificationsDropdown notifications={notifications} />
|
||||
<UserDropdown />
|
||||
{/* Language Dropdown, Notification Dropdown, quick access menu dropdown, user dropdown will be placed here */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavbarContent
|
||||
@@ -0,0 +1,69 @@
|
||||
// Third-party Imports
|
||||
import styled from '@emotion/styled'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { getDictionary } from '@/utils/getDictionary'
|
||||
|
||||
// Component Imports
|
||||
import HorizontalMenu from './HorizontalMenu'
|
||||
|
||||
// Config Imports
|
||||
import themeConfig from '@configs/themeConfig'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
// Util Imports
|
||||
import { horizontalLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
type StyledDivProps = {
|
||||
isContentCompact: boolean
|
||||
isBreakpointReached?: boolean
|
||||
}
|
||||
|
||||
const StyledDiv = styled.div<StyledDivProps>`
|
||||
${({ isContentCompact, isBreakpointReached }) =>
|
||||
!isBreakpointReached &&
|
||||
`
|
||||
padding: ${themeConfig.layoutPadding}px;
|
||||
|
||||
${
|
||||
isContentCompact &&
|
||||
`
|
||||
margin-inline: auto;
|
||||
max-inline-size: ${themeConfig.compactContentWidth}px;
|
||||
`
|
||||
}
|
||||
`}
|
||||
`
|
||||
|
||||
const Navigation = ({ dictionary }: { dictionary: Awaited<ReturnType<typeof getDictionary>> }) => {
|
||||
// Hooks
|
||||
const { settings } = useSettings()
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
|
||||
// Vars
|
||||
const headerContentCompact = settings.navbarContentWidth === 'compact'
|
||||
|
||||
return (
|
||||
<div
|
||||
{...(!isBreakpointReached && {
|
||||
className: classnames(horizontalLayoutClasses.navigation, 'relative flex border-bs')
|
||||
})}
|
||||
>
|
||||
<StyledDiv
|
||||
isContentCompact={headerContentCompact}
|
||||
isBreakpointReached={isBreakpointReached}
|
||||
{...(!isBreakpointReached && {
|
||||
className: classnames(horizontalLayoutClasses.navigationContentWrapper, 'flex items-center is-full plb-2')
|
||||
})}
|
||||
>
|
||||
<HorizontalMenu dictionary={dictionary} />
|
||||
</StyledDiv>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navigation
|
||||
@@ -0,0 +1,104 @@
|
||||
// React Imports
|
||||
import { useRef } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
// Third-party Imports
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
|
||||
// Type Imports
|
||||
import type { ChildrenType } from '@core/types'
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Component Imports
|
||||
import NavHeader from '@menu/components/vertical-menu/NavHeader'
|
||||
import Logo from '@components/layout/shared/Logo'
|
||||
import NavCollapseIcons from '@menu/components/vertical-menu/NavCollapseIcons'
|
||||
|
||||
// Hook Imports
|
||||
import useHorizontalNav from '@menu/hooks/useHorizontalNav'
|
||||
|
||||
// Util Imports
|
||||
import { mapHorizontalToVerticalMenu } from '@menu/utils/menuUtils'
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
const StyledBoxForShadow = styled('div')(({ theme }) => ({
|
||||
top: 60,
|
||||
left: -8,
|
||||
zIndex: 2,
|
||||
opacity: 0,
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
width: 'calc(100% + 15px)',
|
||||
height: theme.mixins.toolbar.minHeight,
|
||||
transition: 'opacity .15s ease-in-out',
|
||||
background: `linear-gradient(var(--mui-palette-background-paper) ${
|
||||
theme.direction === 'rtl' ? '95%' : '5%'
|
||||
}, rgb(var(--mui-palette-background-paperChannel) / 0.85) 30%, rgb(var(--mui-palette-background-paperChannel) / 0.5) 65%, rgb(var(--mui-palette-background-paperChannel) / 0.3) 75%, transparent)`,
|
||||
'&.scrolled': {
|
||||
opacity: 1
|
||||
}
|
||||
}))
|
||||
|
||||
const VerticalNavContent = ({ children }: ChildrenType) => {
|
||||
// Hooks
|
||||
const { isBreakpointReached } = useHorizontalNav()
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
// Refs
|
||||
const shadowRef = useRef(null)
|
||||
|
||||
// Vars
|
||||
const ScrollWrapper = isBreakpointReached ? 'div' : PerfectScrollbar
|
||||
|
||||
const scrollMenu = (container: any, isPerfectScrollbar: boolean) => {
|
||||
container = isBreakpointReached || !isPerfectScrollbar ? container.target : container
|
||||
|
||||
if (shadowRef && container.scrollTop > 0) {
|
||||
// @ts-ignore
|
||||
if (!shadowRef.current.classList.contains('scrolled')) {
|
||||
// @ts-ignore
|
||||
shadowRef.current.classList.add('scrolled')
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
shadowRef.current.classList.remove('scrolled')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavHeader>
|
||||
<Link href={getLocalizedUrl('/', locale as Locale)}>
|
||||
<Logo />
|
||||
</Link>
|
||||
<NavCollapseIcons
|
||||
lockedIcon={<i className='tabler-circle-dot text-xl' />}
|
||||
unlockedIcon={<i className='tabler-circle text-xl' />}
|
||||
closeIcon={<i className='tabler-x text-xl' />}
|
||||
/>
|
||||
</NavHeader>
|
||||
<StyledBoxForShadow ref={shadowRef} />
|
||||
<ScrollWrapper
|
||||
{...(isBreakpointReached
|
||||
? {
|
||||
className: 'bs-full overflow-y-auto overflow-x-hidden',
|
||||
onScroll: container => scrollMenu(container, false)
|
||||
}
|
||||
: {
|
||||
options: { wheelPropagation: false, suppressScrollX: true },
|
||||
onScrollY: container => scrollMenu(container, true)
|
||||
})}
|
||||
>
|
||||
{mapHorizontalToVerticalMenu(children)}
|
||||
</ScrollWrapper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default VerticalNavContent
|
||||
@@ -0,0 +1,117 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { usePathname, useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Popper from '@mui/material/Popper'
|
||||
import Fade from '@mui/material/Fade'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
||||
import MenuList from '@mui/material/MenuList'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
type LanguageDataType = {
|
||||
langCode: Locale
|
||||
langName: string
|
||||
}
|
||||
|
||||
const getLocalePath = (pathName: string, locale: string) => {
|
||||
if (!pathName) return '/'
|
||||
const segments = pathName.split('/')
|
||||
|
||||
segments[1] = locale
|
||||
|
||||
return segments.join('/')
|
||||
}
|
||||
|
||||
// Vars
|
||||
const languageData: LanguageDataType[] = [
|
||||
{
|
||||
langCode: 'en',
|
||||
langName: 'English'
|
||||
},
|
||||
{
|
||||
langCode: 'fr',
|
||||
langName: 'French'
|
||||
},
|
||||
{
|
||||
langCode: 'ar',
|
||||
langName: 'Arabic'
|
||||
}
|
||||
]
|
||||
|
||||
const LanguageDropdown = () => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Refs
|
||||
const anchorRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
// Hooks
|
||||
const pathName = usePathname()
|
||||
const { settings } = useSettings()
|
||||
const { lang } = useParams()
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen(prevOpen => !prevOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton ref={anchorRef} onClick={handleToggle} className='text-textPrimary'>
|
||||
<i className='tabler-language' />
|
||||
</IconButton>
|
||||
<Popper
|
||||
open={open}
|
||||
transition
|
||||
disablePortal
|
||||
placement='bottom-start'
|
||||
anchorEl={anchorRef.current}
|
||||
className='min-is-[160px] !mbs-3 z-[1]'
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Fade
|
||||
{...TransitionProps}
|
||||
style={{ transformOrigin: placement === 'bottom-start' ? 'left top' : 'right top' }}
|
||||
>
|
||||
<Paper className={settings.skin === 'bordered' ? 'border shadow-none' : 'shadow-lg'}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList onKeyDown={handleClose}>
|
||||
{languageData.map(locale => (
|
||||
<MenuItem
|
||||
key={locale.langCode}
|
||||
component={Link}
|
||||
href={getLocalePath(pathName, locale.langCode)}
|
||||
onClick={handleClose}
|
||||
selected={lang === locale.langCode}
|
||||
>
|
||||
{locale.langName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LanguageDropdown
|
||||
@@ -0,0 +1,9 @@
|
||||
import { CircularProgress } from '@mui/material'
|
||||
|
||||
export default function Loading({ size = 60 }: { size?: number }) {
|
||||
return (
|
||||
<div className='fixed inset-0 z-50 flex items-center justify-center bg-white/70'>
|
||||
<CircularProgress size={size} color='primary' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, useRef } from 'react'
|
||||
import type { CSSProperties } from 'react'
|
||||
|
||||
// Third-party Imports
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
// Type Imports
|
||||
import type { VerticalNavContextProps } from '@menu/contexts/verticalNavContext'
|
||||
|
||||
// Component Imports
|
||||
import VuexyLogo from '@core/svg/Logo'
|
||||
|
||||
// Config Imports
|
||||
import themeConfig from '@configs/themeConfig'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
type LogoTextProps = {
|
||||
isHovered?: VerticalNavContextProps['isHovered']
|
||||
isCollapsed?: VerticalNavContextProps['isCollapsed']
|
||||
transitionDuration?: VerticalNavContextProps['transitionDuration']
|
||||
isBreakpointReached?: VerticalNavContextProps['isBreakpointReached']
|
||||
color?: CSSProperties['color']
|
||||
}
|
||||
|
||||
const LogoText = styled.span<LogoTextProps>`
|
||||
color: ${({ color }) => color ?? 'var(--mui-palette-text-primary)'};
|
||||
font-size: 1.375rem;
|
||||
line-height: 1.09091;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.25px;
|
||||
transition: ${({ transitionDuration }) =>
|
||||
`margin-inline-start ${transitionDuration}ms ease-in-out, opacity ${transitionDuration}ms ease-in-out`};
|
||||
|
||||
${({ isHovered, isCollapsed, isBreakpointReached }) =>
|
||||
!isBreakpointReached && isCollapsed && !isHovered
|
||||
? 'opacity: 0; margin-inline-start: 0;'
|
||||
: 'opacity: 1; margin-inline-start: 12px;'}
|
||||
`
|
||||
|
||||
const Logo = ({ color }: { color?: CSSProperties['color'] }) => {
|
||||
// Refs
|
||||
const logoTextRef = useRef<HTMLSpanElement>(null)
|
||||
|
||||
// Hooks
|
||||
const { isHovered, transitionDuration, isBreakpointReached } = useVerticalNav()
|
||||
const { settings } = useSettings()
|
||||
|
||||
// Vars
|
||||
const { layout } = settings
|
||||
|
||||
useEffect(() => {
|
||||
if (layout !== 'collapsed') {
|
||||
return
|
||||
}
|
||||
|
||||
if (logoTextRef && logoTextRef.current) {
|
||||
if (!isBreakpointReached && layout === 'collapsed' && !isHovered) {
|
||||
logoTextRef.current?.classList.add('hidden')
|
||||
} else {
|
||||
logoTextRef.current.classList.remove('hidden')
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isHovered, layout, isBreakpointReached])
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<VuexyLogo className='text-2xl text-primary' />
|
||||
<LogoText
|
||||
color={color}
|
||||
ref={logoTextRef}
|
||||
isHovered={isHovered}
|
||||
isCollapsed={layout === 'collapsed'}
|
||||
transitionDuration={transitionDuration}
|
||||
isBreakpointReached={isBreakpointReached}
|
||||
>
|
||||
{themeConfig.templateName}
|
||||
</LogoText>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Logo
|
||||
@@ -0,0 +1,123 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Popper from '@mui/material/Popper'
|
||||
import Fade from '@mui/material/Fade'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
||||
import MenuList from '@mui/material/MenuList'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
|
||||
// Type Imports
|
||||
import type { Mode } from '@core/types'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
const ModeDropdown = () => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
const [tooltipOpen, setTooltipOpen] = useState(false)
|
||||
|
||||
// Refs
|
||||
const anchorRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
// Hooks
|
||||
const { settings, updateSettings } = useSettings()
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
setTooltipOpen(false)
|
||||
}
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen(prevOpen => !prevOpen)
|
||||
}
|
||||
|
||||
const handleModeSwitch = (mode: Mode) => {
|
||||
handleClose()
|
||||
|
||||
if (settings.mode !== mode) {
|
||||
updateSettings({ mode: mode })
|
||||
}
|
||||
}
|
||||
|
||||
const getModeIcon = () => {
|
||||
if (settings.mode === 'system') {
|
||||
return 'tabler-device-laptop'
|
||||
} else if (settings.mode === 'dark') {
|
||||
return 'tabler-moon-stars'
|
||||
} else {
|
||||
return 'tabler-sun'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
title={settings.mode + ' Mode'}
|
||||
onOpen={() => setTooltipOpen(true)}
|
||||
onClose={() => setTooltipOpen(false)}
|
||||
open={open ? false : tooltipOpen ? true : false}
|
||||
slotProps={{ popper: { className: 'capitalize' } }}
|
||||
>
|
||||
<IconButton ref={anchorRef} onClick={handleToggle} className='text-textPrimary'>
|
||||
<i className={getModeIcon()} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Popper
|
||||
open={open}
|
||||
transition
|
||||
disablePortal
|
||||
placement='bottom-start'
|
||||
anchorEl={anchorRef.current}
|
||||
className='min-is-[160px] !mbs-3 z-[1]'
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Fade
|
||||
{...TransitionProps}
|
||||
style={{ transformOrigin: placement === 'bottom-start' ? 'left top' : 'right top' }}
|
||||
>
|
||||
<Paper className={settings.skin === 'bordered' ? 'border shadow-none' : 'shadow-lg'}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList onKeyDown={handleClose}>
|
||||
<MenuItem
|
||||
className='gap-3'
|
||||
onClick={() => handleModeSwitch('light')}
|
||||
selected={settings.mode === 'light'}
|
||||
>
|
||||
<i className='tabler-sun' />
|
||||
Light
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className='gap-3'
|
||||
onClick={() => handleModeSwitch('dark')}
|
||||
selected={settings.mode === 'dark'}
|
||||
>
|
||||
<i className='tabler-moon-stars' />
|
||||
Dark
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className='gap-3'
|
||||
onClick={() => handleModeSwitch('system')}
|
||||
selected={settings.mode === 'system'}
|
||||
>
|
||||
<i className='tabler-device-laptop' />
|
||||
System
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModeDropdown
|
||||
@@ -0,0 +1,315 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useRef, useState, useEffect } from 'react'
|
||||
import type { MouseEvent, ReactNode } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Badge from '@mui/material/Badge'
|
||||
import Popper from '@mui/material/Popper'
|
||||
import Fade from '@mui/material/Fade'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import Avatar from '@mui/material/Avatar'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import Button from '@mui/material/Button'
|
||||
import type { Theme } from '@mui/material/styles'
|
||||
|
||||
// Third Party Components
|
||||
import classnames from 'classnames'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
|
||||
// Type Imports
|
||||
import type { ThemeColor } from '@core/types'
|
||||
import type { CustomAvatarProps } from '@core/components/mui/Avatar'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
|
||||
// Config Imports
|
||||
import themeConfig from '@configs/themeConfig'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
// Util Imports
|
||||
import { getInitials } from '@/utils/getInitials'
|
||||
|
||||
export type NotificationsType = {
|
||||
title: string
|
||||
subtitle: string
|
||||
time: string
|
||||
read: boolean
|
||||
} & (
|
||||
| {
|
||||
avatarImage?: string
|
||||
avatarIcon?: never
|
||||
avatarText?: never
|
||||
avatarColor?: never
|
||||
avatarSkin?: never
|
||||
}
|
||||
| {
|
||||
avatarIcon?: string
|
||||
avatarColor?: ThemeColor
|
||||
avatarSkin?: CustomAvatarProps['skin']
|
||||
avatarImage?: never
|
||||
avatarText?: never
|
||||
}
|
||||
| {
|
||||
avatarText?: string
|
||||
avatarColor?: ThemeColor
|
||||
avatarSkin?: CustomAvatarProps['skin']
|
||||
avatarImage?: never
|
||||
avatarIcon?: never
|
||||
}
|
||||
)
|
||||
|
||||
const ScrollWrapper = ({ children, hidden }: { children: ReactNode; hidden: boolean }) => {
|
||||
if (hidden) {
|
||||
return <div className='overflow-x-hidden bs-full'>{children}</div>
|
||||
} else {
|
||||
return (
|
||||
<PerfectScrollbar className='bs-full' options={{ wheelPropagation: false, suppressScrollX: true }}>
|
||||
{children}
|
||||
</PerfectScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getAvatar = (
|
||||
params: Pick<NotificationsType, 'avatarImage' | 'avatarIcon' | 'title' | 'avatarText' | 'avatarColor' | 'avatarSkin'>
|
||||
) => {
|
||||
const { avatarImage, avatarIcon, avatarText, title, avatarColor, avatarSkin } = params
|
||||
|
||||
if (avatarImage) {
|
||||
return <Avatar src={avatarImage} />
|
||||
} else if (avatarIcon) {
|
||||
return (
|
||||
<CustomAvatar color={avatarColor} skin={avatarSkin || 'light-static'}>
|
||||
<i className={avatarIcon} />
|
||||
</CustomAvatar>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<CustomAvatar color={avatarColor} skin={avatarSkin || 'light-static'}>
|
||||
{avatarText || getInitials(title)}
|
||||
</CustomAvatar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const NotificationDropdown = ({ notifications }: { notifications: NotificationsType[] }) => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
const [notificationsState, setNotificationsState] = useState(notifications)
|
||||
|
||||
// Vars
|
||||
const notificationCount = notificationsState.filter(notification => !notification.read).length
|
||||
const readAll = notificationsState.every(notification => notification.read)
|
||||
|
||||
// Refs
|
||||
const anchorRef = useRef<HTMLButtonElement>(null)
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
// Hooks
|
||||
const hidden = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||
const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'))
|
||||
const { settings } = useSettings()
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen(prevOpen => !prevOpen)
|
||||
}
|
||||
|
||||
// Read notification when notification is clicked
|
||||
const handleReadNotification = (event: MouseEvent<HTMLElement>, value: boolean, index: number) => {
|
||||
event.stopPropagation()
|
||||
const newNotifications = [...notificationsState]
|
||||
|
||||
newNotifications[index].read = value
|
||||
setNotificationsState(newNotifications)
|
||||
}
|
||||
|
||||
// Remove notification when close icon is clicked
|
||||
const handleRemoveNotification = (event: MouseEvent<HTMLElement>, index: number) => {
|
||||
event.stopPropagation()
|
||||
const newNotifications = [...notificationsState]
|
||||
|
||||
newNotifications.splice(index, 1)
|
||||
setNotificationsState(newNotifications)
|
||||
}
|
||||
|
||||
// Read or unread all notifications when read all icon is clicked
|
||||
const readAllNotifications = () => {
|
||||
const newNotifications = [...notificationsState]
|
||||
|
||||
newNotifications.forEach(notification => {
|
||||
notification.read = !readAll
|
||||
})
|
||||
setNotificationsState(newNotifications)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const adjustPopoverHeight = () => {
|
||||
if (ref.current) {
|
||||
// Calculate available height, subtracting any fixed UI elements' height as necessary
|
||||
const availableHeight = window.innerHeight - 100
|
||||
|
||||
ref.current.style.height = `${Math.min(availableHeight, 550)}px`
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', adjustPopoverHeight)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton ref={anchorRef} onClick={handleToggle} className='text-textPrimary'>
|
||||
<Badge
|
||||
color='error'
|
||||
className='cursor-pointer'
|
||||
variant='dot'
|
||||
overlap='circular'
|
||||
invisible={notificationCount === 0}
|
||||
sx={{
|
||||
'& .MuiBadge-dot': { top: 6, right: 5, boxShadow: 'var(--mui-palette-background-paper) 0px 0px 0px 2px' }
|
||||
}}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
>
|
||||
<i className='tabler-bell' />
|
||||
</Badge>
|
||||
</IconButton>
|
||||
<Popper
|
||||
open={open}
|
||||
transition
|
||||
disablePortal
|
||||
placement='bottom-end'
|
||||
ref={ref}
|
||||
anchorEl={anchorRef.current}
|
||||
{...(isSmallScreen
|
||||
? {
|
||||
className: 'is-full !mbs-3 z-[1] max-bs-[550px] bs-[550px]',
|
||||
modifiers: [
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
padding: themeConfig.layoutPadding
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
: { className: 'is-96 !mbs-3 z-[1] max-bs-[550px] bs-[550px]' })}
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Fade {...TransitionProps} style={{ transformOrigin: placement === 'bottom-end' ? 'right top' : 'left top' }}>
|
||||
<Paper className={classnames('bs-full', settings.skin === 'bordered' ? 'border shadow-none' : 'shadow-lg')}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<div className='bs-full flex flex-col'>
|
||||
<div className='flex items-center justify-between plb-3.5 pli-4 is-full gap-2'>
|
||||
<Typography variant='h6' className='flex-auto'>
|
||||
Notifications
|
||||
</Typography>
|
||||
{notificationCount > 0 && (
|
||||
<Chip size='small' variant='tonal' color='primary' label={`${notificationCount} New`} />
|
||||
)}
|
||||
<Tooltip
|
||||
title={readAll ? 'Mark all as unread' : 'Mark all as read'}
|
||||
placement={placement === 'bottom-end' ? 'left' : 'right'}
|
||||
slotProps={{
|
||||
popper: {
|
||||
sx: {
|
||||
'& .MuiTooltip-tooltip': {
|
||||
transformOrigin:
|
||||
placement === 'bottom-end' ? 'right center !important' : 'right center !important'
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{notificationsState.length > 0 ? (
|
||||
<IconButton size='small' onClick={() => readAllNotifications()} className='text-textPrimary'>
|
||||
<i className={readAll ? 'tabler-mail' : 'tabler-mail-opened'} />
|
||||
</IconButton>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Divider />
|
||||
<ScrollWrapper hidden={hidden}>
|
||||
{notificationsState.map((notification, index) => {
|
||||
const {
|
||||
title,
|
||||
subtitle,
|
||||
time,
|
||||
read,
|
||||
avatarImage,
|
||||
avatarIcon,
|
||||
avatarText,
|
||||
avatarColor,
|
||||
avatarSkin
|
||||
} = notification
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={classnames('flex plb-3 pli-4 gap-3 cursor-pointer hover:bg-actionHover group', {
|
||||
'border-be': index !== notificationsState.length - 1
|
||||
})}
|
||||
onClick={e => handleReadNotification(e, true, index)}
|
||||
>
|
||||
{getAvatar({ avatarImage, avatarIcon, title, avatarText, avatarColor, avatarSkin })}
|
||||
<div className='flex flex-col flex-auto'>
|
||||
<Typography variant='body2' className='font-medium mbe-1' color='text.primary'>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant='caption' color='text.secondary' className='mbe-2'>
|
||||
{subtitle}
|
||||
</Typography>
|
||||
<Typography variant='caption' color='text.disabled'>
|
||||
{time}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className='flex flex-col items-end gap-2'>
|
||||
<Badge
|
||||
variant='dot'
|
||||
color={read ? 'secondary' : 'primary'}
|
||||
onClick={e => handleReadNotification(e, !read, index)}
|
||||
className={classnames('mbs-1 mie-1', {
|
||||
'invisible group-hover:visible': read
|
||||
})}
|
||||
/>
|
||||
<i
|
||||
className='tabler-x text-xl invisible group-hover:visible'
|
||||
onClick={e => handleRemoveNotification(e, index)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</ScrollWrapper>
|
||||
<Divider />
|
||||
<div className='p-4'>
|
||||
<Button fullWidth variant='contained' size='small'>
|
||||
View All Notifications
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationDropdown
|
||||
@@ -0,0 +1,189 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useCallback, useRef, useState, useEffect } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Popper from '@mui/material/Popper'
|
||||
import Fade from '@mui/material/Fade'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import type { Theme } from '@mui/material/styles'
|
||||
|
||||
// Third Party Components
|
||||
import classnames from 'classnames'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
|
||||
// Config Imports
|
||||
import themeConfig from '@configs/themeConfig'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
export type ShortcutsType = {
|
||||
url: string
|
||||
icon: string
|
||||
title: string
|
||||
subtitle: string
|
||||
}
|
||||
|
||||
const ScrollWrapper = ({ children, hidden }: { children: ReactNode; hidden: boolean }) => {
|
||||
if (hidden) {
|
||||
return <div className='overflow-x-hidden bs-full'>{children}</div>
|
||||
} else {
|
||||
return (
|
||||
<PerfectScrollbar className='bs-full' options={{ wheelPropagation: false, suppressScrollX: true }}>
|
||||
{children}
|
||||
</PerfectScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const ShortcutsDropdown = ({ shortcuts }: { shortcuts: ShortcutsType[] }) => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Refs
|
||||
const anchorRef = useRef<HTMLButtonElement>(null)
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
// Hooks
|
||||
const hidden = useMediaQuery((theme: Theme) => theme.breakpoints.down('lg'))
|
||||
const isSmallScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'))
|
||||
const { settings } = useSettings()
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false)
|
||||
}, [])
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
setOpen(prevOpen => !prevOpen)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const adjustPopoverHeight = () => {
|
||||
if (ref.current) {
|
||||
// Calculate available height, subtracting any fixed UI elements' height as necessary
|
||||
const availableHeight = window.innerHeight - 100
|
||||
|
||||
ref.current.style.height = `${Math.min(availableHeight, 550)}px`
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', adjustPopoverHeight)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton ref={anchorRef} onClick={handleToggle} className='text-textPrimary'>
|
||||
<i className='tabler-layout-grid-add' />
|
||||
</IconButton>
|
||||
<Popper
|
||||
open={open}
|
||||
transition
|
||||
disablePortal
|
||||
placement='bottom-end'
|
||||
ref={ref}
|
||||
anchorEl={anchorRef.current}
|
||||
{...(isSmallScreen
|
||||
? {
|
||||
className: 'is-full !mbs-3 z-[1] max-bs-[517px]',
|
||||
modifiers: [
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
padding: themeConfig.layoutPadding
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
: { className: 'is-96 !mbs-3 z-[1] max-bs-[517px]' })}
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Fade {...TransitionProps} style={{ transformOrigin: placement === 'bottom-end' ? 'right top' : 'left top' }}>
|
||||
<Paper className={classnames('bs-full', settings.skin === 'bordered' ? 'border shadow-none' : 'shadow-lg')}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<div className='bs-full flex flex-col'>
|
||||
<div className='flex items-center justify-between plb-3.5 pli-4 is-full gap-2'>
|
||||
<Typography variant='h6' className='flex-auto'>
|
||||
Shortcuts
|
||||
</Typography>
|
||||
<Tooltip
|
||||
title='Add Shortcut'
|
||||
placement={placement === 'bottom-end' ? 'left' : 'right'}
|
||||
slotProps={{
|
||||
popper: {
|
||||
sx: {
|
||||
'& .MuiTooltip-tooltip': {
|
||||
transformOrigin:
|
||||
placement === 'bottom-end' ? 'right center !important' : 'right center !important'
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconButton size='small' className='text-textPrimary'>
|
||||
<i className='tabler-plus' />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Divider />
|
||||
<ScrollWrapper hidden={hidden}>
|
||||
<Grid container>
|
||||
{shortcuts.map((shortcut, index) => (
|
||||
<Grid
|
||||
size={{ xs: 6 }}
|
||||
key={index}
|
||||
onClick={handleClose}
|
||||
className='[&:not(:last-of-type):not(:nth-last-of-type(2))]:border-be odd:border-ie'
|
||||
>
|
||||
<Link
|
||||
href={getLocalizedUrl(shortcut.url, locale as Locale)}
|
||||
className='flex items-center flex-col p-6 gap-3 bs-full hover:bg-actionHover'
|
||||
>
|
||||
<CustomAvatar size={50} className='bg-actionSelected text-textPrimary'>
|
||||
<i className={classnames('text-[1.625rem]', shortcut.icon)} />
|
||||
</CustomAvatar>
|
||||
<div className='flex flex-col items-center text-center'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{shortcut.title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{shortcut.subtitle}</Typography>
|
||||
</div>
|
||||
</Link>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</ScrollWrapper>
|
||||
</div>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShortcutsDropdown
|
||||
@@ -0,0 +1,172 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useRef, useState } from 'react'
|
||||
import type { MouseEvent } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Badge from '@mui/material/Badge'
|
||||
import Avatar from '@mui/material/Avatar'
|
||||
import Popper from '@mui/material/Popper'
|
||||
import Fade from '@mui/material/Fade'
|
||||
import Paper from '@mui/material/Paper'
|
||||
import ClickAwayListener from '@mui/material/ClickAwayListener'
|
||||
import MenuList from '@mui/material/MenuList'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Hook Imports
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
import { useAuthMutation } from '../../../services/mutations/auth'
|
||||
|
||||
// Styled component for badge content
|
||||
const BadgeContentSpan = styled('span')({
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: 'var(--mui-palette-success-main)',
|
||||
boxShadow: '0 0 0 2px var(--mui-palette-background-paper)'
|
||||
})
|
||||
|
||||
const UserDropdown = () => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Refs
|
||||
const anchorRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const { mutate } = useAuthMutation.logout()
|
||||
|
||||
// Hooks
|
||||
const router = useRouter()
|
||||
const { settings } = useSettings()
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
const handleDropdownOpen = () => {
|
||||
!open ? setOpen(true) : setOpen(false)
|
||||
}
|
||||
|
||||
const handleDropdownClose = (event?: MouseEvent<HTMLLIElement> | (MouseEvent | TouchEvent), url?: string) => {
|
||||
if (url) {
|
||||
router.push(getLocalizedUrl(url, locale as Locale))
|
||||
}
|
||||
|
||||
if (anchorRef.current && anchorRef.current.contains(event?.target as HTMLElement)) {
|
||||
return
|
||||
}
|
||||
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
const handleUserLogout = async () => {
|
||||
try {
|
||||
// Sign out from the app
|
||||
// await signOut({ callbackUrl: process.env.NEXT_PUBLIC_APP_URL })
|
||||
mutate()
|
||||
router.push(getLocalizedUrl('/login', locale as Locale))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
// Show above error in a toast like following
|
||||
// toastService.error((err as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Badge
|
||||
ref={anchorRef}
|
||||
overlap='circular'
|
||||
badgeContent={<BadgeContentSpan onClick={handleDropdownOpen} />}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||||
className='mis-2'
|
||||
>
|
||||
<Avatar
|
||||
ref={anchorRef}
|
||||
alt={'John doe'}
|
||||
src={''}
|
||||
onClick={handleDropdownOpen}
|
||||
className='cursor-pointer bs-[38px] is-[38px]'
|
||||
/>
|
||||
</Badge>
|
||||
<Popper
|
||||
open={open}
|
||||
transition
|
||||
disablePortal
|
||||
placement='bottom-end'
|
||||
anchorEl={anchorRef.current}
|
||||
className='min-is-[240px] !mbs-3 z-[1]'
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Fade
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin: placement === 'bottom-end' ? 'right top' : 'left top'
|
||||
}}
|
||||
>
|
||||
<Paper className={settings.skin === 'bordered' ? 'border shadow-none' : 'shadow-lg'}>
|
||||
<ClickAwayListener onClickAway={e => handleDropdownClose(e as MouseEvent | TouchEvent)}>
|
||||
<MenuList>
|
||||
<div className='flex items-center plb-2 pli-6 gap-2' tabIndex={-1}>
|
||||
<Avatar alt={'john doe'} src={''} />
|
||||
<div className='flex items-start flex-col'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{'john doe'}
|
||||
</Typography>
|
||||
<Typography variant='caption'>{'email'}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<Divider className='mlb-1' />
|
||||
<MenuItem className='mli-2 gap-3' onClick={e => handleDropdownClose(e, '/pages/user-profile')}>
|
||||
<i className='tabler-user' />
|
||||
<Typography color='text.primary'>My Profile</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem className='mli-2 gap-3' onClick={e => handleDropdownClose(e, '/pages/account-settings')}>
|
||||
<i className='tabler-settings' />
|
||||
<Typography color='text.primary'>Settings</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem className='mli-2 gap-3' onClick={e => handleDropdownClose(e, '/pages/pricing')}>
|
||||
<i className='tabler-currency-dollar' />
|
||||
<Typography color='text.primary'>Pricing</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem className='mli-2 gap-3' onClick={e => handleDropdownClose(e, '/pages/faq')}>
|
||||
<i className='tabler-help-circle' />
|
||||
<Typography color='text.primary'>FAQ</Typography>
|
||||
</MenuItem>
|
||||
<div className='flex items-center plb-2 pli-3'>
|
||||
<Button
|
||||
fullWidth
|
||||
variant='contained'
|
||||
color='error'
|
||||
size='small'
|
||||
endIcon={<i className='tabler-logout' />}
|
||||
onClick={handleUserLogout}
|
||||
sx={{ '& .MuiButton-endIcon': { marginInlineStart: 1.5 } }}
|
||||
>
|
||||
Logout
|
||||
</Button>
|
||||
</div>
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
</Popper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserDropdown
|
||||
@@ -0,0 +1,160 @@
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
type DefaultSuggestionsType = {
|
||||
sectionLabel: string
|
||||
items: {
|
||||
label: string
|
||||
href: string
|
||||
icon?: string
|
||||
}[]
|
||||
}
|
||||
|
||||
const defaultSuggestions: DefaultSuggestionsType[] = [
|
||||
{
|
||||
sectionLabel: 'Popular Searches',
|
||||
items: [
|
||||
{
|
||||
label: 'Analytics',
|
||||
href: '/dashboards/analytics',
|
||||
icon: 'tabler-trending-up'
|
||||
},
|
||||
{
|
||||
label: 'CRM',
|
||||
href: '/dashboards/crm',
|
||||
icon: 'tabler-chart-pie-2'
|
||||
},
|
||||
{
|
||||
label: 'eCommerce',
|
||||
href: '/dashboards/ecommerce',
|
||||
icon: 'tabler-shopping-cart'
|
||||
},
|
||||
{
|
||||
label: 'User List',
|
||||
href: '/apps/user/list',
|
||||
icon: 'tabler-file-description'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
sectionLabel: 'Apps',
|
||||
items: [
|
||||
{
|
||||
label: 'Calendar',
|
||||
href: '/apps/calendar',
|
||||
icon: 'tabler-calendar'
|
||||
},
|
||||
{
|
||||
label: 'Invoice List',
|
||||
href: '/apps/invoice/list',
|
||||
icon: 'tabler-file-info'
|
||||
},
|
||||
{
|
||||
label: 'User List',
|
||||
href: '/apps/user/list',
|
||||
icon: 'tabler-file-invoice'
|
||||
},
|
||||
{
|
||||
label: 'Roles & Permissions',
|
||||
href: '/apps/roles',
|
||||
icon: 'tabler-lock'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
sectionLabel: 'Pages',
|
||||
items: [
|
||||
{
|
||||
label: 'User Profile',
|
||||
href: '/pages/user-profile',
|
||||
icon: 'tabler-user'
|
||||
},
|
||||
{
|
||||
label: 'Account Settings',
|
||||
href: '/pages/account-settings',
|
||||
icon: 'tabler-settings'
|
||||
},
|
||||
{
|
||||
label: 'Pricing',
|
||||
href: '/pages/pricing',
|
||||
icon: 'tabler-currency-dollar'
|
||||
},
|
||||
{
|
||||
label: 'FAQ',
|
||||
href: '/pages/faq',
|
||||
icon: 'tabler-help-circle'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
sectionLabel: 'Forms & Charts',
|
||||
items: [
|
||||
{
|
||||
label: 'Form Layouts',
|
||||
href: '/forms/form-layouts',
|
||||
icon: 'tabler-layout'
|
||||
},
|
||||
{
|
||||
label: 'Form Validation',
|
||||
href: '/forms/form-validation',
|
||||
icon: 'tabler-checkup-list'
|
||||
},
|
||||
{
|
||||
label: 'Form Wizard',
|
||||
href: '/forms/form-wizard',
|
||||
icon: 'tabler-git-merge'
|
||||
},
|
||||
{
|
||||
label: 'Apex Charts',
|
||||
href: '/charts/apex-charts',
|
||||
icon: 'tabler-chart-ppf'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const DefaultSuggestions = ({ setOpen }: { setOpen: (value: boolean) => void }) => {
|
||||
// Hooks
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
return (
|
||||
<div className='flex grow flex-wrap gap-x-[48px] gap-y-8 plb-14 pli-16 overflow-y-auto overflow-x-hidden bs-full'>
|
||||
{defaultSuggestions.map((section, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='flex flex-col justify-center overflow-x-hidden gap-4 basis-full sm:basis-[calc((100%-3rem)/2)]'
|
||||
>
|
||||
<p className='text-xs leading-[1.16667] uppercase text-textDisabled tracking-[0.8px]'>
|
||||
{section.sectionLabel}
|
||||
</p>
|
||||
<ul className='flex flex-col gap-4'>
|
||||
{section.items.map((item, i) => (
|
||||
<li key={i} className='flex'>
|
||||
<Link
|
||||
href={getLocalizedUrl(item.href, locale as Locale)}
|
||||
className='flex items-center overflow-x-hidden cursor-pointer gap-2 hover:text-primary focus-visible:text-primary focus-visible:outline-0'
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
{item.icon && <i className={classnames(item.icon, 'flex text-xl')} />}
|
||||
<p className='text-[15px] leading-[1.4667] truncate'>{item.label}</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DefaultSuggestions
|
||||
@@ -0,0 +1,67 @@
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
type NoResultData = {
|
||||
label: string
|
||||
href: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
const noResultData: NoResultData[] = [
|
||||
{
|
||||
label: 'Analytics',
|
||||
href: '/dashboards/analytics',
|
||||
icon: 'tabler-chart-pie-2'
|
||||
},
|
||||
{
|
||||
label: 'User Profile',
|
||||
href: '/pages/user-profile',
|
||||
icon: 'tabler-user'
|
||||
},
|
||||
{
|
||||
label: 'CRM',
|
||||
href: '/dashboards/crm',
|
||||
icon: 'tabler-chart-donut-3'
|
||||
}
|
||||
]
|
||||
|
||||
const NoResult = ({ searchValue, setOpen }: { searchValue: string; setOpen: (value: boolean) => void }) => {
|
||||
// Hooks
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-center grow flex-wrap plb-14 pli-16 overflow-y-auto overflow-x-hidden bs-full'>
|
||||
<div className='flex flex-col items-center'>
|
||||
<i className='tabler-file-alert text-[64px] mbe-2.5' />
|
||||
<p className='text-lg font-medium leading-[1.55556] mbe-11'>{`No result for "${searchValue}"`}</p>
|
||||
<p className='text-[15px] leading-[1.4667] mbe-4 text-textDisabled'>Try searching for</p>
|
||||
<ul className='flex flex-col self-start gap-[18px]'>
|
||||
{noResultData.map((item, index) => (
|
||||
<li key={index} className='flex items-center'>
|
||||
<Link
|
||||
href={getLocalizedUrl(item.href, locale as Locale)}
|
||||
className='flex items-center gap-2 hover:text-primary focus-visible:text-primary focus-visible:outline-0'
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<i className={classnames(item.icon, 'text-xl')} />
|
||||
<p className='text-[15px] leading-[1.4667] truncate'>{item.label}</p>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NoResult
|
||||
@@ -0,0 +1,273 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import { useParams, useRouter, usePathname } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from 'cmdk'
|
||||
import { Title, Description } from '@radix-ui/react-dialog'
|
||||
|
||||
// Type Imports
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Component Imports
|
||||
import DefaultSuggestions from './DefaultSuggestions'
|
||||
import NoResult from './NoResult'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
// Style Imports
|
||||
import './styles.css'
|
||||
|
||||
// Data Imports
|
||||
import data from '@/data/searchData'
|
||||
|
||||
type Item = {
|
||||
id: string
|
||||
name: string
|
||||
url: string
|
||||
excludeLang?: boolean
|
||||
icon: string
|
||||
shortcut?: string
|
||||
}
|
||||
|
||||
type Section = {
|
||||
title: string
|
||||
items: Item[]
|
||||
}
|
||||
|
||||
type SearchItemProps = {
|
||||
children: ReactNode
|
||||
shortcut?: string
|
||||
value: string
|
||||
url: string
|
||||
currentPath: string
|
||||
onSelect?: () => void
|
||||
}
|
||||
|
||||
// Transform the data to group items by their sections
|
||||
const transformedData = data.reduce((acc: Section[], item) => {
|
||||
const existingSection = acc.find(section => section.title === item.section)
|
||||
|
||||
const newItem = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
excludeLang: item.excludeLang,
|
||||
icon: item.icon,
|
||||
shortcut: item.shortcut
|
||||
}
|
||||
|
||||
if (existingSection) {
|
||||
existingSection.items.push(newItem)
|
||||
} else {
|
||||
acc.push({ title: item.section, items: [newItem] })
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
// SearchItem Component for introduce the shortcut keys
|
||||
const SearchItem = ({ children, shortcut, value, currentPath, url, onSelect = () => {} }: SearchItemProps) => {
|
||||
return (
|
||||
<CommandItem
|
||||
onSelect={onSelect}
|
||||
value={value}
|
||||
className={classnames('mli-2 mbe-px last:mbe-0 rounded', {
|
||||
'active-searchItem': currentPath === url
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
{shortcut && (
|
||||
<div cmdk-vercel-shortcuts=''>
|
||||
{shortcut.split(' ').map(key => {
|
||||
return <kbd key={key}>{key}</kbd>
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</CommandItem>
|
||||
)
|
||||
}
|
||||
|
||||
// Helper function to filter and limit results per section based on the number of sections
|
||||
const getFilteredResults = (sections: Section[]) => {
|
||||
const limit = sections.length > 1 ? 3 : 5
|
||||
|
||||
return sections.map(section => ({
|
||||
...section,
|
||||
items: section.items.slice(0, limit)
|
||||
}))
|
||||
}
|
||||
|
||||
// Footer component for the search menu
|
||||
const CommandFooter = () => {
|
||||
return (
|
||||
<div cmdk-footer=''>
|
||||
<div className='flex items-center gap-1'>
|
||||
<kbd>
|
||||
<i className='tabler-arrow-up text-base' />
|
||||
</kbd>
|
||||
<kbd>
|
||||
<i className='tabler-arrow-down text-base' />
|
||||
</kbd>
|
||||
<span>to navigate</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<kbd>
|
||||
<i className='tabler-corner-down-left text-base' />
|
||||
</kbd>
|
||||
<span>to open</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<kbd>esc</kbd>
|
||||
<span>to close</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const NavSearch = () => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
|
||||
// Hooks
|
||||
const router = useRouter()
|
||||
const pathName = usePathname()
|
||||
const { settings } = useSettings()
|
||||
const { lang: locale } = useParams()
|
||||
const { isBreakpointReached } = useVerticalNav()
|
||||
|
||||
// When an item is selected from the search results
|
||||
const onSearchItemSelect = (item: Item) => {
|
||||
item.url.startsWith('http')
|
||||
? window.open(item.url, '_blank')
|
||||
: router.push(item.excludeLang ? item.url : getLocalizedUrl(item.url, locale as Locale))
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
// Filter the data based on the search query
|
||||
const filteredData = (sections: Section[], query: string) => {
|
||||
const searchQuery = query.trim().toLowerCase()
|
||||
|
||||
return sections
|
||||
.filter(section => {
|
||||
const sectionMatches = section.title.toLowerCase().includes(searchQuery)
|
||||
|
||||
const itemsMatch = section.items.some(
|
||||
item =>
|
||||
item.name.toLowerCase().includes(searchQuery) ||
|
||||
(item.shortcut && item.shortcut.toLowerCase().includes(searchQuery))
|
||||
)
|
||||
|
||||
return sectionMatches || itemsMatch
|
||||
})
|
||||
.map(section => ({
|
||||
...section,
|
||||
items: section.items.filter(
|
||||
item =>
|
||||
section.title.toLowerCase().includes(searchQuery) ||
|
||||
item.name.toLowerCase().includes(searchQuery) ||
|
||||
(item.shortcut && item.shortcut.toLowerCase().includes(searchQuery))
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
const limitedData = getFilteredResults(filteredData(transformedData, searchValue))
|
||||
|
||||
// Toggle the menu when ⌘K is pressed
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
setOpen(open => !open)
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', down)
|
||||
|
||||
return () => document.removeEventListener('keydown', down)
|
||||
}, [])
|
||||
|
||||
// Reset the search value when the menu is closed
|
||||
useEffect(() => {
|
||||
if (!open && searchValue !== '') {
|
||||
setSearchValue('')
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<>
|
||||
{isBreakpointReached || settings.layout === 'horizontal' ? (
|
||||
<IconButton className='text-textPrimary' onClick={() => setOpen(true)}>
|
||||
<i className='tabler-search text-2xl' />
|
||||
</IconButton>
|
||||
) : (
|
||||
<div className='flex items-center gap-2 cursor-pointer' onClick={() => setOpen(true)}>
|
||||
<IconButton className='text-textPrimary' onClick={() => setOpen(true)}>
|
||||
<i className='tabler-search text-2xl' />
|
||||
</IconButton>
|
||||
<div className='whitespace-nowrap select-none text-textDisabled'>Search ⌘K</div>
|
||||
</div>
|
||||
)}
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<div className='flex items-center justify-between border-be pli-4 plb-3 gap-2'>
|
||||
<Title hidden />
|
||||
<Description hidden />
|
||||
<i className='tabler-search' />
|
||||
<CommandInput value={searchValue} onValueChange={setSearchValue} />
|
||||
<span className='text-textDisabled'>[esc]</span>
|
||||
<i className='tabler-x cursor-pointer' onClick={() => setOpen(false)} />
|
||||
</div>
|
||||
<CommandList>
|
||||
{searchValue ? (
|
||||
limitedData.length > 0 ? (
|
||||
limitedData.map((section, index) => (
|
||||
<CommandGroup key={index} heading={section.title.toUpperCase()} className='text-xs'>
|
||||
{section.items.map((item, index) => {
|
||||
return (
|
||||
<SearchItem
|
||||
shortcut={item.shortcut}
|
||||
key={index}
|
||||
currentPath={pathName}
|
||||
url={getLocalizedUrl(item.url, locale as Locale)}
|
||||
value={`${item.name} ${section.title} ${item.shortcut}`}
|
||||
onSelect={() => onSearchItemSelect(item)}
|
||||
>
|
||||
{item.icon && <i className={classnames('text-xl', item.icon)} />}
|
||||
{item.name}
|
||||
</SearchItem>
|
||||
)
|
||||
})}
|
||||
</CommandGroup>
|
||||
))
|
||||
) : (
|
||||
<CommandEmpty>
|
||||
<NoResult searchValue={searchValue} setOpen={setOpen} />
|
||||
</CommandEmpty>
|
||||
)
|
||||
) : (
|
||||
<DefaultSuggestions setOpen={setOpen} />
|
||||
)}
|
||||
</CommandList>
|
||||
<CommandFooter />
|
||||
</CommandDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavSearch
|
||||
@@ -0,0 +1,131 @@
|
||||
[cmdk-root] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
inset-block-start: 50%;
|
||||
inset-inline-start: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
inline-size: 600px;
|
||||
block-size: 585px;
|
||||
background-color: var(--mui-palette-background-paper);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
z-index: var(--search-z-index);
|
||||
box-shadow: 0px 6px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
[dir='rtl'] [cmdk-root] {
|
||||
transform: translate(50%, -50%);
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
[cmdk-root] {
|
||||
min-block-size: 100dvh;
|
||||
max-block-size: 100dvh;
|
||||
min-inline-size: 100dvw;
|
||||
max-inline-size: 100dvw;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
[data-skin='bordered'] ~ [cmdk-dialog] [cmdk-root] {
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
[cmdk-overlay] {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: var(--backdrop-color);
|
||||
z-index: var(--search-z-index);
|
||||
}
|
||||
|
||||
[cmdk-input] {
|
||||
inline-size: 100%;
|
||||
font-size: 14px;
|
||||
padding-block: 10px;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
[cmdk-input]::placeholder {
|
||||
color: #b3b2b2;
|
||||
}
|
||||
|
||||
[cmdk-list] {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[cmdk-list-sizer] {
|
||||
block-size: 100%;
|
||||
}
|
||||
|
||||
[cmdk-group-heading] {
|
||||
padding-block: 16px 8px;
|
||||
padding-inline: 16px;
|
||||
letter-spacing: 0.8px;
|
||||
color: var(--mui-palette-text-disabled);
|
||||
}
|
||||
|
||||
[cmdk-item] {
|
||||
cursor: pointer;
|
||||
block-size: 38px;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-inline: 16px;
|
||||
}
|
||||
|
||||
.active-searchItem {
|
||||
background: var(--mui-palette-primary-lightOpacity);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[cmdk-item][data-selected='true'] {
|
||||
background: var(--mui-palette-action-hover);
|
||||
}
|
||||
|
||||
[cmdk-item][data-selected='true'].active-searchItem {
|
||||
background: var(--mui-palette-primary-mainOpacity);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[cmdk-empty],
|
||||
[cmdk-loading] {
|
||||
font-size: 14px;
|
||||
block-size: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
[cmdk-footer] {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding-block: 8px;
|
||||
padding-inline: 16px;
|
||||
color: var(--mui-palette-text-secondary);
|
||||
border-block-start: 1px solid var(--border-color);
|
||||
font-size: 12px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[cmdk-vercel-shortcuts] {
|
||||
display: flex;
|
||||
margin-inline-start: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
kbd {
|
||||
font-size: 12px;
|
||||
min-inline-size: 20px;
|
||||
padding: 4px;
|
||||
block-size: 20px;
|
||||
border-radius: 4px;
|
||||
background: var(--mui-palette-action-focus);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Component Imports
|
||||
import LayoutFooter from '@layouts/components/vertical/Footer'
|
||||
import FooterContent from './FooterContent'
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<LayoutFooter>
|
||||
<FooterContent />
|
||||
</LayoutFooter>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
|
||||
// Util Imports
|
||||
import { verticalLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
const FooterContent = () => {
|
||||
// Hooks
|
||||
const { isBreakpointReached } = useVerticalNav()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(verticalLayoutClasses.footerContent, 'flex items-center justify-between flex-wrap gap-4')}
|
||||
>
|
||||
<p>
|
||||
<span className='text-textSecondary'>{`© ${new Date().getFullYear()}, Made with `}</span>
|
||||
<span>{`❤️`}</span>
|
||||
<span className='text-textSecondary'>{` by `}</span>
|
||||
<Link href='https://pixinvent.com' target='_blank' className='text-primary uppercase'>
|
||||
Pixinvent
|
||||
</Link>
|
||||
</p>
|
||||
{!isBreakpointReached && (
|
||||
<div className='flex items-center gap-4'>
|
||||
<Link href='https://themeforest.net/licenses/standard' target='_blank' className='text-primary'>
|
||||
License
|
||||
</Link>
|
||||
<Link href='https://themeforest.net/user/pixinvent/portfolio' target='_blank' className='text-primary'>
|
||||
More Themes
|
||||
</Link>
|
||||
<Link
|
||||
href='https://demos.pixinvent.com/vuexy-nextjs-admin-template/documentation'
|
||||
target='_blank'
|
||||
className='text-primary'
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
<Link href='https://pixinvent.ticksy.com' target='_blank' className='text-primary'>
|
||||
Support
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FooterContent
|
||||
@@ -0,0 +1,23 @@
|
||||
'use client'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
|
||||
const NavToggle = () => {
|
||||
// Hooks
|
||||
const { toggleVerticalNav, isBreakpointReached } = useVerticalNav()
|
||||
|
||||
const handleClick = () => {
|
||||
toggleVerticalNav()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <i className='tabler-menu-2 cursor-pointer' onClick={handleClick} /> */}
|
||||
{/* Comment following code and uncomment above code in order to toggle menu on desktop screens as well */}
|
||||
{isBreakpointReached && <i className='tabler-menu-2 cursor-pointer' onClick={handleClick} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavToggle
|
||||
@@ -0,0 +1,13 @@
|
||||
// Component Imports
|
||||
import LayoutNavbar from '@layouts/components/vertical/Navbar'
|
||||
import NavbarContent from './NavbarContent'
|
||||
|
||||
const Navbar = () => {
|
||||
return (
|
||||
<LayoutNavbar>
|
||||
<NavbarContent />
|
||||
</LayoutNavbar>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navbar
|
||||
@@ -0,0 +1,126 @@
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { ShortcutsType } from '@components/layout/shared/ShortcutsDropdown'
|
||||
import type { NotificationsType } from '@components/layout/shared/NotificationsDropdown'
|
||||
|
||||
// Component Imports
|
||||
import NavToggle from './NavToggle'
|
||||
import NavSearch from '@components/layout/shared/search'
|
||||
import LanguageDropdown from '@components/layout/shared/LanguageDropdown'
|
||||
import ModeDropdown from '@components/layout/shared/ModeDropdown'
|
||||
import ShortcutsDropdown from '@components/layout/shared/ShortcutsDropdown'
|
||||
import NotificationsDropdown from '@components/layout/shared/NotificationsDropdown'
|
||||
import UserDropdown from '@components/layout/shared/UserDropdown'
|
||||
|
||||
// Util Imports
|
||||
import { verticalLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
// Vars
|
||||
const shortcuts: ShortcutsType[] = [
|
||||
{
|
||||
url: '/apps/calendar',
|
||||
icon: 'tabler-calendar',
|
||||
title: 'Calendar',
|
||||
subtitle: 'Appointments'
|
||||
},
|
||||
{
|
||||
url: '/apps/invoice/list',
|
||||
icon: 'tabler-file-dollar',
|
||||
title: 'Invoice App',
|
||||
subtitle: 'Manage Accounts'
|
||||
},
|
||||
{
|
||||
url: '/apps/user/list',
|
||||
icon: 'tabler-user',
|
||||
title: 'Users',
|
||||
subtitle: 'Manage Users'
|
||||
},
|
||||
{
|
||||
url: '/apps/roles',
|
||||
icon: 'tabler-users-group',
|
||||
title: 'Role Management',
|
||||
subtitle: 'Permissions'
|
||||
},
|
||||
{
|
||||
url: '/',
|
||||
icon: 'tabler-device-desktop-analytics',
|
||||
title: 'Dashboard',
|
||||
subtitle: 'User Dashboard'
|
||||
},
|
||||
{
|
||||
url: '/pages/account-settings',
|
||||
icon: 'tabler-settings',
|
||||
title: 'Settings',
|
||||
subtitle: 'Account Settings'
|
||||
}
|
||||
]
|
||||
|
||||
const notifications: NotificationsType[] = [
|
||||
{
|
||||
avatarImage: '/images/avatars/8.png',
|
||||
title: 'Congratulations Flora 🎉',
|
||||
subtitle: 'Won the monthly bestseller gold badge',
|
||||
time: '1h ago',
|
||||
read: false
|
||||
},
|
||||
{
|
||||
title: 'Cecilia Becker',
|
||||
avatarColor: 'secondary',
|
||||
subtitle: 'Accepted your connection',
|
||||
time: '12h ago',
|
||||
read: false
|
||||
},
|
||||
{
|
||||
avatarImage: '/images/avatars/3.png',
|
||||
title: 'Bernard Woods',
|
||||
subtitle: 'You have new message from Bernard Woods',
|
||||
time: 'May 18, 8:26 AM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarIcon: 'tabler-chart-bar',
|
||||
title: 'Monthly report generated',
|
||||
subtitle: 'July month financial report is generated',
|
||||
avatarColor: 'info',
|
||||
time: 'Apr 24, 10:30 AM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarText: 'MG',
|
||||
title: 'Application has been approved 🚀',
|
||||
subtitle: 'Your Meta Gadgets project application has been approved.',
|
||||
avatarColor: 'success',
|
||||
time: 'Feb 17, 12:17 PM',
|
||||
read: true
|
||||
},
|
||||
{
|
||||
avatarIcon: 'tabler-mail',
|
||||
title: 'New message from Harry',
|
||||
subtitle: 'You have new message from Harry',
|
||||
avatarColor: 'error',
|
||||
time: 'Jan 6, 1:48 PM',
|
||||
read: true
|
||||
}
|
||||
]
|
||||
|
||||
const NavbarContent = () => {
|
||||
return (
|
||||
<div className={classnames(verticalLayoutClasses.navbarContent, 'flex items-center justify-between gap-4 is-full')}>
|
||||
<div className='flex items-center gap-4'>
|
||||
<NavToggle />
|
||||
<NavSearch />
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<LanguageDropdown />
|
||||
<ModeDropdown />
|
||||
<ShortcutsDropdown shortcuts={shortcuts} />
|
||||
<NotificationsDropdown notifications={notifications} />
|
||||
<UserDropdown />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavbarContent
|
||||
@@ -0,0 +1,137 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import { styled, useColorScheme, useTheme } from '@mui/material/styles'
|
||||
|
||||
// Type Imports
|
||||
import type { getDictionary } from '@/utils/getDictionary'
|
||||
import type { Mode } from '@core/types'
|
||||
import type { Locale } from '@configs/i18n'
|
||||
|
||||
// Component Imports
|
||||
import VerticalNav, { NavHeader, NavCollapseIcons } from '@menu/vertical-menu'
|
||||
import VerticalMenu from './VerticalMenu'
|
||||
import Logo from '@components/layout/shared/Logo'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
import { useSettings } from '@core/hooks/useSettings'
|
||||
|
||||
// Util Imports
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
|
||||
// Style Imports
|
||||
import navigationCustomStyles from '@core/styles/vertical/navigationCustomStyles'
|
||||
|
||||
type Props = {
|
||||
dictionary: Awaited<ReturnType<typeof getDictionary>>
|
||||
mode: Mode
|
||||
}
|
||||
|
||||
const StyledBoxForShadow = styled('div')(({ theme }) => ({
|
||||
top: 60,
|
||||
left: -8,
|
||||
zIndex: 2,
|
||||
opacity: 0,
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
width: 'calc(100% + 15px)',
|
||||
height: theme.mixins.toolbar.minHeight,
|
||||
transition: 'opacity .15s ease-in-out',
|
||||
background: `linear-gradient(var(--mui-palette-background-paper) ${
|
||||
theme.direction === 'rtl' ? '95%' : '5%'
|
||||
}, rgb(var(--mui-palette-background-paperChannel) / 0.85) 30%, rgb(var(--mui-palette-background-paperChannel) / 0.5) 65%, rgb(var(--mui-palette-background-paperChannel) / 0.3) 75%, transparent)`,
|
||||
'&.scrolled': {
|
||||
opacity: 1
|
||||
}
|
||||
}))
|
||||
|
||||
const Navigation = (props: Props) => {
|
||||
// Props
|
||||
const { dictionary, mode } = props
|
||||
|
||||
// Hooks
|
||||
const verticalNavOptions = useVerticalNav()
|
||||
const { updateSettings, settings } = useSettings()
|
||||
const { lang: locale } = useParams()
|
||||
const { mode: muiMode, systemMode: muiSystemMode } = useColorScheme()
|
||||
const theme = useTheme()
|
||||
|
||||
// Refs
|
||||
const shadowRef = useRef(null)
|
||||
|
||||
// Vars
|
||||
const { isCollapsed, isHovered, collapseVerticalNav, isBreakpointReached } = verticalNavOptions
|
||||
const isSemiDark = settings.semiDark
|
||||
|
||||
const currentMode = muiMode === 'system' ? muiSystemMode : muiMode || mode
|
||||
|
||||
const isDark = currentMode === 'dark'
|
||||
|
||||
const scrollMenu = (container: any, isPerfectScrollbar: boolean) => {
|
||||
container = isBreakpointReached || !isPerfectScrollbar ? container.target : container
|
||||
|
||||
if (shadowRef && container.scrollTop > 0) {
|
||||
// @ts-ignore
|
||||
if (!shadowRef.current.classList.contains('scrolled')) {
|
||||
// @ts-ignore
|
||||
shadowRef.current.classList.add('scrolled')
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
shadowRef.current.classList.remove('scrolled')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (settings.layout === 'collapsed') {
|
||||
collapseVerticalNav(true)
|
||||
} else {
|
||||
collapseVerticalNav(false)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [settings.layout])
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// Sidebar Vertical Menu
|
||||
<VerticalNav
|
||||
customStyles={navigationCustomStyles(verticalNavOptions, theme)}
|
||||
collapsedWidth={71}
|
||||
backgroundColor='var(--mui-palette-background-paper)'
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// The following condition adds the data-dark attribute to the VerticalNav component
|
||||
// when semiDark is enabled and the mode or systemMode is light
|
||||
{...(isSemiDark &&
|
||||
!isDark && {
|
||||
'data-dark': ''
|
||||
})}
|
||||
>
|
||||
{/* Nav Header including Logo & nav toggle icons */}
|
||||
<NavHeader>
|
||||
<Link href={getLocalizedUrl('/', locale as Locale)}>
|
||||
<Logo />
|
||||
</Link>
|
||||
{!(isCollapsed && !isHovered) && (
|
||||
<NavCollapseIcons
|
||||
lockedIcon={<i className='tabler-circle-dot text-xl' />}
|
||||
unlockedIcon={<i className='tabler-circle text-xl' />}
|
||||
closeIcon={<i className='tabler-x text-xl' />}
|
||||
onClick={() => updateSettings({ layout: !isCollapsed ? 'collapsed' : 'vertical' })}
|
||||
/>
|
||||
)}
|
||||
</NavHeader>
|
||||
<StyledBoxForShadow ref={shadowRef} />
|
||||
<VerticalMenu dictionary={dictionary} scrollMenu={scrollMenu} />
|
||||
</VerticalNav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navigation
|
||||
@@ -0,0 +1,403 @@
|
||||
// Next Imports
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
// Third-party Imports
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
|
||||
// Type Imports
|
||||
import type { getDictionary } from '@/utils/getDictionary'
|
||||
import type { VerticalMenuContextProps } from '@menu/components/vertical-menu/Menu'
|
||||
|
||||
// Component Imports
|
||||
import { Menu, SubMenu, MenuItem, MenuSection } from '@menu/vertical-menu'
|
||||
import CustomChip from '@core/components/mui/Chip'
|
||||
|
||||
// import { GenerateVerticalMenu } from '@components/GenerateMenu'
|
||||
|
||||
// Hook Imports
|
||||
import useVerticalNav from '@menu/hooks/useVerticalNav'
|
||||
|
||||
// Styled Component Imports
|
||||
import StyledVerticalNavExpandIcon from '@menu/styles/vertical/StyledVerticalNavExpandIcon'
|
||||
|
||||
// Style Imports
|
||||
import menuItemStyles from '@core/styles/vertical/menuItemStyles'
|
||||
import menuSectionStyles from '@core/styles/vertical/menuSectionStyles'
|
||||
|
||||
// Menu Data Imports
|
||||
// import menuData from '@/data/navigation/verticalMenuData'
|
||||
|
||||
type RenderExpandIconProps = {
|
||||
open?: boolean
|
||||
transitionDuration?: VerticalMenuContextProps['transitionDuration']
|
||||
}
|
||||
|
||||
type Props = {
|
||||
dictionary: Awaited<ReturnType<typeof getDictionary>>
|
||||
scrollMenu: (container: any, isPerfectScrollbar: boolean) => void
|
||||
}
|
||||
|
||||
const RenderExpandIcon = ({ open, transitionDuration }: RenderExpandIconProps) => (
|
||||
<StyledVerticalNavExpandIcon open={open} transitionDuration={transitionDuration}>
|
||||
<i className='tabler-chevron-right' />
|
||||
</StyledVerticalNavExpandIcon>
|
||||
)
|
||||
|
||||
const VerticalMenu = ({ dictionary, scrollMenu }: Props) => {
|
||||
// Hooks
|
||||
const theme = useTheme()
|
||||
const verticalNavOptions = useVerticalNav()
|
||||
const params = useParams()
|
||||
|
||||
// Vars
|
||||
const { isBreakpointReached, transitionDuration } = verticalNavOptions
|
||||
const { lang: locale } = params
|
||||
|
||||
const ScrollWrapper = isBreakpointReached ? 'div' : PerfectScrollbar
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
/* Custom scrollbar instead of browser scroll, remove if you want browser scroll only */
|
||||
<ScrollWrapper
|
||||
{...(isBreakpointReached
|
||||
? {
|
||||
className: 'bs-full overflow-y-auto overflow-x-hidden',
|
||||
onScroll: container => scrollMenu(container, false)
|
||||
}
|
||||
: {
|
||||
options: { wheelPropagation: false, suppressScrollX: true },
|
||||
onScrollY: container => scrollMenu(container, true)
|
||||
})}
|
||||
>
|
||||
{/* Incase you also want to scroll NavHeader to scroll with Vertical Menu, remove NavHeader from above and paste it below this comment */}
|
||||
{/* Vertical Menu */}
|
||||
<Menu
|
||||
popoutMenuOffset={{ mainAxis: 23 }}
|
||||
menuItemStyles={menuItemStyles(verticalNavOptions, theme)}
|
||||
renderExpandIcon={({ open }) => <RenderExpandIcon open={open} transitionDuration={transitionDuration} />}
|
||||
renderExpandedMenuItemIcon={{ icon: <i className='tabler-circle text-xs' /> }}
|
||||
menuSectionStyles={menuSectionStyles(verticalNavOptions, theme)}
|
||||
>
|
||||
<SubMenu
|
||||
label={dictionary['navigation'].dashboards}
|
||||
icon={<i className='tabler-smart-home' />}
|
||||
suffix={<CustomChip label='5' size='small' color='error' round='true' />}
|
||||
>
|
||||
<MenuItem href={`/${locale}/dashboards/crm`}>{dictionary['navigation'].crm}</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/analytics`}>{dictionary['navigation'].analytics}</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/ecommerce`}>{dictionary['navigation'].eCommerce}</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/academy`}>{dictionary['navigation'].academy}</MenuItem>
|
||||
<MenuItem href={`/${locale}/dashboards/logistics`}>{dictionary['navigation'].logistics}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].frontPages} icon={<i className='tabler-files' />}>
|
||||
<MenuItem href='/front-pages/landing-page' target='_blank'>
|
||||
{dictionary['navigation'].landing}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/pricing' target='_blank'>
|
||||
{dictionary['navigation'].pricing}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/payment' target='_blank'>
|
||||
{dictionary['navigation'].payment}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/checkout' target='_blank'>
|
||||
{dictionary['navigation'].checkout}
|
||||
</MenuItem>
|
||||
<MenuItem href='/front-pages/help-center' target='_blank'>
|
||||
{dictionary['navigation'].helpCenter}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuSection label={dictionary['navigation'].appsPages}>
|
||||
<SubMenu label={dictionary['navigation'].eCommerce} icon={<i className='tabler-shopping-cart' />}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].products}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/add`}>{dictionary['navigation'].add}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/products/category`}>
|
||||
{dictionary['navigation'].category}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].orders}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/orders/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/ecommerce/orders/details/5434`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/ecommerce/orders/details'
|
||||
>
|
||||
{dictionary['navigation'].details}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].customers}>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/customers/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/ecommerce/customers/details/879861`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/ecommerce/customers/details'
|
||||
>
|
||||
{dictionary['navigation'].details}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/manage-reviews`}>
|
||||
{dictionary['navigation'].manageReviews}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/referrals`}>{dictionary['navigation'].referrals}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/ecommerce/settings`}>{dictionary['navigation'].settings}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].academy} icon={<i className='tabler-school' />}>
|
||||
<MenuItem href={`/${locale}/apps/academy/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/academy/my-courses`}>{dictionary['navigation'].myCourses}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/academy/course-details`}>
|
||||
{dictionary['navigation'].courseDetails}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].logistics} icon={<i className='tabler-truck' />}>
|
||||
<MenuItem href={`/${locale}/apps/logistics/dashboard`}>{dictionary['navigation'].dashboard}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/logistics/fleet`}>{dictionary['navigation'].fleet}</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/email`}
|
||||
icon={<i className='tabler-mail' />}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/email'
|
||||
>
|
||||
{dictionary['navigation'].email}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/chat`} icon={<i className='tabler-message-circle-2' />}>
|
||||
{dictionary['navigation'].chat}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/calendar`} icon={<i className='tabler-calendar' />}>
|
||||
{dictionary['navigation'].calendar}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/kanban`} icon={<i className='tabler-copy' />}>
|
||||
{dictionary['navigation'].kanban}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].invoice} icon={<i className='tabler-file-description' />}>
|
||||
<MenuItem href={`/${locale}/apps/invoice/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem
|
||||
href={`/${locale}/apps/invoice/preview/4987`}
|
||||
exactMatch={false}
|
||||
activeUrl='/apps/invoice/preview'
|
||||
>
|
||||
{dictionary['navigation'].preview}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/invoice/edit/4987`} exactMatch={false} activeUrl='/apps/invoice/edit'>
|
||||
{dictionary['navigation'].edit}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/invoice/add`}>{dictionary['navigation'].add}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].user} icon={<i className='tabler-user' />}>
|
||||
<MenuItem href={`/${locale}/apps/user/list`}>{dictionary['navigation'].list}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/user/view`}>{dictionary['navigation'].view}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].rolesPermissions} icon={<i className='tabler-lock' />}>
|
||||
<MenuItem href={`/${locale}/apps/roles`}>{dictionary['navigation'].roles}</MenuItem>
|
||||
<MenuItem href={`/${locale}/apps/permissions`}>{dictionary['navigation'].permissions}</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].pages} icon={<i className='tabler-file' />}>
|
||||
<MenuItem href={`/${locale}/pages/user-profile`}>{dictionary['navigation'].userProfile}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/account-settings`}>{dictionary['navigation'].accountSettings}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/faq`}>{dictionary['navigation'].faq}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/pricing`}>{dictionary['navigation'].pricing}</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].miscellaneous}>
|
||||
<MenuItem href={`/${locale}/pages/misc/coming-soon`} target='_blank'>
|
||||
{dictionary['navigation'].comingSoon}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/under-maintenance`} target='_blank'>
|
||||
{dictionary['navigation'].underMaintenance}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/404-not-found`} target='_blank'>
|
||||
{dictionary['navigation'].pageNotFound404}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/misc/401-not-authorized`} target='_blank'>
|
||||
{dictionary['navigation'].notAuthorized401}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].authPages} icon={<i className='tabler-shield-lock' />}>
|
||||
<SubMenu label={dictionary['navigation'].login}>
|
||||
<MenuItem href={`/${locale}/pages/auth/login-v1`} target='_blank'>
|
||||
{dictionary['navigation'].loginV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/login-v2`} target='_blank'>
|
||||
{dictionary['navigation'].loginV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].register}>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-v1`} target='_blank'>
|
||||
{dictionary['navigation'].registerV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-v2`} target='_blank'>
|
||||
{dictionary['navigation'].registerV2}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/register-multi-steps`} target='_blank'>
|
||||
{dictionary['navigation'].registerMultiSteps}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].verifyEmail}>
|
||||
<MenuItem href={`/${locale}/pages/auth/verify-email-v1`} target='_blank'>
|
||||
{dictionary['navigation'].verifyEmailV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/verify-email-v2`} target='_blank'>
|
||||
{dictionary['navigation'].verifyEmailV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].forgotPassword}>
|
||||
<MenuItem href={`/${locale}/pages/auth/forgot-password-v1`} target='_blank'>
|
||||
{dictionary['navigation'].forgotPasswordV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/forgot-password-v2`} target='_blank'>
|
||||
{dictionary['navigation'].forgotPasswordV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].resetPassword}>
|
||||
<MenuItem href={`/${locale}/pages/auth/reset-password-v1`} target='_blank'>
|
||||
{dictionary['navigation'].resetPasswordV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/reset-password-v2`} target='_blank'>
|
||||
{dictionary['navigation'].resetPasswordV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].twoSteps}>
|
||||
<MenuItem href={`/${locale}/pages/auth/two-steps-v1`} target='_blank'>
|
||||
{dictionary['navigation'].twoStepsV1}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/auth/two-steps-v2`} target='_blank'>
|
||||
{dictionary['navigation'].twoStepsV2}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<SubMenu label={dictionary['navigation'].wizardExamples} icon={<i className='tabler-dots' />}>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/checkout`}>{dictionary['navigation'].checkout}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/property-listing`}>
|
||||
{dictionary['navigation'].propertyListing}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/wizard-examples/create-deal`}>
|
||||
{dictionary['navigation'].createDeal}
|
||||
</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem href={`/${locale}/pages/dialog-examples`} icon={<i className='tabler-square' />}>
|
||||
{dictionary['navigation'].dialogExamples}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].widgetExamples} icon={<i className='tabler-chart-bar' />}>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/basic`}>{dictionary['navigation'].basic}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/advanced`}>{dictionary['navigation'].advanced}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/statistics`}>
|
||||
{dictionary['navigation'].statistics}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/charts`}>{dictionary['navigation'].charts}</MenuItem>
|
||||
<MenuItem href={`/${locale}/pages/widget-examples/actions`}>{dictionary['navigation'].actions}</MenuItem>
|
||||
</SubMenu>
|
||||
</MenuSection>
|
||||
<MenuSection label={dictionary['navigation'].formsAndTables}>
|
||||
<MenuItem href={`/${locale}/forms/form-layouts`} icon={<i className='tabler-layout' />}>
|
||||
{dictionary['navigation'].formLayouts}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/forms/form-validation`} icon={<i className='tabler-checkup-list' />}>
|
||||
{dictionary['navigation'].formValidation}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/forms/form-wizard`} icon={<i className='tabler-git-merge' />}>
|
||||
{dictionary['navigation'].formWizard}
|
||||
</MenuItem>
|
||||
<MenuItem href={`/${locale}/react-table`} icon={<i className='tabler-table' />}>
|
||||
{dictionary['navigation'].reactTable}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-checkbox' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/form-elements`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].formELements}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-layout-board-split' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/mui-table`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].muiTables}
|
||||
</MenuItem>
|
||||
</MenuSection>
|
||||
<MenuSection label={dictionary['navigation'].chartsMisc}>
|
||||
<SubMenu label={dictionary['navigation'].charts} icon={<i className='tabler-chart-donut-2' />}>
|
||||
<MenuItem href={`/${locale}/charts/apex-charts`}>{dictionary['navigation'].apex}</MenuItem>
|
||||
<MenuItem href={`/${locale}/charts/recharts`}>{dictionary['navigation'].recharts}</MenuItem>
|
||||
</SubMenu>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-cards' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/foundation`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].foundation}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-atom' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/user-interface/components`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].components}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-list-search' />}
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}/docs/menu-examples/overview`}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
>
|
||||
{dictionary['navigation'].menuExamples}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-lifebuoy' />}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
href='https://pixinvent.ticksy.com'
|
||||
>
|
||||
{dictionary['navigation'].raiseSupport}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<i className='tabler-book-2' />}
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
target='_blank'
|
||||
href={`${process.env.NEXT_PUBLIC_DOCS_URL}`}
|
||||
>
|
||||
{dictionary['navigation'].documentation}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].others} icon={<i className='tabler-box' />}>
|
||||
<MenuItem suffix={<CustomChip label='New' size='small' color='info' round='true' />}>
|
||||
{dictionary['navigation'].itemWithBadge}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
href='https://pixinvent.com'
|
||||
target='_blank'
|
||||
suffix={<i className='tabler-external-link text-xl' />}
|
||||
>
|
||||
{dictionary['navigation'].externalLink}
|
||||
</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].menuLevels}>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel2}</MenuItem>
|
||||
<SubMenu label={dictionary['navigation'].menuLevel2}>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel3}</MenuItem>
|
||||
<MenuItem>{dictionary['navigation'].menuLevel3}</MenuItem>
|
||||
</SubMenu>
|
||||
</SubMenu>
|
||||
<MenuItem disabled>{dictionary['navigation'].disabledMenu}</MenuItem>
|
||||
</SubMenu>
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
{/* <Menu
|
||||
popoutMenuOffset={{ mainAxis: 23 }}
|
||||
menuItemStyles={menuItemStyles(verticalNavOptions, theme)}
|
||||
renderExpandIcon={({ open }) => <RenderExpandIcon open={open} transitionDuration={transitionDuration} />}
|
||||
renderExpandedMenuItemIcon={{ icon: <i className='tabler-circle text-xs' /> }}
|
||||
menuSectionStyles={menuSectionStyles(verticalNavOptions, theme)}
|
||||
>
|
||||
<GenerateVerticalMenu menuData={menuData(dictionary)} />
|
||||
</Menu> */}
|
||||
</ScrollWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default VerticalMenu
|
||||
Reference in New Issue
Block a user