feat(portal): Unique Verifications analytics UI (3/3) - #2193
feat(portal): Unique Verifications analytics UI (3/3)#2193soamdesai-tfh wants to merge 19 commits into
Conversation
7b12100 to
705315e
Compare
7e6bd8d to
ce94b9e
Compare
705315e to
268ecfc
Compare
bd8bfa1 to
263b00c
Compare
268ecfc to
3abc011
Compare
adfe310 to
138fe18
Compare
GET /api/portal/apps/{app_id}/world-id-analytics: database-backed app
authorization with soft-delete 404s, id resolution against both action
tables with ambiguity/cross-app/cross-environment/wrong-source rejection,
PostgreSQL-side combined app aggregation, UTC zero-filling with the
three All Time start branches, bigint-safe integer-string counts, and
source-specific legacy_actions/actions blocks. Lands the fresh-stack
integration suite and the opt-in million-row load gate, which exercise
the data layer and this endpoint together.
The data slice scoped the fresh-stack runner to its own suites; now that integration.test.ts and million.test.ts exist in this tree, re-extend the default path and restore the --million entrypoint and its package script. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3abc011 to
e7030b9
Compare
138fe18 to
2a87888
Compare
Shared WorldIdAnalyticsGraph (Last 7 Days default, lazy cached All Time, seven-bucket grouping, five-minute visible/focus refresh that keeps prior data), the app hero with the legacy-inclusion disclosure, batched per-page action-card previews, the v4 detail graph replacing the live nullifiers_aggregate count, ActionStatsGraph unmounted from both active legacy detail scenes, Verified humans/Human copy replaced with Recent verifications/Nullifier, and the ruled fetch/isolation amendments to the two pre-existing pv3 suites.
Soam's UI ruling: the period switcher is a Select dropdown like the archived PeriodSelector and the app/team switchers, not aria-pressed segment buttons. Interaction mechanics only; every behavioral pin (lazy All Time, mounted cache, URL untouched, refresh semantics, grouped tooltips) is unchanged.
Rebuild the graph presentation on the archived PR 2100-2103 components: a shared Sparkline with uniform stroke (non-scaling-stroke over a stretched viewBox), the dashed hover rule with a count/date popup, and a Select-dropdown period switcher matching the app/team switchers. Card previews render their real Last 7 Days series instead of a hardcoded line. Presentation only; fetch/cache/refresh logic is unchanged.
The dropdown had no whitespace-nowrap and could shrink, so a narrow hero card wrapped "Last 7 Days" onto three lines and pushed it out of the card. Pin the button's width and let the header row wrap instead.
Soam's product call: remove the "Includes legacy World ID actions" help text from the app hero. This is a substantive contract change, not a mechanics amendment — frozen spec sections 3 and 7 mandate the copy and section 8 requires the assertion. Recorded as a deviation.
Soam's product call. Deviation from frozen spec sections 3 and 7, which mandate this copy so the hero total's inclusion of v3 actions (absent from the v4 card grid) is disclosed.
A brand-new app's Last 7 Days series is truncated at its creation date, so it carries one point — and a one-vertex polyline paints nothing, leaving an empty card where the spec requires a flat zero graph. Span a lone (or empty) series across the full width at its own height.
The existing flat-zero test uses a three-point series, so it passed while a newly created entity (one point, truncated at creation) rendered no line at all. Pin that a lone point still produces a drawable segment.
portal-border is #f1f1f1, so the dashed hover line was effectively invisible; use portal-subtle (#9c9c9c) as the archived sparkline did. The tooltip card keeps its light border.
Match archive/pr-2103's ActionCard: taller frame, title/description at the top, a full-width sparkline in the middle, and the count inline with its label in a footer row. Drops the stacked label-over-number block. The archived footer's "last <relative time>" clause stays out — latest verification time is an out-of-scope metric.
actionCardFrameClassName is composed by the team app tiles (Team/page/Apps/App), so the analytics card's taller geometry was enlarging every app tile and failing main's portal-v3-app-grid-design test. Restore that constant byte-for-byte and give the analytics card its own worldIdActionCardFrameClassName. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the app hero on production with the v4 cards, and map legacy v3 detail graphs from app.is_staging so staging actions no longer 400. Co-authored-by: Cursor <cursoragent@cursor.com>
2a87888 to
690969c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b35e2a1888
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| setPeriod(next); | ||
| if (!cache[next]) void load(next); | ||
| }; | ||
| const response = cache[period] ?? cache.last_7_days; |
There was a problem hiding this comment.
Stop labeling failed All Time loads as All Time
When Last 7 Days has loaded and the first All Time request fails, this fallback selects the cached seven-day response while period remains all_time. Because metric is still defined, the error branch is skipped, so the selector says “All Time” and the graph rebuckets and presents the seven-day count as all-time data until a later refresh succeeds. Distinguish a pending request from a failed uncached period, or revert/show an error rather than retaining the fallback after failure.
Useful? React with 👍 / 👎.
| ) | ||
| .catch(() => {}); | ||
| return () => controller.abort(); | ||
| }, [props.appId, page, props.search]); |
There was a problem hiding this comment.
Refetch previews when the action list changes
After a successful create, handleDialogClose invokes onActionsChanged, which refetches the overview and updates props.actions; however, if the search and page number are unchanged, this effect does not rerun. Since actions are ordered newest-first, the new action can immediately appear on the current page without its ID ever being requested, leaving its preview at the fallback value until the user searches or changes pages. Include the visible action IDs in the effect dependency/key.
Useful? React with 👍 / 👎.
| /> | ||
| <div className="flex items-baseline gap-1.5"> | ||
| <span className="font-twk text-20 leading-none font-medium text-portal-heading"> | ||
| {BigInt(props.previewCount ?? "0").toLocaleString()} |
There was a problem hiding this comment.
Avoid reporting unavailable previews as zero
On every initial card render—and permanently after a non-2xx or network failure swallowed by ActionsGrid—previewCount is undefined, so this renders 0 and a flat empty sparkline. That makes loading or unavailable analytics indistinguishable from a genuine zero-verification action and presents inaccurate production data; render a loading/unavailable state until a successful response supplies the metric.
Useful? React with 👍 / 👎.
fee4fd4 to
6c3f84d
Compare
Third of three stacked PRs (on #2192, which is on #2191). The product surfaces.
Scope
WorldIdAnalyticsGraphunderWorldId/common/: Last 7 Days default, lazy + mounted-page-cached All Time, at-most-seven near-equal buckets (sum-preserving, never cumulative), UTC labels/tooltips, five-minute visible refresh + focus refresh, prior data kept through pending/failed refreshes, flat-zero renderingweb/lib/world-id-analytics: BigInt-safe series sums, bucket math, UTC-immune date formattingnullifiers_aggregatecountActionStatsGraphunmounted from both active legacy v3 detail scenes (files kept per owner ruling; itsaction_stats()query no longer runs); "Verified humans"/"Human" copy → "Recent verifications"/"Nullifier"; latest-100 feed preservedaria-labelso preview stats don't change accessible names; ruled fetch/isolation amendments to the two pre-existing pv3 suites (no assertion changes)Verification
This branch's tree is byte-identical to the fully verified feature branch: UI unit tests 51/51 (graph, surfaces, bucketing, amended pv3), full unit sweep green except the pre-existing base
portal-v3-shell-dropdown-focusfailure already fixed on main by 333cfd0 (resolves on rebase),tsc --noEmit,pnpm format:check.