@rqdhw3n/react-admin-layout 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),u=require("react"),T=require("react-router-dom"),x=require("lucide-react");function re(t){if(typeof window>"u")return{};try{const a=localStorage.getItem(t);return a?JSON.parse(a):{}}catch{return{}}}function ne(t,a){if(!(typeof window>"u"))try{localStorage.setItem(t,JSON.stringify(a))}catch{}}const U=u.createContext(null);function $({children:t,storageKey:a="rqdhw3n-admin-layout",defaultCollapsed:s=!1,permissions:r={},initialBreadcrumbs:l=[],initialActivePath:i="/",initialDarkMode:n}){const d=re(a),[o,b]=u.useState(d.sidebarCollapsed??s),[f,v]=u.useState(!1),[h,c]=u.useState(d.darkMode??n??!1),[p,N]=u.useState(l),[S,g]=u.useState(i);u.useEffect(()=>{ne(a,{sidebarCollapsed:o,darkMode:h})},[a,o,h]),u.useEffect(()=>{const y=document.documentElement;h?y.classList.add("dark"):y.classList.remove("dark")},[h]);const k=u.useCallback(()=>b(y=>!y),[]),j=u.useCallback(()=>v(y=>!y),[]),C=u.useCallback(()=>c(y=>!y),[]),A=u.useMemo(()=>({sidebarCollapsed:o,mobileOpen:f,darkMode:h,toggleSidebar:k,setSidebarCollapsed:b,toggleMobile:j,setMobileOpen:v,toggleDarkMode:C,setDarkMode:c,permissions:r,breadcrumbs:p,setBreadcrumbs:N,activePath:S,setActivePath:g}),[o,f,h,k,j,C,r,p,S]);return e.jsx(U.Provider,{value:A,children:t})}function w(){const t=u.useContext(U);if(!t)throw new Error("useLayoutContext must be used within LayoutProvider / AdminLayout");return t}function M(){const{sidebarCollapsed:t,mobileOpen:a,toggleSidebar:s,setSidebarCollapsed:r,toggleMobile:l,setMobileOpen:i}=w();return{collapsed:t,mobileOpen:a,toggle:s,setCollapsed:r,toggleMobile:l,setMobileOpen:i,closeMobile:()=>i(!1)}}function P(t){const a=t.split("/").filter(Boolean);return a.length?a.map((s,r)=>{const l="/"+a.slice(0,r+1).join("/");return{label:s.replace(/-/g," ").replace(/\b\w/g,n=>n.toUpperCase()),path:r<a.length-1?l:void 0}}):[{label:"Home",path:"/"}]}function z(t){var a,s,r="";if(typeof t=="string"||typeof t=="number")r+=t;else if(typeof t=="object")if(Array.isArray(t)){var l=t.length;for(a=0;a<l;a++)t[a]&&(s=z(t[a]))&&(r&&(r+=" "),r+=s)}else for(s in t)t[s]&&(r&&(r+=" "),r+=s);return r}function ie(){for(var t,a,s=0,r="",l=arguments.length;s<l;s++)(t=arguments[s])&&(a=z(t))&&(r&&(r+=" "),r+=a);return r}function m(...t){return ie(t)}function G({children:t}){const{pathname:a}=T.useLocation(),{setActivePath:s,setBreadcrumbs:r,breadcrumbs:l}=w();return u.useEffect(()=>{s(a),l.length||r(P(a))},[a,s,r,l.length]),e.jsx(e.Fragment,{children:t})}function H({children:t,className:a}){return e.jsx("footer",{className:m("mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted",a),children:t??e.jsxs("p",{className:"text-center",children:["© ",new Date().getFullYear()," Admin Layout · Built with @rqdhw3n/react-admin-layout"]})})}function le(t){const a=t.indexOf(".");return a===-1?{resource:t,action:"view"}:{resource:t.slice(0,a),action:t.slice(a+1)}}function J(t,a){if(!a)return!0;const{resource:s,action:r}=le(a),l=t[s];return l!=null&&l.length?l.includes(r)||l.includes("*"):!1}function L(t,a){return t.filter(s=>J(a,s.permission)).map(s=>({...s,children:s.children?L(s.children,a):void 0})).filter(s=>{var r;return!((r=s.children)!=null&&r.length)||s.children.length>0||s.path})}function O({label:t,children:a,collapsed:s,className:r}){return e.jsxs("div",{className:m("mb-4",r),children:[t&&!s&&e.jsx("p",{className:"mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-admin-muted",children:t}),e.jsx("ul",{className:"space-y-0.5",children:a})]})}function q(t,a){return t?a===t||a.startsWith(`${t}/`):!1}function B({item:t,collapsed:a=!1,depth:s=0,activePath:r="/",onNavigate:l,LinkComponent:i,onItemClick:n}){var g,k;const d=!!((g=t.children)!=null&&g.length),o=t.path?q(t.path,r):!1,b=(k=t.children)==null?void 0:k.some(j=>j.path&&q(j.path,r)),[f,v]=u.useState(o||b),h=t.icon,c=m("group flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary",o||b?"bg-admin-primary text-white shadow-sm":"text-admin-muted hover:bg-admin-bg hover:text-admin-text",t.disabled&&"pointer-events-none opacity-50",s>0&&"text-[13px] py-2"),p=e.jsxs(e.Fragment,{children:[h&&e.jsx(h,{className:m("h-[18px] w-[18px] shrink-0",o||b?"text-white":""),"aria-hidden":!0}),!a&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"flex-1 truncate text-left",children:t.label}),t.badge!=null&&e.jsx("span",{className:m("rounded-full px-2 py-0.5 text-[11px] font-semibold",o?"bg-white/20 text-white":"bg-admin-primary/10 text-admin-primary"),children:t.badge}),d&&e.jsx(x.ChevronDown,{className:m("h-4 w-4 shrink-0 transition-transform",f&&"rotate-180"),"aria-hidden":!0})]})]}),N=()=>{if(d){v(j=>!j);return}t.path&&(l==null||l(t.path),n==null||n())},S=t.path&&!d&&i?e.jsx(i,{to:t.path,className:c,onClick:()=>{n==null||n()},children:p}):e.jsx("button",{type:"button",className:c,onClick:N,disabled:t.disabled,children:p});return e.jsxs("li",{children:[S,d&&f&&!a&&e.jsx("ul",{className:"mt-1 space-y-0.5 border-l border-admin-border ml-5 pl-2",children:t.children.map(j=>e.jsx(B,{item:j,collapsed:a,depth:s+1,activePath:r,onNavigate:l,LinkComponent:i,onItemClick:n},j.label+(j.path??"")))})]})}function Q({items:t,appName:a="Admin",appLogo:s,onNavigate:r,activePath:l,LinkComponent:i}){const{mobileOpen:n,closeMobile:d}=M(),{permissions:o,activePath:b}=w(),f=l??b,v=L(t,o);if(u.useEffect(()=>{if(!n)return;const c=p=>{p.key==="Escape"&&d()};return document.addEventListener("keydown",c),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",c),document.body.style.overflow=""}},[n,d]),!n)return null;const h=c=>{r==null||r(c),d()};return e.jsxs("div",{className:"fixed inset-0 z-50 lg:hidden",role:"dialog","aria-modal":"true",children:[e.jsx("button",{type:"button",className:"absolute inset-0 bg-slate-900/50 backdrop-blur-sm animate-fade-in","aria-label":"Close menu",onClick:d}),e.jsxs("aside",{className:m("absolute left-0 top-0 flex h-full w-[min(280px,85vw)] flex-col","border-r border-admin-border bg-admin-surface shadow-admin-lg animate-slide-in"),children:[e.jsxs("div",{className:"flex h-16 items-center justify-between border-b border-admin-border px-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[s??e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white",children:a.charAt(0)}),e.jsx("span",{className:"font-semibold text-admin-text",children:a})]}),e.jsx("button",{type:"button",onClick:d,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Close sidebar",children:e.jsx(x.X,{className:"h-5 w-5"})})]}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{children:v.map(c=>e.jsx(B,{item:c,activePath:f,onNavigate:h,LinkComponent:i,onItemClick:d},c.label+(c.path??"")))})})]})]})}function W({items:t,className:a,LinkComponent:s,onNavigate:r}){if(!t.length)return null;const l=(i,n)=>{const d=m("text-sm transition-colors",n?"font-medium text-admin-text":"text-admin-muted hover:text-admin-text");return!i.path||n?e.jsx("span",{className:d,children:i.label}):s?e.jsx(s,{to:i.path,className:d,children:i.label}):e.jsx("button",{type:"button",className:d,onClick:()=>r==null?void 0:r(i.path),children:i.label})};return e.jsxs("nav",{"aria-label":"Breadcrumb",className:m("flex items-center gap-1.5",a),children:[e.jsx(x.Home,{className:"h-4 w-4 text-admin-muted shrink-0","aria-hidden":!0}),t.map((i,n)=>{const d=n===t.length-1;return e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(x.ChevronRight,{className:"h-3.5 w-3.5 text-admin-muted","aria-hidden":!0}),l(i,d)]},`${i.label}-${n}`)})]})}function X({placeholder:t="Search...",onSearch:a,className:s}){const[r,l]=u.useState(""),i=n=>{n.preventDefault(),a==null||a(r.trim())};return e.jsxs("form",{onSubmit:i,className:m("relative w-full max-w-md",s),children:[e.jsx(x.Search,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted","aria-hidden":!0}),e.jsx("input",{type:"search",value:r,onChange:n=>l(n.target.value),placeholder:t,className:m("h-10 w-full rounded-xl border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text","placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary/20"),"aria-label":"Search"})]})}function Y({notifications:t=[],className:a}){const[s,r]=u.useState(!1),l=u.useRef(null),i=t.filter(n=>!n.read).length;return u.useEffect(()=>{const n=d=>{l.current&&!l.current.contains(d.target)&&r(!1)};return document.addEventListener("mousedown",n),()=>document.removeEventListener("mousedown",n)},[]),e.jsxs("div",{ref:l,className:m("relative",a),children:[e.jsxs("button",{type:"button",onClick:()=>r(n=>!n),className:m("relative inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-label":`Notifications${i?`, ${i} unread`:""}`,"aria-expanded":s,children:[e.jsx(x.Bell,{className:"h-4 w-4","aria-hidden":!0}),i>0&&e.jsx("span",{className:"absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white",children:i>9?"9+":i})]}),s&&e.jsxs("div",{className:"absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in",children:[e.jsx("div",{className:"border-b border-admin-border px-4 py-3",children:e.jsx("p",{className:"text-sm font-semibold text-admin-text",children:"Notifications"})}),e.jsx("ul",{className:"max-h-72 overflow-y-auto p-2",children:t.length===0?e.jsx("li",{className:"px-3 py-6 text-center text-sm text-admin-muted",children:"No notifications"}):t.map(n=>e.jsxs("li",{className:m("rounded-lg px-3 py-2.5 transition-colors hover:bg-admin-bg",!n.read&&"bg-admin-primary/5"),children:[e.jsx("p",{className:"text-sm font-medium text-admin-text",children:n.title}),n.description&&e.jsx("p",{className:"mt-0.5 text-xs text-admin-muted line-clamp-2",children:n.description}),n.time&&e.jsx("p",{className:"mt-1 text-[11px] text-admin-muted",children:n.time})]},n.id))})]})]})}function _(){const{darkMode:t,toggleDarkMode:a,setDarkMode:s}=w();return{isDark:t,toggle:a,setDarkMode:s}}function K({className:t}){const{isDark:a,toggle:s}=_();return e.jsx("button",{type:"button",onClick:s,className:m("inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2",t),"aria-label":a?"Switch to light mode":"Switch to dark mode",children:a?e.jsx(x.Sun,{className:"h-4 w-4","aria-hidden":!0}):e.jsx(x.Moon,{className:"h-4 w-4","aria-hidden":!0})})}function V({user:t,onLogout:a,onSettings:s,className:r}){const[l,i]=u.useState(!1),n=u.useRef(null);u.useEffect(()=>{const o=b=>{n.current&&!n.current.contains(b.target)&&i(!1)};return document.addEventListener("mousedown",o),()=>document.removeEventListener("mousedown",o)},[]);const d=t.name.split(" ").map(o=>o[0]).join("").slice(0,2).toUpperCase();return e.jsxs("div",{ref:n,className:m("relative",r),children:[e.jsxs("button",{type:"button",onClick:()=>i(o=>!o),className:m("flex items-center gap-2 rounded-xl border border-admin-border bg-admin-surface px-2 py-1.5","transition-colors hover:bg-admin-bg focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-expanded":l,"aria-haspopup":"menu",children:[t.avatar?e.jsx("img",{src:t.avatar,alt:"",className:"h-8 w-8 rounded-lg object-cover"}):e.jsx("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-admin-primary text-xs font-semibold text-white",children:d}),e.jsxs("div",{className:"hidden text-left sm:block",children:[e.jsx("p",{className:"text-sm font-medium text-admin-text leading-tight",children:t.name}),e.jsx("p",{className:"text-xs text-admin-muted leading-tight",children:t.email})]}),e.jsx(x.ChevronDown,{className:"hidden h-4 w-4 text-admin-muted sm:block","aria-hidden":!0})]}),l&&e.jsxs("div",{role:"menu",className:"absolute right-0 top-full z-50 mt-2 w-56 rounded-xl border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in",children:[e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",onClick:()=>{s==null||s(),i(!1)},children:[e.jsx(x.Settings,{className:"h-4 w-4 text-admin-muted"}),"Settings"]}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",children:[e.jsx(x.User,{className:"h-4 w-4 text-admin-muted"}),"Profile"]}),e.jsx("hr",{className:"my-1 border-admin-border"}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30",onClick:()=>{a==null||a(),i(!1)},children:[e.jsx(x.LogOut,{className:"h-4 w-4"}),"Log out"]})]})]})}function Z({user:t,notifications:a=[],onSearch:s,searchPlaceholder:r="Search...",showSearch:l=!0,className:i,breadcrumbs:n=[],onMenuClick:d,sidebarCollapsed:o,onToggleSidebar:b}){return e.jsxs("header",{className:m("sticky top-0 z-40 flex h-16 shrink-0 items-center gap-4 border-b border-admin-border","admin-glass bg-admin-surface px-4 lg:px-6",i),children:[e.jsx("button",{type:"button",className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:hidden",onClick:d,"aria-label":"Open menu",children:e.jsx(x.Menu,{className:"h-5 w-5"})}),e.jsx("button",{type:"button",className:"hidden rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:inline-flex",onClick:b,"aria-label":o?"Expand sidebar":"Collapse sidebar",children:o?e.jsx(x.PanelLeftOpen,{className:"h-5 w-5"}):e.jsx(x.PanelLeftClose,{className:"h-5 w-5"})}),e.jsx("div",{className:"hidden min-w-0 flex-1 md:block",children:e.jsx(W,{items:n})}),e.jsxs("div",{className:"flex flex-1 items-center justify-end gap-2 sm:gap-3",children:[l&&e.jsx("div",{className:"hidden flex-1 sm:flex sm:max-w-md",children:e.jsx(X,{placeholder:r,onSearch:s})}),e.jsx(K,{}),e.jsx(Y,{notifications:a}),t&&e.jsx(V,{user:t})]})]})}function I({items:t,collapsed:a,appName:s="Admin",appLogo:r,className:l,onNavigate:i,activePath:n,LinkComponent:d}){const{collapsed:o,toggle:b,closeMobile:f}=M(),{permissions:v,activePath:h}=w(),c=a??o,p=n??h,N=L(t,v),S=g=>{i==null||i(g),f()};return e.jsxs("aside",{className:m("hidden lg:flex flex-col border-r border-admin-border bg-admin-surface transition-all duration-300 ease-in-out",c?"w-[var(--admin-sidebar-collapsed)]":"w-[var(--admin-sidebar-width)]",l),"aria-label":"Sidebar navigation",children:[e.jsxs("div",{className:m("flex h-16 items-center border-b border-admin-border px-4",c?"justify-center":"justify-between gap-2"),children:[e.jsxs("div",{className:m("flex items-center gap-3 min-w-0",c&&"justify-center"),children:[r??e.jsx("div",{className:"flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white shadow-admin",children:s.charAt(0)}),!c&&e.jsx("span",{className:"truncate text-base font-semibold text-admin-text",children:s})]}),!c&&e.jsx("button",{type:"button",onClick:b,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg hover:text-admin-text focus-visible:ring-2 focus-visible:ring-admin-primary","aria-label":"Collapse sidebar",children:e.jsx(x.PanelLeftClose,{className:"h-4 w-4"})})]}),c&&e.jsx("div",{className:"flex justify-center py-2 border-b border-admin-border",children:e.jsx("button",{type:"button",onClick:b,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Expand sidebar",children:e.jsx(x.PanelLeftOpen,{className:"h-4 w-4"})})}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{collapsed:c,children:N.map(g=>e.jsx(B,{item:g,collapsed:c,activePath:p,onNavigate:S,LinkComponent:d},g.label+(g.path??"")))})})]})}function F({children:t,sidebarItems:a,appName:s="Admin",appLogo:r,user:l,notifications:i=[],footer:n,showFooter:d=!0,showSearch:o=!0,searchPlaceholder:b,onSearch:f,breadcrumbs:v,contentClassName:h,activePath:c,onNavigate:p,LinkComponent:N,enableRouter:S=!1}){const{sidebarCollapsed:g,toggleSidebar:k,toggleMobile:j,breadcrumbs:C,setBreadcrumbs:A,activePath:y,setActivePath:ee}=w(),{closeMobile:te}=M(),D=c??y,ae=v??C,R=E=>{ee(E),v||A(P(E)),p==null||p(E),te()},se=g?"lg:pl-[var(--admin-sidebar-collapsed)]":"lg:pl-[var(--admin-sidebar-width)]";return e.jsxs("div",{className:"admin-layout-root min-h-screen bg-admin-bg text-admin-text",children:[e.jsx(I,{items:a,appName:s,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsx(Q,{items:a,appName:s,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsxs("div",{className:m("flex min-h-screen flex-col transition-all duration-300",se),children:[e.jsx(Z,{user:l,notifications:i,onSearch:f,searchPlaceholder:b,showSearch:o,breadcrumbs:ae,onMenuClick:j,sidebarCollapsed:g,onToggleSidebar:k}),e.jsx("main",{className:m("flex-1 p-4 lg:p-6",h),children:t}),d&&e.jsx(H,{children:n})]})]})}function de(t){const{storageKey:a="rqdhw3n-admin-layout",defaultCollapsed:s=!1,initialDarkMode:r=!1,permissions:l={},breadcrumbs:i=[],activePath:n="/",enableRouter:d=!1,children:o,...b}=t,f=u.useMemo(()=>i.length?i:P(n),[i,n]);return e.jsx($,{storageKey:a,defaultCollapsed:s,permissions:l,initialBreadcrumbs:f,initialActivePath:n,initialDarkMode:r,children:d?e.jsx(G,{children:e.jsx(F,{...b,enableRouter:d,children:o})}):e.jsx(F,{...b,enableRouter:d,children:o})})}function oe({to:t,className:a,children:s,onClick:r}){return e.jsx(T.NavLink,{to:t,className:a,onClick:r,children:s})}function ce(){return w()}function ue(){const{breadcrumbs:t,setBreadcrumbs:a}=w(),s=u.useCallback(r=>a(r),[a]);return{items:t,setBreadcrumbs:s}}exports.AdminLayout=de;exports.AdminLayoutRouterSync=G;exports.Breadcrumbs=W;exports.Footer=H;exports.LayoutProvider=$;exports.MobileSidebar=Q;exports.Navbar=Z;exports.NavbarSearch=X;exports.NotificationMenu=Y;exports.RouterLink=oe;exports.Sidebar=I;exports.SidebarGroup=O;exports.SidebarItem=B;exports.ThemeToggle=K;exports.UserMenu=V;exports.cn=m;exports.filterSidebarByPermissions=L;exports.generateBreadcrumbsFromPath=P;exports.hasPermission=J;exports.useBreadcrumbs=ue;exports.useDarkMode=_;exports.useLayoutContext=w;exports.useLayoutState=ce;exports.useSidebar=M;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),u=require("react"),T=require("react-router-dom"),b=require("lucide-react");function ae(t){if(typeof window>"u")return{};try{const s=localStorage.getItem(t);return s?JSON.parse(s):{}}catch{return{}}}function re(t,s){if(!(typeof window>"u"))try{localStorage.setItem(t,JSON.stringify(s))}catch{}}const U=u.createContext(null);function $({children:t,storageKey:s="rqdhw3n-admin-layout",defaultCollapsed:a=!1,permissions:r={},initialBreadcrumbs:l=[],initialActivePath:i="/",initialDarkMode:n}){const d=ae(s),[o,x]=u.useState(d.sidebarCollapsed??a),[f,v]=u.useState(!1),[h,c]=u.useState(d.darkMode??n??!1),[p,N]=u.useState(l),[S,j]=u.useState(i);u.useEffect(()=>{re(s,{sidebarCollapsed:o,darkMode:h})},[s,o,h]),u.useEffect(()=>{const y=document.documentElement;h?y.classList.add("dark"):y.classList.remove("dark")},[h]);const k=u.useCallback(()=>x(y=>!y),[]),g=u.useCallback(()=>v(y=>!y),[]),C=u.useCallback(()=>c(y=>!y),[]),A=u.useMemo(()=>({sidebarCollapsed:o,mobileOpen:f,darkMode:h,toggleSidebar:k,setSidebarCollapsed:x,toggleMobile:g,setMobileOpen:v,toggleDarkMode:C,setDarkMode:c,permissions:r,breadcrumbs:p,setBreadcrumbs:N,activePath:S,setActivePath:j}),[o,f,h,k,g,C,r,p,S]);return e.jsx(U.Provider,{value:A,children:t})}function w(){const t=u.useContext(U);if(!t)throw new Error("useLayoutContext must be used within LayoutProvider / AdminLayout");return t}function M(){const{sidebarCollapsed:t,mobileOpen:s,toggleSidebar:a,setSidebarCollapsed:r,toggleMobile:l,setMobileOpen:i}=w();return{collapsed:t,mobileOpen:s,toggle:a,setCollapsed:r,toggleMobile:l,setMobileOpen:i,closeMobile:()=>i(!1)}}function P(t){const s=t.split("/").filter(Boolean);return s.length?s.map((a,r)=>{const l="/"+s.slice(0,r+1).join("/");return{label:a.replace(/-/g," ").replace(/\b\w/g,n=>n.toUpperCase()),path:r<s.length-1?l:void 0}}):[{label:"Home",path:"/"}]}function z(t){var s,a,r="";if(typeof t=="string"||typeof t=="number")r+=t;else if(typeof t=="object")if(Array.isArray(t)){var l=t.length;for(s=0;s<l;s++)t[s]&&(a=z(t[s]))&&(r&&(r+=" "),r+=a)}else for(a in t)t[a]&&(r&&(r+=" "),r+=a);return r}function ne(){for(var t,s,a=0,r="",l=arguments.length;a<l;a++)(t=arguments[a])&&(s=z(t))&&(r&&(r+=" "),r+=s);return r}function m(...t){return ne(t)}function G({children:t}){const{pathname:s}=T.useLocation(),{setActivePath:a,setBreadcrumbs:r,breadcrumbs:l}=w();return u.useEffect(()=>{a(s),l.length||r(P(s))},[s,a,r,l.length]),e.jsx(e.Fragment,{children:t})}function H({children:t,className:s}){return e.jsx("footer",{className:m("mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted",s),children:t??e.jsxs("p",{className:"text-center",children:["© ",new Date().getFullYear()," Admin Layout · Built with @rqdhw3n/react-admin-layout"]})})}function ie(t){const s=t.indexOf(".");return s===-1?{resource:t,action:"view"}:{resource:t.slice(0,s),action:t.slice(s+1)}}function J(t,s){if(!s)return!0;const{resource:a,action:r}=ie(s),l=t[a];return l!=null&&l.length?l.includes(r)||l.includes("*"):!1}function L(t,s){return t.filter(a=>J(s,a.permission)).map(a=>({...a,children:a.children?L(a.children,s):void 0})).filter(a=>{var r;return!((r=a.children)!=null&&r.length)||a.children.length>0||a.path})}function O({label:t,children:s,collapsed:a,className:r}){return e.jsxs("div",{className:m("mb-4",r),children:[t&&!a&&e.jsx("p",{className:"mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-admin-muted",children:t}),e.jsx("ul",{className:"space-y-0.5",children:s})]})}function q(t,s){return t?s===t||s.startsWith(`${t}/`):!1}function B({item:t,collapsed:s=!1,depth:a=0,activePath:r="/",onNavigate:l,LinkComponent:i,onItemClick:n}){var j,k;const d=!!((j=t.children)!=null&&j.length),o=t.path?q(t.path,r):!1,x=(k=t.children)==null?void 0:k.some(g=>g.path&&q(g.path,r)),[f,v]=u.useState(o||x),h=t.icon,c=m("group flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary",o||x?"bg-admin-primary text-white shadow-sm":"text-admin-muted hover:bg-admin-bg hover:text-admin-text",t.disabled&&"pointer-events-none opacity-50",a>0&&"text-[13px] py-2"),p=e.jsxs(e.Fragment,{children:[h&&e.jsx(h,{className:m("h-[18px] w-[18px] shrink-0",o||x?"text-white":""),"aria-hidden":!0}),!s&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"flex-1 truncate text-left",children:t.label}),t.badge!=null&&e.jsx("span",{className:m("rounded-full px-2 py-0.5 text-[11px] font-semibold",o?"bg-white/20 text-white":"bg-admin-primary/10 text-admin-primary"),children:t.badge}),d&&e.jsx(b.ChevronDown,{className:m("h-4 w-4 shrink-0 transition-transform",f&&"rotate-180"),"aria-hidden":!0})]})]}),N=()=>{if(d){v(g=>!g);return}t.path&&(l==null||l(t.path),n==null||n())},S=t.path&&!d&&i?e.jsx(i,{to:t.path,className:c,onClick:()=>{n==null||n()},children:p}):e.jsx("button",{type:"button",className:c,onClick:N,disabled:t.disabled,children:p});return e.jsxs("li",{children:[S,d&&f&&!s&&e.jsx("ul",{className:"mt-1 space-y-0.5 border-l border-admin-border ml-5 pl-2",children:t.children.map(g=>e.jsx(B,{item:g,collapsed:s,depth:a+1,activePath:r,onNavigate:l,LinkComponent:i,onItemClick:n},g.label+(g.path??"")))})]})}function Q({items:t,appName:s="Admin",appLogo:a,onNavigate:r,activePath:l,LinkComponent:i}){const{mobileOpen:n,closeMobile:d}=M(),{permissions:o,activePath:x}=w(),f=l??x,v=L(t,o);if(u.useEffect(()=>{if(!n)return;const c=p=>{p.key==="Escape"&&d()};return document.addEventListener("keydown",c),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",c),document.body.style.overflow=""}},[n,d]),!n)return null;const h=c=>{r==null||r(c),d()};return e.jsxs("div",{className:"fixed inset-0 z-50 lg:hidden",role:"dialog","aria-modal":"true",children:[e.jsx("button",{type:"button",className:"absolute inset-0 bg-slate-900/50 backdrop-blur-sm animate-fade-in","aria-label":"Close menu",onClick:d}),e.jsxs("aside",{className:m("absolute left-0 top-0 flex h-full w-[min(280px,85vw)] flex-col","border-r border-admin-border bg-admin-surface shadow-admin-lg animate-slide-in"),children:[e.jsxs("div",{className:"flex h-16 items-center justify-between border-b border-admin-border px-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[a??e.jsx("div",{className:"flex h-9 w-9 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white",children:s.charAt(0)}),e.jsx("span",{className:"font-semibold text-admin-text",children:s})]}),e.jsx("button",{type:"button",onClick:d,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Close sidebar",children:e.jsx(b.X,{className:"h-5 w-5"})})]}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{children:v.map(c=>e.jsx(B,{item:c,activePath:f,onNavigate:h,LinkComponent:i,onItemClick:d},c.label+(c.path??"")))})})]})]})}function W({items:t,className:s,LinkComponent:a,onNavigate:r}){if(!t.length)return null;const l=(i,n)=>{const d=m("text-sm transition-colors",n?"font-medium text-admin-text":"text-admin-muted hover:text-admin-text");return!i.path||n?e.jsx("span",{className:d,children:i.label}):a?e.jsx(a,{to:i.path,className:d,children:i.label}):e.jsx("button",{type:"button",className:d,onClick:()=>r==null?void 0:r(i.path),children:i.label})};return e.jsxs("nav",{"aria-label":"Breadcrumb",className:m("flex items-center gap-1.5",s),children:[e.jsx(b.Home,{className:"h-4 w-4 text-admin-muted shrink-0","aria-hidden":!0}),t.map((i,n)=>{const d=n===t.length-1;return e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(b.ChevronRight,{className:"h-3.5 w-3.5 text-admin-muted","aria-hidden":!0}),l(i,d)]},`${i.label}-${n}`)})]})}function X({placeholder:t="Search...",onSearch:s,className:a}){const[r,l]=u.useState(""),i=n=>{n.preventDefault(),s==null||s(r.trim())};return e.jsxs("form",{onSubmit:i,className:m("relative w-full max-w-md",a),children:[e.jsx(b.Search,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted","aria-hidden":!0}),e.jsx("input",{type:"search",value:r,onChange:n=>l(n.target.value),placeholder:t,className:m("h-10 w-full rounded-xl border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text","placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary/20"),"aria-label":"Search"})]})}function Y({notifications:t=[],className:s}){const[a,r]=u.useState(!1),l=u.useRef(null),i=t.filter(n=>!n.read).length;return u.useEffect(()=>{const n=d=>{l.current&&!l.current.contains(d.target)&&r(!1)};return document.addEventListener("mousedown",n),()=>document.removeEventListener("mousedown",n)},[]),e.jsxs("div",{ref:l,className:m("relative",s),children:[e.jsxs("button",{type:"button",onClick:()=>r(n=>!n),className:m("relative inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-label":`Notifications${i?`, ${i} unread`:""}`,"aria-expanded":a,children:[e.jsx(b.Bell,{className:"h-4 w-4","aria-hidden":!0}),i>0&&e.jsx("span",{className:"absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white",children:i>9?"9+":i})]}),a&&e.jsxs("div",{className:"absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in",children:[e.jsx("div",{className:"border-b border-admin-border px-4 py-3",children:e.jsx("p",{className:"text-sm font-semibold text-admin-text",children:"Notifications"})}),e.jsx("ul",{className:"max-h-72 overflow-y-auto p-2",children:t.length===0?e.jsx("li",{className:"px-3 py-6 text-center text-sm text-admin-muted",children:"No notifications"}):t.map(n=>e.jsxs("li",{className:m("rounded-lg px-3 py-2.5 transition-colors hover:bg-admin-bg",!n.read&&"bg-admin-primary/5"),children:[e.jsx("p",{className:"text-sm font-medium text-admin-text",children:n.title}),n.description&&e.jsx("p",{className:"mt-0.5 text-xs text-admin-muted line-clamp-2",children:n.description}),n.time&&e.jsx("p",{className:"mt-1 text-[11px] text-admin-muted",children:n.time})]},n.id))})]})]})}function _(){const{darkMode:t,toggleDarkMode:s,setDarkMode:a}=w();return{isDark:t,toggle:s,setDarkMode:a}}function K({className:t}){const{isDark:s,toggle:a}=_();return e.jsx("button",{type:"button",onClick:a,className:m("inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border","bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text","focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2",t),"aria-label":s?"Switch to light mode":"Switch to dark mode",children:s?e.jsx(b.Sun,{className:"h-4 w-4","aria-hidden":!0}):e.jsx(b.Moon,{className:"h-4 w-4","aria-hidden":!0})})}function V({user:t,onLogout:s,onSettings:a,className:r}){const[l,i]=u.useState(!1),n=u.useRef(null);u.useEffect(()=>{const o=x=>{n.current&&!n.current.contains(x.target)&&i(!1)};return document.addEventListener("mousedown",o),()=>document.removeEventListener("mousedown",o)},[]);const d=t.name.split(" ").map(o=>o[0]).join("").slice(0,2).toUpperCase();return e.jsxs("div",{ref:n,className:m("relative",r),children:[e.jsxs("button",{type:"button",onClick:()=>i(o=>!o),className:m("flex items-center gap-2 rounded-xl border border-admin-border bg-admin-surface px-2 py-1.5","transition-colors hover:bg-admin-bg focus-visible:ring-2 focus-visible:ring-admin-primary"),"aria-expanded":l,"aria-haspopup":"menu",children:[t.avatar?e.jsx("img",{src:t.avatar,alt:"",className:"h-8 w-8 rounded-lg object-cover"}):e.jsx("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-admin-primary text-xs font-semibold text-white",children:d}),e.jsxs("div",{className:"hidden text-left sm:block",children:[e.jsx("p",{className:"text-sm font-medium text-admin-text leading-tight",children:t.name}),e.jsx("p",{className:"text-xs text-admin-muted leading-tight",children:t.email})]}),e.jsx(b.ChevronDown,{className:"hidden h-4 w-4 text-admin-muted sm:block","aria-hidden":!0})]}),l&&e.jsxs("div",{role:"menu",className:"absolute right-0 top-full z-50 mt-2 w-56 rounded-xl border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in",children:[e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",onClick:()=>{a==null||a(),i(!1)},children:[e.jsx(b.Settings,{className:"h-4 w-4 text-admin-muted"}),"Settings"]}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg",children:[e.jsx(b.User,{className:"h-4 w-4 text-admin-muted"}),"Profile"]}),e.jsx("hr",{className:"my-1 border-admin-border"}),e.jsxs("button",{type:"button",role:"menuitem",className:"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30",onClick:()=>{s==null||s(),i(!1)},children:[e.jsx(b.LogOut,{className:"h-4 w-4"}),"Log out"]})]})]})}function Z({user:t,notifications:s=[],onSearch:a,searchPlaceholder:r="Search...",showSearch:l=!0,className:i,breadcrumbs:n=[],onMenuClick:d,sidebarCollapsed:o,onToggleSidebar:x}){return e.jsxs("header",{className:m("sticky top-0 z-40 flex h-16 shrink-0 items-center gap-4 border-b border-admin-border","admin-glass bg-admin-surface px-4 lg:px-6",i),children:[e.jsx("button",{type:"button",className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:hidden",onClick:d,"aria-label":"Open menu",children:e.jsx(b.Menu,{className:"h-5 w-5"})}),e.jsx("button",{type:"button",className:"hidden rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:inline-flex",onClick:x,"aria-label":o?"Expand sidebar":"Collapse sidebar",children:o?e.jsx(b.PanelLeftOpen,{className:"h-5 w-5"}):e.jsx(b.PanelLeftClose,{className:"h-5 w-5"})}),e.jsx("div",{className:"hidden min-w-0 flex-1 md:block",children:e.jsx(W,{items:n})}),e.jsxs("div",{className:"flex flex-1 items-center justify-end gap-2 sm:gap-3",children:[l&&e.jsx("div",{className:"hidden flex-1 sm:flex sm:max-w-md",children:e.jsx(X,{placeholder:r,onSearch:a})}),e.jsx(K,{}),e.jsx(Y,{notifications:s}),t&&e.jsx(V,{user:t})]})]})}function I({items:t,collapsed:s,appName:a="Admin",appLogo:r,className:l,onNavigate:i,activePath:n,LinkComponent:d}){const{collapsed:o,toggle:x,closeMobile:f}=M(),{permissions:v,activePath:h}=w(),c=s??o,p=n??h,N=L(t,v),S=j=>{i==null||i(j),f()};return e.jsxs("aside",{className:m("hidden lg:flex shrink-0 flex-col border-r border-admin-border bg-admin-surface","h-screen sticky top-0 transition-[width] duration-300 ease-in-out",c?"w-[var(--admin-sidebar-collapsed)]":"w-[var(--admin-sidebar-width)]",l),"aria-label":"Sidebar navigation",children:[e.jsxs("div",{className:m("flex h-16 items-center border-b border-admin-border px-4",c?"justify-center":"justify-between gap-2"),children:[e.jsxs("div",{className:m("flex items-center gap-3 min-w-0",c&&"justify-center"),children:[r??e.jsx("div",{className:"flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white shadow-admin",children:a.charAt(0)}),!c&&e.jsx("span",{className:"truncate text-base font-semibold text-admin-text",children:a})]}),!c&&e.jsx("button",{type:"button",onClick:x,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg hover:text-admin-text focus-visible:ring-2 focus-visible:ring-admin-primary","aria-label":"Collapse sidebar",children:e.jsx(b.PanelLeftClose,{className:"h-4 w-4"})})]}),c&&e.jsx("div",{className:"flex justify-center py-2 border-b border-admin-border",children:e.jsx("button",{type:"button",onClick:x,className:"rounded-lg p-2 text-admin-muted hover:bg-admin-bg","aria-label":"Expand sidebar",children:e.jsx(b.PanelLeftOpen,{className:"h-4 w-4"})})}),e.jsx("nav",{className:"flex-1 overflow-y-auto p-3",children:e.jsx(O,{collapsed:c,children:N.map(j=>e.jsx(B,{item:j,collapsed:c,activePath:p,onNavigate:S,LinkComponent:d},j.label+(j.path??"")))})})]})}function F({children:t,sidebarItems:s,appName:a="Admin",appLogo:r,user:l,notifications:i=[],footer:n,showFooter:d=!0,showSearch:o=!0,searchPlaceholder:x,onSearch:f,breadcrumbs:v,contentClassName:h,activePath:c,onNavigate:p,LinkComponent:N,enableRouter:S=!1}){const{sidebarCollapsed:j,toggleSidebar:k,toggleMobile:g,breadcrumbs:C,setBreadcrumbs:A,activePath:y,setActivePath:ee}=w(),{closeMobile:te}=M(),D=c??y,se=v??C,R=E=>{ee(E),v||A(P(E)),p==null||p(E),te()};return e.jsxs("div",{className:"admin-layout-root min-h-screen bg-admin-bg text-admin-text",children:[e.jsx(Q,{items:s,appName:a,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsxs("div",{className:"flex min-h-screen w-full",children:[e.jsx(I,{items:s,appName:a,appLogo:r,activePath:D,onNavigate:R,LinkComponent:N}),e.jsxs("div",{className:"flex min-h-screen min-w-0 flex-1 flex-col",children:[e.jsx(Z,{user:l,notifications:i,onSearch:f,searchPlaceholder:x,showSearch:o,breadcrumbs:se,onMenuClick:g,sidebarCollapsed:j,onToggleSidebar:k}),e.jsx("main",{className:m("flex-1 p-4 lg:p-6",h),children:t}),d&&e.jsx(H,{children:n})]})]})]})}function le(t){const{storageKey:s="rqdhw3n-admin-layout",defaultCollapsed:a=!1,initialDarkMode:r=!1,permissions:l={},breadcrumbs:i=[],activePath:n="/",enableRouter:d=!1,children:o,...x}=t,f=u.useMemo(()=>i.length?i:P(n),[i,n]);return e.jsx($,{storageKey:s,defaultCollapsed:a,permissions:l,initialBreadcrumbs:f,initialActivePath:n,initialDarkMode:r,children:d?e.jsx(G,{children:e.jsx(F,{...x,enableRouter:d,children:o})}):e.jsx(F,{...x,enableRouter:d,children:o})})}function de({to:t,className:s,children:a,onClick:r}){return e.jsx(T.NavLink,{to:t,className:s,onClick:r,children:a})}function oe(){return w()}function ce(){const{breadcrumbs:t,setBreadcrumbs:s}=w(),a=u.useCallback(r=>s(r),[s]);return{items:t,setBreadcrumbs:a}}exports.AdminLayout=le;exports.AdminLayoutRouterSync=G;exports.Breadcrumbs=W;exports.Footer=H;exports.LayoutProvider=$;exports.MobileSidebar=Q;exports.Navbar=Z;exports.NavbarSearch=X;exports.NotificationMenu=Y;exports.RouterLink=de;exports.Sidebar=I;exports.SidebarGroup=O;exports.SidebarItem=B;exports.ThemeToggle=K;exports.UserMenu=V;exports.cn=m;exports.filterSidebarByPermissions=L;exports.generateBreadcrumbsFromPath=P;exports.hasPermission=J;exports.useBreadcrumbs=ce;exports.useDarkMode=_;exports.useLayoutContext=w;exports.useLayoutState=oe;exports.useSidebar=M;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/utils/storage.ts","../src/context/LayoutContext.tsx","../src/hooks/useSidebar.ts","../src/utils/breadcrumbs.ts","../node_modules/clsx/dist/clsx.mjs","../src/utils/cn.ts","../src/components/AdminLayoutRouterSync.tsx","../src/components/Footer.tsx","../src/utils/permissions.ts","../src/components/SidebarGroup.tsx","../src/components/SidebarItem.tsx","../src/components/MobileSidebar.tsx","../src/components/Breadcrumbs.tsx","../src/components/NavbarSearch.tsx","../src/components/NotificationMenu.tsx","../src/hooks/useDarkMode.ts","../src/components/ThemeToggle.tsx","../src/components/UserMenu.tsx","../src/components/Navbar.tsx","../src/components/Sidebar.tsx","../src/components/AdminLayout.tsx","../src/components/RouterLink.tsx","../src/hooks/useLayoutState.ts","../src/hooks/useBreadcrumbs.ts"],"sourcesContent":["import type { LayoutState } from '../types'\r\n\r\nexport type PersistedLayout = Pick<LayoutState, 'sidebarCollapsed' | 'darkMode'>\r\n\r\nexport function loadLayoutState(key: string): Partial<PersistedLayout> {\r\n if (typeof window === 'undefined') return {}\r\n try {\r\n const raw = localStorage.getItem(key)\r\n return raw ? (JSON.parse(raw) as Partial<PersistedLayout>) : {}\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\nexport function saveLayoutState(key: string, state: PersistedLayout): void {\r\n if (typeof window === 'undefined') return\r\n try {\r\n localStorage.setItem(key, JSON.stringify(state))\r\n } catch {\r\n /* ignore quota errors */\r\n }\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n type ReactNode,\r\n} from 'react'\r\nimport type { BreadcrumbItem, LayoutContextValue, PermissionsMap } from '../types'\r\nimport { loadLayoutState, saveLayoutState } from '../utils/storage'\r\n\r\nconst LayoutContext = createContext<LayoutContextValue | null>(null)\r\n\r\nexport type LayoutProviderProps = {\r\n children: ReactNode\r\n storageKey?: string\r\n defaultCollapsed?: boolean\r\n permissions?: PermissionsMap\r\n initialBreadcrumbs?: BreadcrumbItem[]\r\n initialActivePath?: string\r\n initialDarkMode?: boolean\r\n}\r\n\r\nexport function LayoutProvider({\r\n children,\r\n storageKey = 'rqdhw3n-admin-layout',\r\n defaultCollapsed = false,\r\n permissions = {},\r\n initialBreadcrumbs = [],\r\n initialActivePath = '/',\r\n initialDarkMode,\r\n}: LayoutProviderProps) {\r\n const persisted = loadLayoutState(storageKey)\r\n\r\n const [sidebarCollapsed, setSidebarCollapsed] = useState(\r\n persisted.sidebarCollapsed ?? defaultCollapsed,\r\n )\r\n const [mobileOpen, setMobileOpen] = useState(false)\r\n const [darkMode, setDarkMode] = useState(\r\n persisted.darkMode ?? initialDarkMode ?? false,\r\n )\r\n const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbItem[]>(initialBreadcrumbs)\r\n const [activePath, setActivePath] = useState(initialActivePath)\r\n\r\n useEffect(() => {\r\n saveLayoutState(storageKey, { sidebarCollapsed, darkMode })\r\n }, [storageKey, sidebarCollapsed, darkMode])\r\n\r\n useEffect(() => {\r\n const root = document.documentElement\r\n if (darkMode) root.classList.add('dark')\r\n else root.classList.remove('dark')\r\n }, [darkMode])\r\n\r\n const toggleSidebar = useCallback(() => setSidebarCollapsed((v) => !v), [])\r\n const toggleMobile = useCallback(() => setMobileOpen((v) => !v), [])\r\n const toggleDarkMode = useCallback(() => setDarkMode((v) => !v), [])\r\n\r\n const value = useMemo<LayoutContextValue>(\r\n () => ({\r\n sidebarCollapsed,\r\n mobileOpen,\r\n darkMode,\r\n toggleSidebar,\r\n setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n toggleDarkMode,\r\n setDarkMode,\r\n permissions,\r\n breadcrumbs,\r\n setBreadcrumbs,\r\n activePath,\r\n setActivePath,\r\n }),\r\n [\r\n sidebarCollapsed,\r\n mobileOpen,\r\n darkMode,\r\n toggleSidebar,\r\n toggleMobile,\r\n toggleDarkMode,\r\n permissions,\r\n breadcrumbs,\r\n activePath,\r\n ],\r\n )\r\n\r\n return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>\r\n}\r\n\r\nexport function useLayoutContext(): LayoutContextValue {\r\n const ctx = useContext(LayoutContext)\r\n if (!ctx) {\r\n throw new Error('useLayoutContext must be used within LayoutProvider / AdminLayout')\r\n }\r\n return ctx\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useSidebar() {\r\n const {\r\n sidebarCollapsed,\r\n mobileOpen,\r\n toggleSidebar,\r\n setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n } = useLayoutContext()\r\n\r\n return {\r\n collapsed: sidebarCollapsed,\r\n mobileOpen,\r\n toggle: toggleSidebar,\r\n setCollapsed: setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n closeMobile: () => setMobileOpen(false),\r\n }\r\n}\r\n","import type { BreadcrumbItem } from '../types'\r\n\r\nexport function generateBreadcrumbsFromPath(pathname: string): BreadcrumbItem[] {\r\n const segments = pathname.split('/').filter(Boolean)\r\n if (!segments.length) return [{ label: 'Home', path: '/' }]\r\n\r\n return segments.map((seg, i) => {\r\n const path = '/' + segments.slice(0, i + 1).join('/')\r\n const label = seg\r\n .replace(/-/g, ' ')\r\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\r\n return { label, path: i < segments.length - 1 ? path : undefined }\r\n })\r\n}\r\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import clsx, { type ClassValue } from 'clsx'\r\n\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return clsx(inputs)\r\n}\r\n","import { useEffect, type ReactNode } from 'react'\r\nimport { useLocation } from 'react-router-dom'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport { generateBreadcrumbsFromPath } from '../utils/breadcrumbs'\r\n\r\nexport type AdminLayoutRouterSyncProps = {\r\n children: ReactNode\r\n}\r\n\r\nexport function AdminLayoutRouterSync({ children }: AdminLayoutRouterSyncProps) {\r\n const { pathname } = useLocation()\r\n const { setActivePath, setBreadcrumbs, breadcrumbs: manualBreadcrumbs } = useLayoutContext()\r\n\r\n useEffect(() => {\r\n setActivePath(pathname)\r\n if (!manualBreadcrumbs.length) {\r\n setBreadcrumbs(generateBreadcrumbsFromPath(pathname))\r\n }\r\n }, [pathname, setActivePath, setBreadcrumbs, manualBreadcrumbs.length])\r\n\r\n return <>{children}</>\r\n}\r\n","import { cn } from '../utils/cn'\r\n\r\nexport type FooterProps = {\r\n children?: React.ReactNode\r\n className?: string\r\n}\r\n\r\nexport function Footer({ children, className }: FooterProps) {\r\n return (\r\n <footer\r\n className={cn(\r\n 'mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted',\r\n className,\r\n )}\r\n >\r\n {children ?? (\r\n <p className='text-center'>\r\n © {new Date().getFullYear()} Admin Layout · Built with @rqdhw3n/react-admin-layout\r\n </p>\r\n )}\r\n </footer>\r\n )\r\n}\r\n","import type { PermissionsMap, SidebarItemType } from '../types'\r\n\r\nexport function parsePermission(permission: string): { resource: string; action: string } {\r\n const dot = permission.indexOf('.')\r\n if (dot === -1) return { resource: permission, action: 'view' }\r\n return {\r\n resource: permission.slice(0, dot),\r\n action: permission.slice(dot + 1),\r\n }\r\n}\r\n\r\nexport function hasPermission(\r\n permissions: PermissionsMap,\r\n permission?: string,\r\n): boolean {\r\n if (!permission) return true\r\n const { resource, action } = parsePermission(permission)\r\n const allowed = permissions[resource]\r\n if (!allowed?.length) return false\r\n return allowed.includes(action) || allowed.includes('*')\r\n}\r\n\r\nexport function filterSidebarByPermissions(\r\n items: SidebarItemType[],\r\n permissions: PermissionsMap,\r\n): SidebarItemType[] {\r\n return items\r\n .filter((item) => hasPermission(permissions, item.permission))\r\n .map((item) => ({\r\n ...item,\r\n children: item.children\r\n ? filterSidebarByPermissions(item.children, permissions)\r\n : undefined,\r\n }))\r\n .filter((item) => !item.children?.length || item.children.length > 0 || item.path)\r\n}\r\n","import type { ReactNode } from 'react'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type SidebarGroupProps = {\r\n label?: string\r\n children: ReactNode\r\n collapsed?: boolean\r\n className?: string\r\n}\r\n\r\nexport function SidebarGroup({ label, children, collapsed, className }: SidebarGroupProps) {\r\n return (\r\n <div className={cn('mb-4', className)}>\r\n {label && !collapsed && (\r\n <p className='mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-admin-muted'>\r\n {label}\r\n </p>\r\n )}\r\n <ul className='space-y-0.5'>{children}</ul>\r\n </div>\r\n )\r\n}\r\n","import { ChevronDown } from 'lucide-react'\r\nimport { useState } from 'react'\r\nimport type { SidebarItemType } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type SidebarItemProps = {\r\n item: SidebarItemType\r\n collapsed?: boolean\r\n depth?: number\r\n activePath?: string\r\n onNavigate?: (path: string) => void\r\n LinkComponent?: React.ComponentType<{\r\n to: string\r\n className?: string\r\n children: React.ReactNode\r\n onClick?: () => void\r\n }>\r\n onItemClick?: () => void\r\n}\r\n\r\nfunction isActive(path: string | undefined, activePath: string): boolean {\r\n if (!path) return false\r\n return activePath === path || activePath.startsWith(`${path}/`)\r\n}\r\n\r\nexport function SidebarItem({\r\n item,\r\n collapsed = false,\r\n depth = 0,\r\n activePath = '/',\r\n onNavigate,\r\n LinkComponent,\r\n onItemClick,\r\n}: SidebarItemProps) {\r\n const hasChildren = Boolean(item.children?.length)\r\n const active = item.path ? isActive(item.path, activePath) : false\r\n const childActive = item.children?.some((c) => c.path && isActive(c.path, activePath))\r\n const [open, setOpen] = useState(active || childActive)\r\n\r\n const Icon = item.icon\r\n\r\n const baseClass = cn(\r\n 'group flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200',\r\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n active || childActive\r\n ? 'bg-admin-primary text-white shadow-sm'\r\n : 'text-admin-muted hover:bg-admin-bg hover:text-admin-text',\r\n item.disabled && 'pointer-events-none opacity-50',\r\n depth > 0 && 'text-[13px] py-2',\r\n )\r\n\r\n const content = (\r\n <>\r\n {Icon && (\r\n <Icon\r\n className={cn('h-[18px] w-[18px] shrink-0', active || childActive ? 'text-white' : '')}\r\n aria-hidden\r\n />\r\n )}\r\n {!collapsed && (\r\n <>\r\n <span className='flex-1 truncate text-left'>{item.label}</span>\r\n {item.badge != null && (\r\n <span\r\n className={cn(\r\n 'rounded-full px-2 py-0.5 text-[11px] font-semibold',\r\n active ? 'bg-white/20 text-white' : 'bg-admin-primary/10 text-admin-primary',\r\n )}\r\n >\r\n {item.badge}\r\n </span>\r\n )}\r\n {hasChildren && (\r\n <ChevronDown\r\n className={cn('h-4 w-4 shrink-0 transition-transform', open && 'rotate-180')}\r\n aria-hidden\r\n />\r\n )}\r\n </>\r\n )}\r\n </>\r\n )\r\n\r\n const handleClick = () => {\r\n if (hasChildren) {\r\n setOpen((v) => !v)\r\n return\r\n }\r\n if (item.path) {\r\n onNavigate?.(item.path)\r\n onItemClick?.()\r\n }\r\n }\r\n\r\n const row =\r\n item.path && !hasChildren && LinkComponent ? (\r\n <LinkComponent\r\n to={item.path}\r\n className={baseClass}\r\n onClick={() => {\r\n onItemClick?.()\r\n }}\r\n >\r\n {content}\r\n </LinkComponent>\r\n ) : (\r\n <button type='button' className={baseClass} onClick={handleClick} disabled={item.disabled}>\r\n {content}\r\n </button>\r\n )\r\n\r\n return (\r\n <li>\r\n {row}\r\n {hasChildren && open && !collapsed && (\r\n <ul className='mt-1 space-y-0.5 border-l border-admin-border ml-5 pl-2'>\r\n {item.children!.map((child) => (\r\n <SidebarItem\r\n key={child.label + (child.path ?? '')}\r\n item={child}\r\n collapsed={collapsed}\r\n depth={depth + 1}\r\n activePath={activePath}\r\n onNavigate={onNavigate}\r\n LinkComponent={LinkComponent}\r\n onItemClick={onItemClick}\r\n />\r\n ))}\r\n </ul>\r\n )}\r\n </li>\r\n )\r\n}\r\n","import { X } from 'lucide-react'\r\nimport { useEffect } from 'react'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { SidebarProps } from '../types'\r\nimport { filterSidebarByPermissions } from '../utils/permissions'\r\nimport { cn } from '../utils/cn'\r\nimport { SidebarGroup } from './SidebarGroup'\r\nimport { SidebarItem } from './SidebarItem'\r\n\r\nexport type MobileSidebarProps = SidebarProps\r\n\r\nexport function MobileSidebar({\r\n items,\r\n appName = 'Admin',\r\n appLogo,\r\n onNavigate,\r\n activePath: activePathProp,\r\n LinkComponent,\r\n}: MobileSidebarProps) {\r\n const { mobileOpen, closeMobile } = useSidebar()\r\n const { permissions, activePath: ctxPath } = useLayoutContext()\r\n const activePath = activePathProp ?? ctxPath\r\n const filtered = filterSidebarByPermissions(items, permissions)\r\n\r\n useEffect(() => {\r\n if (!mobileOpen) return\r\n const onKey = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') closeMobile()\r\n }\r\n document.addEventListener('keydown', onKey)\r\n document.body.style.overflow = 'hidden'\r\n return () => {\r\n document.removeEventListener('keydown', onKey)\r\n document.body.style.overflow = ''\r\n }\r\n }, [mobileOpen, closeMobile])\r\n\r\n if (!mobileOpen) return null\r\n\r\n const handleNavigate = (path: string) => {\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n return (\r\n <div className='fixed inset-0 z-50 lg:hidden' role='dialog' aria-modal='true'>\r\n <button\r\n type='button'\r\n className='absolute inset-0 bg-slate-900/50 backdrop-blur-sm animate-fade-in'\r\n aria-label='Close menu'\r\n onClick={closeMobile}\r\n />\r\n <aside\r\n className={cn(\r\n 'absolute left-0 top-0 flex h-full w-[min(280px,85vw)] flex-col',\r\n 'border-r border-admin-border bg-admin-surface shadow-admin-lg animate-slide-in',\r\n )}\r\n >\r\n <div className='flex h-16 items-center justify-between border-b border-admin-border px-4'>\r\n <div className='flex items-center gap-3'>\r\n {appLogo ?? (\r\n <div className='flex h-9 w-9 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white'>\r\n {appName.charAt(0)}\r\n </div>\r\n )}\r\n <span className='font-semibold text-admin-text'>{appName}</span>\r\n </div>\r\n <button\r\n type='button'\r\n onClick={closeMobile}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg'\r\n aria-label='Close sidebar'\r\n >\r\n <X className='h-5 w-5' />\r\n </button>\r\n </div>\r\n <nav className='flex-1 overflow-y-auto p-3'>\r\n <SidebarGroup>\r\n {filtered.map((item) => (\r\n <SidebarItem\r\n key={item.label + (item.path ?? '')}\r\n item={item}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n onItemClick={closeMobile}\r\n />\r\n ))}\r\n </SidebarGroup>\r\n </nav>\r\n </aside>\r\n </div>\r\n )\r\n}\r\n","import { ChevronRight, Home } from 'lucide-react'\r\nimport type { BreadcrumbItem } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type BreadcrumbsProps = {\r\n items: BreadcrumbItem[]\r\n className?: string\r\n LinkComponent?: React.ComponentType<{\r\n to: string\r\n className?: string\r\n children: React.ReactNode\r\n }>\r\n onNavigate?: (path: string) => void\r\n}\r\n\r\nexport function Breadcrumbs({\r\n items,\r\n className,\r\n LinkComponent,\r\n onNavigate,\r\n}: BreadcrumbsProps) {\r\n if (!items.length) return null\r\n\r\n const renderLink = (item: BreadcrumbItem, isLast: boolean) => {\r\n const classNames = cn(\r\n 'text-sm transition-colors',\r\n isLast ? 'font-medium text-admin-text' : 'text-admin-muted hover:text-admin-text',\r\n )\r\n\r\n if (!item.path || isLast) {\r\n return <span className={classNames}>{item.label}</span>\r\n }\r\n\r\n if (LinkComponent) {\r\n return (\r\n <LinkComponent to={item.path} className={classNames}>\r\n {item.label}\r\n </LinkComponent>\r\n )\r\n }\r\n\r\n return (\r\n <button type='button' className={classNames} onClick={() => onNavigate?.(item.path!)}>\r\n {item.label}\r\n </button>\r\n )\r\n }\r\n\r\n return (\r\n <nav aria-label='Breadcrumb' className={cn('flex items-center gap-1.5', className)}>\r\n <Home className='h-4 w-4 text-admin-muted shrink-0' aria-hidden />\r\n {items.map((item, index) => {\r\n const isLast = index === items.length - 1\r\n return (\r\n <span key={`${item.label}-${index}`} className='flex items-center gap-1.5'>\r\n <ChevronRight className='h-3.5 w-3.5 text-admin-muted' aria-hidden />\r\n {renderLink(item, isLast)}\r\n </span>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n","import { Search } from 'lucide-react'\r\nimport { useState, type FormEvent } from 'react'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type NavbarSearchProps = {\r\n placeholder?: string\r\n onSearch?: (query: string) => void\r\n className?: string\r\n}\r\n\r\nexport function NavbarSearch({\r\n placeholder = 'Search...',\r\n onSearch,\r\n className,\r\n}: NavbarSearchProps) {\r\n const [query, setQuery] = useState('')\r\n\r\n const handleSubmit = (e: FormEvent) => {\r\n e.preventDefault()\r\n onSearch?.(query.trim())\r\n }\r\n\r\n return (\r\n <form onSubmit={handleSubmit} className={cn('relative w-full max-w-md', className)}>\r\n <Search\r\n className='pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted'\r\n aria-hidden\r\n />\r\n <input\r\n type='search'\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n placeholder={placeholder}\r\n className={cn(\r\n 'h-10 w-full rounded-xl border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text',\r\n 'placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary/20',\r\n )}\r\n aria-label='Search'\r\n />\r\n </form>\r\n )\r\n}\r\n","import { Bell } from 'lucide-react'\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport type { NotificationItem } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type NotificationMenuProps = {\r\n notifications?: NotificationItem[]\r\n className?: string\r\n}\r\n\r\nexport function NotificationMenu({ notifications = [], className }: NotificationMenuProps) {\r\n const [open, setOpen] = useState(false)\r\n const ref = useRef<HTMLDivElement>(null)\r\n const unread = notifications.filter((n) => !n.read).length\r\n\r\n useEffect(() => {\r\n const handler = (e: MouseEvent) => {\r\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener('mousedown', handler)\r\n return () => document.removeEventListener('mousedown', handler)\r\n }, [])\r\n\r\n return (\r\n <div ref={ref} className={cn('relative', className)}>\r\n <button\r\n type='button'\r\n onClick={() => setOpen((v) => !v)}\r\n className={cn(\r\n 'relative inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border',\r\n 'bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text',\r\n 'focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n )}\r\n aria-label={`Notifications${unread ? `, ${unread} unread` : ''}`}\r\n aria-expanded={open}\r\n >\r\n <Bell className='h-4 w-4' aria-hidden />\r\n {unread > 0 && (\r\n <span className='absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white'>\r\n {unread > 9 ? '9+' : unread}\r\n </span>\r\n )}\r\n </button>\r\n\r\n {open && (\r\n <div className='absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in'>\r\n <div className='border-b border-admin-border px-4 py-3'>\r\n <p className='text-sm font-semibold text-admin-text'>Notifications</p>\r\n </div>\r\n <ul className='max-h-72 overflow-y-auto p-2'>\r\n {notifications.length === 0 ? (\r\n <li className='px-3 py-6 text-center text-sm text-admin-muted'>No notifications</li>\r\n ) : (\r\n notifications.map((n) => (\r\n <li\r\n key={n.id}\r\n className={cn(\r\n 'rounded-lg px-3 py-2.5 transition-colors hover:bg-admin-bg',\r\n !n.read && 'bg-admin-primary/5',\r\n )}\r\n >\r\n <p className='text-sm font-medium text-admin-text'>{n.title}</p>\r\n {n.description && (\r\n <p className='mt-0.5 text-xs text-admin-muted line-clamp-2'>{n.description}</p>\r\n )}\r\n {n.time && <p className='mt-1 text-[11px] text-admin-muted'>{n.time}</p>}\r\n </li>\r\n ))\r\n )}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useDarkMode() {\r\n const { darkMode, toggleDarkMode, setDarkMode } = useLayoutContext()\r\n return {\r\n isDark: darkMode,\r\n toggle: toggleDarkMode,\r\n setDarkMode,\r\n }\r\n}\r\n","import { Moon, Sun } from 'lucide-react'\r\nimport { useDarkMode } from '../hooks/useDarkMode'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type ThemeToggleProps = {\r\n className?: string\r\n}\r\n\r\nexport function ThemeToggle({ className }: ThemeToggleProps) {\r\n const { isDark, toggle } = useDarkMode()\r\n\r\n return (\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className={cn(\r\n 'inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border',\r\n 'bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text',\r\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2',\r\n className,\r\n )}\r\n aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}\r\n >\r\n {isDark ? <Sun className='h-4 w-4' aria-hidden /> : <Moon className='h-4 w-4' aria-hidden />}\r\n </button>\r\n )\r\n}\r\n","import { ChevronDown, LogOut, Settings, User } from 'lucide-react'\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport type { AdminUser } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type UserMenuProps = {\r\n user: AdminUser\r\n onLogout?: () => void\r\n onSettings?: () => void\r\n className?: string\r\n}\r\n\r\nexport function UserMenu({ user, onLogout, onSettings, className }: UserMenuProps) {\r\n const [open, setOpen] = useState(false)\r\n const ref = useRef<HTMLDivElement>(null)\r\n\r\n useEffect(() => {\r\n const handler = (e: MouseEvent) => {\r\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener('mousedown', handler)\r\n return () => document.removeEventListener('mousedown', handler)\r\n }, [])\r\n\r\n const initials = user.name\r\n .split(' ')\r\n .map((n) => n[0])\r\n .join('')\r\n .slice(0, 2)\r\n .toUpperCase()\r\n\r\n return (\r\n <div ref={ref} className={cn('relative', className)}>\r\n <button\r\n type='button'\r\n onClick={() => setOpen((v) => !v)}\r\n className={cn(\r\n 'flex items-center gap-2 rounded-xl border border-admin-border bg-admin-surface px-2 py-1.5',\r\n 'transition-colors hover:bg-admin-bg focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n )}\r\n aria-expanded={open}\r\n aria-haspopup='menu'\r\n >\r\n {user.avatar ? (\r\n <img src={user.avatar} alt='' className='h-8 w-8 rounded-lg object-cover' />\r\n ) : (\r\n <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-admin-primary text-xs font-semibold text-white'>\r\n {initials}\r\n </div>\r\n )}\r\n <div className='hidden text-left sm:block'>\r\n <p className='text-sm font-medium text-admin-text leading-tight'>{user.name}</p>\r\n <p className='text-xs text-admin-muted leading-tight'>{user.email}</p>\r\n </div>\r\n <ChevronDown className='hidden h-4 w-4 text-admin-muted sm:block' aria-hidden />\r\n </button>\r\n\r\n {open && (\r\n <div\r\n role='menu'\r\n className='absolute right-0 top-full z-50 mt-2 w-56 rounded-xl border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in'\r\n >\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg'\r\n onClick={() => {\r\n onSettings?.()\r\n setOpen(false)\r\n }}\r\n >\r\n <Settings className='h-4 w-4 text-admin-muted' />\r\n Settings\r\n </button>\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg'\r\n >\r\n <User className='h-4 w-4 text-admin-muted' />\r\n Profile\r\n </button>\r\n <hr className='my-1 border-admin-border' />\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30'\r\n onClick={() => {\r\n onLogout?.()\r\n setOpen(false)\r\n }}\r\n >\r\n <LogOut className='h-4 w-4' />\r\n Log out\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { Menu, PanelLeftClose, PanelLeftOpen } from 'lucide-react'\r\nimport type { NavbarProps } from '../types'\r\nimport { cn } from '../utils/cn'\r\nimport { Breadcrumbs } from './Breadcrumbs'\r\nimport { NavbarSearch } from './NavbarSearch'\r\nimport { NotificationMenu } from './NotificationMenu'\r\nimport { ThemeToggle } from './ThemeToggle'\r\nimport { UserMenu } from './UserMenu'\r\n\r\nexport function Navbar({\r\n user,\r\n notifications = [],\r\n onSearch,\r\n searchPlaceholder = 'Search...',\r\n showSearch = true,\r\n className,\r\n breadcrumbs = [],\r\n onMenuClick,\r\n sidebarCollapsed,\r\n onToggleSidebar,\r\n}: NavbarProps) {\r\n return (\r\n <header\r\n className={cn(\r\n 'sticky top-0 z-40 flex h-16 shrink-0 items-center gap-4 border-b border-admin-border',\r\n 'admin-glass bg-admin-surface px-4 lg:px-6',\r\n className,\r\n )}\r\n >\r\n <button\r\n type='button'\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:hidden'\r\n onClick={onMenuClick}\r\n aria-label='Open menu'\r\n >\r\n <Menu className='h-5 w-5' />\r\n </button>\r\n\r\n <button\r\n type='button'\r\n className='hidden rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:inline-flex'\r\n onClick={onToggleSidebar}\r\n aria-label={sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}\r\n >\r\n {sidebarCollapsed ? (\r\n <PanelLeftOpen className='h-5 w-5' />\r\n ) : (\r\n <PanelLeftClose className='h-5 w-5' />\r\n )}\r\n </button>\r\n\r\n <div className='hidden min-w-0 flex-1 md:block'>\r\n <Breadcrumbs items={breadcrumbs} />\r\n </div>\r\n\r\n <div className='flex flex-1 items-center justify-end gap-2 sm:gap-3'>\r\n {showSearch && (\r\n <div className='hidden flex-1 sm:flex sm:max-w-md'>\r\n <NavbarSearch placeholder={searchPlaceholder} onSearch={onSearch} />\r\n </div>\r\n )}\r\n <ThemeToggle />\r\n <NotificationMenu notifications={notifications} />\r\n {user && <UserMenu user={user} />}\r\n </div>\r\n </header>\r\n )\r\n}\r\n","import { PanelLeftClose, PanelLeftOpen } from 'lucide-react'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { SidebarProps } from '../types'\r\nimport { filterSidebarByPermissions } from '../utils/permissions'\r\nimport { cn } from '../utils/cn'\r\nimport { SidebarGroup } from './SidebarGroup'\r\nimport { SidebarItem } from './SidebarItem'\r\n\r\nexport function Sidebar({\r\n items,\r\n collapsed: collapsedProp,\r\n appName = 'Admin',\r\n appLogo,\r\n className,\r\n onNavigate,\r\n activePath: activePathProp,\r\n LinkComponent,\r\n}: SidebarProps) {\r\n const { collapsed: ctxCollapsed, toggle, closeMobile } = useSidebar()\r\n const { permissions, activePath: ctxPath } = useLayoutContext()\r\n\r\n const collapsed = collapsedProp ?? ctxCollapsed\r\n const activePath = activePathProp ?? ctxPath\r\n const filtered = filterSidebarByPermissions(items, permissions)\r\n\r\n const handleNavigate = (path: string) => {\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n return (\r\n <aside\r\n className={cn(\r\n 'hidden lg:flex flex-col border-r border-admin-border bg-admin-surface transition-all duration-300 ease-in-out',\r\n collapsed ? 'w-[var(--admin-sidebar-collapsed)]' : 'w-[var(--admin-sidebar-width)]',\r\n className,\r\n )}\r\n aria-label='Sidebar navigation'\r\n >\r\n <div\r\n className={cn(\r\n 'flex h-16 items-center border-b border-admin-border px-4',\r\n collapsed ? 'justify-center' : 'justify-between gap-2',\r\n )}\r\n >\r\n <div className={cn('flex items-center gap-3 min-w-0', collapsed && 'justify-center')}>\r\n {appLogo ?? (\r\n <div className='flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white shadow-admin'>\r\n {appName.charAt(0)}\r\n </div>\r\n )}\r\n {!collapsed && (\r\n <span className='truncate text-base font-semibold text-admin-text'>{appName}</span>\r\n )}\r\n </div>\r\n {!collapsed && (\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg hover:text-admin-text focus-visible:ring-2 focus-visible:ring-admin-primary'\r\n aria-label='Collapse sidebar'\r\n >\r\n <PanelLeftClose className='h-4 w-4' />\r\n </button>\r\n )}\r\n </div>\r\n\r\n {collapsed && (\r\n <div className='flex justify-center py-2 border-b border-admin-border'>\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg'\r\n aria-label='Expand sidebar'\r\n >\r\n <PanelLeftOpen className='h-4 w-4' />\r\n </button>\r\n </div>\r\n )}\r\n\r\n <nav className='flex-1 overflow-y-auto p-3'>\r\n <SidebarGroup collapsed={collapsed}>\r\n {filtered.map((item) => (\r\n <SidebarItem\r\n key={item.label + (item.path ?? '')}\r\n item={item}\r\n collapsed={collapsed}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n ))}\r\n </SidebarGroup>\r\n </nav>\r\n </aside>\r\n )\r\n}\r\n","import { useMemo } from 'react'\r\nimport { LayoutProvider } from '../context/LayoutContext'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport type { AdminLayoutProps } from '../types'\r\nimport { generateBreadcrumbsFromPath } from '../utils/breadcrumbs'\r\nimport { cn } from '../utils/cn'\r\nimport { AdminLayoutRouterSync } from './AdminLayoutRouterSync'\r\nimport { Footer } from './Footer'\r\nimport { MobileSidebar } from './MobileSidebar'\r\nimport { Navbar } from './Navbar'\r\nimport { Sidebar } from './Sidebar'\r\n\r\nfunction AdminLayoutInner({\r\n children,\r\n sidebarItems,\r\n appName = 'Admin',\r\n appLogo,\r\n user,\r\n notifications = [],\r\n footer,\r\n showFooter = true,\r\n showSearch = true,\r\n searchPlaceholder,\r\n onSearch,\r\n breadcrumbs: breadcrumbsProp,\r\n contentClassName,\r\n activePath: activePathProp,\r\n onNavigate,\r\n LinkComponent,\r\n enableRouter: _enableRouter = false,\r\n}: AdminLayoutProps) {\r\n const {\r\n sidebarCollapsed,\r\n toggleSidebar,\r\n toggleMobile,\r\n breadcrumbs: ctxBreadcrumbs,\r\n setBreadcrumbs,\r\n activePath: ctxPath,\r\n setActivePath,\r\n } = useLayoutContext()\r\n const { closeMobile } = useSidebar()\r\n\r\n const activePath = activePathProp ?? ctxPath\r\n const breadcrumbs = breadcrumbsProp ?? ctxBreadcrumbs\r\n\r\n const handleNavigate = (path: string) => {\r\n setActivePath(path)\r\n if (!breadcrumbsProp) {\r\n setBreadcrumbs(generateBreadcrumbsFromPath(path))\r\n }\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n const mainOffset = sidebarCollapsed\r\n ? 'lg:pl-[var(--admin-sidebar-collapsed)]'\r\n : 'lg:pl-[var(--admin-sidebar-width)]'\r\n\r\n return (\r\n <div className='admin-layout-root min-h-screen bg-admin-bg text-admin-text'>\r\n <Sidebar\r\n items={sidebarItems}\r\n appName={appName}\r\n appLogo={appLogo}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n <MobileSidebar\r\n items={sidebarItems}\r\n appName={appName}\r\n appLogo={appLogo}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n\r\n <div className={cn('flex min-h-screen flex-col transition-all duration-300', mainOffset)}>\r\n <Navbar\r\n user={user}\r\n notifications={notifications}\r\n onSearch={onSearch}\r\n searchPlaceholder={searchPlaceholder}\r\n showSearch={showSearch}\r\n breadcrumbs={breadcrumbs}\r\n onMenuClick={toggleMobile}\r\n sidebarCollapsed={sidebarCollapsed}\r\n onToggleSidebar={toggleSidebar}\r\n />\r\n\r\n <main className={cn('flex-1 p-4 lg:p-6', contentClassName)}>{children}</main>\r\n\r\n {showFooter && <Footer>{footer}</Footer>}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport function AdminLayout(props: AdminLayoutProps) {\r\n const {\r\n storageKey = 'rqdhw3n-admin-layout',\r\n defaultCollapsed = false,\r\n initialDarkMode = false,\r\n permissions = {},\r\n breadcrumbs: initialBreadcrumbs = [],\r\n activePath: initialActivePath = '/',\r\n enableRouter = false,\r\n children,\r\n ...rest\r\n } = props\r\n\r\n const initialBc = useMemo(\r\n () =>\r\n initialBreadcrumbs.length\r\n ? initialBreadcrumbs\r\n : generateBreadcrumbsFromPath(initialActivePath),\r\n [initialBreadcrumbs, initialActivePath],\r\n )\r\n\r\n return (\r\n <LayoutProvider\r\n storageKey={storageKey}\r\n defaultCollapsed={defaultCollapsed}\r\n permissions={permissions}\r\n initialBreadcrumbs={initialBc}\r\n initialActivePath={initialActivePath}\r\n initialDarkMode={initialDarkMode}\r\n >\r\n {enableRouter ? (\r\n <AdminLayoutRouterSync>\r\n <AdminLayoutInner {...rest} enableRouter={enableRouter}>\r\n {children}\r\n </AdminLayoutInner>\r\n </AdminLayoutRouterSync>\r\n ) : (\r\n <AdminLayoutInner {...rest} enableRouter={enableRouter}>\r\n {children}\r\n </AdminLayoutInner>\r\n )}\r\n </LayoutProvider>\r\n )\r\n}\r\n","import { NavLink } from 'react-router-dom'\r\nimport type { ReactNode } from 'react'\r\n\r\nexport type RouterLinkProps = {\r\n to: string\r\n className?: string\r\n children: ReactNode\r\n onClick?: () => void\r\n}\r\n\r\nexport function RouterLink({ to, className, children, onClick }: RouterLinkProps) {\r\n return (\r\n <NavLink to={to} className={className} onClick={onClick}>\r\n {children}\r\n </NavLink>\r\n )\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useLayoutState() {\r\n return useLayoutContext()\r\n}\r\n","import { useCallback } from 'react'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { BreadcrumbItem } from '../types'\r\n\r\nexport function useBreadcrumbs() {\r\n const { breadcrumbs, setBreadcrumbs } = useLayoutContext()\r\n\r\n const update = useCallback(\r\n (items: BreadcrumbItem[]) => setBreadcrumbs(items),\r\n [setBreadcrumbs],\r\n )\r\n\r\n return {\r\n items: breadcrumbs,\r\n setBreadcrumbs: update,\r\n }\r\n}\r\n"],"names":["loadLayoutState","key","raw","saveLayoutState","state","LayoutContext","createContext","LayoutProvider","children","storageKey","defaultCollapsed","permissions","initialBreadcrumbs","initialActivePath","initialDarkMode","persisted","sidebarCollapsed","setSidebarCollapsed","useState","mobileOpen","setMobileOpen","darkMode","setDarkMode","breadcrumbs","setBreadcrumbs","activePath","setActivePath","useEffect","root","toggleSidebar","useCallback","v","toggleMobile","toggleDarkMode","value","useMemo","jsx","useLayoutContext","ctx","useContext","useSidebar","generateBreadcrumbsFromPath","pathname","segments","seg","i","path","c","r","e","t","f","n","o","clsx","cn","inputs","AdminLayoutRouterSync","useLocation","manualBreadcrumbs","Footer","className","jsxs","parsePermission","permission","dot","hasPermission","resource","action","allowed","filterSidebarByPermissions","items","item","_a","SidebarGroup","label","collapsed","isActive","SidebarItem","depth","onNavigate","LinkComponent","onItemClick","hasChildren","active","childActive","_b","open","setOpen","Icon","baseClass","content","Fragment","ChevronDown","handleClick","row","child","MobileSidebar","appName","appLogo","activePathProp","closeMobile","ctxPath","filtered","onKey","handleNavigate","X","Breadcrumbs","renderLink","isLast","classNames","Home","index","ChevronRight","NavbarSearch","placeholder","onSearch","query","setQuery","handleSubmit","Search","NotificationMenu","notifications","ref","useRef","unread","handler","Bell","useDarkMode","ThemeToggle","isDark","toggle","Sun","Moon","UserMenu","user","onLogout","onSettings","initials","Settings","User","LogOut","Navbar","searchPlaceholder","showSearch","onMenuClick","onToggleSidebar","Menu","PanelLeftOpen","PanelLeftClose","Sidebar","collapsedProp","ctxCollapsed","AdminLayoutInner","sidebarItems","footer","showFooter","breadcrumbsProp","contentClassName","_enableRouter","ctxBreadcrumbs","mainOffset","AdminLayout","props","enableRouter","rest","initialBc","RouterLink","to","onClick","NavLink","useLayoutState","useBreadcrumbs","update"],"mappings":"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","x_google_ignoreList":[4]}
1
+ {"version":3,"file":"index.cjs","sources":["../src/utils/storage.ts","../src/context/LayoutContext.tsx","../src/hooks/useSidebar.ts","../src/utils/breadcrumbs.ts","../node_modules/clsx/dist/clsx.mjs","../src/utils/cn.ts","../src/components/AdminLayoutRouterSync.tsx","../src/components/Footer.tsx","../src/utils/permissions.ts","../src/components/SidebarGroup.tsx","../src/components/SidebarItem.tsx","../src/components/MobileSidebar.tsx","../src/components/Breadcrumbs.tsx","../src/components/NavbarSearch.tsx","../src/components/NotificationMenu.tsx","../src/hooks/useDarkMode.ts","../src/components/ThemeToggle.tsx","../src/components/UserMenu.tsx","../src/components/Navbar.tsx","../src/components/Sidebar.tsx","../src/components/AdminLayout.tsx","../src/components/RouterLink.tsx","../src/hooks/useLayoutState.ts","../src/hooks/useBreadcrumbs.ts"],"sourcesContent":["import type { LayoutState } from '../types'\r\n\r\nexport type PersistedLayout = Pick<LayoutState, 'sidebarCollapsed' | 'darkMode'>\r\n\r\nexport function loadLayoutState(key: string): Partial<PersistedLayout> {\r\n if (typeof window === 'undefined') return {}\r\n try {\r\n const raw = localStorage.getItem(key)\r\n return raw ? (JSON.parse(raw) as Partial<PersistedLayout>) : {}\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\nexport function saveLayoutState(key: string, state: PersistedLayout): void {\r\n if (typeof window === 'undefined') return\r\n try {\r\n localStorage.setItem(key, JSON.stringify(state))\r\n } catch {\r\n /* ignore quota errors */\r\n }\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n type ReactNode,\r\n} from 'react'\r\nimport type { BreadcrumbItem, LayoutContextValue, PermissionsMap } from '../types'\r\nimport { loadLayoutState, saveLayoutState } from '../utils/storage'\r\n\r\nconst LayoutContext = createContext<LayoutContextValue | null>(null)\r\n\r\nexport type LayoutProviderProps = {\r\n children: ReactNode\r\n storageKey?: string\r\n defaultCollapsed?: boolean\r\n permissions?: PermissionsMap\r\n initialBreadcrumbs?: BreadcrumbItem[]\r\n initialActivePath?: string\r\n initialDarkMode?: boolean\r\n}\r\n\r\nexport function LayoutProvider({\r\n children,\r\n storageKey = 'rqdhw3n-admin-layout',\r\n defaultCollapsed = false,\r\n permissions = {},\r\n initialBreadcrumbs = [],\r\n initialActivePath = '/',\r\n initialDarkMode,\r\n}: LayoutProviderProps) {\r\n const persisted = loadLayoutState(storageKey)\r\n\r\n const [sidebarCollapsed, setSidebarCollapsed] = useState(\r\n persisted.sidebarCollapsed ?? defaultCollapsed,\r\n )\r\n const [mobileOpen, setMobileOpen] = useState(false)\r\n const [darkMode, setDarkMode] = useState(\r\n persisted.darkMode ?? initialDarkMode ?? false,\r\n )\r\n const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbItem[]>(initialBreadcrumbs)\r\n const [activePath, setActivePath] = useState(initialActivePath)\r\n\r\n useEffect(() => {\r\n saveLayoutState(storageKey, { sidebarCollapsed, darkMode })\r\n }, [storageKey, sidebarCollapsed, darkMode])\r\n\r\n useEffect(() => {\r\n const root = document.documentElement\r\n if (darkMode) root.classList.add('dark')\r\n else root.classList.remove('dark')\r\n }, [darkMode])\r\n\r\n const toggleSidebar = useCallback(() => setSidebarCollapsed((v) => !v), [])\r\n const toggleMobile = useCallback(() => setMobileOpen((v) => !v), [])\r\n const toggleDarkMode = useCallback(() => setDarkMode((v) => !v), [])\r\n\r\n const value = useMemo<LayoutContextValue>(\r\n () => ({\r\n sidebarCollapsed,\r\n mobileOpen,\r\n darkMode,\r\n toggleSidebar,\r\n setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n toggleDarkMode,\r\n setDarkMode,\r\n permissions,\r\n breadcrumbs,\r\n setBreadcrumbs,\r\n activePath,\r\n setActivePath,\r\n }),\r\n [\r\n sidebarCollapsed,\r\n mobileOpen,\r\n darkMode,\r\n toggleSidebar,\r\n toggleMobile,\r\n toggleDarkMode,\r\n permissions,\r\n breadcrumbs,\r\n activePath,\r\n ],\r\n )\r\n\r\n return <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>\r\n}\r\n\r\nexport function useLayoutContext(): LayoutContextValue {\r\n const ctx = useContext(LayoutContext)\r\n if (!ctx) {\r\n throw new Error('useLayoutContext must be used within LayoutProvider / AdminLayout')\r\n }\r\n return ctx\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useSidebar() {\r\n const {\r\n sidebarCollapsed,\r\n mobileOpen,\r\n toggleSidebar,\r\n setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n } = useLayoutContext()\r\n\r\n return {\r\n collapsed: sidebarCollapsed,\r\n mobileOpen,\r\n toggle: toggleSidebar,\r\n setCollapsed: setSidebarCollapsed,\r\n toggleMobile,\r\n setMobileOpen,\r\n closeMobile: () => setMobileOpen(false),\r\n }\r\n}\r\n","import type { BreadcrumbItem } from '../types'\r\n\r\nexport function generateBreadcrumbsFromPath(pathname: string): BreadcrumbItem[] {\r\n const segments = pathname.split('/').filter(Boolean)\r\n if (!segments.length) return [{ label: 'Home', path: '/' }]\r\n\r\n return segments.map((seg, i) => {\r\n const path = '/' + segments.slice(0, i + 1).join('/')\r\n const label = seg\r\n .replace(/-/g, ' ')\r\n .replace(/\\b\\w/g, (c) => c.toUpperCase())\r\n return { label, path: i < segments.length - 1 ? path : undefined }\r\n })\r\n}\r\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import clsx, { type ClassValue } from 'clsx'\r\n\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return clsx(inputs)\r\n}\r\n","import { useEffect, type ReactNode } from 'react'\r\nimport { useLocation } from 'react-router-dom'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport { generateBreadcrumbsFromPath } from '../utils/breadcrumbs'\r\n\r\nexport type AdminLayoutRouterSyncProps = {\r\n children: ReactNode\r\n}\r\n\r\nexport function AdminLayoutRouterSync({ children }: AdminLayoutRouterSyncProps) {\r\n const { pathname } = useLocation()\r\n const { setActivePath, setBreadcrumbs, breadcrumbs: manualBreadcrumbs } = useLayoutContext()\r\n\r\n useEffect(() => {\r\n setActivePath(pathname)\r\n if (!manualBreadcrumbs.length) {\r\n setBreadcrumbs(generateBreadcrumbsFromPath(pathname))\r\n }\r\n }, [pathname, setActivePath, setBreadcrumbs, manualBreadcrumbs.length])\r\n\r\n return <>{children}</>\r\n}\r\n","import { cn } from '../utils/cn'\r\n\r\nexport type FooterProps = {\r\n children?: React.ReactNode\r\n className?: string\r\n}\r\n\r\nexport function Footer({ children, className }: FooterProps) {\r\n return (\r\n <footer\r\n className={cn(\r\n 'mt-auto border-t border-admin-border bg-admin-surface px-6 py-4 text-sm text-admin-muted',\r\n className,\r\n )}\r\n >\r\n {children ?? (\r\n <p className='text-center'>\r\n © {new Date().getFullYear()} Admin Layout · Built with @rqdhw3n/react-admin-layout\r\n </p>\r\n )}\r\n </footer>\r\n )\r\n}\r\n","import type { PermissionsMap, SidebarItemType } from '../types'\r\n\r\nexport function parsePermission(permission: string): { resource: string; action: string } {\r\n const dot = permission.indexOf('.')\r\n if (dot === -1) return { resource: permission, action: 'view' }\r\n return {\r\n resource: permission.slice(0, dot),\r\n action: permission.slice(dot + 1),\r\n }\r\n}\r\n\r\nexport function hasPermission(\r\n permissions: PermissionsMap,\r\n permission?: string,\r\n): boolean {\r\n if (!permission) return true\r\n const { resource, action } = parsePermission(permission)\r\n const allowed = permissions[resource]\r\n if (!allowed?.length) return false\r\n return allowed.includes(action) || allowed.includes('*')\r\n}\r\n\r\nexport function filterSidebarByPermissions(\r\n items: SidebarItemType[],\r\n permissions: PermissionsMap,\r\n): SidebarItemType[] {\r\n return items\r\n .filter((item) => hasPermission(permissions, item.permission))\r\n .map((item) => ({\r\n ...item,\r\n children: item.children\r\n ? filterSidebarByPermissions(item.children, permissions)\r\n : undefined,\r\n }))\r\n .filter((item) => !item.children?.length || item.children.length > 0 || item.path)\r\n}\r\n","import type { ReactNode } from 'react'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type SidebarGroupProps = {\r\n label?: string\r\n children: ReactNode\r\n collapsed?: boolean\r\n className?: string\r\n}\r\n\r\nexport function SidebarGroup({ label, children, collapsed, className }: SidebarGroupProps) {\r\n return (\r\n <div className={cn('mb-4', className)}>\r\n {label && !collapsed && (\r\n <p className='mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-admin-muted'>\r\n {label}\r\n </p>\r\n )}\r\n <ul className='space-y-0.5'>{children}</ul>\r\n </div>\r\n )\r\n}\r\n","import { ChevronDown } from 'lucide-react'\r\nimport { useState } from 'react'\r\nimport type { SidebarItemType } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type SidebarItemProps = {\r\n item: SidebarItemType\r\n collapsed?: boolean\r\n depth?: number\r\n activePath?: string\r\n onNavigate?: (path: string) => void\r\n LinkComponent?: React.ComponentType<{\r\n to: string\r\n className?: string\r\n children: React.ReactNode\r\n onClick?: () => void\r\n }>\r\n onItemClick?: () => void\r\n}\r\n\r\nfunction isActive(path: string | undefined, activePath: string): boolean {\r\n if (!path) return false\r\n return activePath === path || activePath.startsWith(`${path}/`)\r\n}\r\n\r\nexport function SidebarItem({\r\n item,\r\n collapsed = false,\r\n depth = 0,\r\n activePath = '/',\r\n onNavigate,\r\n LinkComponent,\r\n onItemClick,\r\n}: SidebarItemProps) {\r\n const hasChildren = Boolean(item.children?.length)\r\n const active = item.path ? isActive(item.path, activePath) : false\r\n const childActive = item.children?.some((c) => c.path && isActive(c.path, activePath))\r\n const [open, setOpen] = useState(active || childActive)\r\n\r\n const Icon = item.icon\r\n\r\n const baseClass = cn(\r\n 'group flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-200',\r\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n active || childActive\r\n ? 'bg-admin-primary text-white shadow-sm'\r\n : 'text-admin-muted hover:bg-admin-bg hover:text-admin-text',\r\n item.disabled && 'pointer-events-none opacity-50',\r\n depth > 0 && 'text-[13px] py-2',\r\n )\r\n\r\n const content = (\r\n <>\r\n {Icon && (\r\n <Icon\r\n className={cn('h-[18px] w-[18px] shrink-0', active || childActive ? 'text-white' : '')}\r\n aria-hidden\r\n />\r\n )}\r\n {!collapsed && (\r\n <>\r\n <span className='flex-1 truncate text-left'>{item.label}</span>\r\n {item.badge != null && (\r\n <span\r\n className={cn(\r\n 'rounded-full px-2 py-0.5 text-[11px] font-semibold',\r\n active ? 'bg-white/20 text-white' : 'bg-admin-primary/10 text-admin-primary',\r\n )}\r\n >\r\n {item.badge}\r\n </span>\r\n )}\r\n {hasChildren && (\r\n <ChevronDown\r\n className={cn('h-4 w-4 shrink-0 transition-transform', open && 'rotate-180')}\r\n aria-hidden\r\n />\r\n )}\r\n </>\r\n )}\r\n </>\r\n )\r\n\r\n const handleClick = () => {\r\n if (hasChildren) {\r\n setOpen((v) => !v)\r\n return\r\n }\r\n if (item.path) {\r\n onNavigate?.(item.path)\r\n onItemClick?.()\r\n }\r\n }\r\n\r\n const row =\r\n item.path && !hasChildren && LinkComponent ? (\r\n <LinkComponent\r\n to={item.path}\r\n className={baseClass}\r\n onClick={() => {\r\n onItemClick?.()\r\n }}\r\n >\r\n {content}\r\n </LinkComponent>\r\n ) : (\r\n <button type='button' className={baseClass} onClick={handleClick} disabled={item.disabled}>\r\n {content}\r\n </button>\r\n )\r\n\r\n return (\r\n <li>\r\n {row}\r\n {hasChildren && open && !collapsed && (\r\n <ul className='mt-1 space-y-0.5 border-l border-admin-border ml-5 pl-2'>\r\n {item.children!.map((child) => (\r\n <SidebarItem\r\n key={child.label + (child.path ?? '')}\r\n item={child}\r\n collapsed={collapsed}\r\n depth={depth + 1}\r\n activePath={activePath}\r\n onNavigate={onNavigate}\r\n LinkComponent={LinkComponent}\r\n onItemClick={onItemClick}\r\n />\r\n ))}\r\n </ul>\r\n )}\r\n </li>\r\n )\r\n}\r\n","import { X } from 'lucide-react'\r\nimport { useEffect } from 'react'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { SidebarProps } from '../types'\r\nimport { filterSidebarByPermissions } from '../utils/permissions'\r\nimport { cn } from '../utils/cn'\r\nimport { SidebarGroup } from './SidebarGroup'\r\nimport { SidebarItem } from './SidebarItem'\r\n\r\nexport type MobileSidebarProps = SidebarProps\r\n\r\nexport function MobileSidebar({\r\n items,\r\n appName = 'Admin',\r\n appLogo,\r\n onNavigate,\r\n activePath: activePathProp,\r\n LinkComponent,\r\n}: MobileSidebarProps) {\r\n const { mobileOpen, closeMobile } = useSidebar()\r\n const { permissions, activePath: ctxPath } = useLayoutContext()\r\n const activePath = activePathProp ?? ctxPath\r\n const filtered = filterSidebarByPermissions(items, permissions)\r\n\r\n useEffect(() => {\r\n if (!mobileOpen) return\r\n const onKey = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') closeMobile()\r\n }\r\n document.addEventListener('keydown', onKey)\r\n document.body.style.overflow = 'hidden'\r\n return () => {\r\n document.removeEventListener('keydown', onKey)\r\n document.body.style.overflow = ''\r\n }\r\n }, [mobileOpen, closeMobile])\r\n\r\n if (!mobileOpen) return null\r\n\r\n const handleNavigate = (path: string) => {\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n return (\r\n <div className='fixed inset-0 z-50 lg:hidden' role='dialog' aria-modal='true'>\r\n <button\r\n type='button'\r\n className='absolute inset-0 bg-slate-900/50 backdrop-blur-sm animate-fade-in'\r\n aria-label='Close menu'\r\n onClick={closeMobile}\r\n />\r\n <aside\r\n className={cn(\r\n 'absolute left-0 top-0 flex h-full w-[min(280px,85vw)] flex-col',\r\n 'border-r border-admin-border bg-admin-surface shadow-admin-lg animate-slide-in',\r\n )}\r\n >\r\n <div className='flex h-16 items-center justify-between border-b border-admin-border px-4'>\r\n <div className='flex items-center gap-3'>\r\n {appLogo ?? (\r\n <div className='flex h-9 w-9 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white'>\r\n {appName.charAt(0)}\r\n </div>\r\n )}\r\n <span className='font-semibold text-admin-text'>{appName}</span>\r\n </div>\r\n <button\r\n type='button'\r\n onClick={closeMobile}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg'\r\n aria-label='Close sidebar'\r\n >\r\n <X className='h-5 w-5' />\r\n </button>\r\n </div>\r\n <nav className='flex-1 overflow-y-auto p-3'>\r\n <SidebarGroup>\r\n {filtered.map((item) => (\r\n <SidebarItem\r\n key={item.label + (item.path ?? '')}\r\n item={item}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n onItemClick={closeMobile}\r\n />\r\n ))}\r\n </SidebarGroup>\r\n </nav>\r\n </aside>\r\n </div>\r\n )\r\n}\r\n","import { ChevronRight, Home } from 'lucide-react'\r\nimport type { BreadcrumbItem } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type BreadcrumbsProps = {\r\n items: BreadcrumbItem[]\r\n className?: string\r\n LinkComponent?: React.ComponentType<{\r\n to: string\r\n className?: string\r\n children: React.ReactNode\r\n }>\r\n onNavigate?: (path: string) => void\r\n}\r\n\r\nexport function Breadcrumbs({\r\n items,\r\n className,\r\n LinkComponent,\r\n onNavigate,\r\n}: BreadcrumbsProps) {\r\n if (!items.length) return null\r\n\r\n const renderLink = (item: BreadcrumbItem, isLast: boolean) => {\r\n const classNames = cn(\r\n 'text-sm transition-colors',\r\n isLast ? 'font-medium text-admin-text' : 'text-admin-muted hover:text-admin-text',\r\n )\r\n\r\n if (!item.path || isLast) {\r\n return <span className={classNames}>{item.label}</span>\r\n }\r\n\r\n if (LinkComponent) {\r\n return (\r\n <LinkComponent to={item.path} className={classNames}>\r\n {item.label}\r\n </LinkComponent>\r\n )\r\n }\r\n\r\n return (\r\n <button type='button' className={classNames} onClick={() => onNavigate?.(item.path!)}>\r\n {item.label}\r\n </button>\r\n )\r\n }\r\n\r\n return (\r\n <nav aria-label='Breadcrumb' className={cn('flex items-center gap-1.5', className)}>\r\n <Home className='h-4 w-4 text-admin-muted shrink-0' aria-hidden />\r\n {items.map((item, index) => {\r\n const isLast = index === items.length - 1\r\n return (\r\n <span key={`${item.label}-${index}`} className='flex items-center gap-1.5'>\r\n <ChevronRight className='h-3.5 w-3.5 text-admin-muted' aria-hidden />\r\n {renderLink(item, isLast)}\r\n </span>\r\n )\r\n })}\r\n </nav>\r\n )\r\n}\r\n","import { Search } from 'lucide-react'\r\nimport { useState, type FormEvent } from 'react'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type NavbarSearchProps = {\r\n placeholder?: string\r\n onSearch?: (query: string) => void\r\n className?: string\r\n}\r\n\r\nexport function NavbarSearch({\r\n placeholder = 'Search...',\r\n onSearch,\r\n className,\r\n}: NavbarSearchProps) {\r\n const [query, setQuery] = useState('')\r\n\r\n const handleSubmit = (e: FormEvent) => {\r\n e.preventDefault()\r\n onSearch?.(query.trim())\r\n }\r\n\r\n return (\r\n <form onSubmit={handleSubmit} className={cn('relative w-full max-w-md', className)}>\r\n <Search\r\n className='pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-admin-muted'\r\n aria-hidden\r\n />\r\n <input\r\n type='search'\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n placeholder={placeholder}\r\n className={cn(\r\n 'h-10 w-full rounded-xl border border-admin-border bg-admin-bg pl-10 pr-4 text-sm text-admin-text',\r\n 'placeholder:text-admin-muted focus:border-admin-primary focus:outline-none focus:ring-2 focus:ring-admin-primary/20',\r\n )}\r\n aria-label='Search'\r\n />\r\n </form>\r\n )\r\n}\r\n","import { Bell } from 'lucide-react'\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport type { NotificationItem } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type NotificationMenuProps = {\r\n notifications?: NotificationItem[]\r\n className?: string\r\n}\r\n\r\nexport function NotificationMenu({ notifications = [], className }: NotificationMenuProps) {\r\n const [open, setOpen] = useState(false)\r\n const ref = useRef<HTMLDivElement>(null)\r\n const unread = notifications.filter((n) => !n.read).length\r\n\r\n useEffect(() => {\r\n const handler = (e: MouseEvent) => {\r\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener('mousedown', handler)\r\n return () => document.removeEventListener('mousedown', handler)\r\n }, [])\r\n\r\n return (\r\n <div ref={ref} className={cn('relative', className)}>\r\n <button\r\n type='button'\r\n onClick={() => setOpen((v) => !v)}\r\n className={cn(\r\n 'relative inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border',\r\n 'bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text',\r\n 'focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n )}\r\n aria-label={`Notifications${unread ? `, ${unread} unread` : ''}`}\r\n aria-expanded={open}\r\n >\r\n <Bell className='h-4 w-4' aria-hidden />\r\n {unread > 0 && (\r\n <span className='absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-bold text-white'>\r\n {unread > 9 ? '9+' : unread}\r\n </span>\r\n )}\r\n </button>\r\n\r\n {open && (\r\n <div className='absolute right-0 top-full z-50 mt-2 w-80 rounded-xl border border-admin-border bg-admin-surface shadow-admin-lg animate-fade-in'>\r\n <div className='border-b border-admin-border px-4 py-3'>\r\n <p className='text-sm font-semibold text-admin-text'>Notifications</p>\r\n </div>\r\n <ul className='max-h-72 overflow-y-auto p-2'>\r\n {notifications.length === 0 ? (\r\n <li className='px-3 py-6 text-center text-sm text-admin-muted'>No notifications</li>\r\n ) : (\r\n notifications.map((n) => (\r\n <li\r\n key={n.id}\r\n className={cn(\r\n 'rounded-lg px-3 py-2.5 transition-colors hover:bg-admin-bg',\r\n !n.read && 'bg-admin-primary/5',\r\n )}\r\n >\r\n <p className='text-sm font-medium text-admin-text'>{n.title}</p>\r\n {n.description && (\r\n <p className='mt-0.5 text-xs text-admin-muted line-clamp-2'>{n.description}</p>\r\n )}\r\n {n.time && <p className='mt-1 text-[11px] text-admin-muted'>{n.time}</p>}\r\n </li>\r\n ))\r\n )}\r\n </ul>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useDarkMode() {\r\n const { darkMode, toggleDarkMode, setDarkMode } = useLayoutContext()\r\n return {\r\n isDark: darkMode,\r\n toggle: toggleDarkMode,\r\n setDarkMode,\r\n }\r\n}\r\n","import { Moon, Sun } from 'lucide-react'\r\nimport { useDarkMode } from '../hooks/useDarkMode'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type ThemeToggleProps = {\r\n className?: string\r\n}\r\n\r\nexport function ThemeToggle({ className }: ThemeToggleProps) {\r\n const { isDark, toggle } = useDarkMode()\r\n\r\n return (\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className={cn(\r\n 'inline-flex h-9 w-9 items-center justify-center rounded-lg border border-admin-border',\r\n 'bg-admin-surface text-admin-muted transition-colors hover:bg-admin-bg hover:text-admin-text',\r\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-admin-primary focus-visible:ring-offset-2',\r\n className,\r\n )}\r\n aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}\r\n >\r\n {isDark ? <Sun className='h-4 w-4' aria-hidden /> : <Moon className='h-4 w-4' aria-hidden />}\r\n </button>\r\n )\r\n}\r\n","import { ChevronDown, LogOut, Settings, User } from 'lucide-react'\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport type { AdminUser } from '../types'\r\nimport { cn } from '../utils/cn'\r\n\r\nexport type UserMenuProps = {\r\n user: AdminUser\r\n onLogout?: () => void\r\n onSettings?: () => void\r\n className?: string\r\n}\r\n\r\nexport function UserMenu({ user, onLogout, onSettings, className }: UserMenuProps) {\r\n const [open, setOpen] = useState(false)\r\n const ref = useRef<HTMLDivElement>(null)\r\n\r\n useEffect(() => {\r\n const handler = (e: MouseEvent) => {\r\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\r\n }\r\n document.addEventListener('mousedown', handler)\r\n return () => document.removeEventListener('mousedown', handler)\r\n }, [])\r\n\r\n const initials = user.name\r\n .split(' ')\r\n .map((n) => n[0])\r\n .join('')\r\n .slice(0, 2)\r\n .toUpperCase()\r\n\r\n return (\r\n <div ref={ref} className={cn('relative', className)}>\r\n <button\r\n type='button'\r\n onClick={() => setOpen((v) => !v)}\r\n className={cn(\r\n 'flex items-center gap-2 rounded-xl border border-admin-border bg-admin-surface px-2 py-1.5',\r\n 'transition-colors hover:bg-admin-bg focus-visible:ring-2 focus-visible:ring-admin-primary',\r\n )}\r\n aria-expanded={open}\r\n aria-haspopup='menu'\r\n >\r\n {user.avatar ? (\r\n <img src={user.avatar} alt='' className='h-8 w-8 rounded-lg object-cover' />\r\n ) : (\r\n <div className='flex h-8 w-8 items-center justify-center rounded-lg bg-admin-primary text-xs font-semibold text-white'>\r\n {initials}\r\n </div>\r\n )}\r\n <div className='hidden text-left sm:block'>\r\n <p className='text-sm font-medium text-admin-text leading-tight'>{user.name}</p>\r\n <p className='text-xs text-admin-muted leading-tight'>{user.email}</p>\r\n </div>\r\n <ChevronDown className='hidden h-4 w-4 text-admin-muted sm:block' aria-hidden />\r\n </button>\r\n\r\n {open && (\r\n <div\r\n role='menu'\r\n className='absolute right-0 top-full z-50 mt-2 w-56 rounded-xl border border-admin-border bg-admin-surface p-1.5 shadow-admin-lg animate-fade-in'\r\n >\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg'\r\n onClick={() => {\r\n onSettings?.()\r\n setOpen(false)\r\n }}\r\n >\r\n <Settings className='h-4 w-4 text-admin-muted' />\r\n Settings\r\n </button>\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-admin-text hover:bg-admin-bg'\r\n >\r\n <User className='h-4 w-4 text-admin-muted' />\r\n Profile\r\n </button>\r\n <hr className='my-1 border-admin-border' />\r\n <button\r\n type='button'\r\n role='menuitem'\r\n className='flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 hover:bg-red-50 dark:hover:bg-red-950/30'\r\n onClick={() => {\r\n onLogout?.()\r\n setOpen(false)\r\n }}\r\n >\r\n <LogOut className='h-4 w-4' />\r\n Log out\r\n </button>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { Menu, PanelLeftClose, PanelLeftOpen } from 'lucide-react'\r\nimport type { NavbarProps } from '../types'\r\nimport { cn } from '../utils/cn'\r\nimport { Breadcrumbs } from './Breadcrumbs'\r\nimport { NavbarSearch } from './NavbarSearch'\r\nimport { NotificationMenu } from './NotificationMenu'\r\nimport { ThemeToggle } from './ThemeToggle'\r\nimport { UserMenu } from './UserMenu'\r\n\r\nexport function Navbar({\r\n user,\r\n notifications = [],\r\n onSearch,\r\n searchPlaceholder = 'Search...',\r\n showSearch = true,\r\n className,\r\n breadcrumbs = [],\r\n onMenuClick,\r\n sidebarCollapsed,\r\n onToggleSidebar,\r\n}: NavbarProps) {\r\n return (\r\n <header\r\n className={cn(\r\n 'sticky top-0 z-40 flex h-16 shrink-0 items-center gap-4 border-b border-admin-border',\r\n 'admin-glass bg-admin-surface px-4 lg:px-6',\r\n className,\r\n )}\r\n >\r\n <button\r\n type='button'\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:hidden'\r\n onClick={onMenuClick}\r\n aria-label='Open menu'\r\n >\r\n <Menu className='h-5 w-5' />\r\n </button>\r\n\r\n <button\r\n type='button'\r\n className='hidden rounded-lg p-2 text-admin-muted hover:bg-admin-bg lg:inline-flex'\r\n onClick={onToggleSidebar}\r\n aria-label={sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}\r\n >\r\n {sidebarCollapsed ? (\r\n <PanelLeftOpen className='h-5 w-5' />\r\n ) : (\r\n <PanelLeftClose className='h-5 w-5' />\r\n )}\r\n </button>\r\n\r\n <div className='hidden min-w-0 flex-1 md:block'>\r\n <Breadcrumbs items={breadcrumbs} />\r\n </div>\r\n\r\n <div className='flex flex-1 items-center justify-end gap-2 sm:gap-3'>\r\n {showSearch && (\r\n <div className='hidden flex-1 sm:flex sm:max-w-md'>\r\n <NavbarSearch placeholder={searchPlaceholder} onSearch={onSearch} />\r\n </div>\r\n )}\r\n <ThemeToggle />\r\n <NotificationMenu notifications={notifications} />\r\n {user && <UserMenu user={user} />}\r\n </div>\r\n </header>\r\n )\r\n}\r\n","import { PanelLeftClose, PanelLeftOpen } from 'lucide-react'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { SidebarProps } from '../types'\r\nimport { filterSidebarByPermissions } from '../utils/permissions'\r\nimport { cn } from '../utils/cn'\r\nimport { SidebarGroup } from './SidebarGroup'\r\nimport { SidebarItem } from './SidebarItem'\r\n\r\nexport function Sidebar({\r\n items,\r\n collapsed: collapsedProp,\r\n appName = 'Admin',\r\n appLogo,\r\n className,\r\n onNavigate,\r\n activePath: activePathProp,\r\n LinkComponent,\r\n}: SidebarProps) {\r\n const { collapsed: ctxCollapsed, toggle, closeMobile } = useSidebar()\r\n const { permissions, activePath: ctxPath } = useLayoutContext()\r\n\r\n const collapsed = collapsedProp ?? ctxCollapsed\r\n const activePath = activePathProp ?? ctxPath\r\n const filtered = filterSidebarByPermissions(items, permissions)\r\n\r\n const handleNavigate = (path: string) => {\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n return (\r\n <aside\r\n className={cn(\r\n 'hidden lg:flex shrink-0 flex-col border-r border-admin-border bg-admin-surface',\r\n 'h-screen sticky top-0 transition-[width] duration-300 ease-in-out',\r\n collapsed ? 'w-[var(--admin-sidebar-collapsed)]' : 'w-[var(--admin-sidebar-width)]',\r\n className,\r\n )}\r\n aria-label='Sidebar navigation'\r\n >\r\n <div\r\n className={cn(\r\n 'flex h-16 items-center border-b border-admin-border px-4',\r\n collapsed ? 'justify-center' : 'justify-between gap-2',\r\n )}\r\n >\r\n <div className={cn('flex items-center gap-3 min-w-0', collapsed && 'justify-center')}>\r\n {appLogo ?? (\r\n <div className='flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-admin-primary text-sm font-bold text-white shadow-admin'>\r\n {appName.charAt(0)}\r\n </div>\r\n )}\r\n {!collapsed && (\r\n <span className='truncate text-base font-semibold text-admin-text'>{appName}</span>\r\n )}\r\n </div>\r\n {!collapsed && (\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg hover:text-admin-text focus-visible:ring-2 focus-visible:ring-admin-primary'\r\n aria-label='Collapse sidebar'\r\n >\r\n <PanelLeftClose className='h-4 w-4' />\r\n </button>\r\n )}\r\n </div>\r\n\r\n {collapsed && (\r\n <div className='flex justify-center py-2 border-b border-admin-border'>\r\n <button\r\n type='button'\r\n onClick={toggle}\r\n className='rounded-lg p-2 text-admin-muted hover:bg-admin-bg'\r\n aria-label='Expand sidebar'\r\n >\r\n <PanelLeftOpen className='h-4 w-4' />\r\n </button>\r\n </div>\r\n )}\r\n\r\n <nav className='flex-1 overflow-y-auto p-3'>\r\n <SidebarGroup collapsed={collapsed}>\r\n {filtered.map((item) => (\r\n <SidebarItem\r\n key={item.label + (item.path ?? '')}\r\n item={item}\r\n collapsed={collapsed}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n ))}\r\n </SidebarGroup>\r\n </nav>\r\n </aside>\r\n )\r\n}\r\n","import { useMemo } from 'react'\r\nimport { LayoutProvider } from '../context/LayoutContext'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport { useSidebar } from '../hooks/useSidebar'\r\nimport type { AdminLayoutProps } from '../types'\r\nimport { generateBreadcrumbsFromPath } from '../utils/breadcrumbs'\r\nimport { cn } from '../utils/cn'\r\nimport { AdminLayoutRouterSync } from './AdminLayoutRouterSync'\r\nimport { Footer } from './Footer'\r\nimport { MobileSidebar } from './MobileSidebar'\r\nimport { Navbar } from './Navbar'\r\nimport { Sidebar } from './Sidebar'\r\n\r\nfunction AdminLayoutInner({\r\n children,\r\n sidebarItems,\r\n appName = 'Admin',\r\n appLogo,\r\n user,\r\n notifications = [],\r\n footer,\r\n showFooter = true,\r\n showSearch = true,\r\n searchPlaceholder,\r\n onSearch,\r\n breadcrumbs: breadcrumbsProp,\r\n contentClassName,\r\n activePath: activePathProp,\r\n onNavigate,\r\n LinkComponent,\r\n enableRouter: _enableRouter = false,\r\n}: AdminLayoutProps) {\r\n const {\r\n sidebarCollapsed,\r\n toggleSidebar,\r\n toggleMobile,\r\n breadcrumbs: ctxBreadcrumbs,\r\n setBreadcrumbs,\r\n activePath: ctxPath,\r\n setActivePath,\r\n } = useLayoutContext()\r\n const { closeMobile } = useSidebar()\r\n\r\n const activePath = activePathProp ?? ctxPath\r\n const breadcrumbs = breadcrumbsProp ?? ctxBreadcrumbs\r\n\r\n const handleNavigate = (path: string) => {\r\n setActivePath(path)\r\n if (!breadcrumbsProp) {\r\n setBreadcrumbs(generateBreadcrumbsFromPath(path))\r\n }\r\n onNavigate?.(path)\r\n closeMobile()\r\n }\r\n\r\n return (\r\n <div className='admin-layout-root min-h-screen bg-admin-bg text-admin-text'>\r\n <MobileSidebar\r\n items={sidebarItems}\r\n appName={appName}\r\n appLogo={appLogo}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n\r\n <div className='flex min-h-screen w-full'>\r\n <Sidebar\r\n items={sidebarItems}\r\n appName={appName}\r\n appLogo={appLogo}\r\n activePath={activePath}\r\n onNavigate={handleNavigate}\r\n LinkComponent={LinkComponent}\r\n />\r\n\r\n <div className='flex min-h-screen min-w-0 flex-1 flex-col'>\r\n <Navbar\r\n user={user}\r\n notifications={notifications}\r\n onSearch={onSearch}\r\n searchPlaceholder={searchPlaceholder}\r\n showSearch={showSearch}\r\n breadcrumbs={breadcrumbs}\r\n onMenuClick={toggleMobile}\r\n sidebarCollapsed={sidebarCollapsed}\r\n onToggleSidebar={toggleSidebar}\r\n />\r\n\r\n <main className={cn('flex-1 p-4 lg:p-6', contentClassName)}>{children}</main>\r\n\r\n {showFooter && <Footer>{footer}</Footer>}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport function AdminLayout(props: AdminLayoutProps) {\r\n const {\r\n storageKey = 'rqdhw3n-admin-layout',\r\n defaultCollapsed = false,\r\n initialDarkMode = false,\r\n permissions = {},\r\n breadcrumbs: initialBreadcrumbs = [],\r\n activePath: initialActivePath = '/',\r\n enableRouter = false,\r\n children,\r\n ...rest\r\n } = props\r\n\r\n const initialBc = useMemo(\r\n () =>\r\n initialBreadcrumbs.length\r\n ? initialBreadcrumbs\r\n : generateBreadcrumbsFromPath(initialActivePath),\r\n [initialBreadcrumbs, initialActivePath],\r\n )\r\n\r\n return (\r\n <LayoutProvider\r\n storageKey={storageKey}\r\n defaultCollapsed={defaultCollapsed}\r\n permissions={permissions}\r\n initialBreadcrumbs={initialBc}\r\n initialActivePath={initialActivePath}\r\n initialDarkMode={initialDarkMode}\r\n >\r\n {enableRouter ? (\r\n <AdminLayoutRouterSync>\r\n <AdminLayoutInner {...rest} enableRouter={enableRouter}>\r\n {children}\r\n </AdminLayoutInner>\r\n </AdminLayoutRouterSync>\r\n ) : (\r\n <AdminLayoutInner {...rest} enableRouter={enableRouter}>\r\n {children}\r\n </AdminLayoutInner>\r\n )}\r\n </LayoutProvider>\r\n )\r\n}\r\n","import { NavLink } from 'react-router-dom'\r\nimport type { ReactNode } from 'react'\r\n\r\nexport type RouterLinkProps = {\r\n to: string\r\n className?: string\r\n children: ReactNode\r\n onClick?: () => void\r\n}\r\n\r\nexport function RouterLink({ to, className, children, onClick }: RouterLinkProps) {\r\n return (\r\n <NavLink to={to} className={className} onClick={onClick}>\r\n {children}\r\n </NavLink>\r\n )\r\n}\r\n","import { useLayoutContext } from '../context/LayoutContext'\r\n\r\nexport function useLayoutState() {\r\n return useLayoutContext()\r\n}\r\n","import { useCallback } from 'react'\r\nimport { useLayoutContext } from '../context/LayoutContext'\r\nimport type { BreadcrumbItem } from '../types'\r\n\r\nexport function useBreadcrumbs() {\r\n const { breadcrumbs, setBreadcrumbs } = useLayoutContext()\r\n\r\n const update = useCallback(\r\n (items: BreadcrumbItem[]) => setBreadcrumbs(items),\r\n [setBreadcrumbs],\r\n )\r\n\r\n return {\r\n items: breadcrumbs,\r\n setBreadcrumbs: update,\r\n }\r\n}\r\n"],"names":["loadLayoutState","key","raw","saveLayoutState","state","LayoutContext","createContext","LayoutProvider","children","storageKey","defaultCollapsed","permissions","initialBreadcrumbs","initialActivePath","initialDarkMode","persisted","sidebarCollapsed","setSidebarCollapsed","useState","mobileOpen","setMobileOpen","darkMode","setDarkMode","breadcrumbs","setBreadcrumbs","activePath","setActivePath","useEffect","root","toggleSidebar","useCallback","v","toggleMobile","toggleDarkMode","value","useMemo","jsx","useLayoutContext","ctx","useContext","useSidebar","generateBreadcrumbsFromPath","pathname","segments","seg","i","path","c","r","e","t","f","n","o","clsx","cn","inputs","AdminLayoutRouterSync","useLocation","manualBreadcrumbs","Footer","className","jsxs","parsePermission","permission","dot","hasPermission","resource","action","allowed","filterSidebarByPermissions","items","item","_a","SidebarGroup","label","collapsed","isActive","SidebarItem","depth","onNavigate","LinkComponent","onItemClick","hasChildren","active","childActive","_b","open","setOpen","Icon","baseClass","content","Fragment","ChevronDown","handleClick","row","child","MobileSidebar","appName","appLogo","activePathProp","closeMobile","ctxPath","filtered","onKey","handleNavigate","X","Breadcrumbs","renderLink","isLast","classNames","Home","index","ChevronRight","NavbarSearch","placeholder","onSearch","query","setQuery","handleSubmit","Search","NotificationMenu","notifications","ref","useRef","unread","handler","Bell","useDarkMode","ThemeToggle","isDark","toggle","Sun","Moon","UserMenu","user","onLogout","onSettings","initials","Settings","User","LogOut","Navbar","searchPlaceholder","showSearch","onMenuClick","onToggleSidebar","Menu","PanelLeftOpen","PanelLeftClose","Sidebar","collapsedProp","ctxCollapsed","AdminLayoutInner","sidebarItems","footer","showFooter","breadcrumbsProp","contentClassName","_enableRouter","ctxBreadcrumbs","AdminLayout","props","enableRouter","rest","initialBc","RouterLink","to","onClick","NavLink","useLayoutState","useBreadcrumbs","update"],"mappings":"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","x_google_ignoreList":[4]}