wacrm/src/components/dashboard/empty-state.tsx
Arnas Donauskas ed669424b6
feat(dashboard): replace placeholder with real analytics
Rewrites the dashboard page as a data-driven analytics surface using
existing Supabase tables only — no schema changes, no other pages
touched.

Widgets (all render independent skeletons while their data fetches,
and show a shared empty state when there's not enough data to be
useful):

- Metric cards (Active Conversations, New Contacts Today, Open Deals
  Value, Messages Sent Today) with up/down/no-change delta indicator.
- Quick actions row (New Contact / Deal / Broadcast / Automation).
- Conversations Over Time: SVG line chart, 7/30/90-day tabs, per-day
  hover crosshair + tooltip. Cached per range so tab switches are
  instant.
- Pipeline Value: SVG donut with per-stage arc segments, total in the
  center, legend with deal count + value.
- Average First Response Time: SVG bar chart by weekday with a
  configurable dashed target line and this-week / last-week summary.
- Activity feed: latest 20 items merged across messages, contacts,
  deals, broadcasts, and automation logs — click-through deep-links
  where applicable.

Deviations from spec, noted up-front:
- Strictly dark theme — spec's #f9fafb alternating rows translate to
  slate-900 / slate-900/40 stripes.
- Kept Inter (tabular-nums, bold 28px) for metric values instead of
  Space Grotesk to avoid touching root layout.
- Quick actions link to the relevant list pages (/contacts, etc.)
  rather than auto-opening their create modals — that would require
  modifying those pages.

All aggregation is client-side via RLS-scoped queries. If a tenant's
dataset outgrows a few thousand messages per range window, the
response-time and line-chart queries should migrate to SQL RPCs —
noted as follow-up, not shipped here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 22:16:00 +03:00

37 lines
1.1 KiB
TypeScript

import { BarChart3 } from 'lucide-react'
import type { ComponentType } from 'react'
import { cn } from '@/lib/utils'
/**
* Shared empty-state panel for charts that can't render meaningfully
* without a minimum amount of data. Kept minimal and uniform so the
* three empty states on the dashboard don't each feel like a
* different widget.
*/
export function EmptyState({
title = 'Not enough data yet',
hint,
icon: Icon = BarChart3,
className,
}: {
title?: string
hint?: string
icon?: ComponentType<{ className?: string }>
className?: string
}) {
return (
<div
className={cn(
'flex h-full min-h-40 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-slate-800 bg-slate-900/40 px-4 py-6 text-center',
className,
)}
>
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-slate-800 text-slate-500">
<Icon className="h-5 w-5" />
</div>
<p className="text-sm font-medium text-slate-300">{title}</p>
{hint && <p className="max-w-xs text-xs text-slate-500">{hint}</p>}
</div>
)
}