// Team Grants client. (function () { 'use strict'; var $ = function (id) { return document.getElementById(id); }; function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function n(v) { return Number(v || 0).toLocaleString(); } var state = { tool: null, status: null, level: 0, minLevel: 7 }; function gate(msg) { $('gGate').style.display = 'block'; $('gGate').innerHTML = msg; } // Shown to everyone, including people below level 7 — being helped should be // visible, not silent. function renderReceived(received) { var keys = Object.keys(received || {}); if (!keys.length) { $('gGot').innerHTML = ''; return; } var parts = keys.map(function (k) { var r = received[k]; // by:0 is an admin bonus from the team, not a member grant. var who = r.from.map(function (f) { return Number(f.by) === 0 ? 'the team' : '#' + f.by; }); var uniq = who.filter(function (v, i) { return who.indexOf(v) === i; }); var t = (state.status && state.status.tools[k]) || {}; return '
· ' + n(r.total) + ' ' + esc(t.unit || k) + ' of ' + esc((t.label || k).toLowerCase()) + ', from ' + uniq.join(', ') + '
'; }); $('gGot').innerHTML = '
You have been topped up this month.' + parts.join('') + '
' + 'It is already added to your allowances — nothing to claim.
'; } function renderPools() { var host = $('gPools'); host.innerHTML = ''; var tools = state.status.tools; Object.keys(tools).forEach(function (k) { var t = tools[k]; var d = document.createElement('div'); d.className = 'g-pool' + (state.tool === k ? ' on' : ''); d.innerHTML = '
' + n(t.remaining) + '
' + '
' + esc(t.label) + '
' + '
left to give of ' + n(t.pool) + '
'; d.addEventListener('click', function () { state.tool = k; renderPools(); var el = $('gN'); el.max = Math.min(t.max, t.remaining); el.step = t.step; el.value = Math.min(t.step, t.remaining) || ''; $('gNHint').textContent = 'Up to ' + n(Math.min(t.max, t.remaining)) + ' ' + t.unit + ' in one go. You have ' + n(t.remaining) + ' left to give this month.'; }); host.appendChild(d); }); } function renderHistory(hist) { if (!hist || !hist.length) { $('gHistWrap').style.display = 'none'; return; } $('gHistWrap').style.display = 'block'; var tb = $('gHist'); tb.innerHTML = ''; hist.forEach(function (h) { var t = (state.status.tools[h.tool]) || {}; var tr = document.createElement('tr'); tr.innerHTML = '#' + esc(h.to) + '' + '' + esc(t.label || h.tool) + '' + '' + n(h.n) + ' ' + esc(t.unit || '') + '' + '' + esc(String(h.at || '').slice(0, 10)) + ''; tb.appendChild(tr); }); } async function give() { $('gErr').style.display = 'none'; $('gOk').style.display = 'none'; if (!state.tool) { $('gErr').textContent = 'Pick what you want to give first.'; $('gErr').style.display = 'block'; return; } var to = Number($('gTo').value) || 0; var amount = Number($('gN').value) || 0; if (!to) { $('gErr').textContent = 'Enter the position number you want to help.'; $('gErr').style.display = 'block'; return; } $('gGo').disabled = true; $('gGo').innerHTML = 'Sending…'; try { var r = await fetch('/api/public/suite-grants', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: to, tool: state.tool, n: amount }) }); var d = await r.json(); if (!r.ok) { $('gErr').textContent = d.error || 'That did not go through.'; $('gErr').style.display = 'block'; } else { state.status = d.status; var t = state.status.tools[d.granted.tool] || {}; $('gOk').innerHTML = '✅ Sent. #' + esc(d.granted.to) + ' now has ' + n(d.granted.n) + ' more ' + esc(t.unit || '') + ' this month. Worth telling them — it will just appear in their allowance otherwise.'; $('gOk').style.display = 'block'; renderPools(); renderHistory(state.status.history); } } catch (e) { $('gErr').textContent = 'Connection hiccup — try again.'; $('gErr').style.display = 'block'; } $('gGo').disabled = false; $('gGo').innerHTML = '🎁 Give it to them'; } async function boot() { try { var r = await fetch('/api/public/suite-grants'); 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.status = d.status; state.level = d.level; state.minLevel = d.minLevel; renderReceived(d.status.received); if (!d.status.canGrant) { gate('Team Grants unlock at Vertex (level 7). At that point you can hand your own Suite capacity ' + 'down to anyone in your organisation who needs it. Anything granted to you shows above and is already in your allowances.'); return; } $('gBody').style.display = 'block'; var first = Object.keys(d.status.tools)[0]; state.tool = first; renderPools(); var t = d.status.tools[first]; $('gN').max = Math.min(t.max, t.remaining); $('gN').step = t.step; $('gN').value = Math.min(t.step, t.remaining) || ''; $('gNHint').textContent = 'Up to ' + n(Math.min(t.max, t.remaining)) + ' ' + t.unit + ' in one go.'; renderHistory(d.status.history); } catch (e) {} } document.addEventListener('DOMContentLoaded', function () { boot(); $('gGo').addEventListener('click', give); }); })();