@reddoorla/maintenance 0.67.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/airtable-WGQQ3KAD.js +9 -0
- package/dist/{announce-74ZIAV26.js → announce-72R3LRNT.js} +9 -11
- package/dist/{announce-74ZIAV26.js.map → announce-72R3LRNT.js.map} +1 -1
- package/dist/{bump-deps-HYBYNPMY.js → bump-deps-QNM3H4GI.js} +2 -2
- package/dist/{chunk-YKOOAWVL.js → chunk-2DCA7FHZ.js} +21 -15
- package/dist/chunk-2DCA7FHZ.js.map +1 -0
- package/dist/{chunk-LCKKIJ22.js → chunk-42KKVLYN.js} +3 -3
- package/dist/{chunk-VKAYZVMI.js → chunk-45AUGDFR.js} +2 -1
- package/dist/chunk-45AUGDFR.js.map +1 -0
- package/dist/{chunk-WCN54744.js → chunk-6G7BC5SN.js} +4 -4
- package/dist/{chunk-SYTPVYIK.js → chunk-6JFOAPW4.js} +241 -4
- package/dist/chunk-6JFOAPW4.js.map +1 -0
- package/dist/{chunk-Y4O44LZB.js → chunk-6MT5W6UF.js} +2 -2
- package/dist/{chunk-OAMJINM5.js → chunk-A5KYRGQA.js} +4 -4
- package/dist/{chunk-JDGIO54P.js → chunk-AESJBEM7.js} +43 -3
- package/dist/chunk-AESJBEM7.js.map +1 -0
- package/dist/{chunk-CL5DYUWQ.js → chunk-C3PDS623.js} +3 -1
- package/dist/chunk-C3PDS623.js.map +1 -0
- package/dist/chunk-E2GKKUEX.js +322 -0
- package/dist/chunk-E2GKKUEX.js.map +1 -0
- package/dist/{chunk-KN5OC5YH.js → chunk-GKSXZIMX.js} +3 -3
- package/dist/{chunk-XVR7UM6Z.js → chunk-JNXCDUYB.js} +4 -4
- package/dist/{chunk-ULL4AD3Y.js → chunk-O6G4HHMF.js} +7 -9
- package/dist/{chunk-ULL4AD3Y.js.map → chunk-O6G4HHMF.js.map} +1 -1
- package/dist/{chunk-6LIWDIXU.js → chunk-RINXPU3H.js} +2 -2
- package/dist/{chunk-T34EUM5B.js → chunk-XWPZ46IN.js} +2 -2
- package/dist/{chunk-7MJJEEII.js → chunk-YQMYQAJX.js} +2 -2
- package/dist/cli/bin.js +32 -14
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +8 -8
- package/dist/client/index.d.ts +84 -0
- package/dist/client/index.js +47 -0
- package/dist/client/index.js.map +1 -0
- package/dist/{convert-to-pnpm-5MPZWFHM.js → convert-to-pnpm-XH7NYRVK.js} +2 -2
- package/dist/{digest-MECMZPFE.js → digest-Q2TAQHXF.js} +17 -8
- package/dist/digest-Q2TAQHXF.js.map +1 -0
- package/dist/ensure-site-2PF522VL.js +87 -0
- package/dist/ensure-site-2PF522VL.js.map +1 -0
- package/dist/{github-signals-VWBMQNLH.js → github-signals-CLQC2IPY.js} +7 -7
- package/dist/index.d.ts +7 -1
- package/dist/index.js +103 -37
- package/dist/index.js.map +1 -1
- package/dist/{init-X3BCJAUG.js → init-UA6D7GKW.js} +7 -7
- package/dist/{launch-NQCL5K5H.js → launch-AENAS2O2.js} +15 -17
- package/dist/{launch-NQCL5K5H.js.map → launch-AENAS2O2.js.map} +1 -1
- package/dist/{lighthouse-airtable-QUNOSDT5.js → lighthouse-airtable-UBSXKHAQ.js} +3 -3
- package/dist/{onboard-7JOBZY35.js → onboard-X6KJ5Z2V.js} +2 -2
- package/dist/{orchestrate-JQX72RJE.js → orchestrate-GADNFGDH.js} +5 -7
- package/dist/preflight-D5RQGX4V.js +64 -0
- package/dist/preflight-D5RQGX4V.js.map +1 -0
- package/dist/recipes/sync-configs.js +2 -2
- package/dist/{renovate-dispatch-PWXFZNHN.js → renovate-dispatch-LG4RJGDY.js} +2 -2
- package/dist/{report-QUKNKUUE.js → report-3FJ224H5.js} +8 -9
- package/dist/{report-QUKNKUUE.js.map → report-3FJ224H5.js.map} +1 -1
- package/dist/{self-updating-OKJKU2FE.js → self-updating-JTTFOJ7Q.js} +2 -2
- package/dist/{selftest-6JVYR24F.js → selftest-55OJIWEB.js} +10 -12
- package/dist/{selftest-6JVYR24F.js.map → selftest-55OJIWEB.js.map} +1 -1
- package/dist/{svelte-codemods-GG6EEES5.js → svelte-codemods-KJLNH6FQ.js} +2 -2
- package/dist/{sync-configs-UFHZZPL7.js → sync-configs-TIBO6DUJ.js} +4 -4
- package/dist/{upgrade-S25N3473.js → upgrade-TPTE26FG.js} +2 -2
- package/dist/{websites-U3UZS5VG.js → websites-6YTT5ZTO.js} +2 -2
- package/dist/{write-audits-to-airtable-GAJUCSTY.js → write-audits-to-airtable-IWOO4FFH.js} +4 -4
- package/package.json +5 -1
- package/dist/airtable-33SY2UBZ.js +0 -9
- package/dist/chunk-CL5DYUWQ.js.map +0 -1
- package/dist/chunk-JDGIO54P.js.map +0 -1
- package/dist/chunk-OCS7SZUZ.js +0 -243
- package/dist/chunk-OCS7SZUZ.js.map +0 -1
- package/dist/chunk-OGGRFBL4.js +0 -14
- package/dist/chunk-OGGRFBL4.js.map +0 -1
- package/dist/chunk-SYTPVYIK.js.map +0 -1
- package/dist/chunk-VKAYZVMI.js.map +0 -1
- package/dist/chunk-YKOOAWVL.js.map +0 -1
- package/dist/digest-MECMZPFE.js.map +0 -1
- /package/dist/{airtable-33SY2UBZ.js.map → airtable-WGQQ3KAD.js.map} +0 -0
- /package/dist/{bump-deps-HYBYNPMY.js.map → bump-deps-QNM3H4GI.js.map} +0 -0
- /package/dist/{chunk-LCKKIJ22.js.map → chunk-42KKVLYN.js.map} +0 -0
- /package/dist/{chunk-WCN54744.js.map → chunk-6G7BC5SN.js.map} +0 -0
- /package/dist/{chunk-Y4O44LZB.js.map → chunk-6MT5W6UF.js.map} +0 -0
- /package/dist/{chunk-OAMJINM5.js.map → chunk-A5KYRGQA.js.map} +0 -0
- /package/dist/{chunk-KN5OC5YH.js.map → chunk-GKSXZIMX.js.map} +0 -0
- /package/dist/{chunk-XVR7UM6Z.js.map → chunk-JNXCDUYB.js.map} +0 -0
- /package/dist/{chunk-6LIWDIXU.js.map → chunk-RINXPU3H.js.map} +0 -0
- /package/dist/{chunk-T34EUM5B.js.map → chunk-XWPZ46IN.js.map} +0 -0
- /package/dist/{chunk-7MJJEEII.js.map → chunk-YQMYQAJX.js.map} +0 -0
- /package/dist/{convert-to-pnpm-5MPZWFHM.js.map → convert-to-pnpm-XH7NYRVK.js.map} +0 -0
- /package/dist/{github-signals-VWBMQNLH.js.map → github-signals-CLQC2IPY.js.map} +0 -0
- /package/dist/{init-X3BCJAUG.js.map → init-UA6D7GKW.js.map} +0 -0
- /package/dist/{lighthouse-airtable-QUNOSDT5.js.map → lighthouse-airtable-UBSXKHAQ.js.map} +0 -0
- /package/dist/{onboard-7JOBZY35.js.map → onboard-X6KJ5Z2V.js.map} +0 -0
- /package/dist/{orchestrate-JQX72RJE.js.map → orchestrate-GADNFGDH.js.map} +0 -0
- /package/dist/{renovate-dispatch-PWXFZNHN.js.map → renovate-dispatch-LG4RJGDY.js.map} +0 -0
- /package/dist/{self-updating-OKJKU2FE.js.map → self-updating-JTTFOJ7Q.js.map} +0 -0
- /package/dist/{svelte-codemods-GG6EEES5.js.map → svelte-codemods-KJLNH6FQ.js.map} +0 -0
- /package/dist/{sync-configs-UFHZZPL7.js.map → sync-configs-TIBO6DUJ.js.map} +0 -0
- /package/dist/{upgrade-S25N3473.js.map → upgrade-TPTE26FG.js.map} +0 -0
- /package/dist/{websites-U3UZS5VG.js.map → websites-6YTT5ZTO.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-GAJUCSTY.js.map → write-audits-to-airtable-IWOO4FFH.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-AESJBEM7.js";
|
|
2
|
+
import {
|
|
3
|
+
approveBlockers
|
|
4
|
+
} from "./chunk-E2GKKUEX.js";
|
|
2
5
|
import {
|
|
3
6
|
findDueReports
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-45AUGDFR.js";
|
|
5
8
|
import {
|
|
6
9
|
upgradeSvelte4to5
|
|
7
10
|
} from "./chunk-ZO7WNY57.js";
|
|
@@ -9,7 +12,7 @@ import {
|
|
|
9
12
|
DEFAULT_INIT_STEPS,
|
|
10
13
|
a11yFixturesPage,
|
|
11
14
|
init
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-42KKVLYN.js";
|
|
13
16
|
import {
|
|
14
17
|
convertToPnpm
|
|
15
18
|
} from "./chunk-2X322ZES.js";
|
|
@@ -24,34 +27,35 @@ import {
|
|
|
24
27
|
} from "./chunk-ZYBOT5PM.js";
|
|
25
28
|
import {
|
|
26
29
|
draftReportForSite
|
|
27
|
-
} from "./chunk-
|
|
28
|
-
import "./chunk-
|
|
30
|
+
} from "./chunk-O6G4HHMF.js";
|
|
31
|
+
import "./chunk-RINXPU3H.js";
|
|
29
32
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
parseAddresses,
|
|
34
|
+
sendApprovedReports,
|
|
35
|
+
withGlobalCc
|
|
36
|
+
} from "./chunk-2DCA7FHZ.js";
|
|
33
37
|
import "./chunk-GECESMVS.js";
|
|
34
38
|
import {
|
|
35
39
|
BLURRED_CID,
|
|
36
40
|
CHECK_CID,
|
|
37
|
-
loadBundledImages,
|
|
38
|
-
renderReportHtml
|
|
39
|
-
} from "./chunk-SYTPVYIK.js";
|
|
40
|
-
import {
|
|
41
41
|
checklistFor,
|
|
42
42
|
escapeHtml,
|
|
43
43
|
isChecklistComplete,
|
|
44
44
|
isPendingApproval,
|
|
45
|
+
loadBundledImages,
|
|
46
|
+
renderReportHtml,
|
|
45
47
|
safeUrl
|
|
46
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-6JFOAPW4.js";
|
|
49
|
+
import "./chunk-WEKL2HYJ.js";
|
|
47
50
|
import {
|
|
48
51
|
bumpDeps
|
|
49
52
|
} from "./chunk-D3DQFC32.js";
|
|
53
|
+
import "./chunk-QTHKOIFJ.js";
|
|
50
54
|
import {
|
|
51
55
|
syncConfigs
|
|
52
|
-
} from "./chunk-
|
|
53
|
-
import "./chunk-XKOZUVK2.js";
|
|
56
|
+
} from "./chunk-JNXCDUYB.js";
|
|
54
57
|
import "./chunk-CASCWQQX.js";
|
|
58
|
+
import "./chunk-XKOZUVK2.js";
|
|
55
59
|
import "./chunk-OTGB6YQO.js";
|
|
56
60
|
import {
|
|
57
61
|
ALL_AUDIT_NAMES,
|
|
@@ -62,13 +66,13 @@ import {
|
|
|
62
66
|
runAudits,
|
|
63
67
|
runAuditsAcross,
|
|
64
68
|
securityAudit
|
|
65
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-6G7BC5SN.js";
|
|
70
|
+
import "./chunk-GASBX52O.js";
|
|
71
|
+
import "./chunk-YEGHUODU.js";
|
|
66
72
|
import "./chunk-LKGVSM2O.js";
|
|
67
73
|
import "./chunk-OTBA7DON.js";
|
|
68
74
|
import "./chunk-PH3AGF4Z.js";
|
|
69
75
|
import "./chunk-HIN3P4ZN.js";
|
|
70
|
-
import "./chunk-GASBX52O.js";
|
|
71
|
-
import "./chunk-YEGHUODU.js";
|
|
72
76
|
import {
|
|
73
77
|
fromJsonFile,
|
|
74
78
|
localPath
|
|
@@ -78,18 +82,16 @@ import "./chunk-XXTZBPUY.js";
|
|
|
78
82
|
import "./chunk-XTK5VIQB.js";
|
|
79
83
|
import {
|
|
80
84
|
fromAirtableBase
|
|
81
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-6MT5W6UF.js";
|
|
82
86
|
import "./chunk-U5SZ4FOP.js";
|
|
83
87
|
import {
|
|
84
88
|
SEVERITY_RANK,
|
|
85
89
|
siteSlug
|
|
86
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-C3PDS623.js";
|
|
87
91
|
import "./chunk-HN6I2XWI.js";
|
|
88
|
-
import "./chunk-WEKL2HYJ.js";
|
|
89
92
|
import "./chunk-6VAL7XJT.js";
|
|
90
93
|
import "./chunk-EYODPUHV.js";
|
|
91
94
|
import "./chunk-LYCKN7ZR.js";
|
|
92
|
-
import "./chunk-QTHKOIFJ.js";
|
|
93
95
|
|
|
94
96
|
// src/recipes/index.ts
|
|
95
97
|
var ALL_RECIPE_NAMES = [
|
|
@@ -327,21 +329,66 @@ function checklistBlock(r) {
|
|
|
327
329
|
}).join("");
|
|
328
330
|
return `<div class="checklist" data-checklist-for="${rid}">${boxes}</div>`;
|
|
329
331
|
}
|
|
330
|
-
function approveButton(r) {
|
|
331
|
-
const disabled = isChecklistComplete(r) ? "" : " disabled";
|
|
332
|
-
|
|
332
|
+
function approveButton(r, blocked) {
|
|
333
|
+
const disabled = isChecklistComplete(r) && !blocked ? "" : " disabled";
|
|
334
|
+
const blockedAttr = blocked ? ` data-send-blocked="1"` : "";
|
|
335
|
+
return `<button class="approve" data-report-id="${escapeHtml(r.id)}" data-approve-url="${escapeHtml(`/api/reports/${encodeURIComponent(r.id)}/approve`)}"${blockedAttr}${disabled}>Approve</button>`;
|
|
336
|
+
}
|
|
337
|
+
function preflightChip(findings) {
|
|
338
|
+
const fails = findings.filter((f) => f.level === "fail");
|
|
339
|
+
const warns = findings.filter((f) => f.level === "warn");
|
|
340
|
+
if (fails.length > 0) {
|
|
341
|
+
const title = escapeHtml(fails.map((f) => `${f.check}: ${f.message}`).join("\n"));
|
|
342
|
+
return `<span class="preflight preflight-fail" title="${title}">preflight \u2717 ${fails.length}</span>`;
|
|
343
|
+
}
|
|
344
|
+
if (warns.length > 0) {
|
|
345
|
+
const title = escapeHtml(warns.map((f) => `${f.check}: ${f.message}`).join("\n"));
|
|
346
|
+
return `<span class="preflight preflight-warn" title="${title}">preflight \u26A0 ${warns.length}</span>`;
|
|
347
|
+
}
|
|
348
|
+
return `<span class="preflight preflight-ok" title="recipients, header image and report scores all present">preflight \u2713</span>`;
|
|
349
|
+
}
|
|
350
|
+
function nextDailyRun(now) {
|
|
351
|
+
const run = new Date(now);
|
|
352
|
+
run.setUTCHours(9, 23, 0, 0);
|
|
353
|
+
if (run.getTime() <= now.getTime()) run.setUTCDate(run.getUTCDate() + 1);
|
|
354
|
+
return run;
|
|
333
355
|
}
|
|
334
|
-
function
|
|
356
|
+
function sendTimingLine(now) {
|
|
357
|
+
const minuteOfDay = now.getUTCHours() * 60 + now.getUTCMinutes();
|
|
358
|
+
const RUN_START = 9 * 60 + 23;
|
|
359
|
+
const GRACE_END = 10 * 60 + 30;
|
|
360
|
+
if (minuteOfDay >= RUN_START && minuteOfDay < GRACE_END) {
|
|
361
|
+
return `<span class="muted">today's 09:23 UTC run may still be in flight \u2014 approving now can send within minutes</span>`;
|
|
362
|
+
}
|
|
363
|
+
const run = nextDailyRun(now);
|
|
364
|
+
const leadMin = Math.floor((run.getTime() - now.getTime()) / 6e4);
|
|
365
|
+
const lead = leadMin >= 60 ? `~${Math.floor(leadMin / 60)}h` : leadMin >= 1 ? `~${leadMin} min` : "under 1 min";
|
|
366
|
+
return `<span class="muted">approve \u2260 send: goes out at the next daily run, 09:23 UTC (${lead})</span>`;
|
|
367
|
+
}
|
|
368
|
+
function recipientsLine(site) {
|
|
369
|
+
const to = parseAddresses(site.reportRecipientsTo) ?? parseAddresses(site.pointOfContact) ?? [];
|
|
370
|
+
if (to.length === 0) {
|
|
371
|
+
return `<span class="recipients recipients-missing">recipients: none resolve \u2014 set point of contact in Airtable</span>`;
|
|
372
|
+
}
|
|
373
|
+
const cc = withGlobalCc(parseAddresses(site.reportRecipientsCc), to);
|
|
374
|
+
const ccPart = cc.length > 0 ? ` \xB7 CC ${cc.map(escapeHtml).join(", ")}` : "";
|
|
375
|
+
return `<span class="recipients">To ${to.map(escapeHtml).join(", ")}${ccPart}</span>`;
|
|
376
|
+
}
|
|
377
|
+
function pendingRow(r, site, now) {
|
|
335
378
|
const type = escapeHtml(r.reportType);
|
|
336
379
|
const period = r.period ? escapeHtml(r.period) : "\u2014";
|
|
337
|
-
|
|
380
|
+
const findings = approveBlockers(site, r);
|
|
381
|
+
const blocked = findings.some((f) => f.level === "fail");
|
|
382
|
+
const preview = r.renderedHtmlAttachment ? `<a href="${escapeHtml(safeUrl(r.renderedHtmlAttachment.url))}" rel="noopener noreferrer" title="rendered at draft time \u2014 Commentary/subject edits after drafting are not reflected">draft preview \u25B8</a>` : `<span class="muted">no preview yet</span>`;
|
|
383
|
+
const sendLine = sendTimingLine(now);
|
|
384
|
+
return `<li><div class="pending-head"><strong>${type}</strong> <span class="muted">${period}</span> ${preflightChip(findings)} ${preview} ${approveButton(r, blocked)}</div><div class="pending-info">${recipientsLine(site)} ${sendLine}</div>${checklistBlock(r)}</li>`;
|
|
338
385
|
}
|
|
339
|
-
function pendingSection(reports) {
|
|
386
|
+
function pendingSection(reports, site, now) {
|
|
340
387
|
const pending = reports.filter(isPendingApproval);
|
|
341
388
|
if (pending.length === 0) return "";
|
|
342
389
|
return `<div class="section pending">
|
|
343
390
|
<h2>Pending your yes (${pending.length})</h2>
|
|
344
|
-
<ul class="pending-list">${pending.map(pendingRow).join("")}</ul>
|
|
391
|
+
<ul class="pending-list">${pending.map((r) => pendingRow(r, site, now)).join("")}</ul>
|
|
345
392
|
</div>`;
|
|
346
393
|
}
|
|
347
394
|
function gaUsersCell(r) {
|
|
@@ -358,14 +405,17 @@ function searchCell(r) {
|
|
|
358
405
|
}
|
|
359
406
|
return DASH;
|
|
360
407
|
}
|
|
361
|
-
function reportRow(r) {
|
|
408
|
+
function reportRow(r, site) {
|
|
362
409
|
const date = r.completedOn ? escapeHtml(r.completedOn) : DASH;
|
|
363
410
|
const type = escapeHtml(r.reportType);
|
|
364
411
|
const id = escapeHtml(r.reportId);
|
|
365
412
|
const ga = gaUsersCell(r);
|
|
366
413
|
const search = searchCell(r);
|
|
367
414
|
const link = r.renderedHtmlAttachment ? `<a href="${escapeHtml(safeUrl(r.renderedHtmlAttachment.url))}">view</a>` : `<span class="muted">no attachment</span>`;
|
|
368
|
-
const action = isPendingApproval(r) ? approveButton(
|
|
415
|
+
const action = isPendingApproval(r) ? approveButton(
|
|
416
|
+
r,
|
|
417
|
+
approveBlockers(site, r).some((f) => f.level === "fail")
|
|
418
|
+
) : "";
|
|
369
419
|
return `<tr><td>${date}</td><td>${type}</td><td><code>${id}</code></td><td>${ga}</td><td>${search}</td><td>${link}</td><td>${action}</td></tr>`;
|
|
370
420
|
}
|
|
371
421
|
var SUBMISSIONS_PER_SITE_CAP = 25;
|
|
@@ -471,10 +521,16 @@ th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #eee; }
|
|
|
471
521
|
.empty { color: #999; padding: 1rem; border: 1px dashed #ccc; border-radius: 6px; text-align: center; }
|
|
472
522
|
button.approve { font: inherit; padding: 0.35rem 0.85rem; border: 1px solid #2c7; border-radius: 6px; background: #2c7; color: #fff; cursor: pointer; }
|
|
473
523
|
button.approve:disabled { opacity: 0.6; cursor: default; }
|
|
524
|
+
.pending-info { display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; font-size: 0.82rem; margin: 0.3rem 0 0.15rem; }
|
|
525
|
+
.recipients-missing { color: #e57373; }
|
|
526
|
+
.preflight { font-size: 0.78rem; padding: 0.1rem 0.45rem; border-radius: 999px; white-space: nowrap; }
|
|
527
|
+
.preflight-ok { background: #1b5e2033; color: #7bc67e; }
|
|
528
|
+
.preflight-warn { background: #f9a82533; color: #d79921; }
|
|
529
|
+
.preflight-fail { background: #b71c1c33; color: #e57373; }
|
|
474
530
|
.pending-list { list-style: none; padding: 0; margin: 0; }
|
|
475
531
|
.pending-list li { padding: 0.5rem; border-bottom: 1px solid #eee; }
|
|
476
532
|
@media (prefers-color-scheme: dark) { .pending-list li { border-color: #2a2a2a; } }
|
|
477
|
-
.pending-head { display: flex; align-items: center; gap: 0.5rem; }
|
|
533
|
+
.pending-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; row-gap: 0.25rem; }
|
|
478
534
|
.checklist { display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; margin: 0.5rem 0 0.25rem 0.25rem; }
|
|
479
535
|
.check-item { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; }
|
|
480
536
|
.check-item input { margin: 0; }
|
|
@@ -524,7 +580,7 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
524
580
|
const recentReports = [...reports].sort((a, b) => (b.completedOn ?? "").localeCompare(a.completedOn ?? "")).slice(0, 6);
|
|
525
581
|
const reportsSection = recentReports.length === 0 ? `<div class="empty">No reports yet.</div>` : `<table>
|
|
526
582
|
<thead><tr><th>Completed</th><th>Type</th><th>ID</th><th>GA users</th><th>Search</th><th>Report</th><th></th></tr></thead>
|
|
527
|
-
<tbody>${recentReports.map(reportRow).join("")}</tbody>
|
|
583
|
+
<tbody>${recentReports.map((r) => reportRow(r, site)).join("")}</tbody>
|
|
528
584
|
</table>`;
|
|
529
585
|
return `<!doctype html>
|
|
530
586
|
<html lang="en">
|
|
@@ -541,7 +597,7 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
541
597
|
<div class="meta"><a href="${escapeHtml(urlSafe)}">${escapeHtml(site.url)}</a></div>
|
|
542
598
|
${auditedLine}
|
|
543
599
|
${setupSection(site)}
|
|
544
|
-
${pendingSection(reports)}
|
|
600
|
+
${pendingSection(reports, site, now)}
|
|
545
601
|
|
|
546
602
|
<div class="section">
|
|
547
603
|
<h2>Lighthouse</h2>
|
|
@@ -569,8 +625,18 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
569
625
|
b.disabled = true;
|
|
570
626
|
try {
|
|
571
627
|
const res = await fetch(b.dataset.approveUrl, { method: "POST" });
|
|
572
|
-
|
|
573
|
-
|
|
628
|
+
if (res.ok) {
|
|
629
|
+
b.textContent = "Approved";
|
|
630
|
+
} else {
|
|
631
|
+
// A 409 carries { reason, blockers } \u2014 surface WHY instead of a bare
|
|
632
|
+
// "Failed" next to a possibly-stale green chip. textContent/title
|
|
633
|
+
// assignment only (no innerHTML), so server strings stay inert.
|
|
634
|
+
const data = await res.json().catch(() => null);
|
|
635
|
+
b.textContent = data && data.reason === "send-blocked" ? "Blocked" : "Failed";
|
|
636
|
+
if (data && Array.isArray(data.blockers)) b.title = data.blockers.join("
|
|
637
|
+
");
|
|
638
|
+
b.disabled = false;
|
|
639
|
+
}
|
|
574
640
|
} catch {
|
|
575
641
|
// Network rejection (offline, DNS, abort): mirror the !res.ok path so
|
|
576
642
|
// the button doesn't sit permanently disabled reading "Approve".
|
|
@@ -638,7 +704,7 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
638
704
|
});
|
|
639
705
|
if (!res.ok) throw new Error("bad status");
|
|
640
706
|
const data = await res.json();
|
|
641
|
-
if (approveBtn) approveBtn.disabled = !data.complete;
|
|
707
|
+
if (approveBtn) approveBtn.disabled = !data.complete || approveBtn.dataset.sendBlocked === "1";
|
|
642
708
|
} catch {
|
|
643
709
|
// Revert the optimistic flip so the box reflects the (unchanged) server state.
|
|
644
710
|
cb.checked = !cb.checked;
|