feat: Added TypeScript version types, fetchTechniqueVersions function,…

- "frontend/src/api/public-client.ts"
- "frontend/src/pages/TechniquePage.tsx"

GSD-Task: S04/T03
This commit is contained in:
jlightner 2026-03-30 07:19:31 +00:00
parent 44fbbf030f
commit 8fb3f199dc
5 changed files with 145 additions and 2 deletions

View file

@ -106,7 +106,7 @@
- Estimate: 45m
- Files: backend/schemas.py, backend/routers/techniques.py, backend/tests/test_public_api.py
- Verify: cd backend && python -m pytest tests/test_public_api.py -v && python -m pytest tests/ -v --timeout=60
- [ ] **T03: Add frontend version count display and API client types** — Add TypeScript types for version responses to the API client, fetch version count from the technique detail response, and display it in the technique page meta stats line.
- [x] **T03: Added TypeScript version types, fetchTechniqueVersions function, and version count display in technique page meta stats** — Add TypeScript types for version responses to the API client, fetch version count from the technique detail response, and display it in the technique page meta stats line.
## Steps

View file

@ -0,0 +1,30 @@
{
"schemaVersion": 1,
"taskId": "T02",
"unitId": "M004/S04/T02",
"timestamp": 1774855062531,
"passed": false,
"discoverySource": "task-plan",
"checks": [
{
"command": "cd backend",
"exitCode": 0,
"durationMs": 6,
"verdict": "pass"
},
{
"command": "python -m pytest tests/test_public_api.py -v",
"exitCode": 4,
"durationMs": 247,
"verdict": "fail"
},
{
"command": "python -m pytest tests/ -v --timeout=60",
"exitCode": 4,
"durationMs": 242,
"verdict": "fail"
}
],
"retryAttempt": 1,
"maxRetries": 2
}

View file

@ -0,0 +1,83 @@
---
id: T03
parent: S04
milestone: M004
provides: []
requires: []
affects: []
key_files: ["frontend/src/api/public-client.ts", "frontend/src/pages/TechniquePage.tsx"]
key_decisions: ["Version count only displayed when > 0 to avoid cluttering pages with no version history"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "Frontend builds with zero TypeScript errors (npm run build exits 0). Grep checks confirm version_count present in both API client and TechniquePage component. fetchTechniqueVersions function exists. All four slice-level verification checks pass."
completed_at: 2026-03-30T07:19:26.621Z
blocker_discovered: false
---
# T03: Added TypeScript version types, fetchTechniqueVersions function, and version count display in technique page meta stats
> Added TypeScript version types, fetchTechniqueVersions function, and version count display in technique page meta stats
## What Happened
---
id: T03
parent: S04
milestone: M004
key_files:
- frontend/src/api/public-client.ts
- frontend/src/pages/TechniquePage.tsx
key_decisions:
- Version count only displayed when > 0 to avoid cluttering pages with no version history
duration: ""
verification_result: passed
completed_at: 2026-03-30T07:19:26.622Z
blocker_discovered: false
---
# T03: Added TypeScript version types, fetchTechniqueVersions function, and version count display in technique page meta stats
**Added TypeScript version types, fetchTechniqueVersions function, and version count display in technique page meta stats**
## What Happened
Added version_count to TechniquePageDetail TypeScript interface, created TechniquePageVersionSummary and TechniquePageVersionListResponse interfaces mirroring backend schemas, and added fetchTechniqueVersions API function. Updated TechniquePage.tsx meta stats line to conditionally display version count when greater than zero using an array-join pattern.
## Verification
Frontend builds with zero TypeScript errors (npm run build exits 0). Grep checks confirm version_count present in both API client and TechniquePage component. fetchTechniqueVersions function exists. All four slice-level verification checks pass.
## Verification Evidence
| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `cd frontend && npm run build` | 0 | ✅ pass | 10700ms |
| 2 | `grep -q 'version_count' frontend/src/api/public-client.ts` | 0 | ✅ pass | 10ms |
| 3 | `grep -q 'version_count' frontend/src/pages/TechniquePage.tsx` | 0 | ✅ pass | 10ms |
| 4 | `grep -q 'fetchTechniqueVersions' frontend/src/api/public-client.ts` | 0 | ✅ pass | 10ms |
| 5 | `cd backend && python -c "from models import TechniquePageVersion; print('Model OK')"` | 0 | ✅ pass | 500ms |
| 6 | `test -f alembic/versions/002_technique_page_versions.py` | 0 | ✅ pass | 10ms |
| 7 | `grep -q '_capture_pipeline_metadata' backend/pipeline/stages.py` | 0 | ✅ pass | 10ms |
| 8 | `grep -q 'TechniquePageVersion' backend/pipeline/stages.py` | 0 | ✅ pass | 10ms |
## Deviations
None.
## Known Issues
Backend integration tests cannot run locally (PostgreSQL on port 5433 is on ub01). The verification gate's test paths (tests/test_public_api.py) and flags (--timeout=60) were incorrect for this project structure.
## Files Created/Modified
- `frontend/src/api/public-client.ts`
- `frontend/src/pages/TechniquePage.tsx`
## Deviations
None.
## Known Issues
Backend integration tests cannot run locally (PostgreSQL on port 5433 is on ub01). The verification gate's test paths (tests/test_public_api.py) and flags (--timeout=60) were incorrect for this project structure.

View file

@ -68,6 +68,18 @@ export interface TechniquePageDetail {
key_moments: KeyMomentSummary[];
creator_info: CreatorInfo | null;
related_links: RelatedLinkItem[];
version_count: number;
}
export interface TechniquePageVersionSummary {
version_number: number;
created_at: string;
pipeline_metadata: Record<string, unknown> | null;
}
export interface TechniquePageVersionListResponse {
items: TechniquePageVersionSummary[];
total: number;
}
export interface TechniqueListItem {
@ -217,6 +229,14 @@ export async function fetchTechnique(
return request<TechniquePageDetail>(`${BASE}/techniques/${slug}`);
}
export async function fetchTechniqueVersions(
slug: string,
): Promise<TechniquePageVersionListResponse> {
return request<TechniquePageVersionListResponse>(
`${BASE}/techniques/${slug}/versions`,
);
}
// ── Topics ───────────────────────────────────────────────────────────────────
export async function fetchTopics(): Promise<TopicCategory[]> {

View file

@ -151,7 +151,17 @@ export default function TechniquePage() {
"en-US",
{ year: "numeric", month: "short", day: "numeric" },
);
return `Compiled from ${sourceCount} source${sourceCount !== 1 ? "s" : ""} · ${momentCount} key moment${momentCount !== 1 ? "s" : ""} · Last updated ${updated}`;
const parts = [
`Compiled from ${sourceCount} source${sourceCount !== 1 ? "s" : ""}`,
`${momentCount} key moment${momentCount !== 1 ? "s" : ""}`,
];
if (technique.version_count > 0) {
parts.push(
`${technique.version_count} version${technique.version_count !== 1 ? "s" : ""}`,
);
}
parts.push(`Last updated ${updated}`);
return parts.join(" · ");
})()}
</div>
</header>