refactor: replace loading overlays with skeleton components for improved user experience across various pages
This commit is contained in:
@@ -4,7 +4,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { ApiError } from '@/lib/api/client'
|
||||
import { getFunnel, getFunnelStats, deleteFunnel, type Funnel, type FunnelStats } from '@/lib/api/funnels'
|
||||
import { toast, LoadingOverlay, Select, DatePicker, ChevronLeftIcon, ArrowRightIcon, TrashIcon, useTheme, Button } from '@ciphera-net/ui'
|
||||
import { toast, Select, DatePicker, ChevronLeftIcon, ArrowRightIcon, TrashIcon, useTheme, Button } from '@ciphera-net/ui'
|
||||
import { FunnelDetailSkeleton } from '@/components/skeletons'
|
||||
import Link from 'next/link'
|
||||
import {
|
||||
BarChart,
|
||||
@@ -92,7 +93,7 @@ export default function FunnelReportPage() {
|
||||
}
|
||||
|
||||
if (loading && !funnel) {
|
||||
return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Pulse" />
|
||||
return <FunnelDetailSkeleton />
|
||||
}
|
||||
|
||||
if (loadError === 'not_found' || (!funnel && !stats && !loadError)) {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { listFunnels, deleteFunnel, type Funnel } from '@/lib/api/funnels'
|
||||
import { toast, LoadingOverlay, PlusIcon, ArrowRightIcon, ChevronLeftIcon, TrashIcon, Button } from '@ciphera-net/ui'
|
||||
import { toast, PlusIcon, ArrowRightIcon, ChevronLeftIcon, TrashIcon, Button } from '@ciphera-net/ui'
|
||||
import { FunnelsListSkeleton } from '@/components/skeletons'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function FunnelsPage() {
|
||||
@@ -44,7 +45,7 @@ export default function FunnelsPage() {
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Pulse" />
|
||||
return <FunnelsListSkeleton />
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,6 +11,7 @@ import { toast } from '@ciphera-net/ui'
|
||||
import { getAuthErrorMessage } from '@ciphera-net/ui'
|
||||
import { LoadingOverlay, Button } from '@ciphera-net/ui'
|
||||
import { Select, DatePicker, DownloadIcon } from '@ciphera-net/ui'
|
||||
import { DashboardSkeleton } from '@/components/skeletons'
|
||||
import ExportModal from '@/components/dashboard/ExportModal'
|
||||
import ContentStats from '@/components/dashboard/ContentStats'
|
||||
import TopReferrers from '@/components/dashboard/TopReferrers'
|
||||
@@ -216,7 +217,7 @@ export default function SiteDashboardPage() {
|
||||
}, [siteId, dateRange, todayInterval, multiDayInterval, isSettingsLoaded, loadData, loadRealtime])
|
||||
|
||||
if (loading) {
|
||||
return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Pulse" />
|
||||
return <DashboardSkeleton />
|
||||
}
|
||||
|
||||
if (!site) {
|
||||
|
||||
@@ -6,7 +6,8 @@ import { getSite, type Site } from '@/lib/api/sites'
|
||||
import { getRealtimeVisitors, getSessionDetails, type Visitor, type SessionEvent } from '@/lib/api/realtime'
|
||||
import { toast } from '@ciphera-net/ui'
|
||||
import { getAuthErrorMessage } from '@ciphera-net/ui'
|
||||
import { LoadingOverlay, UserIcon } from '@ciphera-net/ui'
|
||||
import { UserIcon } from '@ciphera-net/ui'
|
||||
import { RealtimeSkeleton, SessionEventsSkeleton } from '@/components/skeletons'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
|
||||
function formatTimeAgo(dateString: string) {
|
||||
@@ -90,7 +91,7 @@ export default function RealtimePage() {
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Realtime" />
|
||||
if (loading) return <RealtimeSkeleton />
|
||||
if (!site) return <div className="p-8">Site not found</div>
|
||||
|
||||
return (
|
||||
@@ -197,9 +198,7 @@ export default function RealtimePage() {
|
||||
Select a visitor on the left to see their activity.
|
||||
</div>
|
||||
) : loadingEvents ? (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-neutral-900 dark:border-white"></div>
|
||||
</div>
|
||||
<SessionEventsSkeleton />
|
||||
) : (
|
||||
<div className="relative pl-6 border-l-2 border-neutral-100 dark:border-neutral-800 space-y-8">
|
||||
{sessionEvents.map((event, idx) => (
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getSite, updateSite, resetSiteData, deleteSite, type Site, type GeoData
|
||||
import { listGoals, createGoal, updateGoal, deleteGoal, type Goal } from '@/lib/api/goals'
|
||||
import { toast } from '@ciphera-net/ui'
|
||||
import { getAuthErrorMessage } from '@ciphera-net/ui'
|
||||
import { LoadingOverlay } from '@ciphera-net/ui'
|
||||
import { SettingsFormSkeleton, GoalsListSkeleton } from '@/components/skeletons'
|
||||
import VerificationModal from '@/components/sites/VerificationModal'
|
||||
import ScriptSetupBlock from '@/components/sites/ScriptSetupBlock'
|
||||
import { PasswordInput } from '@ciphera-net/ui'
|
||||
@@ -318,7 +318,26 @@ export default function SiteSettingsPage() {
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Pulse" />
|
||||
return (
|
||||
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 py-8">
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<div className="h-8 w-40 animate-pulse rounded bg-neutral-100 dark:bg-neutral-800 mb-2" />
|
||||
<div className="h-4 w-64 animate-pulse rounded bg-neutral-100 dark:bg-neutral-800" />
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-8">
|
||||
<nav className="w-full md:w-64 flex-shrink-0 space-y-1">
|
||||
{Array.from({ length: 4 }).map((_, i) => (
|
||||
<div key={i} className="h-12 animate-pulse rounded-xl bg-neutral-100 dark:bg-neutral-800" />
|
||||
))}
|
||||
</nav>
|
||||
<div className="flex-1 bg-white dark:bg-neutral-900 rounded-2xl border border-neutral-200 dark:border-neutral-800 p-6 md:p-8">
|
||||
<SettingsFormSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!site) {
|
||||
@@ -970,7 +989,7 @@ export default function SiteSettingsPage() {
|
||||
</p>
|
||||
</div>
|
||||
{goalsLoading ? (
|
||||
<div className="py-8 text-center text-neutral-500 dark:text-neutral-400">Loading goals…</div>
|
||||
<GoalsListSkeleton />
|
||||
) : (
|
||||
<>
|
||||
{canEdit && (
|
||||
|
||||
@@ -20,7 +20,8 @@ import {
|
||||
import { toast } from '@ciphera-net/ui'
|
||||
import { useTheme } from '@ciphera-net/ui'
|
||||
import { getAuthErrorMessage } from '@ciphera-net/ui'
|
||||
import { LoadingOverlay, Button, Modal } from '@ciphera-net/ui'
|
||||
import { Button, Modal } from '@ciphera-net/ui'
|
||||
import { UptimeSkeleton, ChecksSkeleton } from '@/components/skeletons'
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
@@ -510,9 +511,7 @@ function MonitorCard({
|
||||
|
||||
{/* Response time chart */}
|
||||
{loadingChecks ? (
|
||||
<div className="text-center py-4 text-neutral-500 dark:text-neutral-400 text-sm">
|
||||
Loading checks...
|
||||
</div>
|
||||
<ChecksSkeleton />
|
||||
) : checks.length > 0 ? (
|
||||
<>
|
||||
<ResponseTimeChart checks={checks} />
|
||||
@@ -704,7 +703,7 @@ export default function UptimePage() {
|
||||
setShowEditModal(true)
|
||||
}
|
||||
|
||||
if (loading) return <LoadingOverlay logoSrc="/pulse_icon_no_margins.png" title="Uptime" />
|
||||
if (loading) return <UptimeSkeleton />
|
||||
if (!site) return <div className="p-8 text-neutral-500">Site not found</div>
|
||||
|
||||
const monitors = Array.isArray(uptimeData?.monitors) ? uptimeData.monitors : []
|
||||
|
||||
Reference in New Issue
Block a user