feat: polish Goals & Events dashboard panel
Align visual style with Pages, Referrers, and Locations panels — flat rows with hover state, rank numbers, muted count colors, and a slide-in percentage on hover. Add empty slots for consistent height.
This commit is contained in:
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
|
||||
### Improved
|
||||
|
||||
- **Polished Goals & Events panel.** The Goals & Events block on your dashboard got a visual refresh to match the style of the Pages, Referrers, and Locations panels. Events are now ranked with a number on the left, counts are shown in a consistent style, and hovering any row reveals what percentage of total events that action accounts for — sliding in smoothly from the right.
|
||||
- **Smarter bot protection.** The security checks on shared dashboard access and organization settings now use action-specific tokens tied to each page. A token earned on one page can't be reused on another, making it harder for automated tools to bypass the captcha.
|
||||
- **More resilient under Redis outages.** If the caching layer goes down temporarily, Pulse now continues enforcing rate limits using an in-memory fallback instead of letting all traffic through unchecked. This prevents one infrastructure hiccup from snowballing into a bigger problem.
|
||||
- **Better handling of traffic bursts.** The system can now absorb 5x larger spikes of incoming events before applying backpressure. When events are dropped during extreme bursts, the system now tracks and logs exactly how many — so we can detect and respond to sustained overload before it affects your data.
|
||||
|
||||
@@ -15,6 +15,8 @@ const LIMIT = 10
|
||||
export default function GoalStats({ goalCounts, onSelectEvent }: GoalStatsProps) {
|
||||
const list = (goalCounts || []).slice(0, LIMIT)
|
||||
const hasData = list.length > 0
|
||||
const total = list.reduce((sum, r) => sum + r.count, 0)
|
||||
const emptySlots = Math.max(0, 6 - list.length)
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-2xl p-6 h-full flex flex-col">
|
||||
@@ -25,20 +27,33 @@ export default function GoalStats({ goalCounts, onSelectEvent }: GoalStatsProps)
|
||||
</div>
|
||||
|
||||
{hasData ? (
|
||||
<div className="space-y-2 flex-1 min-h-[200px]">
|
||||
{list.map((row) => (
|
||||
<div className="flex-1 min-h-[200px]">
|
||||
{list.map((row, i) => (
|
||||
<div
|
||||
key={row.event_name}
|
||||
onClick={() => onSelectEvent?.(row.event_name)}
|
||||
className={`flex items-center justify-between py-2 px-3 rounded-lg bg-neutral-50 dark:bg-neutral-800/50 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors${onSelectEvent ? ' cursor-pointer' : ''}`}
|
||||
className={`flex items-center justify-between h-9 group hover:bg-neutral-50 dark:hover:bg-neutral-800 rounded-lg px-2 -mx-2 transition-colors${onSelectEvent ? ' cursor-pointer' : ''}`}
|
||||
>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<span className="text-xs font-medium text-neutral-400 dark:text-neutral-600 w-4 text-right flex-shrink-0 tabular-nums">
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="text-sm font-medium text-neutral-900 dark:text-white truncate">
|
||||
{row.display_name ?? row.event_name.replace(/_/g, ' ')}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-brand-orange tabular-nums">
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-4">
|
||||
<span className="text-xs font-medium text-brand-orange opacity-0 translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200">
|
||||
{total > 0 ? `${Math.round((row.count / total) * 100)}%` : ''}
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-neutral-600 dark:text-neutral-400 tabular-nums">
|
||||
{formatNumber(row.count)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{Array.from({ length: emptySlots }).map((_, i) => (
|
||||
<div key={`empty-${i}`} className="h-9 px-2 -mx-2" aria-hidden="true" />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user