Working version before modification.

This commit is contained in:
Stan
2026-04-20 21:04:54 +02:00
parent 28d167f11f
commit e7127f3215
30 changed files with 7046 additions and 1201 deletions
+218 -285
View File
@@ -6,67 +6,55 @@
<meta name="theme-color" content="#f3efe6" />
<title>Check List PoC</title>
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet" />
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<!--
This document is the shared app shell for both operator and administrator
routes. JavaScript decides which workspace to reveal based on the current
URL so the project can keep one frontend bundle while still presenting two
distinct entry points.
-->
<div class="app-shell">
<aside class="sidebar panel">
<!--
The sidebar keeps app-level actions visible across both workspaces:
sync status, template selection, navigation links, and the local draft
list. That supports quick report switching on small operational screens.
-->
<div class="brand-block">
<p class="eyebrow">Hybrid Inspection Reporting</p>
<h1>Check List</h1>
<p class="lede">
Offline-first proof of concept for template-driven quality reports.
</p>
<div class="d-flex vh-100">
<!-- Sidebar -->
<aside class="sidebar-bs d-flex flex-column border-end bg-light" style="width:280px;min-width:280px;">
<div class="p-3 border-bottom">
<p class="text-uppercase text-muted small fw-semibold mb-0">Hybrid Inspection Reporting</p>
<h5 class="fw-bold mb-0">Check List</h5>
<small class="text-muted">Offline-first proof of concept for template-driven quality reports.</small>
</div>
<div class="sidebar-section">
<div class="status-row">
<span id="connectionBadge" class="badge badge-neutral">Checking connection</span>
<span id="saveBadge" class="badge badge-neutral">No changes</span>
<div class="p-3 border-bottom">
<div class="d-flex gap-2 mb-2">
<span id="connectionBadge" class="badge bg-secondary">Checking connection</span>
<span id="saveBadge" class="badge bg-secondary">No changes</span>
</div>
<button id="syncTemplatesButton" class="button button-secondary" type="button">
Sync templates
<button id="syncTemplatesButton" class="btn btn-outline-secondary btn-sm w-100" type="button">
<i class="bi bi-arrow-repeat me-1"></i>Sync templates
</button>
</div>
<div class="sidebar-section">
<label class="field-label" for="templateSelect">Template</label>
<select id="templateSelect" class="select-input"></select>
<button id="createReportButton" class="button button-primary" type="button">
Create new report
<div class="p-3 border-bottom">
<label class="form-label small fw-semibold" for="templateSelect">Template</label>
<select id="templateSelect" class="form-select form-select-sm mb-2"></select>
<button id="createReportButton" class="btn btn-primary btn-sm w-100" type="button">
<i class="bi bi-plus-lg me-1"></i>Create new report
</button>
</div>
<div class="sidebar-section">
<div class="section-heading-row sidebar-links-heading">
<h2>Access</h2>
<span class="muted-count">Direct links</span>
</div>
<a id="userAreaLink" class="button button-secondary sidebar-link" href="/user">User area</a>
<a id="adminAreaLink" class="button button-secondary sidebar-link" href="/admin">Admin area</a>
<a class="button button-secondary sidebar-link" href="/">Back to portal</a>
<div class="p-3 border-bottom">
<a id="userAreaLink" class="btn btn-outline-secondary btn-sm w-100 mb-1" href="/user">User area</a>
<a id="adminAreaLink" class="btn btn-outline-secondary btn-sm w-100 mb-1" href="/admin">Admin area</a>
<a class="btn btn-outline-secondary btn-sm w-100" href="/">Back to portal</a>
</div>
<div class="sidebar-section grow-section">
<div class="section-heading-row">
<h2>Local reports</h2>
<span id="reportCount" class="muted-count">0</span>
<div class="flex-grow-1 overflow-auto p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="fw-semibold mb-0">Local reports</h6>
<span id="reportCount" class="badge bg-secondary">0</span>
</div>
<!-- F4 — Search and status filter for the local report list -->
<div class="report-filter-row">
<input id="reportSearchInput" class="text-input text-input-small" type="search" placeholder="Search reports" />
<select id="reportFilterSelect" class="select-input select-input-small">
<div class="mb-2">
<input id="reportSearchInput" class="form-control form-control-sm mb-1" type="search" placeholder="Search reports" />
<select id="reportFilterSelect" class="form-select form-select-sm">
<option value="">All statuses</option>
<option value="draft">Draft</option>
<option value="in_progress">In Progress</option>
@@ -79,259 +67,204 @@
</div>
</aside>
<main class="workspace">
<!-- Operator workspace: draft editing, validation, and local attachments. -->
<!-- Main content -->
<main class="flex-grow-1 overflow-auto p-4 bg-white">
<!-- Operator workspace -->
<section id="reportsWorkspace" class="workspace-view workspace-view-active">
<section class="hero panel">
<div>
<p class="eyebrow">Proof of concept frontend</p>
<h2 id="heroTitle">No report selected</h2>
<p id="heroSubtitle" class="hero-copy">
Start by syncing templates and creating a local draft.
</p>
</div>
<div class="hero-actions">
<label class="status-picker">
<span>Status</span>
<select id="reportStatusSelect" class="select-input">
<option value="draft">Draft</option>
<option value="in_progress">In Progress</option>
<option value="ready_for_export">Ready for Export</option>
<option value="exported">Exported</option>
<option value="archived">Archived</option>
</select>
</label>
<button id="submitReportButton" class="button button-secondary" type="button">
Submit
</button>
<button id="exportReportButton" class="button button-secondary" type="button">
Export CSV
</button>
<button id="deleteReportButton" class="button button-ghost" type="button">
Delete report
</button>
</div>
</section>
<section class="summary-grid">
<article class="summary-card panel accent-card">
<p class="summary-label">Template</p>
<strong id="summaryTemplate">Not loaded</strong>
<span id="summaryVersion" class="summary-note">Version -</span>
</article>
<article class="summary-card panel">
<p class="summary-label">Validation</p>
<strong id="validationHeadline">No report selected</strong>
<span id="validationDetail" class="summary-note">Draft validation will appear here.</span>
</article>
<article class="summary-card panel">
<p class="summary-label">Offline cache</p>
<strong id="syncHeadline">No sync yet</strong>
<span id="syncDetail" class="summary-note">Templates are cached locally after the first successful sync.</span>
</article>
</section>
<section class="editor-grid">
<section class="panel editor-panel">
<div class="section-heading-row">
<h2>Report editor</h2>
<span id="editorHint" class="panel-note">Dynamic form rendering from template JSON</span>
</div>
<form id="reportForm" class="report-form">
<div class="empty-state">
<h3>No report open</h3>
<p>Choose a template and create a report to start editing locally.</p>
</div>
</form>
</section>
<aside class="panel inspector-panel">
<div class="section-heading-row">
<h2>Inspector view</h2>
<span class="panel-note">Local draft summary</span>
</div>
<dl id="reportMeta" class="meta-list">
<div>
<dt>Report ID</dt>
<dd>-</dd>
</div>
<div>
<dt>Template</dt>
<dd>-</dd>
</div>
<div>
<dt>Created</dt>
<dd>-</dd>
</div>
<div>
<dt>Updated</dt>
<dd>-</dd>
</div>
</dl>
<div class="validation-block">
<h3>Validation issues</h3>
<ul id="validationList" class="validation-list">
<li>No report selected.</li>
</ul>
</div>
<div class="attachment-policy">
<h3>Image policy</h3>
<p id="imagePolicyText" class="policy-copy">
Load server configuration to see image limits and optimization rules.
</p>
</div>
</aside>
</section>
</section>
<!-- Administrator workspace: server-backed configuration editing. -->
<section id="adminWorkspace" class="workspace-view" hidden>
<section class="hero panel">
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<p class="eyebrow">Administrator workspace</p>
<h2>Configuration control</h2>
<p class="hero-copy">
Update centrally managed image requirements used by the inspection frontend.
</p>
<p class="text-muted small mb-0">Proof of concept frontend</p>
<h3 id="heroTitle" class="fw-bold">No report selected</h3>
<p id="heroSubtitle" class="text-muted">Start by syncing templates and creating a local draft.</p>
</div>
<div class="hero-actions">
<span id="adminSyncState" class="badge badge-neutral">Server-backed settings</span>
<div class="d-flex gap-2 align-items-center">
<label class="d-flex align-items-center gap-1 small">
<span>Status</span>
<select id="reportStatusSelect" class="form-select form-select-sm" style="width:auto">
<option value="draft">Draft</option>
<option value="in_progress">In Progress</option>
<option value="ready_for_export">Ready for Export</option>
<option value="exported">Exported</option>
<option value="archived">Archived</option>
</select>
</label>
<button id="submitReportButton" class="btn btn-outline-secondary btn-sm" type="button">Submit</button>
<button id="exportReportButton" class="btn btn-outline-secondary btn-sm" type="button">Export CSV</button>
<button id="deleteReportButton" class="btn btn-outline-danger btn-sm" type="button">Delete</button>
</div>
</section>
</div>
<section class="editor-grid">
<section class="panel editor-panel">
<div class="section-heading-row">
<h2>Image policy editor</h2>
<span class="panel-note">Updates the active server rule</span>
<!-- Summary cards -->
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="card border-primary">
<div class="card-body py-2 px-3">
<small class="text-muted">Template</small>
<div class="fw-semibold" id="summaryTemplate">Not loaded</div>
<small class="text-muted" id="summaryVersion">Version -</small>
</div>
</div>
<form id="adminImageRulesForm" class="report-form admin-form">
<section class="template-section">
<div class="field-grid">
<div class="field field-full">
<div class="field-header">
<label class="field-label" for="adminPolicyName">Policy name</label>
</div>
<input id="adminPolicyName" name="name" class="text-input" type="text" />
</div>
<div class="field field-full">
<div class="field-header">
<label class="field-label" for="adminAllowedMimeTypes">Allowed MIME types</label>
</div>
<input
id="adminAllowedMimeTypes"
name="allowedMimeTypes"
class="text-input"
type="text"
placeholder="image/jpeg, image/png, image/webp"
/>
<p class="field-help">Comma-separated values used by the attachment field and browser validation.</p>
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminMaxFileSizeMb">Max file size (MB)</label>
</div>
<input id="adminMaxFileSizeMb" name="maxFileSizeMb" class="text-input" type="number" min="1" step="0.1" />
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminMaxAttachmentsPerField">Max attachments per field</label>
</div>
<input id="adminMaxAttachmentsPerField" name="maxAttachmentsPerField" class="text-input" type="number" min="1" step="1" />
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminMaxWidthPx">Max width (px)</label>
</div>
<input id="adminMaxWidthPx" name="maxWidthPx" class="text-input" type="number" min="1" step="1" />
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminMaxHeightPx">Max height (px)</label>
</div>
<input id="adminMaxHeightPx" name="maxHeightPx" class="text-input" type="number" min="1" step="1" />
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminJpegQuality">JPEG quality</label>
</div>
<input id="adminJpegQuality" name="jpegQuality" class="text-input" type="number" min="1" max="100" step="1" />
</div>
<div class="field">
<div class="field-header">
<label class="field-label" for="adminOversizeBehavior">Oversize behavior</label>
</div>
<select id="adminOversizeBehavior" name="oversizeBehavior" class="select-input">
<option value="auto_optimize">Auto optimize</option>
<option value="warn_then_optimize">Warn then optimize</option>
<option value="block">Block oversized files</option>
</select>
</div>
</div>
</section>
<div class="admin-actions">
<button id="saveImageRulesButton" class="button button-primary" type="submit">
Save image policy
</button>
<button id="resetImageRulesButton" class="button button-secondary" type="button">
Reset form
</button>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body py-2 px-3">
<small class="text-muted">Validation</small>
<div class="fw-semibold" id="validationHeadline">No report selected</div>
<small class="text-muted" id="validationDetail">Draft validation will appear here.</small>
</div>
</form>
</section>
<aside class="panel inspector-panel">
<div class="section-heading-row">
<h2>Admin summary</h2>
<span class="panel-note">Live configuration preview</span>
</div>
<dl class="meta-list">
<div>
<dt>Active policy code</dt>
<dd id="adminPolicyCode">-</dd>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body py-2 px-3">
<small class="text-muted">Offline cache</small>
<div class="fw-semibold" id="syncHeadline">No sync yet</div>
<small class="text-muted" id="syncDetail">Templates are cached locally after the first successful sync.</small>
</div>
<div>
<dt>Allowed types</dt>
<dd id="adminPolicyMimeTypes">-</dd>
</div>
<div>
<dt>Optimization</dt>
<dd id="adminPolicyOptimization">-</dd>
</div>
<div>
<dt>Limits</dt>
<dd id="adminPolicyLimits">-</dd>
</div>
</dl>
<div class="validation-block">
<h3>Admin notes</h3>
<ul class="validation-list" id="adminNotesList">
<li>Changes are stored on the server and reused by report attachments.</li>
<li>Operators will use the updated policy after the next sync.</li>
</ul>
</div>
</aside>
</section>
</div>
</div>
<!-- Editor + Inspector -->
<div class="row g-4">
<div class="col-lg-8">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0 fw-semibold">Report editor</h6>
<small id="editorHint" class="text-muted">Dynamic form rendering from template JSON</small>
</div>
<div class="card-body">
<form id="reportForm" class="report-form">
<div class="text-center text-muted py-4">
<h5>No report open</h5>
<p>Choose a template and create a report to start editing locally.</p>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card mb-3">
<div class="card-header fw-semibold">Inspector view</div>
<div class="card-body">
<dl id="reportMeta" class="mb-0">
<dt class="small text-muted">Report ID</dt><dd class="mb-2">-</dd>
<dt class="small text-muted">Template</dt><dd class="mb-2">-</dd>
<dt class="small text-muted">Created</dt><dd class="mb-2">-</dd>
<dt class="small text-muted">Updated</dt><dd class="mb-0">-</dd>
</dl>
</div>
</div>
<div class="card mb-3">
<div class="card-header fw-semibold">Validation issues</div>
<div class="card-body">
<ul id="validationList" class="mb-0 ps-3">
<li>No report selected.</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header fw-semibold">Image policy</div>
<div class="card-body">
<p id="imagePolicyText" class="mb-0 text-muted small">
Load server configuration to see image limits and optimization rules.
</p>
</div>
</div>
</div>
</div>
</section>
<!-- Admin workspace (legacy) -->
<section id="adminWorkspace" class="workspace-view" hidden>
<div class="mb-4">
<p class="text-muted small mb-0">Administrator workspace</p>
<h3 class="fw-bold">Configuration control</h3>
<p class="text-muted">Update centrally managed image requirements used by the inspection frontend.</p>
<span id="adminSyncState" class="badge bg-secondary">Server-backed settings</span>
</div>
<div class="row g-4">
<div class="col-lg-8">
<div class="card">
<div class="card-header fw-semibold">Image policy editor</div>
<div class="card-body">
<form id="adminImageRulesForm">
<div class="row g-3 mb-3">
<div class="col-12">
<label for="adminPolicyName" class="form-label">Policy name</label>
<input id="adminPolicyName" name="name" class="form-control" type="text" />
</div>
<div class="col-12">
<label for="adminAllowedMimeTypes" class="form-label">Allowed MIME types</label>
<input id="adminAllowedMimeTypes" name="allowedMimeTypes" class="form-control" type="text" placeholder="image/jpeg, image/png, image/webp" />
<div class="form-text">Comma-separated values used by the attachment field and browser validation.</div>
</div>
<div class="col-md-6">
<label for="adminMaxFileSizeMb" class="form-label">Max file size (MB)</label>
<input id="adminMaxFileSizeMb" name="maxFileSizeMb" class="form-control" type="number" min="1" step="0.1" />
</div>
<div class="col-md-6">
<label for="adminMaxAttachmentsPerField" class="form-label">Max attachments per field</label>
<input id="adminMaxAttachmentsPerField" name="maxAttachmentsPerField" class="form-control" type="number" min="1" step="1" />
</div>
<div class="col-md-4">
<label for="adminMaxWidthPx" class="form-label">Max width (px)</label>
<input id="adminMaxWidthPx" name="maxWidthPx" class="form-control" type="number" min="1" step="1" />
</div>
<div class="col-md-4">
<label for="adminMaxHeightPx" class="form-label">Max height (px)</label>
<input id="adminMaxHeightPx" name="maxHeightPx" class="form-control" type="number" min="1" step="1" />
</div>
<div class="col-md-4">
<label for="adminJpegQuality" class="form-label">JPEG quality</label>
<input id="adminJpegQuality" name="jpegQuality" class="form-control" type="number" min="1" max="100" step="1" />
</div>
<div class="col-md-6">
<label for="adminOversizeBehavior" class="form-label">Oversize behavior</label>
<select id="adminOversizeBehavior" name="oversizeBehavior" class="form-select">
<option value="auto_optimize">Auto optimize</option>
<option value="warn_then_optimize">Warn then optimize</option>
<option value="block">Block oversized files</option>
</select>
</div>
</div>
<div class="d-flex gap-2">
<button id="saveImageRulesButton" class="btn btn-primary" type="submit">Save image policy</button>
<button id="resetImageRulesButton" class="btn btn-outline-secondary" type="button">Reset form</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header fw-semibold">Admin summary</div>
<div class="card-body">
<dl class="mb-0">
<dt class="small text-muted">Active policy code</dt><dd id="adminPolicyCode" class="mb-2">-</dd>
<dt class="small text-muted">Allowed types</dt><dd id="adminPolicyMimeTypes" class="mb-2">-</dd>
<dt class="small text-muted">Optimization</dt><dd id="adminPolicyOptimization" class="mb-2">-</dd>
<dt class="small text-muted">Limits</dt><dd id="adminPolicyLimits" class="mb-0">-</dd>
</dl>
</div>
</div>
<div class="card mt-3">
<div class="card-header fw-semibold">Admin notes</div>
<div class="card-body">
<ul id="adminNotesList" class="mb-0 ps-3">
<li>Changes are stored on the server and reused by report attachments.</li>
<li>Operators will use the updated policy after the next sync.</li>
</ul>
</div>
</div>
</div>
</div>
</section>
</main>
</div>
<!--
Report list items are rendered from this template at runtime so the sidebar
can update without rebuilding the entire page markup from strings.
-->
<template id="reportListItemTemplate">
<button class="report-list-item" type="button" data-report-id="">
<span class="report-list-item__header">