@reddoorla/maintenance 0.72.0 → 0.75.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/dist/airtable-6L3JD7FY.js +9 -0
- package/dist/{announce-HJCGEVJA.js → announce-V6EYPPUR.js} +4 -4
- package/dist/{blux-FKZYMKLF.js → blux-C24OVDDW.js} +1044 -69
- package/dist/blux-C24OVDDW.js.map +1 -0
- package/dist/{bump-deps-GZEJAUKI.js → bump-deps-OQR3IXLN.js} +2 -2
- package/dist/chunk-2OP4JKIQ.js +19 -0
- package/dist/chunk-2OP4JKIQ.js.map +1 -0
- package/dist/{chunk-BJCA7O4A.js → chunk-3PU2QWOJ.js} +2 -2
- package/dist/{chunk-GI4M2AMO.js → chunk-4BHNP6Y2.js} +2 -2
- package/dist/{chunk-KRHPS3R4.js → chunk-6OYICQSJ.js} +47 -3
- package/dist/chunk-6OYICQSJ.js.map +1 -0
- package/dist/{chunk-BWKK2ZXS.js → chunk-GXVX4HUH.js} +32 -2
- package/dist/chunk-GXVX4HUH.js.map +1 -0
- package/dist/{chunk-KLB2OLPW.js → chunk-HBFMTM2E.js} +3 -3
- package/dist/{chunk-SQJ53Z7R.js → chunk-I55YWV6T.js} +3 -3
- package/dist/{chunk-7P7YETHG.js → chunk-IDBMYDVH.js} +2 -2
- package/dist/{chunk-JGLAWXUC.js → chunk-JR67FZVC.js} +7 -3
- package/dist/chunk-JR67FZVC.js.map +1 -0
- package/dist/{chunk-APTAITXZ.js → chunk-KKL3RYFQ.js} +2 -2
- package/dist/{chunk-NRISG4XD.js → chunk-NUIBQPB3.js} +42 -11
- package/dist/chunk-NUIBQPB3.js.map +1 -0
- package/dist/{chunk-BHE5CUWI.js → chunk-PERP7LSS.js} +269 -18
- package/dist/chunk-PERP7LSS.js.map +1 -0
- package/dist/chunk-RHPDQW6D.js +29 -0
- package/dist/chunk-RHPDQW6D.js.map +1 -0
- package/dist/{chunk-HR5SONJC.js → chunk-SK44IFZN.js} +2 -2
- package/dist/cli/bin.js +31 -19
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.js +6 -6
- package/dist/{convert-to-pnpm-SVFDLXZS.js → convert-to-pnpm-OVMASATZ.js} +2 -2
- package/dist/{digest-VX7Q4MNF.js → digest-LEMZS5ZK.js} +99 -7
- package/dist/digest-LEMZS5ZK.js.map +1 -0
- package/dist/{ensure-site-O7G5ZJS7.js → ensure-site-3SOAWTZF.js} +2 -2
- package/dist/forms/index.js +1 -1
- package/dist/{github-signals-QVW2LB7N.js → github-signals-TF3VSNIN.js} +4 -4
- package/dist/{health-endpoint-IYR5VBUT.js → health-endpoint-JZGYCMTV.js} +2 -2
- package/dist/index.d.ts +104 -19
- package/dist/index.js +122 -30
- package/dist/index.js.map +1 -1
- package/dist/{init-CU6PVZRG.js → init-7PTP4O2K.js} +4 -4
- package/dist/{launch-JWXCTJRG.js → launch-W33FWWOZ.js} +6 -6
- package/dist/{lighthouse-airtable-J5TLASJC.js → lighthouse-airtable-ZXNMDEPE.js} +3 -3
- package/dist/{onboard-6UQE5QIO.js → onboard-PX36YDZA.js} +2 -2
- package/dist/{orchestrate-ZDH24Z6Y.js → orchestrate-NYRYEKQM.js} +3 -3
- package/dist/{preflight-BNTMWTPG.js → preflight-JCQFKB6M.js} +4 -4
- package/dist/{renovate-dispatch-C3ALLACX.js → renovate-dispatch-6II2JVOX.js} +29 -27
- package/dist/renovate-dispatch-6II2JVOX.js.map +1 -0
- package/dist/{report-YLFSGNWR.js → report-POHQEV6F.js} +21 -5
- package/dist/report-POHQEV6F.js.map +1 -0
- package/dist/screenouts-7P5GOZMV.js +57 -0
- package/dist/screenouts-7P5GOZMV.js.map +1 -0
- package/dist/{self-updating-64FNRQB5.js → self-updating-2YSVNMPP.js} +2 -2
- package/dist/{selftest-7DPNGL6R.js → selftest-OO2WDSDM.js} +5 -5
- package/dist/{smoke-suite-TWILWDSF.js → smoke-suite-CSP23NZM.js} +2 -2
- package/dist/spam-classifier-VG4ICIWS.js +217 -0
- package/dist/spam-classifier-VG4ICIWS.js.map +1 -0
- package/dist/submissions-BNRGY3JQ.js +122 -0
- package/dist/submissions-BNRGY3JQ.js.map +1 -0
- package/dist/submissions-MDKWJYMM.js +281 -0
- package/dist/submissions-MDKWJYMM.js.map +1 -0
- package/dist/{svelte-codemods-RVDTTXFD.js → svelte-codemods-V54KOBWU.js} +2 -2
- package/dist/{sync-configs-BUUD6L5Q.js → sync-configs-UBCJRASZ.js} +2 -2
- package/dist/{upgrade-HHKWYJRN.js → upgrade-WR6ICPAG.js} +2 -2
- package/dist/{websites-EH2ULELZ.js → websites-U6642XSE.js} +10 -2
- package/dist/{write-audits-to-airtable-LZDYTNX5.js → write-audits-to-airtable-22T6D733.js} +4 -4
- package/package.json +1 -1
- package/dist/airtable-U2GTDTK5.js +0 -9
- package/dist/blux-FKZYMKLF.js.map +0 -1
- package/dist/chunk-BHE5CUWI.js.map +0 -1
- package/dist/chunk-BWKK2ZXS.js.map +0 -1
- package/dist/chunk-JGLAWXUC.js.map +0 -1
- package/dist/chunk-KRHPS3R4.js.map +0 -1
- package/dist/chunk-NRISG4XD.js.map +0 -1
- package/dist/chunk-QTHKOIFJ.js +0 -13
- package/dist/chunk-QTHKOIFJ.js.map +0 -1
- package/dist/digest-VX7Q4MNF.js.map +0 -1
- package/dist/renovate-dispatch-C3ALLACX.js.map +0 -1
- package/dist/report-YLFSGNWR.js.map +0 -1
- /package/dist/{airtable-U2GTDTK5.js.map → airtable-6L3JD7FY.js.map} +0 -0
- /package/dist/{announce-HJCGEVJA.js.map → announce-V6EYPPUR.js.map} +0 -0
- /package/dist/{bump-deps-GZEJAUKI.js.map → bump-deps-OQR3IXLN.js.map} +0 -0
- /package/dist/{chunk-BJCA7O4A.js.map → chunk-3PU2QWOJ.js.map} +0 -0
- /package/dist/{chunk-GI4M2AMO.js.map → chunk-4BHNP6Y2.js.map} +0 -0
- /package/dist/{chunk-KLB2OLPW.js.map → chunk-HBFMTM2E.js.map} +0 -0
- /package/dist/{chunk-SQJ53Z7R.js.map → chunk-I55YWV6T.js.map} +0 -0
- /package/dist/{chunk-7P7YETHG.js.map → chunk-IDBMYDVH.js.map} +0 -0
- /package/dist/{chunk-APTAITXZ.js.map → chunk-KKL3RYFQ.js.map} +0 -0
- /package/dist/{chunk-HR5SONJC.js.map → chunk-SK44IFZN.js.map} +0 -0
- /package/dist/{convert-to-pnpm-SVFDLXZS.js.map → convert-to-pnpm-OVMASATZ.js.map} +0 -0
- /package/dist/{ensure-site-O7G5ZJS7.js.map → ensure-site-3SOAWTZF.js.map} +0 -0
- /package/dist/{github-signals-QVW2LB7N.js.map → github-signals-TF3VSNIN.js.map} +0 -0
- /package/dist/{health-endpoint-IYR5VBUT.js.map → health-endpoint-JZGYCMTV.js.map} +0 -0
- /package/dist/{init-CU6PVZRG.js.map → init-7PTP4O2K.js.map} +0 -0
- /package/dist/{launch-JWXCTJRG.js.map → launch-W33FWWOZ.js.map} +0 -0
- /package/dist/{lighthouse-airtable-J5TLASJC.js.map → lighthouse-airtable-ZXNMDEPE.js.map} +0 -0
- /package/dist/{onboard-6UQE5QIO.js.map → onboard-PX36YDZA.js.map} +0 -0
- /package/dist/{orchestrate-ZDH24Z6Y.js.map → orchestrate-NYRYEKQM.js.map} +0 -0
- /package/dist/{preflight-BNTMWTPG.js.map → preflight-JCQFKB6M.js.map} +0 -0
- /package/dist/{self-updating-64FNRQB5.js.map → self-updating-2YSVNMPP.js.map} +0 -0
- /package/dist/{selftest-7DPNGL6R.js.map → selftest-OO2WDSDM.js.map} +0 -0
- /package/dist/{smoke-suite-TWILWDSF.js.map → smoke-suite-CSP23NZM.js.map} +0 -0
- /package/dist/{svelte-codemods-RVDTTXFD.js.map → svelte-codemods-V54KOBWU.js.map} +0 -0
- /package/dist/{sync-configs-BUUD6L5Q.js.map → sync-configs-UBCJRASZ.js.map} +0 -0
- /package/dist/{upgrade-HHKWYJRN.js.map → upgrade-WR6ICPAG.js.map} +0 -0
- /package/dist/{websites-EH2ULELZ.js.map → websites-U6642XSE.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-LZDYTNX5.js.map → write-audits-to-airtable-22T6D733.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import "./chunk-KRHPS3R4.js";
|
|
2
|
-
import {
|
|
3
|
-
approveBlockers
|
|
4
|
-
} from "./chunk-KLB2OLPW.js";
|
|
5
1
|
import {
|
|
6
2
|
draftReportForSite
|
|
7
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NUIBQPB3.js";
|
|
8
4
|
import "./chunk-KLVMJYZZ.js";
|
|
5
|
+
import "./chunk-RHPDQW6D.js";
|
|
6
|
+
import "./chunk-6OYICQSJ.js";
|
|
7
|
+
import {
|
|
8
|
+
approveBlockers
|
|
9
|
+
} from "./chunk-HBFMTM2E.js";
|
|
9
10
|
import {
|
|
10
11
|
findDueReports
|
|
11
12
|
} from "./chunk-D4FVAIEJ.js";
|
|
@@ -16,7 +17,7 @@ import {
|
|
|
16
17
|
DEFAULT_INIT_STEPS,
|
|
17
18
|
a11yFixturesPage,
|
|
18
19
|
init
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KKL3RYFQ.js";
|
|
20
21
|
import {
|
|
21
22
|
convertToPnpm
|
|
22
23
|
} from "./chunk-2X322ZES.js";
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
parseAddresses,
|
|
37
38
|
sendApprovedReports,
|
|
38
39
|
withGlobalCc
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-IDBMYDVH.js";
|
|
40
41
|
import "./chunk-GECESMVS.js";
|
|
41
42
|
import {
|
|
42
43
|
BLURRED_CID,
|
|
@@ -54,7 +55,9 @@ import "./chunk-WEKL2HYJ.js";
|
|
|
54
55
|
import {
|
|
55
56
|
bumpDeps
|
|
56
57
|
} from "./chunk-D3DQFC32.js";
|
|
57
|
-
import
|
|
58
|
+
import {
|
|
59
|
+
isLeadFormType
|
|
60
|
+
} from "./chunk-2OP4JKIQ.js";
|
|
58
61
|
import {
|
|
59
62
|
syncConfigs
|
|
60
63
|
} from "./chunk-JNXCDUYB.js";
|
|
@@ -70,7 +73,7 @@ import {
|
|
|
70
73
|
runAudits,
|
|
71
74
|
runAuditsAcross,
|
|
72
75
|
securityAudit
|
|
73
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-PERP7LSS.js";
|
|
74
77
|
import "./chunk-GASBX52O.js";
|
|
75
78
|
import "./chunk-YEGHUODU.js";
|
|
76
79
|
import "./chunk-LKGVSM2O.js";
|
|
@@ -86,12 +89,12 @@ import "./chunk-XXTZBPUY.js";
|
|
|
86
89
|
import "./chunk-XTK5VIQB.js";
|
|
87
90
|
import {
|
|
88
91
|
fromAirtableBase
|
|
89
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-SK44IFZN.js";
|
|
90
93
|
import "./chunk-U5SZ4FOP.js";
|
|
91
94
|
import {
|
|
92
95
|
SEVERITY_RANK,
|
|
93
96
|
siteSlug
|
|
94
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-GXVX4HUH.js";
|
|
95
98
|
import "./chunk-HN6I2XWI.js";
|
|
96
99
|
import "./chunk-6VAL7XJT.js";
|
|
97
100
|
import "./chunk-EYODPUHV.js";
|
|
@@ -183,7 +186,16 @@ function extraFieldsList(raw) {
|
|
|
183
186
|
function isVisibleInStrip(s) {
|
|
184
187
|
return s.status !== "spam_auto";
|
|
185
188
|
}
|
|
186
|
-
function
|
|
189
|
+
function spamReasonTokens(reason) {
|
|
190
|
+
return (reason ?? "").split(",").map((t) => t.trim()).filter((t) => t !== "");
|
|
191
|
+
}
|
|
192
|
+
var REASON_CHIP_MAX_TOKENS = 3;
|
|
193
|
+
function reasonChipText(tokens) {
|
|
194
|
+
const shown = tokens.slice(0, REASON_CHIP_MAX_TOKENS).join(" \xB7 ");
|
|
195
|
+
const more = tokens.length - REASON_CHIP_MAX_TOKENS;
|
|
196
|
+
return more > 0 ? `${shown} +${more} more` : shown;
|
|
197
|
+
}
|
|
198
|
+
function renderSubmissionRowInner(s) {
|
|
187
199
|
const when = s.submittedAt ? escapeHtml(relativeTimeFromNow(s.submittedAt)) : "\u2014";
|
|
188
200
|
const type = escapeHtml(s.formType);
|
|
189
201
|
const who = escapeHtml(s.name || "(no name)");
|
|
@@ -192,28 +204,35 @@ function renderSubmissionRow(s) {
|
|
|
192
204
|
const id = escapeHtml(s.id);
|
|
193
205
|
const url = `/api/submissions/${encodeURIComponent(s.id)}/status`;
|
|
194
206
|
const btn = (label, action) => `<button class="subm-status" data-id="${id}" data-status="${action}" data-url="${url}">${label}</button>`;
|
|
195
|
-
const
|
|
207
|
+
const reasonTokens = spamReasonTokens(s.spamReason);
|
|
208
|
+
const reasonChip = reasonTokens.length > 0 ? ` <span class="subm-reasons">${escapeHtml(reasonChipText(reasonTokens))}</span>` : "";
|
|
209
|
+
const provenance = s.status === "spam_auto" && s.spamScore !== null && s.spamScore !== void 0 ? ` <span class="subm-provenance" title="${escapeHtml(s.spamReason ?? "")}">auto-spam \xB7 ${escapeHtml(String(s.spamScore))}</span>${reasonChip}` : "";
|
|
196
210
|
const recover = s.status === "spam_auto" ? btn("Not spam \u2192 new", "new") : "";
|
|
211
|
+
const bounceChip = s.notifyStatus === "bounced" ? ` <span class="subm-bounce" title="The lead notification email bounced \u2014 the point-of-contact address may be dead">notify bounced</span>` : "";
|
|
197
212
|
const kv = (label, value) => value === null || value === "" ? "" : `<div class="subm-kv"><span class="k">${label}</span> ${escapeHtml(String(value))}</div>`;
|
|
198
213
|
const sourceLink = s.sourceUrl ? `<div class="subm-kv"><span class="k">Source</span> <a href="${escapeHtml(safeUrl(s.sourceUrl))}" rel="noopener noreferrer">${escapeHtml(s.sourceUrl)}</a></div>` : "";
|
|
199
214
|
const messageBlock = s.message ? `<div class="subm-kv"><span class="k">Message</span></div><div class="subm-msg">${escapeHtml(s.message)}</div>` : "";
|
|
215
|
+
const hasScore = s.spamScore !== null && s.spamScore !== void 0;
|
|
216
|
+
const spamDetail = hasScore ? reasonTokens.length > 0 ? `score ${s.spamScore} \u2014 ${reasonTokens.join(", ")}` : `score ${s.spamScore}` : reasonTokens.join(", ");
|
|
200
217
|
const details = [
|
|
201
218
|
kv("Phone", s.phone),
|
|
202
219
|
messageBlock,
|
|
203
220
|
sourceLink,
|
|
204
221
|
kv("UTM", s.utm),
|
|
205
222
|
extraFieldsList(s.extraFields),
|
|
223
|
+
kv("Spam", spamDetail),
|
|
206
224
|
kv("Notify", s.notifyStatus),
|
|
207
225
|
kv("Resend ID", s.resendMessageId),
|
|
208
226
|
kv("Submission #", s.submissionId)
|
|
209
227
|
].join("");
|
|
210
|
-
return `<
|
|
211
|
-
|
|
212
|
-
<summary class="subm-head"><strong>${type}</strong> \xB7 ${who} <span class="muted">${email}</span> <span class="pill subm-${status}">${status}</span>${provenance} <span class="muted">${when}</span></summary>
|
|
228
|
+
return `<details>
|
|
229
|
+
<summary class="subm-head"><strong>${type}</strong> \xB7 ${who} <span class="muted">${email}</span> <span class="pill subm-${status}">${status}</span>${provenance}${bounceChip} <span class="muted">${when}</span></summary>
|
|
213
230
|
<div class="subm-detail">${details}</div>
|
|
214
231
|
</details>
|
|
215
|
-
<div class="subm-actions">${recover}${btn("Read", "read")}${btn("Archive", "archived")}${btn("Spam", "spam")}</div
|
|
216
|
-
|
|
232
|
+
<div class="subm-actions">${recover}${btn("Read", "read")}${btn("Archive", "archived")}${btn("Spam", "spam")}</div>`;
|
|
233
|
+
}
|
|
234
|
+
function renderSubmissionRow(s) {
|
|
235
|
+
return `<li class="subm-item">${renderSubmissionRowInner(s)}</li>`;
|
|
217
236
|
}
|
|
218
237
|
var SUBMISSION_STYLES = `.subm-list { list-style: none; padding: 0; margin: 0; }
|
|
219
238
|
.subm-item { padding: 0.6rem 0; border-bottom: 1px solid #eee; }
|
|
@@ -224,9 +243,10 @@ var SUBMISSION_STYLES = `.subm-list { list-style: none; padding: 0; margin: 0; }
|
|
|
224
243
|
.subm-kv { font-size: 0.9rem; margin: 0.15rem 0; }
|
|
225
244
|
.subm-kv .k { color: #888; margin-right: 0.4rem; }
|
|
226
245
|
summary.subm-head { cursor: pointer; }
|
|
227
|
-
.subm-actions { display: flex; gap: 0.4rem; }
|
|
246
|
+
.subm-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
|
|
228
247
|
button.subm-status { font: inherit; padding: 0.25rem 0.7rem; border: 1px solid #888; border-radius: 6px; background: transparent; color: inherit; cursor: pointer; }
|
|
229
248
|
button.subm-status:disabled { opacity: 0.6; cursor: default; }
|
|
249
|
+
@media (pointer: coarse) { button.subm-status { padding: 0.55rem 0.9rem; } } /* >=40px tap target on touch devices */
|
|
230
250
|
.spam-screen .spam-kv { font-size: 0.95rem; margin: 0.2rem 0; }
|
|
231
251
|
.spam-screen .spam-kv .k { color: #888; display: inline-block; min-width: 11rem; }
|
|
232
252
|
.pill.subm-new { background: #e8f0fe; color: #1a56db; }
|
|
@@ -235,6 +255,8 @@ button.subm-status:disabled { opacity: 0.6; cursor: default; }
|
|
|
235
255
|
.pill.subm-spam { background: #fdecea; color: #b00; }
|
|
236
256
|
.pill.subm-spam_auto { background: #fff4e5; color: #a65a00; }
|
|
237
257
|
.subm-provenance { font-size: 0.72rem; border-radius: 0.25rem; padding: 0 0.35rem; white-space: nowrap; background: #fff4e5; color: #a65a00; }
|
|
258
|
+
.subm-bounce { font-size: 0.72rem; border-radius: 0.25rem; padding: 0 0.35rem; white-space: nowrap; background: #fdecea; color: #b00; font-weight: 700; }
|
|
259
|
+
.subm-reasons { font-size: 0.72rem; color: #999; }
|
|
238
260
|
.subm-viewall { font-size: 0.8rem; font-weight: normal; margin-left: 0.4rem; white-space: nowrap; }`;
|
|
239
261
|
var SUBMISSION_STATUS_SCRIPT = `document.querySelectorAll("button.subm-status").forEach((b) => {
|
|
240
262
|
b.addEventListener("click", async () => {
|
|
@@ -474,16 +496,20 @@ function submissionsSection(submissions, site) {
|
|
|
474
496
|
var SPAM_WINDOW_DAYS = 30;
|
|
475
497
|
function spamScreenSection(totals, submissions, now) {
|
|
476
498
|
const sinceMs = now.getTime() - SPAM_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
|
|
477
|
-
const
|
|
499
|
+
const inWindow = submissions.filter(
|
|
478
500
|
(s) => s.submittedAt !== null && Date.parse(s.submittedAt) >= sinceMs
|
|
479
|
-
)
|
|
501
|
+
);
|
|
502
|
+
const delivered = inWindow.filter((s) => s.status !== "spam_auto" && s.status !== "spam").length;
|
|
503
|
+
const autoFiltered = inWindow.filter((s) => s.status === "spam_auto").length;
|
|
480
504
|
const t = totals ?? { honeypot: 0, tooFast: 0, markedSpam: 0 };
|
|
481
|
-
if (delivered === 0 && t.honeypot === 0 && t.tooFast === 0 && t.markedSpam === 0)
|
|
505
|
+
if (delivered === 0 && autoFiltered === 0 && t.honeypot === 0 && t.tooFast === 0 && t.markedSpam === 0)
|
|
506
|
+
return "";
|
|
482
507
|
const row = (label, n) => `<div class="spam-kv"><span class="k">${label}</span> ${escapeHtml(String(n))}</div>`;
|
|
483
508
|
return `<div class="section spam-screen">
|
|
484
509
|
<h2>Spam screen (30d)</h2>
|
|
485
510
|
${row("Caught \u2014 honeypot", t.honeypot)}
|
|
486
511
|
${row("Caught \u2014 too-fast", t.tooFast)}
|
|
512
|
+
${row("Auto-filtered", autoFiltered)}
|
|
487
513
|
${row("Delivered", delivered)}
|
|
488
514
|
${row("Marked spam", t.markedSpam)}
|
|
489
515
|
</div>`;
|
|
@@ -494,6 +520,26 @@ function setupSection(site) {
|
|
|
494
520
|
const detail = missing.length === 0 ? `<span class="setup-ok">complete</span>` : `<span class="setup-missing">Missing: ${escapeHtml(missing.join(", "))}</span>`;
|
|
495
521
|
return `<div class="setup-line">Setup ${score}/${total} \u2014 ${detail}</div>`;
|
|
496
522
|
}
|
|
523
|
+
function alarmSection(alarm) {
|
|
524
|
+
if (!alarm) return "";
|
|
525
|
+
const chips2 = [];
|
|
526
|
+
for (const it of alarm.items) {
|
|
527
|
+
const cls = it.autoFixExhausted ? "chip critical stuck" : it.severity === "critical" ? "chip critical" : "chip";
|
|
528
|
+
chips2.push(`<span class="${cls}">${escapeHtml(it.title)}</span>`);
|
|
529
|
+
}
|
|
530
|
+
alarm.watchReasons.forEach((reason, i) => {
|
|
531
|
+
const key = alarm.watchAcceptKeys[i];
|
|
532
|
+
const hint = key ? ` \xB7 accept: "${escapeHtml(key)}"` : "";
|
|
533
|
+
chips2.push(
|
|
534
|
+
`<span class="chip" title="Add this exact text to the site's Accepted Watch Conditions to mute this watch">${escapeHtml(reason)}${hint}</span>`
|
|
535
|
+
);
|
|
536
|
+
});
|
|
537
|
+
for (const reason of alarm.acceptedReasons)
|
|
538
|
+
chips2.push(`<span class="chip accepted">\u2713 accepted: ${escapeHtml(reason)}</span>`);
|
|
539
|
+
if (chips2.length === 0) return "";
|
|
540
|
+
const label = alarm.tier === "attention" ? "Needs attention" : alarm.tier === "watch" ? "Watch" : "Status";
|
|
541
|
+
return `<div class="section alarm"><h2>${label}</h2><div class="chips">${chips2.join("")}</div></div>`;
|
|
542
|
+
}
|
|
497
543
|
function detailRow(label, value) {
|
|
498
544
|
const display = typeof value === "string" && value.trim().length > 0 ? escapeHtml(value.trim()) : DASH;
|
|
499
545
|
return `<div class="detail"><dt>${escapeHtml(label)}</dt><dd>${display}</dd></div>`;
|
|
@@ -659,8 +705,16 @@ button.override-submit:disabled { opacity: 0.6; cursor: default; }
|
|
|
659
705
|
.detail dd textarea { min-height: 3.5rem; resize: vertical; }
|
|
660
706
|
.detail-saved { font-size: 0.8rem; color: #2a8; }
|
|
661
707
|
button.trigger-renovate { font: inherit; font-size: 0.8rem; padding: 0.15rem 0.6rem; margin-left: 0.5rem; border: 1px solid #888; border-radius: 6px; background: transparent; color: inherit; cursor: pointer; }
|
|
708
|
+
/* Alarm chip strip \u2014 mirrors fleet-render.ts chip styles (NOT the page's .pill classes). */
|
|
709
|
+
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
|
|
710
|
+
.chip { font-size: 0.8rem; padding: 0.1rem 0.5rem; border-radius: 6px; background: #f0f0f0; }
|
|
711
|
+
@media (prefers-color-scheme: dark) { .chip { background: #222; } }
|
|
712
|
+
.chip.critical { background: #fdecea; color: #b00; }
|
|
713
|
+
.chip.stuck { border: 1px solid #b00; font-weight: 600; }
|
|
714
|
+
.chip.accepted { background: transparent; border: 1px dashed #bbb; color: #888; }
|
|
715
|
+
@media (prefers-color-scheme: dark) { .chip.accepted { border-color: #444; } .chip.critical { background: #2a0f0d; color: #ff8a80; } }
|
|
662
716
|
`;
|
|
663
|
-
function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = null, now = /* @__PURE__ */ new Date()) {
|
|
717
|
+
function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = null, now = /* @__PURE__ */ new Date(), alarm = null) {
|
|
664
718
|
const name = escapeHtml(site.name);
|
|
665
719
|
const urlSafe = safeUrl(site.url);
|
|
666
720
|
const allScoresNull = site.pScore === null && site.rScore === null && site.bpScore === null && site.seoScore === null;
|
|
@@ -698,6 +752,7 @@ function renderSiteDashboardHtml(site, reports, submissions = [], spamTotals = n
|
|
|
698
752
|
<div class="meta"><a href="${escapeHtml(urlSafe)}">${escapeHtml(site.url)}</a></div>
|
|
699
753
|
${auditedLine}
|
|
700
754
|
${setupSection(site)}
|
|
755
|
+
${alarmSection(alarm)}
|
|
701
756
|
${pendingSection(reports, site, now)}
|
|
702
757
|
|
|
703
758
|
<div class="section">
|
|
@@ -907,6 +962,13 @@ function buildNeedsYouFeed(model) {
|
|
|
907
962
|
a.reasons.push(`${p.reportType} ${p.period} ready`);
|
|
908
963
|
a.approval = true;
|
|
909
964
|
}
|
|
965
|
+
for (const u of model.unrecognizedStatus ?? []) {
|
|
966
|
+
const a = get(u.name);
|
|
967
|
+
a.reasons.push(
|
|
968
|
+
`Status "${u.status}" is not a recognized value \u2014 fix it in Airtable (site is invisible to fleet ops)`
|
|
969
|
+
);
|
|
970
|
+
a.watch = true;
|
|
971
|
+
}
|
|
910
972
|
const items = [];
|
|
911
973
|
for (const a of bySlug.values()) {
|
|
912
974
|
if (a.reasons.length === 0) continue;
|
|
@@ -1024,8 +1086,11 @@ function deployBadge(site) {
|
|
|
1024
1086
|
return `<span class="metric ${cls}">${label}</span>`;
|
|
1025
1087
|
}
|
|
1026
1088
|
function submBadge(c) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1089
|
+
const leads = c.newLeads ?? c.newSubmissions ?? 0;
|
|
1090
|
+
const signups = c.newSignups ?? 0;
|
|
1091
|
+
if (leads === 0 && signups === 0) return "";
|
|
1092
|
+
const label = leads > 0 && signups > 0 ? `\u{1F4E5} ${leads} new \xB7 +${signups} newsletter` : leads > 0 ? `\u{1F4E5} ${leads} new` : `\u{1F4E5} +${signups} newsletter`;
|
|
1093
|
+
return `<span class="chip">${label}</span>`;
|
|
1029
1094
|
}
|
|
1030
1095
|
var PILL_LABEL = {
|
|
1031
1096
|
attention: "failing",
|
|
@@ -1043,8 +1108,14 @@ function chips(c) {
|
|
|
1043
1108
|
const cls = it.autoFixExhausted ? "chip critical stuck" : it.severity === "critical" ? "chip critical" : "chip";
|
|
1044
1109
|
return `<span class="${cls}">${attentionBadge(it.status)}${escapeHtml(it.title)}</span>`;
|
|
1045
1110
|
});
|
|
1046
|
-
|
|
1047
|
-
|
|
1111
|
+
const acceptKeys = c.watchAcceptKeys ?? [];
|
|
1112
|
+
c.watchReasons.forEach((reason, i) => {
|
|
1113
|
+
const key = acceptKeys[i];
|
|
1114
|
+
const hint = key ? ` \xB7 accept: "${escapeHtml(key)}"` : "";
|
|
1115
|
+
items.push(
|
|
1116
|
+
`<span class="chip" title="Add this exact text to the site's Accepted Watch Conditions to mute this watch">${escapeHtml(reason)}${hint}</span>`
|
|
1117
|
+
);
|
|
1118
|
+
});
|
|
1048
1119
|
for (const reason of c.acceptedReasons)
|
|
1049
1120
|
items.push(`<span class="chip accepted">\u2713 accepted: ${escapeHtml(reason)}</span>`);
|
|
1050
1121
|
return items.length ? `<div class="chips">${items.join("")}</div>` : "";
|
|
@@ -1228,6 +1299,8 @@ function verdictBar(model, feed) {
|
|
|
1228
1299
|
const { broken, watch, approval } = needsYouCounts(feed);
|
|
1229
1300
|
const preLaunch = model.summary.preLaunch;
|
|
1230
1301
|
const preLaunchTerm = preLaunch > 0 ? `${preLaunch} pre-launch` : null;
|
|
1302
|
+
const archivedCount = model.archived?.length ?? 0;
|
|
1303
|
+
const archivedTerm = archivedCount > 0 ? `${archivedCount} archived` : null;
|
|
1231
1304
|
const healthy = Math.max(0, total - (broken + watch + approval + preLaunch));
|
|
1232
1305
|
const actions = `<div class="fleet-actions">
|
|
1233
1306
|
<button type="button" class="refresh-fleet" data-refresh-url="/api/fleet/refresh">\u21BB Audit fleet</button>
|
|
@@ -1240,7 +1313,7 @@ function verdictBar(model, feed) {
|
|
|
1240
1313
|
</div>`;
|
|
1241
1314
|
if (broken === 0 && watch === 0 && approval === 0) {
|
|
1242
1315
|
const healthyWord = healthy > 0 ? `${healthy} site${healthy === 1 ? "" : "s"} healthy` : null;
|
|
1243
|
-
return render("ok", "\u2713 All clear", [healthyWord, preLaunchTerm, auditedTerm]);
|
|
1316
|
+
return render("ok", "\u2713 All clear", [healthyWord, preLaunchTerm, archivedTerm, auditedTerm]);
|
|
1244
1317
|
}
|
|
1245
1318
|
const watchTerm = watch > 0 ? `${watch} watching` : null;
|
|
1246
1319
|
const approvalTerm = approval > 0 ? `${approval} waiting on you` : null;
|
|
@@ -1251,6 +1324,7 @@ function verdictBar(model, feed) {
|
|
|
1251
1324
|
approvalTerm,
|
|
1252
1325
|
healthyTerm,
|
|
1253
1326
|
preLaunchTerm,
|
|
1327
|
+
archivedTerm,
|
|
1254
1328
|
auditedTerm
|
|
1255
1329
|
]);
|
|
1256
1330
|
}
|
|
@@ -1259,10 +1333,16 @@ function verdictBar(model, feed) {
|
|
|
1259
1333
|
approvalTerm,
|
|
1260
1334
|
healthyTerm,
|
|
1261
1335
|
preLaunchTerm,
|
|
1336
|
+
archivedTerm,
|
|
1262
1337
|
auditedTerm
|
|
1263
1338
|
]);
|
|
1264
1339
|
}
|
|
1265
|
-
return render("soft", `${approval} waiting on you`, [
|
|
1340
|
+
return render("soft", `${approval} waiting on you`, [
|
|
1341
|
+
healthyTerm,
|
|
1342
|
+
preLaunchTerm,
|
|
1343
|
+
archivedTerm,
|
|
1344
|
+
auditedTerm
|
|
1345
|
+
]);
|
|
1266
1346
|
}
|
|
1267
1347
|
var NEEDS_YOU_GROUP_LABEL = {
|
|
1268
1348
|
broken: "Broken",
|
|
@@ -1316,6 +1396,14 @@ function renderRecentlyLane(model) {
|
|
|
1316
1396
|
${rows}
|
|
1317
1397
|
</details>`;
|
|
1318
1398
|
}
|
|
1399
|
+
function renderArchivedLane(model) {
|
|
1400
|
+
const rows = model.archived ?? [];
|
|
1401
|
+
if (rows.length === 0) return "";
|
|
1402
|
+
const lis = rows.map(
|
|
1403
|
+
(s) => `<div class="approve-row"><strong>${escapeHtml(s.name)}</strong> <span class="muted">${escapeHtml(s.status)}</span> <a href="/s/${escapeHtml(s.slug)}">open \u25B8</a></div>`
|
|
1404
|
+
).join("");
|
|
1405
|
+
return `<details class="archived"><summary>\u{1F5C4} Archived (${rows.length})</summary>${lis}</details>`;
|
|
1406
|
+
}
|
|
1319
1407
|
function renderInboxLane(model) {
|
|
1320
1408
|
const subs = model.submissions ?? [];
|
|
1321
1409
|
const spam = model.spam;
|
|
@@ -1340,8 +1428,11 @@ function renderInboxLane(model) {
|
|
|
1340
1428
|
const spamLine = hasSpam ? `<div class="spam-rollup muted">\u{1F6E1} Spam (30d) \u2014 caught ${spam.caught} \xB7 through ${spam.through}</div>` : "";
|
|
1341
1429
|
const spamInSummary = hasSpam ? " \xB7 \u{1F6E1} Spam (30d)" : "";
|
|
1342
1430
|
const autoFilteredLine = hasAutoFiltered ? `<div class="spam-rollup muted">\u{1F6AB} ${autoFiltered} auto-filtered this week \u2014 <a href="/submissions?status=spam_auto">review</a></div>` : "";
|
|
1431
|
+
const leads = subs.filter((s) => isLeadFormType(s.formType)).length;
|
|
1432
|
+
const signups = subs.length - leads;
|
|
1433
|
+
const summaryCount = signups === 0 ? `${leads} new` : `${leads} new \xB7 +${signups} newsletter/rsvp`;
|
|
1343
1434
|
return `<details class="inbox">
|
|
1344
|
-
<summary>\u{1F4E5} Submissions (${
|
|
1435
|
+
<summary>\u{1F4E5} Submissions (${summaryCount})${spamInSummary}</summary>
|
|
1345
1436
|
${rows}${subs.length > 0 ? more : ""}
|
|
1346
1437
|
${spamLine}
|
|
1347
1438
|
${autoFilteredLine}
|
|
@@ -1484,6 +1575,7 @@ function renderCockpitHtml(model) {
|
|
|
1484
1575
|
${renderNeedsYouFeed(feed)}
|
|
1485
1576
|
${renderFleetBrowsePanel(model)}
|
|
1486
1577
|
${renderRecentlyLane(model)}
|
|
1578
|
+
${renderArchivedLane(model)}
|
|
1487
1579
|
${renderInboxLane(model)}
|
|
1488
1580
|
${AUDIT_SCRIPT}
|
|
1489
1581
|
${FLEET_BROWSE_SCRIPT}
|