@zenith-open/zenithcms-admin 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/assets/ApiExplorerPage-UJpoKRI0.js +42 -0
- package/dist/assets/ApiExplorerPage-UJpoKRI0.js.map +1 -0
- package/dist/assets/AuditLogPage-8xYlRl1I.js +1 -0
- package/dist/assets/AuditLogPage-8xYlRl1I.js.map +1 -0
- package/dist/assets/BlockBuilderPage-DcOo3Vnt.js +1 -0
- package/dist/assets/BlockBuilderPage-DcOo3Vnt.js.map +1 -0
- package/dist/assets/BuilderPage-Iu8F_bN1.js +1 -0
- package/dist/assets/BuilderPage-Iu8F_bN1.js.map +1 -0
- package/dist/assets/CollectionHooksPage-Dn_ujtlp.js +7 -0
- package/dist/assets/CollectionHooksPage-Dn_ujtlp.js.map +1 -0
- package/dist/assets/CollectionsPage-BSPHf7H2.js +1 -0
- package/dist/assets/CollectionsPage-BSPHf7H2.js.map +1 -0
- package/dist/assets/ComponentBuilderPage-CT6S12LA.js +14 -0
- package/dist/assets/ComponentBuilderPage-CT6S12LA.js.map +1 -0
- package/dist/assets/DashboardBuilder-Cbi9Ddiu.js +1 -0
- package/dist/assets/DashboardBuilder-Cbi9Ddiu.js.map +1 -0
- package/dist/assets/EmptyState-X_SXKpQY.js +1 -0
- package/dist/assets/EmptyState-X_SXKpQY.js.map +1 -0
- package/dist/assets/MediaLibrary-BzXE95xo.js +1 -0
- package/dist/assets/MediaLibrary-BzXE95xo.js.map +1 -0
- package/dist/assets/PluginsPage-5YRpbP-N.js +10 -0
- package/dist/assets/PluginsPage-5YRpbP-N.js.map +1 -0
- package/dist/assets/RedirectsPage-D_4jAdaI.js +1 -0
- package/dist/assets/RedirectsPage-D_4jAdaI.js.map +1 -0
- package/dist/assets/SchemaBuilderPage-EFA5XIAa.js +14 -0
- package/dist/assets/SchemaBuilderPage-EFA5XIAa.js.map +1 -0
- package/dist/assets/SettingsPage-BRpcMw48.js +33 -0
- package/dist/assets/SettingsPage-BRpcMw48.js.map +1 -0
- package/dist/assets/SetupWizard-D57HIkrs.js +62 -0
- package/dist/assets/SetupWizard-D57HIkrs.js.map +1 -0
- package/dist/assets/SpatialEditor-CPgS7Zrd.js +3 -0
- package/dist/assets/SpatialEditor-CPgS7Zrd.js.map +1 -0
- package/dist/assets/TemplatesPage-B-nNYv3o.js +1 -0
- package/dist/assets/TemplatesPage-B-nNYv3o.js.map +1 -0
- package/dist/assets/TrashPage-Ccusal1w.js +1 -0
- package/dist/assets/TrashPage-Ccusal1w.js.map +1 -0
- package/dist/assets/index-ChcKY5Xe.js +11 -0
- package/dist/assets/index-ChcKY5Xe.js.map +1 -0
- package/dist/assets/index-CxhwdV2K.css +1 -0
- package/dist/assets/login-bg.png +0 -0
- package/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/dist/assets/useUnsavedGuard-CMMKQT9a.js +1 -0
- package/dist/assets/useUnsavedGuard-CMMKQT9a.js.map +1 -0
- package/dist/assets/utils-fgvbH6CB.js +1 -0
- package/dist/assets/utils-fgvbH6CB.js.map +1 -0
- package/dist/assets/vendor-C-9IqjvY.js +1 -0
- package/dist/assets/vendor-C-9IqjvY.js.map +1 -0
- package/dist/assets/vendor-forms-BnBLxarY.js +1 -0
- package/dist/assets/vendor-forms-BnBLxarY.js.map +1 -0
- package/dist/assets/vendor-react-DQVTOTFO.js +195 -0
- package/dist/assets/vendor-react-DQVTOTFO.js.map +1 -0
- package/dist/favicon.svg +1 -0
- package/dist/icons.svg +24 -0
- package/dist/index.html +30 -0
- package/dist/logo/zenith.svg +9 -0
- package/dist/sw.js +3 -0
- package/dist/sw.js.map +1 -0
- package/dist/workbox-9c191d2f.js +3 -0
- package/dist/workbox-9c191d2f.js.map +1 -0
- package/dist/zenith.svg +9 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DashboardBuilder-Cbi9Ddiu.js","names":[],"sources":["../../src/pages/dashboard/DashboardCard.tsx","../../src/pages/dashboard/WidgetErrorBoundary.tsx","../../src/pages/DashboardBuilder.tsx"],"sourcesContent":["import React from 'react'\nimport { cn } from '../../lib/utils'\nimport { useTheme } from '../../context/ThemeContext'\n\ninterface DashboardCardProps {\n title?: string\n icon?: React.ReactNode\n action?: React.ReactNode\n children: React.ReactNode\n className?: string\n noPadding?: boolean\n}\n\nexport const DashboardCard = React.memo(function DashboardCard({ title, icon, action, children, className, noPadding }: DashboardCardProps) {\n const { theme } = useTheme()\n\n return (\n <div\n className={cn(\n 'flex flex-col border z-panel backdrop-blur-md shadow-sm',\n className\n )}\n style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}\n >\n {(title || action) && (\n <div\n className=\"flex items-center justify-between px-5 py-4 border-b shrink-0 border-z-border\"\n >\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <span className=\"text-z-secondary\">\n {icon}\n </span>\n )}\n {title && (\n <h2 className=\"text-sm font-semibold text-z-secondary\">\n {title}\n </h2>\n )}\n </div>\n {action && <div>{action}</div>}\n </div>\n )}\n <div className={cn('flex-1 min-h-0', !noPadding && 'p-5')}>\n {children}\n </div>\n </div>\n )\n})\n","import React from 'react';\nimport { AlertTriangle } from 'lucide-react';\n\ninterface Props {\n children: React.ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n}\n\nexport class WidgetErrorBoundary extends React.Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error('Widget caught error:', error, errorInfo);\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex flex-col items-center justify-center p-4 border border-red-500/20 bg-red-500/10 rounded-none min-h-[100px] h-full\">\n <AlertTriangle className=\"text-red-500 mb-2\" size={24} />\n <span className=\"text-xs font-bold text-red-500\">Widget Error</span>\n <span className=\"text-sm text-red-400 mt-1 truncate max-w-full px-2\">\n {this.state.error?.message || 'Failed to render'}\n </span>\n </div>\n );\n }\n return this.props.children;\n }\n}","import React, { useEffect, useState, useCallback } from 'react'\nimport { Link, useNavigate } from 'react-router-dom'\nimport {\n Activity,\n ArrowRight,\n CheckCircle2,\n AlertTriangle,\n Clock,\n Database,\n FileText,\n History,\n ImageIcon,\n Layers,\n Loader2,\n Plus,\n Radio,\n Users,\n XCircle,\n Globe,\n KeyRound,\n Zap,\n} from 'lucide-react'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport api from '../lib/api'\nimport { DashboardCard } from './dashboard/DashboardCard'\nimport { WidgetErrorBoundary } from './dashboard/WidgetErrorBoundary'\n\n// ── Types ──────────────────────────────────────────────────────────────────────\ninterface HealthData {\n status: string\n database: string\n version: string\n environment: string\n uptime: number\n memory: { heapUsed: string; heapTotal: string; rss: string }\n}\ninterface AuditEntry {\n _id: string\n action: string\n collection?: string\n collectionName?: string\n user?: { email?: string }\n userEmail?: string\n timestamp: string\n status?: string\n}\ninterface AuditStats {\n total: number\n failed: number\n success: number\n byAction: Record<string, number>\n}\ninterface CollectionInfo {\n name: string\n label?: string\n count?: number\n drafts?: boolean\n icon?: string\n}\ninterface PresenceMember {\n userId: string\n email?: string\n collection?: string\n documentId?: string\n color?: string\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\nfunction timeAgo(ts: string) {\n const secs = Math.floor((Date.now() - new Date(ts).getTime()) / 1000)\n if (secs < 60) return `${secs}s ago`\n if (secs < 3600) return `${Math.floor(secs / 60)}m ago`\n if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`\n return `${Math.floor(secs / 86400)}d ago`\n}\n\nfunction uptimeStr(seconds: number) {\n if (seconds == null) return '—'\n const d = Math.floor(seconds / 86400)\n const h = Math.floor((seconds % 86400) / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = Math.floor(seconds % 60)\n if (d > 0) return `${d}d ${h}h ${m}m`\n if (h > 0) return `${h}h ${m}m ${s}s`\n return `${m}m ${s}s`\n}\n\nconst ACTION_PALETTE: Record<string, string> = {\n create: 'text-z-active-text bg-z-active-bg',\n update: 'text-z-active-text bg-z-active-bg',\n delete: 'text-rose-400 bg-rose-500/10',\n login: 'text-sky-400 bg-sky-500/10',\n logout: 'text-z-muted bg-z-panel',\n}\n\nconst INITIALS_COLORS = [\n 'bg-z-accent', 'bg-z-accent', 'bg-sky-600',\n 'bg-amber-600', 'bg-rose-600', 'bg-z-accent',\n]\n\n// ── Stat Pill ─────────────────────────────────────────────────────────────────\nconst StatPill = React.memo(function StatPill({\n label, value, sub, icon: Icon, accent, loading,\n}: {\n label: string; value: string; sub?: string\n icon: React.ElementType; accent?: 'purple' | 'emerald' | 'red'; loading?: boolean\n}) {\n const { theme } = useTheme()\n const accentClass =\n accent === 'purple' ? 'text-z-active-text' :\n accent === 'emerald' ? 'text-z-active-text' :\n accent === 'red' ? 'text-rose-400' :\n 'text-z-primary'\n\n return (\n <div className={cn(\n 'flex flex-col justify-between gap-2 p-5 border transition-colors z-panel backdrop-blur-md shadow-sm'\n )} style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary\">{label}</span>\n <Icon size={13} className=\"text-z-secondary\" />\n </div>\n <div>\n <span className={cn('text-2xl font-semibold leading-none tabular-nums', accentClass)}>\n {loading ? <span className=\"text-z-secondary text-base\">—</span> : value}\n </span>\n {sub && <p className=\"text-sm text-z-secondary mt-1\">{sub}</p>}\n </div>\n </div>\n )\n})\n\n// ── Environment Badge ──────────────────────────────────────────────────────────\nfunction EnvBadge({ env }: { env?: string }) {\n if (!env) return null\n const isProd = env === 'production'\n return (\n <span className={cn(\n 'inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-semibold border',\n isProd\n ? 'bg-rose-500/10 border-rose-500/20 text-rose-400'\n : 'bg-amber-500/10 border-amber-500/20 text-amber-400'\n )}>\n <span className={cn('w-1.5 h-1.5 rounded-full', isProd ? 'bg-rose-400 animate-pulse' : 'bg-amber-400')} />\n {env}\n </span>\n )\n}\n\n// ── Main Dashboard ─────────────────────────────────────────────────────────────\nexport default function Dashboard() {\n const { theme } = useTheme()\n const navigate = useNavigate()\n\n const [health, setHealth] = useState<HealthData | null>(null)\n const [latency, setLatency] = useState<number | null>(null)\n const [auditLogs, setAuditLogs] = useState<AuditEntry[]>([])\n const [auditStats, setAuditStats] = useState<AuditStats | null>(null)\n const [collections, setCollections] = useState<CollectionInfo[]>([])\n const [totalRecords, setTotalRecords] = useState<string>('—')\n const [mediaCount, setMediaCount] = useState<number | null>(null)\n const [membersOnline, setMembersOnline] = useState<PresenceMember[]>([])\n const [memberCount, setMemberCount] = useState<number | null>(null)\n const [hoveredUser, setHoveredUser] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n\n // Make uptime active\n useEffect(() => {\n const timer = setInterval(() => {\n setHealth(prev => prev && prev.uptime != null ? { ...prev, uptime: prev.uptime + 1 } : prev)\n }, 1000)\n return () => clearInterval(timer)\n }, [])\n\n const fetchAll = useCallback(async () => {\n setLoading(true)\n try {\n const t0 = performance.now()\n const results = await Promise.allSettled([\n api.get('/system/health'), // 0\n api.get('/system/audit-logs?limit=8'), // 1\n api.get('/system/audit-logs/stats'), // 2\n api.get('/system/schemas'), // 3 — collections list\n api.get('/system/counts'), // 4\n api.get('/media?pageSize=1&sort=-createdAt'), // 5 — just for total count\n api.get('/presence'), // 6\n ])\n\n if (results[0].status === 'fulfilled') {\n setLatency(Math.round(performance.now() - t0))\n setHealth(results[0].value.data?.data || null)\n }\n if (results[1].status === 'fulfilled') {\n setAuditLogs(results[1].value.data?.data || [])\n }\n if (results[2].status === 'fulfilled') {\n setAuditStats(results[2].value.data?.data || null)\n }\n\n // Build collections from schemas + counts\n const counts: Record<string, number> = results[4].status === 'fulfilled'\n ? results[4].value.data?.data || {}\n : {}\n\n if (results[3].status === 'fulfilled') {\n const schemas = results[3].value.data?.data\n const cols: any[] = schemas?.collections || (Array.isArray(schemas) ? schemas : [])\n setCollections(cols.map((c: any) => ({\n name: c.slug || c.name,\n label: c.label || c.labels?.plural || c.slug || c.name,\n count: counts[c.slug || c.name],\n drafts: !!c.drafts,\n icon: c.admin?.icon,\n })))\n // total records = sum of all counts (excluding internal z_ collections)\n const total = Object.entries(counts)\n .filter(([k]) => !k.startsWith('z_'))\n .reduce((a, [, v]) => a + (v as number), 0)\n setTotalRecords(total > 0 ? total.toLocaleString() : '0')\n // member count from counts\n if (counts['users'] != null) setMemberCount(counts['users'])\n else if (counts['z_users'] != null) setMemberCount(counts['z_users'])\n else if (counts['members'] != null) setMemberCount(counts['members'])\n }\n\n if (results[5].status === 'fulfilled') {\n const pagination = results[5].value.data?.meta?.pagination\n setMediaCount(pagination?.total ?? results[5].value.data?.data?.length ?? null)\n }\n if (results[6].status === 'fulfilled') {\n setMembersOnline(results[6].value.data?.data || [])\n }\n } catch {\n // partial failures are fine\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n fetchAll()\n // Dynamic ultra-low resource polling (every 3s) for presence and system health\n const dynamicInterval = setInterval(() => {\n const t0 = performance.now()\n api.get('/system/health').then(r => {\n setLatency(Math.round(performance.now() - t0))\n setHealth(prev => {\n const newData = r.data?.data || null\n // Keep our local ticking uptime if it's ahead or similar to prevent jitter\n if (prev && newData && newData.uptime) {\n return { ...newData, uptime: Math.max(prev.uptime, newData.uptime) }\n }\n return newData\n })\n }).catch(() => {})\n\n api.get('/presence').then(r => {\n const data = r.data?.data || []\n setMembersOnline(data)\n setMemberCount(prev => prev !== null ? Math.max(prev, data.length) : data.length)\n }).catch(() => {})\n }, 3000)\n\n return () => clearInterval(dynamicInterval)\n }, [fetchAll])\n\n // Global presence heartbeat so users just looking at the dashboard appear online\n useEffect(() => {\n const sendHeartbeat = () => {\n api.post('/presence/heartbeat', {\n collection: 'dashboard',\n documentId: 'dashboard',\n }).catch(() => {})\n }\n sendHeartbeat() // initial\n const interval = setInterval(sendHeartbeat, 30000)\n return () => clearInterval(interval)\n }, [])\n\n const isHealthOk = health?.status === 'ok'\n const isDbOk = health?.database === 'ok'\n\n if (loading) {\n return (\n <div className=\"h-full w-full flex flex-col items-center justify-center gap-4\">\n <Loader2 size={26} className=\"animate-spin text-z-secondary\" strokeWidth={1.5} />\n <p className=\"text-sm font-semibold text-z-secondary animate-pulse\">Loading…</p>\n </div>\n )\n }\n\n const quickActionGlass = theme === 'dark' \n ? 'bg-z-hover hover:bg-z-panel/[0.08] text-z-secondary border border-z-border shadow-sm' \n : 'bg-z-panel/65 backdrop-blur-[12px] hover:bg-z-panel/85 text-z-primary border border-z-border shadow-sm';\n\n const QUICK_ACTIONS = [\n { label: 'New Content', icon: Plus, path: '/collections', color: quickActionGlass },\n { label: 'Media Library', icon: ImageIcon, path: '/media', color: quickActionGlass },\n { label: 'API Explorer', icon: Zap, path: '/settings?tab=api-explorer', color: quickActionGlass },\n { label: 'Schema Builder', icon: Layers, path: '/schema-builder', color: quickActionGlass },\n { label: 'API Keys', icon: KeyRound, path: '/settings?tab=keys', color: quickActionGlass },\n { label: 'Audit Log', icon: History, path: '/audit-log', color: quickActionGlass },\n ]\n\n return (\n <div className=\"min-h-full transition-colors duration-300\">\n <div className=\"p-6 space-y-5 max-w-screen-2xl mx-auto\">\n\n {/* Page Header */}\n <PageHeader\n title=\"Dashboard\"\n description=\"System overview for your Zenith CMS workspace.\"\n icon={<Activity size={20} />}\n actions={\n <div className=\"flex items-center gap-2\">\n <EnvBadge env={health?.environment} />\n {health?.version && (\n <span className=\"text-sm font-semibold text-z-secondary\">\n v{health.version}\n </span>\n )}\n </div>\n }\n />\n\n {/* ── Row 1: Key stats ──────────────────────────────────────────────── */}\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3\">\n <StatPill label=\"Content Records\" value={totalRecords} icon={FileText} />\n <StatPill label=\"Collections\" value={String(collections.length || '—')} icon={Database} />\n <StatPill\n label=\"Media Files\"\n value={mediaCount != null ? mediaCount.toLocaleString() : '—'}\n icon={ImageIcon}\n />\n <StatPill\n label=\"Team Members\"\n value={memberCount != null ? String(memberCount) : '—'}\n sub={membersOnline.length > 0 ? `${membersOnline.length} online now` : undefined}\n icon={Users}\n />\n <StatPill\n label=\"API Latency\"\n value={latency != null ? `${latency}ms` : '—'}\n sub={health ? (isHealthOk ? 'System Operational' : 'System Degraded') : undefined}\n icon={Radio}\n accent={!health ? undefined : latency != null && latency < 300 ? 'emerald' : 'red'}\n />\n <StatPill\n label=\"Database\"\n value={health?.database || (health ? 'Connected' : '—')}\n sub={isDbOk ? 'Status: Operational' : 'Status: Degraded'}\n icon={Activity}\n accent={!health ? undefined : isDbOk ? 'emerald' : 'red'}\n />\n </div>\n </WidgetErrorBoundary>\n\n {/* ── Row 2: Audit stats ───────────────────────────────────────────── */}\n {auditStats && (\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n <StatPill label=\"Total Events\" value={auditStats.total.toLocaleString()} icon={History} />\n <StatPill label=\"Successful Ops\" value={auditStats.success.toLocaleString()} icon={CheckCircle2} accent=\"emerald\" />\n <StatPill label=\"Failed Ops\" value={auditStats.failed.toLocaleString()} icon={XCircle} accent={auditStats.failed > 0 ? 'red' : undefined} />\n </div>\n </WidgetErrorBoundary>\n )}\n\n {/* ── Row 3: Quick Actions ────────────────────────────────────────────── */}\n <DashboardCard title=\"Quick Actions\" icon={<Zap size={13} />}>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2\">\n {QUICK_ACTIONS.map((a) => (\n <button\n key={a.label}\n onClick={() => navigate(a.path)}\n className={cn(\n 'flex items-center gap-2.5 px-3 py-2.5 text-sm font-semibold tracking-wide transition-all',\n a.color\n )}\n >\n <a.icon size={14} />\n {a.label}\n </button>\n ))}\n </div>\n </DashboardCard>\n\n {/* ── Row 4: Collections + Activity ──────────────────────────────────── */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-5\">\n\n {/* Collections */}\n <DashboardCard\n title=\"Collections\"\n icon={<Database size={13} />}\n noPadding\n action={\n <Link to=\"/schema-builder\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Manage <ArrowRight size={11} />\n </Link>\n }\n >\n {collections.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-4 px-6\">\n <Database size={32} className=\"text-z-primary\" strokeWidth={1} />\n <div className=\"text-center\">\n <p className=\"text-sm font-bold text-z-muted\">No collections yet</p>\n <p className=\"text-sm text-z-secondary mt-1\">Create your first collection to start managing content.</p>\n </div>\n <Link to=\"/schema-builder\" className=\"px-5 py-2.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors\">\n + Create Collection\n </Link>\n </div>\n ) : (\n <div>\n {collections.slice(0, 8).map((col) => (\n <Link\n key={col.name}\n to={`/collections/${col.name}`}\n className={cn(\n 'flex items-center justify-between px-5 py-3 group transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]'\n )}\n >\n <div className=\"flex items-center gap-3\">\n <div className={cn('w-6 h-6 flex items-center justify-center border text-z-secondary', theme === 'dark' ? 'bg-z-hover border-z-border' : 'bg-z-input border-z-border')}>\n <Layers size={11} />\n </div>\n <div>\n <span className={cn('text-sm font-bold capitalize', theme === 'dark' ? 'text-z-primary' : 'text-z-primary')}>\n {col.label || col.name}\n </span>\n {col.drafts && (\n <span className=\"ml-2 text-sm font-semibold text-amber-500 bg-amber-500/10 px-1.5 py-0.5\">\n Drafts\n </span>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {col.count != null && (\n <span className=\"text-sm font-semibold tabular-nums text-z-secondary\">\n {col.count.toLocaleString()}\n </span>\n )}\n <ArrowRight size={12} className={cn('transition-transform group-hover:translate-x-0.5', theme === 'dark' ? 'text-z-primary' : 'text-z-secondary')} />\n </div>\n </Link>\n ))}\n {collections.length > 8 && (\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors\">\n +{collections.length - 8} more collections →\n </Link>\n </div>\n )}\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"flex items-center gap-1.5 text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors w-fit\">\n <Plus size={11} /> New Collection\n </Link>\n </div>\n </div>\n )}\n </DashboardCard>\n\n {/* Recent Activity */}\n <DashboardCard\n title=\"Recent Activity\"\n icon={<History size={13} />}\n noPadding\n action={\n <Link to=\"/audit-log\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Full Log <ArrowRight size={11} />\n </Link>\n }\n >\n {auditLogs.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-2\">\n <History size={28} className=\"text-z-primary\" strokeWidth={1} />\n <p className=\"text-sm text-z-secondary\">No activity recorded yet.</p>\n </div>\n ) : (\n <div>\n {auditLogs.map((log) => {\n const actor = log.userEmail || log.user?.email || 'System'\n const initials = actor === 'System' ? 'SY' : actor.slice(0, 2).toUpperCase()\n const colorIdx = actor.charCodeAt(0) % INITIALS_COLORS.length\n const collection = (log.collectionName || log.collection || 'system').replace(/-/g, ' ')\n const isFailed = log.status === 'failed'\n\n return (\n <div\n key={log._id}\n onClick={() => navigate('/audit-log')}\n className={cn(\n 'flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]',\n isFailed && (theme === 'dark' ? 'bg-rose-500/[0.03]' : 'bg-rose-50/50')\n )}\n >\n {/* Avatar */}\n <div className={cn('w-6 h-6 flex items-center justify-center text-z-primary text-sm font-semibold shrink-0', INITIALS_COLORS[colorIdx])}>\n {initials}\n </div>\n {/* Action badge */}\n <span className={cn(\n 'inline-flex px-1.5 py-0.5 text-sm font-semibold shrink-0',\n isFailed ? 'text-rose-400 bg-rose-500/10' : (ACTION_PALETTE[log.action?.toLowerCase()] || 'text-z-muted bg-z-panel')\n )}>\n {log.action}\n </span>\n {/* Collection */}\n <span className={cn('text-sm font-medium flex-1 truncate capitalize', theme === 'dark' ? 'text-z-muted' : 'text-z-secondary')}>\n {collection}\n </span>\n {/* Time */}\n <span className=\"text-sm text-z-secondary shrink-0 tabular-nums\">{timeAgo(log.timestamp)}</span>\n </div>\n )\n })}\n </div>\n )}\n </DashboardCard>\n\n </div>\n\n {/* ── Row 5: Who's online + API health strip ─────────────────────────── */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-5\">\n\n {/* Who's Online — Google Docs style */}\n <DashboardCard title=\"Who's Online\" icon={<Users size={13} />}>\n {membersOnline.length === 0 ? (\n <div className=\"flex items-center gap-2.5\">\n <div className={cn('w-2 h-2 rounded-full', theme === 'dark' ? 'bg-z-border' : 'bg-[var(--z-border)]')} />\n <p className=\"text-sm text-z-secondary\">No one else is online right now.</p>\n </div>\n ) : (\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center -space-x-2.5\">\n {membersOnline.map((m, i) => {\n const email = m.email || m.userId || 'Unknown'\n const name = email.split('@')[0]\n const initials = name.slice(0, 2).toUpperCase()\n const collection = m.collection\n ? m.collection.replace(/-/g, ' ')\n : null\n const avatarColor = m.color || INITIALS_COLORS[i % INITIALS_COLORS.length]\n const isHex = avatarColor.startsWith('#')\n const statusText = collection ? `Editing ${collection}` : 'Browsing'\n\n return (\n <div \n key={m.userId || i} \n className=\"relative group cursor-default\"\n style={{ zIndex: hoveredUser === (m.userId || String(i)) ? 50 : membersOnline.length - i }}\n onMouseEnter={() => setHoveredUser(m.userId || String(i))}\n onMouseLeave={() => setHoveredUser(null)}\n >\n {/* Avatar circle with user color border */}\n <div\n className=\"w-9 h-9 rounded-full flex items-center justify-center text-white text-xs font-bold transition-transform group-hover:-translate-y-1 group-hover:scale-110\"\n style={{\n backgroundColor: isHex ? avatarColor : undefined,\n boxShadow: isHex\n ? `0 0 0 3px ${avatarColor}55, 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`\n : `0 0 0 3px var(--z-accent), 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`,\n }}\n >\n <span className={cn(!isHex && avatarColor)}>{initials}</span>\n </div>\n {/* Pulsing active dot with user color */}\n <span \n className=\"absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 animate-pulse\"\n style={{\n backgroundColor: isHex ? avatarColor : 'var(--z-accent)',\n borderColor: theme === 'dark' ? '#18181b' : '#fff',\n }}\n />\n {/* Framer Motion tooltip */}\n <AnimatePresence>\n {hoveredUser === (m.userId || String(i)) && (\n <motion.div \n initial={{ opacity: 0, y: 5, scale: 0.95 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 5, scale: 0.95 }}\n transition={{ duration: 0.12 }}\n className=\"absolute top-full mt-3 left-1/2 -translate-x-1/2 z-[999] whitespace-nowrap pointer-events-none\"\n >\n <div className={cn(\n \"px-3 py-2 rounded-xl shadow-2xl text-xs font-medium flex items-center gap-2\",\n theme === 'dark' ? 'bg-[#18181b] text-z-primary border border-z-border shadow-black/60' : 'bg-white text-z-primary border shadow-lg'\n )}>\n <div \n className=\"w-2.5 h-2.5 rounded-full shrink-0 animate-pulse\"\n style={{ backgroundColor: isHex ? avatarColor : 'var(--z-accent)' }}\n />\n <span className=\"font-semibold\">{name}</span>\n <span className=\"text-z-muted font-normal border-l border-z-border pl-2\">{statusText}</span>\n </div>\n {/* Arrow */}\n <div className={cn(\n \"absolute -top-1.5 left-1/2 -translate-x-1/2 w-3 h-3 rotate-45\",\n theme === 'dark' ? 'bg-[#18181b] border-l border-t border-z-border' : 'bg-white border-l border-t border-gray-200'\n )} />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n })}\n </div>\n <span className=\"text-sm text-z-secondary font-medium\">\n {membersOnline.length} {membersOnline.length === 1 ? 'person' : 'people'} online\n </span>\n </div>\n )}\n </DashboardCard>\n\n {/* API / DB Health strip */}\n <DashboardCard title=\"System Status\" icon={<Globe size={13} />}>\n <div className=\"space-y-3\">\n {[\n {\n label: 'REST API',\n ok: isHealthOk,\n detail: latency != null ? `${latency}ms latency` : 'Checking…',\n },\n {\n label: 'Database',\n ok: isDbOk,\n detail: health?.database || 'Unknown',\n },\n {\n label: 'Memory',\n ok: true,\n detail: health?.memory?.heapUsed\n ? `${health.memory.heapUsed} / ${health.memory.heapTotal} heap`\n : '—',\n },\n ].map((item) => (\n <div key={item.label} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {item.ok\n ? <CheckCircle2 size={13} className=\"text-z-active-text shrink-0\" />\n : <AlertTriangle size={13} className=\"text-rose-400 shrink-0\" />}\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>{item.label}</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{item.detail}</span>\n </div>\n ))}\n {health?.uptime != null && (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Clock size={13} className=\"text-z-secondary shrink-0\" />\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>Uptime</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{uptimeStr(health.uptime)}</span>\n </div>\n )}\n </div>\n </DashboardCard>\n\n </div>\n\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./rolldown-runtime-CNC7AqOf.js";import{vr as e,xr as t}from"./vendor-react-DQVTOTFO.js";import{t as n}from"./utils-fgvbH6CB.js";t();var r=e(),i=({icon:e,title:t,message:i,action:a,theme:o=`dark`,className:s})=>(0,r.jsxs)(`div`,{className:n(`w-full flex flex-col items-center justify-center p-12 text-center`,o===`dark`?`bg-z-panel border border-z-border`:`bg-[var(--z-bg-input)] border border-z-border shadow-sm`,s),children:[(0,r.jsx)(`div`,{className:n(`w-12 h-12 mb-4 rounded-none-none flex items-center justify-center`,o===`dark`?`bg-z-hover text-z-primary/40`:`bg-[var(--z-border)] text-z-secondary`),children:(0,r.jsx)(e,{size:24})}),(0,r.jsx)(`h3`,{className:n(`text-sm font-semibold mb-2`,`text-z-primary`),children:t}),(0,r.jsx)(`p`,{className:n(`text-xs max-w-sm leading-relaxed mb-6`,o===`dark`?`text-z-primary/40`:`text-z-secondary`),children:i}),a&&(0,r.jsx)(`div`,{className:`mt-2`,children:a})]});export{i as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmptyState-X_SXKpQY.js","names":[],"sources":["../../src/components/EmptyState.tsx"],"sourcesContent":["import React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../lib/utils'\n\ninterface EmptyStateProps {\n icon: LucideIcon\n title: string\n message: string\n action?: React.ReactNode\n theme?: 'light' | 'dark'\n className?: string\n}\n\nconst EmptyState: React.FC<EmptyStateProps> = ({\n icon: Icon,\n title,\n message,\n action,\n theme = 'dark',\n className\n}) => {\n return (\n <div className={cn(\n \"w-full flex flex-col items-center justify-center p-12 text-center\",\n theme === 'dark' ? \"bg-z-panel border border-z-border\" : \"bg-[var(--z-bg-input)] border border-z-border shadow-sm\",\n className\n )}>\n <div className={cn(\n \"w-12 h-12 mb-4 rounded-none-none flex items-center justify-center\",\n theme === 'dark' ? \"bg-z-hover text-z-primary/40\" : \"bg-[var(--z-border)] text-z-secondary\"\n )}>\n <Icon size={24} />\n </div>\n <h3 className={cn(\n \"text-sm font-semibold mb-2\",\n theme === 'dark' ? \"text-z-primary\" : \"text-z-primary\"\n )}>\n {title}\n </h3>\n <p className={cn(\n \"text-xs max-w-sm leading-relaxed mb-6\",\n theme === 'dark' ? \"text-z-primary/40\" : \"text-z-secondary\"\n )}>\n {message}\n </p>\n {action && (\n <div className=\"mt-2\">\n {action}\n </div>\n )}\n </div>\n )\n}\n\nexport default EmptyState\n"],"mappings":"qJAaM,GAAyC,CAC9C,KAAM,EACN,QACA,UACA,SACA,QAAQ,OACR,gBAGA,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAChB,oEACA,IAAU,OAAS,oCAAsC,0DACzD,CACA,WAJA,EAKA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAChB,oEACA,IAAU,OAAS,+BAAiC,uCACpD,YACA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,KAAM,EAAK,CAAA,CACZ,CAAA,GACL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EACf,+BACmB,gBACnB,WACC,CACG,CAAA,GACJ,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EACd,wCACA,IAAU,OAAS,oBAAsB,kBACzC,WACC,CACE,CAAA,EACF,IACD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,gBACd,CACI,CAAA,CAEA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{B as t,C as n,Cn as r,D as i,H as a,J as ee,Lt as o,N as s,Q as c,Ut as l,Wt as u,d,gt as f,ir as p,jn as m,kt as h,l as g,mt as te,pn as _,rr as v,vr as y,xr as b,zn as x}from"./vendor-react-DQVTOTFO.js";import{a as S,o as C,s as w,t as T}from"./utils-fgvbH6CB.js";var E=e(b(),1),D=y();function O(e,t=1){if(!+e)return`0 B`;let n=1024,r=t<0?0:t,i=[`B`,`KB`,`MB`,`GB`,`TB`],a=Math.floor(Math.log(e)/Math.log(n));return`${parseFloat((e/n**a).toFixed(r))} ${i[a]}`}function ne(e){if(!e)return`Unknown`;let t=Math.floor((Date.now()-new Date(e).getTime())/1e3);return t<60?`${t}s ago`:t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`}function k(){let{theme:e}=S(),y=w(e=>e.activeSiteId),[b,k]=(0,E.useState)([]),[A,j]=(0,E.useState)(!0),[M,N]=(0,E.useState)(``),[P,F]=(0,E.useState)(null),[re,ie]=(0,E.useState)([]),[I,L]=(0,E.useState)(null),[R,z]=(0,E.useState)(`grid`),[B,V]=(0,E.useState)(new Set),[H,U]=(0,E.useState)(!1),[W,G]=(0,E.useState)(null),[K,q]=(0,E.useState)(!1);(0,E.useEffect)(()=>{I&&G(I.focalPoint||{x:50,y:50})},[I]);let J=e=>e?e.startsWith(`http`)?e:`${e}`:``,Y=(0,E.useCallback)(async()=>{if(!y){j(!1);return}j(!0);try{let e=(await C.get(`/media`)).data.data||[];k(e),ie(Array.from(new Set(e.map(e=>e.folder).filter(Boolean))))}catch{g.error(`REGISTRY_SYNC_FAILURE`)}finally{j(!1)}},[y]);(0,E.useEffect)(()=>{Y()},[Y]);let ae=async e=>{if(!e.target.files?.[0])return;let t=new FormData;t.append(`file`,e.target.files[0]);try{await C.post(`/upload`,t,{headers:{"Content-Type":`multipart/form-data`}}),g.success(`ASSET_INGESTED`),Y()}catch{g.error(`INGESTION_PROTOCOL_FAILURE`)}},oe=async()=>{let e=window.prompt(`Enter the external media URL to link:`);if(e){if(!e.startsWith(`http`))return g.error(`INVALID_URL_FORMAT`);try{await C.post(`/upload`,{url:e}),g.success(`URL_LINKED_SUCCESSFULLY`),Y()}catch{g.error(`LINKING_PROTOCOL_FAILURE`)}}},se=async()=>{if(I){q(!0);try{await C.patch(`/media/${I._id}`,{focalPoint:W}),g.success(`METADATA_SYNCED`),Y(),L(b.find(e=>e._id===I._id)||I)}catch{g.error(`METADATA_SYNC_FAILED`)}finally{q(!1)}}},ce=async e=>{if(confirm(`Execute purge protocol for this node?`))try{await C.delete(`/media/${e}`),g.success(`NODE_PURGED`),I?._id===e&&L(null),Y()}catch{g.error(`PURGE_SEQUENCE_TERMINATED`)}},X=async()=>{if(B.size!==0&&confirm(`Purge ${B.size} selected assets?`))try{await Promise.all(Array.from(B).map(e=>C.delete(`/media/${e}`))),g.success(`${B.size} ASSETS_PURGED`),V(new Set),I&&B.has(I._id)&&L(null),Y()}catch{g.error(`BULK_PURGE_FAILED`)}},Z=(0,E.useMemo)(()=>b.filter(e=>{let t=(e.name||e.alt||e._id||``).toLowerCase().includes(M.toLowerCase()),n=P?e.folder===P:!0;return t&&n}),[b,M,P]),Q=e=>{V(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},le=()=>{V(e=>e.size===Z.length?new Set:new Set(Z.map(e=>e._id)))},$=(0,E.useMemo)(()=>{let e=0,t=0,n=0;return b.forEach(r=>{let i=r.size||0;e+=i,r.mimetype?.startsWith(`image`)?t+=i:n+=i}),{total:e,images:t,other:n}},[b]);return y?(0,D.jsxs)(`div`,{className:T(`h-full flex flex-col transition-colors duration-300`,e===`dark`?`bg-app text-z-primary`:`bg-[#fafafa] text-z-primary`),children:[(0,D.jsxs)(`div`,{className:`shrink-0 p-6 space-y-4 max-w-screen-2xl mx-auto w-full`,children:[(0,D.jsxs)(`div`,{className:`flex flex-col md:flex-row md:items-center justify-between gap-6`,children:[(0,D.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,D.jsx)(`div`,{className:T(`w-12 h-12 flex items-center justify-center shadow-sm border transition-colors`,e===`dark`?`bg-z-hover border-z-border text-z-primary`:`bg-z-panel border-z-border text-z-primary`),children:(0,D.jsx)(h,{size:20})}),(0,D.jsxs)(`div`,{children:[(0,D.jsx)(`h1`,{className:`text-2xl font-semibold leading-none`,children:`Storage`}),(0,D.jsx)(`p`,{className:`text-sm font-semibold text-z-secondary mt-1`,children:`Enterprise Media Registry`})]})]}),(0,D.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,D.jsxs)(`button`,{onClick:oe,className:T(`px-5 py-2.5 font-semibold text-sm transition-all leading-none flex items-center gap-2 shadow-sm`,e===`dark`?`bg-z-hover text-z-primary border border-z-border hover:bg-[var(--z-bg-hover)]`:`bg-[var(--z-bg-hover)] text-z-primary border border-z-border hover:bg-[var(--z-border)]`),children:[(0,D.jsx)(f,{size:14,strokeWidth:3}),`Link URL`]}),(0,D.jsxs)(`label`,{className:T(`px-5 py-2.5 font-semibold text-sm transition-all leading-none flex items-center gap-2 cursor-pointer shadow-sm`,e===`dark`?`bg-z-panel text-z-primary hover:bg-[var(--z-border)]`:`bg-z-accent text-z-primary hover:bg-app`),children:[(0,D.jsx)(c,{size:14,strokeWidth:3}),`Upload Asset`,(0,D.jsx)(`input`,{type:`file`,className:`hidden`,onChange:ae})]})]})]}),(0,D.jsxs)(`div`,{className:T(`grid grid-cols-2 md:grid-cols-4 gap-4 p-5 border shadow-sm`,e===`dark`?`bg-z-panel backdrop-blur-[12px] border-z-border`:`bg-z-panel border-z-border/60`),children:[(0,D.jsxs)(`div`,{className:`flex flex-col justify-between`,children:[(0,D.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary flex items-center gap-1.5`,children:[(0,D.jsx)(t,{size:11}),` Adapter`]}),(0,D.jsx)(`span`,{className:T(`text-lg font-semibold mt-1`,`text-z-primary`),children:`Local File System`})]}),(0,D.jsxs)(`div`,{className:`flex flex-col justify-between`,children:[(0,D.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary flex items-center gap-1.5`,children:[(0,D.jsx)(u,{size:11}),` Mass`]}),(0,D.jsxs)(`span`,{className:T(`text-lg font-semibold mt-1 tabular-nums`,`text-z-primary`),children:[b.length,` `,(0,D.jsx)(`span`,{className:`text-sm text-z-secondary ml-1`,children:`Assets`})]})]}),(0,D.jsxs)(`div`,{className:`flex flex-col justify-between`,children:[(0,D.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary flex items-center gap-1.5`,children:[(0,D.jsx)(h,{size:11}),` Volume`]}),(0,D.jsx)(`span`,{className:T(`text-lg font-semibold mt-1 tabular-nums text-z-active-text`),children:O($.total)})]}),(0,D.jsxs)(`div`,{className:`flex flex-col justify-end gap-1.5`,children:[(0,D.jsxs)(`div`,{className:`flex justify-between text-sm font-semibold text-z-secondary`,children:[(0,D.jsx)(`span`,{children:`Images`}),(0,D.jsx)(`span`,{children:`Data`})]}),(0,D.jsxs)(`div`,{className:T(`w-full h-2 flex border overflow-hidden`,e===`dark`?`border-z-border bg-z-hover`:`border-z-border bg-[var(--z-bg-hover)]`),children:[(0,D.jsx)(`div`,{className:`h-full bg-z-accent transition-all duration-1000`,style:{width:`${$.total?$.images/$.total*100:0}%`}}),(0,D.jsx)(`div`,{className:`h-full bg-z-accent transition-all duration-1000`,style:{width:`${$.total?$.other/$.total*100:0}%`}})]})]})]})]}),(0,D.jsxs)(`div`,{className:`flex-1 flex overflow-hidden max-w-screen-2xl mx-auto w-full px-6 pb-6 gap-6`,children:[(0,D.jsxs)(`div`,{className:`hidden md:flex w-48 shrink-0 flex-col space-y-1 overflow-y-auto`,children:[(0,D.jsx)(`div`,{className:`text-sm font-semibold text-z-secondary px-3 py-2`,children:`Directories`}),(0,D.jsx)(`button`,{onClick:()=>F(null),className:T(`w-full flex items-center justify-between px-3 py-2.5 transition-all text-sm font-bold`,P===null?e===`dark`?`bg-z-hover text-z-primary border-l-2 border-z-accent`:`bg-[var(--z-bg-hover)] text-z-primary border-l-2 border-z-accent`:`text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)] border-l-2 border-transparent dark:hover:bg-z-panel`),children:(0,D.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,D.jsx)(r,{size:14,className:P===null?`text-z-active-text`:`opacity-40`}),`All Assets`]})}),re.map(t=>(0,D.jsx)(`button`,{onClick:()=>F(t),className:T(`w-full flex items-center justify-between px-3 py-2.5 transition-all text-sm font-bold capitalize`,P===t?e===`dark`?`bg-z-hover text-z-primary border-l-2 border-z-accent`:`bg-[var(--z-bg-hover)] text-z-primary border-l-2 border-z-accent`:`text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)] border-l-2 border-transparent dark:hover:bg-z-panel`),children:(0,D.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,D.jsx)(u,{size:14,className:P===t?`text-z-active-text`:`opacity-40`}),t]})},t))]}),(0,D.jsxs)(`div`,{className:`flex-1 flex flex-col min-w-0 border bg-z-panel dark:bg-z-panel border-z-border dark:border-z-border shadow-sm relative overflow-hidden`,children:[(0,D.jsxs)(`div`,{className:T(`flex items-center justify-between p-3 border-b z-10 bg-z-panel/80 dark:bg-[var(--z-bg-modal)] backdrop-blur-md`,`border-z-border`),children:[(0,D.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,D.jsx)(`button`,{onClick:le,className:`p-1.5 text-z-secondary hover:text-z-active-text transition-colors`,children:B.size>0?(0,D.jsx)(m,{size:16}):(0,D.jsx)(s,{size:16})}),(0,D.jsxs)(`div`,{className:`relative flex items-center`,children:[(0,D.jsx)(a,{size:14,className:`absolute left-2.5 text-z-muted`}),(0,D.jsx)(`input`,{type:`text`,placeholder:`Filter assets...`,value:M,onChange:e=>N(e.target.value),className:T(`pl-8 pr-3 py-1.5 text-sm font-medium w-48 transition-all outline-none border`,e===`dark`?`bg-z-panel border-z-border text-z-primary placeholder:text-z-secondary focus:border-z-accent/50`:`bg-z-input border-z-border focus:border-z-accent/50`)})]}),B.size>0&&(0,D.jsxs)(`div`,{className:`flex items-center gap-2 ml-2 border-l pl-4 border-z-border `,children:[(0,D.jsxs)(`span`,{className:`text-sm font-semibold text-z-active-text`,children:[B.size,` Selected`]}),(0,D.jsx)(`button`,{onClick:X,className:`p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-500/10 rounded transition-colors`,title:`Delete Selected`,children:(0,D.jsx)(n,{size:14})})]})]}),(0,D.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,D.jsx)(`button`,{onClick:()=>z(`grid`),className:T(`p-1.5 rounded transition-colors`,R===`grid`?e===`dark`?`bg-z-hover text-z-primary`:`bg-[var(--z-border)] text-z-primary`:`text-z-muted hover:text-z-secondary dark:hover:text-z-secondary`),children:(0,D.jsx)(x,{size:15})}),(0,D.jsx)(`button`,{onClick:()=>z(`list`),className:T(`p-1.5 rounded transition-colors`,R===`list`?e===`dark`?`bg-z-hover text-z-primary`:`bg-[var(--z-border)] text-z-primary`:`text-z-muted hover:text-z-secondary dark:hover:text-z-secondary`),children:(0,D.jsx)(te,{size:15})}),(0,D.jsx)(`div`,{className:`w-px h-4 bg-[var(--z-border)] mx-1`}),(0,D.jsx)(`button`,{onClick:Y,className:`p-1.5 text-z-muted hover:text-z-secondary dark:hover:text-z-secondary transition-colors`,children:(0,D.jsx)(ee,{size:14,className:A?`animate-spin`:``})})]})]}),(0,D.jsx)(`div`,{className:`flex-1 overflow-y-auto p-4`,children:A&&b.length===0?(0,D.jsx)(`div`,{className:`h-full flex items-center justify-center text-sm font-semibold text-z-muted animate-pulse`,children:`Scanning...`}):Z.length===0?(0,D.jsxs)(`div`,{className:`h-full flex flex-col items-center justify-center text-z-muted gap-3`,children:[(0,D.jsx)(r,{size:32,className:`opacity-20`}),(0,D.jsx)(`p`,{className:`text-sm font-bold`,children:`No assets found`})]}):R===`grid`?(0,D.jsx)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-5 gap-4`,children:Z.map(e=>{let t=B.has(e._id),n=e.mimetype?.startsWith(`image`);return(0,D.jsxs)(`div`,{onClick:()=>L(e),className:T(`group relative border cursor-pointer transition-all duration-200 bg-z-panel dark:bg-app`,t?`border-z-accent ring-1 ring-z-active-border`:`border-z-border hover:border-z-active-border/50`),children:[(0,D.jsx)(`button`,{onClick:t=>{t.stopPropagation(),Q(e._id)},className:T(`absolute top-2 left-2 z-20 p-1 bg-app/50 backdrop-blur text-z-primary transition-opacity`,t?`opacity-100`:`opacity-0 group-hover:opacity-100`),children:t?(0,D.jsx)(m,{size:14,className:`text-z-active-text`}):(0,D.jsx)(s,{size:14})}),(0,D.jsx)(`div`,{className:`aspect-square bg-[var(--z-bg-input)] dark:bg-z-panel flex items-center justify-center overflow-hidden relative`,children:n?(0,D.jsx)(`img`,{src:J(e.url),alt:e.alt,className:`w-full h-full object-cover transition-transform duration-500 group-hover:scale-105`}):(0,D.jsx)(_,{size:32,className:`text-z-secondary dark:text-z-primary/20`,strokeWidth:1})}),(0,D.jsxs)(`div`,{className:`p-3 border-t border-z-border dark:border-z-border`,children:[(0,D.jsx)(`p`,{className:`text-sm font-bold truncate text-z-primary `,children:e.name||`Unnamed`}),(0,D.jsx)(`p`,{className:`text-sm font-semibold text-z-secondary mt-1`,children:O(e.size)})]})]},e._id)})}):(0,D.jsxs)(`div`,{className:`w-full text-left`,children:[(0,D.jsxs)(`div`,{className:`grid grid-cols-[auto_1fr_100px_100px_100px] gap-4 px-4 py-2 border-b border-z-border text-sm font-semibold text-z-secondary sticky top-0 bg-z-panel dark:bg-z-panel backdrop-blur z-10`,children:[(0,D.jsx)(`div`,{className:`w-4`}),(0,D.jsx)(`div`,{children:`Name`}),(0,D.jsx)(`div`,{children:`Size`}),(0,D.jsx)(`div`,{children:`Type`}),(0,D.jsx)(`div`,{children:`Date`})]}),Z.map(e=>{let t=B.has(e._id),n=e.mimetype?.startsWith(`image`);return(0,D.jsxs)(`div`,{onClick:()=>L(e),className:T(`grid grid-cols-[auto_1fr_100px_100px_100px] gap-4 px-4 py-2.5 items-center cursor-pointer border-b border-z-border dark:border-z-border hover:bg-[var(--z-bg-input)] dark:hover:bg-z-panel transition-colors`,t&&`bg-z-active-bg/50 dark:bg-z-active-bg`),children:[(0,D.jsx)(`button`,{onClick:t=>{t.stopPropagation(),Q(e._id)},className:T(`text-z-muted`,t&&`text-z-active-text`),children:t?(0,D.jsx)(m,{size:14}):(0,D.jsx)(s,{size:14})}),(0,D.jsxs)(`div`,{className:`flex items-center gap-3 overflow-hidden`,children:[(0,D.jsx)(`div`,{className:`w-6 h-6 shrink-0 bg-z-hover flex items-center justify-center overflow-hidden`,children:n?(0,D.jsx)(`img`,{src:J(e.url),className:`w-full h-full object-cover`}):(0,D.jsx)(o,{size:12,className:`text-z-muted`})}),(0,D.jsx)(`span`,{className:`text-sm font-bold truncate text-z-primary `,children:e.name||`Unnamed`})]}),(0,D.jsx)(`span`,{className:`text-sm text-z-secondary font-medium tabular-nums`,children:O(e.size)}),(0,D.jsx)(`span`,{className:`text-sm font-semibold text-z-secondary truncate`,children:e.mimetype?.split(`/`)[1]||`DATA`}),(0,D.jsx)(`span`,{className:`text-sm text-z-secondary tabular-nums`,children:ne(e.createdAt)})]},e._id)})]})})]}),(0,D.jsx)(p,{children:I&&(0,D.jsxs)(v.div,{initial:{width:0,opacity:0},animate:{width:320,opacity:1},exit:{width:0,opacity:0},className:`shrink-0 h-full border bg-z-panel dark:bg-app/60 backdrop-blur-xl border-z-border dark:border-z-border shadow-2xl flex flex-col overflow-hidden relative z-20`,children:[(0,D.jsxs)(`div`,{className:`flex items-center justify-between p-4 border-b border-z-border `,children:[(0,D.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary flex items-center gap-2`,children:[(0,D.jsx)(i,{size:12}),` Details`]}),(0,D.jsx)(`button`,{onClick:()=>L(null),className:`p-1 hover:bg-[var(--z-bg-hover)] dark:hover:bg-[var(--z-bg-hover)] rounded transition-colors`,children:(0,D.jsx)(d,{size:14})})]}),(0,D.jsxs)(`div`,{className:`flex-1 overflow-y-auto p-5 space-y-6`,children:[(0,D.jsx)(`div`,{className:`relative w-full aspect-square bg-[var(--z-bg-hover)] dark:bg-app/50 border border-z-border overflow-hidden flex items-center justify-center group select-none`,children:I.mimetype?.startsWith(`image`)?(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(`img`,{src:J(I.url),alt:`Preview`,className:`max-w-full max-h-full object-contain pointer-events-none`}),(0,D.jsx)(`div`,{className:`absolute inset-0 cursor-crosshair`,onMouseDown:e=>{let t=e.currentTarget.getBoundingClientRect(),n=Math.round((e.clientX-t.left)/t.width*100),r=Math.round((e.clientY-t.top)/t.height*100);G({x:Math.max(0,Math.min(100,n)),y:Math.max(0,Math.min(100,r))}),U(!0)}}),W&&(0,D.jsx)(`div`,{className:`absolute pointer-events-none transition-all duration-100 flex items-center justify-center`,style:{left:`${W.x}%`,top:`${W.y}%`,transform:`translate(-50%, -50%)`,width:`20px`,height:`20px`,borderRadius:`50%`,border:`2px solid var(--z-accent)`,boxShadow:`0 0 8px rgba(0,0,0,0.5)`},children:(0,D.jsx)(`div`,{className:`w-1 h-1 bg-z-accent rounded-full`})})]}):(0,D.jsx)(_,{size:48,className:`text-z-secondary dark:text-z-primary/10`,strokeWidth:.5})}),H&&W&&I.mimetype?.startsWith(`image`)&&(0,D.jsxs)(`div`,{className:`flex flex-col gap-2 p-3 bg-z-active-bg dark:bg-z-active-bg border border-z-active-border dark:border-z-accent/20`,children:[(0,D.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,D.jsx)(`span`,{className:`text-sm font-semibold text-z-accent dark:text-z-active-text`,children:`Focal Matrix`}),(0,D.jsxs)(`span`,{className:`text-sm font-mono font-bold text-z-primary `,children:[`X:`,W.x,` Y:`,W.y]})]}),(0,D.jsx)(`button`,{onClick:se,disabled:K,className:`w-full py-1.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors`,children:K?`Saving...`:`Save Coordinates`})]}),(0,D.jsxs)(`div`,{className:`space-y-3`,children:[(0,D.jsx)(`h3`,{className:`text-sm font-semibold text-z-muted border-b border-z-border pb-2`,children:`Properties`}),(0,D.jsxs)(`div`,{className:`space-y-2`,children:[(0,D.jsxs)(`div`,{className:`flex justify-between items-start gap-4`,children:[(0,D.jsx)(`span`,{className:`text-sm text-z-secondary`,children:`Name`}),(0,D.jsx)(`span`,{className:`text-sm font-medium text-right break-all`,children:I.name})]}),(0,D.jsxs)(`div`,{className:`flex justify-between items-center`,children:[(0,D.jsx)(`span`,{className:`text-sm text-z-secondary`,children:`Size`}),(0,D.jsx)(`span`,{className:`text-sm font-medium tabular-nums`,children:O(I.size)})]}),(0,D.jsxs)(`div`,{className:`flex justify-between items-center`,children:[(0,D.jsx)(`span`,{className:`text-sm text-z-secondary`,children:`Type`}),(0,D.jsx)(`span`,{className:`text-sm font-medium`,children:I.mimetype})]}),(0,D.jsxs)(`div`,{className:`flex justify-between items-center`,children:[(0,D.jsx)(`span`,{className:`text-sm text-z-secondary`,children:`Uploaded`}),(0,D.jsx)(`span`,{className:`text-sm font-medium`,children:new Date(I.createdAt).toLocaleDateString()})]}),(0,D.jsxs)(`div`,{className:`flex justify-between items-center`,children:[(0,D.jsx)(`span`,{className:`text-sm text-z-secondary`,children:`ID`}),(0,D.jsx)(`span`,{className:`text-sm font-mono text-z-muted truncate max-w-[120px]`,children:I._id})]})]})]})]}),(0,D.jsxs)(`div`,{className:`p-4 border-t border-z-border grid grid-cols-2 gap-2 bg-[var(--z-bg-input)] dark:bg-app/50`,children:[(0,D.jsxs)(`a`,{href:J(I.url),download:!0,className:`flex items-center justify-center gap-2 py-2 border border-z-border text-sm font-semibold hover:bg-z-panel dark:hover:bg-z-hover transition-colors`,children:[(0,D.jsx)(l,{size:12}),` Download`]}),(0,D.jsxs)(`button`,{onClick:()=>ce(I._id),className:`flex items-center justify-center gap-2 py-2 text-rose-500 border border-rose-100 dark:border-rose-500/20 hover:bg-rose-50 dark:hover:bg-rose-500/10 text-sm font-semibold transition-colors`,children:[(0,D.jsx)(n,{size:12}),` Delete`]})]})]})})]})]}):(0,D.jsxs)(`div`,{className:T(`flex-1 h-full w-full flex flex-col items-center justify-center p-12 transition-colors duration-500`,e===`dark`?`bg-app text-z-primary`:`bg-[#fafafa] text-z-primary`),children:[(0,D.jsx)(u,{size:48,className:`text-z-secondary/30 mb-6`}),(0,D.jsx)(`h2`,{className:`text-xl font-semibold text-z-muted mb-2`,children:`No Site Selected`}),(0,D.jsx)(`p`,{className:`text-sm font-medium text-z-secondary`,children:`Please select a site to manage its media registry.`})]})}export{k as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaLibrary-BzXE95xo.js","names":[],"sources":["../../src/pages/MediaLibrary.tsx"],"sourcesContent":["import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react'\nimport {\n Search,\n Trash2,\n Download,\n Plus,\n X,\n Database,\n RefreshCw,\n Archive,\n Maximize2,\n Binary,\n Clock,\n CheckSquare,\n Square,\n Target,\n Server,\n Image as ImageIcon,\n HardDrive,\n List,\n Grid,\n FileText,\n ChevronRight,\n ChevronLeft,\n Link2\n} from 'lucide-react'\nimport api from '../lib/api'\nimport { cn } from '../lib/utils'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport toast from 'react-hot-toast'\nimport { useTheme } from '../context/ThemeContext'\nimport { useTenantStore } from '../lib/tenantStore'\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\nfunction formatBytes(bytes: number, decimals = 1) {\n if (!+bytes) return '0 B'\n const k = 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB']\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`\n}\n\nfunction timeAgo(ts: string) {\n if (!ts) return 'Unknown'\n const secs = Math.floor((Date.now() - new Date(ts).getTime()) / 1000)\n if (secs < 60) return `${secs}s ago`\n if (secs < 3600) return `${Math.floor(secs / 60)}m ago`\n if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`\n return `${Math.floor(secs / 86400)}d ago`\n}\n\nexport default function MediaLibrary() {\n const { theme } = useTheme()\n const activeSiteId = useTenantStore((state) => state.activeSiteId)\n \n const [files, setFiles] = useState<any[]>([])\n const [loading, setLoading] = useState(true)\n const [search, setSearch] = useState('')\n const [activeFolder, setActiveFolder] = useState<string | null>(null)\n const [folders, setFolders] = useState<string[]>([])\n \n const [selectedFile, setSelectedFile] = useState<any>(null)\n const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid')\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())\n\n // Forensics / Edit mode state\n const [isFocalMode, setIsFocalMode] = useState(false)\n const [focalPoint, setFocalPoint] = useState<{ x: number; y: number } | null>(null)\n const [isProcessing, setIsProcessing] = useState(false)\n\n // Removed blob cache state\n\n useEffect(() => {\n if (selectedFile) {\n setFocalPoint(selectedFile.focalPoint || { x: 50, y: 50 })\n }\n }, [selectedFile])\n\n const getFullUrl = (url?: string) => {\n if (!url) return ''\n if (url.startsWith('http')) return url\n return `${(import.meta.env.VITE_API_URL || '').replace('/api/v1', '')}${url}`\n }\n\n const fetchFiles = useCallback(async () => {\n if (!activeSiteId) {\n setLoading(false)\n return\n }\n setLoading(true)\n try {\n const res = await api.get('/media')\n const allFiles = res.data.data || []\n setFiles(allFiles)\n\n const uniqueFolders = Array.from(\n new Set(allFiles.map((f: any) => f.folder).filter(Boolean))\n ) as string[]\n setFolders(uniqueFolders)\n } catch {\n toast.error('REGISTRY_SYNC_FAILURE')\n } finally {\n setLoading(false)\n }\n }, [activeSiteId])\n\n useEffect(() => {\n fetchFiles()\n }, [fetchFiles])\n\n\n\n const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {\n if (!e.target.files?.[0]) return\n const formData = new FormData()\n formData.append('file', e.target.files[0])\n\n try {\n await api.post('/upload', formData, {\n headers: { 'Content-Type': 'multipart/form-data' },\n })\n toast.success('ASSET_INGESTED')\n fetchFiles()\n } catch {\n toast.error('INGESTION_PROTOCOL_FAILURE')\n }\n }\n\n const handleUrlLink = async () => {\n const url = window.prompt('Enter the external media URL to link:')\n if (!url) return\n if (!url.startsWith('http')) return toast.error('INVALID_URL_FORMAT')\n\n try {\n await api.post('/upload', { url })\n toast.success('URL_LINKED_SUCCESSFULLY')\n fetchFiles()\n } catch {\n toast.error('LINKING_PROTOCOL_FAILURE')\n }\n }\n\n const handleSaveMetadata = async () => {\n if (!selectedFile) return\n setIsProcessing(true)\n try {\n await api.patch(`/media/${selectedFile._id}`, { focalPoint })\n toast.success('METADATA_SYNCED')\n fetchFiles()\n setSelectedFile(files.find(f => f._id === selectedFile._id) || selectedFile) // update local\n } catch {\n toast.error('METADATA_SYNC_FAILED')\n } finally {\n setIsProcessing(false)\n }\n }\n\n const deleteFile = async (id: string) => {\n if (!confirm('Execute purge protocol for this node?')) return\n try {\n await api.delete(`/media/${id}`)\n toast.success('NODE_PURGED')\n if (selectedFile?._id === id) setSelectedFile(null)\n fetchFiles()\n } catch {\n toast.error('PURGE_SEQUENCE_TERMINATED')\n }\n }\n\n const bulkDelete = async () => {\n if (selectedIds.size === 0) return\n if (!confirm(`Purge ${selectedIds.size} selected assets?`)) return\n try {\n await Promise.all(Array.from(selectedIds).map((id) => api.delete(`/media/${id}`)))\n toast.success(`${selectedIds.size} ASSETS_PURGED`)\n setSelectedIds(new Set())\n if (selectedFile && selectedIds.has(selectedFile._id)) setSelectedFile(null)\n fetchFiles()\n } catch {\n toast.error('BULK_PURGE_FAILED')\n }\n }\n\n const filteredFiles = useMemo(() => {\n return files.filter((f: any) => {\n const matchesSearch = (f.name || f.alt || f._id || '').toLowerCase().includes(search.toLowerCase())\n const matchesFolder = activeFolder ? f.folder === activeFolder : true\n return matchesSearch && matchesFolder\n })\n }, [files, search, activeFolder])\n\n const toggleSelect = (id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n const toggleSelectAll = () => {\n setSelectedIds((prev) => {\n if (prev.size === filteredFiles.length) return new Set()\n return new Set(filteredFiles.map((f) => f._id))\n })\n }\n\n // Calculate Metrics\n const metrics = useMemo(() => {\n let total = 0, images = 0, other = 0\n files.forEach(f => {\n const s = f.size || 0\n total += s\n if (f.mimetype?.startsWith('image')) images += s\n else other += s\n })\n return { total, images, other }\n }, [files])\n\n if (!activeSiteId) {\n return (\n <div className={cn('flex-1 h-full w-full flex flex-col items-center justify-center p-12 transition-colors duration-500', theme === 'dark' ? 'bg-app text-z-primary' : 'bg-[#fafafa] text-z-primary')}>\n <Database size={48} className=\"text-z-secondary/30 mb-6\" />\n <h2 className=\"text-xl font-semibold text-z-muted mb-2\">No Site Selected</h2>\n <p className=\"text-sm font-medium text-z-secondary\">Please select a site to manage its media registry.</p>\n </div>\n )\n }\n\n return (\n <div className={cn('h-full flex flex-col transition-colors duration-300', theme === 'dark' ? 'bg-app text-z-primary' : 'bg-[#fafafa] text-z-primary')}>\n \n {/* ── Storage Dashboard Header ───────────────────────────────────────── */}\n <div className=\"shrink-0 p-6 space-y-4 max-w-screen-2xl mx-auto w-full\">\n <div className=\"flex flex-col md:flex-row md:items-center justify-between gap-6\">\n <div className=\"flex items-center gap-4\">\n <div className={cn('w-12 h-12 flex items-center justify-center shadow-sm border transition-colors', theme === 'dark' ? 'bg-z-hover border-z-border text-z-primary' : 'bg-z-panel border-z-border text-z-primary')}>\n <HardDrive size={20} />\n </div>\n <div>\n <h1 className=\"text-2xl font-semibold leading-none\">Storage</h1>\n <p className=\"text-sm font-semibold text-z-secondary mt-1\">Enterprise Media Registry</p>\n </div>\n </div>\n \n <div className=\"flex items-center gap-3\">\n <button\n onClick={handleUrlLink}\n className={cn(\n 'px-5 py-2.5 font-semibold text-sm transition-all leading-none flex items-center gap-2 shadow-sm',\n theme === 'dark' ? 'bg-z-hover text-z-primary border border-z-border hover:bg-[var(--z-bg-hover)]' : 'bg-[var(--z-bg-hover)] text-z-primary border border-z-border hover:bg-[var(--z-border)]'\n )}\n >\n <Link2 size={14} strokeWidth={3} />\n Link URL\n </button>\n <label className={cn(\n 'px-5 py-2.5 font-semibold text-sm transition-all leading-none flex items-center gap-2 cursor-pointer shadow-sm',\n theme === 'dark' ? 'bg-z-panel text-z-primary hover:bg-[var(--z-border)]' : 'bg-z-accent text-z-primary hover:bg-app'\n )}>\n <Plus size={14} strokeWidth={3} />\n Upload Asset\n <input type=\"file\" className=\"hidden\" onChange={handleUpload} />\n </label>\n </div>\n </div>\n\n {/* Metrics Strip */}\n <div className={cn('grid grid-cols-2 md:grid-cols-4 gap-4 p-5 border shadow-sm', theme === 'dark' ? 'bg-z-panel backdrop-blur-[12px] border-z-border' : 'bg-z-panel border-z-border/60')}>\n <div className=\"flex flex-col justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary flex items-center gap-1.5\"><Server size={11} /> Adapter</span>\n <span className={cn('text-lg font-semibold mt-1', 'text-z-primary')}>\n {import.meta.env.VITE_S3_BUCKET ? 'AWS S3' : 'Local File System'}\n </span>\n </div>\n <div className=\"flex flex-col justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary flex items-center gap-1.5\"><Database size={11} /> Mass</span>\n <span className={cn('text-lg font-semibold mt-1 tabular-nums', 'text-z-primary')}>\n {files.length} <span className=\"text-sm text-z-secondary ml-1\">Assets</span>\n </span>\n </div>\n <div className=\"flex flex-col justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary flex items-center gap-1.5\"><HardDrive size={11} /> Volume</span>\n <span className={cn('text-lg font-semibold mt-1 tabular-nums text-z-active-text')}>\n {formatBytes(metrics.total)}\n </span>\n </div>\n <div className=\"flex flex-col justify-end gap-1.5\">\n <div className=\"flex justify-between text-sm font-semibold text-z-secondary\">\n <span>Images</span>\n <span>Data</span>\n </div>\n <div className={cn('w-full h-2 flex border overflow-hidden', theme === 'dark' ? 'border-z-border bg-z-hover' : 'border-z-border bg-[var(--z-bg-hover)]')}>\n <div \n className=\"h-full bg-z-accent transition-all duration-1000\" \n style={{ width: `${metrics.total ? (metrics.images / metrics.total) * 100 : 0}%` }} \n />\n <div \n className=\"h-full bg-z-accent transition-all duration-1000\" \n style={{ width: `${metrics.total ? (metrics.other / metrics.total) * 100 : 0}%` }} \n />\n </div>\n </div>\n </div>\n </div>\n\n {/* ── Main Workspace ─────────────────────────────────────────────────── */}\n <div className=\"flex-1 flex overflow-hidden max-w-screen-2xl mx-auto w-full px-6 pb-6 gap-6\">\n \n {/* Left Sidebar - Folders */}\n <div className=\"hidden md:flex w-48 shrink-0 flex-col space-y-1 overflow-y-auto\">\n <div className=\"text-sm font-semibold text-z-secondary px-3 py-2\">\n Directories\n </div>\n <button\n onClick={() => setActiveFolder(null)}\n className={cn(\n 'w-full flex items-center justify-between px-3 py-2.5 transition-all text-sm font-bold',\n activeFolder === null\n ? (theme === 'dark' ? 'bg-z-hover text-z-primary border-l-2 border-z-accent' : 'bg-[var(--z-bg-hover)] text-z-primary border-l-2 border-z-accent')\n : 'text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)] border-l-2 border-transparent dark:hover:bg-z-panel'\n )}\n >\n <div className=\"flex items-center gap-2.5\">\n <Archive size={14} className={activeFolder === null ? 'text-z-active-text' : 'opacity-40'} />\n All Assets\n </div>\n </button>\n {folders.map(folder => (\n <button\n key={folder}\n onClick={() => setActiveFolder(folder)}\n className={cn(\n 'w-full flex items-center justify-between px-3 py-2.5 transition-all text-sm font-bold capitalize',\n activeFolder === folder\n ? (theme === 'dark' ? 'bg-z-hover text-z-primary border-l-2 border-z-accent' : 'bg-[var(--z-bg-hover)] text-z-primary border-l-2 border-z-accent')\n : 'text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)] border-l-2 border-transparent dark:hover:bg-z-panel'\n )}\n >\n <div className=\"flex items-center gap-2.5\">\n <Database size={14} className={activeFolder === folder ? 'text-z-active-text' : 'opacity-40'} />\n {folder}\n </div>\n </button>\n ))}\n </div>\n\n {/* Center - File Grid/List */}\n <div className=\"flex-1 flex flex-col min-w-0 border bg-z-panel dark:bg-z-panel border-z-border dark:border-z-border shadow-sm relative overflow-hidden\">\n \n {/* Toolbar */}\n <div className={cn('flex items-center justify-between p-3 border-b z-10 bg-z-panel/80 dark:bg-[var(--z-bg-modal)] backdrop-blur-md', 'border-z-border')}>\n <div className=\"flex items-center gap-3\">\n <button onClick={toggleSelectAll} className=\"p-1.5 text-z-secondary hover:text-z-active-text transition-colors\">\n {selectedIds.size > 0 ? <CheckSquare size={16} /> : <Square size={16} />}\n </button>\n \n <div className=\"relative flex items-center\">\n <Search size={14} className=\"absolute left-2.5 text-z-muted\" />\n <input \n type=\"text\" \n placeholder=\"Filter assets...\" \n value={search}\n onChange={e => setSearch(e.target.value)}\n className={cn(\n 'pl-8 pr-3 py-1.5 text-sm font-medium w-48 transition-all outline-none border',\n theme === 'dark' ? 'bg-z-panel border-z-border text-z-primary placeholder:text-z-secondary focus:border-z-accent/50' : 'bg-z-input border-z-border focus:border-z-accent/50'\n )}\n />\n </div>\n\n {selectedIds.size > 0 && (\n <div className=\"flex items-center gap-2 ml-2 border-l pl-4 border-z-border \">\n <span className=\"text-sm font-semibold text-z-active-text\">\n {selectedIds.size} Selected\n </span>\n <button onClick={bulkDelete} className=\"p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-500/10 rounded transition-colors\" title=\"Delete Selected\">\n <Trash2 size={14} />\n </button>\n </div>\n )}\n </div>\n\n <div className=\"flex items-center gap-1\">\n <button onClick={() => setViewMode('grid')} className={cn('p-1.5 rounded transition-colors', viewMode === 'grid' ? (theme === 'dark' ? 'bg-z-hover text-z-primary' : 'bg-[var(--z-border)] text-z-primary') : 'text-z-muted hover:text-z-secondary dark:hover:text-z-secondary')}>\n <Grid size={15} />\n </button>\n <button onClick={() => setViewMode('list')} className={cn('p-1.5 rounded transition-colors', viewMode === 'list' ? (theme === 'dark' ? 'bg-z-hover text-z-primary' : 'bg-[var(--z-border)] text-z-primary') : 'text-z-muted hover:text-z-secondary dark:hover:text-z-secondary')}>\n <List size={15} />\n </button>\n <div className=\"w-px h-4 bg-[var(--z-border)] mx-1\" />\n <button onClick={fetchFiles} className=\"p-1.5 text-z-muted hover:text-z-secondary dark:hover:text-z-secondary transition-colors\">\n <RefreshCw size={14} className={loading ? 'animate-spin' : ''} />\n </button>\n </div>\n </div>\n\n {/* Asset View */}\n <div className=\"flex-1 overflow-y-auto p-4\">\n {loading && files.length === 0 ? (\n <div className=\"h-full flex items-center justify-center text-sm font-semibold text-z-muted animate-pulse\">Scanning...</div>\n ) : filteredFiles.length === 0 ? (\n <div className=\"h-full flex flex-col items-center justify-center text-z-muted gap-3\">\n <Archive size={32} className=\"opacity-20\" />\n <p className=\"text-sm font-bold\">No assets found</p>\n </div>\n ) : viewMode === 'grid' ? (\n // GRID VIEW\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-5 gap-4\">\n {filteredFiles.map(file => {\n const isSelected = selectedIds.has(file._id)\n const isImage = file.mimetype?.startsWith('image')\n return (\n <div \n key={file._id}\n onClick={() => setSelectedFile(file)}\n className={cn(\n 'group relative border cursor-pointer transition-all duration-200 bg-z-panel dark:bg-app',\n isSelected ? 'border-z-accent ring-1 ring-z-active-border' : 'border-z-border hover:border-z-active-border/50'\n )}\n >\n <button\n onClick={(e) => { e.stopPropagation(); toggleSelect(file._id) }}\n className={cn(\n 'absolute top-2 left-2 z-20 p-1 bg-app/50 backdrop-blur text-z-primary transition-opacity',\n isSelected ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'\n )}\n >\n {isSelected ? <CheckSquare size={14} className=\"text-z-active-text\" /> : <Square size={14} />}\n </button>\n\n <div className=\"aspect-square bg-[var(--z-bg-input)] dark:bg-z-panel flex items-center justify-center overflow-hidden relative\">\n {isImage ? (\n <img src={getFullUrl(file.url)} alt={file.alt} className=\"w-full h-full object-cover transition-transform duration-500 group-hover:scale-105\" />\n ) : (\n <Binary size={32} className=\"text-z-secondary dark:text-z-primary/20\" strokeWidth={1} />\n )}\n </div>\n <div className=\"p-3 border-t border-z-border dark:border-z-border\">\n <p className=\"text-sm font-bold truncate text-z-primary \">{file.name || 'Unnamed'}</p>\n <p className=\"text-sm font-semibold text-z-secondary mt-1\">{formatBytes(file.size)}</p>\n </div>\n </div>\n )\n })}\n </div>\n ) : (\n // LIST VIEW\n <div className=\"w-full text-left\">\n <div className=\"grid grid-cols-[auto_1fr_100px_100px_100px] gap-4 px-4 py-2 border-b border-z-border text-sm font-semibold text-z-secondary sticky top-0 bg-z-panel dark:bg-z-panel backdrop-blur z-10\">\n <div className=\"w-4\"></div>\n <div>Name</div>\n <div>Size</div>\n <div>Type</div>\n <div>Date</div>\n </div>\n {filteredFiles.map(file => {\n const isSelected = selectedIds.has(file._id)\n const isImage = file.mimetype?.startsWith('image')\n return (\n <div \n key={file._id}\n onClick={() => setSelectedFile(file)}\n className={cn(\n 'grid grid-cols-[auto_1fr_100px_100px_100px] gap-4 px-4 py-2.5 items-center cursor-pointer border-b border-z-border dark:border-z-border hover:bg-[var(--z-bg-input)] dark:hover:bg-z-panel transition-colors',\n isSelected && 'bg-z-active-bg/50 dark:bg-z-active-bg'\n )}\n >\n <button onClick={(e) => { e.stopPropagation(); toggleSelect(file._id) }} className={cn('text-z-muted', isSelected && 'text-z-active-text')}>\n {isSelected ? <CheckSquare size={14} /> : <Square size={14} />}\n </button>\n <div className=\"flex items-center gap-3 overflow-hidden\">\n <div className=\"w-6 h-6 shrink-0 bg-z-hover flex items-center justify-center overflow-hidden\">\n {isImage ? <img src={getFullUrl(file.url)} className=\"w-full h-full object-cover\" /> : <FileText size={12} className=\"text-z-muted\" />}\n </div>\n <span className=\"text-sm font-bold truncate text-z-primary \">{file.name || 'Unnamed'}</span>\n </div>\n <span className=\"text-sm text-z-secondary font-medium tabular-nums\">{formatBytes(file.size)}</span>\n <span className=\"text-sm font-semibold text-z-secondary truncate\">{file.mimetype?.split('/')[1] || 'DATA'}</span>\n <span className=\"text-sm text-z-secondary tabular-nums\">{timeAgo(file.createdAt)}</span>\n </div>\n )\n })}\n </div>\n )}\n </div>\n </div>\n\n {/* Right Sidebar - Slide Out Forensics */}\n <AnimatePresence>\n {selectedFile && (\n <motion.div\n initial={{ width: 0, opacity: 0 }}\n animate={{ width: 320, opacity: 1 }}\n exit={{ width: 0, opacity: 0 }}\n className=\"shrink-0 h-full border bg-z-panel dark:bg-app/60 backdrop-blur-xl border-z-border dark:border-z-border shadow-2xl flex flex-col overflow-hidden relative z-20\"\n >\n <div className=\"flex items-center justify-between p-4 border-b border-z-border \">\n <span className=\"text-sm font-semibold text-z-secondary flex items-center gap-2\">\n <Target size={12} /> Details\n </span>\n <button onClick={() => setSelectedFile(null)} className=\"p-1 hover:bg-[var(--z-bg-hover)] dark:hover:bg-[var(--z-bg-hover)] rounded transition-colors\">\n <X size={14} />\n </button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-5 space-y-6\">\n \n {/* Preview / Focal Point */}\n <div className=\"relative w-full aspect-square bg-[var(--z-bg-hover)] dark:bg-app/50 border border-z-border overflow-hidden flex items-center justify-center group select-none\">\n {selectedFile.mimetype?.startsWith('image') ? (\n <>\n <img \n src={getFullUrl(selectedFile.url)} \n alt=\"Preview\" \n className=\"max-w-full max-h-full object-contain pointer-events-none\"\n />\n {/* Focal Point Interaction Layer */}\n <div \n className=\"absolute inset-0 cursor-crosshair\"\n onMouseDown={(e) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const x = Math.round(((e.clientX - rect.left) / rect.width) * 100)\n const y = Math.round(((e.clientY - rect.top) / rect.height) * 100)\n setFocalPoint({ x: Math.max(0, Math.min(100, x)), y: Math.max(0, Math.min(100, y)) })\n setIsFocalMode(true)\n }}\n />\n {focalPoint && (\n <div\n className=\"absolute pointer-events-none transition-all duration-100 flex items-center justify-center\"\n style={{ left: `${focalPoint.x}%`, top: `${focalPoint.y}%`, transform: 'translate(-50%, -50%)', width: '20px', height: '20px', borderRadius: '50%', border: '2px solid var(--z-accent)', boxShadow: '0 0 8px rgba(0,0,0,0.5)' }}\n >\n <div className=\"w-1 h-1 bg-z-accent rounded-full\" />\n </div>\n )}\n </>\n ) : (\n <Binary size={48} className=\"text-z-secondary dark:text-z-primary/10\" strokeWidth={0.5} />\n )}\n </div>\n\n {isFocalMode && focalPoint && selectedFile.mimetype?.startsWith('image') && (\n <div className=\"flex flex-col gap-2 p-3 bg-z-active-bg dark:bg-z-active-bg border border-z-active-border dark:border-z-accent/20\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-z-accent dark:text-z-active-text\">Focal Matrix</span>\n <span className=\"text-sm font-mono font-bold text-z-primary \">X:{focalPoint.x} Y:{focalPoint.y}</span>\n </div>\n <button onClick={handleSaveMetadata} disabled={isProcessing} className=\"w-full py-1.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors\">\n {isProcessing ? 'Saving...' : 'Save Coordinates'}\n </button>\n </div>\n )}\n\n {/* Metadata */}\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-z-muted border-b border-z-border pb-2\">Properties</h3>\n \n <div className=\"space-y-2\">\n <div className=\"flex justify-between items-start gap-4\">\n <span className=\"text-sm text-z-secondary\">Name</span>\n <span className=\"text-sm font-medium text-right break-all\">{selectedFile.name}</span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-z-secondary\">Size</span>\n <span className=\"text-sm font-medium tabular-nums\">{formatBytes(selectedFile.size)}</span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-z-secondary\">Type</span>\n <span className=\"text-sm font-medium\">{selectedFile.mimetype}</span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-z-secondary\">Uploaded</span>\n <span className=\"text-sm font-medium\">{new Date(selectedFile.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-sm text-z-secondary\">ID</span>\n <span className=\"text-sm font-mono text-z-muted truncate max-w-[120px]\">{selectedFile._id}</span>\n </div>\n </div>\n </div>\n </div>\n\n {/* Actions Footer */}\n <div className=\"p-4 border-t border-z-border grid grid-cols-2 gap-2 bg-[var(--z-bg-input)] dark:bg-app/50\">\n <a \n href={getFullUrl(selectedFile.url)} \n download \n className=\"flex items-center justify-center gap-2 py-2 border border-z-border text-sm font-semibold hover:bg-z-panel dark:hover:bg-z-hover transition-colors\"\n >\n <Download size={12} /> Download\n </a>\n <button \n onClick={() => deleteFile(selectedFile._id)}\n className=\"flex items-center justify-center gap-2 py-2 text-rose-500 border border-rose-100 dark:border-rose-500/20 hover:bg-rose-50 dark:hover:bg-rose-500/10 text-sm font-semibold transition-colors\"\n >\n <Trash2 size={12} /> Delete\n </button>\n </div>\n\n </motion.div>\n )}\n </AnimatePresence>\n\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{C as t,H as n,I as r,J as i,Jt as a,Kt as o,Ln as s,Wn as c,Wt as l,Z as u,cn as d,d as f,dt as p,fn as m,ir as h,l as g,rr as _,vr as v,wn as y,xr as b}from"./vendor-react-DQVTOTFO.js";import{a as x,o as S,t as C}from"./utils-fgvbH6CB.js";import{d as w,f as T,p as E}from"./index-ChcKY5Xe.js";var D=e(b(),1),O=v(),k=()=>{let{theme:e}=x(),v=e===`dark`,[b,k]=(0,D.useState)(``),[A,j]=(0,D.useState)(`installed`),[M,N]=(0,D.useState)(!0),[P,F]=(0,D.useState)([]),[I,L]=(0,D.useState)(null),[R,z]=(0,D.useState)(null),[B,V]=(0,D.useState)(!1),H=[{id:`cloudinary-asset-storage`,name:`Cloudinary Asset Storage`,author:`Cloudinary Inc.`,version:`2.1.0`,description:`Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.`,downloads:12482,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-z-active-text`})},{id:`resend-email-engine`,name:`Resend Email Engine`,author:`Resend Team`,version:`1.4.3`,description:`Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.`,downloads:8912,verified:!0,icon:(0,O.jsx)(p,{size:16,className:`text-z-secondary`})},{id:`algolia-search-matrix`,name:`Algolia Search Matrix`,author:`Algolia Labs`,version:`3.0.1`,description:`Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.`,downloads:6104,verified:!0,icon:(0,O.jsx)(n,{size:16,className:`text-z-active-text`})},{id:`vercel-deploy-webhook`,name:`Vercel Deploy Webhook`,author:`Vercel Core`,version:`1.0.8`,description:`Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.`,downloads:15221,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-amber-500`})},{id:`openai-copilot-extension`,name:`OpenAI Copilot Extension`,author:`OpenAI Inc.`,version:`4.2.0`,description:`Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.`,downloads:19823,verified:!0,icon:(0,O.jsx)(o,{size:16,className:`text-z-secondary`})},{id:`aws-s3-storage`,name:`AWS S3 Storage Driver`,author:`Amazon Web Services`,version:`3.1.5`,description:`Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.`,downloads:42100,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-amber-500`})},{id:`stripe-billing-portal`,name:`Stripe Billing Portal`,author:`Stripe, Inc.`,version:`1.2.9`,description:`Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.`,downloads:27503,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-indigo-400`})},{id:`posthog-analytics`,name:`PostHog Analytics`,author:`PostHog Team`,version:`2.0.0`,description:`Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.`,downloads:11200,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-rose-500`})}],U=async()=>{try{F(((await S.get(`/system/plugins`)).data.data||[]).map(e=>({...e,id:e.id||e.name.toLowerCase().replace(/\s+/g,`-`),status:e.status||(e.disabled?`inactive`:`active`),type:e.author===`ROOT_KERNEL`?`core`:`third-party`,verified:e.author===`ROOT_KERNEL`,icon:W(e.name)})))}catch(e){console.error(`Failed to fetch plugins`,e),g.error(`Failed to load plugins`)}finally{N(!1)}},W=e=>{let t=e.toLowerCase();return t.includes(`cloudinary`)?(0,O.jsx)(l,{size:16,className:`text-z-active-text`}):t.includes(`resend`)||t.includes(`mail`)?(0,O.jsx)(p,{size:16,className:`text-z-secondary`}):t.includes(`algolia`)||t.includes(`search`)?(0,O.jsx)(n,{size:16,className:`text-z-active-text`}):t.includes(`vercel`)||t.includes(`deploy`)?(0,O.jsx)(y,{size:16,className:`text-amber-500`}):t.includes(`openai`)||t.includes(`copilot`)||t.includes(`ai`)?(0,O.jsx)(o,{size:16,className:`text-z-secondary`}):(0,O.jsx)(u,{size:16,className:`text-z-secondary`})},G=async(e,t)=>{if(!(!e||!t))try{let n=t===`inactive`;await S.post(`/system/plugins/${e}/${n?`enable`:`disable`}`),g.success(`Plugin ${n?`enabled`:`disabled`}`),U()}catch{g.error(`Failed to toggle plugin status`)}},K=async e=>{z(e)},q=e=>{navigator.clipboard.writeText(e),V(!0),setTimeout(()=>V(!1),2e3),g.success(`Copied to clipboard`)};(0,D.useEffect)(()=>{U()},[]);let J=[...H,...P.filter(e=>!H.some(t=>t.name.toLowerCase()===e.name.toLowerCase()))],Y=A===`installed`?P.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())):J.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())),X=e=>P.some(t=>t.name.toLowerCase()===e.toLowerCase());return M?(0,O.jsx)(`div`,{className:`flex justify-center items-center h-[calc(100vh-64px)]`,children:(0,O.jsx)(s,{size:32,className:`animate-spin text-z-secondary`})}):(0,O.jsxs)(`div`,{className:`flex flex-col h-[calc(100vh-64px)] overflow-hidden`,children:[(0,O.jsx)(E,{title:`Plugin System`,actions:(0,O.jsxs)(`div`,{className:`flex gap-2`,children:[(0,O.jsxs)(`div`,{className:C(`flex p-1 border`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(`button`,{onClick:()=>j(`installed`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`installed`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Installed`}),(0,O.jsx)(`button`,{onClick:()=>j(`marketplace`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`marketplace`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Marketplace`})]}),(0,O.jsx)(`button`,{onClick:U,className:C(`px-3 border text-z-secondary hover:text-z-primary transition-colors`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:(0,O.jsx)(i,{size:14})})]})}),(0,O.jsxs)(`div`,{className:`flex-1 overflow-auto p-6 md:p-8 space-y-6`,children:[(0,O.jsx)(`div`,{className:`max-w-md`,children:(0,O.jsxs)(`div`,{className:C(`flex items-center gap-3 px-4 py-2 border transition-all`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(n,{size:14,className:`text-z-secondary`}),(0,O.jsx)(`input`,{type:`text`,placeholder:A===`installed`?`Search installed plugins...`:`Search marketplace...`,value:b,onChange:e=>k(e.target.value),className:`bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary`})]})}),Y.length===0?(0,O.jsxs)(`div`,{className:`text-center py-20 text-z-secondary`,children:[(0,O.jsx)(m,{size:32,className:`mx-auto mb-4 opacity-50`}),(0,O.jsx)(`p`,{className:`text-sm font-semibold`,children:`No plugins found`})]}):(0,O.jsx)(`div`,{className:`grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4`,children:(0,O.jsx)(h,{children:Y.map(e=>{let n=X(e.name);return(0,O.jsx)(_.div,{layout:!0,initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.95},children:(0,O.jsx)(w,{className:`h-full flex flex-col`,children:(0,O.jsxs)(T,{className:`p-5 flex flex-col h-full gap-4`,children:[(0,O.jsx)(`div`,{className:`flex justify-between items-start`,children:(0,O.jsxs)(`div`,{className:`flex gap-3 items-center`,children:[(0,O.jsx)(`div`,{className:C(`w-8 h-8 flex items-center justify-center border`,v?`bg-app border-z-border`:`bg-z-input border-z-border`),children:e.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-sm font-semibold`,children:e.name}),(0,O.jsxs)(`p`,{className:`text-sm text-z-secondary font-bold`,children:[e.author===`ROOT_KERNEL`?`Zenith Core`:e.author,` • v`,e.version]})]})]})}),(0,O.jsx)(`div`,{className:`flex-1`,children:(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed line-clamp-2`,children:e.description})}),(0,O.jsxs)(`div`,{className:`flex items-center justify-between pt-4 border-t border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3 text-sm text-z-secondary font-bold`,children:[e.verified&&(0,O.jsxs)(`span`,{className:`flex items-center gap-1`,children:[(0,O.jsx)(r,{size:10}),` Verified`]}),(0,O.jsxs)(`span`,{children:[e.downloads?.toLocaleString(),` DLs`]})]}),(0,O.jsx)(`div`,{className:`flex gap-2`,children:A===`installed`?(0,O.jsxs)(O.Fragment,{children:[(0,O.jsx)(`button`,{onClick:()=>G(e.id,e.status),className:C(`px-3 py-1.5 border text-sm font-semibold transition-colors`,e.status===`active`?`text-red-500 border-red-500/20 hover:bg-red-500/10`:`text-z-secondary border-z-border/20 hover:bg-z-panel`),children:e.status===`active`?`Disable`:`Enable`}),e.author!==`ROOT_KERNEL`&&(0,O.jsx)(`button`,{onClick:()=>g.error(`Core Protection: Delete via CLI`),className:`p-1.5 text-red-500/70 hover:bg-red-500/10`,children:(0,O.jsx)(t,{size:12})})]}):(0,O.jsxs)(`button`,{disabled:n||I!==null,onClick:()=>K(e),className:C(`px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2`,n?`text-z-secondary border-z-border/20 cursor-not-allowed`:`text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm`),children:[I===e.id?(0,O.jsx)(s,{size:10,className:`animate-spin`}):(0,O.jsx)(c,{size:10}),n?`Installed`:`Install`]})})]})]})})},e.id||e.name)})})})]}),R&&(0,O.jsx)(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,children:(0,O.jsxs)(`div`,{className:C(`max-w-2xl w-full border shadow-2xl flex flex-col`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsxs)(`div`,{className:`flex justify-between items-center p-5 border-b border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,O.jsx)(`div`,{className:`w-10 h-10 border border-z-border bg-z-input flex items-center justify-center`,children:R.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-lg font-bold`,children:R.name}),(0,O.jsx)(`p`,{className:`text-xs text-z-secondary font-semibold`,children:`Official Installation Guide`})]})]}),(0,O.jsx)(`button`,{onClick:()=>z(null),className:`p-2 text-z-secondary hover:text-z-primary transition-colors`,children:(0,O.jsx)(f,{size:20})})]}),(0,O.jsxs)(`div`,{className:`p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar`,children:[(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed`,children:`To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.`}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`1`}),`Install the NPM Package`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsxs)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:[`pnpm add @zenith-open/`,R.id||R.name.toLowerCase().replace(/\s+/g,`-`)]}),(0,O.jsx)(`button`,{onClick:()=>q(`pnpm add @zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}`),className:`absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity`,children:B?(0,O.jsx)(d,{size:14}):(0,O.jsx)(a,{size:14})})]})]}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`2`}),`Enable in your config`]}),(0,O.jsxs)(`p`,{className:`text-xs text-z-secondary`,children:[`Open `,(0,O.jsx)(`code`,{className:`bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border`,children:`cms.config.ts`}),` and add the plugin to the array.`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsx)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:`import { zenithPlugin } from '@zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}'
|
|
2
|
+
|
|
3
|
+
export default buildConfig({
|
|
4
|
+
collections: [...],
|
|
5
|
+
plugins: [
|
|
6
|
+
zenithPlugin({
|
|
7
|
+
// Provide any necessary config options here
|
|
8
|
+
})
|
|
9
|
+
]
|
|
10
|
+
})`}),(0,O.jsx)(`button`,{onClick:()=>q(`import { zenithPlugin } from '@zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}'\n\nexport default buildConfig({\n collections: [...],\n plugins: [\n zenithPlugin({\n // Provide any necessary config options here\n })\n ]\n})`),className:`absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity`,children:B?(0,O.jsx)(d,{size:14}):(0,O.jsx)(a,{size:14})})]})]}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`3`}),`Restart the Server`]}),(0,O.jsx)(`p`,{className:`text-xs text-z-secondary`,children:`Restart your development server and the plugin will be instantly active!`})]})]}),(0,O.jsx)(`div`,{className:`p-4 border-t border-z-border bg-z-input flex justify-end`,children:(0,O.jsx)(`button`,{onClick:()=>z(null),className:`px-6 py-2 bg-z-panel text-z-primary border border-z-border text-sm font-bold shadow-sm hover:bg-z-input transition-colors`,children:`Done`})})]})})]})};export{k as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginsPage-5YRpbP-N.js","names":[],"sources":["../../src/pages/PluginsPage.tsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport {\n Puzzle,\n Settings,\n Trash2,\n ExternalLink,\n Plus,\n Zap,\n Search,\n Box,\n Loader2,\n RefreshCw,\n Cpu,\n Activity,\n ShieldCheck,\n Database,\n Mail,\n DownloadCloud,\n Code,\n Copy,\n Check,\n X\n} from 'lucide-react'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport toast from 'react-hot-toast'\nimport api from '../lib/api'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { Card, CardContent } from '../components/ui/Card'\n\ninterface PluginData {\n id?: string\n name: string\n author?: string\n enabled?: boolean\n version?: string\n description?: string\n status?: string\n type?: string\n verified?: boolean\n downloads?: number\n icon?: React.ReactNode\n}\n\nconst PluginsPage = () => {\n const { theme } = useTheme()\n const dark = theme === 'dark'\n const [searchQuery, setSearchQuery] = useState('')\n const [activeTab, setActiveTab] = useState<'installed' | 'marketplace'>('installed')\n const [loading, setLoading] = useState(true)\n const [plugins, setPlugins] = useState<PluginData[]>([])\n const [installingId, setInstallingId] = useState<string | null>(null)\n const [installGuidePlugin, setInstallGuidePlugin] = useState<any>(null)\n const [copiedCode, setCopiedCode] = useState(false)\n\n const MARKETPLACE_PLUGINS: PluginData[] = [\n {\n id: 'cloudinary-asset-storage',\n name: 'Cloudinary Asset Storage',\n author: 'Cloudinary Inc.',\n version: '2.1.0',\n description: \"Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.\",\n downloads: 12482,\n verified: true,\n icon: <Database size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'resend-email-engine',\n name: 'Resend Email Engine',\n author: 'Resend Team',\n version: '1.4.3',\n description: 'Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.',\n downloads: 8912,\n verified: true,\n icon: <Mail size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'algolia-search-matrix',\n name: 'Algolia Search Matrix',\n author: 'Algolia Labs',\n version: '3.0.1',\n description: 'Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.',\n downloads: 6104,\n verified: true,\n icon: <Search size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'vercel-deploy-webhook',\n name: 'Vercel Deploy Webhook',\n author: 'Vercel Core',\n version: '1.0.8',\n description: 'Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.',\n downloads: 15221,\n verified: true,\n icon: <Activity size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'openai-copilot-extension',\n name: 'OpenAI Copilot Extension',\n author: 'OpenAI Inc.',\n version: '4.2.0',\n description: 'Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.',\n downloads: 19823,\n verified: true,\n icon: <Cpu size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'aws-s3-storage',\n name: 'AWS S3 Storage Driver',\n author: 'Amazon Web Services',\n version: '3.1.5',\n description: 'Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.',\n downloads: 42100,\n verified: true,\n icon: <Database size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'stripe-billing-portal',\n name: 'Stripe Billing Portal',\n author: 'Stripe, Inc.',\n version: '1.2.9',\n description: 'Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.',\n downloads: 27503,\n verified: true,\n icon: <Activity size={16} className=\"text-indigo-400\" />,\n },\n {\n id: 'posthog-analytics',\n name: 'PostHog Analytics',\n author: 'PostHog Team',\n version: '2.0.0',\n description: 'Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.',\n downloads: 11200,\n verified: true,\n icon: <Activity size={16} className=\"text-rose-500\" />,\n },\n ]\n\n const fetchPlugins = async () => {\n try {\n const res = await api.get('/system/plugins')\n const realPlugins = res.data.data || []\n setPlugins(\n realPlugins.map((p: any) => ({\n ...p,\n id: p.id || p.name.toLowerCase().replace(/\\s+/g, '-'),\n status: p.status || (p.disabled ? 'inactive' : 'active'),\n type: p.author === 'ROOT_KERNEL' ? 'core' : 'third-party',\n verified: p.author === 'ROOT_KERNEL',\n icon: getPluginIcon(p.name),\n }))\n )\n } catch (err) {\n console.error('Failed to fetch plugins', err)\n toast.error('Failed to load plugins')\n } finally {\n setLoading(false)\n }\n }\n\n const getPluginIcon = (name: string) => {\n const lowercaseName = name.toLowerCase()\n if (lowercaseName.includes('cloudinary')) return <Database size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('resend') || lowercaseName.includes('mail')) return <Mail size={16} className=\"text-z-secondary\" />\n if (lowercaseName.includes('algolia') || lowercaseName.includes('search')) return <Search size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('vercel') || lowercaseName.includes('deploy')) return <Activity size={16} className=\"text-amber-500\" />\n if (lowercaseName.includes('openai') || lowercaseName.includes('copilot') || lowercaseName.includes('ai')) return <Cpu size={16} className=\"text-z-secondary\" />\n return <Puzzle size={16} className=\"text-z-secondary\" />\n }\n\n const togglePlugin = async (id: string | undefined, currentStatus: string | undefined) => {\n if (!id || !currentStatus) return\n try {\n const newEnabled = currentStatus === 'inactive'\n await api.post(`/system/plugins/${id}/${newEnabled ? 'enable' : 'disable'}`)\n toast.success(`Plugin ${newEnabled ? 'enabled' : 'disabled'}`)\n fetchPlugins()\n } catch {\n toast.error('Failed to toggle plugin status')\n }\n }\n\n const installMarketplacePlugin = async (mpPlugin: (typeof MARKETPLACE_PLUGINS)[0]) => {\n setInstallGuidePlugin(mpPlugin)\n }\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text)\n setCopiedCode(true)\n setTimeout(() => setCopiedCode(false), 2000)\n toast.success('Copied to clipboard')\n }\n\n useEffect(() => {\n fetchPlugins()\n }, [])\n\n const mergedMarketplacePlugins = [\n ...MARKETPLACE_PLUGINS,\n ...plugins.filter(p => !MARKETPLACE_PLUGINS.some(mp => mp.name.toLowerCase() === p.name.toLowerCase()))\n ]\n\n const displayList = (activeTab === 'installed'\n ? plugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n : mergedMarketplacePlugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n ) as PluginData[]\n\n const isInstalled = (name: string) => plugins.some((p) => p.name.toLowerCase() === name.toLowerCase())\n\n if (loading) {\n return (\n <div className=\"flex justify-center items-center h-[calc(100vh-64px)]\">\n <Loader2 size={32} className=\"animate-spin text-z-secondary\" />\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] overflow-hidden\">\n <PageHeader\n title=\"Plugin System\"\n actions={\n <div className=\"flex gap-2\">\n <div className={cn('flex p-1 border', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <button\n onClick={() => setActiveTab('installed')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'installed' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Installed\n </button>\n <button\n onClick={() => setActiveTab('marketplace')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'marketplace' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Marketplace\n </button>\n </div>\n <button\n onClick={fetchPlugins}\n className={cn('px-3 border text-z-secondary hover:text-z-primary transition-colors', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}\n >\n <RefreshCw size={14} />\n </button>\n </div>\n }\n />\n\n <div className=\"flex-1 overflow-auto p-6 md:p-8 space-y-6\">\n <div className=\"max-w-md\">\n <div className={cn('flex items-center gap-3 px-4 py-2 border transition-all', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <Search size={14} className=\"text-z-secondary\" />\n <input\n type=\"text\"\n placeholder={activeTab === 'installed' ? 'Search installed plugins...' : 'Search marketplace...'}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary\"\n />\n </div>\n </div>\n\n {displayList.length === 0 ? (\n <div className=\"text-center py-20 text-z-secondary\">\n <Box size={32} className=\"mx-auto mb-4 opacity-50\" />\n <p className=\"text-sm font-semibold\">No plugins found</p>\n </div>\n ) : (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4\">\n <AnimatePresence>\n {displayList.map((plugin) => {\n const installed = isInstalled(plugin.name)\n return (\n <motion.div\n key={plugin.id || plugin.name}\n layout\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n >\n <Card className=\"h-full flex flex-col\">\n <CardContent className=\"p-5 flex flex-col h-full gap-4\">\n <div className=\"flex justify-between items-start\">\n <div className=\"flex gap-3 items-center\">\n <div className={cn('w-8 h-8 flex items-center justify-center border', dark ? 'bg-app border-z-border' : 'bg-z-input border-z-border')}>\n {plugin.icon}\n </div>\n <div>\n <h3 className=\"text-sm font-semibold\">{plugin.name}</h3>\n <p className=\"text-sm text-z-secondary font-bold\">\n {plugin.author === 'ROOT_KERNEL' ? 'Zenith Core' : plugin.author} • v{plugin.version}\n </p>\n </div>\n </div>\n </div>\n\n {/* Minimalist features instead of descriptive text */}\n <div className=\"flex-1\">\n <p className=\"text-sm text-z-secondary leading-relaxed line-clamp-2\">\n {plugin.description}\n </p>\n </div>\n\n <div className=\"flex items-center justify-between pt-4 border-t border-z-border\">\n <div className=\"flex items-center gap-3 text-sm text-z-secondary font-bold\">\n {plugin.verified && <span className=\"flex items-center gap-1\"><ShieldCheck size={10} /> Verified</span>}\n <span>{plugin.downloads?.toLocaleString()} DLs</span>\n </div>\n\n <div className=\"flex gap-2\">\n {activeTab === 'installed' ? (\n <>\n <button\n onClick={() => togglePlugin(plugin.id, plugin.status)}\n className={cn('px-3 py-1.5 border text-sm font-semibold transition-colors', plugin.status === 'active' ? 'text-red-500 border-red-500/20 hover:bg-red-500/10' : 'text-z-secondary border-z-border/20 hover:bg-z-panel')}\n >\n {plugin.status === 'active' ? 'Disable' : 'Enable'}\n </button>\n {plugin.author !== 'ROOT_KERNEL' && (\n <button onClick={() => toast.error('Core Protection: Delete via CLI')} className=\"p-1.5 text-red-500/70 hover:bg-red-500/10\">\n <Trash2 size={12} />\n </button>\n )}\n </>\n ) : (\n <button\n disabled={installed || installingId !== null}\n onClick={() => installMarketplacePlugin(plugin as any)}\n className={cn('px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2', installed ? 'text-z-secondary border-z-border/20 cursor-not-allowed' : 'text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm')}\n >\n {installingId === plugin.id ? <Loader2 size={10} className=\"animate-spin\" /> : <DownloadCloud size={10} />}\n {installed ? 'Installed' : 'Install'}\n </button>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n )\n })}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {/* Plugin Install Guide Modal */}\n {installGuidePlugin && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4\">\n <div className={cn('max-w-2xl w-full border shadow-2xl flex flex-col', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <div className=\"flex justify-between items-center p-5 border-b border-z-border\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 border border-z-border bg-z-input flex items-center justify-center\">\n {installGuidePlugin.icon}\n </div>\n <div>\n <h3 className=\"text-lg font-bold\">{installGuidePlugin.name}</h3>\n <p className=\"text-xs text-z-secondary font-semibold\">Official Installation Guide</p>\n </div>\n </div>\n <button onClick={() => setInstallGuidePlugin(null)} className=\"p-2 text-z-secondary hover:text-z-primary transition-colors\">\n <X size={20} />\n </button>\n </div>\n \n <div className=\"p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar\">\n <p className=\"text-sm text-z-secondary leading-relaxed\">\n To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.\n </p>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">1</span> \n Install the NPM Package\n </h4>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n pnpm add @zenith-open/{installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}\n </pre>\n <button \n onClick={() => copyToClipboard(`pnpm add @zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">2</span> \n Enable in your config\n </h4>\n <p className=\"text-xs text-z-secondary\">Open <code className=\"bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border\">cms.config.ts</code> and add the plugin to the array.</p>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n{`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\n\nexport default buildConfig({\n collections: [...],\n plugins: [\n zenithPlugin({\n // Provide any necessary config options here\n })\n ]\n})`}\n </pre>\n <button \n onClick={() => copyToClipboard(`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\\n\\nexport default buildConfig({\\n collections: [...],\\n plugins: [\\n zenithPlugin({\\n // Provide any necessary config options here\\n })\\n ]\\n})`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">3</span> \n Restart the Server\n </h4>\n <p className=\"text-xs text-z-secondary\">Restart your development server and the plugin will be instantly active!</p>\n </div>\n\n </div>\n <div className=\"p-4 border-t border-z-border bg-z-input flex justify-end\">\n <button \n onClick={() => setInstallGuidePlugin(null)}\n className=\"px-6 py-2 bg-z-panel text-z-primary border border-z-border text-sm font-bold shadow-sm hover:bg-z-input transition-colors\"\n >\n Done\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default PluginsPage\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{C as t,En as n,H as r,Ht as i,Ln as a,Q as o,_n as s,ir as c,l,rr as u,rt as d,vn as f,vr as p,xr as m}from"./vendor-react-DQVTOTFO.js";import{a as h,o as g,t as _}from"./utils-fgvbH6CB.js";import{d as v,f as y,p as b}from"./index-ChcKY5Xe.js";var x=e(m(),1),S=p(),C=[`301`,`302`,`307`,`308`],w=()=>{let{theme:e}=h(),p=e===`dark`,[m,w]=(0,x.useState)([]),[T,E]=(0,x.useState)(!0),[D,O]=(0,x.useState)(``),[k,A]=(0,x.useState)(1),[j,M]=(0,x.useState)(1),[ee,N]=(0,x.useState)(0),[P,F]=(0,x.useState)(!1),[I,L]=(0,x.useState)(null),[R,z]=(0,x.useState)(null),[B,V]=(0,x.useState)(``),[H,U]=(0,x.useState)(``),[W,G]=(0,x.useState)(`301`),[K,q]=(0,x.useState)(!1),J=(0,x.useCallback)(async()=>{E(!0);try{let e=(await g.get(`/redirects`,{params:{page:k,limit:20,search:D||void 0}})).data;w(e.data||[]),M(e.meta?.pagination?.totalPages||1),N(e.meta?.pagination?.total||0)}catch{l.error(`Failed to load redirects`)}finally{E(!1)}},[k,D]);(0,x.useEffect)(()=>{J()},[J]);let Y=()=>{L(null),V(``),U(``),G(`301`),F(!0)},X=e=>{L(e),V(e.from),U(e.to),G(e.type),F(!0)},Z=async()=>{if(!B||!H){l.error(`Both "From" and "To" paths are required`);return}q(!0);try{I?(await g.patch(`/redirects/${I._id}`,{from:B,to:H,type:W}),l.success(`Redirect updated`)):(await g.post(`/redirects`,{from:B,to:H,type:W}),l.success(`Redirect created`)),F(!1),J()}catch(e){let t=e.response?.data?.error?.message||(e instanceof Error?e.message:String(e))||`Failed to save redirect`;l.error(t)}finally{q(!1)}},Q=async e=>{try{await g.delete(`/redirects/${e}`),l.success(`Redirect deleted`),z(null),J()}catch{l.error(`Failed to delete redirect`)}},$=e=>{switch(e){case`301`:return`text-amber-500 border-amber-500/30`;case`302`:return`text-z-active-text border-z-active-border`;case`307`:return`text-z-secondary border-z-border/30`;case`308`:return`text-z-secondary border-z-border/30`;default:return`text-z-secondary border-z-border/30`}};return(0,S.jsxs)(`div`,{className:`flex flex-col h-[calc(100vh-64px)] overflow-hidden`,children:[(0,S.jsx)(b,{title:`URL Redirects`,description:`Manage 301 and 302 traffic routing.`,icon:(0,S.jsx)(f,{size:24}),backLink:{to:`/`,label:`Dashboard`},actions:(0,S.jsxs)(`button`,{onClick:Y,className:`flex items-center gap-2 px-6 py-2.5 bg-z-accent hover:opacity-90 shadow-sm text-z-primary text-sm font-semibold transition-all rounded-none`,children:[(0,S.jsx)(o,{size:14}),`Add Redirect`]})}),(0,S.jsxs)(`div`,{className:`flex-1 overflow-auto p-6 md:p-8 space-y-6`,children:[(0,S.jsxs)(`div`,{className:`max-w-md relative`,children:[(0,S.jsx)(r,{size:14,className:_(`absolute left-4 top-1/2 -translate-y-1/2`,p?`text-z-secondary`:`text-z-muted`)}),(0,S.jsx)(`input`,{type:`text`,value:D,onChange:e=>{O(e.target.value),A(1)},placeholder:`Search redirect paths...`,className:_(`w-full pl-10 pr-4 py-2.5 text-sm font-semibold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none shadow-sm`,`z-input`)})]}),(0,S.jsxs)(v,{children:[T?(0,S.jsx)(`div`,{className:`flex items-center justify-center py-20 gap-3`,children:(0,S.jsx)(a,{size:20,className:`animate-spin text-z-secondary`})}):m.length===0?(0,S.jsxs)(`div`,{className:`flex flex-col items-center justify-center py-20 gap-4 opacity-50`,children:[(0,S.jsx)(f,{size:32,className:`text-z-secondary`}),(0,S.jsx)(`p`,{className:`text-sm font-semibold text-z-secondary`,children:D?`No matching redirects`:`No redirects configured`})]}):(0,S.jsx)(`div`,{className:`overflow-x-auto min-w-full`,children:(0,S.jsxs)(`table`,{className:`w-full text-left border-collapse`,children:[(0,S.jsx)(`thead`,{children:(0,S.jsxs)(`tr`,{className:_(`text-sm font-semibold border-b`,`text-z-secondary border-z-border`),children:[(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal`,children:`From`}),(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal hidden md:table-cell`,children:`To`}),(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal w-20`,children:`Type`}),(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal w-24 hidden sm:table-cell`,children:`Hits`}),(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal w-24 hidden lg:table-cell`,children:`Last Hit`}),(0,S.jsx)(`th`,{className:`px-5 py-4 font-normal w-24`})]})}),(0,S.jsx)(`tbody`,{children:m.map(e=>(0,S.jsxs)(`tr`,{onClick:()=>X(e),className:_(`text-xs border-b transition-colors cursor-pointer`,p?`border-z-border hover:bg-z-panel`:`border-z-border hover:bg-[var(--z-bg-input)]`),children:[(0,S.jsx)(`td`,{className:`px-5 py-4`,children:(0,S.jsx)(`code`,{className:_(`px-2 py-1 text-xs font-mono font-bold`,p?`bg-z-hover text-z-muted`:`bg-[var(--z-bg-hover)] text-z-secondary`),children:e.from})}),(0,S.jsx)(`td`,{className:`px-5 py-4 hidden md:table-cell`,children:(0,S.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,S.jsx)(s,{size:12,className:`text-z-secondary shrink-0`}),(0,S.jsx)(`span`,{className:_(`font-mono font-bold truncate max-w-[300px]`,`text-z-secondary`),children:e.to})]})}),(0,S.jsx)(`td`,{className:`px-5 py-4`,children:(0,S.jsx)(`span`,{className:_(`inline-block px-2 py-0.5 text-sm font-semibold font-mono border`,$(e.type)),children:e.type})}),(0,S.jsx)(`td`,{className:`px-5 py-4 hidden sm:table-cell`,children:(0,S.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,S.jsx)(d,{size:11,className:p?`text-z-secondary`:`text-z-muted`}),(0,S.jsx)(`span`,{className:_(`font-bold tabular-nums`,`text-z-secondary`),children:e.hits?.toLocaleString()||0})]})}),(0,S.jsx)(`td`,{className:`px-5 py-4 hidden lg:table-cell`,children:(0,S.jsx)(`span`,{className:_(`text-sm font-mono`,p?`text-z-secondary`:`text-z-muted`),children:e.lastHitAt?new Date(e.lastHitAt).toLocaleString():`—`})}),(0,S.jsx)(`td`,{className:`px-5 py-4`,children:(0,S.jsxs)(`div`,{className:`flex items-center gap-2 justify-end`,children:[(0,S.jsx)(`button`,{onClick:t=>{t.stopPropagation(),window.open(e.from,`_blank`)},className:_(`p-1.5 border transition-all`,`border-z-border text-z-secondary hover:text-z-primary`),title:`Test redirect`,children:(0,S.jsx)(i,{size:12})}),(0,S.jsx)(`button`,{onClick:t=>{t.stopPropagation(),z(e._id)},className:`p-1.5 border rounded-none border-red-500/20 text-red-500 hover:bg-red-500/10 transition-all`,title:`Delete`,children:(0,S.jsx)(t,{size:12})})]})})]},e._id))})]})}),j>1&&(0,S.jsxs)(`div`,{className:_(`flex items-center justify-between px-5 py-4 border-t`,`border-z-border`),children:[(0,S.jsxs)(`span`,{className:_(`text-sm font-bold`,`text-z-secondary`),children:[`Page `,k,` of `,j]}),(0,S.jsxs)(`div`,{className:`flex gap-2`,children:[(0,S.jsx)(`button`,{onClick:()=>A(e=>Math.max(1,e-1)),disabled:k<=1,className:_(`px-4 py-2 text-sm font-semibold border rounded-none transition-all`,`border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30`),children:`Prev`}),(0,S.jsx)(`button`,{onClick:()=>A(e=>Math.min(j,e+1)),disabled:k>=j,className:_(`px-4 py-2 text-sm font-semibold border rounded-none transition-all`,`border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30`),children:`Next`})]})]})]}),(0,S.jsx)(c,{children:P&&(0,S.jsx)(u.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:`fixed inset-0 z-50 flex items-center justify-center bg-[var(--z-bg-modal)] backdrop-blur-sm p-4`,children:(0,S.jsx)(u.div,{initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.95},className:`w-full max-w-md`,children:(0,S.jsxs)(v,{children:[(0,S.jsxs)(`div`,{className:_(`px-6 py-4 border-b flex items-center justify-between`,`border-z-border`),children:[(0,S.jsx)(`h2`,{className:`text-sm font-semibold text-z-secondary`,children:I?`Edit Redirect`:`New Redirect`}),(0,S.jsx)(`button`,{onClick:()=>F(!1),className:`text-z-secondary hover:text-z-primary dark:hover:text-z-primary`,children:(0,S.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,children:(0,S.jsx)(`path`,{d:`M18 6L6 18M6 6l12 12`})})})]}),(0,S.jsxs)(y,{className:`p-6 space-y-4`,children:[(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`label`,{className:`block text-sm font-semibold mb-1.5 text-z-secondary`,children:`From Path`}),(0,S.jsx)(`input`,{type:`text`,value:B,onChange:e=>V(e.target.value),placeholder:`/old-path`,className:_(`w-full px-4 py-2.5 text-xs font-mono font-bold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none`,p?`bg-z-panel backdrop-blur-md border-z-border text-z-primary focus:border-z-active-border`:`bg-z-input border-z-border text-z-primary focus:border-z-accent`)})]}),(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`label`,{className:`block text-sm font-semibold mb-1.5 text-z-secondary`,children:`To URL`}),(0,S.jsx)(`input`,{type:`text`,value:H,onChange:e=>U(e.target.value),placeholder:`https://example.com/new-path`,className:_(`w-full px-4 py-2.5 text-xs font-mono font-bold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none`,p?`bg-z-panel backdrop-blur-md border-z-border text-z-primary focus:border-z-active-border`:`bg-z-input border-z-border text-z-primary focus:border-z-accent`)})]}),(0,S.jsxs)(`div`,{children:[(0,S.jsx)(`label`,{className:`block text-sm font-semibold mb-1.5 text-z-secondary`,children:`Redirect Type`}),(0,S.jsx)(`div`,{className:`flex gap-2`,children:C.map(e=>(0,S.jsx)(`button`,{onClick:()=>G(e),className:_(`flex-1 py-2.5 text-sm font-semibold font-mono border rounded-none transition-all`,W===e?p?`border-z-accent text-z-active-text bg-z-active-bg shadow-sm`:`border-z-accent text-z-accent bg-z-active-bg shadow-sm`:p?`border-z-border text-z-secondary hover:text-z-primary hover:bg-z-hover`:`border-z-border text-z-muted hover:text-z-primary hover:bg-[var(--z-bg-input)]`),children:e},e))})]})]}),(0,S.jsxs)(`div`,{className:_(`px-6 py-4 border-t flex justify-end gap-3`,`border-z-border`),children:[(0,S.jsx)(`button`,{onClick:()=>F(!1),className:_(`px-5 py-2.5 text-sm font-semibold transition-colors rounded-none`,p?`text-z-secondary hover:text-z-primary hover:bg-z-hover`:`text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)]`),children:`Cancel`}),(0,S.jsxs)(`button`,{onClick:Z,disabled:K||!B||!H,className:`px-6 py-2.5 bg-z-accent hover:opacity-90 disabled:opacity-50 text-z-primary text-sm font-semibold transition-all shadow-sm rounded-none flex items-center gap-2`,children:[K&&(0,S.jsx)(a,{size:12,className:`animate-spin`}),I?`Update`:`Create`]})]})]})})})}),(0,S.jsx)(c,{children:R&&(0,S.jsx)(u.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},className:`fixed inset-0 z-50 flex items-center justify-center bg-[var(--z-bg-modal)] backdrop-blur-sm p-4`,children:(0,S.jsx)(u.div,{initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.95},className:`w-full max-w-sm`,children:(0,S.jsx)(v,{children:(0,S.jsxs)(y,{className:`p-8 text-center`,children:[(0,S.jsx)(n,{size:32,className:`mx-auto mb-4 text-red-500`}),(0,S.jsx)(`h3`,{className:`text-sm font-semibold mb-2 text-z-primary`,children:`Delete Redirect?`}),(0,S.jsx)(`p`,{className:`text-sm font-bold text-z-secondary mb-6`,children:`This action cannot be undone.`}),(0,S.jsxs)(`div`,{className:`flex gap-3 justify-center`,children:[(0,S.jsx)(`button`,{onClick:()=>z(null),className:_(`px-5 py-2.5 text-sm font-semibold border rounded-none transition-all`,`border-z-border text-z-secondary hover:text-z-primary`),children:`Cancel`}),(0,S.jsx)(`button`,{onClick:()=>Q(R),className:`px-5 py-2.5 bg-red-600 hover:bg-red-500 text-z-primary text-sm font-semibold transition-all rounded-none shadow-sm`,children:`Delete`})]})]})})})})})]})]})};export{w as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RedirectsPage-D_4jAdaI.js","names":[],"sources":["../../src/pages/RedirectsPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react'\nimport {\n ArrowRightLeft,\n Plus,\n Search,\n Trash2,\n ExternalLink,\n Loader2,\n ArrowRight,\n MousePointerClick,\n AlertTriangle,\n} from 'lucide-react'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport toast from 'react-hot-toast'\nimport api from '../lib/api'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { Card, CardContent } from '../components/ui/Card'\n\ninterface RedirectRule {\n _id: string\n from: string\n to: string\n type: string\n hits: number\n lastHitAt?: string\n createdAt: string\n siteId?: string\n}\n\nconst REDIRECT_TYPES = ['301', '302', '307', '308'] as const\n\nconst RedirectsPage = () => {\n const { theme } = useTheme()\n const dark = theme === 'dark'\n\n const [rules, setRules] = useState<RedirectRule[]>([])\n const [loading, setLoading] = useState(true)\n const [search, setSearch] = useState('')\n const [page, setPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n const [total, setTotal] = useState(0)\n const [showEditor, setShowEditor] = useState(false)\n const [editing, setEditing] = useState<RedirectRule | null>(null)\n const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null)\n\n const [formFrom, setFormFrom] = useState('')\n const [formTo, setFormTo] = useState('')\n const [formType, setFormType] = useState('301')\n const [saving, setSaving] = useState(false)\n\n const fetchRules = useCallback(async () => {\n setLoading(true)\n try {\n const res = await api.get('/redirects', {\n params: { page, limit: 20, search: search || undefined },\n })\n const data = res.data\n setRules(data.data || [])\n setTotalPages(data.meta?.pagination?.totalPages || 1)\n setTotal(data.meta?.pagination?.total || 0)\n } catch {\n toast.error('Failed to load redirects')\n } finally {\n setLoading(false)\n }\n }, [page, search])\n\n useEffect(() => {\n fetchRules()\n }, [fetchRules])\n\n const openCreate = () => {\n setEditing(null)\n setFormFrom('')\n setFormTo('')\n setFormType('301')\n setShowEditor(true)\n }\n\n const openEdit = (rule: RedirectRule) => {\n setEditing(rule)\n setFormFrom(rule.from)\n setFormTo(rule.to)\n setFormType(rule.type)\n setShowEditor(true)\n }\n\n const handleSave = async () => {\n if (!formFrom || !formTo) {\n toast.error('Both \"From\" and \"To\" paths are required')\n return\n }\n setSaving(true)\n try {\n if (editing) {\n await api.patch(`/redirects/${editing._id}`, {\n from: formFrom,\n to: formTo,\n type: formType,\n })\n toast.success('Redirect updated')\n } else {\n await api.post('/redirects', {\n from: formFrom,\n to: formTo,\n type: formType,\n })\n toast.success('Redirect created')\n }\n setShowEditor(false)\n fetchRules()\n } catch (err: any) {\n const msg = err.response?.data?.error?.message || (err instanceof Error ? err.message : String(err)) || 'Failed to save redirect'\n toast.error(msg)\n } finally {\n setSaving(false)\n }\n }\n\n const handleDelete = async (id: string) => {\n try {\n await api.delete(`/redirects/${id}`)\n toast.success('Redirect deleted')\n setDeleteConfirm(null)\n fetchRules()\n } catch {\n toast.error('Failed to delete redirect')\n }\n }\n\n const typeColor = (t: string) => {\n switch (t) {\n case '301': return 'text-amber-500 border-amber-500/30'\n case '302': return 'text-z-active-text border-z-active-border'\n case '307': return 'text-z-secondary border-z-border/30'\n case '308': return 'text-z-secondary border-z-border/30'\n default: return 'text-z-secondary border-z-border/30'\n }\n }\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] overflow-hidden\">\n <PageHeader\n title=\"URL Redirects\"\n description=\"Manage 301 and 302 traffic routing.\"\n icon={<ArrowRightLeft size={24} />}\n backLink={{ to: '/', label: 'Dashboard' }}\n actions={\n <button\n onClick={openCreate}\n className=\"flex items-center gap-2 px-6 py-2.5 bg-z-accent hover:opacity-90 shadow-sm text-z-primary text-sm font-semibold transition-all rounded-none\"\n >\n <Plus size={14} />\n Add Redirect\n </button>\n }\n />\n\n <div className=\"flex-1 overflow-auto p-6 md:p-8 space-y-6\">\n <div className=\"max-w-md relative\">\n <Search size={14} className={cn('absolute left-4 top-1/2 -translate-y-1/2', dark ? 'text-z-secondary' : 'text-z-muted')} />\n <input\n type=\"text\"\n value={search}\n onChange={(e) => { setSearch(e.target.value); setPage(1) }}\n placeholder=\"Search redirect paths...\"\n className={cn(\n 'w-full pl-10 pr-4 py-2.5 text-sm font-semibold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none shadow-sm',\n 'z-input'\n )}\n />\n </div>\n\n <Card>\n {loading ? (\n <div className=\"flex items-center justify-center py-20 gap-3\">\n <Loader2 size={20} className=\"animate-spin text-z-secondary\" />\n </div>\n ) : rules.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-20 gap-4 opacity-50\">\n <ArrowRightLeft size={32} className=\"text-z-secondary\" />\n <p className=\"text-sm font-semibold text-z-secondary\">\n {search ? 'No matching redirects' : 'No redirects configured'}\n </p>\n </div>\n ) : (\n <div className=\"overflow-x-auto min-w-full\">\n <table className=\"w-full text-left border-collapse\">\n <thead>\n <tr className={cn(\n 'text-sm font-semibold border-b',\n 'text-z-secondary border-z-border'\n )}>\n <th className=\"px-5 py-4 font-normal\">From</th>\n <th className=\"px-5 py-4 font-normal hidden md:table-cell\">To</th>\n <th className=\"px-5 py-4 font-normal w-20\">Type</th>\n <th className=\"px-5 py-4 font-normal w-24 hidden sm:table-cell\">Hits</th>\n <th className=\"px-5 py-4 font-normal w-24 hidden lg:table-cell\">Last Hit</th>\n <th className=\"px-5 py-4 font-normal w-24\" />\n </tr>\n </thead>\n <tbody>\n {rules.map((rule) => (\n <tr\n key={rule._id}\n onClick={() => openEdit(rule)}\n className={cn(\n 'text-xs border-b transition-colors cursor-pointer',\n dark ? 'border-z-border hover:bg-z-panel' : 'border-z-border hover:bg-[var(--z-bg-input)]'\n )}\n >\n <td className=\"px-5 py-4\">\n <code className={cn('px-2 py-1 text-xs font-mono font-bold', dark ? 'bg-z-hover text-z-muted' : 'bg-[var(--z-bg-hover)] text-z-secondary')}>\n {rule.from}\n </code>\n </td>\n <td className=\"px-5 py-4 hidden md:table-cell\">\n <div className=\"flex items-center gap-2\">\n <ArrowRight size={12} className=\"text-z-secondary shrink-0\" />\n <span className={cn('font-mono font-bold truncate max-w-[300px]', dark ? 'text-z-secondary' : 'text-z-secondary')}>\n {rule.to}\n </span>\n </div>\n </td>\n <td className=\"px-5 py-4\">\n <span className={cn('inline-block px-2 py-0.5 text-sm font-semibold font-mono border', typeColor(rule.type))}>\n {rule.type}\n </span>\n </td>\n <td className=\"px-5 py-4 hidden sm:table-cell\">\n <div className=\"flex items-center gap-1.5\">\n <MousePointerClick size={11} className={dark ? 'text-z-secondary' : 'text-z-muted'} />\n <span className={cn('font-bold tabular-nums', dark ? 'text-z-secondary' : 'text-z-secondary')}>\n {rule.hits?.toLocaleString() || 0}\n </span>\n </div>\n </td>\n <td className=\"px-5 py-4 hidden lg:table-cell\">\n <span className={cn('text-sm font-mono', dark ? 'text-z-secondary' : 'text-z-muted')}>\n {rule.lastHitAt ? new Date(rule.lastHitAt).toLocaleString() : '—'}\n </span>\n </td>\n <td className=\"px-5 py-4\">\n <div className=\"flex items-center gap-2 justify-end\">\n <button\n onClick={(e) => { e.stopPropagation(); window.open(rule.from, '_blank') }}\n className={cn('p-1.5 border transition-all', dark ? 'border-z-border text-z-secondary hover:text-z-primary' : 'border-z-border text-z-secondary hover:text-z-primary')}\n title=\"Test redirect\"\n >\n <ExternalLink size={12} />\n </button>\n <button\n onClick={(e) => { e.stopPropagation(); setDeleteConfirm(rule._id) }}\n className=\"p-1.5 border rounded-none border-red-500/20 text-red-500 hover:bg-red-500/10 transition-all\"\n title=\"Delete\"\n >\n <Trash2 size={12} />\n </button>\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n\n {totalPages > 1 && (\n <div className={cn('flex items-center justify-between px-5 py-4 border-t', 'border-z-border')}>\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>\n Page {page} of {totalPages}\n </span>\n <div className=\"flex gap-2\">\n <button\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page <= 1}\n className={cn('px-4 py-2 text-sm font-semibold border rounded-none transition-all', dark ? 'border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30' : 'border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30')}\n >\n Prev\n </button>\n <button\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\n disabled={page >= totalPages}\n className={cn('px-4 py-2 text-sm font-semibold border rounded-none transition-all', dark ? 'border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30' : 'border-z-border text-z-secondary hover:text-z-primary disabled:opacity-30')}\n >\n Next\n </button>\n </div>\n </div>\n )}\n </Card>\n\n {/* Create/Edit Modal */}\n <AnimatePresence>\n {showEditor && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-[var(--z-bg-modal)] backdrop-blur-sm p-4\"\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n className=\"w-full max-w-md\"\n >\n <Card>\n <div className={cn('px-6 py-4 border-b flex items-center justify-between', 'border-z-border')}>\n <h2 className=\"text-sm font-semibold text-z-secondary\">\n {editing ? 'Edit Redirect' : 'New Redirect'}\n </h2>\n <button onClick={() => setShowEditor(false)} className=\"text-z-secondary hover:text-z-primary dark:hover:text-z-primary\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\"><path d=\"M18 6L6 18M6 6l12 12\"/></svg>\n </button>\n </div>\n\n <CardContent className=\"p-6 space-y-4\">\n <div>\n <label className=\"block text-sm font-semibold mb-1.5 text-z-secondary\">From Path</label>\n <input\n type=\"text\"\n value={formFrom}\n onChange={(e) => setFormFrom(e.target.value)}\n placeholder=\"/old-path\"\n className={cn('w-full px-4 py-2.5 text-xs font-mono font-bold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none', dark ? 'bg-z-panel backdrop-blur-md border-z-border text-z-primary focus:border-z-active-border' : 'bg-z-input border-z-border text-z-primary focus:border-z-accent')}\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-semibold mb-1.5 text-z-secondary\">To URL</label>\n <input\n type=\"text\"\n value={formTo}\n onChange={(e) => setFormTo(e.target.value)}\n placeholder=\"https://example.com/new-path\"\n className={cn('w-full px-4 py-2.5 text-xs font-mono font-bold border outline-none focus-visible:ring-2 focus-visible:ring-z-active-border transition-colors rounded-none', dark ? 'bg-z-panel backdrop-blur-md border-z-border text-z-primary focus:border-z-active-border' : 'bg-z-input border-z-border text-z-primary focus:border-z-accent')}\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-semibold mb-1.5 text-z-secondary\">Redirect Type</label>\n <div className=\"flex gap-2\">\n {REDIRECT_TYPES.map((t) => (\n <button\n key={t}\n onClick={() => setFormType(t)}\n className={cn(\n 'flex-1 py-2.5 text-sm font-semibold font-mono border rounded-none transition-all',\n formType === t ? (dark ? 'border-z-accent text-z-active-text bg-z-active-bg shadow-sm' : 'border-z-accent text-z-accent bg-z-active-bg shadow-sm') : (dark ? 'border-z-border text-z-secondary hover:text-z-primary hover:bg-z-hover' : 'border-z-border text-z-muted hover:text-z-primary hover:bg-[var(--z-bg-input)]')\n )}\n >\n {t}\n </button>\n ))}\n </div>\n </div>\n </CardContent>\n\n <div className={cn('px-6 py-4 border-t flex justify-end gap-3', 'border-z-border')}>\n <button\n onClick={() => setShowEditor(false)}\n className={cn('px-5 py-2.5 text-sm font-semibold transition-colors rounded-none', dark ? 'text-z-secondary hover:text-z-primary hover:bg-z-hover' : 'text-z-secondary hover:text-z-primary hover:bg-[var(--z-bg-input)]')}\n >\n Cancel\n </button>\n <button\n onClick={handleSave}\n disabled={saving || !formFrom || !formTo}\n className=\"px-6 py-2.5 bg-z-accent hover:opacity-90 disabled:opacity-50 text-z-primary text-sm font-semibold transition-all shadow-sm rounded-none flex items-center gap-2\"\n >\n {saving && <Loader2 size={12} className=\"animate-spin\" />}\n {editing ? 'Update' : 'Create'}\n </button>\n </div>\n </Card>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {/* Delete Confirmation */}\n <AnimatePresence>\n {deleteConfirm && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-[var(--z-bg-modal)] backdrop-blur-sm p-4\"\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n className=\"w-full max-w-sm\"\n >\n <Card>\n <CardContent className=\"p-8 text-center\">\n <AlertTriangle size={32} className=\"mx-auto mb-4 text-red-500\" />\n <h3 className=\"text-sm font-semibold mb-2 text-z-primary\">Delete Redirect?</h3>\n <p className=\"text-sm font-bold text-z-secondary mb-6\">\n This action cannot be undone.\n </p>\n <div className=\"flex gap-3 justify-center\">\n <button\n onClick={() => setDeleteConfirm(null)}\n className={cn('px-5 py-2.5 text-sm font-semibold border rounded-none transition-all', dark ? 'border-z-border text-z-secondary hover:text-z-primary' : 'border-z-border text-z-secondary hover:text-z-primary')}\n >\n Cancel\n </button>\n <button\n onClick={() => handleDelete(deleteConfirm)}\n className=\"px-5 py-2.5 bg-red-600 hover:bg-red-500 text-z-primary text-sm font-semibold transition-all rounded-none shadow-sm\"\n >\n Delete\n </button>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n </div>\n )\n}\n\nexport default RedirectsPage\n"],"mappings":"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"}
|