@@ -147,7 +147,14 @@ <h2 class="text-base font-semibold text-gray-900">Instâncias</h2>
147147< script >
148148 const apiKey = localStorage . getItem ( 'apikey' ) || ''
149149
150+ function esc ( str ) {
151+ const d = document . createElement ( 'div' )
152+ d . textContent = str == null ? '' : String ( str )
153+ return d . innerHTML
154+ }
155+
150156 async function apiFetch ( path ) {
157+ if ( ! apiKey ) throw new Error ( 'missing-apikey' )
151158 const res = await fetch ( path , { headers : { apikey : apiKey } } )
152159 if ( ! res . ok ) throw new Error ( `HTTP ${ res . status } ` )
153160 return res . json ( )
@@ -158,52 +165,87 @@ <h2 class="text-base font-semibold text-gray-900">Instâncias</h2>
158165 if ( el ) el . textContent = val
159166 }
160167
168+ function renderMissingApiKey ( ) {
169+ const container = document . getElementById ( 'table-container' )
170+ container . innerHTML = '<div class="px-6 py-10 text-center text-sm text-yellow-600">API Key não configurada. Defina <code>localStorage.setItem(\'apikey\', \'SUA_KEY\')</code> no console e recarregue.</div>'
171+ set ( 'metric-total' , '—' )
172+ set ( 'metric-connected' , '—' )
173+ set ( 'metric-disconnected' , '—' )
174+ }
175+
161176 function renderTable ( instances ) {
162177 const container = document . getElementById ( 'table-container' )
163178 if ( ! instances . length ) {
164179 container . innerHTML = '<div class="px-6 py-10 text-center text-sm text-gray-400">Nenhuma instância encontrada.</div>'
165180 return
166181 }
167- const rows = instances . map ( inst => {
182+
183+ const wrapper = document . createElement ( 'div' )
184+ wrapper . className = 'overflow-x-auto'
185+
186+ const table = document . createElement ( 'table' )
187+ table . className = 'w-full text-left'
188+
189+ const thead = document . createElement ( 'thead' )
190+ thead . innerHTML = `
191+ <tr class="bg-gray-50">
192+ <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Nome</th>
193+ <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Status</th>
194+ <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Telefone</th>
195+ <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Client</th>
196+ <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">AlwaysOnline</th>
197+ </tr>`
198+ table . appendChild ( thead )
199+
200+ const tbody = document . createElement ( 'tbody' )
201+ instances . forEach ( inst => {
168202 const phone = ( inst . jid || '' ) . replace ( / @ .+ / , '' ) || '—'
169- const statusBadge = inst . connected
170- ? `<span class="inline-flex items-center gap-1.5 rounded-full bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700">
171- <span class="h-1.5 w-1.5 rounded-full bg-green-500 pulse"></span>Conectado</span>`
172- : `<span class="inline-flex items-center gap-1.5 rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-500">
173- <span class="h-1.5 w-1.5 rounded-full bg-gray-400"></span>Desconectado</span>`
174- const alwaysOnline = inst . alwaysOnline
203+
204+ const tr = document . createElement ( 'tr' )
205+ tr . className = 'border-t border-gray-100 hover:bg-gray-50 transition-colors'
206+
207+ const tdName = document . createElement ( 'td' )
208+ tdName . className = 'py-3 px-4'
209+ const nameSpan = document . createElement ( 'span' )
210+ nameSpan . className = 'font-medium text-gray-900 text-sm'
211+ nameSpan . textContent = inst . name || '—'
212+ tdName . appendChild ( nameSpan )
213+
214+ const tdStatus = document . createElement ( 'td' )
215+ tdStatus . className = 'py-3 px-4'
216+ tdStatus . innerHTML = inst . connected
217+ ? `<span class="inline-flex items-center gap-1.5 rounded-full bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700"><span class="h-1.5 w-1.5 rounded-full bg-green-500 pulse"></span>Conectado</span>`
218+ : `<span class="inline-flex items-center gap-1.5 rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-500"><span class="h-1.5 w-1.5 rounded-full bg-gray-400"></span>Desconectado</span>`
219+
220+ const tdPhone = document . createElement ( 'td' )
221+ tdPhone . className = 'py-3 px-4 text-sm text-gray-500'
222+ tdPhone . textContent = phone
223+
224+ const tdClient = document . createElement ( 'td' )
225+ tdClient . className = 'py-3 px-4 text-sm text-gray-400'
226+ tdClient . textContent = inst . clientName || '—'
227+
228+ const tdAO = document . createElement ( 'td' )
229+ tdAO . className = 'py-3 px-4'
230+ tdAO . innerHTML = inst . alwaysOnline
175231 ? `<span class="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-medium text-blue-700">⚡ Ativo</span>`
176232 : `<span class="text-xs text-gray-400">—</span>`
177- return `
178- <tr class="border-t border-gray-100 hover:bg-gray-50 transition-colors">
179- <td class="py-3 px-4">
180- <span class="font-medium text-gray-900 text-sm">${ inst . name } </span>
181- </td>
182- <td class="py-3 px-4">${ statusBadge } </td>
183- <td class="py-3 px-4 text-sm text-gray-500">${ phone } </td>
184- <td class="py-3 px-4 text-sm text-gray-400">${ inst . clientName || '—' } </td>
185- <td class="py-3 px-4">${ alwaysOnline } </td>
186- </tr>`
187- } ) . join ( '' )
188-
189- container . innerHTML = `
190- <div class="overflow-x-auto">
191- <table class="w-full text-left">
192- <thead>
193- <tr class="bg-gray-50">
194- <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Nome</th>
195- <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Status</th>
196- <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Telefone</th>
197- <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">Client</th>
198- <th class="py-3 px-4 text-xs font-semibold uppercase tracking-wide text-gray-500">AlwaysOnline</th>
199- </tr>
200- </thead>
201- <tbody>${ rows } </tbody>
202- </table>
203- </div>`
233+
234+ tr . append ( tdName , tdStatus , tdPhone , tdClient , tdAO )
235+ tbody . appendChild ( tr )
236+ } )
237+
238+ table . appendChild ( tbody )
239+ wrapper . appendChild ( table )
240+ container . replaceChildren ( wrapper )
204241 }
205242
206243 async function loadData ( ) {
244+ if ( ! apiKey ) {
245+ renderMissingApiKey ( )
246+ return
247+ }
248+
207249 const icon = document . getElementById ( 'refresh-icon' )
208250 icon . classList . add ( 'spin' )
209251 try {
0 commit comments