- Update decision-log with UI layout decisions (Feb 18, 2026) - Update architecture with frontend layout patterns - Update config.yaml with TDD, documentation, UI standards rules - Create p00-api-documentation change (Scribe annotations) - Create p01-ui-foundation change (types, stores, Lucide) - Create p02-app-layout change (AppLayout, Sidebar, TopBar) - Create p03-dashboard-enhancement change (PageHeader, StatCard) - Create p04-content-patterns change (DataTable, FilterBar) - Create p05-page-migrations change (page migrations) - Fix E2E auth tests (11/11 passing) - Add JWT expiry validation to dashboard guard
237 lines
5.7 KiB
Markdown
237 lines
5.7 KiB
Markdown
# Design: Dashboard Enhancement
|
|
|
|
## PageHeader Component
|
|
|
|
### `src/lib/components/layout/PageHeader.svelte`
|
|
```svelte
|
|
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
|
|
interface Props {
|
|
title: string;
|
|
description?: string;
|
|
children?: Snippet; // Action buttons
|
|
}
|
|
|
|
let { title, description, children }: Props = $props();
|
|
</script>
|
|
|
|
<div class="page-header mb-6">
|
|
<div class="flex items-start justify-between gap-4">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-base-content">{title}</h1>
|
|
{#if description}
|
|
<p class="text-base-content/70 mt-1">{description}</p>
|
|
{/if}
|
|
</div>
|
|
{#if children}
|
|
<div class="flex items-center gap-2">
|
|
{@render children()}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## StatCard Component
|
|
|
|
### `src/lib/components/common/StatCard.svelte`
|
|
```svelte
|
|
<script lang="ts">
|
|
import type { Component } from 'svelte';
|
|
import { TrendingUp, TrendingDown, Minus } from 'lucide-svelte';
|
|
|
|
interface Props {
|
|
title: string;
|
|
value: string | number;
|
|
description?: string;
|
|
trend?: 'up' | 'down' | 'neutral';
|
|
trendValue?: string;
|
|
icon?: Component;
|
|
}
|
|
|
|
let { title, value, description, trend = 'neutral', trendValue, icon: Icon }: Props = $props();
|
|
|
|
$derived trendColor = {
|
|
up: 'text-success',
|
|
down: 'text-error',
|
|
neutral: 'text-base-content/50'
|
|
}[trend];
|
|
|
|
$derived TrendIcon = {
|
|
up: TrendingUp,
|
|
down: TrendingDown,
|
|
neutral: Minus
|
|
}[trend];
|
|
</script>
|
|
|
|
<div class="stat-card card bg-base-100 shadow-sm border border-base-300">
|
|
<div class="card-body p-4">
|
|
<div class="flex items-start justify-between">
|
|
<div class="stat-title text-sm text-base-content/70">{title}</div>
|
|
{#if Icon}
|
|
<div class="text-base-content/50">
|
|
<Icon size={20} />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
<div class="stat-value text-3xl font-bold mt-1">{value}</div>
|
|
<div class="stat-desc flex items-center gap-1 mt-1">
|
|
{#if trendValue}
|
|
<span class={trendColor}>
|
|
<TrendIcon size={14} />
|
|
</span>
|
|
<span class={trendColor}>{trendValue}</span>
|
|
{/if}
|
|
{#if description}
|
|
<span class="text-base-content/50">{description}</span>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Enhanced Dashboard
|
|
|
|
### `src/routes/dashboard/+page.svelte`
|
|
```svelte
|
|
<script lang="ts">
|
|
import PageHeader from '$lib/components/layout/PageHeader.svelte';
|
|
import StatCard from '$lib/components/common/StatCard.svelte';
|
|
import { authStore } from '$lib/stores/auth';
|
|
import { periodStore } from '$lib/stores/period';
|
|
import {
|
|
Folder,
|
|
Users,
|
|
Calendar,
|
|
BarChart3,
|
|
Plus,
|
|
ArrowRight
|
|
} from 'lucide-svelte';
|
|
|
|
// TODO: Fetch from API in future
|
|
const stats = {
|
|
activeProjects: 14,
|
|
teamMembers: 8,
|
|
allocationsThisMonth: 186,
|
|
avgUtilization: 87
|
|
};
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Dashboard | Headroom</title>
|
|
</svelte:head>
|
|
|
|
<PageHeader
|
|
title="Dashboard"
|
|
description="Overview of your resource allocation"
|
|
>
|
|
<button slot="actions" class="btn btn-primary btn-sm gap-2">
|
|
<Plus size={16} />
|
|
New Allocation
|
|
</button>
|
|
</PageHeader>
|
|
|
|
<!-- KPI Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
<StatCard
|
|
title="Active Projects"
|
|
value={stats.activeProjects}
|
|
trend="up"
|
|
trendValue="+2"
|
|
description="from last month"
|
|
icon={Folder}
|
|
/>
|
|
<StatCard
|
|
title="Team Members"
|
|
value={stats.teamMembers}
|
|
trend="neutral"
|
|
description="active"
|
|
icon={Users}
|
|
/>
|
|
<StatCard
|
|
title="Allocations (hrs)"
|
|
value={stats.allocationsThisMonth}
|
|
trend="down"
|
|
trendValue="-12"
|
|
description="vs capacity"
|
|
icon={Calendar}
|
|
/>
|
|
<StatCard
|
|
title="Avg Utilization"
|
|
value="{stats.avgUtilization}%"
|
|
trend="up"
|
|
trendValue="+5%"
|
|
description="from last month"
|
|
icon={BarChart3}
|
|
/>
|
|
</div>
|
|
|
|
<!-- Quick Links -->
|
|
<div class="card bg-base-100 shadow-sm border border-base-300 mb-6">
|
|
<div class="card-body">
|
|
<h2 class="card-title text-lg">Quick Actions</h2>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mt-2">
|
|
<a href="/team-members" class="btn btn-ghost justify-start gap-2">
|
|
<Users size={18} />
|
|
Team
|
|
<ArrowRight size={14} class="ml-auto opacity-50" />
|
|
</a>
|
|
<a href="/projects" class="btn btn-ghost justify-start gap-2">
|
|
<Folder size={18} />
|
|
Projects
|
|
<ArrowRight size={14} class="ml-auto opacity-50" />
|
|
</a>
|
|
<a href="/allocations" class="btn btn-ghost justify-start gap-2">
|
|
<Calendar size={18} />
|
|
Allocate
|
|
<ArrowRight size={14} class="ml-auto opacity-50" />
|
|
</a>
|
|
<a href="/reports/forecast" class="btn btn-ghost justify-start gap-2">
|
|
<BarChart3 size={18} />
|
|
Forecast
|
|
<ArrowRight size={14} class="ml-auto opacity-50" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Placeholder for Allocation Preview -->
|
|
<div class="card bg-base-100 shadow-sm border border-base-300">
|
|
<div class="card-body">
|
|
<h2 class="card-title text-lg">Allocation Preview</h2>
|
|
<p class="text-base-content/50 text-sm">
|
|
Allocation matrix for {$periodStore.selectedPeriod} will appear here.
|
|
</p>
|
|
<div class="skeleton h-48 w-full mt-4"></div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Login Page Polish
|
|
|
|
### Updates to `src/routes/login/+page.svelte`
|
|
- Center vertically using flexbox
|
|
- Add app logo/branding above form
|
|
- Consistent card styling with new layout
|
|
- Better error/success states
|
|
|
|
## File Structure
|
|
```
|
|
src/lib/components/
|
|
├── layout/
|
|
│ └── PageHeader.svelte # NEW
|
|
└── common/
|
|
└── StatCard.svelte # NEW
|
|
|
|
src/routes/
|
|
├── login/+page.svelte # UPDATE
|
|
└── dashboard/+page.svelte # UPDATE
|
|
```
|