docs(ui): Add UI layout refactor plan and OpenSpec changes

- 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
This commit is contained in:
2026-02-18 13:03:08 -05:00
parent de2d83092e
commit b9cb5170da
29 changed files with 3341 additions and 59 deletions

View File

@@ -0,0 +1,236 @@
# 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
```

View File

@@ -0,0 +1,60 @@
# Proposal: Dashboard Enhancement
## Overview
Transform the dashboard page into a data-rich overview with KPI stat cards and allocation preview, using the new layout components.
## Goals
- Create PageHeader component for consistent page titles
- Create StatCard component for KPI display
- Enhance dashboard with team/project/ utilization KPIs
- Add quick links to common actions
- Polish login page for consistency
## Non-Goals
- Allocation matrix component (done in p04)
- Other page implementations (done in p05)
## Priority
**MEDIUM** - First "real" page using new layout
## Scope
### PageHeader Component
- Page title
- Optional description
- Action buttons slot
- Consistent styling
### StatCard Component
- Value display
- Label
- Trend indicator (up/down)
- Icon support
- DaisyUI stat styling
### Dashboard Enhancement
- Row of 4 stat cards (Active Projects, Team Members, Current Month Allocations, Avg Utilization)
- Quick actions section
- Recent activity placeholder
### Login Polish
- Center card vertically
- Add app logo/branding
- Improve form styling
## Success Criteria
- [ ] PageHeader component created
- [ ] StatCard component created
- [ ] Dashboard shows 4 KPI cards
- [ ] Login page polished
- [ ] All tests pass
## Estimated Effort
2-3 hours
## Dependencies
- p02-app-layout
## Blocks
- p04-content-patterns (can start in parallel)
- p05-page-migrations

View File

@@ -0,0 +1,63 @@
# Tasks: Dashboard Enhancement
## Phase 1: PageHeader Component
- [ ] 3.1 Create `src/lib/components/layout/PageHeader.svelte`
- [ ] 3.2 Add title prop (required)
- [ ] 3.3 Add description prop (optional)
- [ ] 3.4 Add children snippet for action buttons
- [ ] 3.5 Style with Tailwind/DaisyUI
- [ ] 3.6 Write component test: renders title
- [ ] 3.7 Write component test: renders description
- [ ] 3.8 Write component test: renders action buttons
## Phase 2: StatCard Component
- [ ] 3.9 Create `src/lib/components/common/` directory
- [ ] 3.10 Create `StatCard.svelte`
- [ ] 3.11 Add title, value props
- [ ] 3.12 Add description prop (optional)
- [ ] 3.13 Add trend prop ('up' | 'down' | 'neutral')
- [ ] 3.14 Add trendValue prop (optional)
- [ ] 3.15 Add icon prop (Lucide component)
- [ ] 3.16 Style trend indicators with colors
- [ ] 3.17 Style with DaisyUI card
- [ ] 3.18 Write component test: renders value
- [ ] 3.19 Write component test: trend colors correct
- [ ] 3.20 Write component test: icon renders
## Phase 3: Dashboard Enhancement
- [ ] 3.21 Update `src/routes/dashboard/+page.svelte`
- [ ] 3.22 Add svelte:head with title
- [ ] 3.23 Add PageHeader component
- [ ] 3.24 Add "New Allocation" button in header
- [ ] 3.25 Add grid of 4 StatCards
- [ ] 3.26 Add Quick Actions card
- [ ] 3.27 Add Allocation Preview placeholder
- [ ] 3.28 Use periodStore for display
- [ ] 3.29 Write E2E test: dashboard renders correctly
## Phase 4: Login Polish
- [ ] 3.30 Update `src/routes/login/+page.svelte`
- [ ] 3.31 Center card vertically in viewport
- [ ] 3.32 Add app branding/logo
- [ ] 3.33 Improve form styling consistency
- [ ] 3.34 Write E2E test: login page centered
## Phase 5: Verification
- [ ] 3.35 Run `npm run check` - no type errors
- [ ] 3.36 Run `npm run test:unit` - all tests pass
- [ ] 3.37 Run `npm run test:e2e` - all E2E tests pass
- [ ] 3.38 Manual test: Dashboard looks correct
- [ ] 3.39 Manual test: Login page looks correct
## Commits
1. `feat(ui): Create PageHeader component`
2. `feat(ui): Create StatCard component with trend indicators`
3. `feat(dashboard): Enhance dashboard with KPI cards and quick actions`
4. `feat(login): Polish login page styling`
5. `test(ui): Add tests for PageHeader and StatCard`