updated apps

This commit is contained in:
2026-07-14 23:57:03 -04:00
parent 6cc7212cef
commit 010e828e9c
797 changed files with 45153 additions and 4246 deletions
@@ -0,0 +1,19 @@
/** localStorage keys for panel UI preferences — single source of truth.
*
* The VALUES are a compatibility contract: they are what users' browsers
* already have stored, so they must never change (the mixed msp-/full-name
* prefixes are historical and stay). Centralizing the names just makes a typo
* in one call site impossible — a DRY audit (2026-07-10) found every key
* duplicated inline at its get/set pair.
*/
export const LS_KEYS = {
overviewTab: "msp-overview-tab",
collapsedSections: "msp-collapsed-sections",
chartRange: "msp-chart-range",
chartHideOutliers: "msp-chart-hide-outliers",
taskSort: "maintenance_supporter_sort",
objectSort: "maintenance_supporter_object_sort",
groupBy: "maintenance_supporter_groupby",
objectView: "maintenance_supporter_object_view",
} as const;
@@ -0,0 +1,11 @@
/** Shared URL-safety guard for rendering user-supplied links.
*
* A user-entered URL (object/task documentation, spare-part shopping link,
* document weblink) must only ever be linkified when it is an absolute http(s)
* URL — never `javascript:`/`data:`/protocol-relative — else it is a stored-XSS
* vector. The `X && /^https?:\/\//i.test(X)` idiom was inlined at ~8 render
* sites; one forgotten copy is a hole, so it lives here once.
*/
export function isSafeHttpUrl(url: string | null | undefined): url is string {
return !!url && /^https?:\/\//i.test(url);
}
@@ -25,6 +25,7 @@ export interface WorksheetLabels {
priority: string;
checklist: string;
notes: string;
parts: string; // "Required parts"
scanView: string; // "Scan to open the task"
scanComplete: string; // "Scan to complete"
manualExcerpt: string; // "Manual excerpt"
@@ -59,6 +60,7 @@ export function buildTaskWorksheetHtml(
qrCompleteDataUri: string | null,
excerpt: WorksheetExcerpt | null,
nowIso: string,
partsLines: string[] = [],
): string {
const meta: Array<[string, string]> = [
[L.object, esc(objectName)],
@@ -127,6 +129,7 @@ export function buildTaskWorksheetHtml(
${meta.map(([k, v]) => `<tr><td>${esc(k)}</td><td>${v}</td></tr>`).join("")}
</table>
${checklist ? `<h2>${esc(L.checklist)}</h2><ul class="check">${checklist}</ul>` : ""}
${partsLines.length ? `<h2>${esc(L.parts)}</h2><ul class="check">${partsLines.map((line) => `<li><span class="box"></span>${esc(line)}</li>`).join("")}</ul>` : ""}
${task.notes ? `<h2>${esc(L.notes)}</h2><div class="notes">${esc(task.notes)}</div>` : ""}
${excerpt ? `<h2>${esc(L.manualExcerpt)}</h2>
<div class="excerpt">${esc(excerpt.title)}${esc(L.pages)} ${excerpt.startPage}${excerpt.endPage}: