diff --git a/PR_LOADING_PATTERNS.md b/PR_LOADING_PATTERNS.md new file mode 100644 index 0000000..85bbeec --- /dev/null +++ b/PR_LOADING_PATTERNS.md @@ -0,0 +1,70 @@ +# feat(design): loading skeletons and async state patterns + +## π Description + +Implements loading skeletons for the three core surfaces (markets list, event detail, events table), inline async loading states for buttons/forms, and error + retry banners for network failures. All patterns are layout-shift-free. + +## π― Type of Change + +- [x] π¨ Style/UI changes +- [x] β¨ New feature (non-breaking change which adds functionality) + +## π Related Issues + +Closes #design/loading + +## π Changes Made + +### New Files + +| File | Purpose | +|------|---------| +| `components/skeletons/markets-skeleton.tsx` | `MarketCardSkeleton` + `MarketsListSkeleton` β mirrors market card layout exactly | +| `components/skeletons/event-detail-skeleton.tsx` | `EventDetailSkeleton` β covers badges, title, stat grid, options, form | +| `components/ui/async-button.tsx` | `AsyncButton` β inline spinner + loading text, auto-disables | +| `components/ui/error-banner.tsx` | `ErrorBanner` β destructive alert with optional Retry action | +| `app/(dashboard)/loading-patterns/page.tsx` | Demo page showing all 3 surfaces + patterns | + +### Modified Files + +| File | Change | +|------|--------| +| `app/(dashboard)/events/event-page/page.tsx` | Added `isLoading`/`fetchError` states; renders skeleton on load, error banner on failure; replaced manual `Loader2` button with `AsyncButton` | +| `components/events/events-section.tsx` | Reads `error` from events store; renders `ErrorBanner` with `loadEvents` as retry callback | + +### Key Design Decisions + +- Skeleton blocks use explicit `h-*`/`w-*` sizes matching real content β no layout shift on reveal. +- `AsyncButton` wraps the existing `Button` primitive; zero API surface change for callers. +- `ErrorBanner` is a thin wrapper over the existing `Alert` component β consistent with the design system. +- Error/retry pattern hooks directly into the Zustand `events-store` `error` + `loadEvents` β no new state needed. + +## π§ͺ Testing + +- [x] β Manual testing completed +- [x] β TypeScript check passes (`pnpm tsc --noEmit` β zero new errors) +- [x] β Mobile responsiveness tested (skeletons use responsive grid classes) + +## πΈ Screenshots + +Visit `/loading-patterns` in the running app to see all three surfaces side-by-side: + +1. **Markets List Skeleton** β 3 shimmer cards matching the marketing widget +2. **Event Detail Skeleton** β full page skeleton for the event detail route +3. **Events Table Skeleton** β existing skeleton wired via `loading.tsx` +4. **AsyncButton** β live demo with 2-second simulated action +5. **ErrorBanner** β with and without retry callback + +## β Pre-submission Checklist + +- [x] β Code follows project style guidelines (Tailwind, shadcn/ui primitives) +- [x] β No TypeScript errors in new files +- [x] β No new dependencies added +- [x] β No breaking changes β all changes are additive +- [x] β Skeletons match real content dimensions (no layout shift) +- [x] β `AsyncButton` is backwards-compatible with `Button` props + +## π§ Additional Notes + +- The two pre-existing TS errors (`activity-timeline-demo/page.tsx`, `typography-example.tsx`) are unrelated to this PR. +- The `/loading-patterns` demo page can be removed before merging to `main` if not needed in production. diff --git a/app/(dashboard)/analytics/page.tsx b/app/(dashboard)/analytics/page.tsx new file mode 100644 index 0000000..c11e2f3 --- /dev/null +++ b/app/(dashboard)/analytics/page.tsx @@ -0,0 +1,73 @@ +import { KpiCard } from "@/components/analytics/kpi-card"; +import { ChartPanel } from "@/components/analytics/chart-panel"; +import { VolumeChart } from "@/components/analytics/volume-chart"; +import { DistributionChart } from "@/components/analytics/distribution-chart"; + +const kpis = [ + { + label: "Total Volume", + value: "$14.8M", + unit: "XLM", + delta: "+18% vs last month", + deltaPositive: true, + tooltip: "Total value of all predictions settled on-chain.", + }, + { + label: "Active Users", + value: "10,241", + delta: "+201 this week", + deltaPositive: true, + tooltip: "Unique wallets that placed at least one prediction.", + }, + { + label: "Markets Created", + value: "1,204", + delta: "+34 this week", + deltaPositive: true, + tooltip: "Total prediction markets opened on the platform.", + }, + { + label: "Avg. Payout Time", + value: "2.4", + unit: "sec", + delta: "-0.3s vs last month", + deltaPositive: true, + tooltip: "Median time from event resolution to wallet credit.", + }, +]; + +export default function AnalyticsPage() { + return ( +
Design reference for skeletons, inline loading, and error/retry states.
+Shown while the markets widget fetches data.
+Shown while the event detail page loads.
+Shown via Next.js loading.tsx on the events list page.
+Spinner replaces icon; text changes to loading copy.
+Inline banner with optional retry callback.
++ {delta} +
+ )} +