Skip to content

Commit 94a82d1

Browse files
iammukeshmclaude
andcommitted
feat(docs): nested sidebar for Modules — Identity, Multitenancy, Auditing sub-groups
- Add SidebarSubGroup type with nested items support - Update DocsSidebar to render sub-labels with indented children - Update DocsPagination to flatten nested items for prev/next - Update Breadcrumbs to show sub-group in trail (Modules > Identity > Users) - Update mobile panel with sub-group headings and indented links - Fix nav menu links to point to correct new paths - Style sub-groups in both desktop sidebar and mobile menu Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3263665 commit 94a82d1

7 files changed

Lines changed: 187 additions & 44 deletions

File tree

docs/src/components/Breadcrumbs.astro

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
import { sidebar } from '../config/docs.config';
2+
import { sidebar, isSubGroup } from '../config/docs.config';
3+
import type { SidebarLink, SidebarSubGroup } from '../config/docs.config';
34
45
interface Props {
56
currentSlug: string;
@@ -8,9 +9,28 @@ interface Props {
89
910
const { currentSlug, title } = Astro.props;
1011
11-
// Find which group this page belongs to
12-
const group = sidebar.find((g) => g.items.some((item) => item.slug === currentSlug));
13-
const groupLabel = group?.label || '';
12+
// Find which group (and optionally sub-group) this page belongs to
13+
let groupLabel = '';
14+
let subGroupLabel = '';
15+
16+
for (const group of sidebar) {
17+
for (const item of group.items) {
18+
if (isSubGroup(item)) {
19+
const sub = item as SidebarSubGroup;
20+
if (sub.items.some((s: SidebarLink) => s.slug === currentSlug)) {
21+
groupLabel = group.label;
22+
subGroupLabel = sub.label;
23+
break;
24+
}
25+
} else {
26+
if ((item as SidebarLink).slug === currentSlug) {
27+
groupLabel = group.label;
28+
break;
29+
}
30+
}
31+
}
32+
if (groupLabel) break;
33+
}
1434
---
1535

1636
<nav class="breadcrumbs" aria-label="Breadcrumb">
@@ -23,6 +43,12 @@ const groupLabel = group?.label || '';
2343
<span class="bc-group">{groupLabel}</span>
2444
</>
2545
)}
46+
{subGroupLabel && (
47+
<>
48+
<span class="bc-sep">/</span>
49+
<span class="bc-group">{subGroupLabel}</span>
50+
</>
51+
)}
2652
<span class="bc-sep">/</span>
2753
<span class="bc-current">{title}</span>
2854
</nav>

docs/src/components/DocsPagination.astro

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
---
2-
import { sidebar } from '../config/docs.config';
2+
import { sidebar, isSubGroup } from '../config/docs.config';
3+
import type { SidebarLink } from '../config/docs.config';
34
45
interface Props {
56
currentSlug: string;
67
}
78
89
const { currentSlug } = Astro.props;
910
10-
// Flatten all items
11-
const allItems = sidebar.flatMap((group) => group.items);
11+
// Flatten all items (handles nested sub-groups)
12+
const allItems: SidebarLink[] = sidebar.flatMap((group) =>
13+
group.items.flatMap((item) =>
14+
isSubGroup(item) ? item.items : [item as SidebarLink]
15+
)
16+
);
1217
const currentIndex = allItems.findIndex((item) => item.slug === currentSlug);
1318
const prev = currentIndex > 0 ? allItems[currentIndex - 1] : null;
1419
const next = currentIndex < allItems.length - 1 ? allItems[currentIndex + 1] : null;

docs/src/components/DocsSidebar.astro

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
import { sidebar } from '../config/docs.config';
2+
import { sidebar, isSubGroup } from '../config/docs.config';
3+
import type { SidebarLink, SidebarSubGroup } from '../config/docs.config';
34
45
interface Props {
56
currentSlug: string;
@@ -13,16 +14,34 @@ const { currentSlug } = Astro.props;
1314
<div class="sidebar-group">
1415
<div class="sidebar-label">{group.label}</div>
1516
<ul class="sidebar-links">
16-
{group.items.map((item) => (
17-
<li>
18-
<a
19-
href={`/docs/${item.slug}/`}
20-
class:list={[{ active: currentSlug === item.slug }]}
21-
>
22-
{item.title}
23-
</a>
24-
</li>
25-
))}
17+
{group.items.map((item) =>
18+
isSubGroup(item) ? (
19+
<li class="sidebar-subgroup">
20+
<div class="sidebar-sublabel">{(item as SidebarSubGroup).label}</div>
21+
<ul class="sidebar-sublinks">
22+
{(item as SidebarSubGroup).items.map((sub: SidebarLink) => (
23+
<li>
24+
<a
25+
href={`/docs/${sub.slug}/`}
26+
class:list={[{ active: currentSlug === sub.slug }]}
27+
>
28+
{sub.title}
29+
</a>
30+
</li>
31+
))}
32+
</ul>
33+
</li>
34+
) : (
35+
<li>
36+
<a
37+
href={`/docs/${(item as SidebarLink).slug}/`}
38+
class:list={[{ active: currentSlug === (item as SidebarLink).slug }]}
39+
>
40+
{(item as SidebarLink).title}
41+
</a>
42+
</li>
43+
)
44+
)}
2645
</ul>
2746
</div>
2847
))}

