WIP: Dashboard redesign — design system overhaul and component updates

Frontend redesign in progress: updated styles, layout, and components
across all pages to align with new design system. Includes Jira API
compliance specs, property tests, and load test script.
This commit is contained in:
root
2026-04-29 14:20:23 +00:00
parent 37119b9c8a
commit 27192dd69f
78 changed files with 9902 additions and 1368 deletions

View File

@@ -0,0 +1 @@
{"specId": "ab9fb651-cc74-49e1-abdf-024a9b090e6f", "workflowType": "requirements-first", "specType": "feature"}

View File

@@ -0,0 +1,199 @@
# Design Document: Dashboard Redesign
## Overview
This design covers the comprehensive visual redesign of the STEAM Security Dashboard frontend. The redesign applies a refined design system extracted to `docs/design-system-redesign/` — evolving the existing dark tactical console aesthetic with an expanded token system, updated typography, refined card surfaces, enhanced severity badges, new layout tokens, proper font loading, a new brand mark, and refined animations.
The redesign is **purely presentational**. All existing behavior, routes, state management, and API calls are preserved. Only JSX style props, inline style objects, CSS custom properties, CSS classes, and the HTML font-loading link change.
### Design Decisions
1. **Token-first migration**: All new design tokens are added to `App.css` `:root` alongside existing tokens. Old token names are preserved so unmigrated components continue to render correctly. This enables incremental page-by-page migration without big-bang breakage.
2. **No new dependencies**: The redesign uses only existing libraries (React, lucide-react, recharts). Fonts load from Google Fonts CDN via a `<link>` tag in `index.html`. The existing Tailwind CDN script in `index.html` remains untouched — it is used by some components and removing it is out of scope.
3. **Dual styling strategy**: The app uses both inline style objects (JS constants in component files) and CSS classes from `App.css`. Both are updated. The UI kit reference files in `docs/design-system-redesign/ui_kits/` use inline styles with `var(--token)` references — the same pattern the production code already uses.
4. **Severity colors are immutable**: Critical (#EF4444), High (#F59E0B), Medium (#0EA5E9), Low (#10B981) — these mappings never change across any component.
---
## Architecture
The redesign does not alter the application architecture. The frontend remains a React 19 SPA (Create React App) with page-level navigation managed in `App.js`, auth via React Context, and `fetch()` API calls with cookie-based auth.
### Migration Flow
```mermaid
graph TD
A[Phase 1: Token Migration] --> B[Phase 2: Font Loading]
B --> C[Phase 3: Global CSS Classes]
C --> D[Phase 4: App Shell]
D --> E[Phase 5: Home Page]
E --> F[Phase 6: Reporting Page]
F --> G[Phase 7: Compliance Page]
G --> H[Phase 8: Knowledge Base Page]
H --> I[Phase 9: Exports Page]
I --> J[Phase 10: Shared Components]
```
Each phase is independently verifiable. After Phase 13, all existing components render correctly with both old and new token names available. Phases 410 migrate individual pages and components one at a time.
---
## Components and Interfaces
### Files Modified (no new files created)
| File | Change Type | Description |
|------|-------------|-------------|
| `frontend/src/App.css` | Token + class update | Port all design tokens from `colors_and_type.css`, update global CSS classes, add semantic type utilities, update animations |
| `frontend/public/index.html` | Font link update | Add Outfit weight 800 to existing Google Fonts link (weight 300 already missing), ensure `display=swap` |
| `frontend/src/App.js` | Inline style update | Update `STYLES` object, stat cards, CVE rows, Quick Lookup, calendar, right-rail panels, top bar, brand mark |
| `frontend/src/components/NavDrawer.js` | Inline style update | Update drawer chrome, nav items, backdrop overlay to use design tokens |
| `frontend/src/components/UserMenu.js` | Inline style update | Update dropdown, avatar, menu items to use design tokens |
| `frontend/src/components/pages/ReportingPage.js` | Inline style update | Update page header, table, charts, buttons, filter chips, status banners |
| `frontend/src/components/pages/CompliancePage.js` | Inline style update | Update teal-accented page header, metric cards, device table, team tabs |
| `frontend/src/components/pages/ComplianceUploadModal.js` | Inline style update | Update modal overlay, card, buttons |
| `frontend/src/components/pages/ComplianceDetailPanel.js` | Inline style update | Update panel chrome, data rows |
| `frontend/src/components/pages/ComplianceChartsPanel.js` | Inline style update | Update chart card wrappers, teal borders |
| `frontend/src/components/pages/KnowledgeBasePage.js` | Inline style update | Update document list, viewer, action buttons |
| `frontend/src/components/pages/ExportsPage.js` | Inline style update | Update page header, export cards, buttons |
| `frontend/src/components/LoginForm.js` | Inline style update | Update form card, inputs, button |
| `frontend/src/components/CalendarWidget.js` | Inline style update | Update calendar grid, day cells, navigation buttons |
| `frontend/src/components/UserManagement.js` | Inline style update | Update group badges, table rows, buttons |
| `frontend/src/components/AuditLog.js` | Inline style update | Update log entry rows, timestamps, action badges |
| `frontend/src/components/NvdSyncModal.js` | Inline style update | Update modal chrome, buttons |
| `frontend/src/components/KnowledgeBaseModal.js` | Inline style update | Update modal chrome, form inputs |
| `frontend/src/components/KnowledgeBaseViewer.js` | Inline style update | Update viewer chrome, markdown content area |
### Token Migration Strategy
The `App.css` `:root` block is updated to include all tokens from `docs/design-system-redesign/colors_and_type.css`. The strategy:
1. **Additive merge**: New tokens are added. Existing tokens that match (e.g., `--intel-darkest`, `--intel-accent`) keep their current values (which already match the design system). No existing token is removed.
2. **Alias tokens added**: Friendly aliases like `--bg-page`, `--bg-surface`, `--fg-1`, `--fg-2`, `--accent`, `--sev-critical` are added so components can use either canonical or alias form.
3. **New token categories added**:
- Surface aliases (`--bg-page`, `--bg-surface`, `--bg-elevated`, `--bg-hover`, `--bg-input`, `--bg-overlay`)
- Foreground aliases (`--fg-1`, `--fg-2`, `--fg-muted`, `--fg-disabled`)
- Border tokens (`--border-subtle`, `--border-default`, `--border-strong`, `--border-focus`)
- Brand accent variants (`--intel-accent-bright`, `--intel-accent-soft`, `--accent`, `--accent-bright`, `--accent-soft`, `--accent-wash`)
- Severity fill tokens (`--sev-critical-bg`, `--sev-high-bg`, `--sev-medium-bg`, `--sev-low-bg`)
- Severity text tokens (`--sev-critical-text`, `--sev-high-text`, `--sev-medium-text`, `--sev-low-text`)
- Group badge tokens (`--group-admin`, `--group-standard`, `--group-leadership`, `--group-readonly`)
- Font family tokens (`--font-ui`, `--font-mono`)
- Type scale tokens (`--fs-display` through `--fs-tiny`)
- Line height, font weight, letter spacing tokens
- Spacing scale (`--sp-1` through `--sp-12`)
- Radii (`--r-xs` through `--r-pill`)
- Elevation shadows (`--shadow-rest` through `--shadow-focus`)
- Severity glows (`--glow-danger`, `--glow-warning`, `--glow-info`, `--glow-success`)
- Heading glow (`--glow-heading`)
- Motion tokens (`--ease-out`, `--ease-in-out`, `--dur-fast`, `--dur-med`, `--dur-slow`)
- Layout tokens (`--topbar-h`, `--drawer-w`, `--panel-w`, `--content-max`, z-index tokens)
### Per-Component Style Mapping
Each component uses a mix of inline style objects and CSS classes. The migration pattern for each:
**Inline style objects** (e.g., `STYLES.statCard` in App.js, hardcoded style props in NavDrawer.js):
- Replace hardcoded hex colors with `var(--token)` references where the token exists
- Update gradient backgrounds to match the Card_Surface treatment from the design system
- Update border values to use the new border tokens
- Update font-family references from `'monospace'` or `'JetBrains Mono', monospace` to `var(--font-mono)`
- Update font-family references from `'Outfit', system-ui, sans-serif` to `var(--font-ui)`
**CSS classes** (e.g., `.intel-card`, `.status-badge`, `.intel-button` in App.css):
- Update to reference new tokens where applicable
- Add new classes (`.stat-card` top-edge gradient rail, semantic type utilities)
- Update animation keyframes to match design system definitions
### App Shell Redesign
The app shell (top bar + nav drawer + user menu) is updated to match `AppShell.jsx` reference:
- **Top bar**: 64px height (`--topbar-h`), `var(--bg-surface)` background, `var(--border-subtle)` bottom border, `var(--z-topbar)` z-index
- **Brand mark**: Typographic stack — "STEAM" in Outfit 700 at 15px, "SECURITY" in Outfit 500 at 9px with 0.18em letter spacing, Shield icon in `var(--accent)` color
- **Nav tabs**: Outfit 13px, 500 weight (600 active), active state uses `var(--accent)` text + `var(--accent-soft)` background
- **Nav drawer**: 240px width (`--drawer-w`), `var(--bg-surface)` background, `var(--border-subtle)` right border, overlay with `var(--bg-overlay)` + `backdrop-filter: blur(4px)`
- **User menu**: Circular avatar with initials in `var(--accent)` on `var(--accent-soft)` background, dropdown with `var(--shadow-popover)` elevation
### Page Identity Colors
Each page has a distinct identity color for its header glow:
| Page | Identity Color | Header Text |
|------|---------------|-------------|
| Home | Sky blue (`#0EA5E9`) | "CVE INTEL" |
| Reporting | Green (`#10B981`) | "REPORTING" |
| Compliance | Teal (`#14B8A6`) | "AEO COMPLIANCE" |
| Knowledge Base | Sky blue or green | Page title |
| Exports | Sky blue | Page title |
All page headers follow the same pattern: JetBrains Mono, 24px, 700 weight, uppercase, 0.1em letter spacing, color-matched text-shadow glow.
---
## Data Models
No data model changes. This redesign is purely presentational — no database schema, API contract, or state shape changes.
---
## Error Handling
No error handling changes. All existing error states, error messages, loading spinners, and fallback UI are preserved. Only their visual styling is updated:
- Error banners use red-tinted backgrounds (`rgba(239,68,68,0.08)`), red borders, AlertCircle icon, and mono font
- Loading spinners use the existing `spin` animation with `var(--accent)` color
- Empty states use the existing pattern with updated token references
---
## Testing Strategy
### Why Property-Based Testing Does Not Apply
This feature is a **pure UI visual redesign**. It changes CSS custom properties, inline style objects, CSS class definitions, and font loading. There are:
- No pure functions with input/output behavior to test
- No data transformations, parsers, or serializers
- No business logic changes
- No state management changes
- No API contract changes
Property-based testing requires universal properties that hold across a wide input space. A visual redesign has no meaningful "for all inputs X, property P(X) holds" statements. The correctness of a visual redesign is verified by visual inspection and snapshot comparison, not by generating random inputs.
### Recommended Testing Approach
**Manual visual verification** (primary):
- Compare each page against the UI kit reference files in `docs/design-system-redesign/ui_kits/`
- Verify token values in browser DevTools (inspect computed styles)
- Check all severity badge colors match the fixed mapping
- Verify font loading (Outfit + JetBrains Mono) in Network tab
- Test hover states, focus rings, transitions, and animations
- Verify scrollbar styling in WebKit browsers
**Snapshot testing** (optional, for regression):
- Capture rendered HTML snapshots of key components before and after migration
- Use React Testing Library's `render()` + inline snapshot assertions
- Focus on structural correctness (correct CSS classes applied, correct inline style values)
**Build verification**:
- `npm run build` in `frontend/` must succeed with zero errors
- No new console warnings related to styling
- No new ESLint warnings
**Cross-browser check**:
- Verify `backdrop-filter: blur()` works in target browsers
- Verify `font-display: swap` prevents FOIT (flash of invisible text)
- Verify webkit scrollbar styling renders correctly
**Incremental verification checklist** (one per migration phase):
1. After token migration: all existing pages render correctly, no broken styles
2. After font loading: Outfit and JetBrains Mono load, `font-display: swap` active
3. After global CSS update: `.intel-card`, `.status-badge`, `.intel-button`, `.intel-input` render correctly
4. After app shell: top bar height, brand mark, nav tabs, drawer, user menu match reference
5. After each page: compare against corresponding UI kit assembly file

View File

@@ -0,0 +1,215 @@
# Requirements Document
## Introduction
This document captures the requirements for a comprehensive visual redesign of the STEAM Security Dashboard frontend. The redesign applies a refined design system extracted to `docs/design-system-redesign/` — evolving the existing dark tactical console aesthetic with an expanded token system, updated typography, refined card surfaces, enhanced severity badges, new layout tokens, proper font loading, a new brand mark, and refined animations. All existing behavior, routes, state management, and API calls are preserved — only presentational JSX, inline styles, and CSS change.
## Glossary
- **Dashboard**: The STEAM Security Dashboard frontend React application served from `frontend/src/`
- **Design_Token_File**: The source-of-truth CSS custom properties file at `docs/design-system-redesign/colors_and_type.css` defining color, typography, spacing, radii, elevation, and motion tokens
- **App_CSS**: The global stylesheet at `frontend/src/App.css` containing CSS variables, utility classes, component classes, and animations
- **UI_Kit**: A self-contained reference implementation in `docs/design-system-redesign/ui_kits/<name>/` consisting of a primitives file (component vocabulary) and a page assembly file (target rendering)
- **Token**: A CSS custom property (e.g., `--intel-accent`, `--sp-4`, `--r-lg`) that encodes a design decision for color, spacing, radius, elevation, or motion
- **App_Shell**: The persistent chrome surrounding page content — top bar, navigation drawer, user menu, and brand mark
- **Page_Component**: A top-level view rendered by the Dashboard — Home (App.js), Reporting, Compliance, Knowledge Base, Exports, or Admin Panel
- **Severity_Badge**: A styled inline element displaying CVE severity level (Critical, High, Medium, Low) with a pulse-glow dot, gradient fill, and tinted border
- **Card_Surface**: A styled container using the diagonal gradient background, sky-blue border, and layered shadow treatment defined in the design system
- **Inline_Style_Object**: A JavaScript object constant defined in a component file and passed to the `style` prop of a React element
- **Google_Fonts_CDN**: The external font service at `fonts.googleapis.com` used to load Outfit and JetBrains Mono typefaces
## Requirements
### Requirement 1: Port Design Tokens to App.css
**User Story:** As a developer, I want the new design tokens ported into App.css, so that all components can reference a single source of truth for colors, typography, spacing, radii, elevation, and motion values.
#### Acceptance Criteria
1. WHEN the Dashboard loads, THE App_CSS SHALL define all CSS custom properties present in the Design_Token_File within the `:root` block, including surface colors, foreground colors, border tokens, brand accent tokens, semantic severity tokens, severity text tokens, severity fill tokens, group badge tokens, font families, type scale, line heights, font weights, letter spacing, spacing scale, radii, elevation shadows, severity glows, heading glow, motion easings, motion durations, and layout tokens
2. WHEN the Dashboard loads, THE App_CSS SHALL preserve all existing CSS custom properties that are not superseded by the Design_Token_File tokens
3. WHEN the Dashboard loads, THE App_CSS SHALL include the alias tokens defined in the Design_Token_File (e.g., `--bg-page`, `--bg-surface`, `--fg-1`, `--fg-2`, `--border-1`, `--accent`, `--sev-critical`) so that components can use either the canonical or alias form
4. WHEN the Dashboard loads, THE App_CSS SHALL define the `--font-ui` and `--font-mono` custom properties matching the Design_Token_File values (`'Outfit', system-ui, -apple-system, sans-serif` and `'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace`)
5. WHEN the Dashboard loads, THE App_CSS SHALL define the spacing scale tokens (`--sp-1` through `--sp-12`) matching the 4px grid from the Design_Token_File
6. WHEN the Dashboard loads, THE App_CSS SHALL define the radii tokens (`--r-xs` through `--r-pill`) matching the Design_Token_File
7. WHEN the Dashboard loads, THE App_CSS SHALL define the elevation tokens (`--shadow-rest`, `--shadow-card`, `--shadow-card-hover`, `--shadow-popover`, `--shadow-modal`, `--shadow-focus`) matching the Design_Token_File
8. WHEN the Dashboard loads, THE App_CSS SHALL define the motion tokens (`--ease-out`, `--ease-in-out`, `--dur-fast`, `--dur-med`, `--dur-slow`) matching the Design_Token_File
9. WHEN the Dashboard loads, THE App_CSS SHALL define the layout tokens (`--topbar-h`, `--drawer-w`, `--panel-w`, `--content-max`, `--z-topbar`, `--z-drawer`, `--z-modal`, `--z-tooltip`) matching the Design_Token_File
### Requirement 2: Load Fonts via Google Fonts CDN
**User Story:** As a user, I want the Dashboard to load Outfit and JetBrains Mono from Google Fonts CDN, so that typography renders consistently with the design system specification.
#### Acceptance Criteria
1. WHEN the Dashboard loads, THE Dashboard SHALL import Outfit (weights 300, 400, 500, 600, 700, 800) and JetBrains Mono (weights 400, 500, 600, 700) from Google_Fonts_CDN
2. WHEN the Dashboard loads, THE App_CSS SHALL set the default font-family on the universal selector (`*`) to `var(--font-ui)` referencing the Outfit font stack
3. WHEN the Dashboard loads, THE Dashboard SHALL apply `font-display: swap` to prevent invisible text during font loading
### Requirement 3: Update Global CSS Classes and Animations
**User Story:** As a developer, I want the global CSS classes in App.css updated to match the new design token values, so that components using class-based styling reflect the redesigned visual language.
#### Acceptance Criteria
1. WHEN a component applies the `intel-card` class, THE App_CSS SHALL render the card with the diagonal gradient background, 1.5px sky-blue border at 0.30 alpha, 8px border-radius, and the `--shadow-card` elevation token
2. WHEN a user hovers over an element with the `intel-card` class, THE App_CSS SHALL increase the border opacity to 0.50, apply `translateY(-2px)`, apply the `--shadow-card-hover` elevation, and sweep a sky-blue shimmer from left to right over 500ms
3. WHEN a component applies the `status-badge` class, THE App_CSS SHALL render the badge with JetBrains Mono font, 0.75rem size, 700 weight, uppercase text, 0.5px letter spacing, 6px border-radius, 2px solid border, and an 8px pulse-glow dot using the `pulse-glow` keyframe animation at 2s interval
4. WHEN a component applies the `intel-button` class, THE App_CSS SHALL render the button with JetBrains Mono font, 600 weight, uppercase text, 0.5px letter spacing, 6px border-radius, and the circular ripple hover effect expanding to 300px
5. WHEN a component applies the `intel-input` class, THE App_CSS SHALL render the input with `var(--bg-input)` background, `var(--border-subtle)` border, 6px border-radius, and on focus apply `var(--border-focus)` border color with `var(--shadow-focus)` ring
6. WHEN a component applies the `stat-card` class, THE App_CSS SHALL render the card with the diagonal gradient, 8px border-radius, a 2px top-edge gradient rail (`linear-gradient(90deg, transparent, #0EA5E9, transparent)`), and the `--shadow-card` elevation
7. WHEN a component applies the `modal-overlay` class, THE App_CSS SHALL render the overlay with `var(--bg-overlay)` background and `backdrop-filter: blur(12px)`
8. THE App_CSS SHALL define the `pulse-glow`, `spin`, `fade-in`, and `scan` keyframe animations matching the Design_Token_File definitions
9. THE App_CSS SHALL define the semantic type utility classes (`t-display`, `t-h1`, `t-h2`, `t-h3`, `t-body`, `t-sm`, `t-meta`, `t-label`, `t-mono`, `t-mono-sm`, `t-code`) matching the Design_Token_File definitions
10. THE App_CSS SHALL define the `*:focus-visible` rule applying `var(--border-focus)` border color and `var(--shadow-focus)` box-shadow with no outline
### Requirement 4: Redesign the App Shell
**User Story:** As a user, I want the top bar, navigation drawer, and user menu to match the new design system, so that the persistent application chrome is visually consistent with the redesigned pages.
#### Acceptance Criteria
1. WHEN the Dashboard loads, THE App_Shell SHALL render a top bar with `var(--topbar-h)` height (64px), `var(--bg-surface)` background, `var(--border-subtle)` bottom border, and `var(--z-topbar)` z-index
2. WHEN the Dashboard loads, THE App_Shell SHALL render the brand mark as a typographic stack with "STEAM" in Outfit 700 weight at 15px and "SECURITY" in Outfit 500 weight at 9px with 0.18em letter spacing, accompanied by a sky-blue Shield icon
3. WHEN the Dashboard loads, THE App_Shell SHALL render navigation tabs in the top bar for Home, Reporting, Compliance, Knowledge Base, and Exports using Outfit font at 13px with 500 weight (600 weight when active)
4. WHEN a user selects a navigation tab, THE App_Shell SHALL highlight the active tab with `var(--accent)` text color and `var(--accent-soft)` background
5. WHEN a user clicks the menu icon, THE App_Shell SHALL open a navigation drawer from the left with `var(--drawer-w)` width (240px), `var(--bg-surface)` background, `var(--border-subtle)` right border, and `var(--z-drawer)` z-index
6. WHEN the navigation drawer is open, THE App_Shell SHALL render a semi-transparent overlay behind the drawer with `var(--bg-overlay)` background and `backdrop-filter: blur(4px)`
7. WHEN the Dashboard loads, THE App_Shell SHALL render the user menu button with a circular avatar showing the user's initials in `var(--accent)` color on `var(--accent-soft)` background, the user's name, and a chevron indicator
8. WHEN a user clicks the user menu button, THE App_Shell SHALL display a dropdown with `var(--bg-surface)` background, `var(--border-subtle)` border, `var(--shadow-popover)` elevation, and `var(--z-drawer)` z-index, showing the user's name, email, group badge, and menu items
### Requirement 5: Redesign the Home Page (App.js)
**User Story:** As a user, I want the Home page to match the new design system, so that the CVE list, stat cards, filters, calendar widget, and right-rail panels reflect the refined visual language.
#### Acceptance Criteria
1. WHEN the Home page loads, THE Dashboard SHALL render stat cards at the top of the page using the Card_Surface treatment with a 2px top-edge gradient rail, color-coded borders (sky for neutral, amber for attention, red for critical), and the `--shadow-card` elevation with severity-tinted glow
2. WHEN the Home page loads, THE Dashboard SHALL render the page title in JetBrains Mono, 24px, 700 weight, sky-blue color, uppercase, with 0.1em letter spacing and the heading glow text-shadow
3. WHEN the Home page loads, THE Dashboard SHALL render CVE row cards using the Card_Surface treatment with 1.5px sky-blue border at 0.12 alpha, 8px border-radius, and a chevron toggle that rotates from -90deg (collapsed) to 0deg (expanded)
4. WHEN a user expands a CVE row, THE Dashboard SHALL display the full description, severity badge with pulse-glow dot, vendor count, document count, and status labels, with vendor entry sub-cards using the nested Card_Surface gradient
5. WHEN the Home page loads, THE Dashboard SHALL render the Quick Lookup section as a Card_Surface with sky-blue identity, containing search input with icon, filter controls, and result banners using tone-coded backgrounds (success green, warning amber, error red)
6. WHEN the Home page loads, THE Dashboard SHALL render the calendar widget with JetBrains Mono font, sky-blue highlight for the current day, severity-colored dots for marked dates, and navigation buttons with sky-blue borders
7. WHEN the Home page loads, THE Dashboard SHALL render right-rail panels (Open Tickets, Archer, Ivanti) as Card_Surface containers with left-rail color accents (amber for tickets, purple for Archer, teal for Ivanti), BigStat centered counts, and scrollable MiniTicket lists
8. WHEN the Home page loads, THE Dashboard SHALL render filter controls using the redesigned input and select styles with `var(--bg-input)` background, sky-blue focus borders, and JetBrains Mono font for data fields
### Requirement 6: Redesign the Reporting Page
**User Story:** As a user, I want the Reporting page to match the new design system, so that the findings table, charts, filters, and toolbar reflect the refined visual language.
#### Acceptance Criteria
1. WHEN the Reporting page loads, THE Dashboard SHALL render the page header with "REPORTING" in JetBrains Mono, 24px, 700 weight, green (#10B981) color, uppercase, with 0.1em letter spacing and green glow text-shadow
2. WHEN the Reporting page loads, THE Dashboard SHALL render the Sync button as a green tinted-fill primary variant and secondary action buttons (Atlas, Export, Queue, Column manager) as sky-blue outlined or tinted-fill variants
3. WHEN the Reporting page loads, THE Dashboard SHALL render the findings table panel as a Card_Surface with sky-blue border at 0.12 alpha, containing a toolbar with mono uppercase labels, filter chips in amber, and pill tabs for Ivanti/Atlas views
4. WHEN the Reporting page loads, THE Dashboard SHALL render table rows with `var(--border-subtle)` bottom borders, severity dots with 7px diameter and colored glow, SLA pills with pill-radius and tinted backgrounds, and workflow badges with 4px radius and tinted borders
5. WHEN a user hovers over a table row, THE Dashboard SHALL apply a `rgba(0,217,255,0.06)` background wash and `0 2px 8px rgba(0,217,255,0.10)` sub-shadow
6. WHEN the Reporting page loads, THE Dashboard SHALL render chart panels as Card_Surface containers with sky-blue borders, mono uppercase title labels, and donut charts using the severity color palette
7. WHEN an error occurs during sync, THE Dashboard SHALL display a status banner with red-tinted background, red border, AlertCircle icon, and mono font error message
### Requirement 7: Redesign the Compliance Page
**User Story:** As a user, I want the Compliance page to match the new design system, so that the metric health cards, device table, charts, and team tabs reflect the teal-accented visual language.
#### Acceptance Criteria
1. WHEN the Compliance page loads, THE Dashboard SHALL render the page header with "AEO COMPLIANCE" in JetBrains Mono, 24px, 700 weight, teal (#14B8A6) color, uppercase, with 0.1em letter spacing and teal glow text-shadow
2. WHEN the Compliance page loads, THE Dashboard SHALL render team tabs (STEAM, ACCESS-ENG) with teal-tinted active state, mono uppercase labels, and 6px border-radius
3. WHEN the Compliance page loads, THE Dashboard SHALL render metric health cards as clickable Card_Surface containers with status-colored borders (green for meeting target, amber for within 15%, red for below 15%), variant pills showing compliance percentages, and a status ribbon at the bottom
4. WHEN a user clicks a metric health card, THE Dashboard SHALL highlight the active card with a status-colored background fill at 0.15 alpha and a solid status-colored border
5. WHEN the Compliance page loads, THE Dashboard SHALL render the device table with teal-tinted borders at 0.15 alpha, mono uppercase column headers, hostname/IP in JetBrains Mono, category-colored metric badges, escalating seen-count badges (slate for 1, amber for 23, red for 4+), and a teal-accented search input
6. WHEN a user hovers over a device row, THE Dashboard SHALL apply a subtle white-alpha background wash and highlight the selected row with a 2px teal left border
7. WHEN the Compliance page loads, THE Dashboard SHALL render chart cards with teal-tinted borders, mono uppercase titles, and the standard Card_Surface gradient background
8. WHEN an admin triggers a rollback, THE Dashboard SHALL display a centered modal with red-tinted border, red mono uppercase title, dark recessed file label, and danger-styled confirm button
### Requirement 8: Redesign the Knowledge Base Page
**User Story:** As a user, I want the Knowledge Base page to match the new design system, so that the document library, viewer, and search interface reflect the refined visual language.
#### Acceptance Criteria
1. WHEN the Knowledge Base page loads, THE Dashboard SHALL render the page header following the same mono uppercase glow pattern used by other pages, with sky-blue or green identity color
2. WHEN the Knowledge Base page loads, THE Dashboard SHALL render document list items using the recessed Card_Surface treatment with `inset 0 2px 4px rgba(0,0,0,0.3)` shadow, sky-blue borders at 0.20 alpha, and hover state increasing border opacity to 0.35
3. WHEN the Knowledge Base page loads, THE Dashboard SHALL render the document viewer with markdown content styled according to the App_CSS `.markdown-content` rules — h1 in sky-blue, h2 in emerald, h3 in amber, code blocks with dark recessed background, and blockquotes with sky-blue left border
4. WHEN the Knowledge Base page loads, THE Dashboard SHALL render action buttons (upload, create, view) using the redesigned button variants with mono uppercase labels and tinted-fill backgrounds
### Requirement 9: Redesign Shared Components
**User Story:** As a developer, I want the shared components (LoginForm, CalendarWidget, UserManagement, AuditLog, NvdSyncModal, KnowledgeBaseModal, KnowledgeBaseViewer) to match the new design system, so that every surface in the application is visually consistent.
#### Acceptance Criteria
1. WHEN the LoginForm renders, THE Dashboard SHALL style the login form using Card_Surface treatment, redesigned input fields with `var(--bg-input)` background and sky-blue focus rings, and the primary button variant
2. WHEN a modal opens, THE Dashboard SHALL render the modal overlay with `var(--bg-overlay)` background and `backdrop-filter: blur(12px)`, and the modal card with `var(--shadow-modal)` elevation and 12px border-radius
3. WHEN the UserManagement component renders, THE Dashboard SHALL style group badges using the token-based group colors (`--group-admin` red, `--group-standard` sky-blue, `--group-leadership` amber, `--group-readonly` grey) with pill-radius and tinted backgrounds
4. WHEN the AuditLog component renders, THE Dashboard SHALL style log entries using the data-row treatment with `var(--border-subtle)` bottom borders, mono font for timestamps and action types, and hover state with sky-blue background wash
5. WHEN the NvdSyncModal renders, THE Dashboard SHALL style the modal content using Card_Surface treatment with the standard modal elevation and redesigned button variants
6. WHEN the CalendarWidget renders, THE Dashboard SHALL style the calendar with JetBrains Mono font, sky-blue current-day highlight with 1px border, severity-colored date markers, and navigation buttons with sky-blue borders
### Requirement 10: Redesign the Exports Page
**User Story:** As a user, I want the Exports page to match the new design system, so that the export tools interface is visually consistent with the rest of the application.
#### Acceptance Criteria
1. WHEN the Exports page loads, THE Dashboard SHALL render the page header following the mono uppercase glow pattern with appropriate identity color
2. WHEN the Exports page loads, THE Dashboard SHALL render export action cards using the Card_Surface treatment with sky-blue borders and the redesigned button variants for export triggers
### Requirement 11: Preserve Existing Behavior
**User Story:** As a user, I want all existing functionality to continue working after the redesign, so that the visual update does not break any workflows.
#### Acceptance Criteria
1. THE Dashboard SHALL preserve all existing page navigation routes and state management logic without modification
2. THE Dashboard SHALL preserve all existing API calls, request parameters, response handling, and error handling without modification
3. THE Dashboard SHALL preserve all existing user interactions — click handlers, form submissions, modal open/close, expand/collapse, drag-and-drop, inline editing — without modification
4. THE Dashboard SHALL preserve all existing role-based access control checks and conditional rendering logic without modification
5. THE Dashboard SHALL preserve all existing data display logic — filtering, sorting, searching, pagination — without modification
### Requirement 12: No New Dependencies
**User Story:** As a developer, I want the redesign to use only existing dependencies, so that the bundle size and dependency surface area remain unchanged.
#### Acceptance Criteria
1. THE Dashboard SHALL use only React, lucide-react, recharts, react-markdown, rehype-sanitize, mermaid, and xlsx as frontend dependencies — no new libraries shall be added
2. THE Dashboard SHALL load Outfit and JetBrains Mono fonts exclusively from Google_Fonts_CDN — no bundled font files shall be added
### Requirement 13: Incremental Migration Approach
**User Story:** As a developer, I want the redesign applied incrementally (tokens first, then page-by-page), so that changes can be verified in isolation and big-bang breakage is avoided.
#### Acceptance Criteria
1. WHEN the token migration is complete, THE App_CSS SHALL be fully functional with both old and new token names available, so that components can be migrated one at a time without breaking unmigrated components
2. WHEN a Page_Component is migrated, THE Dashboard SHALL render the migrated page using the new design tokens and styles while unmigrated pages continue to render correctly using the existing styles
### Requirement 14: Severity Color Mapping Preservation
**User Story:** As a user, I want severity colors to remain semantically fixed, so that Critical is always red, High is always amber, Medium is always sky-blue, and Low is always emerald across every component.
#### Acceptance Criteria
1. THE Dashboard SHALL render Critical severity indicators using `#EF4444` (border/dot), `rgba(239,68,68,0.20)` (fill), and `#FCA5A5` (text) across all Severity_Badge instances, status badges, chart segments, and inline severity references
2. THE Dashboard SHALL render High severity indicators using `#F59E0B` (border/dot), `rgba(245,158,11,0.20)` (fill), and `#FCD34D` (text) across all severity-displaying components
3. THE Dashboard SHALL render Medium severity indicators using `#0EA5E9` (border/dot), `rgba(14,165,233,0.20)` (fill), and `#7DD3FC` (text) across all severity-displaying components
4. THE Dashboard SHALL render Low severity indicators using `#10B981` (border/dot), `rgba(16,185,129,0.20)` (fill), and `#6EE7B7` (text) across all severity-displaying components
### Requirement 15: Brand Mark and Asset Integration
**User Story:** As a user, I want the new STEAM brand mark and severity icons available in the application, so that the visual identity is complete.
#### Acceptance Criteria
1. WHEN the Dashboard loads, THE App_Shell SHALL display the STEAM brand mark as a typographic stack with a Shield icon, matching the `assets/logo.svg` reference — not the previous `AtlasIcon` custom SVG
2. WHEN the Dashboard renders severity icons, THE Dashboard SHALL use the severity icon SVGs from `docs/design-system-redesign/assets/` or equivalent inline SVG representations matching the design system specification
### Requirement 16: Scrollbar and Focus Styling
**User Story:** As a user, I want scrollbars and focus indicators to match the new design system, so that these browser-level affordances are visually integrated.
#### Acceptance Criteria
1. THE App_CSS SHALL style webkit scrollbars with 8px width, `var(--intel-dark)` track background, and `rgba(14,165,233,0.3)` thumb with 4px border-radius, increasing to `rgba(14,165,233,0.5)` on hover
2. THE App_CSS SHALL apply `focus-visible` styling with `var(--border-focus)` border color and `var(--shadow-focus)` box-shadow to all focusable elements, with no outline

View File

@@ -0,0 +1,243 @@
# Implementation Plan: Dashboard Redesign
## Overview
This plan migrates the STEAM Security Dashboard frontend to the refined design system defined in `docs/design-system-redesign/`. The migration is purely presentational — no behavior, routing, state management, or API changes. Each phase is independently verifiable with `npm run build` in `frontend/`. The 10-phase order ensures tokens and global styles land first, then pages migrate one at a time without breaking unmigrated components.
Key references:
- Design tokens source: `docs/design-system-redesign/colors_and_type.css`
- UI kit primitives: `docs/design-system-redesign/ui_kits/cve-dashboard/Primitives.jsx`, `AppShell.jsx`
- Home primitives: `docs/design-system-redesign/ui_kits/home/HomePrimitives.jsx`
- Reporting primitives: `docs/design-system-redesign/ui_kits/reporting/ReportPrimitives.jsx`
- Compliance primitives: `docs/design-system-redesign/ui_kits/compliance/CompPrimitives.jsx`
## Tasks
- [x] 1. Phase 1 — Port design tokens to App.css
- [x] 1.1 Add all new CSS custom properties to the `:root` block in `frontend/src/App.css`
- Merge every token from `docs/design-system-redesign/colors_and_type.css` into the existing `:root` block
- Add surface aliases (`--bg-page`, `--bg-surface`, `--bg-elevated`, `--bg-hover`, `--bg-input`, `--bg-overlay`)
- Add foreground aliases (`--fg-1`, `--fg-2`, `--fg-muted`, `--fg-disabled`, `--fg-3`, `--fg-on-accent`)
- Add border tokens (`--border-subtle`, `--border-default`, `--border-strong`, `--border-focus`, `--border-1`, `--border-2`, `--border-3`)
- Add brand accent variants (`--intel-accent-bright`, `--intel-accent-soft`, `--intel-accent-15`, `--intel-accent-08`, `--accent`, `--accent-bright`, `--accent-soft`, `--accent-wash`, `--accent-hover`)
- Add severity semantic tokens (`--sev-critical`, `--sev-high`, `--sev-medium`, `--sev-low`), severity text tokens (`--sev-critical-text`, `--sev-high-text`, `--sev-medium-text`, `--sev-low-text`), and severity fill tokens (`--sev-critical-bg`, `--sev-high-bg`, `--sev-medium-bg`, `--sev-low-bg`)
- Add group badge tokens (`--group-admin`, `--group-standard`, `--group-leadership`, `--group-readonly`)
- Add font family tokens (`--font-ui`, `--font-mono`)
- Add type scale tokens (`--fs-display` through `--fs-tiny`), line height tokens (`--lh-tight`, `--lh-normal`, `--lh-loose`), font weight tokens (`--fw-regular` through `--fw-bold`), and letter spacing tokens (`--tracking-wide`, `--tracking-wider`)
- Add spacing scale tokens (`--sp-1` through `--sp-12`)
- Add radii tokens (`--r-xs` through `--r-pill`)
- Add elevation tokens (`--shadow-rest`, `--shadow-card`, `--shadow-card-hover`, `--shadow-popover`, `--shadow-modal`, `--shadow-focus`)
- Add severity glow tokens (`--glow-danger`, `--glow-warning`, `--glow-info`, `--glow-success`) and heading glow (`--glow-heading`)
- Add motion tokens (`--ease-out`, `--ease-in-out`, `--dur-fast`, `--dur-med`, `--dur-slow`)
- Add layout tokens (`--topbar-h`, `--drawer-w`, `--panel-w`, `--content-max`, `--z-topbar`, `--z-drawer`, `--z-modal`, `--z-tooltip`)
- Preserve all existing CSS custom properties that are not superseded
- Update the universal selector `*` to use `font-family: var(--font-ui)`
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9_
- [x] 2. Phase 2 — Load fonts via Google Fonts CDN
- [x] 2.1 Update the Google Fonts `<link>` tag in `frontend/public/index.html`
- Ensure Outfit loads weights 300, 400, 500, 600, 700, 800
- Ensure JetBrains Mono loads weights 400, 500, 600, 700
- Ensure `display=swap` is present to prevent invisible text during font loading
- _Requirements: 2.1, 2.3_
- [x] 3. Phase 3 — Update global CSS classes and animations in App.css
- [x] 3.1 Update existing component classes to reference new design tokens
- Update `.intel-card` to use `var(--shadow-card)` and `var(--shadow-card-hover)` elevation tokens, 8px border-radius, and the shimmer sweep on hover over 500ms
- Update `.status-badge` to use `var(--font-mono)`, 0.75rem size, 700 weight, uppercase, 0.5px letter spacing, 6px border-radius, 2px solid border, and `pulse-glow` animation at 2s interval
- Update `.intel-button` to use `var(--font-mono)`, 600 weight, uppercase, 0.5px letter spacing, 6px border-radius, and the circular ripple hover effect expanding to 300px
- Update `.intel-input` to use `var(--bg-input)` background, `var(--border-subtle)` border, 6px border-radius, and on focus apply `var(--border-focus)` border color with `var(--shadow-focus)` ring
- Update `.stat-card` to use the diagonal gradient, 8px border-radius, 2px top-edge gradient rail, and `var(--shadow-card)` elevation
- Update `.modal-overlay` to use `var(--bg-overlay)` background and `backdrop-filter: blur(12px)`
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7_
- [x] 3.2 Update keyframe animations to match design token definitions
- Update `pulse-glow`, `spin`, `fade-in`, and `scan` keyframes to match `colors_and_type.css` definitions
- _Requirements: 3.8_
- [x] 3.3 Add semantic type utility classes
- Add `.t-display`, `.t-h1`, `.t-h2`, `.t-h3`, `.t-body`, `.t-sm`, `.t-meta`, `.t-label`, `.t-mono`, `.t-mono-sm`, `.t-code` classes matching the `colors_and_type.css` definitions
- _Requirements: 3.9_
- [x] 3.4 Add `*:focus-visible` rule and update scrollbar styling
- Add `*:focus-visible` rule applying `var(--border-focus)` border color and `var(--shadow-focus)` box-shadow with no outline
- Update webkit scrollbar styling to use `var(--intel-dark)` track, `rgba(14,165,233,0.3)` thumb with 4px border-radius, and `rgba(14,165,233,0.5)` on hover
- _Requirements: 3.10, 16.1, 16.2_
- [x] 4. Checkpoint — Verify token migration and global CSS
- Run `npm run build` in `frontend/` to confirm zero errors
- Verify all existing pages still render correctly with both old and new token names available
- Ensure all tests pass, ask the user if questions arise.
- _Requirements: 13.1_
- [x] 5. Phase 4 — Redesign the App Shell
- [x] 5.1 Update the top bar styles in `frontend/src/App.js`
- Set top bar to `var(--topbar-h)` height (64px), `var(--bg-surface)` background, `var(--border-subtle)` bottom border, `var(--z-topbar)` z-index
- Replace the brand mark with a typographic stack: "STEAM" in Outfit 700 at 15px, "SECURITY" in Outfit 500 at 9px with 0.18em letter spacing, Shield icon in `var(--accent)` color — matching `AppShell.jsx` reference
- Update navigation tabs to Outfit 13px, 500 weight (600 active), active state uses `var(--accent)` text + `var(--accent-soft)` background — matching `NavTab` in `AppShell.jsx`
- _Requirements: 4.1, 4.2, 4.3, 4.4, 15.1_
- [x] 5.2 Update `frontend/src/components/NavDrawer.js` inline styles
- Set drawer to `var(--drawer-w)` width (240px), `var(--bg-surface)` background, `var(--border-subtle)` right border, `var(--z-drawer)` z-index
- Set overlay to `var(--bg-overlay)` background with `backdrop-filter: blur(4px)` — matching `NavDrawer` in `AppShell.jsx`
- Update drawer items to match `DrawerItem` pattern: Outfit font, 13px, 500 weight (600 active), active uses `var(--accent)` text + `var(--accent-soft)` background
- _Requirements: 4.5, 4.6_
- [x] 5.3 Update `frontend/src/components/UserMenu.js` inline styles
- Update avatar to circular with initials in `var(--accent)` on `var(--accent-soft)` background — matching `UserMenu` in `AppShell.jsx`
- Update dropdown to `var(--bg-surface)` background, `var(--border-subtle)` border, `var(--shadow-popover)` elevation, `var(--z-drawer)` z-index
- Include user name, email, group badge, and menu items in dropdown — matching `AppShell.jsx` reference
- _Requirements: 4.7, 4.8_
- [x] 6. Phase 5 — Redesign the Home Page
- [x] 6.1 Update stat card styles in `frontend/src/App.js`
- Apply Card_Surface treatment with 2px top-edge gradient rail
- Color-code borders: sky for neutral, amber for attention, red for critical
- Apply `var(--shadow-card)` elevation with severity-tinted glow — matching `StatCard` in `HomePrimitives.jsx`
- _Requirements: 5.1_
- [x] 6.2 Update page title and CVE row styles in `frontend/src/App.js`
- Set page title to JetBrains Mono, 24px, 700 weight, sky-blue, uppercase, 0.1em letter spacing, heading glow text-shadow
- Update CVE row cards to Card_Surface treatment with 1.5px sky-blue border at 0.12 alpha, 8px border-radius, chevron toggle rotating from -90deg to 0deg — matching `CVERow` in `HomePrimitives.jsx`
- Update expanded CVE row content: severity badge with pulse-glow dot, vendor count, doc count, status labels
- Update vendor entry sub-cards to nested Card_Surface gradient — matching `VendorEntry` in `HomePrimitives.jsx`
- _Requirements: 5.2, 5.3, 5.4_
- [x] 6.3 Update Quick Lookup section styles in `frontend/src/App.js`
- Apply Card_Surface with sky-blue identity
- Update search input with icon, filter controls — matching `HomeInput` in `HomePrimitives.jsx`
- Update result banners with tone-coded backgrounds (success green, warning amber, error red) — matching `ResultBanner` in `HomePrimitives.jsx`
- _Requirements: 5.5_
- [x] 6.4 Update calendar widget and right-rail panel styles in `frontend/src/App.js`
- Update calendar to JetBrains Mono font, sky-blue current-day highlight, severity-colored dots, navigation buttons with sky-blue borders — matching `CalendarMini` in `HomePrimitives.jsx`
- Update right-rail panels (Open Tickets, Archer, Ivanti) as Card_Surface containers with left-rail color accents (amber, purple, teal), BigStat centered counts, scrollable MiniTicket lists — matching `HomeCard`, `BigStat`, `MiniTicket` in `HomePrimitives.jsx`
- _Requirements: 5.6, 5.7_
- [x] 6.5 Update filter control styles in `frontend/src/App.js`
- Update inputs and selects to `var(--bg-input)` background, sky-blue focus borders, JetBrains Mono font — matching `HomeInput`, `HomeSelect` in `HomePrimitives.jsx`
- _Requirements: 5.8_
- [x] 7. Checkpoint — Verify App Shell and Home Page
- Run `npm run build` in `frontend/` to confirm zero errors
- Ensure all tests pass, ask the user if questions arise.
- _Requirements: 13.2_
- [x] 8. Phase 6 — Redesign the Reporting Page
- [x] 8.1 Update page header and button styles in `frontend/src/components/pages/ReportingPage.js`
- Set header to "REPORTING" in JetBrains Mono, 24px, 700 weight, green (#10B981), uppercase, 0.1em letter spacing, green glow text-shadow — matching `PageHeader` in `ReportPrimitives.jsx`
- Update Sync button to green tinted-fill primary variant — matching `RptButton` primary in `ReportPrimitives.jsx`
- Update secondary buttons (Atlas, Export, Queue, Column manager) to sky-blue outlined or tinted-fill variants — matching `RptButton` neutral/subtle in `ReportPrimitives.jsx`
- _Requirements: 6.1, 6.2_
- [x] 8.2 Update findings table panel and toolbar styles in `frontend/src/components/pages/ReportingPage.js`
- Apply Card_Surface with sky-blue border at 0.12 alpha — matching `KbCard` in `ReportPrimitives.jsx`
- Update toolbar with mono uppercase labels, filter chips in amber, pill tabs for Ivanti/Atlas views — matching `ToolbarLabel`, `FilterChip`, `PillTab` in `ReportPrimitives.jsx`
- _Requirements: 6.3_
- [x] 8.3 Update table row and cell styles in `frontend/src/components/pages/ReportingPage.js`
- Update rows with `var(--border-subtle)` bottom borders
- Update severity dots to 7px diameter with colored glow — matching `SeverityDot` in `ReportPrimitives.jsx`
- Update SLA pills with pill-radius and tinted backgrounds — matching `SlaPill` in `ReportPrimitives.jsx`
- Update workflow badges with 4px radius and tinted borders — matching `WorkflowBadge` in `ReportPrimitives.jsx`
- Apply hover state: `rgba(0,217,255,0.06)` background wash and `0 2px 8px rgba(0,217,255,0.10)` sub-shadow
- _Requirements: 6.4, 6.5_
- [x] 8.4 Update chart panels and error banner styles in `frontend/src/components/pages/ReportingPage.js`
- Update chart panels to Card_Surface with sky-blue borders, mono uppercase title labels — matching `KbCard` in `ReportPrimitives.jsx`
- Update donut charts to use severity color palette
- Update error status banner to red-tinted background, red border, AlertCircle icon, mono font — matching `StatusBanner` in `ReportPrimitives.jsx`
- _Requirements: 6.6, 6.7_
- [x] 9. Phase 7 — Redesign the Compliance Page
- [x] 9.1 Update page header and team tabs in `frontend/src/components/pages/CompliancePage.js`
- Set header to "AEO COMPLIANCE" in JetBrains Mono, 24px, 700 weight, teal (#14B8A6), uppercase, 0.1em letter spacing, teal glow text-shadow — matching `CompPageHeader` in `CompPrimitives.jsx`
- Update team tabs (STEAM, ACCESS-ENG) with teal-tinted active state, mono uppercase labels, 6px border-radius — matching `TeamTabs` in `CompPrimitives.jsx`
- _Requirements: 7.1, 7.2_
- [x] 9.2 Update metric health cards in `frontend/src/components/pages/CompliancePage.js`
- Apply Card_Surface with status-colored borders (green for meeting, amber for within 15%, red for below 15%)
- Add variant pills showing compliance percentages — matching `MetricHealthCard`, `VariantPill` in `CompPrimitives.jsx`
- Add status ribbon at bottom — matching `StatusRibbon` in `CompPrimitives.jsx`
- Highlight active card with status-colored background fill at 0.15 alpha and solid border
- _Requirements: 7.3, 7.4_
- [x] 9.3 Update device table styles in `frontend/src/components/pages/CompliancePage.js`
- Apply teal-tinted borders at 0.15 alpha
- Update column headers to mono uppercase
- Update hostname/IP to JetBrains Mono
- Add category-colored metric badges — matching `MetricBadge` in `CompPrimitives.jsx`
- Add escalating seen-count badges (slate for 1, amber for 23, red for 4+) — matching `SeenBadge` in `CompPrimitives.jsx`
- Add teal-accented search input — matching `CompSearchInput` in `CompPrimitives.jsx`
- Apply hover state with white-alpha background wash and selected row with 2px teal left border — matching `DeviceRow` in `CompPrimitives.jsx`
- _Requirements: 7.5, 7.6_
- [x] 9.4 Update chart cards in `frontend/src/components/pages/CompliancePage.js`
- Apply teal-tinted borders, mono uppercase titles, Card_Surface gradient background — matching `ChartCard` in `CompPrimitives.jsx`
- _Requirements: 7.7_
- [x] 9.5 Update `frontend/src/components/pages/ComplianceUploadModal.js` styles
- Update modal overlay, card, and buttons to match design system tokens
- _Requirements: 9.2_
- [x] 9.6 Update `frontend/src/components/pages/ComplianceDetailPanel.js` styles
- Update panel chrome and data rows to use design tokens
- _Requirements: 7.5_
- [x] 9.7 Update `frontend/src/components/pages/ComplianceChartsPanel.js` styles
- Update chart card wrappers and teal borders to use design tokens
- _Requirements: 7.7_
- [x] 9.8 Update rollback modal in `frontend/src/components/pages/CompliancePage.js`
- Apply centered modal with red-tinted border, red mono uppercase title, dark recessed file label, danger-styled confirm button — matching `RollbackDialog` in `CompPrimitives.jsx`
- _Requirements: 7.8_
- [x] 10. Checkpoint — Verify Reporting and Compliance Pages
- Run `npm run build` in `frontend/` to confirm zero errors
- Ensure all tests pass, ask the user if questions arise.
- [x] 11. Phase 8 — Redesign the Knowledge Base Page
- [x] 11.1 Update `frontend/src/components/pages/KnowledgeBasePage.js` styles
- Set page header to mono uppercase glow pattern with sky-blue or green identity color
- Update document list items to recessed Card_Surface treatment with `inset 0 2px 4px rgba(0,0,0,0.3)` shadow, sky-blue borders at 0.20 alpha, hover state increasing border opacity to 0.35
- Update action buttons (upload, create, view) to redesigned button variants with mono uppercase labels and tinted-fill backgrounds
- _Requirements: 8.1, 8.2, 8.4_
- [x] 11.2 Update `frontend/src/components/KnowledgeBaseModal.js` styles
- Update modal chrome and form inputs to use design tokens
- Apply `var(--bg-overlay)` overlay, `var(--shadow-modal)` elevation, 12px border-radius
- _Requirements: 9.2_
- [x] 11.3 Update `frontend/src/components/KnowledgeBaseViewer.js` styles
- Update viewer chrome and markdown content area
- Ensure `.markdown-content` rules in App.css are consistent: h1 sky-blue, h2 emerald, h3 amber, code blocks with dark recessed background, blockquotes with sky-blue left border
- _Requirements: 8.3_
- [x] 12. Phase 9 — Redesign the Exports Page
- [x] 12.1 Update `frontend/src/components/pages/ExportsPage.js` styles
- Set page header to mono uppercase glow pattern with appropriate identity color
- Update export action cards to Card_Surface treatment with sky-blue borders
- Update buttons to redesigned button variants
- _Requirements: 10.1, 10.2_
- [x] 13. Phase 10 — Redesign Shared Components
- [x] 13.1 Update `frontend/src/components/LoginForm.js` styles
- Apply Card_Surface treatment to login form
- Update input fields to `var(--bg-input)` background with sky-blue focus rings
- Update primary button to redesigned variant
- _Requirements: 9.1_
- [x] 13.2 Update `frontend/src/components/CalendarWidget.js` styles
- Apply JetBrains Mono font throughout
- Set sky-blue current-day highlight with 1px border
- Add severity-colored date markers
- Update navigation buttons with sky-blue borders — matching `CalendarMini` in `HomePrimitives.jsx`
- _Requirements: 9.6_
- [x] 13.3 Update `frontend/src/components/UserManagement.js` styles
- Apply group badges using token-based group colors (`--group-admin` red, `--group-standard` sky-blue, `--group-leadership` amber, `--group-readonly` grey) with pill-radius and tinted backgrounds — matching `GroupBadge` in `Primitives.jsx`
- Update table rows and buttons to use design tokens
- _Requirements: 9.3_
- [x] 13.4 Update `frontend/src/components/AuditLog.js` styles
- Apply data-row treatment with `var(--border-subtle)` bottom borders
- Update timestamps and action types to mono font
- Apply hover state with sky-blue background wash
- _Requirements: 9.4_
- [x] 13.5 Update `frontend/src/components/NvdSyncModal.js` styles
- Apply Card_Surface treatment with standard modal elevation
- Update buttons to redesigned variants
- Apply `var(--bg-overlay)` overlay and `var(--shadow-modal)` elevation
- _Requirements: 9.5_
- [x] 14. Final checkpoint — Verify all pages and shared components
- Run `npm run build` in `frontend/` to confirm zero errors
- Verify no new console warnings related to styling
- Ensure all tests pass, ask the user if questions arise.
- _Requirements: 11.1, 11.2, 11.3, 11.4, 11.5, 12.1, 12.2, 14.1, 14.2, 14.3, 14.4_
## Notes
- This is a pure visual redesign. No behavior, routing, state management, or API changes.
- No new dependencies are added. Fonts load from Google Fonts CDN only.
- Each phase is independently verifiable — run `npm run build` after each to confirm no breakage.
- Severity colors are immutable: Critical (#EF4444), High (#F59E0B), Medium (#0EA5E9), Low (#10B981).
- All existing CSS custom properties are preserved alongside new tokens for backward compatibility.
- UI kit reference files in `docs/design-system-redesign/ui_kits/` are the visual source of truth for each component's target styling.
- Property-based testing does not apply to this feature — it is a pure CSS/style migration with no testable pure functions or data transformations.

View File

@@ -0,0 +1 @@
{"specId": "87e99308-c01c-4c51-906a-3b87e0a65d68", "workflowType": "requirements-first", "specType": "bugfix"}

View File

@@ -0,0 +1,61 @@
# Bugfix Requirements Document
## Introduction
The Jira REST API integration in the STEAM Security Dashboard was submitted for production approval and the reviewer identified three compliance violations that block approval. The `searchIssues()` function uses `POST /rest/api/2/search` instead of the required `GET` with query parameters. The `getIssue()` function performs single-issue `GET /rest/api/2/issue/{key}` calls, which are not allowed — all issue fetching must go through JQL search. Additionally, JQL queries do not consistently include `project = <KEY>` scoping, which is required for all search operations. These issues affect `backend/helpers/jiraApi.js`, `backend/scripts/jira-uat-test.js`, and `docs/jira-api-use-cases.md`.
## Bug Analysis
### Current Behavior (Defect)
1.1 WHEN `searchIssues()` is called with a JQL query THEN the system sends a `POST /rest/api/2/search` request with a JSON body containing `{ jql, startAt, maxResults, fields }`, which is not allowed by the reviewer
1.2 WHEN `searchIssuesByKeys()` is called to bulk-fetch issues by key THEN the system sends a `POST /rest/api/2/search` request (via `searchIssues()`) without a `project = <KEY>` clause in the JQL
1.3 WHEN `getIssue()` is called with a single issue key THEN the system sends a `GET /rest/api/2/issue/{key}?fields=...` request, which is a single-issue GET that the reviewer does not allow
1.4 WHEN the UAT test script exercises use case 3 ("Get Single Issue") THEN it calls `getIssue()` which performs the non-compliant single-issue GET pattern
1.5 WHEN the UAT test script exercises use case 8 ("JQL Search") THEN it calls `searchIssues()` which performs the non-compliant POST to `/rest/api/2/search`
1.6 WHEN the API documentation describes the JQL Search use case THEN it lists the endpoint as `POST /rest/api/2/search`, which does not match the required compliant pattern
1.7 WHEN the API documentation describes the "Get Single Issue" and "Issue Lookup" use cases THEN it lists the endpoint as `GET /rest/api/2/issue/{issueKey}?fields=...`, which is the non-compliant single-issue GET pattern
### Expected Behavior (Correct)
2.1 WHEN `searchIssues()` is called with a JQL query THEN the system SHALL send a `GET /rest/api/2/search` request with query parameters `?jql=<encoded-jql>&fields=<comma-separated-fields>&maxResults=1000&startAt=0` instead of a POST with a JSON body
2.2 WHEN `searchIssuesByKeys()` is called to bulk-fetch issues by key THEN the system SHALL include a `project = <JIRA_PROJECT_KEY>` clause in the JQL query alongside the `key in (...)` clause
2.3 WHEN `getIssue()` is called with a single issue key THEN the system SHALL perform a JQL search using `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<JIRA_PROJECT_KEY>&fields=<fields>&maxResults=1` instead of a direct single-issue GET
2.4 WHEN the UAT test script exercises the single-issue fetch use case THEN it SHALL call the refactored `getIssue()` which uses JQL search, and the test name SHALL reflect the compliant pattern
2.5 WHEN the UAT test script exercises the JQL search use case THEN it SHALL call `searchIssues()` which uses `GET /rest/api/2/search` with query parameters, and the JQL SHALL include `project = <JIRA_PROJECT_KEY>` scoping
2.6 WHEN the API documentation describes the JQL Search use case THEN it SHALL list the endpoint as `GET /rest/api/2/search` with query parameters `?jql=`, `&fields=`, `&maxResults=`, `&startAt=`
2.7 WHEN the API documentation describes the single-issue fetch use case THEN it SHALL describe it as a JQL search using `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1` and SHALL NOT reference `GET /rest/api/2/issue/{key}`
### Unchanged Behavior (Regression Prevention)
3.1 WHEN `createIssue()` is called THEN the system SHALL CONTINUE TO send a `POST /rest/api/2/issue` request with the issue fields in the JSON body
3.2 WHEN `updateIssue()` is called THEN the system SHALL CONTINUE TO send a `PUT /rest/api/2/issue/{key}` request to update a single issue
3.3 WHEN `addComment()` is called THEN the system SHALL CONTINUE TO send a `POST /rest/api/2/issue/{key}/comment` request
3.4 WHEN `transitionIssue()` is called THEN the system SHALL CONTINUE TO send a `POST /rest/api/2/issue/{key}/transitions` request
3.5 WHEN `getTransitions()` is called THEN the system SHALL CONTINUE TO send a `GET /rest/api/2/issue/{key}/transitions` request
3.6 WHEN `testConnection()` is called THEN the system SHALL CONTINUE TO send a `GET /rest/api/2/myself` request
3.7 WHEN the rate limiter checks request counts THEN the system SHALL CONTINUE TO enforce the 1,440 requests/day daily limit and 60 requests/minute burst limit
3.8 WHEN inter-request delays are applied THEN the system SHALL CONTINUE TO enforce 1 second delay between GET requests and 2 second delay between write requests
3.9 WHEN a blocked endpoint path is requested THEN the system SHALL CONTINUE TO reject calls to `/rest/api/2/field` and `/rest/api/2/issue/bulk`
3.10 WHEN `searchIssues()` returns results THEN the system SHALL CONTINUE TO return the same `{ ok, data }` response shape so that all callers remain compatible

View File

@@ -0,0 +1,271 @@
# Jira API Compliance Bugfix Design
## Overview
The Jira REST API integration in the STEAM Security Dashboard has three compliance violations blocking production approval. The `searchIssues()` function uses `POST /rest/api/2/search` instead of the required `GET` with query parameters. The `getIssue()` function performs single-issue `GET /rest/api/2/issue/{key}` calls, which are forbidden — all issue fetching must go through JQL search. JQL queries in `searchIssuesByKeys()` do not include `project = <KEY>` scoping, which is required for all search operations.
The fix converts `searchIssues()` from POST to GET with URL-encoded query parameters, refactors `getIssue()` to delegate to `searchIssues()` with a JQL query, and adds project scoping to `searchIssuesByKeys()`. The UAT test script and API documentation are updated to reflect the compliant patterns. All other functions (`createIssue`, `updateIssue`, `addComment`, `transitionIssue`, `getTransitions`, `testConnection`) and the rate limiting / inter-request delay infrastructure remain unchanged.
## Glossary
- **Bug_Condition (C)**: The condition that triggers the compliance violation — when `searchIssues()` sends a POST, when `getIssue()` sends a single-issue GET, or when JQL queries lack project scoping
- **Property (P)**: The desired behavior — `searchIssues()` uses GET with query parameters, `getIssue()` delegates to JQL search, and all JQL includes `project = <KEY>`
- **Preservation**: Existing behavior of all other Jira API functions, rate limiting, inter-request delays, blocked endpoint guards, and the `{ ok, data }` response shape that must remain unchanged
- **`searchIssues()`**: The function in `backend/helpers/jiraApi.js` that executes JQL queries against the Jira search endpoint
- **`getIssue()`**: The function in `backend/helpers/jiraApi.js` that fetches a single issue by key
- **`searchIssuesByKeys()`**: The function in `backend/helpers/jiraApi.js` that bulk-fetches issues by an array of keys using JQL
- **`JIRA_PROJECT_KEY`**: The environment variable containing the Jira project key used for project scoping in JQL queries
- **Charter compliance**: The set of Jira REST API usage rules posted by Charter that the integration must follow for production approval
## Bug Details
### Bug Condition
The bug manifests in three distinct ways: (1) `searchIssues()` sends a `POST /rest/api/2/search` with a JSON body instead of a `GET` with query parameters, (2) `getIssue()` sends a `GET /rest/api/2/issue/{key}?fields=...` which is a forbidden single-issue GET pattern, and (3) `searchIssuesByKeys()` builds JQL without a `project = <KEY>` clause.
**Formal Specification:**
```
FUNCTION isBugCondition(input)
INPUT: input of type { functionName: string, args: any[] }
OUTPUT: boolean
IF input.functionName == 'searchIssues' THEN
RETURN httpMethodUsed == 'POST'
AND requestPath == '/rest/api/2/search'
AND requestHasJsonBody == true
END IF
IF input.functionName == 'getIssue' THEN
RETURN requestPath MATCHES '/rest/api/2/issue/{key}'
AND httpMethodUsed == 'GET'
AND NOT requestPath CONTAINS '/rest/api/2/search'
END IF
IF input.functionName == 'searchIssuesByKeys' THEN
RETURN jqlQuery NOT CONTAINS 'project ='
END IF
RETURN false
END FUNCTION
```
### Examples
- **searchIssues() — current**: `searchIssues('project = VULN', { maxResults: 10 })` sends `POST /rest/api/2/search` with body `{ jql: "project = VULN", startAt: 0, maxResults: 10, fields: [...] }`. **Expected**: sends `GET /rest/api/2/search?jql=project%20%3D%20VULN&fields=summary%2Cstatus%2C...&maxResults=10&startAt=0`
- **getIssue() — current**: `getIssue('VULN-123')` sends `GET /rest/api/2/issue/VULN-123?fields=summary,status,...`. **Expected**: sends `GET /rest/api/2/search?jql=key%3D%22VULN-123%22%20AND%20project%3DVULN&fields=summary%2Cstatus%2C...&maxResults=1`
- **searchIssuesByKeys() — current**: `searchIssuesByKeys(['VULN-1', 'VULN-2'])` builds JQL `key in ("VULN-1", "VULN-2") AND updated >= -24h` without project scoping. **Expected**: JQL is `key in ("VULN-1", "VULN-2") AND updated >= -24h AND project = VULN`
- **getIssue() response shape — current**: returns `{ ok: true, data: { key, id, self, fields: {...} } }`. **Expected after fix**: still returns `{ ok: true, data: { key, id, self, fields: {...} } }` by extracting the single issue from search results
## Expected Behavior
### Preservation Requirements
**Unchanged Behaviors:**
- `createIssue()` must continue to send `POST /rest/api/2/issue` with issue fields in the JSON body
- `updateIssue()` must continue to send `PUT /rest/api/2/issue/{key}` to update a single issue
- `addComment()` must continue to send `POST /rest/api/2/issue/{key}/comment`
- `transitionIssue()` must continue to send `POST /rest/api/2/issue/{key}/transitions`
- `getTransitions()` must continue to send `GET /rest/api/2/issue/{key}/transitions`
- `testConnection()` must continue to send `GET /rest/api/2/myself`
- Rate limiter must continue to enforce 1,440 requests/day and 60 requests/minute burst limits
- Inter-request delays must continue to enforce 1s between GETs and 2s between writes
- Blocked endpoint guard must continue to reject `/rest/api/2/field` and `/rest/api/2/issue/bulk`
- `searchIssues()` must continue to return `{ ok, data: { total, issues } }` response shape
- `getIssue()` must continue to return `{ ok, data: <single-issue> }` response shape
**Scope:**
All functions that do NOT involve `searchIssues()`, `getIssue()`, or `searchIssuesByKeys()` should be completely unaffected by this fix. This includes:
- All write operations (`createIssue`, `updateIssue`, `addComment`, `transitionIssue`)
- Read operations that do not use the search endpoint (`getTransitions`, `testConnection`)
- Rate limiting and inter-request delay infrastructure
- Blocked endpoint guards
- Module exports and configuration constants
## Hypothesized Root Cause
Based on the bug description and code review, the root causes are:
1. **searchIssues() uses POST instead of GET**: The function was implemented using `jiraPost('/rest/api/2/search', body)` which sends JQL, fields, startAt, and maxResults as a JSON POST body. The Jira API supports both POST and GET for search, but the Charter reviewer requires GET with query parameters. The fix is to switch from `jiraPost` to `jiraGet` with URL-encoded query parameters.
2. **getIssue() uses single-issue GET endpoint**: The function was implemented using `jiraGet('/rest/api/2/issue/{key}?fields=...')` which is the standard Jira single-issue endpoint. The Charter reviewer forbids single-issue GET loops and requires all issue fetching to go through JQL search. The fix is to refactor `getIssue()` to call `searchIssues()` with `key = "{key}" AND project = <KEY>` and `maxResults: 1`, then extract the single issue from the results array.
3. **searchIssuesByKeys() missing project scoping**: The function builds JQL as `key in (...) AND updated >= -24h` but does not include `project = <KEY>`. The Charter compliance rules require all JQL queries to include project scoping. The fix is to append `AND project = ${JIRA_PROJECT_KEY}` to the JQL clause.
4. **UAT test script reflects non-compliant patterns**: Test case 3 ("Get Single Issue") exercises the old `getIssue()` pattern, test case 8 ("JQL Search") exercises the old POST-based `searchIssues()`, and test case 9 ("Bulk Key Search") does not verify project scoping. These need updating to reflect the compliant patterns.
5. **API documentation describes non-compliant endpoints**: The `docs/jira-api-use-cases.md` file lists `POST /rest/api/2/search` for JQL Search and `GET /rest/api/2/issue/{issueKey}?fields=...` for single-issue fetch. Both need updating to describe the compliant patterns.
## Correctness Properties
Property 1: Bug Condition — searchIssues Uses GET With Query Parameters
_For any_ JQL query string, fields array, startAt value, and maxResults value passed to `searchIssues()`, the function SHALL issue a `GET` request to `/rest/api/2/search` with URL-encoded query parameters `?jql=<encoded>&fields=<comma-separated>&maxResults=<n>&startAt=<n>` and SHALL NOT send a POST request or include a JSON body.
**Validates: Requirements 2.1**
Property 2: Bug Condition — getIssue Uses JQL Search Instead of Single-Issue GET
_For any_ issue key passed to `getIssue()`, the function SHALL delegate to `searchIssues()` with JQL `key = "{key}" AND project = <JIRA_PROJECT_KEY>` and `maxResults: 1`, and SHALL NOT send a request to `/rest/api/2/issue/{key}`.
**Validates: Requirements 2.3**
Property 3: Bug Condition — searchIssuesByKeys Includes Project Scoping
_For any_ non-empty array of issue keys passed to `searchIssuesByKeys()`, the JQL query SHALL include a `project = <JIRA_PROJECT_KEY>` clause alongside the `key in (...)` clause.
**Validates: Requirements 2.2**
Property 4: Preservation — Unchanged Functions Retain Original Behavior
_For any_ call to `createIssue()`, `updateIssue()`, `addComment()`, `transitionIssue()`, `getTransitions()`, or `testConnection()`, the fixed code SHALL produce exactly the same HTTP method, URL path, and request body as the original code, preserving all existing write and read operations that are not part of the search/fetch flow.
**Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5, 3.6**
Property 5: Preservation — Response Shape Compatibility
_For any_ successful call to `searchIssues()`, the function SHALL continue to return `{ ok: true, data: { total, issues } }`. _For any_ successful call to `getIssue()`, the function SHALL continue to return `{ ok: true, data: <single-issue-object> }` by extracting the first element from the search results array.
**Validates: Requirements 3.10**
Property 6: Preservation — Rate Limiting and Delays Unchanged
_For any_ sequence of API calls, the rate limiter SHALL continue to enforce the 1,440 requests/day daily limit and 60 requests/minute burst limit, and inter-request delays SHALL continue to enforce 1 second between GET requests and 2 seconds between write requests.
**Validates: Requirements 3.7, 3.8, 3.9**
## Fix Implementation
### Changes Required
Assuming our root cause analysis is correct:
**File**: `backend/helpers/jiraApi.js`
**Function**: `searchIssues()`
**Specific Changes**:
1. **Switch from POST to GET**: Replace `jiraPost('/rest/api/2/search', body)` with `jiraGet('/rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...')`. The JQL string, comma-separated fields, maxResults, and startAt must all be URL-encoded using `encodeURIComponent()`.
2. **Remove JSON body construction**: The `body` object `{ jql, startAt, maxResults, fields }` is no longer needed. All parameters move to query string.
3. **Preserve response parsing**: The `res.status === 200` check and `JSON.parse(res.body)` remain unchanged since the Jira search endpoint returns the same JSON shape for both GET and POST.
**Function**: `searchIssuesByKeys()`
**Specific Changes**:
4. **Add project scoping to JQL**: Change the JQL from `key in (${keyList}) AND updated >= -24h` to `key in (${keyList}) AND updated >= -24h AND project = ${JIRA_PROJECT_KEY}`. The `JIRA_PROJECT_KEY` constant is already available in module scope.
**Function**: `getIssue()`
**Specific Changes**:
5. **Refactor to use searchIssues()**: Replace the direct `jiraGet('/rest/api/2/issue/...')` call with a call to `searchIssues()` using JQL `key = "{issueKey}" AND project = ${JIRA_PROJECT_KEY}` and `maxResults: 1`.
6. **Extract single issue from results**: When the search succeeds, extract `data.issues[0]` from the search results to return as `{ ok: true, data: <issue> }`. If no issues are found (empty results), return `{ ok: false, status: 404, body: 'Issue not found' }`.
7. **Preserve return shape**: The caller expects `{ ok: true, data: { key, id, self, fields: {...} } }` — the individual issue object from the search results array has this same shape.
---
**File**: `backend/scripts/jira-uat-test.js`
**Specific Changes**:
8. **Update test case 3 name**: Change from `'3. Get Single Issue (GET /issue/{key})'` to reflect the JQL-based pattern, e.g., `'3. Get Single Issue (JQL search)'`.
9. **Update test case 8 name**: Change from `'8. JQL Search (POST /search)'` to `'8. JQL Search (GET /search)'`.
10. **Update test case 9 assertions**: Add verification that the JQL used by `searchIssuesByKeys()` includes project scoping. The test already calls `searchIssuesByKeys()` — the underlying function change handles compliance.
11. **Add full-load test**: Add a test case that simulates a 24-hour sync cycle by calling `searchIssues()` with a project-scoped JQL and verifying the response shape.
---
**File**: `docs/jira-api-use-cases.md`
**Specific Changes**:
12. **Update JQL Search use case (8)**: Change endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...`. Update the JQL pattern to include project scoping.
13. **Update Get Single Issue use case (3)**: Change from `GET /rest/api/2/issue/{issueKey}?fields=...` to describe the JQL-based pattern using `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1`.
14. **Update Issue Lookup use case (9)**: Same change as use case 3 — describe JQL-based lookup instead of single-issue GET.
15. **Update compliance summary table**: Change "Bulk reads via JQL" row from `POST /rest/api/2/search` to `GET /rest/api/2/search`. Add a row for single-issue fetch via JQL search.
## Testing Strategy
### Validation Approach
The testing strategy follows a two-phase approach: first, surface counterexamples that demonstrate the compliance violations on unfixed code, then verify the fix produces compliant behavior and preserves all existing functionality.
### Exploratory Bug Condition Checking
**Goal**: Surface counterexamples that demonstrate the compliance violations BEFORE implementing the fix. Confirm or refute the root cause analysis. If we refute, we will need to re-hypothesize.
**Test Plan**: Write unit tests that mock `jiraRequest` and capture the HTTP method, URL path, and body arguments. Run these tests on the UNFIXED code to observe the non-compliant patterns.
**Test Cases**:
1. **searchIssues POST detection**: Call `searchIssues()` and assert the HTTP method is `GET` — will fail on unfixed code because it uses `POST` (will fail on unfixed code)
2. **getIssue single-issue GET detection**: Call `getIssue('VULN-123')` and assert the URL path contains `/rest/api/2/search` — will fail on unfixed code because it uses `/rest/api/2/issue/VULN-123` (will fail on unfixed code)
3. **searchIssuesByKeys project scoping detection**: Call `searchIssuesByKeys(['VULN-1'])` and assert the JQL contains `project =` — will fail on unfixed code because project scoping is missing (will fail on unfixed code)
4. **searchIssues body detection**: Call `searchIssues()` and assert no JSON body is sent — will fail on unfixed code because it sends `{ jql, startAt, maxResults, fields }` (will fail on unfixed code)
**Expected Counterexamples**:
- `searchIssues()` sends `POST` with a JSON body instead of `GET` with query parameters
- `getIssue()` sends `GET /rest/api/2/issue/{key}` instead of `GET /rest/api/2/search?jql=...`
- `searchIssuesByKeys()` builds JQL without `project = <KEY>`
### Fix Checking
**Goal**: Verify that for all inputs where the bug condition holds, the fixed functions produce the expected compliant behavior.
**Pseudocode:**
```
FOR ALL input WHERE isBugCondition(input) DO
result := fixedFunction(input)
ASSERT expectedBehavior(result)
END FOR
```
Specifically:
- For any JQL string passed to `searchIssues()`, the request must be a GET with URL-encoded query parameters
- For any issue key passed to `getIssue()`, the request must go through `searchIssues()` with JQL `key = "{key}" AND project = <KEY>`
- For any key array passed to `searchIssuesByKeys()`, the JQL must include `project = <KEY>`
### Preservation Checking
**Goal**: Verify that for all inputs where the bug condition does NOT hold, the fixed code produces the same result as the original code.
**Pseudocode:**
```
FOR ALL input WHERE NOT isBugCondition(input) DO
ASSERT originalFunction(input) = fixedFunction(input)
END FOR
```
**Testing Approach**: Property-based testing is recommended for preservation checking because:
- It generates many test cases automatically across the input domain
- It catches edge cases that manual unit tests might miss
- It provides strong guarantees that behavior is unchanged for all non-buggy inputs
**Test Plan**: Observe behavior on UNFIXED code first for all unchanged functions, then write property-based tests capturing that behavior.
**Test Cases**:
1. **createIssue preservation**: Observe that `createIssue()` sends `POST /rest/api/2/issue` on unfixed code, then verify this continues after fix
2. **updateIssue preservation**: Observe that `updateIssue()` sends `PUT /rest/api/2/issue/{key}` on unfixed code, then verify this continues after fix
3. **addComment preservation**: Observe that `addComment()` sends `POST /rest/api/2/issue/{key}/comment` on unfixed code, then verify this continues after fix
4. **Response shape preservation**: Observe that `searchIssues()` returns `{ ok, data: { total, issues } }` on unfixed code, then verify the same shape after fix
5. **getIssue response shape preservation**: Observe that `getIssue()` returns `{ ok, data: <issue> }` on unfixed code, then verify the same shape after fix (extracted from search results)
6. **Rate limiter preservation**: Observe that rate limits are enforced on unfixed code, then verify they continue after fix
### Unit Tests
- Test `searchIssues()` sends GET with correctly URL-encoded query parameters for various JQL strings
- Test `searchIssues()` handles special characters in JQL (quotes, spaces, operators) via proper encoding
- Test `getIssue()` delegates to `searchIssues()` with correct JQL and `maxResults: 1`
- Test `getIssue()` extracts single issue from search results and returns `{ ok, data: <issue> }`
- Test `getIssue()` returns `{ ok: false }` when search returns empty results
- Test `searchIssuesByKeys()` includes `project = <KEY>` in JQL
- Test `searchIssuesByKeys()` with empty array returns `{ ok: true, data: { total: 0, issues: [] } }`
### Property-Based Tests
- Generate random JQL strings and verify `searchIssues()` always uses GET method with query parameters and never sends a POST body
- Generate random issue keys and verify `getIssue()` always routes through `/rest/api/2/search` with `maxResults=1` and project scoping
- Generate random arrays of issue keys and verify `searchIssuesByKeys()` always includes `project = <KEY>` in the JQL
- Generate random inputs for unchanged functions (`createIssue`, `updateIssue`, `addComment`) and verify they produce identical HTTP method, path, and body as the original implementation
### Integration Tests
- Run the UAT test script against a mock or UAT Jira instance and verify all test cases pass with compliant patterns
- Test a full 24-hour sync cycle simulation: `searchIssues()` with project-scoped JQL, verify response shape, verify rate limit accounting
- Test `getIssue()` end-to-end: call with a known key, verify the response contains the expected issue data extracted from search results
- Test `searchIssuesByKeys()` end-to-end: call with a mix of valid and invalid keys, verify project-scoped JQL and partial results handling

View File

@@ -0,0 +1,139 @@
# Implementation Plan
- [x] 1. Write bug condition exploration test
- **Property 1: Bug Condition** — Jira API Compliance Violations
- **CRITICAL**: This test MUST FAIL on unfixed code — failure confirms the bugs exist
- **DO NOT attempt to fix the test or the code when it fails**
- **NOTE**: This test encodes the expected behavior — it will validate the fix when it passes after implementation
- **GOAL**: Surface counterexamples that demonstrate the three compliance violations
- **Scoped PBT Approach**: Scope properties to the three concrete bug conditions:
1. `searchIssues()` sends POST instead of GET — generate random JQL strings and assert the HTTP method captured is `GET` and the request path starts with `/rest/api/2/search?` with query parameters (not a JSON body)
2. `getIssue()` sends a single-issue GET to `/rest/api/2/issue/{key}` — generate random issue keys and assert the request path contains `/rest/api/2/search` (not `/rest/api/2/issue/`)
3. `searchIssuesByKeys()` builds JQL without `project =` — generate random arrays of issue keys and assert the JQL string passed to the search contains `project =`
- Mock `jiraRequest` to capture HTTP method, URL path, and body arguments without making real HTTP calls
- Use `fast-check` arbitraries to generate JQL strings, issue keys (e.g., `fc.tuple(fc.stringMatching(/^[A-Z]{2,6}$/), fc.integer({ min: 1, max: 99999 }))` for `KEY-123` patterns), and key arrays
- Test file: `backend/__tests__/jira-api-compliance.property.test.js`
- Run test on UNFIXED code
- **EXPECTED OUTCOME**: Test FAILS (this is correct — it proves the bugs exist)
- Document counterexamples found: `searchIssues()` uses POST, `getIssue()` hits `/rest/api/2/issue/{key}`, `searchIssuesByKeys()` JQL lacks `project =`
- Mark task complete when test is written, run, and failure is documented
- _Requirements: 1.1, 1.2, 1.3_
- [x] 2. Write preservation property tests (BEFORE implementing fix)
- **Property 2: Preservation** — Unchanged Jira API Functions
- **IMPORTANT**: Follow observation-first methodology
- Observe behavior on UNFIXED code for non-buggy functions:
- `createIssue({ project: { key: 'TEST' }, summary: 'x', issuetype: { name: 'Task' } })` sends `POST` to `/rest/api/2/issue` with JSON body containing `{ fields: {...} }`
- `updateIssue('TEST-1', { summary: 'y' })` sends `PUT` to `/rest/api/2/issue/TEST-1` with JSON body containing `{ fields: {...} }`
- `addComment('TEST-1', 'comment text')` sends `POST` to `/rest/api/2/issue/TEST-1/comment` with JSON body containing `{ body: 'comment text' }`
- `transitionIssue('TEST-1', '5')` sends `POST` to `/rest/api/2/issue/TEST-1/transitions` with JSON body containing `{ transition: { id: '5' } }`
- `getTransitions('TEST-1')` sends `GET` to `/rest/api/2/issue/TEST-1/transitions`
- `testConnection()` sends `GET` to `/rest/api/2/myself`
- Write property-based tests using `fast-check` that verify for all generated inputs:
1. `createIssue()` always sends `POST /rest/api/2/issue` with `{ fields }` body — generate random field objects
2. `updateIssue()` always sends `PUT /rest/api/2/issue/{key}` with `{ fields }` body — generate random keys and field objects
3. `addComment()` always sends `POST /rest/api/2/issue/{key}/comment` with `{ body }` — generate random keys and comment strings
4. `transitionIssue()` always sends `POST /rest/api/2/issue/{key}/transitions` with `{ transition: { id } }` — generate random keys and transition IDs
5. `getTransitions()` always sends `GET /rest/api/2/issue/{key}/transitions` — generate random keys
6. `testConnection()` always sends `GET /rest/api/2/myself`
7. Response shape: `searchIssues()` returns `{ ok, data: { total, issues } }` and `getIssue()` returns `{ ok, data: <issue> }` — verify shape is preserved
- Mock `jiraRequest` to capture method, path, body and return appropriate mock responses
- Test file: `backend/__tests__/jira-api-preservation.property.test.js`
- Verify tests pass on UNFIXED code
- **EXPECTED OUTCOME**: Tests PASS (this confirms baseline behavior to preserve)
- Mark task complete when tests are written, run, and passing on unfixed code
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10_
- [x] 3. Fix the core API helper (`backend/helpers/jiraApi.js`)
- [x] 3.1 Convert `searchIssues()` from POST to GET with query parameters
- Replace `jiraPost('/rest/api/2/search', body)` with `jiraGet('/rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...')`
- URL-encode JQL string with `encodeURIComponent(jql)`
- Comma-join and encode fields array with `encodeURIComponent(fields.join(','))`
- Encode `maxResults` and `startAt` as query parameters
- Remove the JSON body object `{ jql, startAt, maxResults, fields }`
- Preserve the `res.status === 200` check and `JSON.parse(res.body)` response parsing
- Preserve the `{ ok, data }` return shape
- _Bug_Condition: searchIssues uses POST /rest/api/2/search with JSON body_
- _Expected_Behavior: searchIssues uses GET /rest/api/2/search?jql=&fields=&maxResults=&startAt=_
- _Preservation: Response shape { ok, data: { total, issues } } unchanged_
- _Requirements: 2.1_
- [x] 3.2 Add project scoping to `searchIssuesByKeys()` JQL
- Change JQL from `` key in (${keyList}) AND updated >= -24h `` to `` key in (${keyList}) AND updated >= -24h AND project = ${JIRA_PROJECT_KEY} ``
- `JIRA_PROJECT_KEY` is already available in module scope
- _Bug_Condition: searchIssuesByKeys JQL lacks project = clause_
- _Expected_Behavior: JQL includes project = JIRA_PROJECT_KEY_
- _Preservation: Return shape and searchIssues delegation unchanged_
- _Requirements: 2.2_
- [x] 3.3 Refactor `getIssue()` to delegate to `searchIssues()` via JQL
- Replace `jiraGet('/rest/api/2/issue/${encodeURIComponent(issueKey)}?fields=...')` with a call to `searchIssues()` using JQL `key = "${issueKey}" AND project = ${JIRA_PROJECT_KEY}` and `maxResults: 1`
- Extract `data.issues[0]` from search results to return as `{ ok: true, data: <issue> }`
- Return `{ ok: false, status: 404, body: 'Issue not found' }` when search returns empty results
- Preserve the `{ ok, data: <single-issue> }` return shape for callers
- _Bug_Condition: getIssue sends GET /rest/api/2/issue/{key} (single-issue GET)_
- _Expected_Behavior: getIssue delegates to searchIssues with JQL key = "{key}" AND project = KEY_
- _Preservation: Return shape { ok, data: { key, fields } } unchanged_
- _Requirements: 2.3_
- [x] 3.4 Verify bug condition exploration test now passes
- **Property 1: Expected Behavior** — Jira API Compliance Violations
- **IMPORTANT**: Re-run the SAME test from task 1 — do NOT write a new test
- The test from task 1 encodes the expected behavior
- When this test passes, it confirms the expected behavior is satisfied
- Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache`
- **EXPECTED OUTCOME**: Test PASSES (confirms bugs are fixed)
- _Requirements: 2.1, 2.2, 2.3_
- [x] 3.5 Verify preservation tests still pass
- **Property 2: Preservation** — Unchanged Jira API Functions
- **IMPORTANT**: Re-run the SAME tests from task 2 — do NOT write new tests
- Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache`
- **EXPECTED OUTCOME**: Tests PASS (confirms no regressions)
- Confirm all unchanged functions still produce the same HTTP method, path, and body
- [x] 4. Update the UAT test script (`backend/scripts/jira-uat-test.js`)
- [x] 4.1 Update test case 3 name to reflect JQL-based pattern
- Change `'3. Get Single Issue (GET /issue/{key})'` to `'3. Get Single Issue (JQL search)'`
- The test body calls `jiraApi.getIssue()` which now delegates to JQL search — no logic change needed in the test function itself
- _Requirements: 2.4_
- [x] 4.2 Update test case 8 name to reflect GET method
- Change `'8. JQL Search (POST /search)'` to `'8. JQL Search (GET /search)'`
- Add project-scoped JQL to the test: include `AND project = ${jiraApi.JIRA_PROJECT_KEY}` in the JQL string passed to `searchIssues()`
- _Requirements: 2.5_
- [x] 4.3 Update test case 9 to verify project scoping
- Add a log entry or assertion that the bulk key search includes project scoping
- The underlying `searchIssuesByKeys()` now includes `project = <KEY>` — the test validates the function works correctly with the compliant JQL
- _Requirements: 2.5_
- [x] 5. Update the API documentation (`docs/jira-api-use-cases.md`)
- [x] 5.1 Update compliance summary table
- Change "Bulk reads via JQL" row endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search`
- Add a row for "Single-issue fetch" describing JQL-based lookup via `GET /rest/api/2/search?jql=key="KEY"&...`
- _Requirements: 2.6, 2.7_
- [x] 5.2 Update Use Case 3 (Get Single Issue)
- Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1`
- Update the description to explain the JQL-based pattern
- _Requirements: 2.7_
- [x] 5.3 Update Use Case 8 (JQL Search / Bulk Sync)
- Change endpoint from `POST /rest/api/2/search` to `GET /rest/api/2/search?jql=...&fields=...&maxResults=...&startAt=...`
- Update JQL pattern to include `project = <KEY>` scoping
- _Requirements: 2.6_
- [x] 5.4 Update Use Case 9 (Issue Lookup)
- Change endpoint from `GET /rest/api/2/issue/{issueKey}?fields=...` to `GET /rest/api/2/search?jql=key="ISSUE-KEY" AND project=<KEY>&fields=...&maxResults=1`
- Update the description to match the JQL-based lookup pattern
- _Requirements: 2.7_
- [x] 6. Checkpoint — Ensure all tests pass
- Run `npx jest backend/__tests__/jira-api-compliance.property.test.js --no-cache` — all bug condition tests pass
- Run `npx jest backend/__tests__/jira-api-preservation.property.test.js --no-cache` — all preservation tests pass
- Run `npx jest --no-cache` — all existing tests in the project still pass
- Ensure all tests pass, ask the user if questions arise.