feat: add shimmer bar when dashboard is refetching after filter change
Shows a thin animated orange bar below the filter bar while SWR revalidates, so users know their filter was applied. Hidden on initial load where the skeleton already provides feedback.
This commit is contained in:
@@ -237,7 +237,7 @@ export default function SiteDashboardPage() {
|
|||||||
// Single dashboard request replaces focused hooks (overview, pages, locations,
|
// Single dashboard request replaces focused hooks (overview, pages, locations,
|
||||||
// devices, referrers, goals). The backend runs all queries in parallel
|
// devices, referrers, goals). The backend runs all queries in parallel
|
||||||
// and caches the result in Redis for efficient data loading.
|
// and caches the result in Redis for efficient data loading.
|
||||||
const { data: dashboard, isLoading: dashboardLoading, error: dashboardError } = useDashboard(siteId, dateRange.start, dateRange.end, interval, filtersParam || undefined)
|
const { data: dashboard, isLoading: dashboardLoading, isValidating: dashboardValidating, error: dashboardError } = useDashboard(siteId, dateRange.start, dateRange.end, interval, filtersParam || undefined)
|
||||||
const { data: realtimeData } = useRealtime(siteId)
|
const { data: realtimeData } = useRealtime(siteId)
|
||||||
const { data: prevStats } = useStats(siteId, prevRange.start, prevRange.end)
|
const { data: prevStats } = useStats(siteId, prevRange.start, prevRange.end)
|
||||||
const { data: prevDailyStats } = useDailyStats(siteId, prevRange.start, prevRange.end, interval)
|
const { data: prevDailyStats } = useDailyStats(siteId, prevRange.start, prevRange.end, interval)
|
||||||
@@ -531,6 +531,13 @@ export default function SiteDashboardPage() {
|
|||||||
<FilterBar filters={filters} onRemove={handleRemoveFilter} onClear={handleClearFilters} />
|
<FilterBar filters={filters} onRemove={handleRemoveFilter} onClear={handleClearFilters} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Refetch indicator — visible when SWR is revalidating with stale data on screen */}
|
||||||
|
{dashboardValidating && !dashboardLoading && (
|
||||||
|
<div className="h-0.5 w-full rounded-full bg-neutral-100 dark:bg-neutral-800 overflow-hidden mb-2">
|
||||||
|
<div className="h-full w-1/3 rounded-full bg-brand-orange animate-[shimmer_1.2s_ease-in-out_infinite]" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Advanced Chart with Integrated Stats */}
|
{/* Advanced Chart with Integrated Stats */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<Chart
|
<Chart
|
||||||
|
|||||||
@@ -27,11 +27,16 @@ const config: Config = {
|
|||||||
'0%': { opacity: '0' },
|
'0%': { opacity: '0' },
|
||||||
'100%': { opacity: '1' },
|
'100%': { opacity: '1' },
|
||||||
},
|
},
|
||||||
|
shimmer: {
|
||||||
|
'0%': { transform: 'translateX(-100%)' },
|
||||||
|
'100%': { transform: 'translateX(400%)' },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
'cell-highlight': 'cell-highlight 0.5s ease forwards',
|
'cell-highlight': 'cell-highlight 0.5s ease forwards',
|
||||||
'cell-flash': 'cell-flash 0.6s ease forwards',
|
'cell-flash': 'cell-flash 0.6s ease forwards',
|
||||||
'fade-in': 'fade-in 150ms ease-out',
|
'fade-in': 'fade-in 150ms ease-out',
|
||||||
|
shimmer: 'shimmer 1.2s ease-in-out infinite',
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['var(--font-plus-jakarta-sans)', 'system-ui', 'sans-serif'],
|
sans: ['var(--font-plus-jakarta-sans)', 'system-ui', 'sans-serif'],
|
||||||
|
|||||||
Reference in New Issue
Block a user