// client/app.js: the page's script. It calls the backend on the same // address, so the browser sends the sign-in cookie with each request. // The rows' name, and the route that adds and lists the signed-in user's rows. const PLURAL = 'notes'; const ROUTE = '/api/notes'; // The fields of one row, each with its kind: the form's inputs and the list's columns. const FIELDS = [{ name: 'title', kind: 'text' }]; // The input each kind of field takes. const INPUT_TYPES = { text: 'text', integer: 'number', boolean: 'checkbox', timestamp: 'datetime-local' }; // Why the AI model gave no answer, by the error the route answers with 503. const UNANSWERED = { allowance_drawn: "This month's AI allowance is drawn, so the model gives no answer until it resets.", allowance_unavailable: 'The platform holds no key for the AI allowance yet, so the model gives no answer now.', upstream_quota: 'The AI model is out of capacity now: ask again in a while.', }; const byId = (id) => document.getElementById(id); // A field's value as the route takes it: a string, a number, true or false, or an ISO time. function valueOf(field, input) { if (field.kind === 'boolean') return input.checked; if (input.value === '') return field.kind === 'text' ? '' : null; if (field.kind === 'integer') return Number(input.value); if (field.kind === 'timestamp') return new Date(input.value).toISOString(); return input.value; } // The signed-in user, from the platform's own route: null where no one is signed in. async function signedInUser() { const response = await fetch('/__account/me'); if (response.status === 200) return (await response.json()).user; if (response.status === 404) { byId('who').textContent = "This address serves no sign-in. Sign-in is on the application's deployed address; a local run's API routes take a session as a Bearer."; return undefined; } return null; } async function showRows() { const response = await fetch(ROUTE); const list = byId('rows'); list.replaceChildren(); if (response.status !== 200) return; for (const row of await response.json()) { const item = document.createElement('li'); item.textContent = FIELDS.map((field) => (FIELDS.length === 1 ? String(row[field.name]) : `${field.name}: ${row[field.name]}`)).join(', '); list.append(item); } } function buildForm() { const inputs = FIELDS.map((field) => { const label = document.createElement('label'); label.textContent = field.name; const input = document.createElement('input'); input.type = INPUT_TYPES[field.kind]; input.name = field.name; if (field.kind !== 'boolean') input.required = true; label.append(input); byId('fields').append(label); return input; }); byId('add').addEventListener('submit', async (event) => { event.preventDefault(); const row = Object.fromEntries(FIELDS.map((field, index) => [field.name, valueOf(field, inputs[index])])); const response = await fetch(ROUTE, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(row) }); const error = byId('add-error'); error.hidden = response.status === 201; if (response.status !== 201) { error.textContent = `The row was not added (status ${response.status}).`; return; } byId('add').reset(); await showRows(); }); } function buildAsk() { byId('ask').addEventListener('submit', async (event) => { event.preventDefault(); const answer = byId('answer'); answer.textContent = 'Asking.'; const response = await fetch('/api/ask', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ question: byId('question').value }) }); if (response.status === 200) answer.textContent = (await response.json()).answer; else if (response.status === 503) answer.textContent = UNANSWERED[(await response.json()).error] ?? 'The AI model gave no answer now.'; else answer.textContent = `The question was not answered (status ${response.status}).`; }); } async function start() { const user = await signedInUser(); if (user === undefined) return; if (user === null) { byId('who').textContent = 'No one is signed in.'; byId('sign-in').hidden = false; return; } const address = user.addresses?.find((held) => held.verified)?.email; byId('who').textContent = `Signed in as ${address ?? user.id}.`; byId('sign-out').hidden = false; byId('rows-title').textContent = `Your ${PLURAL}`; byId('signed-in').hidden = false; buildForm(); buildAsk(); await showRows(); } start();