import { useEffect, useRef, useState, type ChangeEvent } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { User, Bell, Shield, Database, Monitor, RefreshCw, Power, Server, Smartphone, Wifi, Copy, Check, ChevronDown, LogOut, Pencil, MessageCircle, type LucideIcon, } from 'lucide-react'; import { useStore } from '@/store/useStore'; import { useCurrency, CURRENCY_OPTIONS } from '@/store/useCurrencyStore'; import type { CurrencyCode } from '@/store/useCurrencyStore'; import { API_URL } from '@/lib/api'; import { fetchCommsSettings, testCommsProviderConnection, updateCommsSettings } from '@/lib/commsApi'; import type { CommsProvider, CommsSettings } from '@/types/commsTypes'; import { clearVelocityToken, getVelocityToken, normalizeVelocityRole, resolveVelocityFirstName, uploadVelocityAvatar, } from '@/lib/velocityPlatformClient'; // ── Design tokens (matching inventory glassmorphism) ───────────────────────── const GLASS = { background: 'rgba(14, 16, 21, 0.72)', border: '1px solid rgba(255,255,255,0.08)', backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', } as const; const INNER_SURFACE = { background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.07)', } as const; // ── Shared primitives ──────────────────────────────────────────────────────── function GlassCard({ children, delay = 0, className = '', }: { children: React.ReactNode; delay?: number; className?: string; }) { return ( {children} ); } function SectionHeader({ icon: Icon, title, accent = 'hsl(var(--accent))' }: { icon: LucideIcon; title: string; accent?: string }) { return (

{title}

); } function SettingsRow({ label, description, children, danger = false, }: { label: string; description?: string; children: React.ReactNode; danger?: boolean; }) { return (

{label}

{description && (

{description}

)}
{children}
); } // ── Toggle ─────────────────────────────────────────────────────────────────── function Toggle({ enabled, onChange }: { enabled: boolean; onChange: (v: boolean) => void }) { return ( ); } // ── Dark Select ────────────────────────────────────────────────────────────── function DarkSelect({ value, onChange, options, }: { value: string; onChange: (v: string) => void; options: { value: string; label: string }[]; }) { const [open, setOpen] = useState(false); const current = options.find((o) => o.value === value) ?? options[0]; return (
{open && ( {options.map((opt) => ( ))} )} {open &&
setOpen(false)} />}
); } // ── Ghost button ───────────────────────────────────────────────────────────── function GhostButton({ children, onClick, danger = false }: { children: React.ReactNode; onClick?: () => void; danger?: boolean }) { return ( {children} ); } // ── Text input ─────────────────────────────────────────────────────────────── // ── System Status ──────────────────────────────────────────────────────────── function SystemStatusCard() { const { status, updateStatus } = useStore(); const lastSync = status.lastSync instanceof Date ? status.lastSync : new Date(status.lastSync); return (
{/* Connection pill */}
{status.isConnected &&
}

Backend Connection

{status.isConnected ? 'Connected to live Velocity backend' : 'Connection unavailable'}

{status.serverStatus.toUpperCase()}
{/* Version / sync grid */}
{[ { label: 'Version', value: status.version }, { label: 'Last Sync', value: Number.isNaN(lastSync.getTime()) ? 'Unavailable' : lastSync.toLocaleTimeString() }, ].map(({ label, value }) => (

{label}

{value}

))}
{/* Actions */}
{ updateStatus({ serverStatus: 'syncing' }); window.location.reload(); }} > Sync Now window.location.reload()} > Restart
); } // ── iOS Device Connection ──────────────────────────────────────────────────── function CompanionSurfacesCard() { const [copied, setCopied] = useState(false); const token = getVelocityToken(); const maskedToken = token ? `${token.slice(0, 8)}...${token.slice(-6)}` : 'No active bearer token'; const handleCopy = (text: string) => { void navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1800); }; return (
{token &&
}

WebOS Session Token

{token ? 'Reusable by Oracle and protected WebOS routes.' : 'No authenticated backend session is currently present.'}

{token ? ACTIVE : }

Runtime Access

{maskedToken}

