Skip to content

Commit 45531b3

Browse files
author
aligneddev
committed
dashboard stats
1 parent d3bca05 commit 45531b3

50 files changed

Lines changed: 3631 additions & 226 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

specs/012-dashboard-stats/tasks.md

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
**Purpose**: Add the dashboard-specific frontend dependency and prepare the shared visualization foundation.
1313

14-
- [ ] T001 Add the `recharts` dependency in `src/BikeTracking.Frontend/package.json` and `src/BikeTracking.Frontend/package-lock.json`
15-
- [ ] T002 Create the shared ShadCN-style chart wrapper in `src/BikeTracking.Frontend/src/components/ui/chart.tsx`
14+
- [x] T001 Add the `recharts` dependency in `src/BikeTracking.Frontend/package.json` and `src/BikeTracking.Frontend/package-lock.json`
15+
- [x] T002 Create the shared ShadCN-style chart wrapper in `src/BikeTracking.Frontend/src/components/ui/chart.tsx`
1616

1717
---
1818

@@ -22,13 +22,13 @@
2222

2323
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
2424

25-
- [ ] T003 Create the dashboard API contracts in `src/BikeTracking.Api/Contracts/DashboardContracts.cs`
26-
- [ ] T004 Extend dashboard preference fields in `src/BikeTracking.Api/Contracts/UsersContracts.cs`
27-
- [ ] T005 [P] Add ride snapshot fields in `src/BikeTracking.Api/Infrastructure/Persistence/Entities/RideEntity.cs`
28-
- [ ] T006 [P] Add dashboard preference fields in `src/BikeTracking.Api/Infrastructure/Persistence/Entities/UserSettingsEntity.cs`
29-
- [ ] T007 Configure snapshot and preference persistence in `src/BikeTracking.Api/Infrastructure/Persistence/BikeTrackingDbContext.cs`
30-
- [ ] T008 Create the EF Core migration in `src/BikeTracking.Api/Infrastructure/Persistence/Migrations/`
31-
- [ ] T009 Add migration coverage for the new migration in `src/BikeTracking.Api.Tests/Infrastructure/MigrationTestCoveragePolicyTests.cs`
25+
- [x] T003 Create the dashboard API contracts in `src/BikeTracking.Api/Contracts/DashboardContracts.cs`
26+
- [x] T004 Extend dashboard preference fields in `src/BikeTracking.Api/Contracts/UsersContracts.cs`
27+
- [x] T005 [P] Add ride snapshot fields in `src/BikeTracking.Api/Infrastructure/Persistence/Entities/RideEntity.cs`
28+
- [x] T006 [P] Add dashboard preference fields in `src/BikeTracking.Api/Infrastructure/Persistence/Entities/UserSettingsEntity.cs`
29+
- [x] T007 Configure snapshot and preference persistence in `src/BikeTracking.Api/Infrastructure/Persistence/BikeTrackingDbContext.cs`
30+
- [x] T008 Create the EF Core migration in `src/BikeTracking.Api/Infrastructure/Persistence/Migrations/`
31+
- [x] T009 Add migration coverage for the new migration in `src/BikeTracking.Api.Tests/Infrastructure/MigrationTestCoveragePolicyTests.cs`
3232

3333
**Checkpoint**: Foundation ready. Core dashboard, historical accuracy, and optional metric work can now proceed.
3434

@@ -44,26 +44,26 @@
4444

