[bug] Milestone list: not scoped to project; missing overdue/today visual indicators #33

Closed
opened 2026-05-05 08:07:13 +00:00 by casper-stevens · 4 comments
Member

Context

Identified in home#211: milestones are disconnected from projects — you cannot link a project to a sprint/milestone from the milestone list, and there are no visual indicators for overdue or due-today milestones.

Current state

milestones_list handler (handlers/mod.rs:4957) loads all milestones for the space with no project filter. The milestone list template renders a flat list with no date-based highlighting.

Milestone.project_sid is a required field (not Option) so every milestone already belongs to a project — it's just not used for filtering.

Fix

1. Project-scoped milestone list

  • Add optional ?project=<sid> query param to the milestone list handler
  • Filter milestones by project_sid when param is present
  • Add a project dropdown filter to the milestone list template
  • Link the "Milestones" tab in project detail to /c/:context/milestones?project=<sid>

2. Overdue / due-today indicators

  • In the milestone list template, compare milestone.due_date against today's date
  • Apply Bootstrap badge or row color: danger for overdue, warning for due today
  • Show a calendar icon or badge next to the due date

Notes

  • Due dates are stored as strings (ISO 8601); compare lexicographically or parse as NaiveDate
  • The project detail page already calls get_milestones_for_project so that view is correct — only the standalone milestone list needs the filter
## Context Identified in home#211: milestones are disconnected from projects — you cannot link a project to a sprint/milestone from the milestone list, and there are no visual indicators for overdue or due-today milestones. ## Current state `milestones_list` handler (`handlers/mod.rs:4957`) loads all milestones for the space with no project filter. The milestone list template renders a flat list with no date-based highlighting. `Milestone.project_sid` is a required field (not `Option`) so every milestone already belongs to a project — it's just not used for filtering. ## Fix ### 1. Project-scoped milestone list - Add optional `?project=<sid>` query param to the milestone list handler - Filter milestones by `project_sid` when param is present - Add a project dropdown filter to the milestone list template - Link the "Milestones" tab in project detail to `/c/:context/milestones?project=<sid>` ### 2. Overdue / due-today indicators - In the milestone list template, compare `milestone.due_date` against today's date - Apply Bootstrap badge or row color: `danger` for overdue, `warning` for due today - Show a calendar icon or badge next to the due date ## Notes - Due dates are stored as strings (ISO 8601); compare lexicographically or parse as NaiveDate - The project detail page already calls `get_milestones_for_project` so that view is correct — only the standalone milestone list needs the filter
Author
Member

Implementation Spec: Issue #33 — Milestone List: Project Scoping + Overdue/Today Visual Indicators

Objective

Fix the standalone milestone list page (/c/:context/milestones) so that:

  1. It can be filtered to show only milestones belonging to a specific project, via an optional ?project=<sid> query param.
  2. It displays visual indicators (Bootstrap danger badge for overdue, warning for due-today) on each milestone row based on due_date vs. today's date.
  3. The Milestones section in the project detail page links through to the filtered milestone list.

Requirements

  • Add a MilestonesListQuery struct with an optional project: Option<String> field, mirroring KanbanQuery.
  • Update milestones_list handler signature to accept Query(params): Query<MilestonesListQuery>.
  • When params.project is Some(sid) and non-empty, filter the full milestone list to only those where m.project_sid == sid.
  • Pass selected_project: String (empty string when no filter) into MilestonesListTemplate.
  • In MilestonesListTemplate, add a project filter dropdown rendered as a <form method="GET"> (server-side redirect, not JS-only), with an "All Projects" default option plus one <option> per project, pre-selected when it matches selected_project.
  • In each milestone row, compare m.due_date against today's date (ISO 8601 string, lexicographic comparison is valid). Apply a Bootstrap bg-danger badge for overdue milestones and bg-warning badge for due-today milestones. Skip the indicator when due_date is None or when the milestone status is Completed or Missed.
  • In ProjectDetailTemplate, update the milestones section heading to include a link to /c/{context}/milestones?project={project_sid}.

Files to Modify

File Description
crates/hero_biz_ui/src/web/handlers/mod.rs Add MilestonesListQuery struct; update milestones_list handler to accept query param and filter milestones; pass selected_project to template
crates/hero_biz_ui/src/web/templates/mod.rs Add selected_project: String field to MilestonesListTemplate; add project dropdown filter form; add due-date indicator logic to row rendering; update ProjectDetailTemplate milestones section link

Implementation Plan

Step 1: Add MilestonesListQuery struct and update milestones_list handler

File: crates/hero_biz_ui/src/web/handlers/mod.rs

  • Add MilestonesListQuery { project: Option<String> } struct after KanbanQuery
  • Update milestones_list to accept Query(params): Query<MilestonesListQuery> as fourth parameter
  • Compute selected_project = params.project.unwrap_or_default()
  • Filter milestones by project_sid when selected_project is non-empty
  • Pass selected_project to the template

Step 2: Update MilestonesListTemplate struct and render method

