- Scope the new bookings_checkin banner to that page's own tablist only — bookings_view has a ui-tabs-nav tablist too, so it was duplicating alongside the existing arrival-reminder banner there - options.js: persist whatever the default-tablet <select> ends up showing, not just on manual change — a <select> auto-selects its first <option> without firing 'change', so a single-tablet setup (or anyone happy with the first option) never actually got a default saved despite the dropdown looking selected, which sent every injected button to the manual picker Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
118 lines
5.2 KiB
JavaScript
118 lines
5.2 KiB
JavaScript
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 =>
|
|
`<option value="${d.device_id}">${escapeHtml(d.name)}</option>`
|
|
).join('');
|
|
if (preselectId && devices.some(d => d.device_id === preselectId)) {
|
|
defaultDeviceSelect.value = preselectId;
|
|
}
|
|
defaultSection.style.display = '';
|
|
// A <select> always shows *something* selected — the browser defaults to
|
|
// the first <option> with no explicit selection, with no 'change' event
|
|
// firing for that. Without this, a machine with only one tablet (or
|
|
// whoever's happy with the first one) never actually got a default
|
|
// saved, despite the dropdown visibly showing one selected — the
|
|
// injected buttons would then always fall back to the manual picker
|
|
// (confirmed live 2026-10-01). Persisting whatever ends up selected here
|
|
// keeps storage and the visible dropdown state from ever drifting apart.
|
|
chrome.storage.local.set({ defaultDeviceId: defaultDeviceSelect.value });
|
|
} 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';
|
|
const { defaultDeviceId } = await chrome.storage.local.get('defaultDeviceId');
|
|
refreshDeviceList(defaultDeviceId);
|
|
}
|
|
|
|
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';
|
|
const { defaultDeviceId } = await chrome.storage.local.get('defaultDeviceId');
|
|
refreshDeviceList(defaultDeviceId);
|
|
} catch (err) {
|
|
statusEl.textContent = `Failed: ${err.message}`;
|
|
statusEl.style.color = '#dc2626';
|
|
}
|
|
}
|
|
|
|
document.getElementById('save').addEventListener('click', save);
|
|
document.getElementById('test').addEventListener('click', test);
|
|
load();
|