4545
> **NOTE**: Write these tests first, ensure they fail, and get user confirmation before implementation.
4646
47-
- [ ] T010 [US1] Add dashboard aggregation unit tests in `src/BikeTracking.Api.Tests/Application/Dashboard/GetDashboardServiceTests.cs`
48-
- [ ] T011 [US1] Add dashboard endpoint tests in `src/BikeTracking.Api.Tests/Endpoints/DashboardEndpointsTests.cs`
49-
- [ ] T012 [US1] Add dashboard API client tests in `src/BikeTracking.Frontend/src/services/dashboard-api.test.ts`
50-
- [ ] T013 [US1] Add dashboard page rendering tests in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.test.tsx`
51-
- [ ] T014 [US1] Add dashboard landing and totals E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
47+
- [x] T010 [US1] Add dashboard aggregation unit tests in `src/BikeTracking.Api.Tests/Application/Dashboard/GetDashboardServiceTests.cs`
48+
- [x] T011 [US1] Add dashboard endpoint tests in `src/BikeTracking.Api.Tests/Endpoints/DashboardEndpointsTests.cs`
49+
- [x] T012 [US1] Add dashboard API client tests in `src/BikeTracking.Frontend/src/services/dashboard-api.test.ts`
50+
- [x] T013 [US1] Add dashboard page rendering tests in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.test.tsx`
51+
- [x] T014 [US1] Add dashboard landing and totals E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
5252

5353
### Implementation for User Story 1
5454

