Smooth out the jarring visual pop when loading skeletons are replaced
by real content. Only animates after an actual skeleton was shown —
cached data still renders instantly with no delay.
Replace manual useState/useEffect fetch patterns with SWR hooks so
cached data renders instantly on tab revisit. Skeleton loading now
only appears on the initial cold load, not every navigation.
New hooks: useFunnels, useUptimeStatus, useGoals, useReportSchedules,
useSubscription — all with background revalidation.
Remove the individual session journey page and make the live visitor
count a static indicator. Prepares for the new aggregated User Journeys
feature (v0.17).
Replaces 4 separate frustration API calls with single useBehavior hook.
Removes manual fetchData, loading/error state, and refresh interval—SWR
handles caching, revalidation, and error state automatically.
Frustration APIs and dashboard API are separate — when frustration
calls fail, Scroll Depth still rendered from cached SWR data,
creating a broken mixed state. Now tracks error state and hides
the bottom section entirely on failure.
- Remove column headers for cleaner look
- Show secondary info (avg, sessions, last seen) on hover
- Add orange percentage badge that slides in on hover
- Add empty row padding for consistent card height
- Add column headers to rage/dead click tables
- Rich empty states with icons matching dashboard pattern
- Add frustration trend comparison chart (current vs previous period)
- Show "New" badge instead of misleading "+100%" when previous period is 0
- Click-to-copy on CSS selectors with toast feedback
- Normalize min-height to 270px for consistent card sizing
- Fix page title to include site domain (Behavior · domain | Pulse)
- Add "last seen" column with relative timestamps
Add send hour, day of week/month selectors to report schedule modal.
Schedule cards now show descriptive delivery times like
"Every Monday at 9:00 AM (UTC)". Timezone picker moved into modal.
Replace 5-second setInterval polling with EventSource connection to the
new /realtime/stream SSE endpoint. The server pushes visitor updates
instead of each client independently polling. Auto-reconnects on
connection drops.
Replace useDashboardOverview, useDashboardPages, useDashboardLocations,
useDashboardDevices, useDashboardReferrers, useDashboardPerformance, and
useDashboardGoals with a single useDashboard hook that calls the existing
/dashboard batch endpoint. This endpoint runs all queries in parallel on
the backend and caches the result in Redis (30s TTL).
Reduces dashboard requests from 12 to 6 per refresh cycle (50% reduction).
At 1,000 concurrent users: ~6,000 req/min instead of 12,000.
The setInterval that drives the "Live · Xs ago" display was at the page level,
forcing all 10+ dashboard components to re-render every second. Now it lives
inside Chart — the only consumer — so the rest of the dashboard is unaffected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When navigating to dashboard with cached SWR data, setLastUpdatedAt
triggered a second full render of the entire dashboard immediately
after the first. Using a ref instead avoids this — the value still
updates and Chart reads it on the next 1-second tick re-render.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DottedMap: move createMap, stagger helpers, and base dots path to module
scope so they compute once on module load and survive unmount/remount
cycles — eliminates all recomputation when switching tabs.
Dashboard: restore static imports for the 5 above-fold components
(Chart, ContentStats, TopReferrers, Locations, TechSpecs) now that
their heavy computations are memoized. Keeps below-fold components
(PerformanceStats, GoalStats, ScrollDepth, Campaigns, etc.) dynamic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memoize createMap() in DottedMap (was regenerating 8000 SVG dots every
render) and convert 11 heavy dashboard components to next/dynamic imports
so the page shell renders instantly instead of blocking on one massive
render pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SiteNav now lives in the [id] layout instead of each page, so it stays
mounted during route transitions. Switching between Dashboard, Uptime,
Funnels, and Settings no longer flashes a full-page skeleton.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drops in the exact 21st.dev FunnelChart component with motion/react,
curved bezier segments, layered rings, and spring hover animations.
Removes the previous custom SVG implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the recharts BarChart with a custom funnel component using clip-path
trapezoid segments, framer-motion animations, and hover interactions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ChartContainer, ChartConfig, ChartTooltip, ChartTooltipContent
primitives ported from ShadCN's chart pattern. Refactor all 3 chart
locations (dashboard, funnels, uptime) to use CSS variable-driven
theming instead of duplicated CHART_COLORS_LIGHT/DARK objects.
- Add --chart-1 through --chart-5, --chart-grid, --chart-axis CSS vars
- Remove duplicated color objects from 3 files (-223 lines)
- Add accessibilityLayer to all charts
- Rounded bar corners on funnel chart
- Tooltips use Tailwind dark classes instead of imperative style props
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Custom calendar (DatePicker) instead of native date input
- Custom dropdown (Select) instead of native select
- EU date format (DD/MM/YYYY) in tooltips and form
- Right-click context menu on chart to add annotations
- Optional time field (HH:MM) for precise timestamps
- Escape key to dismiss, loading state on save
- Bump @ciphera-net/ui to 0.0.95
Adds toggle in Data & Privacy > Filtering section to exclude entries
where geographic data could not be resolved from location stats.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dashboard, Uptime, Funnels, and Settings now use a consistent
underline tab bar with orange active indicator, matching the
existing panel tab design language.
Pair Campaigns with Goals & Events in a half-width grid to avoid empty
space. Show medium and campaign on a visible second line under source
name instead of subtle inline text.
Rebuild Campaigns from table layout to simple row-based design matching
Content, Referrers, Locations, and Technology panels. Add click-to-filter
by utm_source. Change filter button icon from plus to funnel.
Filter dropdowns previously only showed ~10 values from cached dashboard
data. Now lazy-loads up to 100 values per dimension when the dropdown
opens. Also removes duplicate "Direct" entry from referrer suggestions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the 7-chip row with a single "+ Filter" button that opens a
compact popover: dimension list → operator + search + values. Much
cleaner default state, same functionality.
Dimension chips (Page, Referrer, Country, Browser, OS, Device + More)
with popover dropdowns showing real values from current dashboard data
with counts. Operators inline, search/type custom values, click to apply.
- Deduplicate filters so clicking the same item twice doesn't stack identical pills
- Normalize "Direct" referrer to empty string so direct traffic filtering works
- Pass active filters through to Campaigns component so it respects dashboard filters