feat(apps): AI Credits analytics data widget - #179
Draft
goodbounties-nanoclaw-agent[bot] wants to merge 41 commits into
Draft
feat(apps): AI Credits analytics data widget#179goodbounties-nanoclaw-agent[bot] wants to merge 41 commits into
goodbounties-nanoclaw-agent[bot] wants to merge 41 commits into
Conversation
Adds a reusable Scorecard component to packages/ui — a single-metric KPI card with label, formatted value, optional trend indicator, and bare/card variants. Implements #139's compact/decimal/none formatting modes via a shared formatMetricValue utility, and a golden-ratio modular typography scale (base 24px, ratio 1.618, 12px floor) across sm/md/lg sizes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- formatMetricValue: fix a rounding-boundary bug where a scaled value could round up to the next unit's threshold without promoting (e.g. 999_950 -> "1000.0K" instead of "1.0M"); guard non-finite input (NaN/Infinity) with a "--" fallback shared by all consumers; note the intentional en-US locale. - Scorecard: set both testID and data-testid so React Native test tooling and web DOM queries both work; mark the trend arrow SVG as decorative (accessible=false / aria-hidden) since adjacent text already conveys direction; give resolveThemeColor a visible fallback and dev warning instead of silently rendering an empty fill; type its theme param via ReturnType<typeof useTheme> instead of an unsafe double cast. - package.json: move react-native-svg to peerDependencies (kept in devDependencies for local build/test) to avoid native-module duplication for consumers. - Storybook story + smoke test: render all 5 mock-data rows in both the bare and card variants with per-row testIDs, and assert all 10 are visible so the test matches what it claims to cover. Co-Authored-By: Claude <noreply@anthropic.com>
…ing pass
- formatMetricValue: whole numbers below the compact threshold render
without decimals ("47", not "47.0").
- Scorecard card variant: vertically centers content regardless of
whether a trend row is present.
- Value text uses $color instead of $primary; prefix/suffix render at
fontWeight 400 in $placeholderColor, subordinate to the value.
- Card variant elevation now uses a lightness-overlay + top highlight
instead of a hard border, scoped to Scorecard's <Card> call site so
the shared Card primitive is untouched.
- Spacing (label-to-value gap, value-to-trend gap, card padding) now
derives from the same golden-ratio constants as the type scale.
- Bump the Scorecard smoke test's viewport height to fit the taller
card layout without clipping the screenshot.
Co-Authored-By: Claude <noreply@anthropic.com>
Second consumer (PieDonutChart, and the 3 charts after it) needs the same theme-token-to-raw-color resolution Scorecard already does privately. Extracted to packages/ui/src/utils/resolveThemeColor.ts rather than duplicating a 5th time, mirroring how formatMetricValue was shared. Scorecard.tsx is untouched — it already shipped under a prior issue and migrating it is out of this task's scope. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Generalizes FundingDistributionChart's SVG arc technique into a themeable packages/ui primitive: computed ring geometry supports innerRadius from 0 (filled pie) to a thin donut, sort/maxSlices "Other" aggregation, legend with percentages, empty/stress states, and onSegmentPress. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Vertical/horizontal categorical bar chart with nice-number axis ticks, zero-baseline-aware single-edge-rounded bars, graceful long-label truncation, and sub-pixel-safe rendering for large category counts. Follows Scorecard/PieDonutChart conventions (createComponent, theme color resolution, golden-ratio type scale). Also adds a Text primitive to the Storybook react-native-svg web shim — needed because BarChart is the first chart to render labels inside the SVG (for pixel-precise axis alignment) rather than via Tamagui Text, and the shim never had to support that primitive before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Time-series line/area chart with linear/monotone/step interpolation, optional area-fill gradient, reference lines, multi-series with an optional secondary y-axis, null-gap handling (connectNulls), and adaptive x-axis label thinning/truncation for dense datasets, mirroring BarChart's established truncateLabelToWidth pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Shared CHART_FONT_FAMILY constant applied to every SVG <Text> in BarChart and LineAreaChart, matching the default preset's sans-serif typography stack instead of the browser's default serif fallback (react-native-svg's Text isn't part of Tamagui's styling system, same reason resolveThemeColor exists for fill/stroke). - PieDonutChart's Default story now shows the spec default (innerRadius 0.6, donut with center metric) instead of overriding it to a flat pie; the pie mode moves to its own PurePie story. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
centerContentMaxWidth was derived from ringRadius * (1 - innerRadius), which shrinks as the hole grows instead of growing with it — ringRadius is the stroke's centerline, not the hole radius. Compute the actual hole radius (ringRadius - strokeWidth / 2) and size the available square off that instead. Also default the center value's own formatting to 0 decimals (450K, not 450.0K) so typical aggregated totals fit the hole at the component's default size. Co-Authored-By: Claude <noreply@anthropic.com>
Pure-Tamagui data table with typed columns, sorting, striping, sticky header, and horizontal/vertical scroll. Applies the design-hierarchy rules from the Pie/Bar/Line fix round: muted $placeholderColor headers (bold weight kept per spec's own rule 4) vs full-contrast $color data values. Co-Authored-By: Claude <noreply@anthropic.com>
formatMetricValue's compact formatter always applied toFixed(decimals) to the scaled value, even when it landed on a whole unit (892000 -> "892.0K" instead of "892K"). Mirrors the same whole-number check already used in the below-threshold branch. Affects BarChart/LineAreaChart axis labels and DataTable cells. Also backfills missing Playwright screenshot coverage requested in QA: Pie/Donut empty + stress, Bar empty, Line/Area step/multi-series/gap/empty/ single-point, DataTable empty/null-cells/card variant. Co-Authored-By: Claude <noreply@anthropic.com>
…pped elements Several new QA-round screenshots (Pie/Donut, Bar, Line/Area empty states, Line/Area step/single-point, DataTable empty/nulls) used element-level locator screenshots instead of the established screenshotStory() full-iframe helper, producing tightly-cropped composites inconsistent with the rest of the suite. DataTable/Default now uses a taller viewport (1800px) so the card variant, previously captured as a separate cropped shot, renders within the same full-page capture as the bare table. Co-Authored-By: Claude <noreply@anthropic.com>
examples/react-web's local web shim for react-native-svg never got the Text/Defs/LinearGradient/Stop passthroughs that examples/storybook's copy already has. BarChart and LineAreaChart import these directly from react-native-svg, which resolves to this shim in the web build, so example-react-web#build failed at "Text" is not exported by reactNativeSvg.tsx — and since all 3 PR #148 Vercel preview checks share this turbo build task, all 3 were failing on it. Mirrors the existing storybook shim's implementation exactly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
examples/html/src/shims/reactNativeSvg.tsx had the identical gap already fixed in examples/react-web (missing Text/Defs/LinearGradient/Stop), which was the next failure turbo hit on the shared example-html#build task once the react-web build was unblocked. apps/superfluid-campaign-web and apps/ai-credits-web each keep their own differently-structured shim (src/reactNativeSvgWeb.tsx, aliased directly in vite.config.ts rather than living under a shims/ folder) with a wider pre-existing gap: also missing G, needed since PieDonutChart. Added G/Text/Defs/LinearGradient/Stop to both, following each file's own per-element-typed function style rather than the shims/ copies' createPassthroughSvgPrimitive factory. Full `pnpm build` across the monorepo now succeeds. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nt stories Add an explicit-render Controllable story to each analytics component (BarChart, PieDonutChart, LineAreaChart, DataTable, Scorecard), leaving Default fixed/static with a comment noting Controls are inert there by design, matching the Stepper/Card convention. Root cause of the reported inertness: any argType-covered prop absent from a story's args object renders as a non-functional "Set boolean/ string/number" placeholder button in Storybook's live Controls panel instead of a working control, until clicked once to initialize a default. Every argType-covered prop across all 5 components now has an explicit default in Controllable's args, matched to each component's own real internal default value, so every control renders live and interactive on first load. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
New standalone read-only Vite/React app (@goodwidget/ai-credits-analytics-web) visualizing AntSeed AI Credits activity: G$ volume, credit usage, and unique wallet counts, sourced from the AntSeed Analytics Worker with a local demo fallback. Ported from GoodDollar/data-team's reference dashboard, composed entirely from existing @goodwidget/ui chart primitives (Scorecard, LineAreaChart, BarChart, DataTable). No wallet connection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…s-web Vercel token/project-id secret naming confirmed by Laurence; job/environment naming scoped to this new workflow only per the same confirmation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
goodbounties-nanoclaw-agent Bot
added a commit
that referenced
this pull request
Aug 24, 2026
Implements Thales's 7 blocking QA fixes on PR #179: - Hover tooltips on LineAreaChart, BarChart, PieDonutChart (shared packages/ui chart components), via new ChartTooltip primitive - Y-axis floor at 0 when all series data is non-negative - Adaptive x-axis date-label thinning, always keeping first/last tick - Chart titles rendered as H5 - Rotated Y-axis title labels via new yAxisLabel prop - Right-aligned numeric columns in the Daily Summary table - Loading skeleton (new SkeletonBlock primitive) shown within 1s instead of a blank wait during the live fetch Regenerated baseline screenshots for tests/design-system/smoke.spec.ts and tests/widgets/gooddata-widget/states.spec.ts to reflect the updated chart/table rendering; both suites pass in full, confirming no regressions in the other consumers of the shared packages/ui chart components. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
Implements Thales's 7 blocking QA fixes on PR #179: - Hover tooltips on LineAreaChart, BarChart, PieDonutChart (shared packages/ui chart components), via new ChartTooltip primitive - Y-axis floor at 0 when all series data is non-negative - Adaptive x-axis date-label thinning, always keeping first/last tick - Chart titles rendered as H5 - Rotated Y-axis title labels via new yAxisLabel prop - Right-aligned numeric columns in the Daily Summary table - Loading skeleton (new SkeletonBlock primitive) shown within 1s instead of a blank wait during the live fetch Regenerated baseline screenshots for tests/design-system/smoke.spec.ts and tests/widgets/gooddata-widget/states.spec.ts to reflect the updated chart/table rendering; both suites pass in full, confirming no regressions in the other consumers of the shared packages/ui chart components. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…cimal precision - ChartTooltip splits label/value into separate Text nodes so the numeric value never truncates; the series name ellipsizes first if space is tight. - Tooltip width is now derived from actual header/row content (estimateChartTooltipWidthPx), replacing the old fixed 200px constant. - New formatChartAxisValue wrapper (trimIntegers: false) is the default formatter for LineAreaChart/BarChart axis ticks and tooltips, so a chart's hover values always match its own axis-tick decimal precision instead of formatMetricValue's per-value integer-trimming quirk. - Extracted the duplicated text-width-estimate/truncate helpers (previously copy-pasted in LineAreaChart and BarChart) into a shared utils module now that ChartTooltip needs the same logic too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…ement Replaces the old "every Nth label" adaptive-skip approach in LineAreaChart and BarChart (vertical layout) with a shared computeXAxisLabelPlan() utility that: - always renders the first/last category, anchored so its label can't overhang past the plot edge - sizes minimum label spacing off the widest *actually rendered* formatted label (dates, numbers, or arbitrary strings) at its real font size, not a fixed per-dashboard guess - distributes intermediate labels at equal pixel intervals, snapped to the nearest real category index with a deterministic floor-tiebreak on exact ties - runs an explicit post-snap collision check (dropping crowded intermediates, never an anchor) and a boundary check against the reserved Y-axis label gutter The old skip factor could still leave the last category unlabeled or let two labels touch once real (non-average) label widths landed on unevenly spaced snapped points — this makes both guarantees explicit and verifiable instead of incidental. BarChart's horizontal layout is untouched: its category axis stacks labels top-to-bottom in a fixed-width column, a height-crowding problem unrelated to this x-axis fix, so it keeps computeLabelSkipFactor. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…label-width floors Fix B follow-up, prompted by review questions on d9a5cf7: 1. Removed the two fixed-56px fallback constants (X_LABEL_APPROX_WIDTH_PX in LineAreaChart, CATEGORY_LABEL_APPROX_SIZE_PX in BarChart). They were not inert defaults — Math.max against the real measured widest-label width made them a live override any time every real label was narrower than 56px, silently packing short-label charts more sparsely than their actual content needed. Violates the no-hardcoded-values rule; the widest-real-label width alone is now the floor (0 initial value). 2. While visually re-verifying (1) against a short-label edge case, direct SVG getBBox() measurement caught a real, pre-existing collision in computeXAxisLabelPlan's step-8 check: the flat `labelWidthPx + minGapPx` threshold assumes every adjacent label pair is middle-anchored, but the first/last anchors are start/end-anchored and extend their full label width toward the interior instead of half. Adjacent to those anchors the flat check under-counts the required gap by labelWidthPx / 2 — confirmed via measurement as an actual ~0.6px (visually merged) overlap in a 20-short-category case. Added rightwardReachPx/leftwardReachPx/requiredGapPx, which compute each pair's true required gap from each entry's own textAnchor, and used them in place of the flat threshold in both the forward collision-check loop and the trailing anchor-boundary pop loop. The old flat formula still seeds the approximate maxTotalLabels capacity estimate (upper bound only); correctness now comes from requiredGapPx. Re-verified: rebuilt, linted (0 errors), re-measured the short-label edge case (previously-overlapping labels now correctly dropped, all gaps healthy), re-ran the four original stress-test scenarios (LineAreaChart 100/1095 points, BarChart 100/1000 categories) with the corrected math, and ran the full 45-test smoke suite — all pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…ter collision, rename Wallets label Y-axis titles (LineAreaChart/BarChart) now render at the same Caption tier as tick labels instead of the roomier x-axis-title tier, since they share the same constrained left gutter. That surfaced a pre-existing bug: the left padding gutter was a fixed 48px regardless of how wide the actual formatted tick labels were, so a wide tick value (e.g. "50.0K") could reach far enough left to collide with/garble the rotated y-axis title. Fixed by computing the required left gutter from the real widest formatted tick label (plus the title's own footprint when set) and widening the padding to fit, mirroring the existing dynamic-width approach already used for x-axis labels. Explicit per-instance padding overrides still take precedence. Also renames the Unique Wallets chart's yAxisLabel from "Count" to "Wallets" per QA. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…non-bold weight QA3 Fix D. LineAreaChart's LEGEND_LABEL_BASE_SIZE_PX and PieDonutChart's LEGEND_LABEL_SIZE_PX were pinned to the axis-title tier (CHART_BASE_SIZE_PX / GOLDEN_RATIO). For LineAreaChart, which scales its non-title text with the container up to MAX_SCALE_RATIO (2x) while the chart title stays fixed, this let the legend render larger than the chart's own title on wide containers (measured 29.67px legend vs 24px title) - the "oversized" legend QA flagged. Move both to the tick-label/Caption tier (CHART_BASE_SIZE_PX / GOLDEN_RATIO ** 2), which stays below the fixed title size across the full MIN_SCALE_RATIO-MAX_SCALE_RATIO range (0.6-2), not just this dashboard's specific container widths. Also add an explicit fontWeight: '400' on both legend label components so "not bold" is a pinned invariant rather than an inherited default. BarChart has no legend/multi-series concept, so it's unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…se DataTable's shared center default TABLE_COLUMNS in AiCreditsDashboard.tsx explicitly set align: 'left' on the date column and align: 'right' on every numeric/currency column, overriding DataTable's own default (column.align ?? 'center' in both HeaderCell and DataCell). Removed the per-column overrides so all 7 columns fall through to the shared component default instead of this dashboard re-deriving its own left/right-by-type rule. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…f.description DataTable columns can now set a description string that shows as a hover/focus tooltip on the header, and every Daily Summary column in the AI Credits dashboard now documents what it measures. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…inspection
Adds a valueType field ('integer' | 'currency' | 'decimal' | 'auto') to
LineAreaChart/BarChart so axis ticks and tooltips share one formatter
instead of independently guessing decimal precision from the value
itself. Wires the AI Credits dashboard: G$ Volume -> decimal,
AI Credits Used -> currency, Unique Wallets -> integer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…nd values
'decimal' valueType (rates, percentages, G$ volume) must render round
values without padded zeros ("20K" not "20.00K") while still showing
up to its precision when a value isn't round ("20.50K"); 'currency'
and 'integer' stay fixed-precision regardless of roundness since
those must never drop a real decimal (a currency value landing on a
whole dollar amount still needs "5.00"). Trimming is keyed off the
declared valueType, not runtime inspection of the value itself.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…xed 280px Tooltip width now derives from the hosting chart's own real pixel width (viewBoxWidth for LineAreaChart/BarChart, geometry.size for PieDonutChart) instead of a hardcoded TOOLTIP_MAX_WIDTH_PX constant, so a label only truncates once it would genuinely exceed 80% of the chart's width rather than an arbitrary fixed cap that could truncate short labels on wide charts or fit long ones on narrow charts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…-sizing The prior fix (d41d7c2) added the 80%-cap mechanism but the tooltip still truncated "Credit Users" because two things underestimated the row's real rendered width: - estimateTextWidthPx used a character-count heuristic instead of measuring actual glyph widths, so estimates diverged from what the browser renders. Now measures via canvas.measureText() on web, with the heuristic kept only as a React Native fallback (no canvas/DOM). - estimateRowContentWidthPx only accounted for one inter-element gap, though the row's XStack applies gap="$2" twice (dot->label and label->value). - The tooltip frame's 1px border (border-box sizing) was never subtracted from the content-area math, costing 2px the row needed. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
… fix header/data column misalignment resolveColumnFlexStyle's no-explicit-width branch only set flex:1, leaving flexBasis at its CSS default of "auto" (content-based sizing). Since the header row and each data row are separate, independent flex containers, each one distributed width based on its own children's content — a wide header label vs a short data value in the same column produced different per-row flex-basis inputs, landing same-index columns at different pixel boundaries between header and data rows. Pinning flexBasis to 0 makes every row split width purely by the flex ratio, independent of content, so same-index columns land on identical boundaries across all rows. Updates the 10 story-datatable-* Storybook baselines (evenly-redistributed column widths) and the 3 gooddata-widget baselines affected by the same Daily Summary table. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…ap with chart width CHART_TO_LEGEND_GAP_BASE_PX was routed through scalePx() using the same golden-ratio tier as font-driven sizes, so on wide/responsive dashboard charts (scaleRatio approaching the 2x cap) the gap ballooned to ~30px, reading as excessive whitespace between the chart and its legend. Fixes it the same way titleSizePx and PieDonutChart's own legend gap are already handled in this codebase: as a flat, unscaled structural gap (CHART_TO_LEGEND_GAP_PX = 10) instead of a font-legibility size that needs to track chart width. Scoped to LineAreaChart only — BarChart has no legend. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…xis for G$ Volume chart Wires the AI Credits dashboard's "Streamed" series to LineAreaChart's secondaryYAxis prop so it gets its own scale instead of being flattened against the much larger "One-time Deposits" series. Also fixes secondaryYAxis.label, which previously had no effect (dead prop, never rendered), by adding the missing axis title, and color-codes each axis to match its series once a secondary axis is active — generically, only when the primary axis still represents exactly one series, to avoid a misleading color choice when multiple series still share it. Also corrects the QA fixture's streamed/deposits values, which were an exact 1/5 ratio on every day — coincidentally producing identical normalized curves on independent axes and hiding one line behind the other, masking the fix in the dashboard screenshot. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
… fixed 200px LineAreaChart and BarChart now measure their PlotArea's resolved layout height via useMeasuredHeight and use it for the SVG height/viewBox, so charts fill whatever vertical space their container offers instead of rendering at a fixed 200px. Bumped the pre-layout fallback height to 280px so charts render taller even without a flex ancestor providing extra room. Rotated y-axis titles now wrap to a second line at a word boundary (wrapLabelToTwoLines) before falling back to ellipsis truncation, fixing the clipped "One-time Deposits (G$)" title on the G$ Volume dual-axis chart now that the chart is taller. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…ooltip/label clipping estimateTextWidthPx() used canvas measureText() (and a character-count fallback) with no rounding, so its estimate could land a fraction of a pixel narrower than the real DOM-rendered text width for the same string/font. Every caller (ChartTooltip sizing/truncation, axis-title wrapping, legend truncation) treats "estimate <= available width" as "fits without truncating" — that sub-pixel shortfall was enough for CSS text-overflow:ellipsis to clip text the JS logic believed would render in full, e.g. BarChart's tooltip row "Claims by Chain : 45.2K" getting cut to "Claims by Cha…" even though LineAreaChart's tooltip (shorter per-series labels) rarely landed on the same precision boundary. Math.ceil() on both estimation branches guarantees the estimate is never smaller than the true render, at the cost of at most ~1px of imperceptible extra reserved space. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
…-item gap slightly Both the legend label font size and the gap between legend items read a touch large on wide embeds (near MAX_SCALE_RATIO). Introduced LEGEND_SIZE_SCALE_FACTOR (0.9) and applied it to both LEGEND_LABEL_BASE_SIZE_PX and the legend's inter-item gap (replacing the flat, non-responsive "$4" Tamagui spacing token with a computed LEGEND_ITEM_GAP_PX derived from the same factor), so the reduction stays proportional to the label size rather than a separately hardcoded value. Scoped to LineAreaLegend only — BarChart has no legend UI. Co-Authored-By: Claude <noreply@anthropic.com> On-Behalf-Of: BountyCoder[onecli] (yaskkeryodtdijpv)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #178 (plan sub-issue for #176 — AI Credits analytics data widget).
Summary
apps/ai-credits-analytics-web(Vite + React +TamaguiProvider), following theapps/ai-credits-web/apps/superfluid-campaign-webscaffold pattern. No wallet connection anywhere.@goodwidget/uichart components:Scorecard,LineAreaChart,BarChart), and a paginated daily-summary table (DataTable+ an app-localPaginatedDataTablewrapper, sinceDataTablehas no built-in paging).src/lib/analyticsApi.ts,analyticsConversions.ts,generateDemoData.ts,src/hooks/useAnalyticsData.ts) is an internal module inside the app, not a separate package, per plan. Ported fromGoodDollar/data-team's reference dashboard (projects/antseed-analytics/dashboard/app.js): live-fetch-with-demo-fallback state machine, manual Live/Demo toggle (stays clickable even when live is unavailable — shows an inline "not yet deployed" message instead of disabling), 5-minute auto-refresh, and a refresh button that POSTs then re-fetches.Number().packages/uicomponents were needed — all 5 analytics chart components already cover this issue's requirements.Out of scope (per Bounty Lead sign-off on the plan)
ai-credits-web/superfluid-campaign-webdeploy workflows' generic job/environment naming is not touched here — tracked as a separate follow-up..github/workflows/deploy-ai-credits-analytics-web.yml) is drafted and committed locally but intentionally not included in this PR pending Laurence confirming the Vercel project/secret specifics for this app — will be pushed as a follow-up commit once confirmed.Testing
pnpm turbo run build --filter=@goodwidget/ai-credits-analytics-web— passes.pnpm turbo run lint --filter=@goodwidget/ai-credits-analytics-web— passes.apps/ai-credits-analytics-web/tests/dashboard.spec.ts(desktop/Chromium project) — 6/6 passing, covering: live data render, demo fallback, toggling to Live while unavailable (inline message, toggle stays clickable), refresh loading state, empty state, and table pagination.Human-reviewer checklist
antseed-integrationPR [Plan] Build packages/savings-widget: SDK integration, UI mapping, and execution checklist #19 link from the "live unavailable" copy (replaced with generic "not yet deployed" text) is correct.PaginatedDataTablewrapper (vs. promoting pagination intopackages/ui'sDataTable) is the right call for now.deploy-ai-credits-analytics-web.yml(committed locally, not in this PR) once Laurence confirms Vercel project/secret specifics, before it's pushed.