185 files
This commit is contained in:
@@ -18,24 +18,6 @@ const PANEL_VERSION = (() => {
|
||||
return el ? sanitize(new URLSearchParams(el.src.split("?")[1] || "").get("v")) : "?";
|
||||
})();
|
||||
|
||||
const TABS = [
|
||||
{ id: "children", lk: "panel.tab_children" },
|
||||
{ id: "activity", lk: "panel.tab_activity" },
|
||||
{ id: "insights", lk: "panel.tab_insights" },
|
||||
{ id: "chores", lk: "panel.tab_chores" },
|
||||
{ id: "rewards", lk: "panel.tab_rewards" },
|
||||
{ id: "penalties", lk: "panel.tab_penalties" },
|
||||
{ id: "bonuses", lk: "panel.tab_bonuses" },
|
||||
{ id: "groups", lk: "panel.tab_groups" },
|
||||
{ id: "quests", lk: "panel.tab_quests" },
|
||||
{ id: "challenges", lk: "panel.tab_challenges" },
|
||||
{ id: "badges", lk: "panel.tab_badges" },
|
||||
{ id: "templates", lk: "panel.tab_templates" },
|
||||
{ id: "notifications", lk: "panel.tab_notifications" },
|
||||
{ id: "audit", lk: "panel.tab_audit" },
|
||||
{ id: "settings", lk: "panel.tab_settings", label: "⚙" },
|
||||
];
|
||||
|
||||
const BADGE_METRICS = [
|
||||
{ v: "total_points", lk: "badge.metric_total_points" },
|
||||
{ v: "total_chores", lk: "badge.metric_total_chores" },
|
||||
@@ -1181,12 +1163,29 @@ class TaskMatePanel extends HTMLElement {
|
||||
async _doApproveAll() {
|
||||
const pending = this._state?.pending_completions || [];
|
||||
if (pending.length === 0) return;
|
||||
if (this._approvingAll) return;
|
||||
if (!confirm(this._t("panel.activity_approve_all_confirm", { count: pending.length }))) return;
|
||||
const { ok, err } = await this._callWS({
|
||||
type: "taskmate/approve_all_chores",
|
||||
completion_ids: pending.map(c => c.id),
|
||||
});
|
||||
if (!ok) { this._showToast("err", this._t("panel.toast_approve_failed", { error: err })); return; }
|
||||
// Acknowledge the tap straight away (#799). The batch is fast since #794,
|
||||
// but not instant, and a button that looks untouched is indistinguishable
|
||||
// from one that did nothing — which is how #794 came to be reported.
|
||||
this._approvingAll = true;
|
||||
this._render();
|
||||
let result;
|
||||
try {
|
||||
result = await this._callWS({
|
||||
type: "taskmate/approve_all_chores",
|
||||
completion_ids: pending.map(c => c.id),
|
||||
});
|
||||
} finally {
|
||||
// Cleared the moment the call settles, whatever it did — a button stuck
|
||||
// spinning is worse than no busy state at all.
|
||||
this._approvingAll = false;
|
||||
}
|
||||
if (!result.ok) {
|
||||
this._showToast("err", this._t("panel.toast_approve_failed", { error: result.err }));
|
||||
this._render();
|
||||
return;
|
||||
}
|
||||
await this._fetchState();
|
||||
this._showToast("ok", this._t("panel.activity_approve_all_done", { count: pending.length }));
|
||||
}
|
||||
@@ -1531,6 +1530,10 @@ class TaskMatePanel extends HTMLElement {
|
||||
image_url: d.image_url || "",
|
||||
points: Number(d.points) || 0,
|
||||
assigned_to: d.assigned_to || [],
|
||||
// The dialog tracks this via toggle-depends, but it was never sent, so a
|
||||
// dependency could neither be saved nor cleared — _ws_update_chore only
|
||||
// applies fields present in the message (#793).
|
||||
depends_on: d.depends_on || [],
|
||||
requires_approval: !!d.requires_approval,
|
||||
time_category: d.time_category || "anytime",
|
||||
claim_allowance_minutes: Math.max(0, Number(d.claim_allowance_minutes) || 0),
|
||||
@@ -3120,7 +3123,9 @@ class TaskMatePanel extends HTMLElement {
|
||||
? `<span class="tm-pill tm-pill-warn">${pendingCompletions.length + pendingClaims.length + pendingSwaps.length}</span>`
|
||||
: `<span class="tm-pill tm-pill-success">${this._t("panel.activity_pending_all_clear")}</span>`}
|
||||
${pendingCompletions.length > 0
|
||||
? `<button type="button" class="tm-btn tm-btn-raised tm-btn-sm" data-act="approve-all-chores" style="margin-left:auto">${this._t("panel.activity_approve_all")}</button>`
|
||||
? `<button type="button" class="tm-btn tm-btn-raised tm-btn-sm" data-act="approve-all-chores" style="margin-left:auto"${this._approvingAll ? " disabled" : ""}>${this._approvingAll
|
||||
? `<span class="tm-btn-spinner"></span>${this._t("panel.activity_approve_all_busy")}`
|
||||
: this._t("panel.activity_approve_all")}</button>`
|
||||
: ""}
|
||||
</h3>
|
||||
${pendingCompletions.length === 0 && pendingClaims.length === 0 && pendingSwaps.length === 0 ? `
|
||||
@@ -3434,7 +3439,7 @@ class TaskMatePanel extends HTMLElement {
|
||||
const assigned = (q.assigned_to && q.assigned_to.length)
|
||||
? children.filter(c => q.assigned_to.includes(c.id)).map(c => c.name)
|
||||
: [this._t("panel.quest_all_children")];
|
||||
const stepNames = steps.map((sid, i) => {
|
||||
const stepNames = steps.map((sid) => {
|
||||
const name = choreById[sid] ? choreById[sid].name : this._t("panel.quest_missing_chore");
|
||||
return `<li>${this._esc(name)}</li>`;
|
||||
}).join("");
|
||||
@@ -6554,6 +6559,24 @@ class TaskMatePanel extends HTMLElement {
|
||||
background: var(--tm-accent-soft);
|
||||
border-color: var(--tm-accent);
|
||||
}
|
||||
.tm-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes tm-spin { to { transform: rotate(360deg); } }
|
||||
.tm-btn-spinner {
|
||||
width: 12px; height: 12px;
|
||||
border: 2px solid currentColor;
|
||||
border-top-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: tm-spin 0.7s linear infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Still reads as busy — the ring just doesn't rotate. */
|
||||
.tm-btn-spinner { animation: none; opacity: 0.7; }
|
||||
}
|
||||
.tm-btn-raised {
|
||||
background: var(--tm-accent);
|
||||
color: var(--text-primary-color, #fff);
|
||||
|
||||
Reference in New Issue
Block a user