@reddoorla/maintenance 0.42.0 → 0.43.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/cli/bin.js CHANGED
@@ -575,6 +575,39 @@ var init_write_audits_to_airtable = __esm({
575
575
  }
576
576
  });
577
577
 
578
+ // src/reports/checklist.ts
579
+ function checklistFor(type) {
580
+ return type === "Maintenance" ? MAINTENANCE_CHECKLIST : type === "Testing" ? TESTING_CHECKLIST : [];
581
+ }
582
+ function isChecklistComplete(report) {
583
+ return checklistFor(report.reportType).every((i) => report.checklist[i.field] === true);
584
+ }
585
+ var MAINTENANCE_CHECKLIST, TESTING_CHECKLIST, ALL_CHECKLIST_FIELDS;
586
+ var init_checklist = __esm({
587
+ "src/reports/checklist.ts"() {
588
+ "use strict";
589
+ MAINTENANCE_CHECKLIST = [
590
+ { key: "logs", label: "Reviewed Logs", field: "Maint: Reviewed Logs" },
591
+ { key: "cms", label: "CMS Checked", field: "Maint: CMS Checked" },
592
+ { key: "dns", label: "DNS Checked", field: "Maint: DNS Checked" },
593
+ { key: "google", label: "Google Indexed", field: "Maint: Google Indexed" },
594
+ { key: "cert", label: "Reviewed Certificate", field: "Maint: Reviewed Certificate" },
595
+ { key: "security", label: "Security Updates", field: "Maint: Security Updates" }
596
+ ];
597
+ TESTING_CHECKLIST = [
598
+ { key: "desktop", label: "Desktop Browsers", field: "Test: Desktop Browsers" },
599
+ { key: "mobile", label: "Mobile Browsers", field: "Test: Mobile Browsers" },
600
+ { key: "packages", label: "Package Updates", field: "Test: Package Updates" },
601
+ { key: "bottle", label: "Bottlenecks", field: "Test: Bottlenecks" },
602
+ { key: "forms", label: "Form Functionality", field: "Test: Form Functionality" },
603
+ { key: "animation", label: "Animation Functionality", field: "Test: Animation Functionality" }
604
+ ];
605
+ ALL_CHECKLIST_FIELDS = [...MAINTENANCE_CHECKLIST, ...TESTING_CHECKLIST].map(
606
+ (i) => i.field
607
+ );
608
+ }
609
+ });
610
+
578
611
  // src/reports/airtable/reports.ts
579
612
  function toReportType(raw) {
580
613
  if (raw && REPORT_TYPES.includes(raw)) return raw;
@@ -613,7 +646,8 @@ function mapRow2(rec) {
613
646
  approvedBy: f["Approved By"] ?? null,
614
647
  deliveryStatus: f["Delivery status"] ?? "pending",
615
648
  renderedHtmlAttachment: html,
616
- resendMessageId: f["Resend message ID"] ?? null
649
+ resendMessageId: f["Resend message ID"] ?? null,
650
+ checklist: Object.fromEntries(ALL_CHECKLIST_FIELDS.map((name) => [name, Boolean(f[name])]))
617
651
  };
618
652
  }
619
653
  function lighthouseFromFields(f) {
@@ -717,6 +751,7 @@ var REPORTS_TABLE, REPORT_TYPES;
717
751
  var init_reports = __esm({
718
752
  "src/reports/airtable/reports.ts"() {
719
753
  "use strict";
754
+ init_checklist();
720
755
  REPORTS_TABLE = "Reports";
721
756
  REPORT_TYPES = ["Maintenance", "Testing", "Launch", "Announcement"];
722
757
  }
@@ -779,13 +814,16 @@ var init_copy = __esm({
779
814
  "Continuous integration + automatic dependency updates",
780
815
  "Analytics and uptime monitoring"
781
816
  ],
782
- announceHeading: "YOUR MONTHLY REPORT",
783
- announceBody: "We've set up ongoing monitoring and maintenance for your site. Each month we quietly check that everything's healthy and up to date \u2014 and now you'll get a short report so you can see it at a glance.",
817
+ announceHeading: "YOUR ONGOING SITE CARE",
818
+ announceBody: "We've completed a full test of your site and set it up for ongoing care to keep it fast, secure, and healthy. Here's what you can expect from us going forward:",
819
+ announceCadenceHeading: "WHAT TO EXPECT",
820
+ announceTestingLabel: "Full site testing",
821
+ announceMaintenanceLabel: "Routine maintenance",
784
822
  announceMonitorItems: ["Performance", "Accessibility", "Security", "Uptime"],
785
- announcePreviewLabel: "A snapshot of your latest scores:",
823
+ announcePreviewLabel: "From your latest full site test:",
786
824
  announceImprovementResend: "Your contact forms now deliver straight to your inbox through reliable infrastructure, so no inquiry slips through the cracks.",
787
825
  announceImprovementSvelte5: "We've modernized your site to the latest framework \u2014 it's faster, more secure, and built to last.",
788
- announceCadence: "You'll receive this every month. There's nothing you need to do.",
826
+ announceCadence: "After each one we'll send you a short report like this \u2014 there's nothing you need to do.",
789
827
  announceOpenDoor: "And if you'd ever like to expand the scope, add features, or freshen anything up, just reply \u2014 we'd love to help."
790
828
  };
791
829
  }
@@ -1140,6 +1178,23 @@ function buildAnnouncementMjml(data) {
1140
1178
  ).join("")}
1141
1179
  </mj-column>
1142
1180
  </mj-section>` : "";
1181
+ const cad = data.cadence;
1182
+ const cadenceLines = [];
1183
+ if (cad && cad.testing !== "None")
1184
+ cadenceLines.push(`${copy.announceTestingLabel} \u2014 ${FREQ_PHRASE[cad.testing]}`);
1185
+ if (cad && cad.maintenance !== "None")
1186
+ cadenceLines.push(`${copy.announceMaintenanceLabel} \u2014 ${FREQ_PHRASE[cad.maintenance]}`);
1187
+ const cadenceSection = cadenceLines.length > 0 ? `
1188
+ <mj-section background-color="white">
1189
+ <mj-column>
1190
+ <mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">${escapeXml(copy.announceCadenceHeading)}</mj-text>
1191
+ ${cadenceLines.map(
1192
+ (line) => `
1193
+ <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="4px" padding-bottom="4px">\u2022 ${escapeXml(line)}</mj-text>`
1194
+ ).join("")}
1195
+ <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="12px">${escapeXml(copy.announceCadence)}</mj-text>
1196
+ </mj-column>
1197
+ </mj-section>` : "";
1143
1198
  const monitorRows = copy.announceMonitorItems.map(
1144
1199
  (item) => `
1145
1200
  <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="4px" padding-bottom="4px">\u2022 ${escapeXml(item)}</mj-text>`
@@ -1178,6 +1233,7 @@ function buildAnnouncementMjml(data) {
1178
1233
  <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="8px">${escapeXml(copy.announceBody)}</mj-text>
1179
1234
  </mj-column>
1180
1235
  </mj-section>
1236
+ ${cadenceSection}
1181
1237
  ${improvementsSection}
1182
1238
  <mj-section background-color="white">
1183
1239
  <mj-column>
@@ -1193,8 +1249,7 @@ function buildAnnouncementMjml(data) {
1193
1249
  </mj-section>
1194
1250
  <mj-section background-color="white">
1195
1251
  <mj-column>
1196
- <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="36px">${escapeXml(copy.announceCadence)}</mj-text>
1197
- <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="8px">${escapeXml(copy.announceOpenDoor)}</mj-text>
1252
+ <mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="36px">${escapeXml(copy.announceOpenDoor)}</mj-text>
1198
1253
  </mj-column>
1199
1254
  </mj-section>
1200
1255
  <mj-section background-color="white">
@@ -1211,12 +1266,17 @@ function buildAnnouncementMjml(data) {
1211
1266
  </mj-body>
1212
1267
  </mjml>`;
1213
1268
  }
