const baseUrlInput = document.getElementById('baseUrl'); const apiKeyInput = document.getElementById('apiKey'); const statusEl = document.getElementById('status'); const defaultSection = document.getElementById('defaultTabletSection'); const defaultDeviceSelect = document.getElementById('defaultDevice'); async function load() { const { baseUrl, apiKey, defaultDeviceId } = await chrome.storage.local.get(['baseUrl', 'apiKey', 'defaultDeviceId']); if (baseUrl) baseUrlInput.value = baseUrl; if (apiKey) apiKeyInput.value = apiKey; // deliberately shown back here (options page, // not the popup) so the value can be corrected without regenerating the key. if (baseUrl && apiKey) refreshDeviceList(defaultDeviceId); } // Shared by Test Connection and the default-tablet picker — one fetch, two // uses, rather than hitting the API twice for what's the same data. async function fetchDevices() { const { baseUrl, apiKey } = await chrome.storage.local.get(['baseUrl', 'apiKey']); if (!baseUrl || !apiKey) throw new Error('Set both fields and save first.'); const res = await fetch(`${baseUrl}/api/staff/devices`, { headers: { Authorization: `Bearer ${apiKey}` }, }); const body = await res.json().catch(() => ([])); if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`); return body; } // Populates the "default tablet for this machine" picker — see project_checkin // memory's extension notes: the popup pre-selects this but still lets staff // override it; the injected buttons skip asking entirely and push straight // to it unless it's busy. Selection auto-saves on change, no separate button // — it's a small enough setting not to warrant one. async function refreshDeviceList(preselectId) { try { const devices = await fetchDevices(); if (!devices.length) { defaultSection.style.display = 'none'; return; } defaultDeviceSelect.innerHTML = devices.map(d => `` ).join(''); if (preselectId) defaultDeviceSelect.value = preselectId; defaultSection.style.display = ''; } catch { // Not configured/reachable yet — leave the section hidden rather than // showing an empty/broken picker; Test Connection surfaces the real error. defaultSection.style.display = 'none'; } } defaultDeviceSelect.addEventListener('change', () => { chrome.storage.local.set({ defaultDeviceId: defaultDeviceSelect.value }); }); function escapeHtml(s) { const div = document.createElement('div'); div.textContent = s; return div.innerHTML; } // tablet-checkin's own base URL isn't known in advance (dev today, a // different production URL later), so it can't be pre-declared in the // manifest's host_permissions — Chrome would need republishing/reloading // every time the URL changed. Requesting it dynamically here instead (needs // a user gesture, which this Save click provides) means the extension's own // fetches bypass CORS entirely for that origin once granted, rather than // depending on the server sending back Access-Control-Allow-Origin for an // extension origin ID that isn't fixed/predictable per install anyway. async function save() { const baseUrl = baseUrlInput.value.trim().replace(/\/$/, ''); const apiKey = apiKeyInput.value.trim(); if (baseUrl) { const granted = await chrome.permissions.request({ origins: [`${baseUrl}/*`] }); if (!granted) { statusEl.textContent = 'Not saved — permission for that URL was denied. This extension needs it to talk to tablet-checkin.'; statusEl.style.color = '#dc2626'; return; } } await chrome.storage.local.set({ baseUrl, apiKey }); statusEl.textContent = 'Saved.'; statusEl.style.color = '#16a34a'; refreshDeviceList(); } async function test() { statusEl.textContent = 'Testing…'; statusEl.style.color = '#64748b'; try { const devices = await fetchDevices(); statusEl.textContent = `Connected — found ${devices.length} tablet(s).`; statusEl.style.color = '#16a34a'; refreshDeviceList(); } catch (err) { statusEl.textContent = `Failed: ${err.message}`; statusEl.style.color = '#dc2626'; } } document.getElementById('save').addEventListener('click', save); document.getElementById('test').addEventListener('click', test); load();