|
1 | 1 | {{ define "content" }} |
2 | | -<div id="dashboard-vue-app" class="h-full flex overflow-hidden"> |
| 2 | +<div class="h-full flex overflow-hidden"> |
3 | 3 | <!-- Compact Request List --> |
4 | 4 | <div class="w-80 lg:w-96 border-r border-slate-800 bg-slate-900/30 flex flex-col shrink-0"> |
5 | 5 | <div class="p-4 border-b border-slate-800 flex items-center justify-between bg-slate-900/50"> |
|
42 | 42 | <div class="flex items-center gap-3 whitespace-nowrap"> |
43 | 43 | {{ if .OtherEndpoints }} |
44 | 44 | <div class="relative" id="switch-container"> |
45 | | - <button v-on:click="toggleSwitchDropdown" class="text-xs font-bold text-slate-300 hover:text-white bg-slate-800 hover:bg-slate-700 px-3 py-1.5 rounded-lg transition-all flex items-center gap-1.5"> |
| 45 | + <button id="switch-btn" onclick="toggleSwitchDropdown()" class="text-xs font-bold text-slate-300 hover:text-white bg-slate-800 hover:bg-slate-700 px-3 py-1.5 rounded-lg transition-all flex items-center gap-1.5"> |
46 | 46 | <i class="fas fa-exchange-alt text-[10px]"></i> |
47 | 47 | Switch |
48 | | - <i class="fas fa-chevron-down text-[8px]" v-bind:class="getChevronClass()"></i> |
| 48 | + <i id="switch-chevron" class="fas fa-chevron-down text-[8px] transition-transform duration-200"></i> |
49 | 49 | </button> |
50 | | - <transition name="dropdown"> |
51 | | - <div v-if="switchDropdownOpen" class="absolute right-0 mt-2 w-64 bg-slate-900 border border-slate-800 rounded-lg shadow-xl py-1 z-50"> |
| 50 | + <div id="switch-dropdown" class="hidden absolute right-0 mt-2 w-64 bg-slate-900 border border-slate-800 rounded-lg shadow-xl py-1 z-50"> |
52 | 51 | {{ range .OtherEndpoints }} |
53 | 52 | <a href="/{{ .ID }}" class="block px-4 py-2 text-[10px] font-mono text-slate-400 hover:text-white hover:bg-slate-800 truncate flex items-center gap-2 border-l-4 switch-endpoint-item" data-endpoint-id="{{ .ID }}"> |
54 | 53 | <div class="w-1.5 h-1.5 rounded-full shrink-0 switch-endpoint-dot" data-endpoint-id="{{ .ID }}"></div> |
|
60 | 59 | <i class="fas fa-plus text-[8px] mr-2"></i>Create New |
61 | 60 | </a> |
62 | 61 | </div> |
63 | | - </div> |
64 | | - </transition> |
| 62 | + </div> |
65 | 63 | </div> |
66 | 64 | {{ end }} |
67 | 65 | <span class="flex items-center gap-2 px-3 py-1 rounded-full text-xs font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20"> |
|
113 | 111 |
|
114 | 112 | <script> |
115 | 113 | (function() { |
116 | | - // Loading overlay helpers (plain DOM, not Vue) |
| 114 | + // Loading overlay helpers |
117 | 115 | function showLoadingOverlay() { |
118 | 116 | var overlay = document.getElementById("loading-overlay"); |
119 | 117 | var content = document.getElementById("request-detail-content"); |
|
139 | 137 | window.showLoadingOverlay = showLoadingOverlay; |
140 | 138 | window.hideLoadingOverlay = hideLoadingOverlay; |
141 | 139 |
|
142 | | - const { createApp } = Vue; |
143 | | - |
144 | | - // Vue app for dashboard interactions (minimal - just for dropdown) |
145 | | - const dashboardApp = createApp({ |
146 | | - data() { |
147 | | - return { |
148 | | - switchDropdownOpen: false, |
149 | | - endpointColor: null |
| 140 | + // Switch dropdown toggle (plain JavaScript) |
| 141 | + var switchDropdownOpen = false; |
| 142 | + function toggleSwitchDropdown() { |
| 143 | + var dropdown = document.getElementById("switch-dropdown"); |
| 144 | + var chevron = document.getElementById("switch-chevron"); |
| 145 | + switchDropdownOpen = !switchDropdownOpen; |
| 146 | + if (dropdown) { |
| 147 | + if (switchDropdownOpen) { |
| 148 | + dropdown.classList.remove("hidden"); |
| 149 | + } else { |
| 150 | + dropdown.classList.add("hidden"); |
150 | 151 | } |
151 | | - }, |
152 | | - mounted() { |
153 | | - // Set endpoint color on mount and apply to header |
154 | | - {{ if .Endpoint }} |
155 | | - var endpointIdElement = document.getElementById("endpoint-header"); |
156 | | - if(endpointIdElement && typeof getEndpointColor === "function") { |
157 | | - var endpointId = endpointIdElement.getAttribute("data-endpoint-id"); |
158 | | - if(endpointId) { |
159 | | - this.endpointColor = getEndpointColor(endpointId); |
160 | | - if (this.endpointColor) { |
161 | | - var header = document.getElementById("endpoint-header"); |
162 | | - var dot = document.getElementById("endpoint-color-dot"); |
163 | | - var url = document.getElementById("endpoint-url"); |
164 | | - if (header) { |
165 | | - header.classList.add("border-l-4", this.endpointColor.border); |
166 | | - header.classList.remove("border-slate-800"); |
167 | | - } |
168 | | - if (dot) { |
169 | | - dot.classList.remove("bg-brand-500"); |
170 | | - dot.classList.add(this.endpointColor.accent); |
171 | | - } |
172 | | - if (url) { |
173 | | - url.classList.remove("text-brand-400"); |
174 | | - url.classList.add(this.endpointColor.text); |
175 | | - } |
| 152 | + } |
| 153 | + if (chevron) { |
| 154 | + if (switchDropdownOpen) { |
| 155 | + chevron.classList.add("rotate-180"); |
| 156 | + } else { |
| 157 | + chevron.classList.remove("rotate-180"); |
| 158 | + } |
| 159 | + } |
| 160 | + } |
| 161 | + window.toggleSwitchDropdown = toggleSwitchDropdown; |
| 162 | + |
| 163 | + // Close dropdown when clicking outside |
| 164 | + document.addEventListener("click", function(event) { |
| 165 | + var container = document.getElementById("switch-container"); |
| 166 | + if (container && !container.contains(event.target) && switchDropdownOpen) { |
| 167 | + switchDropdownOpen = false; |
| 168 | + var dropdown = document.getElementById("switch-dropdown"); |
| 169 | + var chevron = document.getElementById("switch-chevron"); |
| 170 | + if (dropdown) dropdown.classList.add("hidden"); |
| 171 | + if (chevron) chevron.classList.remove("rotate-180"); |
| 172 | + } |
| 173 | + }); |
| 174 | + |
| 175 | + // Set endpoint color on load |
| 176 | + {{ if .Endpoint }} |
| 177 | + (function() { |
| 178 | + var endpointIdElement = document.getElementById("endpoint-header"); |
| 179 | + if(endpointIdElement && typeof getEndpointColor === "function") { |
| 180 | + var endpointId = endpointIdElement.getAttribute("data-endpoint-id"); |
| 181 | + if(endpointId) { |
| 182 | + var endpointColor = getEndpointColor(endpointId); |
| 183 | + if (endpointColor) { |
| 184 | + var header = document.getElementById("endpoint-header"); |
| 185 | + var dot = document.getElementById("endpoint-color-dot"); |
| 186 | + var url = document.getElementById("endpoint-url"); |
| 187 | + if (header) { |
| 188 | + header.classList.add("border-l-4", endpointColor.border); |
| 189 | + header.classList.remove("border-slate-800"); |
| 190 | + } |
| 191 | + if (dot) { |
| 192 | + dot.classList.remove("bg-brand-500"); |
| 193 | + dot.classList.add(endpointColor.accent); |
| 194 | + } |
| 195 | + if (url) { |
| 196 | + url.classList.remove("text-brand-400"); |
| 197 | + url.classList.add(endpointColor.text); |
176 | 198 | } |
177 | 199 | } |
178 | 200 | } |
179 | | - {{ end }} |
180 | | - }, |
181 | | - methods: { |
182 | | - toggleSwitchDropdown() { |
183 | | - this.switchDropdownOpen = !this.switchDropdownOpen; |
184 | | - }, |
185 | | - getChevronClass() { |
186 | | - return this.switchDropdownOpen ? "rotate-180" : ""; |
187 | | - }, |
188 | | - closeSwitchDropdown(event) { |
189 | | - var container = document.getElementById("switch-container"); |
190 | | - if (container && !container.contains(event.target)) { |
191 | | - this.switchDropdownOpen = false; |
192 | | - } |
| 201 | + } |
| 202 | + })(); |
| 203 | + {{ end }} |
| 204 | + |
| 205 | + // Handle HTMX events for smooth transitions |
| 206 | + document.body.addEventListener("htmx:beforeRequest", function(e) { |
| 207 | + if (e.detail.target && e.detail.target.id === "request-detail-content") { |
| 208 | + showLoadingOverlay(); |
| 209 | + } |
| 210 | + }); |
| 211 | + |
| 212 | + document.body.addEventListener("htmx:afterSwap", function(e) { |
| 213 | + if (e.detail.target && e.detail.target.id === "request-detail-content") { |
| 214 | + hideLoadingOverlay(); |
| 215 | + if (typeof initRequestDetail === "function") { |
| 216 | + initRequestDetail(); |
193 | 217 | } |
194 | | - }, |
195 | | - mounted() { |
196 | | - // Close dropdown when clicking outside |
197 | | - document.addEventListener("click", this.closeSwitchDropdown); |
198 | | - |
199 | | - // Handle HTMX events for smooth transitions (using plain DOM, not Vue state) |
200 | | - document.body.addEventListener("htmx:beforeRequest", function(e) { |
201 | | - if (e.detail.target && e.detail.target.id === "request-detail-content") { |
202 | | - showLoadingOverlay(); |
203 | | - } |
204 | | - }); |
205 | | - |
206 | | - document.body.addEventListener("htmx:afterSwap", function(e) { |
207 | | - if (e.detail.target && e.detail.target.id === "request-detail-content") { |
208 | | - hideLoadingOverlay(); |
209 | | - // Initialize request detail (format JSON, update line numbers) |
210 | | - if (typeof initRequestDetail === "function") { |
211 | | - initRequestDetail(); |
212 | | - } |
213 | | - } |
214 | | - }); |
215 | | - |
216 | | - // Reset loading state on HTMX errors |
217 | | - document.body.addEventListener("htmx:responseError", function(e) { |
218 | | - hideLoadingOverlay(); |
219 | | - }); |
220 | | - |
221 | | - document.body.addEventListener("htmx:sendError", function(e) { |
222 | | - hideLoadingOverlay(); |
223 | | - }); |
224 | | - }, |
225 | | - beforeUnmount() { |
226 | | - document.removeEventListener("click", this.closeSwitchDropdown); |
227 | 218 | } |
228 | 219 | }); |
229 | 220 |
|
| 221 | + document.body.addEventListener("htmx:responseError", function(e) { |
| 222 | + hideLoadingOverlay(); |
| 223 | + }); |
| 224 | + |
| 225 | + document.body.addEventListener("htmx:sendError", function(e) { |
| 226 | + hideLoadingOverlay(); |
| 227 | + }); |
| 228 | + |
230 | 229 | // Copy endpoint URL function |
231 | 230 | function copyEndpointUrl() { |
232 | 231 | const urlElement = document.getElementById("endpoint-url"); |
|
260 | 259 | } |
261 | 260 | window.handleDeleteEndpoint = handleDeleteEndpoint; |
262 | 261 |
|
263 | | - // Mount Vue app |
264 | | - dashboardApp.mount("#dashboard-vue-app"); |
265 | | - |
266 | 262 | // Handle request item click |
267 | 263 | function handleRequestItemClick(element) { |
268 | 264 | // Remove active state from all items |
|
0 commit comments