BunnyCDN, Search tab, journeys redesign, and dashboard polish #52

Merged
uz1mani merged 86 commits from staging into main 2026-03-17 10:08:26 +00:00
50 changed files with 4677 additions and 1568 deletions
Showing only changes of commit e9ec86b10b - Show all commits

View File

@@ -11,13 +11,7 @@ import { API_URL, APP_URL } from '@/lib/api/client'
import { integrations, getIntegration } from '@/lib/integrations' import { integrations, getIntegration } from '@/lib/integrations'
import { toast, Toggle, Select, CheckIcon } from '@ciphera-net/ui' import { toast, Toggle, Select, CheckIcon } from '@ciphera-net/ui'
const FRAMEWORK_OPTIONS = [ const FRAMEWORKS = integrations.filter((i) => i.category === 'framework').slice(0, 10)
{ value: '', label: 'Choose framework' },
...integrations
.filter((i) => i.category === 'framework')
.slice(0, 10)
.map((i) => ({ value: i.id, label: i.name })),
]
const STORAGE_OPTIONS = [ const STORAGE_OPTIONS = [
{ value: 'local', label: 'Cross-tab (localStorage)' }, { value: 'local', label: 'Cross-tab (localStorage)' },
@@ -25,10 +19,10 @@ const STORAGE_OPTIONS = [
] ]
const TTL_OPTIONS = [ const TTL_OPTIONS = [
{ value: '24', label: '24 hours' }, { value: '24', label: '24h' },
{ value: '48', label: '48 hours' }, { value: '48', label: '48h' },
{ value: '168', label: '7 days' }, { value: '168', label: '7d' },
{ value: '720', label: '30 days' }, { value: '720', label: '30d' },
] ]
const FEATURES = [ const FEATURES = [
@@ -111,20 +105,31 @@ export default function ScriptSetupBlock({
return ( return (
<div className={className}> <div className={className}>
{/* ── Script snippet ──────────────────────────────────────────────── */} {/* ── Script snippet ──────────────────────────────────────────────── */}
<div className="rounded-2xl border border-neutral-800 bg-neutral-950 overflow-hidden"> <div className="rounded-2xl border border-neutral-200 dark:border-neutral-800 overflow-hidden">
<div className="flex items-center justify-between px-5 py-3 border-b border-neutral-800"> {/* * Orange accent bar */}
<span className="text-xs font-medium text-neutral-400 uppercase tracking-wider"> <div className="h-1 bg-gradient-to-r from-brand-orange via-brand-orange/60 to-transparent" />
Your tracking script <div className="bg-neutral-950">
<div className="flex items-center justify-between px-5 py-3">
<div className="flex items-center gap-2">
{/* * Terminal dots */}
<div className="flex gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-red-500/70" />
<div className="w-2.5 h-2.5 rounded-full bg-yellow-500/70" />
<div className="w-2.5 h-2.5 rounded-full bg-green-500/70" />
</div>
<span className="text-xs font-medium text-neutral-500 ml-2">
tracking script
</span> </span>
</div>
<button <button
type="button" type="button"
onClick={copyScript} onClick={copyScript}
className="flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-lg transition-colors cursor-pointer bg-neutral-800 hover:bg-neutral-700 text-neutral-300 hover:text-white" className="flex items-center gap-1.5 text-xs font-medium px-3 py-1.5 rounded-lg transition-all cursor-pointer bg-brand-orange/10 hover:bg-brand-orange/20 text-brand-orange border border-brand-orange/20"
> >
{copied ? ( {copied ? (
<> <>
<CheckIcon className="w-3.5 h-3.5 text-green-400" /> <CheckIcon className="w-3.5 h-3.5" />
<span className="text-green-400">Copied</span> Copied
</> </>
) : ( ) : (
<> <>
@@ -137,10 +142,11 @@ export default function ScriptSetupBlock({
)} )}
</button> </button>
</div> </div>
<pre className="px-5 py-4 text-[13px] leading-relaxed font-mono text-neutral-200 whitespace-pre-wrap break-all overflow-x-auto"> <pre className="px-5 pb-5 text-[13px] leading-relaxed font-mono text-neutral-300 whitespace-pre-wrap break-all overflow-x-auto selection:bg-brand-orange/30">
{scriptSnippet} {scriptSnippet}
</pre> </pre>
</div> </div>
</div>
{/* ── Feature toggles ─────────────────────────────────────────────── */} {/* ── Feature toggles ─────────────────────────────────────────────── */}
<div className="mt-6"> <div className="mt-6">
@@ -184,10 +190,10 @@ export default function ScriptSetupBlock({
<h4 className="text-sm font-semibold text-neutral-900 dark:text-white mb-3"> <h4 className="text-sm font-semibold text-neutral-900 dark:text-white mb-3">
Visitor identity Visitor identity
</h4> </h4>
<div className="flex flex-col sm:flex-row gap-3"> <div className="flex items-end gap-3">
<div className="flex-1"> <div className="min-w-0">
<label className="text-xs font-medium text-neutral-500 dark:text-neutral-400 mb-1 block"> <label className="text-xs font-medium text-neutral-500 dark:text-neutral-400 mb-1 block">
Storage mode Storage
</label> </label>
<Select <Select
variant="input" variant="input"
@@ -197,9 +203,9 @@ export default function ScriptSetupBlock({
/> />
</div> </div>
{storage === 'local' && ( {storage === 'local' && (
<div className="sm:w-40"> <div>
<label className="text-xs font-medium text-neutral-500 dark:text-neutral-400 mb-1 block"> <label className="text-xs font-medium text-neutral-500 dark:text-neutral-400 mb-1 block">
TTL (expiry) TTL
</label> </label>
<Select <Select
variant="input" variant="input"
@@ -215,40 +221,48 @@ export default function ScriptSetupBlock({
{/* ── Framework guide ─────────────────────────────────────────────── */} {/* ── Framework guide ─────────────────────────────────────────────── */}
{showFrameworkPicker && ( {showFrameworkPicker && (
<div className="mt-6"> <div className="mt-6">
<h4 className="text-sm font-semibold text-neutral-900 dark:text-white mb-3"> <div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-semibold text-neutral-900 dark:text-white">
Setup guide Setup guide
</h4> </h4>
<div className="flex items-end gap-3"> <Link
<div className="flex-1"> href="/integrations"
<Select target="_blank"
variant="input" rel="noopener noreferrer"
value={framework} className="text-xs font-medium text-neutral-500 dark:text-neutral-400 hover:text-brand-orange transition-colors"
onChange={setFramework} >
options={FRAMEWORK_OPTIONS} All integrations
placeholder="Choose framework" </Link>
/> </div>
<div className="flex flex-wrap gap-2">
{FRAMEWORKS.map((fw) => (
<button
key={fw.id}
type="button"
onClick={() => setFramework(framework === fw.id ? '' : fw.id)}
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-sm transition-all cursor-pointer ${
framework === fw.id
? 'border-brand-orange bg-brand-orange/10 text-brand-orange'
: 'border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 text-neutral-600 dark:text-neutral-400 hover:border-neutral-300 dark:hover:border-neutral-700 hover:text-neutral-900 dark:hover:text-white'
}`}
>
<span className="[&_svg]:h-4 [&_svg]:w-4 shrink-0 flex items-center">
{fw.icon}
</span>
<span className="font-medium">{fw.name}</span>
</button>
))}
</div> </div>
{selectedIntegration && ( {selectedIntegration && (
<Link <Link
href={`/integrations/${framework}`} href={`/integrations/${framework}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="shrink-0 text-sm font-medium text-brand-orange hover:text-brand-orange/80 transition-colors pb-2" className="inline-flex items-center gap-1 mt-3 text-sm font-medium text-brand-orange hover:text-brand-orange/80 transition-colors"
> >
See {selectedIntegration.name} guide See full {selectedIntegration.name} guide
</Link> </Link>
)} )}
{!selectedIntegration && (
<Link
href="/integrations"
target="_blank"
rel="noopener noreferrer"
className="shrink-0 text-sm font-medium text-neutral-500 hover:text-neutral-400 transition-colors pb-2"
>
All integrations
</Link>
)}
</div>
</div> </div>
)} )}
</div> </div>