Mobile and tablet pairing is intentionally deferred until the next delivery phase. This WebOS pass does not simulate device pairing.

window.location.reload()} className="flex-1 py-2.5 rounded-xl text-sm font-semibold flex items-center justify-center gap-2 transition-all" style={{ background: 'hsl(var(--accent))', color: 'hsl(var(--accent-fg))' }} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.97 }} > <> Refresh WebOS handleCopy(API_URL)}>Copy API URL
); } // ── Profile ────────────────────────────────────────────────────────────────── function ProfileSettings() { const { user, login } = useStore(); const fileInputRef = useRef(null); const [avatarError, setAvatarError] = useState(null); const [isUploadingAvatar, setIsUploadingAvatar] = useState(false); const displayName = user?.fullName?.trim() || user?.name?.trim() || user?.email?.trim() || user?.id || 'Authenticated User'; const firstName = resolveVelocityFirstName({ user_id: user?.id ?? '', full_name: user?.fullName ?? user?.name ?? null, email: user?.email ?? null, }); const initials = (user?.fullName || user?.name || user?.email || 'AU') .split(/[\s@._-]+/) .filter(Boolean) .slice(0, 2) .map((n) => n[0]?.toUpperCase() ?? '') .join('') || 'AU'; const roleLabel = normalizeVelocityRole(user?.role) .toLowerCase() .split('_') .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(' ') || 'Authenticated User'; const handleAvatarUpload = async (event: ChangeEvent) => { const file = event.target.files?.[0]; if (!file || !user) { return; } setAvatarError(null); setIsUploadingAvatar(true); try { const { avatar_url } = await uploadVelocityAvatar(file); login({ ...user, avatar: avatar_url, }); } catch (error) { setAvatarError(error instanceof Error ? error.message : 'Failed to upload profile picture.'); } finally { setIsUploadingAvatar(false); if (event.target) { event.target.value = ''; } } }; return (
{user?.avatar ? ( {displayName} ) : (
{initials}
)}

{displayName}

{roleLabel}

