- |
+ |
{org.business_name || 'N/A'}
|
diff --git a/app/admin/page.tsx b/app/admin/page.tsx
index e4aa99d..05bb448 100644
--- a/app/admin/page.tsx
+++ b/app/admin/page.tsx
@@ -9,9 +9,9 @@ export default function AdminDashboard() {
href="/admin/orgs"
className="block transition-transform hover:scale-[1.02] rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm"
>
- Organizations
- Manage organization plans and limits
-
+ Organizations
+ Manage organization plans and limits
+
View all organizations, check billing status, and manually grant plans.
@@ -19,9 +19,9 @@ export default function AdminDashboard() {
href="/admin/filtered-traffic"
className="block transition-transform hover:scale-[1.02] rounded-2xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-sm"
>
- Filtered Traffic
- Monitor blocked referrer spam
-
+ Filtered Traffic
+ Monitor blocked referrer spam
+
View domains blocked by the spam filter and check for false positives.
diff --git a/app/changelog/page.tsx b/app/changelog/page.tsx
index c0b9c84..39d8366 100644
--- a/app/changelog/page.tsx
+++ b/app/changelog/page.tsx
@@ -18,7 +18,7 @@ export default function ChangelogPage() {
return (
-
+
Changelog
diff --git a/app/notifications/page.tsx b/app/notifications/page.tsx
index 1e7ea60..eb31ee3 100644
--- a/app/notifications/page.tsx
+++ b/app/notifications/page.tsx
@@ -122,8 +122,8 @@ export default function NotificationsPage() {
)}
- Notifications
-
+ Notifications
+
Manage which notifications you receive in{' '}
Organization Settings → Notifications
@@ -137,7 +137,7 @@ export default function NotificationsPage() {
{error}
) : notifications.length === 0 ? (
-
+
No notifications yet
Manage which notifications you receive in{' '}
@@ -159,11 +159,11 @@ export default function NotificationsPage() {
{getTypeIcon(n.type)}
-
+
{n.title}
{n.body && (
- {n.body}
+ {n.body}
)}
{formatTimeAgo(n.created_at)}
@@ -182,11 +182,11 @@ export default function NotificationsPage() {
{getTypeIcon(n.type)}
-
+
{n.title}
{n.body && (
- {n.body}
+ {n.body}
)}
{formatTimeAgo(n.created_at)}
diff --git a/app/onboarding/page.tsx b/app/onboarding/page.tsx
index 72b5307..dcbd41f 100644
--- a/app/onboarding/page.tsx
+++ b/app/onboarding/page.tsx
@@ -47,7 +47,7 @@ export default function OnboardingPage() {
-
+
Welcome to Pulse
diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx
index c5b00b9..664999e 100644
--- a/app/share/[id]/page.tsx
+++ b/app/share/[id]/page.tsx
@@ -195,7 +195,7 @@ export default function PublicDashboardPage() {
-
+
Protected Dashboard
@@ -210,7 +210,7 @@ export default function PublicDashboardPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter password"
- className="w-full px-4 py-2 border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-800 text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange focus:border-transparent"
+ className="w-full px-4 py-2 border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-800 text-white focus:ring-2 focus:ring-brand-orange focus:border-transparent"
autoFocus
/>
@@ -270,7 +270,7 @@ export default function PublicDashboardPage() {
Public Dashboard
-
+
void }) {
+ return (
+
+ )
+}
diff --git a/app/sites/[id]/behavior/page.tsx b/app/sites/[id]/behavior/page.tsx
index 8e0a2c4..050b635 100644
--- a/app/sites/[id]/behavior/page.tsx
+++ b/app/sites/[id]/behavior/page.tsx
@@ -2,7 +2,7 @@
import { useCallback, useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
-import { getDateRange, formatDate } from '@ciphera-net/ui'
+import { getDateRange, formatDate, getThisWeekRange, getThisMonthRange } from '@/lib/utils/dateRanges'
import { Select, DatePicker } from '@ciphera-net/ui'
import dynamic from 'next/dynamic'
import { getRageClicks, getDeadClicks } from '@/lib/api/stats'
@@ -15,20 +15,6 @@ import { BehaviorSkeleton, useMinimumLoading, useSkeletonFade } from '@/componen
const ScrollDepth = dynamic(() => import('@/components/dashboard/ScrollDepth'))
-function getThisWeekRange(): { start: string; end: string } {
- const today = new Date()
- const dayOfWeek = today.getDay()
- const monday = new Date(today)
- monday.setDate(today.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1))
- return { start: formatDate(monday), end: formatDate(today) }
-}
-
-function getThisMonthRange(): { start: string; end: string } {
- const today = new Date()
- const firstOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
- return { start: formatDate(firstOfMonth), end: formatDate(today) }
-}
-
export default function BehaviorPage() {
const params = useParams()
const siteId = params.id as string
@@ -74,10 +60,10 @@ export default function BehaviorPage() {
{/* Header */}
-
+
Behavior
-
+
Frustration signals and user engagement patterns
diff --git a/app/sites/[id]/cdn/error.tsx b/app/sites/[id]/cdn/error.tsx
new file mode 100644
index 0000000..e11400b
--- /dev/null
+++ b/app/sites/[id]/cdn/error.tsx
@@ -0,0 +1,13 @@
+'use client'
+
+import ErrorDisplay from '@/components/ErrorDisplay'
+
+export default function CDNError({ reset }: { error: Error; reset: () => void }) {
+ return (
+
+ )
+}
diff --git a/app/sites/[id]/cdn/page.tsx b/app/sites/[id]/cdn/page.tsx
index a244b55..b8872d0 100644
--- a/app/sites/[id]/cdn/page.tsx
+++ b/app/sites/[id]/cdn/page.tsx
@@ -177,10 +177,10 @@ export default function CDNPage() {
-
+
Connect BunnyCDN
-
+
Monitor your CDN performance including bandwidth usage, cache hit rates, request volumes, and geographic distribution.
-
+
CDN Analytics
-
+
BunnyCDN performance, bandwidth, and cache metrics
@@ -281,7 +281,7 @@ export default function CDNPage() {
{/* Bandwidth chart */}
- Bandwidth
+ Bandwidth
{daily.length > 0 ? (
@@ -317,8 +317,8 @@ export default function CDNPage() {
if (!active || !payload?.length) return null
return (
- {formatDateShort(label)}
-
+ {formatDateShort(label)}
+
Total: {formatBytes(payload[0]?.value as number)}
{payload[1] && (
@@ -359,7 +359,7 @@ export default function CDNPage() {
{/* Requests chart */}
- Requests
+ Requests
{daily.length > 0 ? (
@@ -385,8 +385,8 @@ export default function CDNPage() {
if (!active || !payload?.length) return null
return (
- {formatDateShort(label)}
-
+ {formatDateShort(label)}
+
{formatNumber(payload[0]?.value as number)} requests
@@ -405,7 +405,7 @@ export default function CDNPage() {
{/* Errors chart */}
- Errors
+ Errors
{daily.length > 0 ? (
- {formatDateShort(label)}
+ {formatDateShort(label)}
{payload.map((entry) => (
{entry.name}: {formatNumber(entry.value as number)}
@@ -464,7 +464,7 @@ export default function CDNPage() {
{/* Traffic Distribution */}
- Traffic Distribution
+ Traffic Distribution
{countries.length > 0 ? (
<>
@@ -480,9 +480,9 @@ export default function CDNPage() {
{cc && getFlagIcon(cc)}
- {city}
+ {city}
-
+
{formatBytes(row.bandwidth)}
@@ -530,13 +530,13 @@ function OverviewCard({
return (
- {label}
- {value}
+ {label}
+ {value}
{changeLabel && (
{changeLabel} vs previous period
diff --git a/app/sites/[id]/funnels/[funnelId]/page.tsx b/app/sites/[id]/funnels/[funnelId]/page.tsx
index d8fe3c8..0ce4fe1 100644
--- a/app/sites/[id]/funnels/[funnelId]/page.tsx
+++ b/app/sites/[id]/funnels/[funnelId]/page.tsx
@@ -25,7 +25,7 @@ export default function FunnelReportPage() {
const [funnel, setFunnel] = useState (null)
const [stats, setStats] = useState(null)
const [loading, setLoading] = useState(true)
- const [dateRange, setDateRange] = useState(getDateRange(30))
+ const [dateRange, setDateRange] = useState(() => getDateRange(30))
const [datePreset, setDatePreset] = useState<'7' | '30' | 'custom'>('30')
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
const [loadError, setLoadError] = useState<'not_found' | 'forbidden' | 'error' | null>(null)
@@ -154,7 +154,7 @@ export default function FunnelReportPage() {
-
+
{funnel.name}
{funnel.description && (
@@ -236,7 +236,7 @@ export default function FunnelReportPage() {
{trends && trends.dates.length > 1 && (
-
+
Conversion Trends
@@ -322,10 +322,10 @@ export default function FunnelReportPage() {
- | Step |
- Visitors |
- Drop-off |
- Conversion |
+ Step |
+ Visitors |
+ Drop-off |
+ Conversion |
@@ -338,13 +338,13 @@ export default function FunnelReportPage() {
{i + 1}
- {step.step.name}
- {step.step.value}
+ {step.step.name}
+ {step.step.value}
-
+
{step.visitors.toLocaleString()}
|
diff --git a/app/sites/[id]/funnels/page.tsx b/app/sites/[id]/funnels/page.tsx
index cec7b5b..5638bf0 100644
--- a/app/sites/[id]/funnels/page.tsx
+++ b/app/sites/[id]/funnels/page.tsx
@@ -40,7 +40,7 @@ export default function FunnelsPage() {
-
+
Funnels
@@ -65,7 +65,7 @@ export default function FunnelsPage() {
className="mb-6"
unoptimized
/>
-
+
No funnels yet
@@ -89,7 +89,7 @@ export default function FunnelsPage() {
-
+
{funnel.name}
{funnel.description && (
diff --git a/app/sites/[id]/journeys/page.tsx b/app/sites/[id]/journeys/page.tsx
index 10d66e6..94fab56 100644
--- a/app/sites/[id]/journeys/page.tsx
+++ b/app/sites/[id]/journeys/page.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
import { motion } from 'framer-motion'
-import { getDateRange, formatDate } from '@ciphera-net/ui'
+import { getDateRange, formatDate, getThisWeekRange, getThisMonthRange } from '@/lib/utils/dateRanges'
import { Select, DatePicker } from '@ciphera-net/ui'
import ColumnJourney from '@/components/journeys/ColumnJourney'
import SankeyJourney from '@/components/journeys/SankeyJourney'
@@ -18,20 +18,6 @@ import {
const DEFAULT_DEPTH = 4
-function getThisWeekRange(): { start: string; end: string } {
- const today = new Date()
- const dayOfWeek = today.getDay()
- const monday = new Date(today)
- monday.setDate(today.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1))
- return { start: formatDate(monday), end: formatDate(today) }
-}
-
-function getThisMonthRange(): { start: string; end: string } {
- const today = new Date()
- const firstOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
- return { start: formatDate(firstOfMonth), end: formatDate(today) }
-}
-
export default function JourneysPage() {
const params = useParams()
const siteId = params.id as string
@@ -91,10 +77,10 @@ export default function JourneysPage() {
{/* Header */}
-
+
Journeys
-
+
How visitors navigate through your site
@@ -143,7 +129,7 @@ export default function JourneysPage() {
{/* Depth slider */}
-
+
2 steps
{depth} steps deep
@@ -196,7 +182,7 @@ export default function JourneysPage() {
aria-selected={viewMode === mode}
className={`relative px-3 py-1 text-xs font-medium transition-colors capitalize focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-orange rounded cursor-pointer ${
viewMode === mode
- ? 'text-neutral-900 dark:text-white'
+ ? 'text-white'
: 'text-neutral-400 dark:text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300'
}`}
>
@@ -232,7 +218,7 @@ export default function JourneysPage() {
{/* Footer */}
{totalSessions > 0 && (
-
+
{totalSessions.toLocaleString()} sessions tracked
)}
diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx
index 5ad39da..892f135 100644
--- a/app/sites/[id]/page.tsx
+++ b/app/sites/[id]/page.tsx
@@ -17,7 +17,7 @@ import {
type Stats,
type DailyStat,
} from '@/lib/api/stats'
-import { getDateRange, formatDate } from '@ciphera-net/ui'
+import { getDateRange, formatDate, getThisWeekRange, getThisMonthRange } from '@/lib/utils/dateRanges'
import { toast } from '@ciphera-net/ui'
import { Button } from '@ciphera-net/ui'
import { Select, DatePicker, DownloadIcon } from '@ciphera-net/ui'
@@ -63,19 +63,6 @@ function loadSavedSettings(): {
}
}
-function getThisWeekRange(): { start: string; end: string } {
- const today = new Date()
- const dayOfWeek = today.getDay()
- const monday = new Date(today)
- monday.setDate(today.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1))
- return { start: formatDate(monday), end: formatDate(today) }
-}
-
-function getThisMonthRange(): { start: string; end: string } {
- const today = new Date()
- const firstOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
- return { start: formatDate(firstOfMonth), end: formatDate(today) }
-}
function getInitialDateRange(): { start: string; end: string } {
const settings = loadSavedSettings()
@@ -442,7 +429,7 @@ export default function SiteDashboardPage() {
-
+
{site.name}
diff --git a/app/sites/[id]/pagespeed/error.tsx b/app/sites/[id]/pagespeed/error.tsx
new file mode 100644
index 0000000..04362bb
--- /dev/null
+++ b/app/sites/[id]/pagespeed/error.tsx
@@ -0,0 +1,13 @@
+'use client'
+
+import ErrorDisplay from '@/components/ErrorDisplay'
+
+export default function PageSpeedError({ reset }: { error: Error; reset: () => void }) {
+ return (
+
+ )
+}
diff --git a/app/sites/[id]/pagespeed/page.tsx b/app/sites/[id]/pagespeed/page.tsx
index 0e9354b..4eb5f27 100644
--- a/app/sites/[id]/pagespeed/page.tsx
+++ b/app/sites/[id]/pagespeed/page.tsx
@@ -9,6 +9,7 @@ import { toast, Button } from '@ciphera-net/ui'
import { motion } from 'framer-motion'
import ScoreGauge from '@/components/pagespeed/ScoreGauge'
import { AreaChart as VisxAreaChart, Area as VisxArea, Grid as VisxGrid, XAxis as VisxXAxis, YAxis as VisxYAxis, ChartTooltip as VisxChartTooltip } from '@/components/ui/area-chart'
+import { useMinimumLoading, useSkeletonFade } from '@/components/skeletons'
// * Metric status thresholds (Google's Core Web Vitals thresholds)
function getMetricStatus(metric: string, value: number | null): { label: string; color: string } {
@@ -223,8 +224,10 @@ export default function PageSpeedPage() {
}
}
- // * Loading state
- if (isLoading && !latestChecks) return
+ // * Loading state with minimum display time (consistent with other pages)
+ const showSkeleton = useMinimumLoading(isLoading && !latestChecks)
+ const fadeClass = useSkeletonFade(showSkeleton)
+ if (showSkeleton) return
if (!site) return Site not found
const enabled = config?.enabled ?? false
@@ -235,10 +238,10 @@ export default function PageSpeedPage() {
{/* Header */}
-
+
PageSpeed
-
+
Monitor your site's performance and Core Web Vitals
@@ -246,14 +249,14 @@ export default function PageSpeedPage() {
{/* Empty state */}
-
+
PageSpeed monitoring is disabled
-
+
Enable PageSpeed monitoring to track your site's performance scores, Core Web Vitals, and get actionable improvement suggestions.
@@ -263,7 +266,7 @@ export default function PageSpeedPage() {
|