# Requirements: QSP-39734 — Fix 30-day history chart: show dates instead of day numbers in tooltip and axis

## Status

✅ Refinement complete — approved by human (2026-03-25)

---

## Refinement Log

### Step 1 — Session start & codebase investigation (2026-03-25)

**Code investigation findings:**

- `src/app/components/GradeHistoryContainer.tsx`: The chart component. Uses `CategoricalBarChart` from `@dynatrace/strato-components-preview/charts`. Passes `convertGradesToGradeHistory(data)` as the chart's `data` prop. No tooltip or axis formatter configured beyond `formatter={numberToGrade}` on `ValueAxis`.
- `src/app/utils/score-and-grade.utils.ts` (line 124): `convertGradesToGradeHistory(grades: Grade[])` maps grades to `{ category: index.toString(), value: gradeToNumber(grade), color: GRADE_COLORS[grade] }`. The `category: index.toString()` (e.g. "0", "1", …, "30") is the root cause — this string is what appears in both the tooltip and the axis.
- `src/app/utils/score-and-grade.utils.spec.ts` (line 153): Test asserts `actual[0].category === '0'` and `actual[actual.length - 1].category === '5'` — must be updated.
- `src/app/hooks/useGradeHistory.ts`: Calls `generateGradeHistory(data, 31)` (31 = today + 30 days back). Index 0 = 30 days ago, index 30 = today.
- `src/app/utils/dateTimeUtils.ts`: `formatDate` parses and formats as `'MMM dd, yyyy, HH:mm'` — too long for axis labels. Need a short format (e.g. `"Mar 18"`).
- **Strato CategoricalBarChart**: The `category` field drives BOTH the tooltip text AND the axis tick labels — there is no separate tooltip formatter. Changing `category` from an index string to a date string fixes both at once.

**Consumers of `convertGradesToGradeHistory`:**

| File | Role |
|---|---|
| `src/app/components/GradeHistoryContainer.tsx` | Calls the function for chart data |
| `src/app/utils/score-and-grade.utils.spec.ts` | Tests the function; asserts `category` equals index string → must update |
| `component/tests/components/grade-history-container.spec.tsx` | Component test checks axis text with `index.toString()` → must update |
| `component/page-objects/grade-history-container.po.ts` | `CATEGORY_AXIS` locator — no change needed |

**GradeHistoryContainer used in 3 places:**
- `src/app/pages/Overview.tsx`
- `src/app/pages/AppScorePage.tsx`
- `src/app/components/overview-aggregation-view/overview-tab-content.tsx`

### Step 2 — Human answers to clarifying questions (2026-03-25)

| Question | Answer |
|---|---|
| Date format for tooltip & axis | `"Mar 18"` — short month name + day (no leading zero) |
| Axis `tickLabelLayout` | Default (do not add `"vertical"`) |
| "30 days ago / Today" footer | Replace with actual dates (e.g. `"Feb 23"` and `"Mar 25"`) |
| Scope | All charts of that kind (every `GradeHistoryContainer` instance) |

### Step 3 — Chart type change to TimeseriesChart.Bar (2026-03-25)

**Context:** During implementation the coding agent switched from `CategoricalBarChart` to `TimeseriesChart.Bar`. The human approved the visual result in browser verification. Agent Smith subsequently blocked it as scope-creep due to the "Out of Scope: no chart type change" line in requirements. After review, the chart type change is the correct and agreed-upon approach; requirements are updated here to reflect that.

**Decisions:**
- `TimeseriesChart.Bar` replaces `CategoricalBarChart` — dates on X-axis come from `Timeseries` timestamps automatically; no manual `category` string needed.
- `TimeseriesChart.Legend hidden` — suppresses the irrelevant "Grade" series label.
- `TimeseriesChart.YAxis formatter={(value) => numberToGrade(Math.round(value))} min={0} max={5}` — Y-axis shows grade letters, matching the old `CategoricalBarChart.ValueAxis` behaviour.
- `convertGradesToTimeseries(grades, referenceDate?)` added to `score-and-grade.utils.ts` — returns `Timeseries` with `datapoints: { start: Date, end: Date, value: number }[]`.
- `convertGradesToGradeHistory` is retained in `score-and-grade.utils.ts` as dead production code; its existing unit tests remain as coverage.
- "Changing chart type or data source — no change" removed from Out of Scope; the chart type change is in scope and approved.

---

## Summary

The 30-day history bar chart currently displays bar index numbers (`"0"`, `"1"`, …, `"30"`) in both the tooltip and the x-axis. The fix replaces these with real dates formatted as `"Mar 18"`. The leftmost bar's date also replaces the hardcoded `"30 days ago"` text below the chart. All three pages that render the history chart are affected automatically because the fix is contained in one utility function.

---

## Acceptance Criteria

1. Hovering over a bar in any 30-day history chart shows the date in `"Mar 18"` format (not a number).
2. No regression on grade display in the tooltip (grade letter still shown).
3. The label below the left edge of the chart shows the actual start date (e.g. `"Feb 23"`) instead of `"30 days ago"`.
4. The label below the right edge of the chart still shows `"Today"`.
5. All existing unit tests and component tests are updated to assert the new date format.

---

## Key Decisions

