Add category grouping to sidebar and dashboard
Sidebar now renders uncategorised apps flat and categorised apps in collapsible accordion sections; active category stays expanded. Dashboard groups tiles under category section headings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
15c2064483
commit
affb1368ac
3 changed files with 153 additions and 33 deletions
|
|
@ -7,6 +7,16 @@ import type { User, App } from '../types'
|
|||
export function Dashboard({ user }: { user: User }) {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const uncategorised = user.apps.filter(a => !a.category)
|
||||
const grouped = user.apps.reduce<Record<string, App[]>>((acc, app) => {
|
||||
if (!app.category) return acc
|
||||
if (!acc[app.category]) acc[app.category] = []
|
||||
acc[app.category].push(app)
|
||||
return acc
|
||||
}, {})
|
||||
const categoryNames = Object.keys(grouped).sort()
|
||||
const hasAny = user.apps.length > 0
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100dvh' }}>
|
||||
<Sidebar user={user} />
|
||||
|
|
@ -16,25 +26,48 @@ export function Dashboard({ user }: { user: User }) {
|
|||
<span style={{ color: 'var(--text-dark)', fontWeight: 600 }}>{user.name}</span>
|
||||
</p>
|
||||
|
||||
{user.apps.length === 0 ? (
|
||||
{!hasAny && (
|
||||
<p style={{ color: 'var(--text-mid)', padding: '3rem 0', textAlign: 'center' }}>
|
||||
No apps assigned yet. Contact an administrator.
|
||||
</p>
|
||||
) : (
|
||||
<div style={{
|
||||
display: 'grid', gap: '1rem',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(210px, 1fr))',
|
||||
}}>
|
||||
{user.apps.map(app => (
|
||||
<AppTile key={app.slug} app={app} onOpen={() => navigate(`/app/${app.slug}`)} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Uncategorised apps */}
|
||||
{uncategorised.length > 0 && (
|
||||
<AppGrid apps={uncategorised} onOpen={slug => navigate(`/app/${slug}`)} />
|
||||
)}
|
||||
|
||||
{/* Categorised groups */}
|
||||
{categoryNames.map(cat => (
|
||||
<div key={cat} style={{ marginTop: uncategorised.length > 0 ? '2rem' : 0 }}>
|
||||
<h2 style={{
|
||||
fontSize: '0.72rem', fontWeight: 700, color: 'var(--text-mid)',
|
||||
textTransform: 'uppercase', letterSpacing: '0.1em',
|
||||
marginBottom: '0.875rem',
|
||||
}}>
|
||||
{cat}
|
||||
</h2>
|
||||
<AppGrid apps={grouped[cat]} onOpen={slug => navigate(`/app/${slug}`)} />
|
||||
</div>
|
||||
))}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AppGrid({ apps, onOpen }: { apps: App[]; onOpen: (slug: string) => void }) {
|
||||
return (
|
||||
<div style={{
|
||||
display: 'grid', gap: '1rem',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(210px, 1fr))',
|
||||
}}>
|
||||
{apps.map(app => (
|
||||
<AppTile key={app.slug} app={app} onOpen={() => onOpen(app.slug)} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AppTile({ app, onOpen }: { app: App; onOpen: () => void }) {
|
||||
return (
|
||||
<div style={{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue