Refactor: move AppShell to global layout, add breadcrumbs, and restructure profile routes

This commit is contained in:
2026-03-26 17:10:40 +08:00
parent 0d0dc187a5
commit 34804127c5
20 changed files with 548 additions and 992 deletions

View File

@@ -1,8 +1,10 @@
import {
ActionIcon,
Anchor,
Avatar,
Badge,
Box,
Breadcrumbs,
Divider,
Group,
Text,
@@ -20,14 +22,70 @@ interface HeaderProps {
}
export function Header({ onSidebarToggle }: HeaderProps) {
const _location = useLocation();
const location = useLocation();
const navigate = useNavigate();
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const dark = colorScheme === "dark";
const pathnames = location.pathname.split("/").filter((x) => x);
const breadcrumbItems = [
<Anchor
key="home"
onClick={() => navigate({ to: "/" })}
c="white"
size="sm"
underline="hover"
>
Desa Darmasaba
</Anchor>,
...pathnames.map((value, index) => {
const to = `/${pathnames.slice(0, index + 1).join("/")}`;
const isLast = index === pathnames.length - 1;
// Map route path to human-readable label
const labelMap: Record<string, string> = {
"kinerja-divisi": "Kinerja Divisi",
"pengaduan-layanan-publik": "Pengaduan & Layanan Publik",
"jenna-analytic": "Jenna Analytic",
"demografi-pekerjaan": "Demografi & Kependudukan",
"keuangan-anggaran": "Keuangan & Anggaran",
bumdes: "Bumdes & UMKM",
sosial: "Sosial",
keamanan: "Keamanan",
bantuan: "Bantuan",
pengaturan: "Pengaturan",
umum: "Umum",
notifikasi: "Notifikasi",
"akses-dan-tim": "Akses & Tim",
profile: "Profil",
edit: "Edit",
};
const label =
labelMap[value] || value.charAt(0).toUpperCase() + value.slice(1);
return isLast ? (
<Text key={to} c="white" size="sm" fw={600}>
{label}
</Text>
) : (
<Anchor
key={to}
onClick={() => navigate({ to })}
c="white"
size="sm"
underline="hover"
>
{label}
</Anchor>
);
}),
];
return (
<Group justify="space-between" w="100%">
{/* Title */}
{/* Title & Breadcrumbs */}
<Group gap="md">
<ActionIcon
onClick={onSidebarToggle}
@@ -42,6 +100,18 @@ export function Header({ onSidebarToggle }: HeaderProps) {
style={{ width: "70%", height: "70%" }}
/>
</ActionIcon>
<Breadcrumbs
separator={
<Text c="white" size="xs">
/
</Text>
}
styles={{
separator: { color: "white" },
}}
>
{breadcrumbItems}
</Breadcrumbs>
</Group>
{/* Right Section */}

View File

@@ -0,0 +1,66 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import type React from "react";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
interface MainLayoutProps {
children: React.ReactNode;
}
export function MainLayout({ children }: MainLayoutProps) {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
{children}
</AppShell.Main>
</AppShell>
);
}

View File

@@ -20,6 +20,7 @@ import { Route as JennaAnalyticRouteImport } from './routes/jenna-analytic'
import { Route as DemografiPekerjaanRouteImport } from './routes/demografi-pekerjaan'
import { Route as BumdesRouteImport } from './routes/bumdes'
import { Route as BantuanRouteImport } from './routes/bantuan'
import { Route as ProfileRouteRouteImport } from './routes/profile/route'
import { Route as PengaturanRouteRouteImport } from './routes/pengaturan/route'
import { Route as AdminRouteRouteImport } from './routes/admin/route'
import { Route as IndexRouteImport } from './routes/index'
@@ -91,6 +92,11 @@ const BantuanRoute = BantuanRouteImport.update({
path: '/bantuan',
getParentRoute: () => rootRouteImport,
} as any)
const ProfileRouteRoute = ProfileRouteRouteImport.update({
id: '/profile',
path: '/profile',
getParentRoute: () => rootRouteImport,
} as any)
const PengaturanRouteRoute = PengaturanRouteRouteImport.update({
id: '/pengaturan',
path: '/pengaturan',
@@ -112,9 +118,9 @@ const UsersIndexRoute = UsersIndexRouteImport.update({
getParentRoute: () => rootRouteImport,
} as any)
const ProfileIndexRoute = ProfileIndexRouteImport.update({
id: '/profile/',
path: '/profile/',
getParentRoute: () => rootRouteImport,
id: '/',
path: '/',
getParentRoute: () => ProfileRouteRoute,
} as any)
const AdminIndexRoute = AdminIndexRouteImport.update({
id: '/',
@@ -127,9 +133,9 @@ const UsersIdRoute = UsersIdRouteImport.update({
getParentRoute: () => rootRouteImport,
} as any)
const ProfileEditRoute = ProfileEditRouteImport.update({
id: '/profile/edit',
path: '/profile/edit',
getParentRoute: () => rootRouteImport,
id: '/edit',
path: '/edit',
getParentRoute: () => ProfileRouteRoute,
} as any)
const PengaturanUmumRoute = PengaturanUmumRouteImport.update({
id: '/umum',
@@ -171,6 +177,7 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/admin': typeof AdminRouteRouteWithChildren
'/pengaturan': typeof PengaturanRouteRouteWithChildren
'/profile': typeof ProfileRouteRouteWithChildren
'/bantuan': typeof BantuanRoute
'/bumdes': typeof BumdesRoute
'/demografi-pekerjaan': typeof DemografiPekerjaanRoute
@@ -227,6 +234,7 @@ export interface FileRoutesById {
'/': typeof IndexRoute
'/admin': typeof AdminRouteRouteWithChildren
'/pengaturan': typeof PengaturanRouteRouteWithChildren
'/profile': typeof ProfileRouteRouteWithChildren
'/bantuan': typeof BantuanRoute
'/bumdes': typeof BumdesRoute
'/demografi-pekerjaan': typeof DemografiPekerjaanRoute
@@ -257,6 +265,7 @@ export interface FileRouteTypes {
| '/'
| '/admin'
| '/pengaturan'
| '/profile'
| '/bantuan'
| '/bumdes'
| '/demografi-pekerjaan'
@@ -312,6 +321,7 @@ export interface FileRouteTypes {
| '/'
| '/admin'
| '/pengaturan'
| '/profile'
| '/bantuan'
| '/bumdes'
| '/demografi-pekerjaan'
@@ -341,6 +351,7 @@ export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AdminRouteRoute: typeof AdminRouteRouteWithChildren
PengaturanRouteRoute: typeof PengaturanRouteRouteWithChildren
ProfileRouteRoute: typeof ProfileRouteRouteWithChildren
BantuanRoute: typeof BantuanRoute
BumdesRoute: typeof BumdesRoute
DemografiPekerjaanRoute: typeof DemografiPekerjaanRoute
@@ -352,9 +363,7 @@ export interface RootRouteChildren {
SigninRoute: typeof SigninRoute
SignupRoute: typeof SignupRoute
SosialRoute: typeof SosialRoute
ProfileEditRoute: typeof ProfileEditRoute
UsersIdRoute: typeof UsersIdRoute
ProfileIndexRoute: typeof ProfileIndexRoute
UsersIndexRoute: typeof UsersIndexRoute
}
@@ -437,6 +446,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof BantuanRouteImport
parentRoute: typeof rootRouteImport
}
'/profile': {
id: '/profile'
path: '/profile'
fullPath: '/profile'
preLoaderRoute: typeof ProfileRouteRouteImport
parentRoute: typeof rootRouteImport
}
'/pengaturan': {
id: '/pengaturan'
path: '/pengaturan'
@@ -467,10 +483,10 @@ declare module '@tanstack/react-router' {
}
'/profile/': {
id: '/profile/'
path: '/profile'
path: '/'
fullPath: '/profile/'
preLoaderRoute: typeof ProfileIndexRouteImport
parentRoute: typeof rootRouteImport
parentRoute: typeof ProfileRouteRoute
}
'/admin/': {
id: '/admin/'
@@ -488,10 +504,10 @@ declare module '@tanstack/react-router' {
}
'/profile/edit': {
id: '/profile/edit'
path: '/profile/edit'
path: '/edit'
fullPath: '/profile/edit'
preLoaderRoute: typeof ProfileEditRouteImport
parentRoute: typeof rootRouteImport
parentRoute: typeof ProfileRouteRoute
}
'/pengaturan/umum': {
id: '/pengaturan/umum'
@@ -581,10 +597,25 @@ const PengaturanRouteRouteWithChildren = PengaturanRouteRoute._addFileChildren(
PengaturanRouteRouteChildren,
)
interface ProfileRouteRouteChildren {
ProfileEditRoute: typeof ProfileEditRoute
ProfileIndexRoute: typeof ProfileIndexRoute
}
const ProfileRouteRouteChildren: ProfileRouteRouteChildren = {
ProfileEditRoute: ProfileEditRoute,
ProfileIndexRoute: ProfileIndexRoute,
}
const ProfileRouteRouteWithChildren = ProfileRouteRoute._addFileChildren(
ProfileRouteRouteChildren,
)
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AdminRouteRoute: AdminRouteRouteWithChildren,
PengaturanRouteRoute: PengaturanRouteRouteWithChildren,
ProfileRouteRoute: ProfileRouteRouteWithChildren,
BantuanRoute: BantuanRoute,
BumdesRoute: BumdesRoute,
DemografiPekerjaanRoute: DemografiPekerjaanRoute,
@@ -596,9 +627,7 @@ const rootRouteChildren: RootRouteChildren = {
SigninRoute: SigninRoute,
SignupRoute: SignupRoute,
SosialRoute: SosialRoute,
ProfileEditRoute: ProfileEditRoute,
UsersIdRoute: UsersIdRoute,
ProfileIndexRoute: ProfileIndexRoute,
UsersIndexRoute: UsersIndexRoute,
}
export const routeTree = rootRouteImport

View File

@@ -3,7 +3,12 @@ import { protectedRouteMiddleware } from "@/middleware/authMiddleware";
import { authStore } from "@/store/auth";
import "@mantine/core/styles.css";
import "@mantine/dates/styles.css";
import { createRootRoute, Outlet } from "@tanstack/react-router";
import {
createRootRoute,
Outlet,
useRouterState,
} from "@tanstack/react-router";
import { MainLayout } from "@/components/layout/main-layout";
export const Route = createRootRoute({
component: RootComponent,
@@ -21,5 +26,18 @@ export const Route = createRootRoute({
});
function RootComponent() {
return <Outlet />;
const routerState = useRouterState();
const isPublicRoute = ["/signin", "/signup", "/admin", "/profile"].some(
(path) => routerState.location.pathname.startsWith(path),
);
if (isPublicRoute) {
return <Outlet />;
}
return (
<MainLayout>
<Outlet />
</MainLayout>
);
}

View File

@@ -4,7 +4,6 @@ import {
Box,
Button,
Card,
Container,
Grid,
Group,
Progress,
@@ -55,7 +54,7 @@ function DashboardComponent() {
];
return (
<Container size="lg" py="xl">
<Box py="xl">
<Title
order={1}
ta="center"
@@ -195,6 +194,6 @@ function DashboardComponent() {
</Card>
</Grid.Col>
</Grid>
</Container>
</Box>
);
}

View File

@@ -315,9 +315,7 @@ function DashboardLayout() {
</AppShell.Navbar>
<AppShell.Main>
<Box p="lg" style={{ minHeight: "calc(100vh - 100px)" }}>
<Outlet />
</Box>
<Outlet />
</AppShell.Main>
</AppShell>
);

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import HelpPage from "@/components/help-page";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/bantuan")({
component: BantuanRoute,
component: HelpPage,
});
function BantuanRoute() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<HelpPage />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import BumdesPage from "@/components/bumdes-page";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/bumdes")({
component: BumdesRoute,
component: BumdesPage,
});
function BumdesRoute() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<BumdesPage />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
import DemografiPekerjaan from "../components/demografi-pekerjaan";
export const Route = createFileRoute("/demografi-pekerjaan")({
component: DemografiPekerjaanPage,
component: DemografiPekerjaan,
});
function DemografiPekerjaanPage() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<DemografiPekerjaan />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,72 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { createFileRoute } from "@tanstack/react-router";
import { useState } from "react";
import { DashboardContent } from "@/components/dashboard-content";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
export const Route = createFileRoute("/")({
component: DashboardPage,
component: DashboardContent,
});
function DashboardPage() {
const [opened, { toggle }] = useDisclosure();
const [sidebarCollapsed, setSidebarCollapsed] = useDisclosure(false);
const [clickCount, setClickCount] = useState(0);
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
const handleMainClick = () => {
if (!sidebarCollapsed) {
const newCount = clickCount + 1;
setClickCount(newCount);
if (newCount === 2) {
setSidebarCollapsed.toggle();
setClickCount(0);
} else {
setTimeout(() => setClickCount(0), 300);
}
}
};
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
<Header onSidebarToggle={setSidebarCollapsed.toggle} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<DashboardContent />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import JennaAnalytic from "@/components/jenna-analytic";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/jenna-analytic")({
component: JennaAnalyticPage,
component: JennaAnalytic,
});
function JennaAnalyticPage() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<JennaAnalytic />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import KeamananPage from "@/components/keamanan-page";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/keamanan")({
component: KeamananRoute,
component: KeamananPage,
});
function KeamananRoute() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<KeamananPage />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import KeuanganAnggaran from "@/components/keuangan-anggaran";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/keuangan-anggaran")({
component: KeuanganAnggaranPage,
component: KeuanganAnggaran,
});
function KeuanganAnggaranPage() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<KeuanganAnggaran />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import KinerjaDivisi from "@/components/kinerja-divisi";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/kinerja-divisi")({
component: KinerjaDivisiPage,
component: KinerjaDivisi,
});
function KinerjaDivisiPage() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<KinerjaDivisi />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import PengaduanLayananPublik from "@/components/pengaduan-layanan-publik";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/pengaduan-layanan-publik")({
component: PengaduanLayananPublikPage,
component: PengaduanLayananPublik,
});
function PengaduanLayananPublikPage() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<PengaduanLayananPublik />
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,84 +1,5 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import {
createFileRoute,
Outlet,
useRouterState,
} from "@tanstack/react-router";
import { useEffect } from "react";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
import { createFileRoute, Outlet } from "@tanstack/react-router";
export const Route = createFileRoute("/pengaturan")({
component: PengaturanLayout,
component: Outlet,
});
function PengaturanLayout() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const isMobile = useMediaQuery("(max-width: 48em)");
const _routerState = useRouterState();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
// Auto close navbar on route change (mobile only)
useEffect(() => {
if (isMobile && opened) {
toggleMobile();
}
}, [isMobile, opened, toggleMobile]);
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="lg" align="center" wrap="nowrap">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<div className="p-2">
<Outlet />
</div>
</AppShell.Main>
</AppShell>
);
}

View File

@@ -1,7 +1,7 @@
import {
Box,
Button,
Card,
Container,
Divider,
Group,
Stack,
@@ -63,77 +63,72 @@ function EditProfile() {
};
return (
<Container size="sm" py={50}>
<Stack gap="xl">
<Group justify="space-between" align="center">
<Box>
<Title order={1} c="orange.6">
Edit Profil
</Title>
<Text c="dimmed" size="sm">
Perbarui informasi profil publik Anda
</Text>
</Box>
<Button
variant="subtle"
color="gray"
leftSection={<IconChevronLeft size={18} />}
onClick={() => navigate({ to: "/profile" })}
>
Kembali
</Button>
</Group>
<Divider style={{ opacity: 0.1 }} />
<Card
withBorder
radius="md"
p="xl"
style={{ border: "1px solid var(--mantine-color-default-border)" }}
<Stack gap="xl" px={"lg"}>
<Group justify="space-between" align="center">
<Box>
<Title order={1} c="orange.6">
Edit Profil
</Title>
<Text c="dimmed" size="sm">
Perbarui informasi profil publik Anda
</Text>
</Box>
<Button
variant="subtle"
color="gray"
leftSection={<IconChevronLeft size={18} />}
onClick={() => navigate({ to: "/profile" })}
>
<form onSubmit={form.onSubmit(handleUpdateProfile)}>
<Stack gap="md">
<TextInput
label="Nama Lengkap"
placeholder="Masukkan nama lengkap Anda"
{...form.getInputProps("name")}
styles={{
label: { marginBottom: 8 },
input: {
backgroundColor: "var(--mantine-color-default-soft)",
},
}}
/>
<TextInput
label="URL Foto Profil"
placeholder="https://example.com/photo.jpg"
{...form.getInputProps("image")}
styles={{
label: { marginBottom: 8 },
input: {
backgroundColor: "var(--mantine-color-default-soft)",
},
}}
/>
<Button
type="submit"
fullWidth
mt="lg"
size="md"
color="orange"
loading={isUpdating}
leftSection={<IconEdit size={18} />}
>
Simpan Perubahan
</Button>
</Stack>
</form>
</Card>
</Stack>
</Container>
Kembali
</Button>
</Group>
<Divider style={{ opacity: 0.1 }} />
<Card
withBorder
radius="md"
p="xl"
style={{ border: "1px solid var(--mantine-color-default-border)" }}
>
<form onSubmit={form.onSubmit(handleUpdateProfile)}>
<Stack gap="md">
<TextInput
label="Nama Lengkap"
placeholder="Masukkan nama lengkap Anda"
{...form.getInputProps("name")}
styles={{
label: { marginBottom: 8 },
input: {
backgroundColor: "var(--mantine-color-default-soft)",
},
}}
/>
<TextInput
label="URL Foto Profil"
placeholder="https://example.com/photo.jpg"
{...form.getInputProps("image")}
styles={{
label: { marginBottom: 8 },
input: {
backgroundColor: "var(--mantine-color-default-soft)",
},
}}
/>
<Button
type="submit"
fullWidth
mt="lg"
size="md"
color="orange"
loading={isUpdating}
leftSection={<IconEdit size={18} />}
>
Simpan Perubahan
</Button>
</Stack>
</form>
</Card>
</Stack>
);
}
// Need Box from @mantine/core
import { Box } from "@mantine/core";

View File

@@ -6,7 +6,6 @@ import {
Button,
Card,
Code,
Container,
Divider,
Grid,
Group,
@@ -141,213 +140,211 @@ function Profile() {
);
return (
<Container size="md" py={50}>
<Stack gap="xl">
{/* Header Section */}
<Group justify="space-between" align="center">
<Box>
<Title order={1} c="orange.6">
Profil Saya
</Title>
<Text c="dimmed" size="sm">
Kelola informasi akun dan pengaturan keamanan Anda
</Text>
</Box>
<Group>
{snap.user?.role === "admin" && (
<Button
variant="light"
color="orange"
leftSection={<IconDashboard size={18} />}
onClick={() => navigate({ to: "/admin" })}
>
Admin Panel
</Button>
)}
<Stack gap="xl" px={"lg"}>
{/* Header Section */}
<Group justify="space-between" align="center">
<Box>
<Title order={1} c="orange.6">
Profil Saya
</Title>
<Text c="dimmed" size="sm">
Kelola informasi akun dan pengaturan keamanan Anda
</Text>
</Box>
<Group>
{snap.user?.role === "admin" && (
<Button
variant="light"
color="blue"
leftSection={<IconEdit size={18} />}
onClick={() => navigate({ to: "/profile/edit" })}
color="orange"
leftSection={<IconDashboard size={18} />}
onClick={() => navigate({ to: "/admin" })}
>
Edit Profil
Admin Panel
</Button>
<Button
variant="outline"
color="red"
leftSection={<IconLogout size={18} />}
onClick={openLogoutModal}
>
Keluar
</Button>
</Group>
)}
<Button
variant="light"
color="blue"
leftSection={<IconEdit size={18} />}
onClick={() => navigate({ to: "/profile/edit" })}
>
Edit Profil
</Button>
<Button
variant="outline"
color="red"
leftSection={<IconLogout size={18} />}
onClick={openLogoutModal}
>
Keluar
</Button>
</Group>
</Group>
<Divider style={{ opacity: 0.1 }} />
<Divider style={{ opacity: 0.1 }} />
{/* Profile Overview Card */}
<Card withBorder radius="lg" p={0} style={{ overflow: "hidden" }}>
<Box
h={120}
style={{
background:
"linear-gradient(45deg, var(--mantine-color-gray-filled) 0%, var(--mantine-color-dark-filled) 100%)",
borderBottom: "1px solid var(--mantine-color-default-border)",
}}
/>
<Box px="xl" pb="xl" style={{ marginTop: rem(-60) }}>
<Group align="flex-end" gap="xl" mb="md">
<Avatar
src={snap.user?.image}
size={120}
radius={120}
style={{
border: "4px solid var(--mantine-color-body)",
boxShadow: "var(--mantine-shadow-md)",
}}
>
{snap.user?.name?.charAt(0).toUpperCase()}
</Avatar>
<Stack gap={0} pb="md">
<Title order={2}>{snap.user?.name}</Title>
<Group gap="xs">
<Text c="dimmed" size="sm">
{snap.user?.email}
{/* Profile Overview Card */}
<Card withBorder radius="lg" p={0} style={{ overflow: "hidden" }}>
<Box
h={120}
style={{
background:
"linear-gradient(45deg, var(--mantine-color-gray-filled) 0%, var(--mantine-color-dark-filled) 100%)",
borderBottom: "1px solid var(--mantine-color-default-border)",
}}
/>
<Box px="xl" pb="xl" style={{ marginTop: rem(-60) }}>
<Group align="flex-end" gap="xl" mb="md">
<Avatar
src={snap.user?.image}
size={120}
radius={120}
style={{
border: "4px solid var(--mantine-color-body)",
boxShadow: "var(--mantine-shadow-md)",
}}
>
{snap.user?.name?.charAt(0).toUpperCase()}
</Avatar>
<Stack gap={0} pb="md">
<Title order={2}>{snap.user?.name}</Title>
<Group gap="xs">
<Text c="dimmed" size="sm">
{snap.user?.email}
</Text>
<Text c="dimmed" size="xs">
</Text>
<Badge
variant="dot"
color={snap.user?.role === "admin" ? "orange" : "blue"}
size="sm"
>
{snap.user?.role || "user"}
</Badge>
</Group>
</Stack>
</Group>
</Box>
</Card>
<Grid gutter="lg">
<Grid.Col span={{ base: 12, md: 7 }}>
<Stack gap="md">
<Title order={4} c="orange.6">
Informasi Identitas
</Title>
<Grid gutter="sm">
<Grid.Col span={6}>
<InfoField
icon={IconUser}
label="Nama Lengkap"
value={snap.user?.name}
/>
</Grid.Col>
<Grid.Col span={6}>
<InfoField
icon={IconShield}
label="Peran"
value={snap.user?.role || "User"}
/>
</Grid.Col>
<Grid.Col span={12}>
<InfoField
icon={IconAt}
label="Alamat Email"
value={snap.user?.email}
copyable
id="email"
/>
</Grid.Col>
<Grid.Col span={12}>
<InfoField
icon={IconId}
label="Unique User ID"
value={snap.user?.id}
copyable
id="userid"
/>
</Grid.Col>
</Grid>
</Stack>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 5 }}>
<Stack gap="md">
<Title order={4} c="orange.6">
Keamanan & Sesi
</Title>
<Card
withBorder
radius="md"
p="lg"
style={{
border: "1px solid var(--mantine-color-default-border)",
}}
>
<Stack gap="md">
<Box>
<Text size="xs" c="dimmed" tt="uppercase" fw={700} mb={8}>
Sesi Saat Ini
</Text>
<Text c="dimmed" size="xs">
<Group justify="space-between" align="center">
<Badge color="green" variant="light">
Aktif Sekarang
</Badge>
<Text size="xs" c="dimmed">
ID: {snap.session?.id?.substring(0, 8)}...
</Text>
</Group>
</Box>
<Box>
<Text size="xs" c="dimmed" tt="uppercase" fw={700} mb={8}>
Session Token
</Text>
<Badge
variant="dot"
color={snap.user?.role === "admin" ? "orange" : "blue"}
size="sm"
>
{snap.user?.role || "user"}
</Badge>
</Group>
<Group gap="xs" wrap="nowrap">
<Code
block
style={{
fontSize: rem(11),
flex: 1,
}}
>
{snap.session?.token
? `${snap.session.token.substring(0, 32)}...`
: "N/A"}
</Code>
<ActionIcon
variant="light"
color="gray"
onClick={() =>
snap.session?.token &&
copyToClipboard(snap.session.token, "token")
}
>
{copied === "token" ? (
<IconCheck size={16} />
) : (
<IconCopy size={16} />
)}
</ActionIcon>
</Group>
</Box>
<Button
variant="light"
color="gray"
fullWidth
leftSection={<IconExternalLink size={16} />}
>
Riwayat Sesi
</Button>
</Stack>
</Group>
</Box>
</Card>
<Grid gutter="lg">
<Grid.Col span={{ base: 12, md: 7 }}>
<Stack gap="md">
<Title order={4} c="orange.6">
Informasi Identitas
</Title>
<Grid gutter="sm">
<Grid.Col span={6}>
<InfoField
icon={IconUser}
label="Nama Lengkap"
value={snap.user?.name}
/>
</Grid.Col>
<Grid.Col span={6}>
<InfoField
icon={IconShield}
label="Peran"
value={snap.user?.role || "User"}
/>
</Grid.Col>
<Grid.Col span={12}>
<InfoField
icon={IconAt}
label="Alamat Email"
value={snap.user?.email}
copyable
id="email"
/>
</Grid.Col>
<Grid.Col span={12}>
<InfoField
icon={IconId}
label="Unique User ID"
value={snap.user?.id}
copyable
id="userid"
/>
</Grid.Col>
</Grid>
</Stack>
</Grid.Col>
<Grid.Col span={{ base: 12, md: 5 }}>
<Stack gap="md">
<Title order={4} c="orange.6">
Keamanan & Sesi
</Title>
<Card
withBorder
radius="md"
p="lg"
style={{
border: "1px solid var(--mantine-color-default-border)",
}}
>
<Stack gap="md">
<Box>
<Text size="xs" c="dimmed" tt="uppercase" fw={700} mb={8}>
Sesi Saat Ini
</Text>
<Group justify="space-between" align="center">
<Badge color="green" variant="light">
Aktif Sekarang
</Badge>
<Text size="xs" c="dimmed">
ID: {snap.session?.id?.substring(0, 8)}...
</Text>
</Group>
</Box>
<Box>
<Text size="xs" c="dimmed" tt="uppercase" fw={700} mb={8}>
Session Token
</Text>
<Group gap="xs" wrap="nowrap">
<Code
block
style={{
fontSize: rem(11),
flex: 1,
}}
>
{snap.session?.token
? `${snap.session.token.substring(0, 32)}...`
: "N/A"}
</Code>
<ActionIcon
variant="light"
color="gray"
onClick={() =>
snap.session?.token &&
copyToClipboard(snap.session.token, "token")
}
>
{copied === "token" ? (
<IconCheck size={16} />
) : (
<IconCopy size={16} />
)}
</ActionIcon>
</Group>
</Box>
<Button
variant="light"
color="gray"
fullWidth
leftSection={<IconExternalLink size={16} />}
>
Riwayat Sesi
</Button>
</Stack>
</Card>
</Stack>
</Grid.Col>
</Grid>
</Stack>
</Container>
</Card>
</Stack>
</Grid.Col>
</Grid>
</Stack>
);
}

