From cc268c320e1dbc94107b8c7bffdd70f7e0824651 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sat, 7 Mar 2026 19:10:23 +0100 Subject: [PATCH] feat: replace ghost buttons with underline tab bar for site navigation Dashboard, Uptime, Funnels, and Settings now use a consistent underline tab bar with orange active indicator, matching the existing panel tab design language. --- CHANGELOG.md | 9 ++++++ app/sites/[id]/funnels/page.tsx | 25 ++++++--------- app/sites/[id]/page.tsx | 38 ++++------------------ app/sites/[id]/settings/page.tsx | 3 ++ app/sites/[id]/uptime/page.tsx | 18 ++++------- components/dashboard/SiteNav.tsx | 54 ++++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 59 deletions(-) create mode 100644 components/dashboard/SiteNav.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 76af938..dbcb278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Improved + +- **Cleaner site navigation.** Dashboard, Uptime, Funnels, and Settings now use an underline tab bar instead of floating buttons. The active section is highlighted with an orange underline, making it easy to see where you are and switch between views. + +### Fixed + +- **City and region data is now accurate.** Location data was incorrectly showing the CDN server's location (e.g. Paris, Villeurbanne) instead of the visitor's actual city. Fixed by reading the correct visitor IP header from Bunny CDN. +- **"Reset Data" now clears everything.** Previously, resetting a site's data in Settings only removed pageviews and daily stats. Uptime check history, uptime daily stats, and cached dashboard data were left behind. All collected data is now properly cleared when you reset, while your site configuration, goals, funnels, and uptime monitors are kept. + ## [0.13.0-alpha] - 2026-03-07 ### Added diff --git a/app/sites/[id]/funnels/page.tsx b/app/sites/[id]/funnels/page.tsx index 2b565c4..ff3e0e9 100644 --- a/app/sites/[id]/funnels/page.tsx +++ b/app/sites/[id]/funnels/page.tsx @@ -6,6 +6,7 @@ import { listFunnels, deleteFunnel, type Funnel } from '@/lib/api/funnels' import { toast, PlusIcon, ArrowRightIcon, ChevronLeftIcon, TrashIcon, Button } from '@ciphera-net/ui' import { FunnelsListSkeleton, useMinimumLoading } from '@/components/skeletons' import Link from 'next/link' +import SiteNav from '@/components/dashboard/SiteNav' export default function FunnelsPage() { const params = useParams() @@ -52,14 +53,10 @@ export default function FunnelsPage() { return (
+ +
-
- - - +

Funnels @@ -68,14 +65,12 @@ export default function FunnelsPage() { Track user journeys and identify drop-off points

-
- - - -
+ + +
{funnels.length === 0 ? ( diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx index 4c878d0..61809c0 100644 --- a/app/sites/[id]/page.tsx +++ b/app/sites/[id]/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useAuth } from '@/lib/auth/context' + import { logger } from '@/lib/utils/logger' import { useCallback, useEffect, useState, useMemo } from 'react' import { useParams, useRouter, useSearchParams } from 'next/navigation' @@ -34,6 +34,7 @@ import PerformanceStats from '@/components/dashboard/PerformanceStats' import GoalStats from '@/components/dashboard/GoalStats' import ScrollDepth from '@/components/dashboard/ScrollDepth' import Campaigns from '@/components/dashboard/Campaigns' +import SiteNav from '@/components/dashboard/SiteNav' import FilterBar from '@/components/dashboard/FilterBar' import AddFilterDropdown, { type FilterSuggestion, type FilterSuggestions } from '@/components/dashboard/AddFilterDropdown' import EventProperties from '@/components/dashboard/EventProperties' @@ -79,8 +80,8 @@ function getInitialDateRange(): { start: string; end: string } { } export default function SiteDashboardPage() { - const { user } = useAuth() - const canEdit = user?.role === 'owner' || user?.role === 'admin' + + const params = useParams() const router = useRouter() @@ -494,39 +495,12 @@ export default function SiteDashboardPage() { ]} />
-
-
- - - {canEdit && ( - - )} -
+ + {/* Dimension Filters */}
diff --git a/app/sites/[id]/settings/page.tsx b/app/sites/[id]/settings/page.tsx index e5d34ea..fefa324 100644 --- a/app/sites/[id]/settings/page.tsx +++ b/app/sites/[id]/settings/page.tsx @@ -15,6 +15,7 @@ import { APP_URL } from '@/lib/api/client' import { generatePrivacySnippet } from '@/lib/utils/privacySnippet' import { useUnsavedChanges } from '@/lib/hooks/useUnsavedChanges' import { getSubscription, type SubscriptionDetails } from '@/lib/api/billing' +import SiteNav from '@/components/dashboard/SiteNav' import { getRetentionOptionsForPlan, formatRetentionMonths } from '@/lib/plans' import { motion, AnimatePresence } from 'framer-motion' import { useAuth } from '@/lib/auth/context' @@ -403,6 +404,8 @@ export default function SiteSettingsPage() { return (
+ +

Site Settings

diff --git a/app/sites/[id]/uptime/page.tsx b/app/sites/[id]/uptime/page.tsx index 85a666b..09ac33c 100644 --- a/app/sites/[id]/uptime/page.tsx +++ b/app/sites/[id]/uptime/page.tsx @@ -21,6 +21,7 @@ import { toast } from '@ciphera-net/ui' import { useTheme } from '@ciphera-net/ui' import { getAuthErrorMessage } from '@ciphera-net/ui' import { Button, Modal } from '@ciphera-net/ui' +import SiteNav from '@/components/dashboard/SiteNav' import { UptimeSkeleton, ChecksSkeleton, useMinimumLoading } from '@/components/skeletons' import { AreaChart, @@ -723,21 +724,14 @@ export default function UptimePage() { transition={{ duration: 0.2 }} className="w-full max-w-6xl mx-auto px-4 sm:px-6 py-8" > + + {/* Header */}
-
- - / -

- Uptime -

-
+

+ Uptime +

Monitor your endpoints and track availability over time

diff --git a/components/dashboard/SiteNav.tsx b/components/dashboard/SiteNav.tsx new file mode 100644 index 0000000..0c3bae6 --- /dev/null +++ b/components/dashboard/SiteNav.tsx @@ -0,0 +1,54 @@ +'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard' +import { useAuth } from '@/lib/auth/context' + +interface SiteNavProps { + siteId: string +} + +export default function SiteNav({ siteId }: SiteNavProps) { + const pathname = usePathname() + const handleTabKeyDown = useTabListKeyboard() + const { user } = useAuth() + const canEdit = user?.role === 'owner' || user?.role === 'admin' + + const tabs = [ + { label: 'Dashboard', href: `/sites/${siteId}` }, + { label: 'Uptime', href: `/sites/${siteId}/uptime` }, + { label: 'Funnels', href: `/sites/${siteId}/funnels` }, + ...(canEdit ? [{ label: 'Settings', href: `/sites/${siteId}/settings` }] : []), + ] + + const isActive = (href: string) => { + if (href === `/sites/${siteId}`) { + return pathname === href || pathname === `${href}/realtime` + } + return pathname.startsWith(href) + } + + return ( +
+ +
+ ) +}