File: crates/hero_biz_ui/src/web/templates/mod.rs

  • Add selected_project: String field to MilestonesListTemplate
  • In render(): compute today via chrono::Utc::now().format("%Y-%m-%d")
  • Build due_indicator per milestone: bg-danger badge if overdue, bg-warning badge if due today, empty string otherwise — skip for Completed/Missed milestones
  • Append due_indicator to the due date cell
  • Build project_options HTML string from self.projects
  • Add project filter <form method="GET"> with dropdown above the table
  • Add project_options and selected_project to the format! named args

File: crates/hero_biz_ui/src/web/templates/mod.rs

  • In the milestones subgrid section, add a "View All" link pointing to /c/{context}/milestones?project={project_sid}

Acceptance Criteria

  • GET /c/:context/milestones returns all milestones (no regression)
  • GET /c/:context/milestones?project=<sid> returns only milestones for that project
  • Project dropdown appears above the table; selecting submits the filter; "All Projects" clears it
  • Overdue milestones show a red Overdue badge; due-today milestones show a yellow Today badge
  • Milestones with no due date or status Completed/Missed show no badge
  • Project detail milestones section has a "View All" link to the filtered list
  • Build compiles without errors

Notes

  • Date comparison is lexicographic — ISO 8601 YYYY-MM-DD strings compare correctly with </==/> as plain strings
  • No store changes needed — filter is in-memory in the handler
  • project_sid is a non-optional String on Milestone
  • chrono is already used in the codebase for date formatting
## Implementation Spec: Issue #33 — Milestone List: Project Scoping + Overdue/Today Visual Indicators ### Objective Fix the standalone milestone list page (`/c/:context/milestones`) so that: 1. It can be filtered to show only milestones belonging to a specific project, via an optional `?project=<sid>` query param. 2. It displays visual indicators (Bootstrap `danger` badge for overdue, `warning` for due-today) on each milestone row based on `due_date` vs. today's date. 3. The Milestones section in the project detail page links through to the filtered milestone list. --- ### Requirements - Add a `MilestonesListQuery` struct with an optional `project: Option<String>` field, mirroring `KanbanQuery`. - Update `milestones_list` handler signature to accept `Query(params): Query<MilestonesListQuery>`. - When `params.project` is `Some(sid)` and non-empty, filter the full milestone list to only those where `m.project_sid == sid`. - Pass `selected_project: String` (empty string when no filter) into `MilestonesListTemplate`. - In `MilestonesListTemplate`, add a project filter dropdown rendered as a `<form method="GET">` (server-side redirect, not JS-only), with an "All Projects" default option plus one `<option>` per project, pre-selected when it matches `selected_project`. - In each milestone row, compare `m.due_date` against today's date (ISO 8601 string, lexicographic comparison is valid). Apply a Bootstrap `bg-danger` badge for overdue milestones and `bg-warning` badge for due-today milestones. Skip the indicator when `due_date` is `None` or when the milestone `status` is `Completed` or `Missed`. - In `ProjectDetailTemplate`, update the milestones section heading to include a link to `/c/{context}/milestones?project={project_sid}`. --- ### Files to Modify | File | Description | |---|---| | `crates/hero_biz_ui/src/web/handlers/mod.rs` | Add `MilestonesListQuery` struct; update `milestones_list` handler to accept query param and filter milestones; pass `selected_project` to template | | `crates/hero_biz_ui/src/web/templates/mod.rs` | Add `selected_project: String` field to `MilestonesListTemplate`; add project dropdown filter form; add due-date indicator logic to row rendering; update `ProjectDetailTemplate` milestones section link | --- ### Implementation Plan #### Step 1: Add `MilestonesListQuery` struct and update `milestones_list` handler **File:** `crates/hero_biz_ui/src/web/handlers/mod.rs` - Add `MilestonesListQuery { project: Option<String> }` struct after `KanbanQuery` - Update `milestones_list` to accept `Query(params): Query<MilestonesListQuery>` as fourth parameter - Compute `selected_project = params.project.unwrap_or_default()` - Filter `milestones` by `project_sid` when `selected_project` is non-empty - Pass `selected_project` to the template #### Step 2: Update `MilestonesListTemplate` struct and render method **File:** `crates/hero_biz_ui/src/web/templates/mod.rs` - Add `selected_project: String` field to `MilestonesListTemplate` - In `render()`: compute `today` via `chrono::Utc::now().format("%Y-%m-%d")` - Build `due_indicator` per milestone: `bg-danger` badge if overdue, `bg-warning` badge if due today, empty string otherwise — skip for Completed/Missed milestones - Append `due_indicator` to the due date cell - Build `project_options` HTML string from `self.projects` - Add project filter `<form method="GET">` with dropdown above the table - Add `project_options` and `selected_project` to the `format!` named args #### Step 3: Update `ProjectDetailTemplate` milestones link **File:** `crates/hero_biz_ui/src/web/templates/mod.rs` - In the milestones subgrid section, add a "View All" link pointing to `/c/{context}/milestones?project={project_sid}` --- ### Acceptance Criteria - [ ] `GET /c/:context/milestones` returns all milestones (no regression) - [ ] `GET /c/:context/milestones?project=<sid>` returns only milestones for that project - [ ] Project dropdown appears above the table; selecting submits the filter; "All Projects" clears it - [ ] Overdue milestones show a red `Overdue` badge; due-today milestones show a yellow `Today` badge - [ ] Milestones with no due date or status Completed/Missed show no badge - [ ] Project detail milestones section has a "View All" link to the filtered list - [ ] Build compiles without errors --- ### Notes - Date comparison is lexicographic — ISO 8601 `YYYY-MM-DD` strings compare correctly with `<`/`==`/`>` as plain strings - No store changes needed — filter is in-memory in the handler - `project_sid` is a non-optional `String` on `Milestone` - `chrono` is already used in the codebase for date formatting
Author
Member