docs/src/components/Nav.astro

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import logo from '../assets/icon.png';
1717

1818
<nav class="hd-nav">
1919
<a href="/docs/introduction/">Docs</a>
20-
<a href="/docs/architecture/">Architecture</a>
21-
<a href="/docs/modules/identity/">Modules</a>
22-
<a href="/docs/adding-a-feature/">Guides</a>
20+
<a href="/docs/modules/identity/overview/">Modules</a>
21+
<a href="/docs/building-blocks/overview/">Building Blocks</a>
22+
<a href="/docs/deployment/overview/">Deployment</a>
2323
</nav>
2424

2525
<div class="hd-end">
@@ -54,9 +54,9 @@ import logo from '../assets/icon.png';
5454

5555
<div class="hd-mob" id="hd-mob">
5656
<a href="/docs/introduction/">Docs</a>
57-
<a href="/docs/architecture/">Architecture</a>
58-
<a href="/docs/modules/identity/">Modules</a>
59-
<a href="/docs/adding-a-feature/">Guides</a>
57+
<a href="/docs/modules/identity/overview/">Modules</a>
58+
<a href="/docs/building-blocks/overview/">Building Blocks</a>
59+
<a href="/docs/deployment/overview/">Deployment</a>
6060
<a href="https://github.com/fullstackhero/dotnet-starter-kit" target="_blank" rel="noopener">GitHub</a>
6161
<div class="hd-mob-icons">
6262
<a href="https://github.com/fullstackhero/dotnet-starter-kit" target="_blank" rel="noopener" aria-label="GitHub">

docs/src/config/docs.config.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@ export interface SidebarLink {
33
slug: string;
44
}
55

