Skip to content

Commit 23f7020

Browse files
committed
Better sidebar logic
1 parent 9481359 commit 23f7020

File tree

1 file changed

+131
-128
lines changed

1 file changed

+131
-128
lines changed

src/lib/components/layout/AppSidebar.svelte

Lines changed: 131 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
Zap,
2222
Cpu,
2323
} from '@lucide/svelte';
24-
import type { ApiUserSelf } from '$lib/types/ApiUser';
2524
2625
let currentUser = $derived($UserStore.self);
2726
@@ -30,7 +29,6 @@
3029
3130
interface Entry {
3231
title: string;
33-
auth?: true | RoleType[];
3432
}
3533
3634
interface Item extends Entry {
@@ -50,113 +48,124 @@
5048
menus: Menu[];
5149
}
5250
53-
const groups: Group[] = [
54-
{
55-
title: 'General',
56-
menus: [
57-
{
58-
title: 'Home',
59-
auth: true,
60-
Icon: House,
61-
href: '/home',
62-
},
51+
let generalGroup = $derived.by<Group>(() => {
52+
const isAuthenticated = !!currentUser;
53+
54+
let menus: Menu[] = [
55+
{
56+
title: 'Home',
57+
Icon: House,
58+
href: isAuthenticated ? '/home' : '/',
59+
},
60+
];
61+
62+
if (isAuthenticated) {
63+
menus = menus.concat([
6364
{
6465
title: 'Shockers',
65-
auth: true,
6666
Icon: Zap,
6767
href: '/shockers',
6868
},
6969
{
7070
title: 'Hubs',
71-
auth: true,
7271
Icon: Router,
7372
href: '/hubs',
7473
},
7574
{
7675
title: 'Shares',
77-
auth: true,
7876
Icon: Link,
7977
href: '/shares',
8078
},
81-
{
82-
title: 'Flashtool',
83-
Icon: Cpu,
84-
href: '/flashtool',
85-
},
86-
],
87-
},
88-
];
79+
]);
80+
}
8981
90-
const footerGroups: Group[] = [
91-
{
92-
title: 'Admin',
93-
auth: [RoleType.Admin, RoleType.System],
94-
collapsible: { open: false },
95-
menus: [
96-
{
97-
title: 'Monitoring',
98-
Icon: SquareActivity,
99-
subItems: [
100-
{
101-
title: 'Online Hubs',
102-
href: '/admin/online-hubs',
103-
},
104-
],
105-
},
106-
{
107-
title: 'Management',
108-
Icon: Wrench,
109-
subItems: [
110-
{
111-
title: 'Users',
112-
href: '/admin/users',
113-
},
114-
],
115-
},
116-
{
117-
title: 'Hangfire',
118-
Icon: Timer,
119-
href: '/hangfire',
120-
target: '_blank',
121-
},
122-
],
123-
},
124-
{
125-
title: 'Settings',
126-
auth: true,
127-
menus: [
128-
{
129-
title: 'Account',
130-
Icon: UserPen,
131-
href: '/settings/account',
132-
},
133-
{
134-
title: 'Sessions',
135-
Icon: MonitorSmartphone,
136-
href: '/settings/sessions',
137-
},
138-
{
139-
title: 'API Tokens',
140-
Icon: KeyRound,
141-
href: '/settings/api-tokens',
142-
},
143-
{
144-
title: 'Danger Zone',
145-
Icon: TriangleAlert,
146-
class: 'text-red-500!',
147-
href: '/settings/danger-zone',
148-
},
149-
],
150-
},
151-
];
82+
menus.push({
83+
title: 'Flashtool',
84+
Icon: Cpu,
85+
href: '/flashtool',
86+
});
15287
153-
function meetsReq(currentUser: ApiUserSelf | null, entry: Entry) {
154-
if (!entry.auth) return true;
155-
if (currentUser === null) return false;
156-
if (entry.auth === true) return true;
88+
return {
89+
title: 'General',
90+
menus,
91+
};
92+
});
93+
const adminGroup: Group = {
94+
title: 'Admin',
95+
collapsible: { open: false },
96+
menus: [
97+
{
98+
title: 'Monitoring',
99+
Icon: SquareActivity,
100+
subItems: [
101+
{
102+
title: 'Online Hubs',
103+
href: '/admin/online-hubs',
104+
},
105+
],
106+
},
107+
{
108+
title: 'Management',
109+
Icon: Wrench,
110+
subItems: [
111+
{
112+
title: 'Users',
113+
href: '/admin/users',
114+
},
115+
],
116+
},
117+
{
118+
title: 'Hangfire',
119+
Icon: Timer,
120+
href: '/hangfire',
121+
target: '_blank',
122+
},
123+
],
124+
};
125+
const settingsGroup: Group = {
126+
title: 'Settings',
127+
menus: [
128+
{
129+
title: 'Account',
130+
Icon: UserPen,
131+
href: '/settings/account',
132+
},
133+
{
134+
title: 'Sessions',
135+
Icon: MonitorSmartphone,
136+
href: '/settings/sessions',
137+
},
138+
{
139+
title: 'API Tokens',
140+
Icon: KeyRound,
141+
href: '/settings/api-tokens',
142+
},
143+
{
144+
title: 'Danger Zone',
145+
Icon: TriangleAlert,
146+
class: 'text-red-500!',
147+
href: '/settings/danger-zone',
148+
},
149+
],
150+
};
151+
152+
let headerGroups = $derived([generalGroup]);
153+
let footerGroups = $derived.by(() => {
154+
const isAuthenticated = !!currentUser;
155+
156+
let groups: Group[] = [];
157+
158+
if (isAuthenticated) {
159+
if (currentUser.roles.includes(RoleType.Admin)) {
160+
groups.push(adminGroup);
161+
}
162+
163+
groups.push(settingsGroup);
164+
}
165+
166+
return groups;
167+
});
157168
158-
return entry.auth.some((role) => currentUser.roles.includes(role));
159-
}
160169
function isPathMatch(path: string, href?: string) {
161170
return path === href || path.startsWith(href + '/');
162171
}
@@ -191,57 +200,51 @@
191200
{#if menu.subItems}
192201
<Sidebar.MenuSub>
193202
{#each menu.subItems as subItem (subItem.title)}
194-
{#if meetsReq(currentUser, subItem)}
195-
{@render menuSubItemSection(subItem)}
196-
{/if}
203+
{@render menuSubItemSection(subItem)}
197204
{/each}
198205
</Sidebar.MenuSub>
199206
{/if}
200207
{/snippet}
201208

202-
{#snippet groupContentSection(currentUser: ApiUserSelf | null, group: Group)}
209+
{#snippet groupContentSection(group: Group)}
203210
<Sidebar.GroupContent>
204211
<Sidebar.Menu>
205212
{#each group.menus as menu (menu.title)}
206-
{#if meetsReq(currentUser, menu)}
207-
{@render menuSection(menu)}
208-
{/if}
213+
{@render menuSection(menu)}
209214
{/each}
210215
</Sidebar.Menu>
211216
</Sidebar.GroupContent>
212217
{/snippet}
213218

214-
{#snippet groupsSection(currentUser: ApiUserSelf | null, groups: Group[])}
219+
{#snippet groupsSection(groups: Group[])}
215220
{#each groups as group (group.title)}
216-
{#if meetsReq(currentUser, group)}
217-
{#if group.collapsible === undefined}
221+
{#if group.collapsible === undefined}
222+
<Sidebar.Group>
223+
<Sidebar.GroupLabel>{group.title}</Sidebar.GroupLabel>
224+
{@render groupContentSection(group)}
225+
</Sidebar.Group>
226+
{:else}
227+
<Collapsible.Root
228+
open={group.collapsible.open ||
229+
(sidebarContext.state === 'collapsed' && !sidebarContext.isMobile)}
230+
class="group/collapsible"
231+
>
218232
<Sidebar.Group>
219-
<Sidebar.GroupLabel>{group.title}</Sidebar.GroupLabel>
220-
{@render groupContentSection(currentUser, group)}
233+
<Sidebar.GroupLabel>
234+
{#snippet child({ props })}
235+
<Collapsible.Trigger {...props}>
236+
{group.title}
237+
<ChevronDown
238+
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"
239+
/>
240+
</Collapsible.Trigger>
241+
{/snippet}
242+
</Sidebar.GroupLabel>
243+
<Collapsible.Content>
244+
{@render groupContentSection(group)}
245+
</Collapsible.Content>
221246
</Sidebar.Group>
222-
{:else}
223-
<Collapsible.Root
224-
open={group.collapsible.open ||
225-
(sidebarContext.state === 'collapsed' && !sidebarContext.isMobile)}
226-
class="group/collapsible"
227-
>
228-
<Sidebar.Group>
229-
<Sidebar.GroupLabel>
230-
{#snippet child({ props })}
231-
<Collapsible.Trigger {...props}>
232-
{group.title}
233-
<ChevronDown
234-
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"
235-
/>
236-
</Collapsible.Trigger>
237-
{/snippet}
238-
</Sidebar.GroupLabel>
239-
<Collapsible.Content>
240-
{@render groupContentSection(currentUser, group)}
241-
</Collapsible.Content>
242-
</Sidebar.Group>
243-
</Collapsible.Root>
244-
{/if}
247+
</Collapsible.Root>
245248
{/if}
246249
{/each}
247250
{/snippet}
@@ -262,9 +265,9 @@
262265
</a>
263266
</Sidebar.Header>
264267
<Sidebar.Content>
265-
{@render groupsSection(currentUser, groups)}
268+
{@render groupsSection(headerGroups)}
266269
<div class="grow-1"></div>
267-
{@render groupsSection(currentUser, footerGroups)}
270+
{@render groupsSection(footerGroups)}
268271
</Sidebar.Content>
269272
<Sidebar.Footer></Sidebar.Footer>
270273
</Sidebar.Root>

0 commit comments

Comments
 (0)