Family board: choosing a goal from the chore picker returns to it with the new progress bar
build-container / build (push) Canceled after 0s
build-container / build (push) Canceled after 0s
This commit is contained in:
1 parent
32db6256e8
commit
6ba9992e55
1 file changed
+6
-6
+6
-6
@@ -12,11 +12,11 @@ function weekCard(w) {
|
||||
return w.people.map(p => { const mine = w.tiers.filter(t => p.tiers.includes(t.id)), goal = mine.find(t => t.id === p.goal), top = Math.max(1, goal ? goal.points : 0, p.points); return `<div style="margin-top:8px"><div style="display:flex;justify-content:space-between"><b>${esc(p.name)}</b><small class="sub">${p.points} pts · ${p.count} done</small></div><div class="bar2" style="position:relative">${goal ? `<span style="position:absolute;top:0;left:${Math.min(100, Math.round(goal.points / top * 100))}%;width:2px;height:100%;background:var(--text);opacity:.5"></span>` : ''}<i style="width:${Math.round(p.points / top * 100)}%"></i></div>${goal ? `<small>${p.points >= goal.points ? '🎯 ' + esc(goal.title) + ' reached! 🎉' : '🎯 ' + (goal.points - p.points) + ' more for ' + esc(goal.title)}</small>` : mine.length ? `<button type="button" class="btn ghost" data-goalfor="${esc(p.id)}" style="margin-top:4px;min-height:40px">Choose a reward</button>` : ''}</div>`; }).join('');
|
||||
}
|
||||
// ---- Choose a reward to aim for: pick it from a pop-up, then PIN (unless a child with no PIN), and it is set as that person
|
||||
function goalFlow(pid) {
|
||||
function goalFlow(pid, back) {
|
||||
const p = data.people.find(x => x.id === pid), w = data.week; if (!p || !w) return; const mine = w.tiers.filter(t => ((w.people.find(x => x.id === pid) || {}).tiers || []).includes(t.id));
|
||||
const { box, close } = sheet(`<h3 style="margin:0 0 4px">${esc(p.name.split(' ')[0])}, what are you aiming for?</h3><p class="sub" style="margin:0 0 8px">Pick one reward for this week.</p><div class="list" style="text-align:left">${mine.map(t => `<button class="row" data-g="${esc(t.id)}" style="width:100%"><span class="grow"><b>${esc(t.title)}</b><small>${t.points} points in a week${t.minutes ? ' · +' + t.minutes + ' min screen time' : ''}</small></span></button>`).join('')}</div><div class="err" id="e"></div><p><button class="btn ghost" id="no" style="width:100%;justify-content:center">Cancel</button></p>`);
|
||||
box.querySelector('#no').onclick = close;
|
||||
const send = async (reward, pin) => { const r = await fetch('/api/board/goal', { method: 'POST', body: JSON.stringify({ uid: p.id, pin, reward, date: ymd(new Date()) }) }), j = await r.json().catch(() => ({})); if (!r.ok) throw new Error(j.error || 'That did not work'); close(); await load(); toast('Goal chosen'); };
|
||||
const send = async (reward, pin) => { const r = await fetch('/api/board/goal', { method: 'POST', body: JSON.stringify({ uid: p.id, pin, reward, date: ymd(new Date()) }) }), j = await r.json().catch(() => ({})); if (!r.ok) throw new Error(j.error || 'That did not work'); const wp = data.week && data.week.people.find(x => x.id === p.id); if (wp) wp.goal = reward || null; close(); await load(); if (back) back(pin); else toast('Goal chosen'); };
|
||||
box.querySelectorAll('[data-g]').forEach(b => b.onclick = () => { const reward = b.dataset.g;
|
||||
if (p.kid && !p.pin) return send(reward).catch(e => { box.querySelector('#e').textContent = e.message; });
|
||||
if (!p.pin) { box.querySelector('#e').textContent = p.name + ' has not set a PIN yet. Set one in Settings on your own device.'; return; }
|
||||
@@ -115,8 +115,8 @@ function whoThen(title, payload, doneMsg) {
|
||||
function toast(msg) { let t = document.getElementById('bt'); if (!t) { t = document.createElement('div'); t.id = 'bt'; t.style.cssText = 'position:fixed;left:50%;bottom:24px;transform:translateX(-50%);background:var(--panel2);border:1px solid var(--line);padding:12px 20px;border-radius:20px;font-size:18px;z-index:99'; document.body.appendChild(t); } t.textContent = msg; t.hidden = false; clearTimeout(t._t); t._t = setTimeout(() => { t.hidden = true; }, 2500); }
|
||||
|
||||
// ---- Pick your chores: tap your name, enter your PIN once (a child with no PIN goes straight in), then do, take or put back as many chores as you like
|
||||
function choreHub(p) {
|
||||
if (!p) return; let pin = '';
|
||||
function choreHub(p, pin0) {
|
||||
if (!p) return; let pin = pin0 || '';
|
||||
const { box, close } = sheet(''), today = ymd(new Date()), head = `<div style="display:flex;align-items:center;gap:10px;margin-bottom:6px"><span class="av" style="background:${p.color};width:44px;height:44px;border-radius:50%;color:#fff;display:grid;place-items:center;font-size:20px;font-weight:700;flex:none">${esc((p.name.trim()[0] || '?').toUpperCase())}</span><h3 style="margin:0;text-align:left">${esc(p.name.split(' ')[0])}'s chores</h3></div>`;
|
||||
const act = async (c, action) => { const r = await fetch('/api/board/chore', { method: 'POST', body: JSON.stringify({ uid: p.id, pin, chore: c.chore, action, date: today }) }), j = await r.json().catch(() => ({})); if (!r.ok) { if (p.pin && /pin/i.test(j.error || '')) { pin = ''; return askPin(j.error); } box.querySelector('#e').textContent = j.error || 'That did not work'; return; } await load(); paint(); };
|
||||
const paint = () => {
|
||||
@@ -128,7 +128,7 @@ function choreHub(p) {
|
||||
+ (pool.length ? h('Up for grabs. Take one!') + pool.map(c => row(c, `<button class="btn ghost" data-a="claim" data-c="${esc(c.chore)}" data-u="${esc(c.user || '')}">Take it</button><button class="btn" data-a="done" data-c="${esc(c.chore)}" data-u="${esc(c.user || '')}">I did it</button>`)).join('') : '')
|
||||
+ (done.length ? h('Finished') + done.map(c => row(c, `<button class="btn ghost" data-a="undo" data-c="${esc(c.chore)}" data-u="${esc(c.user || '')}">Undo</button>`, 'dn')).join('') : '')
|
||||
+ `</div><div class="err" id="e"></div><div style="display:flex;gap:10px;margin-top:10px"><button class="btn ghost" id="sgn" style="flex:1;justify-content:center">Open my account</button><button class="btn" id="no" style="flex:1;justify-content:center">Close</button></div>`;
|
||||
const pg = box.querySelector('#pgl'); if (pg) pg.onclick = e => { e.preventDefault(); close(); goalFlow(p.id); };
|
||||
const pg = box.querySelector('#pgl'); if (pg) pg.onclick = e => { e.preventDefault(); close(); goalFlow(p.id, pn => choreHub(p, pn)); };
|
||||
box.querySelector('#no').onclick = close; box.querySelector('#sgn').onclick = () => { close(); pick(p); };
|
||||
box.querySelectorAll('[data-a]').forEach(b => b.onclick = () => { b.disabled = true; const c = data.chores.find(x => x.chore === b.dataset.c && (x.user || '') === b.dataset.u); if (c) act(c, b.dataset.a); else { b.disabled = false; load().then(paint); } });
|
||||
};
|
||||
@@ -137,5 +137,5 @@ function choreHub(p) {
|
||||
const dots = () => { box.querySelector('#dots').innerHTML = Array.from({ length: Math.max(4, pin.length) }, (_, i) => `<i class="${i < pin.length ? 'on' : ''}"></i>`).join(''); }; dots(); box.querySelector('#no').onclick = close;
|
||||
box.querySelectorAll('.pad button').forEach(b => b.onclick = () => { let k = b.dataset.k; box.querySelector('#e').textContent = ''; if (k === '⌫') pin = pin.slice(0, -1); else if (k !== '✓' && pin.length < 8) { pin += k; if (p.pinLen && pin.length === p.pinLen) k = '✓'; } if (k === '✓') { if (pin) return paint(); } dots(); });
|
||||
};
|
||||
if (p.kid && !p.pin) return paint(); if (!p.pin) { box.innerHTML = head + `<p>${esc(p.name)} has not set a PIN yet. Set one in Settings on your own device.</p><button class="btn ghost" id="no" style="width:100%;justify-content:center">OK</button>`; box.querySelector('#no').onclick = close; return; } askPin();
|
||||
if (p.kid && !p.pin || pin0) return paint(); if (!p.pin) { box.innerHTML = head + `<p>${esc(p.name)} has not set a PIN yet. Set one in Settings on your own device.</p><button class="btn ghost" id="no" style="width:100%;justify-content:center">OK</button>`; box.querySelector('#no').onclick = close; return; } askPin();
|
||||
}
|
||||
Reference in new issue
Block a user