Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Syncopated Dashboard</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" /> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| rustic: { | |
| 900: '#1c1917', | |
| 800: '#292524', | |
| 700: '#44403c', | |
| 600: '#57534e', | |
| 500: '#78716c', | |
| }, | |
| rust: { | |
| DEFAULT: '#c2410c', | |
| light: '#ea580c', | |
| dim: 'rgba(194, 65, 12, 0.2)', | |
| } | |
| }, | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap'); | |
| body { | |
| background-color: #1c1917; | |
| color: #e7e5e4; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #1c1917; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #44403c; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #c2410c; | |
| } | |
| /* Chatbot Popup Animations */ | |
| .chatbot-toggle { | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .chatbot-panel { | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| transform-origin: bottom right; | |
| } | |
| .chatbot-panel.closed { | |
| opacity: 0; | |
| transform: scale(0.95) translateY(20px); | |
| pointer-events: none; | |
| } | |
| /* Link Cards Hover Effects */ | |
| .link-card { | |
| transition: all 0.2s ease; | |
| border-left: 3px solid transparent; | |
| } | |
| .link-card:hover { | |
| background-color: #292524; | |
| border-left: 3px solid #c2410c; | |
| transform: translateX(4px); | |
| } | |
| input:focus, | |
| select:focus { | |
| outline: none; | |
| border-color: #c2410c; | |
| box-shadow: 0 0 0 2px rgba(194, 65, 12, 0.2); | |
| } | |
| .nav-item.active { | |
| background-color: rgba(194, 65, 12, 0.1); | |
| color: #c2410c; | |
| border-right: 3px solid #c2410c; | |
| } | |
| .iframe-container { | |
| background: #000; | |
| border-radius: 0.5rem; | |
| overflow: hidden; | |
| } | |
| /* Dashboard Split Pane */ | |
| .dashboard-split { | |
| display: flex; | |
| flex-direction: column; | |
| height: calc(100vh - 4rem - 3rem); | |
| gap: 1rem; | |
| } | |
| .top-pane { | |
| flex: 1; | |
| min-height: 0; | |
| overflow: hidden; | |
| } | |
| .bottom-pane { | |
| flex: 0 0 280px; | |
| min-height: 280px; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* Shortcuts Grid */ | |
| .shortcuts-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); | |
| gap: 0.75rem; | |
| } | |
| .shortcut-item { | |
| background: #1c1917; | |
| border: 1px solid #44403c; | |
| border-radius: 0.5rem; | |
| padding: 1rem; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 0.5rem; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| min-height: 90px; | |
| } | |
| .shortcut-item:hover { | |
| background: #292524; | |
| border-color: #c2410c; | |
| transform: translateY(-2px); | |
| } | |
| .shortcut-item i { | |
| font-size: 1.5rem; | |
| } | |
| .shortcut-item span { | |
| font-size: 0.75rem; | |
| text-align: center; | |
| color: #a8a29e; | |
| } | |
| /* Pane resize handle */ | |
| .resize-handle { | |
| height: 6px; | |
| background: #44403c; | |
| border-radius: 3px; | |
| cursor: ns-resize; | |
| margin: 0.25rem 0; | |
| position: relative; | |
| } | |
| .resize-handle:hover { | |
| background: #c2410c; | |
| } | |
| /* Pane header */ | |
| .pane-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 0.5rem 0; | |
| margin-bottom: 0.5rem; | |
| } | |
| .pane-title { | |
| font-size: 0.875rem; | |
| font-weight: 600; | |
| color: #d6d3d1; | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| } | |
| /* Modal backdrop */ | |
| .modal-backdrop { | |
| background: rgba(0, 0, 0, 0.7); | |
| backdrop-filter: blur(4px); | |
| } | |
| /* Quick action buttons */ | |
| .quick-action-btn { | |
| transition: all 0.2s ease; | |
| } | |
| .quick-action-btn:hover { | |
| background: #c2410c; | |
| transform: scale(1.05); | |
| } | |
| /* Activity feed styling */ | |
| .activity-item { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 0.75rem; | |
| padding: 0.5rem 0; | |
| border-bottom: 1px solid #44403c; | |
| } | |
| .activity-item:last-child { | |
| border-bottom: none; | |
| } | |
| .activity-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| margin-top: 6px; | |
| } | |
| </style> | |
| </head> | |
| <body class="h-screen flex flex-col overflow-hidden"> | |
| <!-- Header --> | |
| <header class="h-16 bg-rustic-800 border-b border-rustic-700 flex items-center justify-between px-6 shrink-0 z-20"> | |
| <div class="flex items-center space-x-4"> | |
| <div class="w-8 h-8 rounded bg-rust flex items-center justify-center shadow-lg shadow-rust/20"> | |
| <i class="fas fa-cube text-white text-sm"></i> | |
| </div> | |
| <h1 class="text-xl font-semibold tracking-wide text-gray-100">SYNCOPATED</h1> | |
| </div> | |
| <div class="flex-1 max-w-2xl mx-8 hidden md:block"> | |
| <div class="flex space-x-2"> | |
| <form id="meta-search-form" class="relative flex-1"> | |
| <input | |
| type="text" | |
| id="search-input" | |
| name="q" | |
| placeholder="Search Global..." | |
| class="w-full bg-rustic-900 border border-rustic-700 rounded-md py-1.5 px-4 pl-9 text-sm text-gray-300 focus:outline-none focus:border-rust transition" | |
| /> | |
| <button type="submit" class="absolute left-3 top-2 text-rustic-500"> | |
| <i class="fas fa-globe text-xs"></i> | |
| </button> | |
| </form> | |
| <form id="rubygems-search-form" class="relative w-64 hidden lg:block"> | |
| <input | |
| type="text" | |
| id="rubygems-search-input" | |
| name="q" | |
| placeholder="RubyGems..." | |
| class="w-full bg-rustic-900 border border-rustic-700 rounded-md py-1.5 px-4 pl-9 text-sm text-gray-300 focus:outline-none focus:border-rust transition" | |
| /> | |
| <button type="submit" class="absolute left-3 top-2 text-rustic-500"> | |
| <i class="fas fa-gem text-xs text-rust-light"></i> | |
| </button> | |
| </form> | |
| </div> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-xs text-rustic-500 hover:text-rust transition">Built with anycoder</a> | |
| <button class="text-gray-400 hover:text-rust-light transition"> | |
| <i class="fas fa-bell"></i> | |
| </button> | |
| <div | |
| class="w-8 h-8 rounded-full bg-rustic-700 flex items-center justify-center cursor-pointer border border-rustic-600"> | |
| <i class="fas fa-user text-gray-400 text-xs"></i> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Layout --> | |
| <div class="flex flex-1 overflow-hidden"> | |
| <!-- Left Column: Link Modals (Prominently Featured) --> | |
| <aside class="w-80 bg-rustic-900 border-r border-rustic-700 flex flex-col overflow-y-auto shrink-0 hidden md:flex"> | |
| <div class="p-4"> | |
| <h2 class="text-xs font-bold text-rustic-500 uppercase tracking-wider mb-4 pl-2">Launcher</h2> | |
| <!-- Dashboard View (Link Categories) --> | |
| <div id="dashboard-links-view" class=""> | |
| <!-- Linksspace-y-6 will be injected here via JS --> | |
| </div> | |
| </div> | |
| <!-- Secondary Nav for other tabs --> | |
| <div class="mt-auto p-4 border-t border-rustic-700"> | |
| <nav class="space-y-1"> | |
| <button class="nav-item w-full text-left px-3 py-2 rounded-md text-sm font-medium flex items-center space-x-3 text-gray-400 hover:bg-rustic-800" data-tab="documents"> | |
| <i class="fas fa-file-alt w-4"></i> | |
| <span>Documents</span> | |
| </button> | |
| <button class="nav-item w-full text-left px-3 py-2 rounded-md text-sm font-medium flex items-center space-x-3 text-gray-400 hover:bg-rustic-800" data-tab="generate"> | |
| <i class="fas fa-code w-4"></i> | |
| <span>Generate</span> | |
| </button> | |
| <button class="nav-item w-full text-left px-3 py-2 rounded-md text-sm font-medium flex items-center space-x-3 text-gray-400 hover:bg-rustic-800" data-tab="settings"> | |
| <i class="fas fa-cog w-4"></i> | |
| <span>Settings</span> | |
| </button> | |
| </nav> | |
| </div> | |
| </aside> | |
| <!-- Right Column: Main Content Area --> | |
| <main class="flex-1 bg-rustic-900 overflow-y-auto relative p-6" id="main-content-area"> | |
| <!-- Tab Content: Documents --> | |
| <div id="documents" class="tab-content hidden"> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| <div class="bg-rustic-800 rounded-xl p-1 border border-rustic-700 shadow-lg"> | |
| <div | |
| class="bg-rustic-900 rounded-t-lg px-4 py-3 border-b border-rustic-700 flex justify-between items-center"> | |
| <span class="text-sm font-medium text-gray-300">Document Management</span> | |
| <i class="fas fa-external-link-alt text-xs text-gray-500"></i> | |
| </div> | |
| <div class="h-64 iframe-container"> | |
| <iframe src="https://docs.example.com/browse" class="w-full h-full" allowfullscreen></iframe> | |
| </div> | |
| </div> | |
| <div class="bg-rustic-800 rounded-xl p-1 border border-rustic-700 shadow-lg"> | |
| <div | |
| class="bg-rustic-900 rounded-t-lg px-4 py-3 border-b border-rustic-700 flex justify-between items-center"> | |
| <span class="text-sm font-medium text-gray-300">Knowledge Base</span> | |
| <i class="fas fa-external-link-alt text-xs text-gray-500"></i> | |
| </div> | |
| <div class="h-64 iframe-container"> | |
| <iframe src="https://wiki.example.com/home" class="w-full h-full" allowfullscreen></iframe> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tab Content: Generate --> | |
| <div id="generate" class="tab-content hidden"> | |
| <div class="grid grid-cols-1 gap-6"> | |
| <div class="bg-rustic-800 rounded-xl p-1 border border-rustic-700 shadow-lg"> | |
| <div | |
| class="bg-rustic-900 rounded-t-lg px-4 py-3 border-b border-rustic-700 flex justify-between items-center"> | |
| <span class="text-sm font-medium text-gray-300">Code Generator</span> | |
| <i class="fas fa-robot text-rust"></i> | |
| </div> | |
| <div class="h-96 iframe-container"> | |
| <iframe src="https://directory.example.com" class="w-full h-full" allowfullscreen></iframe> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tab Content: Settings --> | |
| <div id="settings" class="tab-content hidden"> | |
| <div class="max-w-2xl"> | |
| <div class="bg-rustic-800 rounded-xl p-6 border border-rustic-700"> | |
| <h3 class="text-lg font-medium text-white mb-6">Data Management</h3> | |
| <div class="flex items-center space-x-4"> | |
| <button id="exportLinksBtn" class="bg-rust hover:bg-rust-light text-white font-medium py-2 px-4 rounded-lg flex items-center space-x-2 transition shadow-lg shadow-rust/20"> | |
| <i class="fas fa-file-export"></i> | |
| <span>Export Config</span> | |
| </button> | |
| <button id="importLinksBtn" class="bg-rustic-700 hover:bg-rustic-600 text-white font-medium py-2 px-4 rounded-lg flex items-center space-x-2 transition"> | |
| <i class="fas fa-file-import"></i> | |
| <span>Import Config</span> | |
| </button> | |
| <input type="file" id="importFileInput" class="hidden" accept=".json" /> | |
| </div> | |
| <p class="text-sm text-rustic-500 mt-4"> | |
| Manage your sidebar link configuration here. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Default View: Dashboard with Split Panes --> | |
| <div id="dashboard" class="tab-content block h-full"> | |
| <!-- Split Dashboard Layout --> | |
| <div class="dashboard-split"> | |
| <!-- Top Pane: Welcome & Activity --> | |
| <div class="top-pane bg-rustic-800 rounded-xl border border-rustic-700 p-4 overflow-y-auto"> | |
| <div class="pane-header"> | |
| <span class="pane-title">Dashboard Overview</span> | |
| <div class="flex items-center space-x-2"> | |
| <span class="text-xs text-rustic-500" id="currentDate"></span> | |
| <div class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div> | |
| </div> | |
| </div> | |
| <!-- Welcome Section --> | |
| <div class="mb-6"> | |
| <h2 class="text-2xl font-semibold text-white mb-1">Welcome back!</h2> | |
| <p class="text-rustic-500 text-sm">Here's what's happening with your projects today.</p> | |
| </div> | |
| <!-- Stats Cards --> | |
| <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6"> | |
| <div class="bg-rustic-900 rounded-lg p-4 border border-rustic-700"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <i class="fas fa-project-diagram text-blue-400"></i> | |
| <span class="text-xs text-green-400">+12%</span> | |
| </div> | |
| <div class="text-xl font-semibold text-white">24</div> | |
| <div class="text-xs text-rustic-500">Active Projects</div> | |
| </div> | |
| <div class="bg-rustic-900 rounded-lg p-4 border border-rustic-700"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <i class="fas fa-users text-purple-400"></i> | |
| <span class="text-xs text-green-400">+5%</span> | |
| </div> | |
| <div class="text-xl font-semibold text-white">156</div> | |
| <div class="text-xs text-rustic-500">Team Members</div> | |
| </div> | |
| <div class="bg-rustic-900 rounded-lg p-4 border border-rustic-700"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <i class="fas fa-ticket-alt text-yellow-400"></i> | |
| <span class="text-xs text-red-400">-3%</span> | |
| </div> | |
| <div class="text-xl font-semibold text-white">18</div> | |
| <div class="text-xs text-rustic-500">Open Tickets</div> | |
| </div> | |
| <div class="bg-rustic-900 rounded-lg p-4 border border-rustic-700"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <i class="fas fa-check-circle text-green-400"></i> | |
| <span class="text-xs text-green-400">+8%</span> | |
| </div> | |
| <div class="text-xl font-semibold text-white">142</div> | |
| <div class="text-xs text-rustic-500">Completed</div> | |
| </div> | |
| </div> | |
| <!-- Recent Activity Feed --> | |
| <div class="bg-rustic-900 rounded-lg p-4 border border-rustic-700"> | |
| <h3 class="text-sm font-semibold text-gray-300 mb-4">Recent Activity</h3> | |
| <div class="space-y-3" id="activityFeed"> | |
| <div class="activity-item"> | |
| <div class="activity-dot bg-blue-400"></div> | |
| <div> | |
| <p class="text-sm text-gray-300">New project <span class="text-white font-medium">"Mobile App v2"</span> created</p> | |
| <p class="text-xs text-rustic-500">2 hours ago</p> | |
| </div> | |
| </div> | |
| <div class="activity-item"> | |
| <div class="activity-dot bg-green-400"></div> | |
| <div> | |
| <p class="text-sm text-gray-300"><span class="text-white font-medium">Sarah Chen</span> completed task "API Integration"</p> | |
| <p class="text-xs text-rustic-500">4 hours ago</p> | |
| </div> | |
| </div> | |
| <div class="activity-item"> | |
| <div class="activity-dot bg-yellow-400"></div> | |
| <p> | |
| <div class="text-sm text-gray-300">Ticket #2847 assigned to <span class="text-white font-medium">Support Team</span></p> | |
| <p class="text-xs text-rustic-500">6 hours ago</p> | |
| </div> | |
| </div> | |
| <div class="activity-item"> | |
| <div class="activity-dot bg-purple-400"></div> | |
| <div> | |
| <p class="text-sm text-gray-300"><span class="text-white font-medium">Weekly Report</span> generated successfully</p> | |
| <p class="text-xs text-rustic-500">Yesterday</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Resize Handle --> | |
| <div class="resize-handle" id="resizeHandle"></div> | |
| <!-- Bottom Pane: Quick Shortcuts --> | |
| <div class="bottom-pane bg-rustic-800 rounded-xl border border-rustic-700 p-4 overflow-hidden"> | |
| <div class="pane-header"> | |
| <span class="pane-title">Quick Shortcuts</span> | |
| <button id="openShortcutsModal" class="text-rust hover:text-rust-light transition text-sm flex items-center space-x-1"> | |
| <i class="fas fa-edit"></i> | |
| <span>Customize</span> | |
| </button> | |
| </div> | |
| <!-- Shortcuts Grid --> | |
| <div class="shortcuts-grid flex-1 overflow-y-auto" id="shortcutsGrid"> | |
| <!-- Shortcuts will be injected here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <!-- Chatbot Popup --> | |
| <div class="fixed bottom-6 right-6 z-50 flex flex-col items-end"> | |
| <div id="chatbot-panel" class="chatbot-panel closed bg-rustic-800 rounded-xl shadow-2xl border border-rustic-700 w-[380px] h-[500px] mb-4 flex flex-col overflow-hidden"> | |
| <div class="bg-rust p-3 flex justify-between items-center shrink-0"> | |
| <div class="flex items-center space-x-2"> | |
| <div class="w-2 h-2 bg-white rounded-full animate-pulse"></div> | |
| <span class="font-medium text-white text-sm">Assistant</span> | |
| </div> | |
| <button id="closeChatBtn" class="text-white hover:text-gray-200"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="flex-1 bg-black relative"> | |
| <iframe src="http://ninjabot/chatbot/YuXRYou2mbnDqCXc" class="w-full h-full" frameborder="0" allow="microphone"></iframe> | |
| </div> | |
| </div> | |
| <button id="chatbot-toggle-btn" class="chatbot-toggle w-14 h-14 bg-rust hover:bg-rust-light rounded-full shadow-lg shadow-rust/40 flex items-center justify-center text-white transform hover:scale-110"> | |
| <i class="fas fa-comment-dots text-xl"></i> | |
| </button> | |
| </div> | |
| <!-- Link Settings Modal --> | |
| <div id="linkSettingsModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center hidden z-[60]"> | |
| <div class="bg-rustic-800 border border-rustic-700 rounded-lg p-6 w-full max-w-md shadow-2xl"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h3 class="text-xl font-semibold text-white">Edit Launcher</h3> | |
| <button id="modalCloseBtn" class="text-gray-400 hover:text-white transition"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-1">Category Title</label> | |
| <input type="text" id="categoryName" class="w-full bg-rustic-900 border border-rustic-700 rounded px-3 py-2 text-white" /> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-2">Links</label> | |
| <div id="linkItems" class="space-y-3 max-h-64 overflow-y-auto pr-2"></div> | |
| <button id="addNewLinkBtn" class="mt-3 text-rust text-sm flex items-center space-x-1 hover:text-rust-light transition"> | |
| <i class="fas fa-plus"></i> | |
| <span>Add New Link</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="flex justify-end space-x-3 mt-8"> | |
| <button id="modalCancelBtn" class="px-4 py-2 rounded-md bg-rustic-900 hover:bg-rustic-700 text-gray-300 transition border border-rustic-700">Cancel</button> | |
| <button id="saveChangesBtn" class="px-4 py-2 rounded-md bg-rust hover:bg-rust-light text-white transition shadow-lg shadow-rust/20">Save Changes</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Shortcuts Modal --> | |
| <div id="shortcutsModal" class="fixed inset-0 modal-backdrop flex items-center justify-center hidden z-[70]"> | |
| <div class="bg-rustic-800 border border-rustic-700 rounded-xl p-6 w-full max-w-lg shadow-2xl max-h-[80vh] flex flex-col"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h3 class="text-xl font-semibold text-white">Customize Shortcuts</h3> | |
| <button id="shortcutsModalClose" class="text-gray-400 hover:text-white transition"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-4 flex-1 overflow-y-auto pr-2"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-2">Add New Shortcut</label> | |
| <div class="flex space-x-2 mb-4"> | |
| <input type="text" id="shortcutName" placeholder="Name" class="flex-1 bg-rustic-900 border border-rustic-700 rounded px-3 py-2 text-white text-sm" /> | |
| <input type="text" id="shortcutUrl" placeholder="URL" class="flex-1 bg-rustic-900 border border-rustic-700 rounded px-3 py-2 text-white text-sm" /> | |
| <select id="shortcutIcon" class="bg-rustic-900 border border-rustic-700 rounded px-2 py-2 text-white text-sm"> | |
| <option value="fa-link">🔗</option> | |
| <option value="fa-globe">🌐</option> | |
| <option value="fa-tasks">✓</option> | |
| <option value="fa-chart-line">📈</option> | |
| <option value="fa-cog">⚙️</option> | |
| <option value="fa-envelope">✉️</option> | |
| <option value="fa-calendar">📅</option> | |
| <option value="fa-bell">🔔</option> | |
| <option value="fa-star">⭐</option> | |
| <option value="fa-heart">❤️</option> | |
| <option value="fa-user">👤</option> | |
| <option value="fa-users">👥</option> | |
| <option value="fa-folder">📁</option> | |
| <option value="fa-file">📄</option> | |
| <option value="fa-image">🖼️</option> | |
| <option value="fa-play">▶️</option> | |
| </select> | |
| <select id="shortcutColor" class="bg-rustic-900 border border-rustic-700 rounded px-2 py-2 text-white text-sm"> | |
| <option value="blue">Blue</option> | |
| <option value="green">Green</option> | |
| <option value="purple">Purple</option> | |
| <option value="yellow">Yellow</option> | |
| <option value="red">Red</option> | |
| <option value="teal">Teal</option> | |
| <option value="rust">Rust</option> | |
| <option value="gray">Gray</option> | |
| </select> | |
| </div> | |
| <button id="addShortcutBtn" class="w-full bg-rustic-700 hover:bg-rustic-600 text-white py-2 rounded-lg transition flex items-center justify-center space-x-2"> | |
| <i class="fas fa-plus"></i> | |
| <span>Add Shortcut</span> | |
| </button> | |
| </div> | |
| <div class="border-t border-rustic-700 pt-4"> | |
| <label class="block text-sm font-medium text-gray-400 mb-3">Current Shortcuts</label> | |
| <div id="shortcutItems" class="space-y-2 max-h-48 overflow-y-auto"></div> | |
| </div> | |
| </div> | |
| <div class="flex justify-end space-x-3 mt-6 pt-4 border-t border-rustic-700"> | |
| <button id="shortcutsModalCancel" class="px-4 py-2 rounded-md bg-rustic-900 hover:bg-rustic-700 text-gray-300 transition border border-rustic-700">Close</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener("DOMContentLoaded", () => { | |
| // --- State --- | |
| let currentCategory = ""; | |
| const storageKey = "dashboardLinks"; | |
| const shortcutsStorageKey = "dashboardShortcuts"; | |
| const iconClasses = { | |
| blue: "fa-tachometer-alt text-blue-400", | |
| green: "fa-project-diagram text-green-400", | |
| purple: "fa-comments text-purple-400", | |
| yellow: "fa-calendar text-yellow-400", | |
| red: "fa-book text-red-400", | |
| teal: "fa-users text-teal-400", | |
| rust: "fa-link text-rust-light", | |
| gray: "fa-anchor text-gray-400" | |
| }; | |
| // Shortcut colors mapping | |
| const shortcutColors = { | |
| blue: "text-blue-400", | |
| green: "text-green-400", | |
| purple: "text-purple-400", | |
| yellow: "text-yellow-400", | |
| red: "text-red-400", | |
| teal: "text-teal-400", | |
| rust: "text-rust-light", | |
| gray: "text-gray-400" | |
| }; | |
| const defaultLinks = { | |
| quick: { | |
| title: "Quick Access", | |
| links: [ | |
| { name: "Analytics Dashboard", url: "#", color: "blue" }, | |
| { name: "Project Tracker", url: "#", color: "green" }, | |
| { name: "Team Chat", url: "#", color: "purple" }, | |
| ], | |
| }, | |
| resources: { | |
| title: "Company Hub", | |
| links: [ | |
| { name: "Company Calendar", url: "#", color: "yellow" }, | |
| { name: "Employee Handbook", url: "#", color: "red" }, | |
| { name: "Org Directory", url: "#", color: "teal" }, | |
| ], | |
| }, | |
| support: { | |
| title: "Support Zone", | |
| links: [ | |
| { name: "Submit Ticket", url: "#", color: "rust" }, | |
| { name: "Help Center", url: "#", color: "gray" }, | |
| ], | |
| } | |
| }; | |
| const defaultShortcuts = [ | |
| { name: "Dashboard", url: "#", icon: "fa-tachometer-alt", color: "blue" }, | |
| { name: "Analytics", url: "#", icon: "fa-chart-line", color: "green" }, | |
| { name: "Messages", url: "#", icon: "fa-envelope", color: "purple" }, | |
| { name: "Calendar", url: "#", icon: "fa-calendar", color: "yellow" }, | |
| { name: "Settings", url: "#", icon: "fa-cog", color: "gray" }, | |
| { name: "Support", url: "#", icon: "fa-life-ring", color: "red" }, | |
| ]; | |
| // --- Functions --- | |
| const getStoredLinks = () => { | |
| const links = localStorage.getItem(storageKey); | |
| return links ? JSON.parse(links) : defaultLinks; | |
| }; | |
| const saveLinksToStorage = (links) => { | |
| localStorage.setItem(storageKey, JSON.stringify(links)); | |
| }; | |
| // Shortcuts functions | |
| const getStoredShortcuts = () => { | |
| const shortcuts = localStorage.getItem(shortcutsStorageKey); | |
| return shortcuts ? JSON.parse(shortcuts) : defaultShortcuts; | |
| }; | |
| const saveShortcutsToStorage = (shortcuts) => { | |
| localStorage.setItem(shortcutsStorageKey, JSON.stringify(shortcuts)); | |
| }; | |
| const renderSidebarLinks = () => { | |
| const container = document.getElementById("dashboard-links-view"); | |
| const allLinks = getStoredLinks(); | |
| container.innerHTML = ""; | |
| Object.keys(allLinks).forEach((category) => { | |
| const data = allLinks[category]; | |
| const section = document.createElement("div"); | |
| section.className = "mb-2"; | |
| const header = document.createElement("div"); | |
| header.className = "flex justify-between items-center mb-2 px-2"; | |
| header.innerHTML = ` | |
| <h3 class="font-semibold text-gray-200 text-sm uppercase tracking-wide">${data.title}</h3> | |
| <button class="text-rustic-500 hover:text-rust transition text-xs" data-category="${category}"> | |
| <i class="fas fa-cog"></i> | |
| </button> | |
| `; | |
| const list = document.createElement("div"); | |
| list.className = "space-y-1"; | |
| data.links.forEach(link => { | |
| const icon = iconClasses[link.color] || "fa-link text-gray-400"; | |
| const a = document.createElement("a"); | |
| a.href = link.url; | |
| a.target = "_blank"; | |
| a.className = "link-card flex items-center space-x-3 py-2 px-3 rounded-md text-gray-400 hover:text-white cursor-pointer"; | |
| a.innerHTML = ` | |
| <i class="fas ${icon} w-4 text-center"></i> | |
| <span class="text-sm truncate">${link.name}</span> | |
| `; | |
| list.appendChild(a); | |
| }); | |
| section.appendChild(header); | |
| section.appendChild(list); | |
| container.appendChild(section); | |
| }); | |
| document.querySelectorAll("#dashboard-links-view button[data-category]").forEach(btn => { | |
| btn.addEventListener("click", (e) => showLinkSettings(e.currentTarget.dataset.category)); | |
| }); | |
| }; | |
| // Render shortcuts in bottom pane | |
| const renderShortcuts = () => { | |
| const container = document.getElementById("shortcutsGrid"); | |
| const shortcuts = getStoredShortcuts(); | |
| container.innerHTML = ""; | |
| shortcuts.forEach((shortcut, index) => { | |
| const iconColor = shortcutColors[shortcut.color] || "text-gray-400"; | |
| const div = document.createElement("div"); | |
| div.className = "shortcut-item"; | |
| div.innerHTML = ` | |
| <i class="fas ${shortcut.icon} ${iconColor}"></i> | |
| <span class="truncate w-full">${shortcut.name}</span> | |
| `; | |
| div.addEventListener("click", () => { | |
| if(shortcut.url && shortcut.url !== "#") { | |
| window.open(shortcut.url, "_blank"); | |
| } | |
| }); | |
| container.appendChild(div); | |
| }); | |
| }; | |
| const hideLinkSettings = () => document.getElementById("linkSettingsModal").classList.add("hidden"); | |
| const hideShortcutsModal = () => document.getElementById("shortcutsModal").classList.add("hidden"); | |
| const showShortcutsModal = () => { | |
| renderShortcutItems(); | |
| document.getElementById("shortcutsModal").classList.remove("hidden"); | |
| }; | |
| const addNewLink = (name = "", url = "", color = "rust") => { | |
| const linkItems = document.getElementById("linkItems"); | |
| const newLink = document.createElement("div"); | |
| newLink.className = "flex items-center space-x-2 link-item-entry"; | |
| let optionsHtml = Object.keys(iconClasses) | |
| .map((key) => `<option value="${key}" ${key === color ? "selected" : ""}>${key.charAt(0).toUpperCase() + key.slice(1)}</option>`) | |
| .join(""); | |
| newLink.innerHTML = ` | |
| <input type="text" placeholder="Name" class="flex-1 bg-rustic-900 border border-rustic-700 rounded px-2 py-1.5 text-white text-sm" value="${name}"> | |
| <input type="text" placeholder="URL" class="flex-1 bg-rustic-900 border border-rustic-700 rounded px-2 py-1.5 text-white text-sm" value="${url}"> | |
| <select class="bg-rustic-900 border border-rustic-700 rounded px-1 py-1.5 text-white text-sm">${optionsHtml}</select> | |
| <button class="text-red-500 hover:text-red-400 p-1.5 remove-link-btn"><i class="fas fa-trash"></i></button> | |
| `; | |
| linkItems.appendChild(newLink); | |
| }; | |
| const showLinkSettings = (category) => { | |
| currentCategory = category; | |
| const allLinks = getStoredLinks(); | |
| const data = allLinks[currentCategory]; | |
| document.getElementById("categoryName").value = data.title; | |
| const linkItemsContainer = document.getElementById("linkItems"); | |
| linkItemsContainer.innerHTML = ""; | |
| if (data.links) { | |
| data.links.forEach(link => addNewLink(link.name, link.url, link.color)); | |
| } | |
| document.getElementById("linkSettingsModal").classList.remove("hidden"); | |
| }; | |
| const saveLinkSettings = () => { | |
| const newTitle = document.getElementById("categoryName").value; | |
| const linkItems = document.querySelectorAll("#linkItems .link-item-entry"); | |
| const newLinks = Array.from(linkItems) | |
| .map((item) => ({ | |
| name: item.children[0].value, | |
| url: item.children[1].value, | |
| color: item.children[2].value, | |
| })) | |
| .filter((link) => link.name && link.url); | |
| const allLinks = getStoredLinks(); | |
| allLinks[currentCategory] = { title: newTitle, links: newLinks }; | |
| saveLinksToStorage(allLinks); | |
| renderSidebarLinks(); | |
| hideLinkSettings(); | |
| }; | |
| // Render shortcut items in modal | |
| const renderShortcutItems = () => { | |
| const container = document.getElementById("shortcutItems"); | |
| const shortcuts = getStoredShortcuts(); | |
| container.innerHTML = ""; | |
| shortcuts.forEach((shortcut, index) => { | |
| const iconColor = shortcutColors[shortcut.color] || "text-gray-400"; | |
| const div = document.createElement("div"); | |
| div.className = "flex items-center justify-between bg-rustic-900 rounded px-3 py-2"; | |
| div.innerHTML = ` | |
| <div class="flex items-center space-x-3"> | |
| <i class="fas ${shortcut.icon} ${iconColor}"></i> | |
| <span class="text-sm text-white">${shortcut.name}</span> | |
| </div> | |
| <button class="text-red-500 hover:text-red-400 remove-shortcut-btn" data-index="${index}"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| `; | |
| container.appendChild(div); | |
| }); | |
| // Attach remove listeners | |
| document.querySelectorAll(".remove-shortcut-btn").forEach(btn => { | |
| btn.addEventListener("click", (e) => { | |
| const idx = parseInt(e.currentTarget.dataset.index); | |
| const shortcuts = getStoredShortcuts(); | |
| shortcuts.splice(idx, 1); | |
| saveShortcutsToStorage(shortcuts); | |
| renderShortcutItems(); | |
| renderShortcuts(); | |
| }); | |
| }); | |
| }; | |
| const addNewShortcut = () => { | |
| const name = document.getElementById("shortcutName").value.trim(); | |
| const url = document.getElementById("shortcutUrl").value.trim(); | |
| const icon = document.getElementById("shortcutIcon").value; | |
| const color = document.getElementById("shortcutColor").value; | |
| if (!name) { | |
| alert("Please enter a shortcut name"); | |
| return; | |
| } | |
| const shortcuts = getStoredShortcuts(); | |
| shortcuts.push({ name, url: url || "#", icon, color }); | |
| saveShortcutsToStorage(shortcuts); | |
| // Clear inputs | |
| document.getElementById("shortcutName").value = ""; | |
| document.getElementById("shortcutUrl").value = ""; | |
| renderShortcutItems(); | |
| renderShortcuts(); | |
| }; | |
| // --- Chatbot Logic --- | |
| const chatPanel = document.getElementById('chatbot-panel'); | |
| const chatBtn = document.getElementById('chatbot-toggle-btn'); | |
| const closeChatBtn = document.getElementById('closeChatBtn'); | |
| let isChatOpen = false; | |
| const toggleChat = () => { | |
| isChatOpen = !isChatOpen; | |
| if(isChatOpen) { | |
| chatPanel.classList.remove('closed'); | |
| chatBtn.classList.add('hidden'); | |
| } else { | |
| chatPanel.classList.add('closed'); | |
| setTimeout(() => { | |
| if(!isChatOpen) chatBtn.classList.remove('hidden'); | |
| }, 300); | |
| } | |
| }; | |
| chatBtn.addEventListener('click', toggleChat); | |
| closeChatBtn.addEventListener('click', toggleChat); | |
| // --- Tab Logic --- | |
| const switchTab = (event) => { | |
| const tabId = event.currentTarget.dataset.tab; | |
| document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden')); | |
| document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('block')); | |
| const target = document.getElementById(tabId); | |
| if(target) { | |
| target.classList.remove('hidden'); | |
| target.classList.add('block'); | |
| } | |
| document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active')); | |
| event.currentTarget.classList.add('active'); | |
| }; | |
| // --- Search Handlers --- | |
| const handleMetaSearch = (e) => { | |
| e.preventDefault(); | |
| const q = document.getElementById('search-input').value; | |
| if(q) window.open(`https://www.google.com/search?q=${encodeURIComponent(q)}`, '_blank'); | |
| }; | |
| const handleRubyGemsSearch = (e) => { | |
| e.preventDefault(); | |
| const q = document.getElementById('rubygems-search-input').value; | |
| if(q) window.open(`https://rubygems.org/search?query=${encodeURIComponent(q)}`, '_blank'); | |
| }; | |
| // --- Import/Export --- | |
| const handleExport = () => { | |
| const data = localStorage.getItem(storageKey); | |
| if(!data) return; | |
| const a = document.createElement('a'); | |
| a.href = URL.createObjectURL(new Blob([data], {type: 'application/json'})); | |
| a.download = 'links.json'; | |
| a.click(); | |
| }; | |
| const handleImport = (e) => { | |
| const file = e.target.files[0]; | |
| if(!file) return; | |
| const r = new FileReader(); | |
| r.onload = (ev) => { | |
| try { | |
| const data = JSON.parse(ev.target.result); | |
| localStorage.setItem(storageKey, JSON.stringify(data)); | |
| renderSidebarLinks(); | |
| alert("Imported successfully"); | |
| } catch(err) { alert("Invalid file"); } | |
| }; | |
| r.readAsText(file); | |
| e.target.value = ''; | |
| }; | |
| // --- Pane Resize Logic --- | |
| const setupPaneResize = () => { | |
| const handle = document.getElementById("resizeHandle"); | |
| const topPane = document.querySelector(".top-pane"); | |
| const bottomPane = document.querySelector(".bottom-pane"); | |
| let isResizing = false; | |
| let startY = 0; | |
| let startTopHeight = 0; | |
| handle.addEventListener("mousedown", (e) => { | |
| isResizing = true; | |
| startY = e.clientY; | |
| startTopHeight = topPane.offsetHeight; | |
| document.body.style.cursor = "ns-resize"; | |
| document.body.style.userSelect = "none"; | |
| }); | |
| document.addEventListener("mousemove", (e) => { | |
| if (!isResizing) return; | |
| const deltaY = e.clientY - startY; | |
| const newTopHeight = startTopHeight + deltaY; | |
| const totalHeight = topPane.parentElement.offsetHeight - 16; // Account for gap | |
| if (newTopHeight > 100 && newTopHeight < totalHeight - 150) { | |
| topPane.style.flex = `0 0 ${newTopHeight}px`; | |
| bottomPane.style.flex = `0 0 ${totalHeight - newTopHeight}px`; | |
| } | |
| }); | |
| document.addEventListener("mouseup", () => { | |
| if |