{isUploadingAvatar && (

Uploading profile picture...

)}
{avatarError && (

{avatarError}

)}
{firstName || 'Unavailable'} {user?.id ?? 'Unavailable'} {roleLabel}
); } // ── Notifications ──────────────────────────────────────────────────────────── function NotificationSettings() { const [s, setS] = useState({ newLeads: true, sentimentAlerts: true, viewings: true, systemUpdates: false, emailDigest: true }); const rows: { key: keyof typeof s; label: string; desc: string }[] = [ { key: 'newLeads', label: 'New Lead Alerts', desc: 'Get notified when a new lead is captured' }, { key: 'sentimentAlerts', label: 'Sentiment Alerts', desc: 'Alert when visitor sentiment drops' }, { key: 'viewings', label: 'Viewing Reminders', desc: 'Reminders for scheduled viewings' }, { key: 'systemUpdates', label: 'System Updates', desc: 'Notifications about system maintenance' }, { key: 'emailDigest', label: 'Daily Email Digest', desc: 'Summary of daily activity' }, ]; return (
{rows.map(({ key, label, desc }) => ( setS({ ...s, [key]: v })} /> ))}
); } // ── Security ───────────────────────────────────────────────────────────────── function SecuritySettings() { const [timeout, setTimeout_] = useState('30'); const { logout } = useStore(); const token = getVelocityToken(); return (
{token ? 'Present' : 'Missing'} Managed outside WebOS { clearVelocityToken(); logout(); }} > Sign Out
); } // ── Display ────────────────────────────────────────────────────────────────── function DisplaySettings() { const [reducedMotion, setReducedMotion] = useState(false); const [compactMode, setCompactMode] = useState(false); const [language, setLanguage] = useState('en'); const [timezone, setTimezone] = useState('dxb'); const { currency, setCurrency } = useCurrency(); return (
{/* ── Currency ── */} setCurrency(v as CurrencyCode)} options={CURRENCY_OPTIONS.map((o) => ({ value: o.code, label: `${o.flag} ${o.symbol} — ${o.label}`, }))} />
); } // ── Data & Privacy ─────────────────────────────────────────────────────────── function CommunicationsSettings() { const [settings, setSettings] = useState(null); const [draft, setDraft] = useState>({}); const [statusText, setStatusText] = useState('Loading provider settings...'); const [saving, setSaving] = useState(false); const current: CommsSettings = { provider: 'mock', providerBaseUrl: '', providerApiKey: '', instanceId: '', phoneNumberId: '', webhookCallbackUrl: '/api/comms/webhooks/{provider}', webhookSecretSet: false, autoLinkByPhone: true, createCrmInteractionOnInbound: true, defaultCountryCode: '91', transcriptionProvider: 'none', ...(settings ?? {}), ...draft, }; useEffect(() => { let cancelled = false; void fetchCommsSettings() .then((value) => { if (cancelled) return; setSettings(value); setStatusText('Settings loaded from backend.'); }) .catch((error) => { if (cancelled) return; setStatusText(error instanceof Error ? error.message : 'Unable to load comms settings.'); }); return () => { cancelled = true; }; }, []); const update = (key: K, value: CommsSettings[K]) => { setDraft((prev) => ({ ...prev, [key]: value })); }; const save = async () => { setSaving(true); try { await updateCommsSettings(draft); const latest = await fetchCommsSettings(); setSettings(latest); setDraft({}); setStatusText('Communications settings saved.'); } catch (error) { setStatusText(error instanceof Error ? error.message : 'Failed to save communications settings.'); } finally { setSaving(false); } }; const test = async () => { try { const result = await testCommsProviderConnection(); setStatusText(result.message || (result.success ? 'Provider connection succeeded.' : 'Provider connection failed.')); } catch (error) { setStatusText(error instanceof Error ? error.message : 'Provider test failed.'); } }; const fieldClass = "w-64 rounded-xl px-3 py-2 text-sm text-white placeholder-zinc-500 outline-none"; return (
update('provider', v as CommsProvider)} options={[ { value: 'mock', label: 'Mock' }, { value: 'waha', label: 'WAHA' }, { value: 'evolution', label: 'Evolution API' }, { value: 'meta_cloud', label: 'Meta Cloud API' }, ]} /> update('providerBaseUrl', event.target.value)} placeholder="http://127.0.0.1:3000" /> update('providerApiKey', event.target.value)} placeholder="Provider API key" /> update('instanceId', event.target.value)} placeholder="default" /> {current.webhookCallbackUrl || '/api/comms/webhooks/{provider}'} update('autoLinkByPhone', v)} /> update('createCrmInteractionOnInbound', v)} /> update('transcriptionProvider', v as CommsSettings['transcriptionProvider'])} options={[ { value: 'none', label: 'None' }, { value: 'local', label: 'Local Whisper' }, { value: 'openai', label: 'OpenAI' }, ]} />

{statusText}

Test {saving ? 'Saving...' : 'Save'}
); } function DataSettings() { const [retention, setRetention] = useState('90'); const { leads, messages, units, status } = useStore(); const exportSnapshot = () => { const blob = new Blob([ JSON.stringify( { exported_at: new Date().toISOString(), status, lead_count: leads.length, message_threads: Object.keys(messages).length, inventory_count: units.length, leads, messages, units, }, null, 2, ), ], { type: 'application/json' }); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = `velocity-webos-export-${Date.now()}.json`; anchor.click(); URL.revokeObjectURL(url); }; const clearUiCache = () => { localStorage.removeItem('velocity-webos-storage'); localStorage.removeItem('pv-currency'); window.location.reload(); }; return (
Backend managed Export Clear
); } // ── About ──────────────────────────────────────────────────────────────────── function AboutSection() { const token = getVelocityToken(); return (
V

Velocity WebOS

Real Estate Operating System

Version 2.2.0 CRM {token ? 'Authenticated session active' : 'No active session'}
Backend origin: {API_URL}
); } // ── Main ───────────────────────────────────────────────────────────────────── export function Settings() { return (
{/* Row 1: System + iOS */}
{/* Row 2: Profile + Notifications */}
{/* Row 3: Security + Display */}
{/* Row 4: Communications + Data */}
{/* Row 5: About */}
); }