View File

@@ -0,0 +1,69 @@
import {
AppShell,
Button,
Container,
Group,
Text,
useMantineColorScheme,
} from "@mantine/core";
import { IconChevronLeft } from "@tabler/icons-react";
import { createFileRoute, Outlet, useNavigate } from "@tanstack/react-router";
export const Route = createFileRoute("/profile")({
component: ProfileLayout,
});
function ProfileLayout() {
const navigate = useNavigate();
const { colorScheme } = useMantineColorScheme();
const dark = colorScheme === "dark";
return (
<AppShell
header={{ height: 60 }}
padding="md"
styles={{
main: {
backgroundColor: dark
? "var(--mantine-color-dark-8)"
: "var(--mantine-color-gray-0)",
},
}}
>
<AppShell.Header
style={{
borderBottom: "1px solid var(--mantine-color-default-border)",
backgroundColor: dark ? "var(--mantine-color-dark-7)" : "white",
paddingLeft: "1rem",
paddingRight: "1rem",
}}
>
<Group h="100%" justify="space-between">
<Group>
<Button
variant="subtle"
color="gray"
leftSection={<IconChevronLeft size={16} />}
onClick={() => navigate({ to: "/" })}
>
Kembali ke Dashboard
</Button>
</Group>
<Text fw={700} size="lg" c="orange.6">
PENGATURAN AKUN
</Text>
<Box w={150} />
</Group>
</AppShell.Header>
<AppShell.Main>
<Outlet />
</AppShell.Main>
</AppShell>
);
}
import { Box } from "@mantine/core";

