Skip to content

feat(support): support dashboard overview with triage and statistics#1130

Merged
TaprootFreak merged 4 commits into
DFXswiss:developfrom
joshuakrueger-dfx:feat/support-dashboard-overview
Jun 30, 2026
Merged

feat(support): support dashboard overview with triage and statistics#1130
TaprootFreak merged 4 commits into
DFXswiss:developfrom
joshuakrueger-dfx:feat/support-dashboard-overview

Conversation

@joshuakrueger-dfx

Copy link
Copy Markdown
Contributor

Support dashboard — overview

A triage-focused landing for the support dashboard. Opening "Support Dashboard" from the burger menu now shows this overview; the full ticket list moves to /support/dashboard/all.

What's included

  • My tickets — tickets assigned to the logged-in agent (resolved from the backend support/issue/clerk mapping, no localStorage), shown as assigned / open on one card.
  • Waiting longer than — customer-waiting tiers 1h / 12h / 24h (24h = escalated). The pills are clickable filters that drive a time-sorted list (longest wait first); 24h shows the escalations.
  • Statistics tab — selectable period (7d / 30d / 6m / 12m): new tickets, avg messages per ticket, tickets/day, and a day/month trend chart. Backed by the new support/issue/statistics aggregate with a client-side fallback.
  • Pure aging/escalation/statistics logic extracted to src/util/support-stats.ts (no React deps) with unit tests.

Notes

Checklist

  • tsc clean, ESLint clean
  • Unit tests (support-stats) green
  • Smoke-test on dev with a support/admin login

Pairs with DFXswiss/api#3942 (backend). Works before it deploys via the client-side stats fallback; merge the backend first for accurate numbers and clerk resolution.

@joshuakrueger-dfx joshuakrueger-dfx force-pushed the feat/support-dashboard-overview branch 4 times, most recently from 1ab293e to 425d395 Compare June 22, 2026 10:20

