Skip to content

Commit 5277506

Browse files
aligneddevaligneddev
andauthored
005 view history page (#18)
* phase 3, failing tests * us3 tests pass * working --------- Co-authored-by: aligneddev <aligneddev@github.com>
1 parent 2dacfc4 commit 5277506

33 files changed

Lines changed: 2676 additions & 10 deletions

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,3 @@ src/.vs/*
6363
.fake
6464
src/BikeTracking.Frontend/test-results/
6565
src/BikeTracking.Frontend/playwright-report/
66-
src/BikeTracking.Frontend/playwright-report/index.html

.specify/scripts/powershell/common.ps1

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ function Test-FeatureBranch {
8484
Write-Output "Feature branches should be named like: 001-feature-name"
8585
return $false
8686
}
87+
88+
# Enforce max 4 words in branch suffix (after the numeric prefix).
89+
$suffix = $Branch -replace '^[0-9]{3}-', ''
90+
$suffixParts = ($suffix -split '-') | Where-Object { $_ }
91+
if ($suffixParts.Count -gt 4) {
92+
Write-Output "ERROR: Feature branch suffix exceeds 4 words. Current branch: $Branch"
93+
Write-Output "Feature branches must use max 4 words after the numeric prefix (e.g., 005-view-history-page)."
94+
return $false
95+
}
96+
8797
return $true
8898
}
8999

.specify/scripts/powershell/create-new-feature.ps1

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@ function ConvertTo-CleanBranchName {
135135

136136
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
137137
}
138+
139+
function Limit-BranchSuffixWords {
140+
param(
141+
[string]$Suffix,
142+
[int]$MaxWords = 4
143+
)
144+
145+
$parts = ($Suffix -split '-') | Where-Object { $_ }
146+
if ($parts.Count -le $MaxWords) {
147+
return ($parts -join '-')
148+
}
149+
150+
return (($parts | Select-Object -First $MaxWords) -join '-')
151+
}
138152
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
139153
if (-not $fallbackRoot) {
140154
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
@@ -212,6 +226,9 @@ if ($ShortName) {
212226
$branchSuffix = Get-BranchName -Description $featureDesc
213227
}
214228

229+
# Enforce a consistent word cap for branch suffixes.
230+
$branchSuffix = Limit-BranchSuffixWords -Suffix $branchSuffix -MaxWords 4
231+
215232
# Determine branch number
216233
if ($Number -eq 0) {
217234
if ($hasGit) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Specification Quality Checklist: Ride History Page
2+
3+
**Purpose**: Validate specification completeness and quality before proceeding to planning
4+
**Created**: 2026-03-26
5+
**Feature**: [spec.md](../spec.md)
6+
7+
## Content Quality
8+
9+
- [x] No implementation details (languages, frameworks, APIs)
10+
- [x] Focused on user value and business needs
11+
- [x] Written for non-technical stakeholders
12+
- [x] All mandatory sections completed
13+
14+
## Requirement Completeness
15+
16+
- [x] No [NEEDS CLARIFICATION] markers remain
17+
- [x] Requirements are testable and unambiguous
18+
- [x] Success criteria are measurable
19+
- [x] Success criteria are technology-agnostic (no implementation details)
20+
- [x] All acceptance scenarios are defined
21+
- [x] Edge cases are identified
22+
- [x] Scope is clearly bounded
23+
- [x] Dependencies and assumptions identified
24+
25+
## Feature Readiness
26+
27+
- [x] All functional requirements have clear acceptance criteria
28+
- [x] User scenarios cover primary flows
29+
- [x] Feature meets measurable outcomes defined in Success Criteria
30+
- [x] No implementation details leak into specification
31+
32+
## Notes
33+
34+
- All checklist items pass. Spec is ready for `/speckit.clarify` or `/speckit.plan`.
35+
- Assumption made: dashboard page already exists as a logged-in shell; this feature adds widgets to it.
36+
- Assumption made: "this month" and "this year" calculated relative to rider's local system date.
37+
- Grid pagination/virtualization approach is left as an implementation decision.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Feature Specification: Ride History Page
2+
3+
**Feature Branch**: `005-view-history-page`
4+
**Created**: 2026-03-26
5+
**Status**: Draft
6+
**Input**: User description: "Create the view history page to see all past miles. Add summaries at the top for miles this month, this year, and overall total with visuals. Show all rides in a TanstackUI grid with filters for date ranges. Show total miles for the date range. Make components so the total overall and year can be shown on the dashboard as well"
7+
8+
## User Scenarios & Testing *(mandatory)*
9+
10+
### User Story 1 - View Ride History with Summary Stats (Priority: P1)
11+
12+
As a logged-in rider, I want to open a history page that shows all my past rides and my overall mileage totals so I can understand my cumulative progress at a glance.
13+
14+
**Why this priority**: Seeing the history and summary stats is the core purpose of this page. Without it the feature provides no value. All other stories build on this foundation.
15+
16+
**Independent Test**: Can be fully tested by navigating to the history page after recording at least one ride and verifying that the page displays summary tiles (this month, this year, all-time) and at least one row in the ride list.
17+
18+
**Acceptance Scenarios**:
19+
20+
1. **Given** the rider is logged in and has recorded rides, **When** they navigate to the history page, **Then** three summary tiles are visible at the top showing miles this month, miles this year, and all-time total miles.
21+
2. **Given** the rider is logged in and has recorded rides, **When** they view the history page, **Then** all rides appear in a data grid showing at minimum ride date and miles.
22+
3. **Given** the rider is logged in and has no rides recorded, **When** they navigate to the history page, **Then** all three summary tiles show zero and the grid displays an empty-state message.
23+
24+
---
25+
26+
### User Story 2 - Filter Rides by Date Range and See Filtered Total (Priority: P2)
27+
28+
As a rider, I want to narrow the ride list to a specific date range and see the total miles for that period so I can track progress over custom time windows (e.g., a training block or vacation).
29+
30+
**Why this priority**: Date range filtering with a running total is a key analytical tool that makes raw history data actionable. It can be developed and tested independently of the dashboard work.
31+
32+
**Independent Test**: Can be fully tested by recording rides on multiple dates, applying a date range filter that includes only some rides, and confirming the grid and total reflect only the rides within the selected range.
33+
34+
**Acceptance Scenarios**:
35+
36+
1. **Given** the rider has rides on various dates, **When** they set a start date and end date filter, **Then** only rides within that inclusive range appear in the grid.
37+
2. **Given** a date range filter is applied, **When** the rider views the filtered grid, **Then** the total miles displayed below or near the grid reflects only the filtered results.
38+
3. **Given** a date range filter is applied that matches no rides, **When** the rider views the grid, **Then** an empty-state message is shown and the filtered total displays zero.
39+
4. **Given** a date range filter is active, **When** the rider clears the filter, **Then** all rides are shown again and the total reflects the full history.
40+
41+
---
42+
43+
### User Story 3 - Dashboard Mileage Widgets (Priority: P3)
44+
45+
As a rider, I want to see my all-time total miles and year-to-date miles on the main dashboard so I can check my key stats without navigating to the full history page.
46+
47+
**Why this priority**: Dashboard visibility of key stats increases daily engagement and gives riders instant motivation on login. It relies on the same data computed for the history page summary tiles and shares the same reusable components.
48+
49+
**Independent Test**: Can be fully tested independently by verifying the dashboard page displays the all-time total and year-to-date mileage widgets, and that the values match those shown on the history summary tiles for the same rider.
50+
51+
**Acceptance Scenarios**:
52+
53+
1. **Given** the rider is logged in and has recorded rides, **When** they view the dashboard, **Then** the all-time total miles and year-to-date miles are visible.
54+
2. **Given** the rider records a new ride, **When** they visit the dashboard, **Then** the displayed totals reflect the newly added miles on next load.
55+
3. **Given** the rider has no rides recorded, **When** they view the dashboard, **Then** the total and year-to-date widgets show zero.
56+
57+
---
58+
59+
### Edge Cases
60+
61+
- What happens when a rider has rides but none in the current month? The "this month" summary tile must show zero.
62+
- What happens when a rider has rides but none in the current year? The "this year" summary tile must show zero.
63+
- How does the grid handle a very large number of rides? The grid must remain navigable (pagination or virtualization).
64+
- What happens if the start date of the filter is after the end date? The system must prevent or handle this invalid range gracefully.
65+
- What happens when ride data includes partial dates near timezone boundaries? Displayed dates should reflect the rider's local time as originally entered.
66+
67+
## Requirements *(mandatory)*
68+
69+
### Functional Requirements
70+
71+
- **FR-001**: System MUST provide a History page accessible only to authenticated riders.
72+
- **FR-002**: System MUST display three summary stat tiles at the top of the History page: miles this month, miles this year, and all-time total miles.
73+
- **FR-003**: Summary stat tiles MUST include a visual element (such as an icon, chart, or graphical indicator) alongside the numeric total.
74+
- **FR-004**: System MUST display all rides for the authenticated rider in a data grid on the History page.
75+
- **FR-005**: The data grid MUST display at minimum: ride date and miles for each ride.
76+
- **FR-006**: The data grid MUST display optional fields (ride duration and temperature) when those values were recorded for a ride.
77+
- **FR-007**: System MUST provide date range filter controls (start date and end date) above the data grid.
78+
- **FR-008**: When a date range filter is applied, the data grid MUST show only rides with dates within the inclusive range.
79+
- **FR-009**: System MUST display a total miles value that reflects only the rides currently visible in the grid (filtered or unfiltered).
80+
- **FR-010**: When no date range filter is active, the data grid MUST show all rides and the total MUST reflect all-time miles.
81+
- **FR-011**: System MUST show an empty-state message in the grid when no rides match the current filter.
82+
- **FR-012**: Summary stat tiles MUST be implemented as reusable components that can be embedded in other pages.
83+
- **FR-013**: The Dashboard page MUST embed the all-time total miles and year-to-date miles components using the reusable stat components from FR-012.
84+
- **FR-014**: Summary stat values MUST be accurate as of the most recent page load; real-time push updates are not required.
85+
86+
### Key Entities
87+
88+
- **Ride**: A single recorded ride event with ride date, miles, optional duration in minutes, and optional temperature. Rides are associated with a specific rider and are immutable once recorded.
89+
- **RideSummary**: Aggregated total miles for a defined period — this month, this year, all-time, or a custom date range. Derived by summing ride miles for the period.
90+
- **DateRangeFilter**: A user-supplied start date and end date used to restrict which rides appear in the grid and which rides contribute to the filtered total.
91+
92+
## Success Criteria *(mandatory)*
93+
94+
### Measurable Outcomes
95+
96+
- **SC-001**: Riders can navigate to the History page and see their complete ride list and summary stats in a single view without additional interactions.
97+
- **SC-002**: All three summary tiles (this month, this year, all-time) are visible above the fold on standard laptop screen sizes without scrolling.
98+
- **SC-003**: Riders can apply a date range filter and have the grid and total update without a full page reload.
99+
- **SC-004**: The filtered total always matches the sum of miles in the currently visible grid rows.
100+
- **SC-005**: Dashboard mileage widgets display values consistent with the History page summary tiles for the same rider.
101+
- **SC-006**: Riders with no recorded rides see zeros rather than errors or blank spaces for all stats.
102+
103+
## Assumptions
104+
105+
- Ride dates are stored and displayed using the local time value entered by the rider (as established in feature 004).
106+
- "This month" and "this year" are calculated relative to the rider's system locale/date, consistent with how ride date/time input works.
107+
- The dashboard page already exists (or will exist as part of the logged-in shell); this feature adds widgets to it rather than creating a new dashboard from scratch.
108+
- Grid pagination or virtualization is acceptable to handle large ride counts; infinite scroll or traditional pagination are both valid approaches.
109+
- Visual indicators on summary tiles do not require animated or real-time charts; static icons or simple graphical displays satisfy the requirement.
110+
111+
## Out of Scope
112+
113+
- Editing or deleting recorded rides (read-only history view).
114+
- Exporting ride data to CSV or other formats.
115+
- Sharing ride history with other users.
116+
- Real-time push updates when a new ride is recorded in another browser tab.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
openapi: 3.0.3
2+
info:
3+
title: BikeTracking Ride History API
4+
version: 1.0.0
5+
description: Authenticated ride history query with summaries and date-range filtering.
6+
7+
paths:
8+
/api/rides/history:
9+
get:
10+
summary: Get ride history rows with summary totals for the authenticated rider
11+
operationId: getRideHistory
12+
parameters:
13+
- in: query
14+
name: from
15+
schema:
16+
type: string
17+
format: date
18+
required: false
19+
description: Inclusive start date filter in rider-local date semantics.
20+
- in: query
21+
name: to
22+
schema:
23+
type: string
24+
format: date
25+
required: false
26+
description: Inclusive end date filter in rider-local date semantics.
27+
- in: query
28+
name: page
29+
schema:
30+
type: integer
31+
minimum: 1
32+
default: 1
33+
required: false
34+
- in: query
35+
name: pageSize
36+
schema:
37+
type: integer
38+
minimum: 1
39+
maximum: 200
40+
default: 25
41+
required: false
42+
responses:
43+
'200':
44+
description: History and summaries resolved.
45+
content:
46+
application/json:
47+
schema:
48+
$ref: '#/components/schemas/RideHistoryResponse'
49+
'400':
50+
description: Invalid query parameters (e.g., from > to)
51+
content:
52+
application/json:
53+
schema:
54+
$ref: '#/components/schemas/ErrorResponse'
55+
'401':
56+
description: Unauthorized
57+
content:
58+
application/json:
59+
schema:
60+
$ref: '#/components/schemas/ErrorResponse'
61+
62+
components:
63+
schemas:
64+
MileageSummary:
65+
type: object
66+
required:
67+
- miles
68+
- rideCount
69+
- period
70+
properties:
71+
miles:
72+
type: number
73+
minimum: 0
74+
rideCount:
75+
type: integer
76+
minimum: 0
77+
period:
78+
type: string
79+
enum:
80+
- thisMonth
81+
- thisYear
82+
- allTime
83+
- filtered
84+
description: The time period this summary represents.
85+
86+
RideHistoryRow:
87+
type: object
88+
required:
89+
- rideId
90+
- rideDateTimeLocal
91+
- miles
92+
properties:
93+
rideId:
94+
type: integer
95+
format: int64
96+
rideDateTimeLocal:
97+
type: string
98+
format: date-time
99+
miles:
100+
type: number
101+
exclusiveMinimum: 0
102+
rideMinutes:
103+
type: integer
104+
nullable: true
105+
minimum: 1
106+
temperature:
107+
type: number
108+
nullable: true
109+
110+
RideHistoryResponse:
111+
type: object
112+
required:
113+
- summaries
114+
- filteredTotal
115+
- rides
116+
- page
117+
- pageSize
118+
- totalRows
119+
properties:
120+
summaries:
121+
type: object
122+
required:
123+
- thisMonth
124+
- thisYear
125+
- allTime
126+
properties:
127+
thisMonth:
128+
$ref: '#/components/schemas/MileageSummary'
129+
thisYear:
130+
$ref: '#/components/schemas/MileageSummary'
131+
allTime:
132+
$ref: '#/components/schemas/MileageSummary'
133+
filteredTotal:
134+
$ref: '#/components/schemas/MileageSummary'
135+
rides:
136+
type: array
137+
items:
138+
$ref: '#/components/schemas/RideHistoryRow'
139+
page:
140+
type: integer
141+
minimum: 1
142+
pageSize:
143+
type: integer
144+
minimum: 1
145+
maximum: 200
146+
totalRows:
147+
type: integer
148+
minimum: 0
149+
150+
ErrorResponse:
151+
type: object
152+
required:
153+
- code
154+
- message
155+
properties:
156+
code:
157+
type: string
158+
message:
159+
type: string
160+
details:
161+
type: array
162+
items:
163+
type: string

0 commit comments

Comments
 (0)