Files
qwen3-6-lora/data/raw/sanitized/plans/puedes-conectarte-a-jira-delightful-bonbon.md
T

90 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GFIBER-707 — Fix "Cumulative Results" percentages + add "Pending" card
## Context
Jira **GFIBER-707** (Bug, High priority): the "Cumulative results" dashboard panel shows incorrect percentages for the Customer-Initiated / Agent-Initiated cards. Acceptance criteria:
- A third blue card **"Pending"** is added to the right of the existing two.
- The 3 cards, added together, sum **100%**.
**Root cause found in code** (`utils/dashboard/metrics.ts:157-160`): `customerInitiatedRate` and `agentInitiatedRate` are computed as `count / team.offers * 100`, where `team.offers = accepted + rejected`. But the numerators (`customerInitiated`, `agentInitiated`, `unverifiedAccepted`) are counted only over **accepted** roster tickets and, by construction, exactly partition `accepted` (every accepted ticket is customer-initiated, agent-initiated, or unclassified/null). Since `offers >= accepted` whenever there's at least one rejected ticket, the two existing rates under-report and can never sum to 100% — confirmed by the unit test at `__tests__/utils/dashboard/metrics.test.ts:170-183` (`offers = 3 accepted + 1 declined = 4`, so a fully-classified accepted ticket only registers as 25%, not 100/N%).
The fix: switch the denominator for all three initiation rates to `metrics.accepted` (which is already what the tile's caption text claims — `"{count} initiated by {initiator} / {total} total accepted offers"` already uses `total=metrics.accepted`). This makes `customerInitiatedRate + agentInitiatedRate + unverifiedAcceptedRate === 100` exactly whenever there's at least one accepted ticket, satisfying the AC. The two existing percentages will change (increase) as a side effect — this is the actual bug fix, not a regression.
Decisions confirmed with the user:
- Denominator for all 3 rates → `metrics.accepted` (not `offers`).
- Third card label: **"Pending Validation Rate"**, caption: `"{count} pending validation / {total} total accepted offer(s)"`.
## Changes
### 1. `utils/dashboard/metrics.ts`
- In `buildDashboardMetrics`, change:
```ts
const customerInitiatedRate = team.offers > 0 ? (customerInitiated / team.offers) * 100 : 0;
const agentInitiatedRate = team.offers > 0 ? (agentInitiated / team.offers) * 100 : 0;
```
to use `team.accepted` as the denominator, and add:
```ts
const unverifiedAcceptedRate = team.accepted > 0 ? (unverifiedAccepted / team.accepted) * 100 : 0;
```
- Add `unverifiedAcceptedRate: number` to the `DashboardMetrics` type (next to `agentInitiatedRate`), with a doc comment matching the existing style (`/** (unverifiedAccepted / accepted) × 100 — percentage of accepted offers not yet classified. */`).
- Update the doc comments on `customerInitiatedRate`/`agentInitiatedRate` to say "percentage of accepted offers" (not "total offers"), and update the return statement to include `unverifiedAcceptedRate`.
### 2. `packages/design-system/src/components/InitiationRateTile/index.tsx`
- Widen `initiator: "customer" | "agent"` to `initiator: "customer" | "agent" | "pending"`.
- Adjust the caption line: when `initiator === "pending"`, render `"{count} pending validation / {total} total accepted offer(s)"` instead of `"{count} initiated by {initiator} / ..."`. Simplest approach: branch on `initiator === "pending"` for the middle phrase ("pending validation" vs `initiated by {initiator}`), keeping the rest of the sentence identical.
- No style changes needed — `style.module.scss` already renders every tile with the same blue tint (`--fiber-blue-light` / `--fiber-text-blue`), matching the "blue card" requirement in the AC.
- Update the doc comment on the `initiator` prop to mention the "pending" case.
### 3. `packages/design-system/src/components/InitiationRateTile/InitiationRateTile.stories.tsx`
- Add a `PendingValidation` story (`label: "Pending Validation Rate"`, `initiator: "pending"`, plausible `rate`/`count`/`total`), and widen the story's grid `render` from `gridTemplateColumns: "1fr 1fr"` to `"1fr 1fr 1fr"` (or leave 2-col if stories render independently — check current behavior first).
### 4. `app/(protected)/dashboard/page.tsx` (`CumulativeResults`, ~lines 439-463)
- Change the grid wrapper from `grid grid-cols-2 gap-3 sm:grid-cols-2` to 3 columns (`grid grid-cols-1 gap-3 sm:grid-cols-3`, matching the responsive pattern used elsewhere in the file).
- Add a third `<InitiationRateTile>`:
```tsx
<InitiationRateTile
label="Pending Validation Rate"
rate={metrics.unverifiedAcceptedRate}
count={metrics.unverifiedAccepted}
total={metrics.accepted}
initiator="pending"
/>
```
- Leave the helper-text condition (`customerInitiatedRate === 0 && agentInitiatedRate === 0`) as-is — it still correctly means "nothing classified yet" (in which case pending = 100%).
### 5. `app/(protected)/dashboard/api/export/route.ts` (Sheet 3 — Performance summary, ~lines 346-352)
- After the existing `agentRateRow`, add:
```ts
const pendingRateRow = sheet3.addRow(["Pending Validation Rate", metrics.unverifiedAcceptedRate / 100]);
pendingRateRow.getCell(2).numFmt = "0.0%";
```
This is a new row inserted into Block A, which shifts every subsequent row (blank separator, leaderboard header, leaderboard data rows) down by 1 in the worksheet.
## Tests to update
- **`__tests__/utils/dashboard/metrics.test.ts`**: update existing assertions that hardcode the old `offers`-based math (e.g. `"only counts initiation types from roster users"`, `"counts customer_initiated and agent_initiated correctly"` — the `(1/4)*100` expectations become `(1/accepted)*100`), and add:
- New assertions for `unverifiedAcceptedRate`.
- A dedicated test asserting `customerInitiatedRate + agentInitiatedRate + unverifiedAcceptedRate === 100` (within floating-point tolerance) for a realistic mixed fixture — this directly encodes the GFIBER-707 acceptance criterion.
- **`packages/design-system/.../InitiationRateTile.test.tsx`** and **`.test.cy.tsx`**: add a "pending" case asserting the `"N pending validation / M total accepted offer(s)"` caption text.
- **`__tests__/dashboard-export-route.test.ts`**: this file has **hardcoded row indices** that must shift by +1 because of the new export row:
- `"Block A includes the new initiation type rows after Pitch Rate"` (line ~436-450): loop range `2..14` → `2..15`; add `expect(metricLabels).toContain("Pending Validation Rate")`.
- `"Block B leaderboard has one row per agent user"` (line ~452-464): header now at row **17** (was 16), data rows **18/19** (were 17/18) — update the comment and indices.
- `"includes zero-sales agents in the leaderboard"` (line ~466-476): loop range `17..18` → `18..19`.
## Verification
1. Run the affected unit test suites:
```bash
npx vitest run __tests__/utils/dashboard/metrics.test.ts __tests__/dashboard-export-route.test.ts
```
2. Run the design-system component tests for the tile:
```bash
npx vitest run packages/design-system/src/components/InitiationRateTile
```
3. Full test suite + typecheck to catch any other consumer of `DashboardMetrics`/`InitiationRateTile`'s `initiator` prop:
```bash
npm run build # or the repo's typecheck script — confirms the widened `initiator` union doesn't break other call sites
npm test
```
4. Manually load `/dashboard` (any team view) in a browser and confirm: 3 blue cards render side by side, and the 3 rate percentages sum to 100.0% for a mix of accepted tickets with `customer_initiated`, `agent_initiated`, and `null` initiation types.