// Voice Profile client. Fields come from the server so the form and the stored // shape can never drift apart. (function () { 'use strict'; var $ = function (id) { return document.getElementById(id); }; var state = { fields: [], profile: {} }; function gate(msg) { $('vGate').style.display = 'block'; $('vGate').innerHTML = msg; $('vCard').style.display = 'none'; $('vState').style.display = 'none'; } function renderFields() { var host = $('vFields'); host.innerHTML = ''; state.fields.forEach(function (f) { var wrap = document.createElement('div'); wrap.className = 'v-f'; var lab = document.createElement('label'); lab.setAttribute('for', 'vf_' + f.key); lab.textContent = f.label; wrap.appendChild(lab); var hint = document.createElement('div'); hint.className = 'hint'; hint.textContent = f.hint; wrap.appendChild(hint); var ta = document.createElement('textarea'); ta.id = 'vf_' + f.key; ta.rows = f.max > 400 ? 6 : 2; ta.maxLength = f.max; ta.value = state.profile[f.key] || ''; wrap.appendChild(ta); var count = document.createElement('div'); count.className = 'v-count'; var upd = function () { count.textContent = ta.value.length + ' / ' + f.max; }; ta.addEventListener('input', upd); upd(); wrap.appendChild(count); host.appendChild(wrap); }); } function showState(p) { var el = $('vState'); el.style.display = 'block'; var filled = state.fields.filter(function (f) { return String((p || {})[f.key] || '').trim().length > 2; }).length; if (p && p.complete) { el.className = 'v-state'; el.innerHTML = '✅ Your voice profile is on. The Copy Engine and Email Engine are both writing with it — ' + filled + ' of ' + state.fields.length + ' answers filled in.'; } else { el.className = 'v-state off'; el.innerHTML = 'Not active yet. Fill in at least a couple of these — especially the writing sample, ' + 'which teaches it more than the rest combined — and everything the Suite writes will start sounding like you.'; } } function collect() { var out = {}; state.fields.forEach(function (f) { var el = $('vf_' + f.key); out[f.key] = el ? el.value : ''; }); return out; } async function save() { $('vErr').style.display = 'none'; $('vOk').style.display = 'none'; $('vSave').disabled = true; var prev = $('vSave').textContent; $('vSave').textContent = 'Saving…'; try { var r = await fetch('/api/public/suite-voice', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(collect()) }); var d = await r.json(); if (!r.ok) { $('vErr').textContent = d.error || 'That did not save — try again.'; $('vErr').style.display = 'block'; } else { state.profile = d.profile; showState(d.profile); $('vOk').innerHTML = d.profile.complete ? '✅ Saved. Head to the Copy Engine and write something — it should sound noticeably more like you.' : '✅ Saved. Add a bit more (the writing sample especially) and it will switch on.'; $('vOk').style.display = 'block'; } } catch (e) { $('vErr').textContent = 'Connection hiccup — try again.'; $('vErr').style.display = 'block'; } $('vSave').disabled = false; $('vSave').textContent = prev; } async function clear() { if (!window.confirm('Clear your voice profile? The Suite will go back to writing in the team voice.')) return; try { var r = await fetch('/api/public/suite-voice', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ clear: true }) }); var d = await r.json(); if (r.ok) { state.profile = d.profile; renderFields(); showState(d.profile); $('vOk').innerHTML = 'Cleared. Back to the team voice.'; $('vOk').style.display = 'block'; } } catch (e) {} } async function boot() { try { var r = await fetch('/api/public/suite-voice'); if (r.status === 401) { gate('You’re not signed in yet. Open the Suite and connect the wallet that holds your position, then come back.'); return; } if (r.status === 403) { var g = await r.json(); gate((g.error || 'Not open for this position yet.') + ' See your Suite.'); return; } if (!r.ok) return; var d = await r.json(); state.fields = d.fields || []; state.profile = d.profile || {}; renderFields(); showState(state.profile); } catch (e) {} } document.addEventListener('DOMContentLoaded', function () { boot(); $('vSave').addEventListener('click', save); $('vClear').addEventListener('click', clear); }); })();