| Decision | Rationale |
|---|---|
| Use `TimeseriesChart.Bar` instead of `CategoricalBarChart` | Better visual fit: dates on X-axis come from `Timeseries` timestamps automatically; legend hidden to suppress irrelevant "Grade" label; approved by human in browser verification |
| Format: `"MMM d"` (e.g. `"Mar 18"`) | User-specified; concise, unambiguous, no leading zero |
| No `tickLabelLayout="vertical"` | User chose Strato default; Strato handles overflow/truncation internally |
| Replace `"30 days ago"` with actual date | User-specified; makes the time range explicit |
| Add `formatShortDate` to `dateTimeUtils.ts` | Reuses existing domain; avoids new file; format is `MMM d` |
| Add `convertGradesToTimeseries(grades, referenceDate?)` to `score-and-grade.utils.ts` | `TimeseriesChart` requires `Timeseries`-typed data; `referenceDate` enables deterministic tests |
| Retain `convertGradesToGradeHistory` in `score-and-grade.utils.ts` | Dead code in production; retained to preserve existing unit test coverage |

---

## Data Model

No interface changes. `GradeHistoryContainer` props stay:
```typescript
{ title: string; subtitle?: string; data: Grade[]; isLoading?: boolean }
```

New function added to `score-and-grade.utils.ts`:
```typescript
// Timeseries type from @dynatrace/strato-components-preview/charts
function convertGradesToTimeseries(grades: Grade[], referenceDate?: Date): Timeseries
```

`referenceDate` defaults to `new Date()` (today). Each datapoint:
```typescript
{
  start: subDays(referenceDate, grades.length - 1 - index),
  end: subDays(referenceDate, grades.length - 2 - index),
  value: gradeToNumber(grade),
}
```

`convertGradesToGradeHistory` is retained in `score-and-grade.utils.ts` unchanged — it is no longer called from production code but its unit tests remain as coverage.

---

## UX / UI Specification

### Chart Component
- `TimeseriesChart` + `TimeseriesChart.Bar` renders a vertical bar chart.
- `TimeseriesChart.Legend hidden` — suppresses the irrelevant "Grade" series label.
- `TimeseriesChart.YAxis formatter={(value) => numberToGrade(Math.round(value))} min={0} max={5}` — Y-axis shows grade letters.

### Tooltip (hover on a bar)
- Shows: date range derived from `datapoints.start`/`end` (e.g. `"Mar 18 – Mar 19"`) + grade letter.
- Date display is automatic — `TimeseriesChart` derives it from the `Timeseries` timestamps.

### X-axis
- Shows dates automatically from `Timeseries` datapoint timestamps — no manual `category` string needed.
- Strato's default layout (horizontal, auto-truncate on overflow) — no `tickLabelLayout` change.

### Footer below chart (in `GradeHistoryContainer`)
- Left label: actual start date formatted as `"Mar 18"` (was hardcoded `"30 days ago"`).
- Right label: `"Today"` — unchanged.

---

## API Contracts

_Not applicable — no API change._

---

## DQL Queries

_Not applicable — pure frontend display change._

---

## Implementation Plan

1. **`src/app/utils/dateTimeUtils.ts`** — Add `formatShortDate(date: Date): string` using `format(date, 'MMM d')` from `date-fns`.
2. **`src/app/utils/score-and-grade.utils.ts`** — Add `convertGradesToTimeseries(grades: Grade[], referenceDate?: Date): Timeseries`. Retain `convertGradesToGradeHistory` unchanged (dead production code; unit tests remain).
3. **`src/app/components/GradeHistoryContainer.tsx`** — Replace `CategoricalBarChart` with `TimeseriesChart` + `TimeseriesChart.Bar data={convertGradesToTimeseries(data)}`; add `TimeseriesChart.Legend hidden`; add `TimeseriesChart.YAxis formatter={(value) => numberToGrade(Math.round(value))} min={0} max={5}`; replace `"30 days ago"` text with `formatShortDate(subDays(new Date(), data.length - 1))`.
4. **`src/app/utils/score-and-grade.utils.spec.ts`** — Keep existing `convertGradesToGradeHistory` tests unchanged; add tests for `convertGradesToTimeseries`.
5. **`src/app/utils/dateTimeUtils.spec.ts`** — Add tests for `formatShortDate`.
6. **`component/tests/components/grade-history-container.spec.tsx`** — Update footer assertions to use computed date strings; update chart interaction assertions for `TimeseriesChart`.

---

## Unit Test Plan

### `dateTimeUtils.spec.ts`
- `formatShortDate(new Date(2026, 2, 18))` → `"Mar 18"`
- `formatShortDate(new Date(2026, 0, 1))` → `"Jan 1"` (no leading zero)
- `formatShortDate(new Date(2026, 11, 31))` → `"Dec 31"`

### `score-and-grade.utils.spec.ts` — `convertGradesToGradeHistory` (retained, no change to tests)
- Existing tests remain; they test the index-string `category` output.

### `score-and-grade.utils.spec.ts` — `convertGradesToTimeseries`
- `convertGradesToTimeseries(['A', 'B'], new Date(2026, 2, 25))`:
  - `result.datapoints[0].value === 5` (A → 5); `start` equals Mar 24
  - `result.datapoints[1].value === 4` (B → 4); `start` equals Mar 25 (referenceDate)
- Single-grade array: `result.datapoints[0].start` equals referenceDate.
- Empty array: `result.datapoints` is `[]`.

---

## Edge Cases & Error Handling

| Case | Behaviour |
|---|---|
| Empty grades array | `convertGradesToGradeHistory([])` returns `[]` — no change |
| Single grade | Only bar is today's date |
| Month boundary | `date-fns` `subDays` handles correctly |

---

## Out of Scope

- Changing the y-axis (grade letter axis) — no change.
- Changing tooltip layout/styling — no change.
- Persisting date range selection — not requested.








