Complete React modernization of the advertising dashboard with real API integration, modern data fetching, toast notifications, loading states, and interactive charts.
npm install --save @tanstack/react-queryrecharts- Already installed for charts@radix-ui/*- Already installed for UI components- Custom toast hook at
/hooks/use-toast.ts
- React Query (
@tanstack/react-query) for server state management - Automatic caching, refetching, and background updates
- Optimistic updates for better UX
- Built-in loading and error states
- Server state: React Query
- Client state: React hooks (useState, useReducer)
- No global state library needed for current scope
File: /lib/providers/QueryProvider.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'- Wraps app to provide React Query functionality
- Configures caching strategy (1min stale, 5min gc)
- Single retry on failure
useAdvertiserBookings(status, limit)- Fetch bookings with filtersuseBookingsSummary()- Get booking counts (active/scheduled/past)- Returns:
{ data, isLoading, error, refetch }
useBillingSummary()- Get billing metrics from Stripe- Fetches: current balance, month spend, lifetime spend, payment methods
- Handles Stripe integration gracefully
Added new skeleton loaders:
StatCardSkeleton- For metric cardsBookingItemSkeleton- For booking listsTableRowSkeleton- For table rows
Modern Recharts implementation:
PerformanceChart- Single metric area chartMultiMetricChart- Multi-line chart for comparisons- Responsive containers
- Custom tooltips with theme integration
- Gradient fills and smooth animations
Changes:
- ✅ Replaced hardcoded
$0.00with real data from API - ✅ Added loading skeletons
- ✅ Added error handling with visual feedback
- ✅ Shows payment method details when available
- ✅ Displays upcoming invoice amounts
Before:
<p className="text-3xl font-bold">$0.00</p>After:
const { data: billing, isLoading, error } = useBillingSummary()
{isLoading ? (
<StatCardSkeleton />
) : (
<p className="text-3xl font-bold">
${billing?.month_spend.toFixed(2) ?? '0.00'}
</p>
)}Changes:
- ✅ Replaced hardcoded zeros with real booking counts
- ✅ Added recent bookings list with real data
- ✅ Clickable bookings that link to judge profiles
- ✅ Status badges (active/scheduled)
- ✅ Loading states and empty states
- ✅ "View all" links for pagination
API Integration:
- Uses
/api/advertising/bookings/summaryfor counts - Uses
/api/advertising/bookings?status=activefor list
Changes:
- ✅ Replaced
alert()calls with modern toast notifications - ✅ Added success/error toasts for:
- Pause campaign
- Resume campaign
- Delete campaign
- ✅ Enhanced stat cards with gradient hover effects
- ✅ Better visual hierarchy with Trellis design tokens
Before:
alert('Failed to pause campaign')After:
toast({
title: 'Failed to pause campaign',
description: 'An error occurred. Please try again.',
variant: 'destructive',
})Changes:
- ✅ Replaced fake booking data with real API calls
- ✅ Created
ActiveBookingsListcomponent - ✅ Created
UpcomingBookingsListcomponent - ✅ Shows first 2 bookings, links to view all
- ✅ Loading skeletons during fetch
- ✅ Links to judge profiles
Before:
{stats.active_bookings > 0 ? (
<div>Judge Smith - Position 1</div> // Hardcoded
) : (
<p>No active ad spots</p>
)}After:
function ActiveBookingsList({ activeCount }) {
const { data, isLoading } = useAdvertiserBookings('active', 2)
// Real data rendering
}Changes:
- ✅ Replaced "Chart coming soon" with real Recharts
- ✅ Multi-metric line chart (impressions, clicks, spend)
- ✅ Interactive tooltips
- ✅ Dual Y-axes for different metric scales
- ✅ Enhanced metric cards with gradients
- ✅ Color-coded by metric type
Chart Features:
- Responsive design
- Theme-aware colors (uses CSS variables)
- Date formatting
- Value formatting (currency for spend)
- Smooth animations
- Legend with metric names
Added to provider tree:
<ClerkProvider>
<QueryProvider>
{children}
<Toaster />
</QueryProvider>
</ClerkProvider>-
GET /api/advertising/bookings?status={status}&limit={limit}- Returns: bookings array, total_count, has_more
- Filters: active, scheduled, past, all
-
GET /api/advertising/bookings/summary- Returns: { active, scheduled, past, total }
GET /api/advertising/billing/summary- Returns: current_balance, month_spend, lifetime_spend, payment_method details
- Integrates with Stripe customer API
GET /api/advertising/performance?time_range={range}- Returns: summary metrics, campaign breakdown, time_series data
All components use proper TypeScript types from:
/types/advertising.ts- Core advertising types/hooks/*.ts- Hook return types- Component-specific interfaces
function ActiveBookingsList() {
// Sub-component with focused responsibility
}
export default function AdvertiserOverview() {
return <ActiveBookingsList />
}if (isLoading) return <Skeleton />
if (error) return <ErrorState />
return <DataView data={data} />{data.length === 0 ? (
<EmptyState>
<p>No data</p>
<Button>Get Started</Button>
</EmptyState>
) : (
<DataList />
)}- Shows loading immediately
- Displays data when available
- Handles errors gracefully
- Provides helpful actions
- Semantic HTML structure
- ARIA labels on interactive elements
- Keyboard navigation support
- Screen reader announcements for toasts
- Focus management
- Color contrast compliance
- 1 minute stale time (data feels instant)
- 5 minute garbage collection
- Automatic background refetching
- Request deduplication
- Lazy loading for charts
- Memoized calculations
- Efficient re-renders
- Skeleton loaders prevent layout shift
- Tree-shakeable imports
- Code splitting at route level
- Only load Recharts when needed
| Feature | Before | After |
|---|---|---|
| Billing data | $0.00 everywhere | Real Stripe data |
| Booking counts | All zeros | Live from database |
| Active bookings | Fake "Judge Smith" | Real judges, clickable |
| Campaign actions | alert() popups |
Toast notifications |
| Performance chart | "Coming soon" text | Interactive Recharts |
| Loading states | None | Skeleton loaders |
| Error handling | Generic errors | Specific, actionable messages |
| Empty states | Plain text | Helpful CTAs |
// Test custom hooks
describe('useAdvertiserBookings', () => {
it('fetches bookings with correct status filter', async () => {
const { result } = renderHook(() => useAdvertiserBookings('active'))
await waitFor(() => expect(result.current.data).toBeDefined())
})
})// Test component with API
describe('BillingManagement', () => {
it('displays billing data from API', async () => {
render(<BillingManagement {...props} />)
expect(await screen.findByText(/\$\d+\.\d{2}/)).toBeInTheDocument()
})
})// Test full user flow
test('advertiser can view and manage campaigns', async ({ page }) => {
await page.goto('/dashboard/advertiser/campaigns')
await page.click('text=Pause')
await expect(page.locator('.toast')).toContainText('Campaign paused')
})- Real-time Updates: Add WebSocket support for live metrics
- Advanced Filters: Date range pickers, multi-select filters
- Export Features: CSV/PDF export for reports
- Batch Operations: Bulk pause/resume campaigns
- A/B Testing: Compare campaign performance
- Budget Alerts: Notify when budget thresholds reached
- Predictive Analytics: ML-based performance forecasts
- ✅ Removed hardcoded mock data
- ✅ Replaced alert() with modern UI
- ✅ Added proper error boundaries
- ✅ Implemented loading states
- ✅ Type-safe API interactions
To modernize other dashboard components:
- Add React Query Hook:
// hooks/useYourData.ts
export function useYourData() {
return useQuery({
queryKey: ['your-data'],
queryFn: async () => {
const res = await fetch('/api/your-endpoint')
return res.json()
},
})
}- Update Component:
function YourComponent() {
const { data, isLoading, error } = useYourData()
if (isLoading) return <YourSkeleton />
if (error) return <ErrorState />
return <YourDataView data={data} />
}- Replace Alerts with Toasts:
import { toast } from '@/hooks/use-toast'
// Instead of: alert('Success!')
toast({
title: 'Success!',
description: 'Operation completed successfully',
})TypeScript errors about missing modules
- These are temporary compilation issues that resolve on rebuild
- Run:
npm run devto restart dev server
React Query data not updating
- Check query key uniqueness
- Verify refetchOnWindowFocus settings
- Use React Query DevTools in development
Toasts not appearing
- Ensure
<Toaster />is in provider tree - Check z-index conflicts
- Verify toast hook import
/types/advertising.ts- TypeScript types/app/api/advertising/*/route.ts- API endpoints/hooks/use*.ts- Custom React Query hooks/components/ui/skeleton.tsx- Loading UI/lib/providers/QueryProvider.tsx- Data fetching setup
All components follow the Trellis design system:
- CSS variables for theming
- Consistent spacing/sizing
- Semantic color tokens
- Accessible by default
Track these metrics to measure success:
- Time to interactive (loading states)
- Error rate (error boundaries catching issues)
- User engagement (click-through on bookings)
- Data accuracy (matches backend)
- Performance scores (lighthouse)
The advertising dashboard has been fully modernized with:
- ✅ Real API integration replacing all mock data
- ✅ Modern React patterns (hooks, suspense boundaries)
- ✅ Professional loading and error states
- ✅ Interactive charts with Recharts
- ✅ Toast notifications replacing alerts
- ✅ Type-safe data fetching with React Query
- ✅ Accessible, responsive UI components
All components are production-ready and follow React best practices.