Merge pull request #48 from ciphera-net/staging

Site verification status UI
This commit is contained in:
Usman
2026-03-13 17:20:12 +01:00
committed by GitHub
5 changed files with 38 additions and 13 deletions

View File

@@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Improved
- **Sites now show their verification status.** Each site on your dashboard now displays either a green "Active" badge (if verified) or an amber "Unverified" badge. When you verify your tracking script installation, the status is saved permanently — no more showing "Active" for sites that haven't been set up yet.
- **Verification status visible in Settings too.** Once your tracking script is verified, the Settings page shows a green confirmation bar instead of the verify button — so you can tell at a glance that everything is working. A "Re-verify" link is still there if you ever need to check again.
- **Cleaner page paths in your reports.** Pages like `/products?_t=123456` or `/about?session=abc` now correctly show as `/products` and `/about`. Only marketing attribution parameters (like UTM tags) are preserved for traffic source tracking — all other junk parameters are automatically removed, so your Top Pages and Journeys stay clean without us having to chase down every new parameter format.
- **Easier to hover country dots on the map.** The orange location markers on the world map are now much easier to interact with — you no longer need pixel-perfect aim to see the tooltip.
- **Smoother chart curves and filled area.** The dashboard chart line now flows with natural curves instead of sharp flat tops at peaks. The area beneath the line is filled with a soft transparent orange gradient that fades toward the bottom, making trends easier to read at a glance.

View File

@@ -713,13 +713,17 @@ export default function SiteSettingsPage() {
<button
type="button"
onClick={() => setShowVerificationModal(true)}
className="flex items-center gap-2 px-4 py-2 bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-neutral-700 text-neutral-700 dark:text-neutral-300 rounded-xl hover:bg-neutral-50 dark:hover:bg-neutral-700 transition-all text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-orange focus-visible:ring-offset-2"
className={`flex items-center gap-2 px-4 py-2 border rounded-xl transition-all text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${
site.is_verified
? 'bg-green-50 dark:bg-green-900/10 border-green-200 dark:border-green-900/30 text-green-700 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-900/20 focus-visible:ring-green-500'
: 'bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-700 text-neutral-700 dark:text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-700 focus-visible:ring-brand-orange'
}`}
>
<ZapIcon className="w-4 h-4" />
Verify Installation
{site.is_verified ? <CheckIcon className="w-4 h-4" /> : <ZapIcon className="w-4 h-4" />}
{site.is_verified ? 'Verified' : 'Verify Installation'}
</button>
<p className="text-xs text-neutral-500 dark:text-neutral-400">
Check if your site is sending data correctly.
{site.is_verified ? 'Your site is sending data correctly.' : 'Check if your site is sending data correctly.'}
</p>
</div>
</div>
@@ -1614,6 +1618,7 @@ export default function SiteSettingsPage() {
isOpen={showVerificationModal}
onClose={() => setShowVerificationModal(false)}
site={site}
onVerified={() => mutateSite()}
/>
</div>
)

View File

@@ -62,13 +62,22 @@ function SiteCard({ site, stats, statsLoading, onDelete, canDelete }: SiteCardPr
</div>
</div>
<div className="flex items-center gap-2 rounded-full bg-green-50 px-2 py-1 text-xs font-medium text-green-700 dark:bg-green-900/20 dark:text-green-400">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
</span>
Active
</div>
{site.is_verified ? (
<div className="flex items-center gap-2 rounded-full bg-green-50 px-2 py-1 text-xs font-medium text-green-700 dark:bg-green-900/20 dark:text-green-400">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
</span>
Active
</div>
) : (
<div className="flex items-center gap-2 rounded-full bg-amber-50 px-2 py-1 text-xs font-medium text-amber-700 dark:bg-amber-900/20 dark:text-amber-400">
<span className="relative flex h-2 w-2">
<span className="relative inline-flex rounded-full h-2 w-2 bg-amber-500"></span>
</span>
Unverified
</div>
)}
</div>
{/* Mini Stats Grid */}

View File

@@ -9,7 +9,7 @@ import {
AlertTriangleIcon,
ZapIcon
} from '@ciphera-net/ui'
import { Site } from '@/lib/api/sites'
import { Site, verifySite } from '@/lib/api/sites'
import { getRealtime } from '@/lib/api/stats'
import { toast, Button } from '@ciphera-net/ui'
@@ -17,9 +17,10 @@ interface VerificationModalProps {
isOpen: boolean
onClose: () => void
site: Site
onVerified?: () => void
}
export default function VerificationModal({ isOpen, onClose, site }: VerificationModalProps) {
export default function VerificationModal({ isOpen, onClose, site, onVerified }: VerificationModalProps) {
const [mounted, setMounted] = useState(false)
const [status, setStatus] = useState<'idle' | 'checking' | 'success' | 'error'>('idle')
const [attempts, setAttempts] = useState(0)
@@ -56,6 +57,7 @@ export default function VerificationModal({ isOpen, onClose, site }: Verificatio
if (data.visitors > 0) {
setStatus('success')
toast.success('Connection established!')
try { await verifySite(site.id); onVerified?.() } catch {}
}
} catch (e) {
// Ignore errors

View File

@@ -25,6 +25,7 @@ export interface Site {
hide_unknown_locations?: boolean
// Data retention (months); 0 = keep forever
data_retention_months?: number
is_verified?: boolean
created_at: string
updated_at: string
}
@@ -91,3 +92,9 @@ export async function resetSiteData(id: string): Promise<void> {
method: 'POST',
})
}
export async function verifySite(id: string): Promise<void> {
await apiRequest(`/sites/${id}/verify`, {
method: 'POST',
})
}