initial commit

This commit is contained in:
ferdiansyah783
2025-08-05 12:35:40 +07:00
commit fffa2ead5c
1069 changed files with 118056 additions and 0 deletions
@@ -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
+9
View File
@@ -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>
)
}
+89
View File
@@ -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;
}
+13
View File
@@ -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
+13
View File
@@ -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