feat(support): support dashboard overview with triage and statistics#1130
Conversation
1ab293e to
425d395
Compare
TaprootFreak
left a comment
There was a problem hiding this comment.
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).
| }, | ||
| { | ||
| path: 'support/dashboard', | ||
| element: withSuspense(<SupportDashboardOverviewScreen />), |
There was a problem hiding this comment.
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/alland 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]; |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
| export type StatGranularity = 'day' | 'month'; | ||
|
|
||
| // Selectable analysis periods for the statistics tab. | ||
| export const STAT_PERIODS: { days: number; label: string }[] = [ |
There was a problem hiding this comment.
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.
| // 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--) { |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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.
| "Replied": "Beantwortet", | ||
| "Waiting": "Wartet", | ||
| "Avg messages / ticket": "Ø Nachrichten / Vorgang", | ||
| "Your support overview": "Deine Support Übersicht", |
There was a problem hiding this comment.
Nit: "Deine Support Übersicht" should be the hyphenated compound "Support-Übersicht" (German orthography; consistent with "Support-Dashboard" added just above).
|
Pushed two follow-ups on top of your review fixes ( 1. Removed the 2. Reworked the visual spec to the existing convention. It now authenticates with the 3. Regenerated the two baselines natively on macOS/chromium against the local stack ( Also bounded the stats period filters to |
- 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.
fba0735 to
d03d993
Compare
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
support/issue/clerkmapping, no localStorage), shown as assigned / open on one card.1h / 12h / 24h(24h = escalated). The pills are clickable filters that drive a time-sorted list (longest wait first); 24h shows the escalations.support/issue/statisticsaggregate with a client-side fallback.src/util/support-stats.ts(no React deps) with unit tests.Notes
Checklist