1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-w5lXls15.js"; 3import { a as ai, g as getAiModel } from "./ai-B0Mlroaa.js"; 4import { aX as ArrowLeft, eH as WandSparkles, bN as History, b2 as Upload, c4 as RotateCcw, fi as PanelLeft, b9 as Settings, bK as PanelsTopLeft, fj as Braces, dZ as Server, fk as FileCode, bo as Monitor, cf as Plus, fl as FolderSearch, fm as ArrowUpNarrowWide, fn as ArrowDownWideNarrow, cx as List, ez as CirclePlus, cd as Save, f5 as GripVertical, b5 as Trash2, cM as RefreshCw } from "./vendor-D0P4XgNj.js"; 5import "./vendor-i18n-BC7ZMXWQ.js"; 6import "./vendor-ai-gQv1oOjO.js"; 7let DEFAULT_CODE = { 8 ui: `import React from 'react'; 9 10const NewModule = () => { 11 return <div className="p-6">New Module</div>; 12}; 13 14export default NewModule;`, 15 api: `// Add to lib/api.ts 16 17getNewData: async () => { 18 const res = await fetch('/api/new-module'); 19 return res.json(); 20},`, 21 server: `// Add to server.js 22 23app.get('/api/new-module', async (req, res) => { 24 res.json({ message: 'Hello from Server' }); 25});` 26}; 27let ICONS = ["box", "users", "shield", "activity", "database", "lock", "flag", "star", "music", "settings"]; 28const HIDDEN_SUB_MODULES = [ 29 "RegionManager", 30 "TranslationManager", 31 "AdminSettings", 32 "SocialSettings", 33 "EmailSettings", 34 "AppearanceSettings", 35 "TrustScoreConfig", 36 "CreditManager", 37 "SearchSettings", 38 "BackupManager", 39 "AiSettings" 40]; 41const SNAKE_TO_LEGACY_MAP = { 42 admin_module_studio: "modules", 43 admin_settings: "settings", 44 appearance_settings: "appearance", 45 b_i_dashboard: "dashboard", 46 backup_manager: "backup", 47 credit_manager: "coins", 48 data_migration: "migration", 49 database_manager: "database", 50 dev_agent: "dev_agent", 51 docu_center: "documentation", 52 email_settings: "email_settings", 53 file_system: "files", 54 graphic_manager: "graphics", 55 help_center_manager: "help_content", 56 media_library: "media", 57 region_manager: "region_manager", 58 site_module_studio: "site_modules", 59 social_settings: "social_settings", 60 system_info: "info", 61 system_logs: "logs", 62 system_settings: "system_settings", 63 tag_manager: "tags", 64 ticket_support: "tickets", 65 translation_manager: "languages", 66 trust_safety: "trust", 67 trust_score_config: "trust_score_config", 68 user_management: "users", 69 ai_settings: "ai_settings", 70 worker_manager: "worker_manager", 71 web_scraper: "scraper" 72}; 73const DEFAULT_SYSTEM_MODULES = [ 74 { id: "dashboard", label: "Intelligence Dashboard" }, 75 { id: "documentation", label: "DocuCenter" }, 76 { id: "dev_agent", label: "AI Developer" }, 77 { id: "trust", label: "Trust & Safety" }, 78 { id: "users", label: "User Governance" }, 79 { id: "media", label: "Content Assets" }, 80 { id: "worker_manager", label: "Worker Manager" }, 81 { id: "tags", label: "Tag Dictionary" }, 82 { id: "tickets", label: "Support Desk" }, 83 { id: "help_content", label: "Resource Center" }, 84 { id: "modules", label: "Admin Module Studio" }, 85 { id: "site_modules", label: "Site Architect" }, 86 { id: "menu_manager", label: "Menu Manager" }, 87 { id: "files", label: "File System" }, 88 { id: "graphics", label: "Graphic Manager" }, 89 { id: "database", label: "Schema & Data" }, 90 { id: "info", label: "System Overview" }, 91 { id: "logs", label: "System Logs" }, 92 { id: "system_settings", label: "System Settings" }, 93 { id: "backup", label: "Backup Manager" }, 94 { id: "appearance", label: "Appearance" }, 95 { id: "settings", label: "Admin Roles" }, 96 { id: "languages", label: "Translation Manager" }, 97 { id: "system_manager", label: "System Deployments" }, 98 { id: "ai_settings", label: "AI Configuration" }, 99 { id: "email_settings", label: "Email Configuration" }, 100 { id: "search_settings", label: "Search Engine" }, 101 { id: "social_settings", label: "Social & Sharing" }, 102 { id: "trust_score_config", label: "Trust Score Rules" }, 103 { id: "region_manager", label: "Regional Settings" }, 104 { id: "migration", label: "Data Migration" }, 105 { id: "coins", label: "Usage & Quotas" }, 106 { id: "soundfonts", label: "Soundfont Engine" }, 107 { id: "scraper", label: "Web Scraper & Importer" }, 108 { id: "archive_importer", label: "High-Speed MIDI Archive Importer" }, 109 { id: "cluster_sync", label: "Multi-Region Cluster" }, 110 { id: "wireguard", label: "Wireguard Management" },
111 { id: "monitoring", label: "Infrastructure Analytics" }, 112 { id: "live_monitor", label: "Live Monitor" }, 113 { id: "gsc_settings", label: "Google Search Console" }, 114 { id: "subscription_manager", label: "Supporter Registry" } 115]; 116const DEFAULT_MENU_CONFIG = [ 117 { id: "head-dashboard", type: "heading", label: "Dashboard & Performance" }, 118 { id: "dashboard", type: "module" }, 119 { id: "info", type: "module" }, 120 { id: "logs", type: "module" }, 121 { id: "live_monitor", type: "module" }, 122 { id: "worker_manager", type: "module" }, 123 { id: "system_manager", type: "module" }, 124 { id: "dev_agent", type: "module" }, 125 { id: "sep-dash", type: "separator" }, 126 { id: "head-content", type: "heading", label: "Content & Media" }, 127 { id: "media", type: "module" }, 128 { id: "tags", type: "module" }, 129 { id: "graphics", type: "module" }, 130 { id: "files", type: "module" }, 131 { id: "documentation", type: "module" }, 132 { id: "soundfonts", type: "module" }, 133 { id: "scraper", type: "module" }, 134 { id: "sep-cont", type: "separator" }, 135 { id: "head-user", type: "heading", label: "User Governance" }, 136 { id: "users", type: "module" }, 137 { id: "subscription_manager", type: "module" }, 138 { id: "coins", type: "module" }, 139 { id: "trust", type: "module" }, 140 { id: "tickets", type: "module" }, 141 { id: "help_content", type: "module" }, 142 { id: "sep-user", type: "separator" }, 143 { id: "head-creative", type: "heading", label: "Creative Studio" }, 144 { id: "modules", type: "module" }, 145 { id: "site_modules", type: "module" }, 146 { id: "menu_manager", type: "module" }, 147 { id: "languages", type: "module" }, 148 { id: "region_manager", type: "module" }, 149 { id: "sep-create", type: "separator" }, 150 { id: "head-db", type: "heading", label: "Database Operations" }, 151 { id: "backup", type: "module" }, 152 { id: "database", type: "module" }, 153 { id: "migration", type: "module" }, 154 { id: "sep-db", type: "separator" }, 155 { id: "head-sys", type: "heading", label: "System Settings" }, 156 { id: "system_settings", type: "module" }, 157 { id: "ai_settings", type: "module" }, 158 { id: "appearance", type: "module" }, 159 { id: "email_settings", type: "module" }, 160 { id: "search_settings", type: "module" }, 161 { id: "social_settings", type: "module" }, 162 { id: "trust_score_config", type: "module" }, 163 { id: "settings", type: "module" } 164]; 165const ModuleCard = ({ module, onClick }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick, className: "group relative bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-5 hover:border-indigo-500 hover:shadow-lg transition-all cursor-pointer flex flex-col h-48", children: [ 166 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-3", children: [ 167 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-2 rounded-lg ${module.status === "active" ? "bg-indigo-50 text-indigo-600 dark:bg-indigo-900/20 dark:text-indigo-400" : "bg-zinc-100 text-zinc-500 dark:bg-zinc-800"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 24 }) }), 168 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider ${module.status === "active" ? "bg-emerald-100 text-emerald-700" : "bg-amber-100 text-amber-700"}`, children: module.status }) 169 ] }), 170 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white mb-1 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors", children: module.name }), 171 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 line-clamp-2 mb-4", children: module.description }), 172 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-auto flex items-center justify-between text-xs text-zinc-400", children: [ 173 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 174 "v", 175 1, 176 " ⢠", 177 module.updatedBy 178 ] }), 179 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: new Date(module.lastUpdated).toLocaleDateString() }) 180 ] }) 181] }); 182const ModuleListItem = ({ module, onClick }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick, className: "group flex items-center gap-4 p-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl hover:border-indigo-500 hover:shadow-md transition-all cursor-pointer", children: [
183 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-2 rounded-lg shrink-0 ${module.status === "active" ? "bg-indigo-50 text-indigo-600 dark:bg-indigo-900/20 dark:text-indigo-400" : "bg-zinc-100 text-zinc-500 dark:bg-zinc-800"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 20 }) }), 184 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [ 185 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white text-sm truncate group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors", children: module.name }), 186 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 truncate", children: module.description }) 187 ] }), 188 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 shrink-0", children: [ 189 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider ${module.status === "active" ? "bg-emerald-100 text-emerald-700" : "bg-amber-100 text-amber-700"}`, children: module.status }), 190 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-400 flex flex-col items-end min-w-[80px]", children: [
191 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: new Date(module.lastUpdated).toLocaleDateString() }), 192 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] opacity-70", children: [ 193 "by ", 194 module.updatedBy 195 ] }) 196 ] }) 197 ] }) 198] }); 199const MenuManager = ({ modules }) => { 200 const [menuItems, setMenuItems] = reactExports.useState([]); 201 const [availableItems, setAvailableItems] = reactExports.useState([]); 202 const [isSaving, setIsSaving] = reactExports.useState(false); 203 const [isScanning, setIsScanning] = reactExports.useState(false); 204 const [draggedIdx, setDraggedIdx] = reactExports.useState(null); 205 const [scannedModules, setScannedModules] = reactExports.useState(DEFAULT_SYSTEM_MODULES); 206 reactExports.useEffect(() => { 207 loadMenu(); 208 }, [modules, scannedModules]); 209 const handleScan = async () => { 210 setIsScanning(true); 211 try { 212 const fileData = await api.readDevAgentFile(["web/src/pages/AdminPage.tsx", "src/pages/AdminPage.tsx"]); 213 const content = fileData["web/src/pages/AdminPage.tsx"] || fileData["src/pages/AdminPage.tsx"]; 214 if (content) { 215 const regexNew = /['"]?([a-zA-Z0-9_-]+)['"]?\s*:\s*{\s*id:\s*['"]\1['"],\s*label:\s*['"]([^'"]+)['"]/g; 216 const regexLegacy = /['"]?([a-zA-Z0-9_-]+)['"]?\s*:\s*{\s*filename:\s*['"][^'"]+['"],\s*label:\s*['"]([^'"]+)['"]/g; 217 let match; 218 const foundModules = []; 219 while ((match = regexNew.exec(content)) !== null) { 220 foundModules.push({ id: match[1], label: match[2] }); 221 } 222 while ((match = regexLegacy.exec(content)) !== null) { 223 if (!foundModules.some((m) => m.id === match[1])) { 224 foundModules.push({ id: match[1], label: match[2] }); 225 } 226 } 227 if (foundModules.length > 0) { 228 setScannedModules(foundModules); 229 alert(`Scan complete. Found ${foundModules.length} registered modules.`); 230 } else { 231 alert("Scan complete but no modules found using standard registry pattern."); 232 } 233 } else { 234 alert("Could not read AdminPage.tsx. Ensure server is running."); 235 } 236 } catch (e) { 237 console.error(e); 238 alert("Scan failed."); 239 } finally { 240 setIsScanning(false); 241 } 242 }; 243 const loadMenu = async () => { 244 const setting = await api.getSystemSetting("admin_menu_config"); 245 const settingValue = setting == null ? void 0 : setting.value; 246 const currentConfig = settingValue && settingValue.length > 0 ? settingValue : DEFAULT_MENU_CONFIG; 247 const activeIds = new Set(currentConfig.map((i) => i.id)); 248 const available = []; 249 scannedModules.forEach((sys) => { 250 if (!activeIds.has(sys.id)) { 251 available.push({ id: sys.id, label: sys.label, type: "module", isSystem: true }); 252 } 253 }); 254 modules.forEach((mod) => { 255 const legacyId = SNAKE_TO_LEGACY_MAP[mod.id] || mod.id; 256 if (activeIds.has(mod.id) || activeIds.has(legacyId)) return; 257 if (!available.some((a) => a.id === mod.id || a.id === legacyId)) { 258 available.push({ id: mod.id, label: mod.name, type: "module", isSystem: false }); 259 } 260 }); 261 const hydratedMenu = currentConfig.map((item) => { 262 if (item.type === "separator") return { ...item, label: "--- Separator ---" }; 263 if (item.type === "heading") return { ...item, label: item.label || "New Section" }; 264 const sys = scannedModules.find((s) => s.id === item.id); 265 if (sys) return { ...item, label: sys.label, isSystem: true }; 266 const mod = modules.find((m) => m.id === item.id || SNAKE_TO_LEGACY_MAP[m.id] === item.id); 267 if (mod) return { ...item, label: mod.name, isSystem: false }; 268 return { ...item, label: `Unknown Module (${item.id})`, isMissing: true }; 269 }); 270 setMenuItems(hydratedMenu); 271 setAvailableItems(available); 272 }; 273 const handleSave = async () => { 274 setIsSaving(true); 275 const configToSave = menuItems.map((item) => ({ 276 id: item.id, 277 type: item.type, 278 label: item.type === "heading" ? item.label : void 0 279 })); 280 await api.saveSystemSetting("admin_menu_config", configToSave); 281 setIsSaving(false); 282 alert("Menu Configuration Saved."); 283 }; 284 const addToMenu = (item) => { 285 setMenuItems([...menuItems, item]); 286 setAvailableItems(availableItems.filter((i) => i.id !== item.id)); 287 }; 288 const removeFromMenu = (index) => { 289 const item = menuItems[index]; 290 setMenuItems(menuItems.filter((_, i) => i !== index)); 291 if (item.type === "module") { 292 setAvailableItems([...availableItems, item]); 293 } 294 }; 295 const addSeparator = () => { 296 setMenuItems([...menuItems, { id: `sep-${Date.now()}`, type: "separator", label: "--- Separator ---" }]); 297 }; 298 const addHeading = () => { 299 setMenuItems([...menuItems, { id: `heading-${Date.now()}`, type: "heading", label: "NEW SECTION" }]); 300 }; 301 const handleReset = () => { 302 if (confirm("Reset menu to default layout?")) { 303 const currentConfig = DEFAULT_MENU_CONFIG; 304 const activeIds = new Set(currentConfig.map((i) => i.id)); 305 const available = [];
306 scannedModules.forEach((sys) => { 307 if (!activeIds.has(sys.id)) available.push({ id: sys.id, label: sys.label, type: "module", isSystem: true }); 308 }); 309 modules.forEach((mod) => { 310 if (!activeIds.has(mod.id)) available.push({ id: mod.id, label: mod.name, type: "module", isSystem: false }); 311 }); 312 const hydratedMenu = currentConfig.map((item) => { 313 if (item.type === "separator") return { ...item, label: "--- Separator ---" }; 314 if (item.type === "heading") return { ...item, label: item.label || "New Section" }; 315 const sys = scannedModules.find((s) => s.id === item.id); 316 if (sys) return { ...item, label: sys.label, isSystem: true }; 317 const mod = modules.find((m) => m.id === item.id); 318 if (mod) return { ...item, label: mod.name, isSystem: false }; 319 return { ...item, label: "Unknown Module (Missing)", isMissing: true }; 320 }); 321 setMenuItems(hydratedMenu); 322 setAvailableItems(available); 323 } 324 }; 325 const onDragStart = (e, index) => { 326 setDraggedIdx(index); 327 e.dataTransfer.effectAllowed = "move"; 328 e.dataTransfer.setData("text/plain", index.toString()); 329 }; 330 const onDragOver = (e, index) => { 331 e.preventDefault(); 332 e.dataTransfer.dropEffect = "move"; 333 }; 334 const onDrop = (e, dropIndex) => { 335 e.preventDefault(); 336 const dragIndexStr = e.dataTransfer.getData("text/plain"); 337 const dragIndex = parseInt(dragIndexStr); 338 const actualDragIdx = !isNaN(dragIndex) ? dragIndex : draggedIdx; 339 if (actualDragIdx === null || actualDragIdx === dropIndex) return; 340 const newItems = [...menuItems]; 341 const [draggedItem] = newItems.splice(actualDragIdx, 1); 342 newItems.splice(dropIndex, 0, draggedItem); 343 setMenuItems(newItems); 344 setDraggedIdx(null); 345 }; 346 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full gap-6", children: [ 347 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden shadow-sm", children: [
348 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 flex justify-between items-center", children: [ 349 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white", children: "Active Sidebar Menu" }), 350 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 351 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleReset, className: "px-3 py-1.5 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded text-xs font-bold text-zinc-700 dark:text-zinc-300", children: "Reset" }), 352 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: addSeparator, className: "px-3 py-1.5 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded text-xs font-bold text-zinc-700 dark:text-zinc-300 flex items-center gap-1", children: [ 353 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlus, { size: 14 }), 354 " Separator" 355 ] }),
356 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: addHeading, className: "px-3 py-1.5 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded text-xs font-bold text-zinc-700 dark:text-zinc-300 flex items-center gap-1", children: [ 357 /* @__PURE__ */ jsxRuntimeExports.jsx(CirclePlus, { size: 14 }), 358 " Section Header" 359 ] }), 360 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleSave, disabled: isSaving, className: "px-4 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded text-xs font-bold flex items-center gap-1 disabled:opacity-50", children: isSaving ? "Saving..." : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 361 /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { size: 14 }), 362 " Save Layout" 363 ] }) }) 364 ] }) 365 ] }), 366 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-1 bg-zinc-100 dark:bg-zinc-950/50", children: [ 367 menuItems.map((item, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 368 "div", 369 { 370 draggable: true, 371 onDragStart: (e) => onDragStart(e, idx), 372 onDragOver: (e) => onDragOver(e), 373 onDrop: (e) => onDrop(e, idx), 374 className: `flex items-center gap-3 p-2 rounded-lg border cursor-move transition-all ${draggedIdx === idx ? "opacity-50 border-dashed border-indigo-500 bg-indigo-50 dark:bg-indigo-900/10" : item.type === "separator" ? "bg-zinc-200/50 dark:bg-zinc-800/50 border-dashed border-zinc-400 dark:border-zinc-600" : item.type === "heading" ? "bg-indigo-50/50 dark:bg-indigo-950/10 border-indigo-200 dark:border-indigo-800 shadow-sm" : "bg-white dark:bg-zinc-900 border-zinc-200 dark:border-zinc-800 shadow-sm hover:border-indigo-300 dark:hover:border-indigo-700"}`, 375 children: [
376 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0.5 text-zinc-400 cursor-grab active:cursor-grabbing", children: /* @__PURE__ */ jsxRuntimeExports.jsx(GripVertical, { size: 16 }) }), 377 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0", children: item.type === "separator" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500 uppercase tracking-widest text-center block w-full", children: "Separator" }) : item.type === "heading" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 378 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold uppercase tracking-wider text-indigo-500 shrink-0", children: "Section:" }), 379 /* @__PURE__ */ jsxRuntimeExports.jsx( 380 "input", 381 { 382 type: "text", 383 value: item.label, 384 onChange: (e) => { 385 const newItems = [...menuItems]; 386 newItems[idx].label = e.target.value; 387 setMenuItems(newItems); 388 }, 389 className: "bg-transparent border-b border-zinc-300 dark:border-zinc-700 focus:border-indigo-500 text-xs font-black uppercase tracking-wider text-zinc-800 dark:text-white outline-none w-full px-1 py-0.5", 390 placeholder: "Enter section name..." 391 } 392 ) 393 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
394 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-800 dark:text-zinc-200", children: item.label }), 395 item.isSystem ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-indigo-100 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 px-1.5 rounded", children: "System" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 px-1.5 rounded", children: "Dynamic" }), 396 item.isMissing && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-red-100 text-red-600 px-1.5 rounded", children: "Missing" }) 397 ] }) }), 398 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => removeFromMenu(idx), className: "p-1.5 text-zinc-400 hover:text-red-500 transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 16 }) }) 399 ] 400 }, 401 item.id 402 )), 403 menuItems.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-8 text-center text-zinc-400 text-sm", children: "Menu is empty. Add items from the list." }) 404 ] }) 405 ] }), 406 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-80 flex flex-col bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden shadow-sm", children: [
407 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 flex justify-between items-center", children: [ 408 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white", children: "Available Items" }), 409 /* @__PURE__ */ jsxRuntimeExports.jsxs( 410 "button", 411 { 412 onClick: handleScan, 413 disabled: isScanning, 414 className: "text-xs bg-indigo-100 hover:bg-indigo-200 text-indigo-700 dark:bg-indigo-900/40 dark:hover:bg-indigo-900/60 dark:text-indigo-300 font-bold px-2 py-1 rounded flex items-center gap-1 transition-colors border border-indigo-200 dark:border-indigo-800 shadow-sm", 415 title: "Scan AdminPage.tsx for new modules", 416 children: [ 417 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 12, className: isScanning ? "animate-spin" : "" }), 418 " ", 419 isScanning ? "Scanning..." : "Scan" 420 ] 421 } 422 ) 423 ] }),
424 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-1", children: [ 425 availableItems.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-3 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg hover:border-indigo-400 dark:hover:border-indigo-600 transition-colors group", children: [ 426 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 427 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300", children: item.label }), 428 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400", children: item.isSystem ? "System Module" : "Custom Module" }) 429 ] }), 430 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => addToMenu(item), className: "p-1.5 bg-zinc-100 dark:bg-zinc-700 hover:bg-indigo-100 dark:hover:bg-indigo-900/30 text-zinc-500 hover:text-indigo-600 dark:hover:text-indigo-400 rounded-full transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 16 }) }) 431 ] }, item.id)), 432 availableItems.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-8 text-center text-zinc-400 text-sm", children: "All items added." }) 433 ] }) 434 ] }) 435 ] }); 436}; 437const AdminModuleStudio = ({ refreshTrigger }) => { 438 const [modules, setModules] = reactExports.useState([]); 439 const [studioConfig, setStudioConfig] = reactExports.useState(null); 440 reactExports.useEffect(() => { 441 fetch("/api/admin/studio/config").then((res) => res.json()).then((data) => { 442 if (data.admin) { 443 if (data.admin.defaultCode) DEFAULT_CODE = data.admin.defaultCode; 444 if (data.admin.icons) ICONS = data.admin.icons; 445 setStudioConfig(data.admin); 446 } 447 }).catch(console.error); 448 }, []); 449 const [activeDraft, setActiveDraft] = reactExports.useState(null); 450 const [activeTab, setActiveTab] = reactExports.useState("ui"); 451 const [isAiGenerating, setIsAiGenerating] = reactExports.useState(false); 452 const [prompt, setPrompt] = reactExports.useState(""); 453 const [deployStep, setDeployStep] = reactExports.useState(0); 454 const [history, setHistory] = reactExports.useState([]); 455 const [showHistory, setShowHistory] = reactExports.useState(false); 456 const [mainViewMode, setMainViewMode] = reactExports.useState("studio"); 457 const [viewMode, setViewMode] = reactExports.useState("list"); 458 const [sortBy, setSortBy] = reactExports.useState("updated"); 459 const [sortDir, setSortDir] = reactExports.useState("desc"); 460 reactExports.useEffect(() => { 461 loadModules(); 462 }, [refreshTrigger]); 463 reactExports.useEffect(() => { 464 if (activeDraft && !activeDraft.isNew) { 465 loadHistory(activeDraft.id); 466 } 467 }, [activeDraft == null ? void 0 : activeDraft.id]);
468 const loadModules = async () => { 469 const data = await api.getAdminModules(); 470 setModules(data); 471 }; 472 const loadHistory = async (id) => { 473 const data = await api.getModuleHistory(id); 474 setHistory(data); 475 }; 476 const sortedModules = [...modules].sort((a, b) => { 477 let res = 0; 478 if (sortBy === "name") res = a.name.localeCompare(b.name); 479 else if (sortBy === "status") res = a.status.localeCompare(b.status); 480 else res = new Date(a.lastUpdated).getTime() - new Date(b.lastUpdated).getTime(); 481 return sortDir === "asc" ? res : -res; 482 }); 483 const handleSelect = (mod) => { 484 setActiveDraft({ 485 id: mod.id, 486 name: mod.name, 487 description: mod.description, 488 path: mod.path, 489 iconKey: mod.iconKey || "box", 490 addToSidebar: true, 491 requiresSuperAdmin: false, 492 codes: { 493 ui: mod.content, 494 api: `// API logic for ${mod.name} would appear here`, 495 server: `// Server logic for ${mod.name} would appear here` 496 }, 497 isNew: false 498 }); 499 setShowHistory(false); 500 }; 501 const handleNew = () => { 502 setActiveDraft({ 503 id: `mod-${Date.now()}`, 504 name: "Untitled Module", 505 description: "A new system module.", 506 path: "/src/pages/AdminModules/Untitled.tsx", 507 iconKey: "box", 508 addToSidebar: true, 509 requiresSuperAdmin: false, 510 codes: { ...DEFAULT_CODE }, 511 isNew: true 512 }); 513 setHistory([]); 514 setShowHistory(false); 515 }; 516 const handleScanFiles = async () => { 517 var _a; 518 if (!confirm("Scan file system for unregistered Admin Modules? This will add any found files in 'pages/AdminModules' to the database.")) return; 519 try { 520 const files = await api.getFileSystem(); 521 const moduleFiles = files.filter( 522 (f) => (f.path.startsWith("/src/pages/AdminModules/") || f.path.startsWith("/web/src/pages/AdminModules/")) && f.path.endsWith(".tsx") && !HIDDEN_SUB_MODULES.some((hidden) => f.path.includes(hidden)) 523 ); 524 const dbModules = await api.getAdminModules(); 525 const existingPaths = new Set(dbModules.map((m) => m.path)); 526 let addedCount = 0; 527 for (const file of moduleFiles) { 528 const normalizedPath = file.path.startsWith("/") ? file.path : "/" + file.path; 529 const alreadyExists = Array.from(existingPaths).some( 530 (p) => (p.startsWith("/") ? p : "/" + p) === normalizedPath 531 ); 532 if (!alreadyExists) { 533 const filename = ((_a = normalizedPath.split("/").pop()) == null ? void 0 : _a.replace(".tsx", "")) || "Unknown"; 534 const name = filename.replace(/([A-Z])/g, " $1").trim(); 535 const id = filename.toLowerCase().replace(/\s+/g, "_"); 536 await api.createAdminModule( 537 name, 538 normalizedPath, 539 "Automatically imported from file system.", 540 file.content, 541 "System Scanner" 542 ); 543 addedCount++; 544 } 545 } 546 alert(`Scan complete. Registered ${addedCount} new modules.`); 547 loadModules(); 548 } catch (e) { 549 console.error(e); 550 alert("Scan failed."); 551 } 552 }; 553 const handleAiGenerate = async () => { 554 if (!prompt) return; 555 setIsAiGenerating(true); 556 try { 557 const currentCode = (activeDraft == null ? void 0 : activeDraft.codes.ui) || ""; 558 const isUpdate = !(activeDraft == null ? void 0 : activeDraft.isNew) && currentCode.length > 50; 559 const uiPrompt = isUpdate ? `You are an expert React Developer. UPDATE the following existing component code based on the user request: "${prompt}". 560 Current Code: 561 ${currentCode} 562 563 Return the FULL updated code. Do not use markdown.` : `You are a Full Stack Engineer. Generate a React Admin Dashboard Component based on: "${prompt}". 564 Requirements: 565 - Use Tailwind CSS for styling (dark mode compatible). 566 - Use Lucide React icons. 567 - Export as default. 568 - Provide ONLY code, no markdown.`; 569 const uiResponse = await ai.models.generateContent({ 570 model: getAiModel("gemini-3-pro-preview"), 571 // High reasoning 572 contents: uiPrompt 573 }); 574 const uiCode = (uiResponse.text || "").replace(/^```(tsx|jsx|javascript)?/, "").replace(/```$/, "").trim(); 575 if (activeDraft) {
576 setActiveDraft({ 577 ...activeDraft, 578 // If it was new, suggest a name based on prompt 579 name: activeDraft.isNew ? prompt.split(" ")[0] + " Manager" : activeDraft.name, 580 description: activeDraft.isNew ? `AI Generated module for: ${prompt}` : activeDraft.description, 581 codes: { 582 ...activeDraft.codes, 583 ui: uiCode 584 } 585 }); 586 } 587 } catch (e) { 588 console.error("AI Gen Error", e); 589 alert("Failed to generate module. Check API Key."); 590 } finally { 591 setIsAiGenerating(false); 592 setPrompt(""); 593 } 594 }; 595 const handleDeploy = async () => { 596 setDeployStep(2); 597 await new Promise((r) => setTimeout(r, 1e3)); 598 if (activeDraft) { 599 if (activeDraft.isNew) { 600 await api.createAdminModule(activeDraft.name, activeDraft.path, activeDraft.description, activeDraft.codes.ui, "Admin (Architect)"); 601 } else { 602 await api.updateAdminModule(activeDraft.id, activeDraft.codes.ui, "Deployed via Studio", "Admin"); 603 } 604 } 605 setDeployStep(0);
606 loadModules(); 607 if (activeDraft && !activeDraft.isNew) loadHistory(activeDraft.id); 608 alert("Module Deployed Successfully!"); 609 }; 610 const handleRevert = async (version) => { 611 if (!activeDraft || !confirm(`Revert to version from ${new Date(version.createdAt).toLocaleString()}?`)) return; 612 await api.revertModuleVersion(activeDraft.id, version.id); 613 const updatedHistory = await api.getModuleHistory(activeDraft.id); 614 const latestModule = (await api.getAdminModules()).find((m) => m.id === activeDraft.id); 615 if (latestModule) { 616 setActiveDraft((prev) => prev ? { 617 ...prev, 618 codes: { ...prev.codes, ui: latestModule.content } 619 } : null); 620 setHistory(updatedHistory); 621 } 622 }; 623 if (activeDraft) { 624 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full bg-zinc-50 dark:bg-black fixed inset-0 z-[50]", children: [ 625 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-16 border-b border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 px-6 flex items-center justify-between shrink-0", children: [
626 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 627 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setActiveDraft(null), className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full transition-colors text-zinc-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 20 }) }), 628 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 629 /* @__PURE__ */ jsxRuntimeExports.jsx( 630 "input", 631 { 632 className: "font-bold text-zinc-900 dark:text-white bg-transparent outline-none placeholder:text-zinc-400", 633 value: activeDraft.name, 634 onChange: (e) => setActiveDraft({ ...activeDraft, name: e.target.value }), 635 placeholder: "Module Name" 636 } 637 ), 638 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-zinc-500", children: [
639 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: activeDraft.path }), 640 !activeDraft.isNew && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-zinc-100 dark:bg-zinc-800 px-1.5 rounded", children: [ 641 "v", 642 history.length + 1 643 ] }) 644 ] }) 645 ] }) 646 ] }), 647 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 648 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden md:flex items-center bg-zinc-100 dark:bg-zinc-800 rounded-lg p-1 border border-zinc-200 dark:border-zinc-700", children: [ 649 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 text-indigo-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(WandSparkles, { size: 16, className: isAiGenerating ? "animate-spin" : "" }) }), 650 /* @__PURE__ */ jsxRuntimeExports.jsx( 651 "input", 652 { 653 value: prompt, 654 onChange: (e) => setPrompt(e.target.value), 655 onKeyDown: (e) => e.key === "Enter" && handleAiGenerate(), 656 placeholder: activeDraft.isNew ? "Describe a new module..." : "Describe changes...", 657 className: "bg-transparent border-none outline-none text-xs w-64 text-zinc-900 dark:text-white", 658 disabled: isAiGenerating 659 } 660 ),
661 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleAiGenerate, disabled: !prompt || isAiGenerating, className: "px-3 py-1 bg-white dark:bg-zinc-700 rounded-md text-xs font-bold shadow-sm hover:text-indigo-500 transition-colors", children: activeDraft.isNew ? "Generate" : "Update" }) 662 ] }), 663 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 w-px bg-zinc-200 dark:bg-zinc-800 mx-2" }), 664 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setShowHistory(!showHistory), className: `p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-500 transition-colors ${showHistory ? "bg-zinc-100 dark:bg-zinc-800 text-indigo-500" : ""}`, title: "Version History", children: /* @__PURE__ */ jsxRuntimeExports.jsx(History, { size: 20 }) }), 665 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => setDeployStep(1), className: "flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-bold bg-indigo-600 hover:bg-indigo-700 text-white transition-colors shadow-lg shadow-indigo-500/20", children: [
666 /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 16 }), 667 " Deploy" 668 ] }) 669 ] }) 670 ] }), 671 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex overflow-hidden", children: [ 672 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-72 bg-white dark:bg-zinc-900 border-r border-zinc-200 dark:border-zinc-800 flex flex-col shrink-0 transition-all", children: showHistory ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full", children: [ 673 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 font-bold text-xs uppercase text-zinc-500 flex justify-between items-center", children: [ 674 "Version History", 675 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 rounded", children: history.length }) 676 ] }), 677 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-2", children: [ 678 history.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-xs text-zinc-500 py-4", children: "No history available." }), 679 history.map((ver) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 rounded-lg border border-zinc-200 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-950 hover:border-indigo-300 dark:hover:border-indigo-700 transition-colors group", children: [
680 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-1", children: [ 681 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-xs text-zinc-900 dark:text-white", children: [ 682 "v", 683 ver.versionNumber 684 ] }), 685 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: new Date(ver.createdAt).toLocaleDateString() }) 686 ] }), 687 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 line-clamp-2 mb-2", children: ver.changeLog }), 688 /* @__PURE__ */ jsxRuntimeExports.jsxs( 689 "button", 690 { 691 onClick: () => handleRevert(ver), 692 className: "w-full py-1 text-[10px] font-bold bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded text-zinc-600 dark:text-zinc-400 hover:text-indigo-500 hover:border-indigo-500 transition-colors flex items-center justify-center gap-1", 693 children: [
694 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 10 }), 695 " Revert to this version" 696 ] 697 } 698 ) 699 ] }, ver.id)) 700 ] }) 701 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 702 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 border-b border-zinc-200 dark:border-zinc-800 font-bold text-xs uppercase text-zinc-500", children: "Configuration" }), 703 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 space-y-6 overflow-y-auto", children: [ 704 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 705 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-700 dark:text-zinc-300 mb-2", children: "Description" }), 706 /* @__PURE__ */ jsxRuntimeExports.jsx( 707 "textarea", 708 { 709 className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-3 text-xs resize-none h-24 focus:border-indigo-500 outline-none text-zinc-900 dark:text-white", 710 value: activeDraft.description, 711 onChange: (e) => setActiveDraft({ ...activeDraft, description: e.target.value }) 712 } 713 ) 714 ] }),
715 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 716 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-700 dark:text-zinc-300 mb-2", children: "Icon" }), 717 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-5 gap-2", children: ICONS.map((icon) => /* @__PURE__ */ jsxRuntimeExports.jsx( 718 "button", 719 { 720 onClick: () => setActiveDraft({ ...activeDraft, iconKey: icon }), 721 className: `aspect-square rounded-lg flex items-center justify-center border transition-all ${activeDraft.iconKey === icon ? "bg-indigo-50 border-indigo-500 text-indigo-600" : "bg-zinc-50 dark:bg-zinc-800 border-transparent text-zinc-400 hover:border-zinc-300"}`, 722 children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 bg-current rounded-full" }) 723 }, 724 icon 725 )) }) 726 ] }), 727 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
728 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-3 p-3 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800 border border-transparent hover:border-zinc-300 dark:hover:border-zinc-700 transition-all", children: [ 729 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: activeDraft.addToSidebar, onChange: (e) => setActiveDraft({ ...activeDraft, addToSidebar: e.target.checked }), className: "rounded border-zinc-300 text-indigo-600 focus:ring-indigo-500" }), 730 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [ 731 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [ 732 /* @__PURE__ */ jsxRuntimeExports.jsx(PanelLeft, { size: 14 }), 733 " Add to Sidebar" 734 ] }), 735 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500", children: "Auto-register in AdminPage" }) 736 ] }) 737 ] }),
738 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-3 p-3 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800 border border-transparent hover:border-zinc-300 dark:hover:border-zinc-700 transition-all", children: [ 739 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: activeDraft.requiresSuperAdmin, onChange: (e) => setActiveDraft({ ...activeDraft, requiresSuperAdmin: e.target.checked }), className: "rounded border-zinc-300 text-indigo-600 focus:ring-indigo-500" }), 740 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [ 741 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [ 742 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 14 }), 743 " Super Admin Only" 744 ] }), 745 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-500", children: "Restrict access level" }) 746 ] }) 747 ] }) 748 ] }) 749 ] }) 750 ] }) }),
751 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col bg-[#1e1e1e]", children: [ 752 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-[#252526] border-b border-[#333]", children: [ 753 /* @__PURE__ */ jsxRuntimeExports.jsxs( 754 "button", 755 { 756 onClick: () => setActiveTab("ui"), 757 className: `px-6 py-3 text-xs font-bold flex items-center gap-2 border-r border-[#333] transition-colors ${activeTab === "ui" ? "bg-[#1e1e1e] text-white border-t-2 border-t-indigo-500" : "text-zinc-500 hover:text-zinc-300"}`, 758 children: [ 759 /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { size: 14 }), 760 " UI Component (.tsx)" 761 ] 762 } 763 ), 764 /* @__PURE__ */ jsxRuntimeExports.jsxs( 765 "button", 766 { 767 onClick: () => setActiveTab("api"), 768 className: `px-6 py-3 text-xs font-bold flex items-center gap-2 border-r border-[#333] transition-colors ${activeTab === "api" ? "bg-[#1e1e1e] text-white border-t-2 border-t-indigo-500" : "text-zinc-500 hover:text-zinc-300"}`, 769 children: [
770 /* @__PURE__ */ jsxRuntimeExports.jsx(Braces, { size: 14 }), 771 " Client API (.ts)" 772 ] 773 } 774 ), 775 /* @__PURE__ */ jsxRuntimeExports.jsxs( 776 "button", 777 { 778 onClick: () => setActiveTab("server"), 779 className: `px-6 py-3 text-xs font-bold flex items-center gap-2 border-r border-[#333] transition-colors ${activeTab === "server" ? "bg-[#1e1e1e] text-white border-t-2 border-t-indigo-500" : "text-zinc-500 hover:text-zinc-300"}`, 780 children: [ 781 /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { size: 14 }), 782 " Server Routes (.js)" 783 ] 784 } 785 ) 786 ] }), 787 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 relative", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 788 "textarea", 789 { 790 value: activeDraft.codes[activeTab], 791 onChange: (e) => setActiveDraft({ 792 ...activeDraft, 793 codes: { ...activeDraft.codes, [activeTab]: e.target.value } 794 }), 795 className: "absolute inset-0 w-full h-full bg-transparent text-zinc-300 font-mono text-sm p-6 resize-none outline-none", 796 spellCheck: false 797 } 798 ) }) 799 ] }) 800 ] }), 801 deployStep > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [
802 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: () => setDeployStep(0) }), 803 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative w-full max-w-2xl bg-white dark:bg-zinc-900 rounded-xl shadow-2xl p-8 border border-zinc-200 dark:border-zinc-800 animate-in zoom-in-95", children: deployStep === 1 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 804 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 mb-6", children: [ 805 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-indigo-100 dark:bg-indigo-900/30 rounded-full text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 24 }) }), 806 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 807 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Deploy Module?" }),
808 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400", children: activeDraft.isNew ? "This will create a new module and register system files." : "This will update the live module and archive the current version." }) 809 ] }) 810 ] }), 811 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3 mb-8", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-3 bg-zinc-50 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700", children: [ 812 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 813 /* @__PURE__ */ jsxRuntimeExports.jsx(FileCode, { size: 18, className: "text-blue-500" }), 814 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: activeDraft.path }) 815 ] }), 816 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs px-2 py-0.5 rounded font-bold ${activeDraft.isNew ? "bg-emerald-100 text-emerald-700" : "bg-amber-100 text-amber-700"}`, children: activeDraft.isNew ? "CREATE" : "UPDATE" }) 817 ] }) }),
818 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 justify-end", children: [ 819 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setDeployStep(0), className: "px-4 py-2 text-zinc-500 hover:text-zinc-900 dark:hover:text-white font-bold", children: "Cancel" }), 820 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleDeploy, className: "px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold shadow-lg shadow-indigo-500/20", children: "Confirm Deployment" }) 821 ] }) 822 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-8", children: [ 823 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" }), 824 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Deploying..." }), 825 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 mt-2", children: "Compiling assets and updating database." }) 826 ] }) }) 827 ] }) 828 ] }); 829 } 830 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 max-w-[1600px] mx-auto h-full flex flex-col animate-in fade-in", children: [ 831 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
832 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 833 /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-2xl font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [ 834 /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 28, className: "text-indigo-500" }), 835 " Admin Module Studio" 836 ] }), 837 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 mt-1", children: "Full-stack module generator and menu manager." }) 838 ] }), 839 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-zinc-100 dark:bg-zinc-800 p-1 rounded-lg border border-zinc-200 dark:border-zinc-700", children: [ 840 /* @__PURE__ */ jsxRuntimeExports.jsx( 841 "button", 842 { 843 onClick: () => setMainViewMode("studio"), 844 className: `px-4 py-2 rounded-md text-sm font-bold transition-all ${mainViewMode === "studio" ? "bg-white dark:bg-zinc-700 shadow text-indigo-600 dark:text-indigo-400" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-white"}`, 845 children: "Module Editor" 846 } 847 ),
848 /* @__PURE__ */ jsxRuntimeExports.jsx( 849 "button", 850 { 851 onClick: () => setMainViewMode("menu"), 852 className: `px-4 py-2 rounded-md text-sm font-bold transition-all ${mainViewMode === "menu" ? "bg-white dark:bg-zinc-700 shadow text-indigo-600 dark:text-indigo-400" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-white"}`, 853 children: "Menu Manager" 854 } 855 ) 856 ] }) 857 ] }), 858 mainViewMode === "menu" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MenuManager, { modules }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 859 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row justify-between items-end sm:items-center mb-6 gap-4", children: [ 860 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 861 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleNew, className: "bg-zinc-900 dark:bg-white text-white dark:text-black hover:opacity-90 px-4 py-2 rounded-lg font-bold flex items-center gap-2 transition-all", children: [
862 /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 18 }), 863 " New Module" 864 ] }), 865 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleScanFiles, className: "bg-indigo-100 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 hover:bg-indigo-200 dark:hover:bg-indigo-900/50 px-4 py-2 rounded-lg font-bold flex items-center gap-2 transition-all border border-indigo-200 dark:border-indigo-800", children: [ 866 /* @__PURE__ */ jsxRuntimeExports.jsx(FolderSearch, { size: 18 }), 867 " Scan Files" 868 ] }), 869 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 w-px bg-zinc-200 dark:bg-zinc-800 mx-2" }), 870 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-1", children: [ 871 /* @__PURE__ */ jsxRuntimeExports.jsxs( 872 "select", 873 { 874 value: sortBy, 875 onChange: (e) => setSortBy(e.target.value), 876 className: "bg-transparent text-xs font-bold text-zinc-700 dark:text-zinc-300 outline-none px-2 py-1 cursor-pointer", 877 children: [
878 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "updated", children: "Last Updated" }), 879 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "name", children: "Name" }), 880 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "status", children: "Status" }) 881 ] 882 } 883 ), 884 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px h-4 bg-zinc-200 dark:bg-zinc-800 mx-1" }), 885 /* @__PURE__ */ jsxRuntimeExports.jsx( 886 "button", 887 { 888 onClick: () => setSortDir((prev) => prev === "asc" ? "desc" : "asc"), 889 className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded text-zinc-500", 890 children: sortDir === "asc" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpNarrowWide, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDownWideNarrow, { size: 14 }) 891 } 892 ) 893 ] }) 894 ] }), 895 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-zinc-100 dark:bg-zinc-800 p-1 rounded-lg border border-zinc-200 dark:border-zinc-700", children: [
896 /* @__PURE__ */ jsxRuntimeExports.jsx( 897 "button", 898 { 899 onClick: () => setViewMode("grid"), 900 className: `p-1.5 rounded-md transition-all ${viewMode === "grid" ? "bg-white dark:bg-zinc-700 shadow text-indigo-600 dark:text-indigo-400" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-300"}`, 901 children: /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { size: 16 }) 902 } 903 ), 904 /* @__PURE__ */ jsxRuntimeExports.jsx( 905 "button", 906 { 907 onClick: () => setViewMode("list"), 908 className: `p-1.5 rounded-md transition-all ${viewMode === "list" ? "bg-white dark:bg-zinc-700 shadow text-indigo-600 dark:text-indigo-400" : "text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-300"}`, 909 children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 16 }) 910 } 911 ) 912 ] }) 913 ] }), 914 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: viewMode === "grid" ? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 pb-20 overflow-y-auto" : "flex flex-col gap-3 pb-20 overflow-y-auto", children: [ 915 sortedModules.map((mod) => viewMode === "grid" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ModuleCard, { module: mod, onClick: () => handleSelect(mod) }, mod.id) : /* @__PURE__ */ jsxRuntimeExports.jsx(ModuleListItem, { module: mod, onClick: () => handleSelect(mod) }, mod.id)), 916 viewMode === "grid" && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleNew, className: "border-2 border-dashed border-zinc-300 dark:border-zinc-800 rounded-xl p-5 flex flex-col items-center justify-center text-zinc-400 hover:border-indigo-500 hover:text-indigo-500 transition-all h-48 group bg-zinc-50/50 dark:bg-zinc-900/50", children: [
917 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-full bg-zinc-100 dark:bg-zinc-800 group-hover:bg-indigo-50 dark:group-hover:bg-indigo-900/20 flex items-center justify-center mb-2 transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 24 }) }), 918 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-sm", children: "Create New" }) 919 ] }) 920 ] }) 921 ] }) 922 ] }); 923}; 924export { 925 AdminModuleStudio as default 926};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.