Build & Test Results

Build: PASS
Tests: 1 passed, 0 failed

Details:

  • hero_biz_ui: 1 test passed (parser::tests::test_name_fix)
  • hero_biz_app, hero_biz_sdk, and binary targets: no tests defined
  • All doc-test suites: no tests defined

Build completed in 29.56s, test run completed in 42.45s (dev profile).

## Build & Test Results **Build:** PASS **Tests:** 1 passed, 0 failed **Details:** - `hero_biz_ui`: 1 test passed (`parser::tests::test_name_fix`) - `hero_biz_app`, `hero_biz_sdk`, and binary targets: no tests defined - All doc-test suites: no tests defined Build completed in 29.56s, test run completed in 42.45s (dev profile).
Author
Member

Implementation Complete

Changes Made

crates/hero_biz_ui/src/web/handlers/mod.rs

  • Added MilestonesListQuery { project: Option<String> } struct (after KanbanQuery)
  • Updated milestones_list handler to accept Query(params): Query<MilestonesListQuery> as a fourth parameter
  • When ?project=<sid> is present, milestones are filtered in-memory by project_sid; unfiltered when absent
  • selected_project is passed to MilestonesListTemplate

crates/hero_biz_ui/src/web/templates/mod.rs

  • Added selected_project: String field to MilestonesListTemplate
  • render() computes today's date once via chrono::Utc::now().format("%Y-%m-%d")
  • Per-milestone due_indicator: bg-danger badge for overdue, bg-warning for due today; empty for Completed/Missed milestones or those with no due date
  • Due date table cell updated to append due_indicator
  • Project filter <form method="GET"> dropdown added above the milestone table, with auto-submit on change and a Clear link
  • ProjectDetailTemplate milestones section updated with a "View All" link to /c/{context}/milestones?project={project_sid}

Test Results

Build: PASS (29.56s, only hero_biz_ui recompiled)
Tests: 1 passed, 0 failed

Acceptance Criteria Status

  • GET /c/:context/milestones returns all milestones (no regression)
  • GET /c/:context/milestones?project=<sid> returns only milestones for that project
  • Project dropdown filter above the table; selecting submits the filter; Clear resets it
  • Overdue milestones show a red Overdue badge; due-today milestones show a yellow Today badge
  • Milestones with no due date or status Completed/Missed show no badge
  • Project detail milestones section has a View All link to the filtered list
  • Build compiles without errors
## Implementation Complete ### Changes Made **`crates/hero_biz_ui/src/web/handlers/mod.rs`** - Added `MilestonesListQuery { project: Option<String> }` struct (after `KanbanQuery`) - Updated `milestones_list` handler to accept `Query(params): Query<MilestonesListQuery>` as a fourth parameter - When `?project=<sid>` is present, milestones are filtered in-memory by `project_sid`; unfiltered when absent - `selected_project` is passed to `MilestonesListTemplate` **`crates/hero_biz_ui/src/web/templates/mod.rs`** - Added `selected_project: String` field to `MilestonesListTemplate` - `render()` computes today's date once via `chrono::Utc::now().format("%Y-%m-%d")` - Per-milestone `due_indicator`: `bg-danger` badge for overdue, `bg-warning` for due today; empty for Completed/Missed milestones or those with no due date - Due date table cell updated to append `due_indicator` - Project filter `<form method="GET">` dropdown added above the milestone table, with auto-submit on change and a Clear link - `ProjectDetailTemplate` milestones section updated with a "View All" link to `/c/{context}/milestones?project={project_sid}` ### Test Results Build: PASS (29.56s, only `hero_biz_ui` recompiled) Tests: 1 passed, 0 failed ### Acceptance Criteria Status - [x] `GET /c/:context/milestones` returns all milestones (no regression) - [x] `GET /c/:context/milestones?project=<sid>` returns only milestones for that project - [x] Project dropdown filter above the table; selecting submits the filter; Clear resets it - [x] Overdue milestones show a red Overdue badge; due-today milestones show a yellow Today badge - [x] Milestones with no due date or status Completed/Missed show no badge - [x] Project detail milestones section has a View All link to the filtered list - [x] Build compiles without errors
Author
Member

Fixed in commit 4873d01 on branch development_casper.

Fixed in commit 4873d01 on branch development_casper.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_biz#33
No description provided.