diff --git a/DHF/items/00_uc/UC-006.yaml b/DHF/items/00_uc/UC-006.yaml new file mode 100644 index 0000000..ef4f606 --- /dev/null +++ b/DHF/items/00_uc/UC-006.yaml @@ -0,0 +1,29 @@ +id: UC-006 +title: View Release History +content: 'A user opens the release notes page to find out what changed in the current + and previous software versions. + + + Actor: Radiation Therapist / Physician / Dosimetrist / Administrator + + + Preconditions: + + - The application is running and the user can navigate to any page + + + Primary Flow: + + 1. User navigates to the release notes page from within the application + + 2. System displays a list of software releases in reverse-chronological order + + 3. Each entry shows the version number, release date, and a summary of changes + + 4. User reviews the change entries to understand what is new or fixed + + + Postconditions: + + - User understands which features and fixes are included in the installed version' +type: UC diff --git a/DHF/items/01_req_crs/CRS-013.yaml b/DHF/items/01_req_crs/CRS-013.yaml new file mode 100644 index 0000000..5df6cf6 --- /dev/null +++ b/DHF/items/01_req_crs/CRS-013.yaml @@ -0,0 +1,22 @@ +id: CRS-013 +derives_from: +- UC-006 +title: Users shall be able to view software version and release change history +content: 'As a radiation therapist, physician, dosimetrist, or administrator, I need + to access a release notes page within the application that lists each software version + with its release date and a summary of changes, so that I can confirm which features + and fixes are present in the installed version. + + + Verification criteria: The release notes page is reachable from the application + navigation without authentication, displays entries in reverse-chronological order, + and each entry includes at minimum a version number, release date, and change summary + text.' +user_group: Radiation Therapist / Physician / Dosimetrist / Administrator +priority: Medium +testing: 'T1: Given the application is running, a release notes page is accessible + from within the application and displays at least one release entry with a version + number and change summary. + + ' +type: CRS diff --git a/DHF/items/02_req_sys/SYS-016.yaml b/DHF/items/02_req_sys/SYS-016.yaml new file mode 100644 index 0000000..1f3adad --- /dev/null +++ b/DHF/items/02_req_sys/SYS-016.yaml @@ -0,0 +1,27 @@ +id: SYS-016 +satisfies: +- CRS-013 +title: System shall provide an in-application release notes page displaying version + history +content: The system shall provide a dedicated release notes page accessible from within + the application. The page shall list software releases in reverse-chronological + order. Each release entry shall include the version number, release date, and a + human-readable summary of changes. The page shall be reachable via application navigation + without requiring authentication beyond normal application access. +category: Functional +verification_method: +- Test +- Demonstration +testing: 'T1: Given the application is running, the release notes page is reachable + and renders at least one release entry containing a version number and change summary. + + T2: Given multiple release entries, they are ordered newest-first by version or + date. + + ' +critical_safety: false +verification_criteria: The release notes page renders at /release-notes with at least + one entry; each entry displays a non-empty version string, a release date, and a + non-empty change summary; entries are ordered newest-first; the page is accessible + without any authentication gate. +type: SYS diff --git a/DHF/items/03_req_srs/SRS-031.yaml b/DHF/items/03_req_srs/SRS-031.yaml new file mode 100644 index 0000000..4f1ab10 --- /dev/null +++ b/DHF/items/03_req_srs/SRS-031.yaml @@ -0,0 +1,23 @@ +id: SRS-031 +derives_from: +- SYS-016 +title: Software shall display a dedicated release notes page listing version, date, + and change entries +content: 'The client application shall render a dedicated release notes page at the + route /release-notes. The page shall present a statically defined list of release + entries in reverse-chronological order. Each entry shall include: (1) the version + string, (2) the release date, and (3) one or more change description items. The + page shall be linked from the application navigation so users can reach it without + returning to the workspace. The release data shall be embedded in the client source + as a static constant to avoid runtime dependencies.' +verification_method: +- Test +testing: 'T1: Given the release notes page, it renders the release data with at least + one entry showing a version string and change summary. + + T2: Given multiple release entries, the first rendered entry is the most recent + version. + + ' +critical_safety: false +type: SRS diff --git a/DHF/items/04_modules/MODULE-010.yaml b/DHF/items/04_modules/MODULE-010.yaml new file mode 100644 index 0000000..8e3d309 --- /dev/null +++ b/DHF/items/04_modules/MODULE-010.yaml @@ -0,0 +1,23 @@ +id: MODULE-010 +title: ApplicationPages +content: 'Standalone informational and administrative pages rendered outside the clinical + workspace viewport. + + + Responsibilities: + + - About page (/about): product description, tech stack, compliance standards, version + + - Issues/Change Requests page (/issues): GitHub-backed CR submission and monitoring + + - Settings page (/settings): user preferences and configuration + + - Release Notes page (/release-notes): version history and change summaries + + + Each page is a React route component registered in App.tsx (React Router 7). Pages + share the application shell (navigation, routing) but are otherwise independent + of the clinical workspace stores (volumeStore, structureStore). Pages may use uiStore + only for global UI state (theme, sidebar). No DICOM or contouring logic belongs + in this module.' +type: MODULE diff --git a/DHF/items/05_swdd/SWDD-013.yaml b/DHF/items/05_swdd/SWDD-013.yaml new file mode 100644 index 0000000..502ea38 --- /dev/null +++ b/DHF/items/05_swdd/SWDD-013.yaml @@ -0,0 +1,22 @@ +id: SWDD-013 +implements: +- SRS-031 +module: +- MODULE-010 +title: Release notes static data model and ReleaseNotes page component +content: "Release data is a static constant defined in apps/client/src/pages/ReleaseNotes.tsx\ + \ (or a colocated releaseData.ts). The data structure is an array of ReleaseEntry\ + \ objects sorted newest-first:\n\n interface ReleaseEntry {\n version: string;\ + \ // semver string, e.g. \"1.2.0\"\n date: string; // ISO-8601 date, e.g.\ + \ \"2026-06-05\"\n changes: string[]; // one item per change bullet\n }\n\n\ + Statically embedding the data avoids runtime network dependencies and makes the\ + \ content predictable and auditable.\n\nThe ReleaseNotes page component maps over\ + \ the data array and renders each entry as a card: version and date in the header,\ + \ change items as a bulleted list. The component does not use any Zustand stores.\n\ + \nRouting: App.tsx registers the route } />. Navigation: LeftSidebar.tsx adds a link to /release-notes in the System\ + \ footer row alongside the existing Issues and Settings links.\n\nStyling follows\ + \ the ApplicationPages pattern (bg-gray-50 page background, bg-white cards with\ + \ border-gray-200, text-gray-900/600/400 — light-mode page layout used by About\ + \ and Settings), not the dark clinical workspace theme." +type: SWDD diff --git a/DHF/items/06_sys_arch/SYSARCH-001.yaml b/DHF/items/06_sys_arch/SYSARCH-001.yaml index 2b893bf..3bb6e73 100644 --- a/DHF/items/06_sys_arch/SYSARCH-001.yaml +++ b/DHF/items/06_sys_arch/SYSARCH-001.yaml @@ -1,35 +1,4 @@ id: SYSARCH-001 -title: Browser Client -status: approved -content: |- - The primary software item running in the clinical user's browser. Responsible for - all user interaction, DICOM rendering, contouring workflow, and local state management. - - Technology stack: - - React 18 + TypeScript (strict) + Vite — component framework and build tooling - - Cornerstone3D — GPU-accelerated DICOM rendering via WebGL (axial/sagittal/coronal MPR) - - Zustand + Immer — reactive state management (volumeStore, structureStore, uiStore) - - Tailwind CSS — dark clinical UI theme - - Key sub-components: - - ViewportManager: manages Cornerstone3D rendering contexts and MPR layout - - MPRController: coordinates multi-planar reconstruction across viewports - - ContourEngine: freehand, polygon, brush, and eraser contouring tools; undo/redo - - DICOMweb client: QIDO-RS (query), WADO-RS (retrieve), STOW-RS (store) via /dicom-web proxy - - IndexedDB adapter: browser-local auto-save of in-progress structure set drafts - - External interfaces: - - /dicom-web/* → Orthanc DICOMweb (proxied by Vite in dev, reverse proxy in prod) - - /api/* → ASP.NET Core API (proxied by Vite in dev) - - /ws → WebSocket for real-time updates (proxied) - - Data flows: - - Image loading: QIDO-RS query → WADO-RS metadata → Cornerstone image IDs → - VolumeBuilder → Cornerstone3D volume → ViewportManager.setVolume() → GPU render - - Contouring: user gesture → ContourEngine.addContour() → UndoRedoManager → - structureStore → React re-render - - Draft persistence: structureStore dirty → IndexedDB auto-save → restore on reload - - Structure upload: active RTSTRUCT → STOW-RS to DICOM repository design: - SYS-002 - SYS-003 @@ -44,3 +13,25 @@ design: - SYS-012 - SYS-014 - SYS-015 +- SYS-016 +title: Browser Client +status: approved +content: "The primary software item running in the clinical user's browser. Responsible\ + \ for\nall user interaction, DICOM rendering, contouring workflow, and local state\ + \ management.\n\nTechnology stack:\n- React 18 + TypeScript (strict) + Vite — component\ + \ framework and build tooling\n- Cornerstone3D — GPU-accelerated DICOM rendering\ + \ via WebGL (axial/sagittal/coronal MPR)\n- Zustand + Immer — reactive state management\ + \ (volumeStore, structureStore, uiStore)\n- Tailwind CSS — dark clinical UI theme\n\ + \nKey sub-components:\n- ViewportManager: manages Cornerstone3D rendering contexts\ + \ and MPR layout\n- MPRController: coordinates multi-planar reconstruction across\ + \ viewports\n- ContourEngine: freehand, polygon, brush, and eraser contouring tools;\ + \ undo/redo\n- DICOMweb client: QIDO-RS (query), WADO-RS (retrieve), STOW-RS (store)\ + \ via /dicom-web proxy\n- IndexedDB adapter: browser-local auto-save of in-progress\ + \ structure set drafts\n\nExternal interfaces:\n- /dicom-web/* → Orthanc DICOMweb\ + \ (proxied by Vite in dev, reverse proxy in prod)\n- /api/* → ASP.NET Core API (proxied\ + \ by Vite in dev)\n- /ws → WebSocket for real-time updates (proxied)\n\nData flows:\n\ + - Image loading: QIDO-RS query → WADO-RS metadata → Cornerstone image IDs →\n VolumeBuilder\ + \ → Cornerstone3D volume → ViewportManager.setVolume() → GPU render\n- Contouring:\ + \ user gesture → ContourEngine.addContour() → UndoRedoManager →\n structureStore\ + \ → React re-render\n- Draft persistence: structureStore dirty → IndexedDB auto-save\ + \ → restore on reload\n- Structure upload: active RTSTRUCT → STOW-RS to DICOM repository" diff --git a/DHF/items/09_cr/CR-014.yaml b/DHF/items/09_cr/CR-014.yaml new file mode 100644 index 0000000..762e697 --- /dev/null +++ b/DHF/items/09_cr/CR-014.yaml @@ -0,0 +1,81 @@ +id: CR-014 +affected_risk_items: [] +title: Add a release note page for the software +type: CR +description: 'I want to add a release note page for the software, so I can know what + is changed. + + + Source issue: https://github.com/itercharles/ContourLab/issues/118' +justification: I want to know what feature or defect fix are included in the software. +priority: Medium +requested_by: itercharles +target_version: 2026-W23 +category: Feature +content: there is release note page, can be accessible from the software, displaying + the summary of each changes of each version. +status: new +impact_assessment: 'Approved. Complexity: small. Affected subsystems: client. Related + CRs: CR-012. Adds a standalone release notes page following the established React + router pattern; no backend or clinical workflow changes required.' +implementation_notes: "## Overview\nCR-014 adds a dedicated Release Notes page to\ + \ ContourLab, accessible from within the application. The page lists software releases\ + \ in reverse-chronological order, with each entry showing a version number, release\ + \ date, and change bullet points. Release data is statically embedded in the component\ + \ source — no network call or backend change is needed. The feature follows the\ + \ existing application-page pattern (About, Issues, Settings) used throughout the\ + \ frontend.\n\n## Current State\nThe application has four routes defined in `apps/client/src/App.tsx`:\n\ + - `/workspace` → MainWorkspace\n- `/settings` → Settings\n- `/issues` → Issues\n\ + - `/about` → About\n\nNavigation links for Issues and Settings are in the System\ + \ footer row of `apps/client/src/components/layout/LeftSidebar.tsx` (the bottom\ + \ strip with `text-[11px] uppercase tracking-widest` System label and two icon-button\ + \ Links).\n\nThe `About.tsx` page at `apps/client/src/pages/About.tsx` follows a\ + \ light-mode card layout (bg-gray-50 page, bg-white cards, border-gray-200 borders)\ + \ that the new page should match.\n\nThe app has no existing release notes data\ + \ source — data must be authored at implementation time.\n\n## Changes Required\n\ + - **`apps/client/src/pages/ReleaseNotes.tsx`** — new file: the page component plus\ + \ the static release data constant. Follows the About.tsx layout convention (light-mode,\ + \ max-w-5xl centered layout).\n- **`apps/client/src/App.tsx`** — modify existing:\ + \ add `import ReleaseNotes from ./pages/ReleaseNotes` and `} />` before the catch-all redirect.\n- **`apps/client/src/components/layout/LeftSidebar.tsx`**\ + \ — modify existing: add a Link to `/release-notes` in the System footer row icon\ + \ group, alongside the Issues and Settings links.\n- **`apps/client/src/pages/ReleaseNotes.test.tsx`**\ + \ — new file: Vitest component tests covering SRS-031 T1 and T2 test points.\n\n\ + ## Implementation Steps\n1. Author the `ReleaseEntry` interface and `RELEASE_NOTES`\ + \ constant in `ReleaseNotes.tsx`. Include at least the current version entry sourced\ + \ from `package.json` (already imported in About.tsx as `import { version } from\ + \ ../../package.json`).\n2. Implement the `ReleaseNotes` component: header matching\ + \ About.tsx style, one card per entry with version + date header and a bulleted\ + \ change list.\n3. Register the route in `App.tsx`: import the component and add\ + \ the route before the `` catch-all.\n4. Add the navigation link\ + \ in `LeftSidebar.tsx`: add a ``\ + \ icon button in the System footer row. Use a changelog-style SVG icon (e.g., list\ + \ or document icon at 13×13px, same style as the Issues icon).\n5. Write tests in\ + \ `ReleaseNotes.test.tsx` covering T1 (page renders at least one entry with version\ + \ and change text) and T2 (first rendered entry is the most recent — assert `RELEASE_NOTES[0].version`\ + \ appears first in the DOM).\n6. Run `pnpm --filter @contourlab/client typecheck`\ + \ — expect zero errors.\n7. Run `pnpm --filter @contourlab/client test` — all tests\ + \ must pass.\n8. Manual check: open the app, click the release notes icon in the\ + \ sidebar System footer, confirm the page loads and shows version entries.\n\n##\ + \ Edge Cases & Constraints\n- `RELEASE_NOTES` must have at least one entry or the\ + \ page renders an empty state — guard or ensure the constant is non-empty.\n- The\ + \ `/release-notes` route must appear before the `` catch-all in\ + \ App.tsx or it will never match.\n- Styling must follow the light-mode page pattern\ + \ (bg-gray-50, bg-white cards) from About.tsx — not the dark clinical workspace\ + \ theme. The page is outside the workspace viewport.\n- No `rounded-xl` or larger.\ + \ No inline styles. No `any` TypeScript.\n- The `reloadDocument` prop on the Link\ + \ in LeftSidebar is used by existing navigation links (Issues, Settings) to do a\ + \ full navigation away from the Cornerstone3D viewport; use it here too.\n- SRS-031\ + \ requirement says data is a static constant — do not fetch from GitHub Releases\ + \ API or read a file at runtime.\n\n## Tests\n- **Unit / component tests** (`ReleaseNotes.test.tsx`):\n\ + \ - `@links:SRS-031` annotation on the describe block\n - T1: render the component\ + \ with the default static data; assert at least one version string and one change\ + \ item appear in the DOM. `@testing:T1`\n - T2: assert that `RELEASE_NOTES[0].version`\ + \ appears before `RELEASE_NOTES[1].version` in the rendered output (if there are\ + \ ≥2 entries). `@testing:T2`\n- **Typecheck**: `pnpm --filter @contourlab/client\ + \ typecheck` — zero errors\n- **Manual confirmation**: navigate to /release-notes\ + \ in a running dev server; verify the page renders correctly with at least one release\ + \ entry." +affected_items: +- CR-014 +- SYSARCH-001 diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index bf6ccdb..19f2748 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -2,6 +2,7 @@ import { Routes, Route, Navigate } from 'react-router-dom'; import About from './pages/About'; import Issues from './pages/Issues'; import MainWorkspace from './pages/MainWorkspace'; +import ReleaseNotes from './pages/ReleaseNotes'; import Settings from './pages/Settings'; function App() { @@ -11,6 +12,7 @@ function App() { } /> } /> } /> + } /> } /> ); diff --git a/apps/client/src/components/layout/LeftSidebar.tsx b/apps/client/src/components/layout/LeftSidebar.tsx index 01e2c57..b278c76 100644 --- a/apps/client/src/components/layout/LeftSidebar.tsx +++ b/apps/client/src/components/layout/LeftSidebar.tsx @@ -103,6 +103,21 @@ export default function LeftSidebar() { System
+ + + + + + + + + { + // @testing:T1 + it('renders at least one entry with a version string and change text', () => { + render(); + expect(RELEASE_NOTES.length).toBeGreaterThan(0); + expect(screen.getByText(`v${RELEASE_NOTES[0].version}`)).toBeTruthy(); + expect(screen.getByText(RELEASE_NOTES[0].changes[0])).toBeTruthy(); + }); + + // @testing:T2 + it('renders the most recent entry first', () => { + if (RELEASE_NOTES.length < 2) return; + render(); + const first = screen.getByText(`v${RELEASE_NOTES[0].version}`); + const second = screen.getByText(`v${RELEASE_NOTES[1].version}`); + expect( + first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING, + ).toBeTruthy(); + }); +}); diff --git a/apps/client/src/pages/ReleaseNotes.tsx b/apps/client/src/pages/ReleaseNotes.tsx new file mode 100644 index 0000000..83c2f57 --- /dev/null +++ b/apps/client/src/pages/ReleaseNotes.tsx @@ -0,0 +1,42 @@ +import { RELEASE_NOTES } from './releaseNotesData'; + +function ReleaseNotes() { + return ( +
+
+
+

Release Notes

+

+ What's changed in each version of ContourLab +

+
+
+ +
+ {RELEASE_NOTES.map((entry) => ( +
+
+

+ v{entry.version} +

+ {entry.date} +
+
    + {entry.changes.map((change) => ( +
  • + + {change} +
  • + ))} +
+
+ ))} +
+
+ ); +} + +export default ReleaseNotes; diff --git a/apps/client/src/pages/releaseNotesData.ts b/apps/client/src/pages/releaseNotesData.ts new file mode 100644 index 0000000..4f145b3 --- /dev/null +++ b/apps/client/src/pages/releaseNotesData.ts @@ -0,0 +1,31 @@ +import { version } from '../../package.json'; + +export interface ReleaseEntry { + version: string; + date: string; + changes: string[]; +} + +export const RELEASE_NOTES: ReleaseEntry[] = [ + { + version, + date: '2026-06-05', + changes: [ + 'Added dedicated release notes page accessible from the sidebar system footer', + 'Added navigation icon in the left sidebar system footer row for release notes', + ], + }, + { + version: '0.1.0', + date: '2026-05-01', + changes: [ + 'Initial release of ContourLab', + 'DICOM repository browser with patient/series worklist', + 'Multi-viewport image display with Cornerstone3D rendering', + 'RTSTRUCT import, structure list, and per-structure visibility toggles', + 'Real-time collaborative contour editing over WebSocket', + 'Issues and change request tracking page', + 'Settings page with display and preference controls', + ], + }, +];