View File

@@ -1,66 +1,6 @@
import { AppShell, Burger, Group, useMantineColorScheme } from "@mantine/core";
import { createFileRoute } from "@tanstack/react-router";
import { Header } from "@/components/header";
import { Sidebar } from "@/components/sidebar";
import SosialPage from "@/components/sosial-page";
import { useSidebarFullscreen } from "@/hooks/use-sidebar-fullscreen";
export const Route = createFileRoute("/sosial")({
component: SosialRoute,
component: SosialPage,
});
function SosialRoute() {
const {
opened,
toggleMobile,
sidebarCollapsed,
toggleSidebar,
handleMainClick,
} = useSidebarFullscreen();
const { colorScheme } = useMantineColorScheme();
const headerBgColor = colorScheme === "dark" ? "#11192D" : "#19355E";
const navbarBgColor = colorScheme === "dark" ? "#11192D" : "white";
const mainBgColor = colorScheme === "dark" ? "#11192D" : "#edf3f8ff";
return (
<AppShell
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened, desktop: sidebarCollapsed },
}}
padding="md"
>
<AppShell.Header bg={headerBgColor}>
<Group h="100%" px="md">
<Burger
opened={opened}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Header onSidebarToggle={toggleSidebar} />
</Group>
</AppShell.Header>
<AppShell.Navbar
p="md"
bg={navbarBgColor}
style={{ display: "flex", flexDirection: "column" }}
>
<div style={{ flex: 1, overflowY: "auto" }}>
<Sidebar />
</div>
</AppShell.Navbar>
<AppShell.Main
bg={mainBgColor}
onClick={handleMainClick}
style={{ cursor: sidebarCollapsed ? "default" : "pointer" }}
>
<SosialPage />
</AppShell.Main>
</AppShell>
);
}