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 { q as extractCode } from "./utils-CEVb7nN1.js"; 5import { aX as ArrowLeft, eH as WandSparkles, bN as History, b2 as Upload, c4 as RotateCcw, bK as PanelsTopLeft, b0 as FileText, fk as FileCode, bo as Monitor, cf as Plus, fl as FolderSearch, fm as ArrowUpNarrowWide, fn as ArrowDownWideNarrow, cx as List, bn as Globe, e7 as Lock } from "./vendor-D0P4XgNj.js"; 6import "./vendor-i18n-BC7ZMXWQ.js"; 7import "./vendor-ai-gQv1oOjO.js"; 8let DEFAULT_PAGE_CODE = `import React from 'react'; 9import { useTranslation } from 'react-i18next'; 10 11const NewPage = () => { 12 const { t } = useTranslation(); 13 return ( 14 <div className="p-6 max-w-[1200px] mx-auto animate-in fade-in"> 15 <h1 className="text-3xl font-bold text-zinc-900 dark:text-white mb-4">New Page</h1> 16 <p className="text-zinc-600 dark:text-zinc-400">Start building your content here.</p> 17 </div> 18 ); 19}; 20 21export default NewPage;`; 22let ICONS = ["layout", "home", "compass", "user", "clock", "thumbs-up", "bell", "tv", "hash", "list-music", "info", "mail", "star", "heart", "settings"]; 23const 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: [ 24 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-3", children: [ 25 /* @__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(Globe, { size: 24 }) }), 26 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
27 /* @__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 }), 28 module.visibility === "admin" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-purple-100 text-purple-700 flex items-center gap-1", children: [ 29 /* @__PURE__ */ jsxRuntimeExports.jsx(Lock, { size: 8 }), 30 " Admin" 31 ] }) 32 ] }) 33 ] }), 34 /* @__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 }), 35 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 line-clamp-2 mb-4", children: module.description }), 36 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-auto flex items-center justify-between text-xs text-zinc-400", children: [ 37 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono opacity-70", children: module.path.split("/").pop() }), 38 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: module.lastUpdated && new Date(module.lastUpdated).toLocaleDateString() }) 39 ] }) 40] }); 41const 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: [ 42 /* @__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(Globe, { size: 20 }) }), 43 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [ 44 /* @__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 }), 45 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 truncate", children: module.description }) 46 ] }), 47 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 shrink-0", children: [ 48 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
49 /* @__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 }), 50 module.visibility && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider", children: module.visibility }) 51 ] }), 52 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-400 flex flex-col items-end min-w-[80px]", children: [ 53 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: module.lastUpdated ? new Date(module.lastUpdated).toLocaleDateString() : "N/A" }), 54 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] opacity-70", children: [ 55 "by ", 56 module.updatedBy 57 ] }) 58 ] }) 59 ] }) 60] }); 61const SiteModuleStudio = ({ refreshTrigger }) => { 62 const [modules, setModules] = reactExports.useState([]); 63 const [studioConfig, setStudioConfig] = reactExports.useState(null); 64 reactExports.useEffect(() => { 65 fetch("/api/admin/studio/config").then((res) => res.json()).then((data) => { 66 if (data.site) { 67 if (data.site.defaultCode) DEFAULT_PAGE_CODE = data.site.defaultCode; 68 if (data.site.icons) ICONS = data.site.icons; 69 setStudioConfig(data.site); 70 } 71 }).catch(console.error); 72 }, []); 73 const [activeDraft, setActiveDraft] = reactExports.useState(null); 74 const [activeTab, setActiveTab] = reactExports.useState("component"); 75 const [isAiGenerating, setIsAiGenerating] = reactExports.useState(false); 76 const [prompt, setPrompt] = reactExports.useState(""); 77 const [deployStep, setDeployStep] = reactExports.useState(0); 78 const [history, setHistory] = reactExports.useState([]); 79 const [showHistory, setShowHistory] = reactExports.useState(false); 80 const [viewMode, setViewMode] = reactExports.useState("list"); 81 const [sortBy, setSortBy] = reactExports.useState("updated"); 82 const [sortDir, setSortDir] = reactExports.useState("desc"); 83 reactExports.useEffect(() => { 84 loadModules(); 85 }, [refreshTrigger]); 86 reactExports.useEffect(() => { 87 if (activeDraft && !activeDraft.isNew) { 88 loadHistory(activeDraft.id); 89 } 90 }, [activeDraft == null ? void 0 : activeDraft.id]);
91 const loadModules = async () => { 92 const data = await api.getSitePages(); 93 setModules(data); 94 }; 95 const loadHistory = async (id) => { 96 const data = await api.getSitePageHistory(id); 97 setHistory(data); 98 }; 99 const sortedModules = [...modules].sort((a, b) => { 100 let res = 0; 101 if (sortBy === "name") res = a.name.localeCompare(b.name); 102 else if (sortBy === "status") res = a.status.localeCompare(b.status); 103 else res = new Date(a.lastUpdated || 0).getTime() - new Date(b.lastUpdated || 0).getTime(); 104 return sortDir === "asc" ? res : -res; 105 }); 106 const handleSelect = (mod) => { 107 setActiveDraft({ 108 id: mod.id, 109 name: mod.name, 110 description: mod.description, 111 path: mod.path, 112 iconKey: mod.iconKey || "layout", 113 content: mod.content, 114 visibility: mod.visibility || "public", 115 isNew: false 116 }); 117 setShowHistory(false); 118 setActiveTab("component"); 119 }; 120 const handleNew = () => { 121 setActiveDraft({ 122 id: `page-${Date.now()}`, 123 name: "Untitled Page", 124 description: "A new public page.", 125 path: "/pages/Site/Untitled.tsx", 126 iconKey: "layout", 127 content: DEFAULT_PAGE_CODE, 128 visibility: "public", 129 isNew: true 130 }); 131 setHistory([]); 132 setShowHistory(false); 133 setActiveTab("component"); 134 }; 135 const handleScanFiles = async () => { 136 var _a; 137 if (!confirm("Scan file system for unregistered Site Pages? This will add any found files in 'pages/Site' to the database.")) return; 138 try { 139 const files = await api.getFileSystem(); 140 const siteFiles = files.filter((f) => f.path.startsWith("/pages/Site/") && f.path.endsWith(".tsx")); 141 const dbModules = await api.getSitePages(); 142 const existingPaths = new Set(dbModules.map((m) => m.path)); 143 let addedCount = 0; 144 for (const file of siteFiles) { 145 const normalizedPath = file.path.startsWith("/") ? file.path : "/" + file.path; 146 const alreadyExists = Array.from(existingPaths).some( 147 (p) => (p.startsWith("/") ? p : "/" + p) === normalizedPath 148 ); 149 if (!alreadyExists) { 150 const filename = ((_a = normalizedPath.split("/").pop()) == null ? void 0 : _a.replace(".tsx", "")) || "Unknown"; 151 const name = filename.replace(/([A-Z])/g, " $1").trim(); 152 const id = filename.toLowerCase().replace(/\s+/g, "_"); 153 await api.createSitePage( 154 name, 155 normalizedPath, 156 "Automatically imported from file system.", 157 file.content, 158 "System Scanner", 159 "admin" 160 ); 161 addedCount++; 162 } 163 } 164 alert(`Scan complete. Registered ${addedCount} new pages.`); 165 loadModules(); 166 } catch (e) { 167 console.error(e); 168 alert("Scan failed."); 169 } 170 }; 171 const handleAiGenerate = async () => { 172 if (!prompt) return; 173 setIsAiGenerating(true); 174 try { 175 const currentCode = (activeDraft == null ? void 0 : activeDraft.content) || ""; 176 const isUpdate = !(activeDraft == null ? void 0 : activeDraft.isNew) && currentCode.length > 50; 177 const systemPrompt = isUpdate ? `You are an expert React Frontend Developer. UPDATE the following existing page component based on the user request: "${prompt}". 178 Current Code: 179 ${currentCode} 180 181 Technical Constraints: 182 - Use 'lucide-react' for icons. 183 - Use 'react-i18next'. 184 - Use Tailwind CSS. 185 - Return ONLY the full valid updated code.` : `You are an expert React Frontend Developer. 186 Create a high-quality, production-ready React component for a public website page. 187 188 User Requirement: "${prompt}" 189 190 Technical Constraints: 191 - Use functional components with hooks. 192 - Use 'lucide-react' for icons. 193 - Use 'react-i18next' for text. 194 - Use Tailwind CSS for styling (dark mode support). 195 - Return ONLY the raw code string.`; 196 const response = await ai.models.generateContent({ 197 model: getAiModel("gemini-3-pro-preview"), 198 // High reasoning 199 contents: systemPrompt 200 }); 201 const rawText = response.text || ""; 202 const cleanCode = extractCode(rawText); 203 if (activeDraft) {
204 setActiveDraft({ 205 ...activeDraft, 206 name: activeDraft.isNew ? prompt.length > 20 ? prompt.substring(0, 20) + "..." : prompt : activeDraft.name, 207 description: activeDraft.isNew ? `AI Generated page based on: "${prompt}"` : activeDraft.description, 208 content: cleanCode 209 }); 210 } 211 } catch (e) { 212 console.error("Gemini Generation Error:", e); 213 alert("Failed to generate code. Please check your API key configuration."); 214 } finally { 215 setIsAiGenerating(false); 216 setPrompt(""); 217 } 218 }; 219 const handleDeploy = async () => { 220 setDeployStep(2); 221 await new Promise((r) => setTimeout(r, 1500)); 222 if (activeDraft) { 223 if (activeDraft.isNew) { 224 await api.createSitePage(activeDraft.name, activeDraft.path, activeDraft.description, activeDraft.content, "Admin", activeDraft.visibility); 225 } else { 226 await api.updateSitePage(activeDraft.id, { 227 content: activeDraft.content, 228 name: activeDraft.name, 229 description: activeDraft.description, 230 iconKey: activeDraft.iconKey, 231 visibility: activeDraft.visibility 232 }, "Updated via Studio", "Admin"); 233 } 234 } 235 setDeployStep(0);
236 loadModules(); 237 if (activeDraft && !activeDraft.isNew) loadHistory(activeDraft.id); 238 alert("Page Deployed Successfully!"); 239 }; 240 const handleRevert = async (version) => { 241 if (!activeDraft || !confirm(`Revert to version from ${new Date(version.createdAt).toLocaleString()}?`)) return; 242 await api.revertSitePageVersion(activeDraft.id, version.id); 243 const updatedHistory = await api.getSitePageHistory(activeDraft.id); 244 const latestPage = (await api.getSitePages()).find((p) => p.id === activeDraft.id); 245 if (latestPage) { 246 setActiveDraft((prev) => prev ? { 247 ...prev, 248 content: latestPage.content 249 } : null); 250 setHistory(updatedHistory); 251 } 252 }; 253 if (activeDraft) { 254 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full bg-zinc-50 dark:bg-black fixed inset-0 z-[50]", children: [ 255 /* @__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: [
256 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 257 /* @__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 }) }), 258 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 259 /* @__PURE__ */ jsxRuntimeExports.jsx( 260 "input", 261 { 262 className: "font-bold text-zinc-900 dark:text-white bg-transparent outline-none placeholder:text-zinc-400", 263 value: activeDraft.name, 264 onChange: (e) => setActiveDraft({ ...activeDraft, name: e.target.value }), 265 placeholder: "Page Name" 266 } 267 ), 268 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-zinc-500", children: [
269 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: activeDraft.path }), 270 !activeDraft.isNew && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-zinc-100 dark:bg-zinc-800 px-1.5 rounded", children: [ 271 "v", 272 history.length + 1 273 ] }) 274 ] }) 275 ] }) 276 ] }), 277 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 278 /* @__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: [ 279 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 text-indigo-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(WandSparkles, { size: 16, className: isAiGenerating ? "animate-spin" : "" }) }), 280 /* @__PURE__ */ jsxRuntimeExports.jsx( 281 "input", 282 { 283 value: prompt, 284 onChange: (e) => setPrompt(e.target.value), 285 onKeyDown: (e) => e.key === "Enter" && handleAiGenerate(), 286 placeholder: activeDraft.isNew ? "Describe page layout..." : "Describe changes...", 287 className: "bg-transparent border-none outline-none text-xs w-64 text-zinc-900 dark:text-white", 288 disabled: isAiGenerating 289 } 290 ),
291 /* @__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" }) 292 ] }), 293 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-8 w-px bg-zinc-200 dark:bg-zinc-800 mx-2" }), 294 /* @__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 }) }), 295 /* @__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: [
296 /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 16 }), 297 " Deploy" 298 ] }) 299 ] }) 300 ] }), 301 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex overflow-hidden", children: [ 302 /* @__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: [ 303 /* @__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: [ 304 "Version History", 305 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 rounded", children: history.length }) 306 ] }), 307 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto p-2 space-y-2", children: [ 308 history.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-xs text-zinc-500 py-4", children: "No history available." }), 309 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: [
310 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-1", children: [ 311 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-xs text-zinc-900 dark:text-white", children: [ 312 "v", 313 ver.versionNumber 314 ] }), 315 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: new Date(ver.createdAt).toLocaleDateString() }) 316 ] }), 317 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 line-clamp-2 mb-2", children: ver.changeLog }), 318 /* @__PURE__ */ jsxRuntimeExports.jsxs( 319 "button", 320 { 321 onClick: () => handleRevert(ver), 322 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", 323 children: [
324 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 10 }), 325 " Revert to this version" 326 ] 327 } 328 ) 329 ] }, ver.id)) 330 ] }) 331 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 332 /* @__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" }), 333 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 space-y-6 overflow-y-auto", children: [ 334 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 335 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-700 dark:text-zinc-300 mb-2", children: "Icon" }), 336 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-5 gap-2", children: ICONS.map((icon) => /* @__PURE__ */ jsxRuntimeExports.jsx( 337 "button", 338 { 339 onClick: () => setActiveDraft({ ...activeDraft, iconKey: icon }), 340 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"}`, 341 children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 bg-current rounded-full" }) 342 }, 343 icon 344 )) }) 345 ] }),
346 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 347 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-700 dark:text-zinc-300 mb-2", children: "Visibility" }), 348 /* @__PURE__ */ jsxRuntimeExports.jsxs( 349 "select", 350 { 351 value: activeDraft.visibility, 352 onChange: (e) => setActiveDraft({ ...activeDraft, visibility: e.target.value }), 353 className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-2 text-xs outline-none focus:border-indigo-500 text-zinc-900 dark:text-white", 354 children: [ 355 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "public", children: "Public (Everyone)" }), 356 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "private", children: "Private (Hidden)" }),
357 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "admin", children: "Admin Only" }) 358 ] 359 } 360 ) 361 ] }) }) 362 ] }) 363 ] }) }), 364 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col bg-[#1e1e1e]", children: [ 365 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-[#252526] border-b border-[#333]", children: [ 366 /* @__PURE__ */ jsxRuntimeExports.jsxs( 367 "button", 368 { 369 onClick: () => setActiveTab("component"), 370 className: `px-6 py-3 text-xs font-bold flex items-center gap-2 border-r border-[#333] transition-colors ${activeTab === "component" ? "bg-[#1e1e1e] text-white border-t-2 border-t-indigo-500" : "text-zinc-500 hover:text-zinc-300"}`, 371 children: [ 372 /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { size: 14 }), 373 " Page Component (.tsx)" 374 ] 375 } 376 ),
377 /* @__PURE__ */ jsxRuntimeExports.jsxs( 378 "button", 379 { 380 onClick: () => setActiveTab("description"), 381 className: `px-6 py-3 text-xs font-bold flex items-center gap-2 border-r border-[#333] transition-colors ${activeTab === "description" ? "bg-[#1e1e1e] text-white border-t-2 border-t-indigo-500" : "text-zinc-500 hover:text-zinc-300"}`, 382 children: [ 383 /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { size: 14 }), 384 " Documentation (DB)" 385 ] 386 } 387 ) 388 ] }), 389 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 relative", children: activeTab === "component" ? /* @__PURE__ */ jsxRuntimeExports.jsx( 390 "textarea", 391 { 392 value: activeDraft.content, 393 onChange: (e) => setActiveDraft({ 394 ...activeDraft, 395 content: e.target.value 396 }), 397 className: "absolute inset-0 w-full h-full bg-transparent text-zinc-300 font-mono text-sm p-6 resize-none outline-none", 398 spellCheck: false 399 } 400 ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 w-full h-full bg-[#1e1e1e] p-6", children: [
401 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-500 uppercase mb-2", children: "Module Description (Stored in Database)" }), 402 /* @__PURE__ */ jsxRuntimeExports.jsx( 403 "textarea", 404 { 405 value: activeDraft.description, 406 onChange: (e) => setActiveDraft({ ...activeDraft, description: e.target.value }), 407 className: "w-full h-full bg-[#252526] text-zinc-300 p-4 resize-none outline-none border border-[#333] rounded-lg focus:border-indigo-500 transition-colors font-sans text-sm leading-relaxed", 408 placeholder: "Describe the functionality and purpose of this page..." 409 } 410 ) 411 ] }) }) 412 ] }) 413 ] }), 414 deployStep > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fixed inset-0 z-[100] flex items-center justify-center p-4", children: [ 415 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm", onClick: () => setDeployStep(0) }),
416 /* @__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: [ 417 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 mb-6", children: [ 418 /* @__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 }) }), 419 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 420 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Deploy Site Page?" }), 421 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400", children: activeDraft.isNew ? "This will create a new page visible in the Site Navigation." : "This will update the live page and archive the current version." }) 422 ] }) 423 ] }),
424 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 mb-8", children: [ 425 /* @__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: [ 426 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 427 /* @__PURE__ */ jsxRuntimeExports.jsx(FileCode, { size: 18, className: "text-blue-500" }), 428 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-700 dark:text-zinc-300", children: activeDraft.path }) 429 ] }), 430 /* @__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" }) 431 ] }), 432 activeDraft.isNew && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-100 dark:border-blue-900/30 text-xs text-blue-700 dark:text-blue-300 flex items-start gap-2", children: [
433 /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 14, className: "mt-0.5" }), 434 "The page will be automatically registered in the App router and Side Navigation." 435 ] }) 436 ] }), 437 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 justify-end", children: [ 438 /* @__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" }), 439 /* @__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" }) 440 ] }) 441 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-8", children: [ 442 /* @__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" }),
443 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold text-zinc-900 dark:text-white", children: "Deploying..." }), 444 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 mt-2", children: "Compiling assets and updating routing table." }) 445 ] }) }) 446 ] }) 447 ] }); 448 } 449 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 max-w-[1600px] mx-auto h-full flex flex-col animate-in fade-in", children: [ 450 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-8", children: [ 451 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 452 /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-2xl font-bold text-white flex items-center gap-2", children: [ 453 /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { size: 28, className: "text-indigo-500" }), 454 " Site Architect" 455 ] }), 456 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-400 mt-1", children: "Manage public facing pages and landing content." }) 457 ] }),
458 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 459 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleNew, className: "bg-white text-black hover:opacity-90 px-4 py-2 rounded-lg font-bold flex items-center gap-2 transition-all", children: [ 460 /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 18 }), 461 " New Page" 462 ] }), 463 /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: handleScanFiles, className: "bg-indigo-900/30 text-indigo-300 hover:bg-indigo-900/50 px-4 py-2 rounded-lg font-bold flex items-center gap-2 transition-all border border-indigo-800", children: [ 464 /* @__PURE__ */ jsxRuntimeExports.jsx(FolderSearch, { size: 18 }), 465 " Scan Files" 466 ] }) 467 ] }) 468 ] }), 469 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row justify-between items-end sm:items-center mb-6 gap-4", children: [ 470 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-900 border border-zinc-800 rounded-lg p-1", children: [
471 /* @__PURE__ */ jsxRuntimeExports.jsxs( 472 "select", 473 { 474 value: sortBy, 475 onChange: (e) => setSortBy(e.target.value), 476 className: "bg-transparent text-xs font-bold text-zinc-300 outline-none px-2 py-1 cursor-pointer", 477 children: [ 478 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "updated", children: "Last Updated" }), 479 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "name", children: "Name" }), 480 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "status", children: "Status" }) 481 ] 482 } 483 ), 484 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px h-4 bg-zinc-800 mx-1" }), 485 /* @__PURE__ */ jsxRuntimeExports.jsx( 486 "button", 487 { 488 onClick: () => setSortDir((prev) => prev === "asc" ? "desc" : "asc"), 489 className: "p-1 hover:bg-zinc-800 rounded text-zinc-500", 490 children: sortDir === "asc" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpNarrowWide, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowDownWideNarrow, { size: 14 }) 491 } 492 ) 493 ] }) }),
494 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-zinc-800 p-1 rounded-lg border border-zinc-700", children: [ 495 /* @__PURE__ */ jsxRuntimeExports.jsx( 496 "button", 497 { 498 onClick: () => setViewMode("grid"), 499 className: `p-1.5 rounded-md transition-all ${viewMode === "grid" ? "bg-zinc-700 shadow text-indigo-400" : "text-zinc-500 hover:text-zinc-300"}`, 500 children: /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { size: 16 }) 501 } 502 ), 503 /* @__PURE__ */ jsxRuntimeExports.jsx( 504 "button", 505 { 506 onClick: () => setViewMode("list"), 507 className: `p-1.5 rounded-md transition-all ${viewMode === "list" ? "bg-zinc-700 shadow text-indigo-400" : "text-zinc-500 hover:text-zinc-300"}`, 508 children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 16 }) 509 } 510 ) 511 ] }) 512 ] }), 513 /* @__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 overflow-y-auto custom-scrollbar flex-1 min-h-0 pb-20" : "flex flex-col gap-3 overflow-y-auto custom-scrollbar flex-1 min-h-0 pb-20", children: [ 514 sortedModules.map((mod) => viewMode === "grid" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ModuleCard, { module: mod, onClick: () => { 515 handleSelect(mod); 516 } }, mod.id) : /* @__PURE__ */ jsxRuntimeExports.jsx(ModuleListItem, { module: mod, onClick: () => { 517 handleSelect(mod); 518 } }, mod.id)), 519 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: [
520 /* @__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 }) }), 521 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-sm", children: "Create New" }) 522 ] }) 523 ] }) 524 ] }); 525}; 526export { 527 SiteModuleStudio as default 528};
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.