55-
- [ ] T015 [US1] Implement dashboard aggregation logic in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
56-
- [ ] T016 [US1] Implement the dashboard endpoint in `src/BikeTracking.Api/Endpoints/DashboardEndpoints.cs`
57-
- [ ] T017 [US1] Register dashboard services and endpoint mapping in `src/BikeTracking.Api/Program.cs`
58-
- [ ] T018 [US1] Implement the dashboard API client in `src/BikeTracking.Frontend/src/services/dashboard-api.ts`
59-
- [ ] T019 [US1] Create dashboard summary card and metric components in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-summary-card.tsx`
60-
- [ ] T020 [US1] Create dashboard chart section components in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-chart-section.tsx`
61-
- [ ] T021 [US1] Create dashboard empty and partial-data callouts in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-status-panel.tsx`
62-
- [ ] T022 [US1] Build the dashboard page layout in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.tsx`
63-
- [ ] T023 [US1] Implement dashboard styling in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.css`
64-
- [ ] T024 [US1] Make the dashboard the authenticated main page in `src/BikeTracking.Frontend/src/App.tsx`
65-
- [ ] T025 [US1] Update the main navigation links for the new dashboard route in `src/BikeTracking.Frontend/src/components/app-header/app-header.tsx`
66-
- [ ] T026 [US1] Replace the legacy miles page with a redirect shell in `src/BikeTracking.Frontend/src/pages/miles/miles-shell-page.tsx`
55+
- [x] T015 [US1] Implement dashboard aggregation logic in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
56+
- [x] T016 [US1] Implement the dashboard endpoint in `src/BikeTracking.Api/Endpoints/DashboardEndpoints.cs`
57+
- [x] T017 [US1] Register dashboard services and endpoint mapping in `src/BikeTracking.Api/Program.cs`
58+
- [x] T018 [US1] Implement the dashboard API client in `src/BikeTracking.Frontend/src/services/dashboard-api.ts`
59+
- [x] T019 [US1] Create dashboard summary card and metric components in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-summary-card.tsx`
60+
- [x] T020 [US1] Create dashboard chart section components in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-chart-section.tsx`
61+
- [x] T021 [US1] Create dashboard empty and partial-data callouts in `src/BikeTracking.Frontend/src/components/dashboard/dashboard-status-panel.tsx`
62+
- [x] T022 [US1] Build the dashboard page layout in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.tsx`
63+
- [x] T023 [US1] Implement dashboard styling in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.css`
64+
- [x] T024 [US1] Make the dashboard the authenticated main page in `src/BikeTracking.Frontend/src/App.tsx`
65+
- [x] T025 [US1] Update the main navigation links for the new dashboard route in `src/BikeTracking.Frontend/src/components/app-header/app-header.tsx`
66+
- [x] T026 [US1] Replace the legacy miles page with a redirect shell in `src/BikeTracking.Frontend/src/pages/miles/miles-shell-page.tsx`
6767

6868
**Checkpoint**: User Story 1 is independently functional when an authenticated rider lands on the dashboard and sees baseline cards, averages, and charts without using ride history.
6969

@@ -77,17 +77,17 @@
7777

7878
### Tests for User Story 2 ⚠️
7979

80-
- [ ] T027 [US2] Extend ride write-service tests for snapshot capture in `src/BikeTracking.Api.Tests/Application/RidesApplicationServiceTests.cs`
81-
- [ ] T028 [US2] Add snapshot persistence coverage in `src/BikeTracking.Api.Tests/Infrastructure/RidesPersistenceTests.cs`
82-
- [ ] T029 [US2] Add historical-savings stability E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
80+
- [x] T027 [US2] Extend ride write-service tests for snapshot capture in `src/BikeTracking.Api.Tests/Application/RidesApplicationServiceTests.cs`
81+
- [x] T028 [US2] Add snapshot persistence coverage in `src/BikeTracking.Api.Tests/Infrastructure/RidesPersistenceTests.cs`
82+
- [x] T029 [US2] Add historical-savings stability E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
8383

8484
### Implementation for User Story 2
8585

86-
- [ ] T030 [US2] Add snapshot fields to recorded ride events in `src/BikeTracking.Api/Application/Events/RideRecordedEventPayload.cs`
87-
- [ ] T031 [US2] Add snapshot fields to edited ride events in `src/BikeTracking.Api/Application/Events/RideEditedEventPayload.cs`
88-
- [ ] T032 [US2] Capture calculation snapshots during ride creation in `src/BikeTracking.Api/Application/Rides/RecordRideService.cs`
89-
- [ ] T033 [US2] Refresh calculation snapshots during ride edits in `src/BikeTracking.Api/Application/Rides/EditRideService.cs`
90-
- [ ] T034 [US2] Apply snapshot-first and legacy fallback rules in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
86+
- [x] T030 [US2] Add snapshot fields to recorded ride events in `src/BikeTracking.Api/Application/Events/RideRecordedEventPayload.cs`
87+
- [x] T031 [US2] Add snapshot fields to edited ride events in `src/BikeTracking.Api/Application/Events/RideEditedEventPayload.cs`
88+
- [x] T032 [US2] Capture calculation snapshots during ride creation in `src/BikeTracking.Api/Application/Rides/RecordRideService.cs`
89+
- [x] T033 [US2] Refresh calculation snapshots during ride edits in `src/BikeTracking.Api/Application/Rides/EditRideService.cs`
90+
- [x] T034 [US2] Apply snapshot-first and legacy fallback rules in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
9191

9292
**Checkpoint**: User Story 2 is independently functional when changing user settings no longer rewrites prior ride savings in the dashboard.
9393

@@ -101,18 +101,18 @@
101101

102102
### Tests for User Story 3 ⚠️
103103

104-
- [ ] T035 [US3] Extend user settings service tests for dashboard approvals in `src/BikeTracking.Api.Tests/Application/Users/UserSettingsServiceTests.cs`
105-
- [ ] T036 [US3] Extend user settings endpoint tests for dashboard approvals in `src/BikeTracking.Api.Tests/Endpoints/UsersEndpointsTests.cs`
106-
- [ ] T037 [US3] Extend settings page approval tests in `src/BikeTracking.Frontend/src/pages/settings/SettingsPage.test.tsx`
107-
- [ ] T038 [US3] Add optional-metric approval E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
104+
- [x] T035 [US3] Extend user settings service tests for dashboard approvals in `src/BikeTracking.Api.Tests/Application/Users/UserSettingsServiceTests.cs`
105+
- [x] T036 [US3] Extend user settings endpoint tests for dashboard approvals in `src/BikeTracking.Api.Tests/Endpoints/UsersEndpointsTests.cs`
106+
- [x] T037 [US3] Extend settings page approval tests in `src/BikeTracking.Frontend/src/pages/settings/SettingsPage.test.tsx`
107+
- [x] T038 [US3] Add optional-metric approval E2E coverage in `src/BikeTracking.Frontend/tests/e2e/dashboard.spec.ts`
108108

109109
### Implementation for User Story 3
110110

111-
- [ ] T039 [US3] Persist dashboard approval fields in `src/BikeTracking.Api/Application/Users/UserSettingsService.cs`
112-
- [ ] T040 [US3] Accept and return dashboard approval fields in `src/BikeTracking.Api/Endpoints/UsersEndpoints.cs`
113-
- [ ] T041 [US3] Extend frontend user settings DTOs in `src/BikeTracking.Frontend/src/services/users-api.ts`
114-
- [ ] T042 [US3] Add gallons avoided and goal progress approval controls in `src/BikeTracking.Frontend/src/pages/settings/SettingsPage.tsx`
115-
- [ ] T043 [US3] Render optional metric suggestions and approved metrics in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.tsx`
111+
- [x] T039 [US3] Persist dashboard approval fields in `src/BikeTracking.Api/Application/Users/UserSettingsService.cs`
112+
- [x] T040 [US3] Accept and return dashboard approval fields in `src/BikeTracking.Api/Endpoints/UsersEndpoints.cs`
113+
- [x] T041 [US3] Extend frontend user settings DTOs in `src/BikeTracking.Frontend/src/services/users-api.ts`
114+
- [x] T042 [US3] Add gallons avoided and goal progress approval controls in `src/BikeTracking.Frontend/src/pages/settings/SettingsPage.tsx`
115+
- [x] T043 [US3] Render optional metric suggestions and approved metrics in `src/BikeTracking.Frontend/src/pages/dashboard/dashboard-page.tsx`
116116

