diff --git a/libs/ui/src/presentation/components/base/ContentContainer.tsx b/libs/ui/src/presentation/components/base/ContentContainer.tsx
new file mode 100644
index 00000000..1ccc2228
--- /dev/null
+++ b/libs/ui/src/presentation/components/base/ContentContainer.tsx
@@ -0,0 +1,12 @@
+import { YStack } from 'tamagui';
+import { ReactNode } from 'react';
+
+type ContentContainerProps = {
+ children: ReactNode;
+};
+
+export const ContentContainer = ({ children }: ContentContainerProps) => (
+
+ {children}
+
+);
diff --git a/libs/ui/src/presentation/components/base/Layout.tsx b/libs/ui/src/presentation/components/base/Layout.tsx
index c2e20430..1f72a635 100644
--- a/libs/ui/src/presentation/components/base/Layout.tsx
+++ b/libs/ui/src/presentation/components/base/Layout.tsx
@@ -1,7 +1,9 @@
-import { PortalProvider, XStack, YStack } from 'tamagui';
+import { Menu } from '@tamagui/lucide-icons';
+import { Button, PortalProvider, XStack, YStack, useMedia } from 'tamagui';
import { Sidebar } from './Sidebar';
import { Navbar } from './Navbar/Navbar';
-import { ReactNode } from 'react';
+import { ContentContainer } from './ContentContainer';
+import { ReactNode, useEffect, useState } from 'react';
export type LayoutProps = {
children: React.ReactNode;
@@ -18,18 +20,53 @@ export const Layout = ({
rightActionItem,
onLogoutPress,
}: LayoutProps) => {
+ const media = useMedia();
+ const [isSidebarShown, setIsSidebarShown] = useState(false);
+
+ // Default: open on desktop, closed on mobile/tablet
+ useEffect(() => {
+ setIsSidebarShown(!!media.gtMd);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []); // Intentionally runs once on mount with initial media value
+
+ const onToggleSidebar = () => setIsSidebarShown((prev) => !prev);
+
+ const hamburgerButton = (
+
+ );
+
return (
-
+
-
- {children}
+
+ {children}
diff --git a/libs/ui/src/presentation/components/base/Navbar/Navbar.tsx b/libs/ui/src/presentation/components/base/Navbar/Navbar.tsx
index 1cf25c9c..605223a8 100644
--- a/libs/ui/src/presentation/components/base/Navbar/Navbar.tsx
+++ b/libs/ui/src/presentation/components/base/Navbar/Navbar.tsx
@@ -7,21 +7,26 @@ export type NavbarProps = {
title: string;
showBackButton?: boolean;
rightActionItem?: ReactNode;
+ leftActionItem?: ReactNode;
};
export const Navbar = ({
title,
showBackButton,
rightActionItem,
+ leftActionItem,
}: NavbarProps) => {
const { onBackButtonPress } = useNavbarState();
return (
-
+
+ {leftActionItem}
{showBackButton && (
)}
- {title}
+
+ {title}
+
{rightActionItem}
diff --git a/libs/ui/src/presentation/components/base/ResponsiveStack.tsx b/libs/ui/src/presentation/components/base/ResponsiveStack.tsx
new file mode 100644
index 00000000..e726f8ac
--- /dev/null
+++ b/libs/ui/src/presentation/components/base/ResponsiveStack.tsx
@@ -0,0 +1,8 @@
+import { XStack } from 'tamagui';
+import type { XStackProps } from 'tamagui';
+
+export type ResponsiveStackProps = XStackProps;
+
+export const ResponsiveStack = (props: ResponsiveStackProps) => (
+
+);
diff --git a/libs/ui/src/presentation/components/base/Sidebar/Sidebar.state.tsx b/libs/ui/src/presentation/components/base/Sidebar/Sidebar.state.tsx
index 51c4ce0d..1b01b099 100644
--- a/libs/ui/src/presentation/components/base/Sidebar/Sidebar.state.tsx
+++ b/libs/ui/src/presentation/components/base/Sidebar/Sidebar.state.tsx
@@ -7,7 +7,6 @@ import {
} from '@tamagui/lucide-icons';
import { NamedExoticComponent, useEffect, useState } from 'react';
import { useRouter } from 'solito/router';
-import {} from 'solito';
type MenuItem = {
title: string;
@@ -61,17 +60,14 @@ const items: MenuItem[] = [
];
export const useSidebarState = () => {
- const [isShown, setIsShown] = useState(true);
-
- const onToggleButtonPress = () => setIsShown((prev) => !prev);
-
const router = useRouter();
-
const [accordionValue, setAccordionValue] = useState();
const [currentPath, setCurrentPath] = useState();
useEffect(() => {
- setCurrentPath(window.location.pathname);
+ if (typeof window !== 'undefined') {
+ setCurrentPath(window.location.pathname);
+ }
}, []);
useEffect(() => {
@@ -89,8 +85,6 @@ export const useSidebarState = () => {
}, [currentPath]);
return {
- isShown,
- onToggleButtonPress,
router,
items,
accordionValue,
diff --git a/libs/ui/src/presentation/components/base/Sidebar/Sidebar.tsx b/libs/ui/src/presentation/components/base/Sidebar/Sidebar.tsx
index f2f058dc..62d4812f 100644
--- a/libs/ui/src/presentation/components/base/Sidebar/Sidebar.tsx
+++ b/libs/ui/src/presentation/components/base/Sidebar/Sidebar.tsx
@@ -1,6 +1,5 @@
import {
ChevronDown,
- ChevronsLeft,
ChevronsRight,
LogOut,
} from '@tamagui/lucide-icons';
@@ -14,130 +13,146 @@ import {
XStack,
YGroup,
YStack,
+ useMedia,
} from 'tamagui';
import { useSidebarState } from './Sidebar.state';
import { Link } from 'solito/link';
+import { Sheet } from '../Sheet';
export type SidebarProps = {
onLogoutPress: () => void;
+ isShown: boolean;
+ onToggle: () => void;
+ onOpenChange: (isOpen: boolean) => void;
};
export const Sidebar = (props: SidebarProps) => {
- const {
- isShown,
- onToggleButtonPress,
- router,
- items,
- accordionValue,
- setAccordionValue,
- currentPath,
- } = useSidebarState();
+ const { isShown, onToggle, onOpenChange } = props;
+ const media = useMedia();
+ const { router, items, accordionValue, setAccordionValue, currentPath } =
+ useSidebarState();
+ const sidebarContent = (
+
+
+
+ Gatherloop POS
+
+
+
+ {items.map((item) => (
+
+ {
+ if (item.path) {
+ event.preventDefault();
+ router.push(item.path);
+ }
+ }}
+ >
+ {({ open }: { open: boolean }) => (
+ <>
+
+
+ {item.title}
+
+ {item.subItems && (
+
+
+
+ )}
+ >
+ )}
+
+
+ {item.subItems && (
+
+
+ {item.subItems.map((subItem, index) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+ )}
+
+ ))}
+
+
+
+
+
+
+
+ );
+
+ // Mobile/tablet (md and below): off-canvas Sheet drawer
+ if (!media.gtMd) {
+ return (
+
+ {sidebarContent}
+
+ );
+ }
+
+ // Desktop (gtMd): classic inline sidebar
return (
<>
-
-
-
- Gatherloop POS
-
-
-
- {items.map((item) => (
-
- {
- if (item.path) {
- event.preventDefault();
- router.push(item.path);
- }
- }}
- >
- {({ open }: { open: boolean }) => (
- <>
-
-
- {item.title}
-
- {item.subItems && (
-
-
-
- )}
- >
- )}
-
-
- {item.subItems && (
-
-
- {item.subItems.map((subItem, index) => (
-
-
-
- {subItem.title}
-
-
-
- ))}
-
-
- )}
-
- ))}
-
-
-
-
-
-
-
+ {sidebarContent}
{!isShown && (
+ />
)}
>
);
diff --git a/libs/ui/src/presentation/components/base/index.tsx b/libs/ui/src/presentation/components/base/index.tsx
index 4c3b8e72..c43ee292 100644
--- a/libs/ui/src/presentation/components/base/index.tsx
+++ b/libs/ui/src/presentation/components/base/index.tsx
@@ -14,3 +14,5 @@ export * from './Markdown';
export * from './Tabs';
export * from './ConfirmationAlert';
export * from './SkeletonView';
+export * from './ContentContainer';
+export * from './ResponsiveStack';