1214
- var RED2, GREY2, SCORE_PREVIEW;
1269
+ var FREQ_PHRASE, RED2, GREY2, SCORE_PREVIEW;
1215
1270
  var init_template3 = __esm({
1216
1271
  "src/reports/announcement-email/template.ts"() {
1217
1272
  "use strict";
1218
1273
  init_copy();
1219
1274
  init_template();
1275
+ FREQ_PHRASE = {
1276
+ Monthly: "every month",
1277
+ Quarterly: "every quarter",
1278
+ Yearly: "every year"
1279
+ };
1220
1280
  RED2 = "#C00";
1221
1281
  GREY2 = "#757575";
1222
1282
  SCORE_PREVIEW = [
@@ -1827,6 +1887,13 @@ async function sendApprovedReports(options = {}) {
1827
1887
  return { output: lines.join("\n"), code: anyFailed ? 1 : 0 };
1828
1888
  }
1829
1889
  async function sendOne(client, base, site, report) {
1890
+ if (!isChecklistComplete(report)) {
1891
+ const items = checklistFor(report.reportType);
1892
+ const done = items.filter((i) => report.checklist[i.field] === true).length;
1893
+ throw new Error(
1894
+ `Report ${report.reportId} checklist incomplete \u2014 ${done}/${items.length} items checked`
1895
+ );
1896
+ }
1830
1897
  if (!site.headerImage) {
1831
1898
  throw new Error(`Site '${site.name}' has no Header image set on the Websites row`);
1832
1899
  }
@@ -1970,6 +2037,7 @@ var init_orchestrate = __esm({
1970
2037
  init_header_image();
1971
2038
  init_resend();
1972
2039
  init_idempotency();
2040
+ init_checklist();
1973
2041
  FROM_ADDRESS2 = "Reddoor Reports <reports@reddoorla.com>";
1974
2042
  REPLY_TO = "info@reddoorla.com";
1975
2043
  MONTHS2 = [
@@ -6373,6 +6441,10 @@ async function announce(deps) {
6373
6441
  commentary: null,
6374
6442
  copy: resolveCopy(w),
6375
6443
  headerImageCid: `${slug}-header`,
6444
+ // The client's go-forward pace, read straight off the Websites row — the email
6445
+ // states each cadence ("Full site testing — every quarter", etc.); a "None" pace
6446
+ // is omitted so we never claim a cadence the site isn't on.
6447
+ cadence: { maintenance: w.maintenanceFreq, testing: w.testingFreq },
6376
6448
  // Default-on fleet-wide for v1: both recent-improvement callouts render for every
6377
6449
  // site. Operator review (the draft never auto-sends) is the relevance backstop;
6378
6450
  // per-site conditioning of these flags is a future lever, not a v1 requirement.
@@ -6426,7 +6498,7 @@ function draftInputFor2(w, scores, now, period) {
6426
6498
  completedOn: now,
6427
6499
  lighthouse: scores,
6428
6500
  lastTestedDate: null,
6429
- subjectOverride: `Your new monthly report for ${w.name}`
6501
+ subjectOverride: `Your testing & maintenance schedule for ${w.name}`
6430
6502
  };
6431
6503
  }
6432
6504