117117
**Checkpoint**: User Story 3 is independently functional when optional metrics stay hidden until approved and appear only after rider opt-in.
118118

@@ -122,9 +122,9 @@
122122

123123
**Purpose**: Final validation and cleanup across stories.
124124

125-
- [ ] T044 [P] Update manual API examples for dashboard and settings approvals in `src/BikeTracking.Api/BikeTracking.Api.http`
126-
- [ ] T045 Code cleanup and shared helper refactoring in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
127-
- [ ] T046 [P] Run full validation from `specs/012-dashboard-stats/quickstart.md`
125+
- [x] T044 [P] Update manual API examples for dashboard and settings approvals in `src/BikeTracking.Api/BikeTracking.Api.http`
126+
- [x] T045 Code cleanup and shared helper refactoring in `src/BikeTracking.Api/Application/Dashboard/GetDashboardService.cs`
127+
- [x] T046 [P] Run full validation from `specs/012-dashboard-stats/quickstart.md`
128128

129129
---
130130

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using BikeTracking.Api.Application.Dashboard;
2+
using BikeTracking.Api.Infrastructure.Persistence;
3+
using BikeTracking.Api.Infrastructure.Persistence.Entities;
4+
using Microsoft.EntityFrameworkCore;
5+
6+
namespace BikeTracking.Api.Tests.Application.Dashboard;
7+
8+
public sealed class GetDashboardServiceTests
9+
{
10+
[Fact]
11+
public void GetDashboardService_TypeExists()
12+
{
13+
var serviceType = typeof(BikeTrackingDbContext).Assembly.GetType(
14+
"BikeTracking.Api.Application.Dashboard.GetDashboardService"
15+
);
16+
17+
Assert.NotNull(serviceType);
18+
}
19+
20+
[Fact]
21+
public void GetDashboardService_ExposesAsyncReadMethod()
22+
{
23+
var serviceType = typeof(BikeTrackingDbContext).Assembly.GetType(
24+
"BikeTracking.Api.Application.Dashboard.GetDashboardService"
25+
);
26+
27+
Assert.NotNull(serviceType);
28+
29+
var method = serviceType!.GetMethod("GetAsync") ?? serviceType.GetMethod("ExecuteAsync");
30+
31+
Assert.NotNull(method);
32+
}
33+
34+
[Fact]
35+
public async Task GetDashboardService_UsesRideSnapshotsForSavings_WhenCurrentSettingsChanged()
36+
{
37+
using var dbContext = CreateDbContext();
38+
var rider = new UserEntity
39+
{
40+
DisplayName = "Dashboard Snapshot Rider",
41+
NormalizedName = "dashboard snapshot rider",
42+
CreatedAtUtc = DateTime.UtcNow,
43+
};
44+
dbContext.Users.Add(rider);
45+
await dbContext.SaveChangesAsync();
46+
47+
dbContext.UserSettings.Add(
48+
new UserSettingsEntity
49+
{
50+
UserId = rider.UserId,
51+
AverageCarMpg = 40m,
52+
MileageRateCents = 80m,
53+
UpdatedAtUtc = DateTime.UtcNow,
54+
}
55+
);
56+
57+
dbContext.Rides.Add(
58+
new RideEntity
59+
{
60+
RiderId = rider.UserId,
61+
RideDateTimeLocal = DateTime.Now,
62+
Miles = 10m,
63+
GasPricePerGallon = 3m,
64+
SnapshotAverageCarMpg = 20m,
65+
SnapshotMileageRateCents = 50m,
66+
CreatedAtUtc = DateTime.UtcNow,
67+
}
68+
);
69+
await dbContext.SaveChangesAsync();
70+
71+
var service = new GetDashboardService(dbContext);
72+
var dashboard = await service.GetAsync(rider.UserId);
73+
74+
Assert.Equal(5m, dashboard.Totals.MoneySaved.MileageRateSavings);
75+
Assert.Equal(1.5m, dashboard.Totals.MoneySaved.FuelCostAvoided);
76+
Assert.Equal(6.5m, dashboard.Totals.MoneySaved.CombinedSavings);
77+
}
78+
79+
[Fact]
80+
public async Task GetDashboardService_ExcludesLegacyRideWithoutSnapshot_FromSavings()
81+
{
82+
using var dbContext = CreateDbContext();
83+
var rider = new UserEntity
84+
{
85+
DisplayName = "Legacy Snapshot Rider",
86+
NormalizedName = "legacy snapshot rider",
87+
CreatedAtUtc = DateTime.UtcNow,
88+
};
89+
dbContext.Users.Add(rider);
90+
await dbContext.SaveChangesAsync();
91+
92+
dbContext.Rides.Add(
93+
new RideEntity
94+
{
95+
RiderId = rider.UserId,
96+
RideDateTimeLocal = DateTime.Now,
97+
Miles = 8m,
98+
GasPricePerGallon = 3.2m,
99+
CreatedAtUtc = DateTime.UtcNow,
100+
}
101+
);
102+
await dbContext.SaveChangesAsync();
103+
104+
var service = new GetDashboardService(dbContext);
105+
var dashboard = await service.GetAsync(rider.UserId);
106+
107+
Assert.Null(dashboard.Totals.MoneySaved.MileageRateSavings);
108+
Assert.Null(dashboard.Totals.MoneySaved.FuelCostAvoided);
109+
Assert.Null(dashboard.Totals.MoneySaved.CombinedSavings);
110+
Assert.Equal(0, dashboard.Totals.MoneySaved.QualifiedRideCount);
111+
Assert.Equal(1, dashboard.MissingData.RidesMissingSavingsSnapshot);
112+
Assert.Equal(1, dashboard.Totals.AllTimeMiles.RideCount);
113+
Assert.Equal(8m, dashboard.Totals.AllTimeMiles.Miles);
114+
}
115+
116+
private static BikeTrackingDbContext CreateDbContext()
117+
{
118+
var options = new DbContextOptionsBuilder<BikeTrackingDbContext>()
119+
.UseInMemoryDatabase(Guid.NewGuid().ToString())
120+
.Options;
121+
122+
return new BikeTrackingDbContext(options);
123+
}
124+
}

0 commit comments

Comments
 (0)