diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b451162
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+dist/
+frontend/dist/
+*.js.map
+# compiled JS artefacts next to TS sources
+frontend/src/**/*.js
+frontend/package-lock.json
+.env
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index f830a89..461d0a0 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -1,9 +1,21 @@
-import { NavLink, useNavigate } from 'react-router-dom'
+import { useState, useEffect } from 'react'
+import { NavLink, useNavigate, useLocation } from 'react-router-dom'
import {
- Banknote, ClipboardList, BarChart2, Wallet, Vault, FileText, Settings, LogOut,
+ Banknote, ClipboardList, BarChart2, Wallet, Vault, FileText, Settings, LogOut, Menu,
} from 'lucide-react'
import { can, type User, type CashupCap } from '../types'
+function useIsMobile() {
+ const [isMobile, setIsMobile] = useState(() => window.innerWidth < 700)
+ useEffect(() => {
+ const mq = window.matchMedia('(max-width: 699px)')
+ const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches)
+ mq.addEventListener('change', handler)
+ return () => mq.removeEventListener('change', handler)
+ }, [])
+ return isMobile
+}
+
interface Props {
user: User
children: React.ReactNode
@@ -22,6 +34,11 @@ const navItems: { to: string; label: string; icon: typeof Banknote; cap?: Cashup
export function Layout({ user, children }: Props) {
const navigate = useNavigate()
+ const location = useLocation()
+ const isMobile = useIsMobile()
+ const [menuOpen, setMenuOpen] = useState(false)
+
+ useEffect(() => { setMenuOpen(false) }, [location.pathname])
async function logout() {
await fetch('/cashup/api/auth/logout', { method: 'POST', credentials: 'include' })
@@ -29,49 +46,80 @@ export function Layout({ user, children }: Props) {
window.location.reload()
}
+ const sidebar = (
+
+ )
+
return (
-
- {/* Sidebar */}
-