@TaprootFreak TaprootFreak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review notes on the support dashboard overview — overall solid, well-structured work with good unit coverage. Flagging a few items inline, mainly the visual-regression baseline (CONTRIBUTING) and the OPEN_STATES interaction with the new ticket states from the backend PRs (api#3942 / api#3950).

Comment thread src/App.tsx
},
{
path: 'support/dashboard',
element: withSuspense(<SupportDashboardOverviewScreen />),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Visual-regression baseline not regenerated for the repointed route

This makes /support/dashboard render the new overview screen (old list moves to /support/dashboard/all), but e2e/support-dashboard.spec.ts still targets /support/dashboard: it asserts getByText('Open Issues') (spec line 66) — absent from the new screen — and captures toHaveScreenshot('support-dashboard-01-overview.png') (spec line 68) against the old screen's baseline.

Per CONTRIBUTING ("when a change affects the UI, regenerate the affected screenshots and commit them with the change") this should land in the same PR. These specs don't gate CI, but they're the documented before→after review aid.

Suggested fix:

  • repoint the existing test to /support/dashboard/all and refresh its baseline;
  • add a spec + baseline for the new overview at /support/dashboard;
  • regenerate on macOS/chromium: npx playwright test support-dashboard --update-snapshots.


type DashboardTab = 'overview' | 'statistics';

const OPEN_STATES = [SupportIssueInternalState.CREATED, SupportIssueInternalState.PENDING];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OPEN_STATES excludes the new ticket states introduced by api#3950

OPEN_STATES = [CREATED, PENDING] is the only state filter for the whole overview (getIssueList({ states: OPEN_STATES.join(',') })). Once api#3950 lets staff set InProgress / InClarification, such a ticket disappears from My tickets, Waiting tickets and Escalations — even while the customer is waiting. The backend escalation cron (api#3942) hardcodes the same pair, so those tickets are also never escalated to Telegram. Note the customer auto-reopen only resets Completed/OnHold/Canceled back to Pending, so InProgress/InClarification never return to an open state on their own.

Suggest defining this as "all non-terminal states" and keeping it in sync with the backend.

// if that endpoint is unavailable we fall back to computing from the most recent tickets.
const loadStats = useCallback(
(periodDays: number): void => {
setStatsLoading(true);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statsError is never reset, so a stale error can mask fresh statistics

loadStats clears statsError only in the primary success branch; the fallback success path (getIssueList({ take: 1000 }).then(computeStatistics)) sets statistics without clearing the error. Because StatisticsView renders error ? <ErrorHint/> : loading || !statistics ? <Spinner/> : <content/>, a previously-set error then hides both the freshly loaded chart and the spinner. Add setStatsError(undefined) at the top of loadStats.

Comment thread src/util/support-stats.ts Outdated
export type StatGranularity = 'day' | 'month';

// Selectable analysis periods for the statistics tab.
export const STAT_PERIODS: { days: number; label: string }[] = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded German period labels bypass i18n

STAT_PERIODS labels ('7 Tage', '30 Tage', '6 Monate', '12 Monate') are rendered raw in StatisticsView ({p.label}), while every neighbouring string goes through translate('screens/support', …). Non-German users get a fully localized dashboard except these four buttons. Suggest storing a key (e.g. '{{days}} days' / '{{months}} months') and translating at render.

Comment thread src/util/support-stats.ts
// pre-fill empty buckets oldest → newest
const buckets = new Map<string, number>();
if (granularity === 'day') {
for (let d = Math.round(periodDays) - 1; d >= 0; d--) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trend buckets can sum to less than total

inPeriod keeps tickets with daysSince(created) <= periodDays (a rolling window), but the daily pre-fill covers round(periodDays) midnight-aligned days and monthly uses round(periodDays / 30) buckets. A boundary-aged ticket passes the inPeriod filter (counted in total) yet its bucket key falls before the oldest bucket and is dropped from trend, so the chart can undercount vs the headline total. The same off-by-one exists on the backend monthly branch (api#3942) — worth aligning both.

"This IBAN already exists in another DFX customer account.": "Diese IBAN existiert bereits in einem anderen DFX-Kundenkonto."
},
"screens/support": {
"Avg resolution time": "Ø Lösungszeit",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New screens/support keys are added only to de.json. fr.json / it.json already translate this section, so French/Italian support staff will see English fallbacks for these ~31 keys. Either add the translations or confirm EN-fallback is acceptable here.

Comment thread src/translations/languages/de.json Outdated
"Replied": "Beantwortet",
"Waiting": "Wartet",
"Avg messages / ticket": "Ø Nachrichten / Vorgang",
"Your support overview": "Deine Support Übersicht",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "Deine Support Übersicht" should be the hyphenated compound "Support-Übersicht" (German orthography; consistent with "Support-Dashboard" added just above).

@TaprootFreak

Copy link
Copy Markdown
Collaborator

Pushed two follow-ups on top of your review fixes (10d57b3, 052c777) — context below.

1. Removed the ?preview=1 scaffolding from production code. The preview path disabled the support-clerk guard (useSupportDashboardGuard('/', !isPreview)) and rendered hardcoded sample data — both shipped in the prod bundle purely to feed the visual baseline. In a prod build, anyone (incl. logged-out) could open /support/dashboard?preview=1 and load the dashboard shell. It's fake-data-only (no real-data path), but it's an auth-bypass + sample-data generators where they don't belong. Reverted the guard to useSupportDashboardGuard() and dropped previewIssues/previewStatsIssues and the isPreview branches.

2. Reworked the visual spec to the existing convention. It now authenticates with the ADMIN_SEED admin session against the local API (same pattern as compliance.spec.ts) instead of in-page preview data. It needs the local API stack running with api and services as siblings, like the other e2e specs.

3. Regenerated the two baselines natively on macOS/chromium against the local stack (npm run setup seed → zero-state dashboard). Both tests pass (overview + statistics tabs).

Also bounded the stats period filters to <= now so a future-dated ticket can't break the trend sum-to-total. Your OPEN_STATES, statsError and fr/it i18n fixes are untouched.

joshuakrueger-dfx and others added 4 commits June 30, 2026 22:49
- new overview landing at /support/dashboard; the full ticket list moves to /support/dashboard/all

- 'Meine Tickets' resolves the agent from the logged-in account (backend mapping, no localStorage); customer-waiting tiers (1h/12h/24h) whose pills filter a time-sorted escalation list

- limit increase requests surfaced as a dedicated, highlighted section

- statistics tab: selectable period (7d/30d/6m/12m), new tickets, avg messages/ticket, tickets/day, avg resolution time + resolution-time-by-type, trend chart; backed by the server aggregate with a client-side fallback

- pure aging/escalation/statistics logic extracted to support-stats.ts (no React deps) with unit tests
- OPEN_STATES as complement of terminal states so new backend states
  (InProgress/InClarification) stay visible in the overview filters
- reset statsError before each load so a stale error can't mask fresh stats
- i18n the statistics period labels (de/fr/it) instead of hardcoded German
- align trend buckets with the period window so total equals their sum
- translate new screens/support keys for fr/it (were de-only)
- add visual-regression spec + baselines for the overview (preview mode)
…ual test via session

- remove the ?preview=1 path that disabled the support-clerk guard and rendered sample data in the production bundle; the overview is now unconditionally guarded again
- rework the visual-regression spec to authenticate with an admin session token against the local API (mirroring the compliance specs) instead of in-page preview data
- bound the stats period filters to <= now so a future-dated ticket can't break the trend sum-to-total
- remove the stale preview-generated baselines; regenerate locally with: REACT_APP_API_URL=http://localhost:3000 npx playwright test e2e/support-dashboard-overview.spec.ts --project=chromium --update-snapshots
Generated natively on macOS/chromium against the local API stack (loc seed) for the reworked, scaffolding-free e2e spec.
@TaprootFreak TaprootFreak force-pushed the feat/support-dashboard-overview branch from fba0735 to d03d993 Compare June 30, 2026 20:57
@TaprootFreak TaprootFreak merged commit 7bb93c9 into DFXswiss:develop Jun 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants