Added base appshell
This commit is contained in:
parent
1cbb80ede9
commit
52b0f09440
92
package.json
92
package.json
|
|
@ -1,47 +1,49 @@
|
|||
{
|
||||
"name": "dashboard-template",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^1.0.14",
|
||||
"@mantine/core": "^7.4.0",
|
||||
"@mantine/form": "^7.4.0",
|
||||
"@mantine/hooks": "^7.4.0",
|
||||
"@prisma/client": "5.7.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-query-devtools": "^4.36.1",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/next": "^10.45.0",
|
||||
"@trpc/react-query": "^10.45.0",
|
||||
"@trpc/server": "^10.45.0",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"bcrypt": "^5.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"next": "14.0.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.6",
|
||||
"@types/react": "^18.2.47",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss-preset-mantine": "^1.12.3",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prisma": "^5.7.1",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
"name": "dashboard-template",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^1.0.14",
|
||||
"@mantine/core": "^7.4.0",
|
||||
"@mantine/form": "^7.4.0",
|
||||
"@mantine/hooks": "^7.4.0",
|
||||
"@prisma/client": "5.7.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-query-devtools": "^4.36.1",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/next": "^10.45.0",
|
||||
"@trpc/react-query": "^10.45.0",
|
||||
"@trpc/server": "^10.45.0",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"bcrypt": "^5.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"next": "14.0.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"sass": "^1.70.0",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.6",
|
||||
"@types/react": "^18.2.47",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "14.0.4",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss-preset-mantine": "^1.12.3",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prisma": "^5.7.1",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -52,13 +52,19 @@ dependencies:
|
|||
version: 9.0.2
|
||||
next:
|
||||
specifier: 14.0.4
|
||||
version: 14.0.4(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 14.0.4(react-dom@18.2.0)(react@18.2.0)(sass@1.70.0)
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-icons:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(react@18.2.0)
|
||||
sass:
|
||||
specifier: ^1.70.0
|
||||
version: 1.70.0
|
||||
superjson:
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
|
|
@ -594,7 +600,7 @@ packages:
|
|||
'@trpc/client': 10.45.0(@trpc/server@10.45.0)
|
||||
'@trpc/react-query': 10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/server': 10.45.0
|
||||
next: 14.0.4(react-dom@18.2.0)(react@18.2.0)
|
||||
next: 14.0.4(react-dom@18.2.0)(react@18.2.0)(sass@1.70.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
|
@ -798,7 +804,6 @@ packages:
|
|||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/aproba@2.0.0:
|
||||
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
|
||||
|
|
@ -970,7 +975,6 @@ packages:
|
|||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/brace-expansion@1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
|
|
@ -989,7 +993,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
fill-range: 7.0.1
|
||||
dev: true
|
||||
|
||||
/browserslist@4.22.2:
|
||||
resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
|
||||
|
|
@ -1055,7 +1058,6 @@ packages:
|
|||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
|
|
@ -1666,7 +1668,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
dev: true
|
||||
|
||||
/find-up@5.0.0:
|
||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||
|
|
@ -1722,7 +1723,6 @@ packages:
|
|||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/function-bind@1.1.2:
|
||||
|
|
@ -1791,7 +1791,6 @@ packages:
|
|||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
dev: true
|
||||
|
||||
/glob-parent@6.0.2:
|
||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||
|
|
@ -1934,6 +1933,10 @@ packages:
|
|||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/immutable@4.3.4:
|
||||
resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==}
|
||||
dev: false
|
||||
|
||||
/import-fresh@3.3.0:
|
||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -1997,7 +2000,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-boolean-object@1.1.2:
|
||||
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
||||
|
|
@ -2028,7 +2030,6 @@ packages:
|
|||
/is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/is-finalizationregistry@1.0.2:
|
||||
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
|
||||
|
|
@ -2052,7 +2053,6 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
dev: true
|
||||
|
||||
/is-map@2.0.2:
|
||||
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
||||
|
|
@ -2073,7 +2073,6 @@ packages:
|
|||
/is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/is-path-inside@3.0.3:
|
||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||
|
|
@ -2437,7 +2436,7 @@ packages:
|
|||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
dev: true
|
||||
|
||||
/next@14.0.4(react-dom@18.2.0)(react@18.2.0):
|
||||
/next@14.0.4(react-dom@18.2.0)(react@18.2.0)(sass@1.70.0):
|
||||
resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==}
|
||||
engines: {node: '>=18.17.0'}
|
||||
hasBin: true
|
||||
|
|
@ -2460,6 +2459,7 @@ packages:
|
|||
postcss: 8.4.31
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
sass: 1.70.0
|
||||
styled-jsx: 5.1.1(react@18.2.0)
|
||||
watchpack: 2.4.0
|
||||
optionalDependencies:
|
||||
|
|
@ -2508,7 +2508,6 @@ packages:
|
|||
/normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/normalize-range@0.1.2:
|
||||
resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
|
||||
|
|
@ -2674,7 +2673,6 @@ packages:
|
|||
/picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
dev: true
|
||||
|
||||
/pify@2.3.0:
|
||||
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
||||
|
|
@ -2853,6 +2851,14 @@ packages:
|
|||
scheduler: 0.23.0
|
||||
dev: false
|
||||
|
||||
/react-icons@5.0.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
|
|
@ -2960,7 +2966,6 @@ packages:
|
|||
engines: {node: '>=8.10.0'}
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/reflect.getprototypeof@1.0.4:
|
||||
resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
|
||||
|
|
@ -3056,6 +3061,16 @@ packages:
|
|||
is-regex: 1.1.4
|
||||
dev: true
|
||||
|
||||
/sass@1.70.0:
|
||||
resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chokidar: 3.5.3
|
||||
immutable: 4.3.4
|
||||
source-map-js: 1.0.2
|
||||
dev: false
|
||||
|
||||
/scheduler@0.23.0:
|
||||
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
|
||||
dependencies:
|
||||
|
|
@ -3364,7 +3379,6 @@ packages:
|
|||
engines: {node: '>=8.0'}
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import { AppShell } from '@mantine/core'
|
||||
import React from 'react'
|
||||
|
||||
export default function AppNavbar() {
|
||||
return (
|
||||
<AppShell.Navbar p="md">a</AppShell.Navbar>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,33 +1,21 @@
|
|||
"use client"
|
||||
import { AppShell, AppShellHeader, Burger } from '@mantine/core'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
import logo from "@/assets/logos/logo.png"
|
||||
import AppHeader from '../components/AppHeader'
|
||||
import AppNavbar from '../components/AppNavbar'
|
||||
import AppHeader from '../../components/AppHeader'
|
||||
import AppNavbar from '../../components/AppNavbar'
|
||||
import DashboardLayout from '@/components/DashboardLayout'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function layout(props: Props) {
|
||||
|
||||
const [openNavbar, { toggle }] = useDisclosure(false)
|
||||
export default function Layout(props: Props) {
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
padding="md"
|
||||
header={{ height: 60 }}
|
||||
navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !openNavbar } }}
|
||||
>
|
||||
{/* Header */}
|
||||
<AppHeader />
|
||||
|
||||
{/* Navbar */}
|
||||
<AppNavbar />
|
||||
|
||||
<AppShell.Main>{props.children}</AppShell.Main>
|
||||
</AppShell>
|
||||
<DashboardLayout>
|
||||
{props.children}
|
||||
</DashboardLayout>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
BIN
src/assets/logos/logo-dsg.png
Normal file
BIN
src/assets/logos/logo-dsg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -3,22 +3,25 @@ import React from 'react'
|
|||
import { AppShell, Burger, Group } from "@mantine/core"
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import Image from 'next/image'
|
||||
import logo from "@/assets/logos/logo.png"
|
||||
import logo from "@/assets/logos/logo-dsg.png"
|
||||
|
||||
export default function AppHeader() {
|
||||
interface Props {
|
||||
openNavbar: boolean,
|
||||
toggle: () => void
|
||||
}
|
||||
|
||||
const [openNavbar, { toggle }] = useDisclosure()
|
||||
export default function AppHeader(props: Props) {
|
||||
|
||||
return (
|
||||
<AppShell.Header>
|
||||
<Group h="100%" px="md">
|
||||
<Burger
|
||||
opened={openNavbar}
|
||||
onClick={toggle}
|
||||
opened={props.openNavbar}
|
||||
onClick={props.toggle}
|
||||
hiddenFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
<Image src={logo} alt='' height={30} />
|
||||
<Image src={logo} alt='' height={57} />
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
)
|
||||
19
src/components/AppNavbar/Navbar.tsx
Normal file
19
src/components/AppNavbar/Navbar.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { AppShell, ScrollArea } from '@mantine/core'
|
||||
import React from 'react'
|
||||
import allMenu from './_data/allMenu'
|
||||
import MenuItem from './_components/MenuItem'
|
||||
|
||||
export default function AppNavbar() {
|
||||
|
||||
const menus = allMenu.map((menu, i) => <MenuItem menu={menu} key={i} />)
|
||||
|
||||
return (
|
||||
<AppShell.Navbar p="md">
|
||||
<ScrollArea style={{flex: "1"}}>
|
||||
<div>
|
||||
{menus}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</AppShell.Navbar>
|
||||
)
|
||||
}
|
||||
23
src/components/AppNavbar/_components/ChildMenu.tsx
Normal file
23
src/components/AppNavbar/_components/ChildMenu.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { Text } from "@mantine/core";
|
||||
import React from "react";
|
||||
|
||||
import classNames from "./childMenu.module.css";
|
||||
import { MenuItem } from "../_data/allMenu";
|
||||
import { isNotEmpty } from "@mantine/form";
|
||||
|
||||
interface Props {
|
||||
item: NonNullable<MenuItem["children"]>[number];
|
||||
}
|
||||
|
||||
export default function ChildMenu(props: Props) {
|
||||
return (
|
||||
<Text<"a">
|
||||
component="a"
|
||||
className={classNames.link}
|
||||
href={props.item.link}
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
{props.item.label}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
71
src/components/AppNavbar/_components/MenuItem.tsx
Normal file
71
src/components/AppNavbar/_components/MenuItem.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
Group,
|
||||
ThemeIcon,
|
||||
UnstyledButton,
|
||||
rem,
|
||||
} from "@mantine/core";
|
||||
import { MenuItem } from "../_data/allMenu";
|
||||
import { TbChevronRight } from "react-icons/tb";
|
||||
|
||||
import classNames from "./menuItem.module.css";
|
||||
import ChildMenu from "./ChildMenu";
|
||||
|
||||
interface Props {
|
||||
menu: MenuItem;
|
||||
}
|
||||
|
||||
export default function MenuItem({ menu }: Props) {
|
||||
const hasChildren = Array.isArray(menu.children);
|
||||
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const toggleOpenMenu = () => {
|
||||
setOpened((prev) => !prev);
|
||||
};
|
||||
|
||||
const subItems = (hasChildren ? menu.children! : []).map((child, i) => (
|
||||
<ChildMenu key={i} item={child} />
|
||||
));
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Main Section */}
|
||||
<UnstyledButton
|
||||
onClick={toggleOpenMenu}
|
||||
className={classNames.control}
|
||||
>
|
||||
<Group justify="space-between" gap={0}>
|
||||
{/* Left Section */}
|
||||
<Box style={{ display: "flex", alignItems: "center" }}>
|
||||
{/* Icon */}
|
||||
<ThemeIcon variant="light" size={30} color={menu.color}>
|
||||
<menu.icon
|
||||
style={{ width: rem(18), height: rem(18) }}
|
||||
/>
|
||||
</ThemeIcon>
|
||||
|
||||
{/* Label */}
|
||||
<Box ml="md">{menu.label}</Box>
|
||||
</Box>
|
||||
|
||||
{/* Right Section (Chevron if available) */}
|
||||
{hasChildren && (
|
||||
<TbChevronRight
|
||||
// stroke="1.5"
|
||||
style={{
|
||||
width: rem(16),
|
||||
height: rem(16),
|
||||
transform: opened ? "rotate(-90deg)" : "rotate(90deg)",
|
||||
}}
|
||||
className={classNames.chevron}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
{hasChildren ? <Collapse in={opened}>{subItems}</Collapse> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
16
src/components/AppNavbar/_components/childMenu.module.css
Normal file
16
src/components/AppNavbar/_components/childMenu.module.css
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.link {
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
|
||||
padding-left: var(--mantine-spacing-md);
|
||||
margin-left: var(--mantine-spacing-xl);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
border-left: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
||||
}
|
||||
}
|
||||
17
src/components/AppNavbar/_components/menuItem.module.css
Normal file
17
src/components/AppNavbar/_components/menuItem.module.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.control {
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
|
||||
color: var(--mantine-color-text);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
|
||||
@mixin hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
|
||||
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
||||
}
|
||||
}
|
||||
|
||||
.chevron {
|
||||
transition: transform 200ms ease;
|
||||
}
|
||||
31
src/components/AppNavbar/_data/allMenu.ts
Normal file
31
src/components/AppNavbar/_data/allMenu.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import React from "react";
|
||||
import { TbLayoutDashboard, TbUsers } from "react-icons/tb";
|
||||
|
||||
export interface MenuItem {
|
||||
label: string,
|
||||
icon: React.FC<any>,
|
||||
children?: {
|
||||
label: string,
|
||||
link: string,
|
||||
}[],
|
||||
color?: string,
|
||||
}
|
||||
|
||||
const allMenu: MenuItem[] = [
|
||||
{
|
||||
label: "Dashboard",
|
||||
icon: TbLayoutDashboard,
|
||||
},
|
||||
{
|
||||
label: "Users",
|
||||
icon: TbUsers,
|
||||
color: "green",
|
||||
children: [
|
||||
{ label: "Users", link: "#"},
|
||||
{ label: "Roles", link: "#"},
|
||||
{ label: "Permissions", link: "#"},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export default allMenu;
|
||||
35
src/components/DashboardLayout/DashboardLayout.tsx
Normal file
35
src/components/DashboardLayout/DashboardLayout.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
"use client";
|
||||
import React from "react";
|
||||
import { AppShell, AppShellHeader, Burger } from "@mantine/core";
|
||||
import AppHeader from "../AppHeader";
|
||||
import AppNavbar from "../AppNavbar";
|
||||
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function DashboardLayout(props: Props) {
|
||||
const [openNavbar, { toggle }] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
padding="md"
|
||||
header={{ height: 70 }}
|
||||
navbar={{
|
||||
width: 300,
|
||||
breakpoint: "sm",
|
||||
collapsed: { mobile: !openNavbar },
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<AppHeader openNavbar={openNavbar} toggle={toggle} />
|
||||
|
||||
{/* Navbar */}
|
||||
<AppNavbar />
|
||||
|
||||
<AppShell.Main>{props.children}</AppShell.Main>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
3
src/components/DashboardLayout/index.ts
Normal file
3
src/components/DashboardLayout/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import DashboardLayout from "./DashboardLayout";
|
||||
|
||||
export default DashboardLayout;
|
||||
Loading…
Reference in New Issue
Block a user