6-
export interface SidebarGroup {
6+
export interface SidebarSubGroup {
77
label: string;
88
items: SidebarLink[];
99
}
1010

11+
export type SidebarItem = SidebarLink | SidebarSubGroup;
12+
13+
export interface SidebarGroup {
14+
label: string;
15+
items: SidebarItem[];
16+
}
17+
18+
export function isSubGroup(item: SidebarItem): item is SidebarSubGroup {
19+
return 'items' in item && !('slug' in item);
20+
}
21+
1122
export const sidebar: SidebarGroup[] = [
1223
{
1324
label: 'Getting Started',
@@ -32,15 +43,30 @@ export const sidebar: SidebarGroup[] = [
3243
{
3344
label: 'Modules',
3445
items: [
35-
{ title: 'Identity Overview', slug: 'modules/identity/overview' },
36-
{ title: 'Users', slug: 'modules/identity/users' },
37-
{ title: 'Roles & Permissions', slug: 'modules/identity/roles-permissions' },
38-
{ title: 'Authentication', slug: 'modules/identity/authentication' },
39-
{ title: 'Sessions & Groups', slug: 'modules/identity/sessions-groups' },
40-
{ title: 'Multitenancy Overview', slug: 'modules/multitenancy/overview' },
41-
{ title: 'Tenant Provisioning', slug: 'modules/multitenancy/provisioning' },
42-
{ title: 'Auditing Overview', slug: 'modules/auditing/overview' },
43-
{ title: 'Querying Audits', slug: 'modules/auditing/querying' },
46+
{
47+
label: 'Identity',
48+
items: [
49+
{ title: 'Overview', slug: 'modules/identity/overview' },
50+
{ title: 'Users', slug: 'modules/identity/users' },
51+
{ title: 'Roles & Permissions', slug: 'modules/identity/roles-permissions' },
52+
{ title: 'Authentication', slug: 'modules/identity/authentication' },
53+
{ title: 'Sessions & Groups', slug: 'modules/identity/sessions-groups' },
54+
],
55+
},
56+
{
57+
label: 'Multitenancy',
58+
items: [
59+
{ title: 'Overview', slug: 'modules/multitenancy/overview' },
60+
{ title: 'Provisioning', slug: 'modules/multitenancy/provisioning' },
61+
],
62+
},
63+
{
64+
label: 'Auditing',
65+
items: [
66+
{ title: 'Overview', slug: 'modules/auditing/overview' },
67+
{ title: 'Querying Audits', slug: 'modules/auditing/querying' },
68+
],
69+
},
4470
],
4571
},
4672
{
@@ -61,7 +87,7 @@ export const sidebar: SidebarGroup[] = [
6187
{
6288
label: 'Cross-Cutting Concerns',
6389
items: [
64-
{ title: 'Authentication & Authorization', slug: 'cross-cutting/authentication-authorization' },
90+
{ title: 'Auth & Authorization', slug: 'cross-cutting/authentication-authorization' },
6591
{ title: 'Multitenancy Deep Dive', slug: 'cross-cutting/multitenancy-deep-dive' },
6692
{ title: 'Exception Handling', slug: 'cross-cutting/exception-handling' },
6793
{ title: 'Observability', slug: 'cross-cutting/observability' },

docs/src/layouts/Docs.astro

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import DocsToc from '../components/DocsToc.astro';
77
import DocsPagination from '../components/DocsPagination.astro';
88
import Breadcrumbs from '../components/Breadcrumbs.astro';
99
import SearchModal from '../components/SearchModal.astro';
10-
import { sidebar } from '../config/docs.config';
10+
import { sidebar, isSubGroup } from '../config/docs.config';
11+
import type { SidebarLink, SidebarSubGroup } from '../config/docs.config';
1112
import '../styles/docs.css';
1213
1314
interface Heading {
@@ -121,9 +122,9 @@ const breadcrumbSchema = {
121122
{/* Quick links card */}
122123
<div class="dmp-card dmp-quick">
123124
<a href="/docs/introduction/">Docs</a>
124-
<a href="/docs/architecture/">Architecture</a>
125-
<a href="/docs/modules/identity/">Modules</a>
126-
<a href="/docs/adding-a-feature/">Guides</a>
125+
<a href="/docs/modules/identity/overview/">Modules</a>
126+
<a href="/docs/building-blocks/overview/">Building Blocks</a>
127+
<a href="/docs/deployment/overview/">Deployment</a>
127128
<a href="https://github.com/fullstackhero/dotnet-starter-kit" target="_blank" rel="noopener">GitHub</a>
128129
</div>
129130

@@ -154,11 +155,22 @@ const breadcrumbSchema = {
154155
<>
155156
<div class="dmp-label">{group.label}</div>
156157
<ul class="dmp-nav">
157-
{group.items.map((item) => (
158-
<li>
159-
<a href={`/docs/${item.slug}/`} class:list={[{ active: slug === item.slug }]}>{item.title}</a>
160-
</li>
161-
))}
158+
{group.items.map((item) =>
159+
isSubGroup(item) ? (
160+
<>
161+
<li class="dmp-sublabel">{(item as SidebarSubGroup).label}</li>
162+
{(item as SidebarSubGroup).items.map((sub: SidebarLink) => (
163+
<li>
164+
<a href={`/docs/${sub.slug}/`} class:list={['dmp-sub', { active: slug === sub.slug }]}>{sub.title}</a>
165+
</li>
166+
))}
167+
</>
168+
) : (
169+
<li>
170+
<a href={`/docs/${(item as SidebarLink).slug}/`} class:list={[{ active: slug === (item as SidebarLink).slug }]}>{(item as SidebarLink).title}</a>
171+
</li>
172+
)
173+
)}
162174
</ul>
163175
</>
164176
))}

docs/src/styles/docs.css

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,43 @@
7878
font-weight: 500;
7979
}
8080

81+
/* Sidebar sub-groups (nested menus, e.g. Modules > Identity) */
82+
.sidebar-subgroup {
83+
margin-top: 0.15rem;
84+
}
85+
.sidebar-sublabel {
86+
font-size: 0.72rem;
87+
font-weight: 500;
88+
color: var(--text-dim);
89+
padding: 0.3rem 0.5rem 0.15rem;
90+
letter-spacing: 0.01em;
91+
}
92+
.sidebar-sublinks {
93+
list-style: none;
94+
padding: 0;
95+
margin: 0;
96+
}
97+
.sidebar-sublinks a {
98+
display: block;
99+
font-size: 0.75rem;
100+
font-weight: 400;
101+
color: var(--text-faint);
102+
padding: 0.22rem 0.5rem 0.22rem 1.1rem;
103+
border-left: 2px solid transparent;
104+
margin-left: -1px;
105+
border-radius: 0 4px 4px 0;
106+
transition: color 0.12s, border-color 0.12s, background 0.12s;
107+
}
108+
.sidebar-sublinks a:hover {
109+
color: var(--text);
110+
background: rgba(128,128,128,0.04);
111+
}
112+
.sidebar-sublinks a.active {
113+
color: var(--brand);
114+
border-left-color: var(--brand);
115+
font-weight: 500;
116+
}
117+
81118
/* ── Main Content ────────────────────────────────────────── */
82119

83120
.docs-content {
@@ -628,6 +665,24 @@ body.sidebar-open {
628665
:root[data-theme="dark"] .dmp-nav a.active {
629666
background: rgba(74,222,128,0.06);
630667
}
668+
.dmp-sublabel {
669+
font-size: 0.7rem;
670+
font-weight: 600;
671+
text-transform: uppercase;
672+
letter-spacing: 0.04em;
673+
color: var(--text-faint);
674+
padding: 0.6rem 0.4rem 0.15rem 0.6rem;
675+
list-style: none;
676+
margin-top: 0.15rem;
677+
}
678+
.dmp-sublabel:first-child {
679+
padding-top: 0.2rem;
680+
}
681+
.dmp-nav a.dmp-sub {
682+
padding-left: 1.4rem;
683+
font-size: 0.82rem;
684+
color: var(--text-dim);
685+
}
631686
.dmp-indent {
632687
padding-left: 1.15rem !important;
633688
font-size: 0.8rem !important;

0 commit comments

Comments
 (0)