| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> |
| <title>RepEx - Report Express | Review & Setup</title> |
|
|
| <link rel="stylesheet" href="style.css" /> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script defer src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
|
|
| <style> |
| img { -webkit-print-color-adjust: exact; print-color-adjust: exact; } |
| </style> |
| </head> |
|
|
| <body class="bg-gray-50 min-h-screen"> |
| <main class="max-w-4xl mx-auto my-8 bg-white shadow-sm ring-1 ring-gray-200 rounded-xl p-6 md:p-8"> |
| |
| <header class="mb-8 border-b border-gray-200 pb-4"> |
| <div class="grid grid-cols-[auto,1fr,auto] items-center gap-4"> |
| <div class="flex items-center"> |
| <img |
| src="https://huggingface.co/spaces/ChristopherJKoen/docusnap-report-wizard/resolve/main/images/Picture3.png" |
| alt="Company logo" |
| class="h-12 w-auto object-contain" |
| loading="eager" |
| /> |
| </div> |
|
|
| <div class="text-center"> |
| <h1 class="text-2xl md:text-3xl font-bold text-gray-900 whitespace-nowrap"> |
| RepEx - Report Express |
| </h1> |
| <p class="text-gray-600 whitespace-nowrap"> |
| Review uploads → pick examples → continue to report viewer |
| </p> |
| </div> |
|
|
| <div class="flex justify-end"> |
| <span class="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-xs font-semibold text-gray-700"> |
| <i data-feather="check-circle" class="h-4 w-4"></i> |
| Uploads processed |
| </span> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <section class="mb-8" aria-labelledby="what-next"> |
| <h2 id="what-next" class="text-xl font-semibold text-gray-800 border-b border-gray-200 pb-2 mb-4"> |
| What happens on this page |
| </h2> |
|
|
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-4"> |
| <div class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-blue-50 border border-blue-100 mb-3"> |
| <i data-feather="image" class="h-5 w-5 text-blue-700"></i> |
| </div> |
| <h3 class="font-semibold text-gray-900 mb-1">Select example photos</h3> |
| <p class="text-sm text-gray-600"> |
| Choose which uploaded images should appear as example figures in the report. |
| </p> |
| </div> |
|
|
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-4"> |
| <div class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-emerald-50 border border-emerald-100 mb-3"> |
| <i data-feather="file-text" class="h-5 w-5 text-emerald-700"></i> |
| </div> |
| <h3 class="font-semibold text-gray-900 mb-1">Confirm documents</h3> |
| <p class="text-sm text-gray-600"> |
| Ensure supporting PDFs/DOCX are correct (and later attach to export if needed). |
| </p> |
| </div> |
|
|
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-4"> |
| <div class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-amber-50 border border-amber-100 mb-3"> |
| <i data-feather="table" class="h-5 w-5 text-amber-700"></i> |
| </div> |
| <h3 class="font-semibold text-gray-900 mb-1">Use Excel/CSV data</h3> |
| <p class="text-sm text-gray-600"> |
| If Excel/CSV exists, it will populate report data areas automatically in later steps. |
| </p> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="mb-8" aria-labelledby="review-uploads"> |
| <h2 id="review-uploads" class="text-xl font-semibold text-gray-800 border-b border-gray-200 pb-2 mb-6"> |
| Review uploaded files |
| </h2> |
|
|
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> |
| |
| <div class="lg:col-span-2"> |
| <div class="flex items-center justify-between mb-3"> |
| <h3 class="text-lg font-semibold text-gray-900">Photos</h3> |
| <span id="photoCount" class="text-sm font-semibold text-gray-600">0 files</span> |
| </div> |
|
|
| <div class="rounded-lg border border-gray-200 bg-white p-4"> |
| <p class="text-sm text-gray-600 mb-3"> |
| Select images to use as example figures in the report. |
| <span class="font-semibold text-gray-800">Recommended:</span> 2–6 images. |
| </p> |
|
|
| <div id="photoGrid" class="grid grid-cols-2 md:grid-cols-3 gap-3"> |
| |
| </div> |
|
|
| <div class="mt-4 flex flex-wrap items-center justify-between gap-3"> |
| <div class="text-sm text-gray-600"> |
| Selected for report: <span id="photoSelected" class="font-semibold text-gray-900">0</span> |
| </div> |
| <div class="flex gap-2"> |
| <button id="selectAllPhotos" type="button" |
| class="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"> |
| <i data-feather="check-square" class="h-4 w-4"></i> |
| Select all |
| </button> |
| <button id="clearPhotos" type="button" |
| class="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"> |
| <i data-feather="square" class="h-4 w-4"></i> |
| Clear |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="space-y-6"> |
| |
| <div> |
| <div class="flex items-center justify-between mb-3"> |
| <h3 class="text-lg font-semibold text-gray-900">Documents</h3> |
| <span id="docCount" class="text-sm font-semibold text-gray-600">0 files</span> |
| </div> |
|
|
| <div class="rounded-lg border border-gray-200 bg-white p-4"> |
| <ul id="docList" class="space-y-2 text-sm text-gray-700"> |
| |
| </ul> |
| <p id="docHint" class="text-xs text-gray-500 mt-3"> |
| PDFs/DOCX appear here after processing. |
| </p> |
| </div> |
| </div> |
|
|
| |
| <div> |
| <div class="flex items-center justify-between mb-3"> |
| <h3 class="text-lg font-semibold text-gray-900">Data files</h3> |
| <span id="dataCount" class="text-sm font-semibold text-gray-600">0 files</span> |
| </div> |
|
|
| <div class="rounded-lg border border-gray-200 bg-white p-4"> |
| <div id="dataBox" class="text-sm text-gray-700"> |
| |
| </div> |
| <p class="text-xs text-gray-500 mt-3"> |
| If present, these files will populate report tables/fields automatically. |
| </p> |
| </div> |
| </div> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="mb-4" aria-label="Continue to report viewer"> |
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-4 flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between"> |
| <div class="text-sm text-gray-600"> |
| Next step: |
| <span id="readyStatus" class="font-semibold text-amber-700">Choose report example images to continue…</span> |
| </div> |
|
|
| <button |
| id="continueBtn" |
| type="button" |
| class="inline-flex items-center justify-center gap-2 rounded-lg bg-emerald-600 px-5 py-2.5 text-white font-semibold hover:bg-emerald-700 transition disabled:opacity-50 disabled:cursor-not-allowed" |
| disabled |
| > |
| <i data-feather="arrow-right" class="h-5 w-5"></i> |
| Continue to Report Viewer |
| </button> |
| </div> |
|
|
| <p class="text-xs text-gray-500 mt-3"> |
| Note: This page assumes uploads were completed on a previous “Processing” page. |
| </p> |
| </section> |
|
|
| |
| <footer class="mt-12 text-center text-xs text-gray-500"> |
| <p>Prosento - © 2026 All Rights Reserved</p> |
| <p class="mt-1">Workflow: Processing → Review uploads → Report viewer → Edit → Export</p> |
| </footer> |
| </main> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', () => { |
| |
| if (window.feather && typeof window.feather.replace === 'function') { |
| window.feather.replace(); |
| } |
| |
| |
| |
| const processedUploads = { |
| photos: [ |
| { name: 'photo_01.jpg', url: 'https://huggingface.co/spaces/ChristopherJKoen/minefix-simm-inspector/resolve/main/images/Picture2.png' }, |
| { name: 'photo_02.jpg', url: 'https://huggingface.co/spaces/ChristopherJKoen/minefix-simm-inspector/resolve/main/images/Screenshot%202026-02-02%20100102.png' }, |
| { name: 'photo_03.jpg', url: 'https://huggingface.co/spaces/ChristopherJKoen/minefix-simm-inspector/resolve/main/images/Picture2.png' } |
| ], |
| documents: [ |
| { name: 'inspection_notes.pdf', type: 'PDF' }, |
| { name: 'supporting_docs.docx', type: 'DOCX' } |
| ], |
| dataFiles: [ |
| { name: 'report_data.xlsx', type: 'XLSX' } |
| ] |
| }; |
| |
| |
| const state = { |
| selectedPhotoIds: new Set(), |
| }; |
| |
| |
| const photoGrid = document.getElementById('photoGrid'); |
| const photoCount = document.getElementById('photoCount'); |
| const photoSelected = document.getElementById('photoSelected'); |
| const selectAllPhotosBtn = document.getElementById('selectAllPhotos'); |
| const clearPhotosBtn = document.getElementById('clearPhotos'); |
| |
| const docList = document.getElementById('docList'); |
| const docCount = document.getElementById('docCount'); |
| const docHint = document.getElementById('docHint'); |
| |
| const dataBox = document.getElementById('dataBox'); |
| const dataCount = document.getElementById('dataCount'); |
| |
| const readyStatus = document.getElementById('readyStatus'); |
| const continueBtn = document.getElementById('continueBtn'); |
| |
| function renderUploads() { |
| |
| const photos = processedUploads.photos || []; |
| photoCount.textContent = `${photos.length} file${photos.length === 1 ? '' : 's'}`; |
| |
| photoGrid.innerHTML = photos.map((p, idx) => { |
| const id = `photo-${idx}`; |
| return ` |
| <label class="group cursor-pointer"> |
| <input type="checkbox" class="sr-only photoCheck" data-photo-id="${id}"> |
| <div class="rounded-lg border border-gray-200 bg-gray-50 overflow-hidden group-has-[:checked]:ring-2 group-has-[:checked]:ring-emerald-200 group-has-[:checked]:border-emerald-300 transition"> |
| <div class="relative"> |
| <img src="${p.url}" alt="${p.name}" class="h-28 w-full object-cover" loading="eager"> |
| <div class="absolute top-2 right-2 inline-flex items-center justify-center rounded-full bg-white/90 border border-gray-200 p-1.5 text-gray-700 group-has-[:checked]:bg-emerald-50 group-has-[:checked]:border-emerald-200 group-has-[:checked]:text-emerald-700"> |
| <i data-feather="check" class="h-4 w-4"></i> |
| </div> |
| </div> |
| <div class="p-2"> |
| <div class="text-xs font-semibold text-gray-900 truncate">${p.name}</div> |
| <div class="text-xs text-gray-500">Click to select for report</div> |
| </div> |
| </div> |
| </label> |
| `; |
| }).join(''); |
| |
| |
| const docs = processedUploads.documents || []; |
| docCount.textContent = `${docs.length} file${docs.length === 1 ? '' : 's'}`; |
| docList.innerHTML = docs.length |
| ? docs.map(d => ` |
| <li class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2"> |
| <div class="flex items-center gap-2 min-w-0"> |
| <i data-feather="file-text" class="h-4 w-4 text-gray-600"></i> |
| <span class="truncate text-gray-800">${d.name}</span> |
| </div> |
| <span class="text-xs font-semibold text-gray-600">${d.type}</span> |
| </li> |
| `).join('') |
| : `<li class="text-sm text-gray-500">No supporting documents detected.</li>`; |
| docHint.style.display = docs.length ? 'none' : 'block'; |
| |
| |
| const dataFiles = processedUploads.dataFiles || []; |
| dataCount.textContent = `${dataFiles.length} file${dataFiles.length === 1 ? '' : 's'}`; |
| dataBox.innerHTML = dataFiles.length |
| ? dataFiles.map(f => ` |
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-3 text-sm text-gray-700 mb-2 last:mb-0"> |
| <div class="flex items-center justify-between gap-3"> |
| <div class="flex items-center gap-2 min-w-0"> |
| <i data-feather="table" class="h-4 w-4 text-amber-700"></i> |
| <span class="truncate font-semibold text-gray-900">${f.name}</span> |
| </div> |
| <span class="text-xs font-semibold text-gray-600">${f.type}</span> |
| </div> |
| <div class="text-xs text-gray-600 mt-1"> |
| Will populate report data areas (tables/fields). |
| </div> |
| </div> |
| `).join('') |
| : ` |
| <div class="rounded-lg border border-gray-200 bg-gray-50 p-3 text-sm text-gray-600"> |
| <div class="font-semibold text-gray-800 mb-1">No Excel/CSV detected</div> |
| If you upload a CSV or Excel file, RepEx can auto-populate report data fields. |
| </div> |
| `; |
| |
| |
| if (window.feather && typeof window.feather.replace === 'function') { |
| window.feather.replace(); |
| } |
| } |
| |
| function updateSelectionState() { |
| photoSelected.textContent = String(state.selectedPhotoIds.size); |
| const canContinue = state.selectedPhotoIds.size > 0; |
| continueBtn.disabled = !canContinue; |
| |
| if (!canContinue) { |
| readyStatus.textContent = 'Choose report example images to continue…'; |
| readyStatus.className = 'font-semibold text-amber-700'; |
| } else { |
| readyStatus.textContent = 'Ready. Continue to report viewer.'; |
| readyStatus.className = 'font-semibold text-emerald-700'; |
| } |
| } |
| |
| |
| renderUploads(); |
| updateSelectionState(); |
| |
| |
| photoGrid.addEventListener('change', (e) => { |
| const cb = e.target.closest('.photoCheck'); |
| if (!cb) return; |
| const id = cb.dataset.photoId; |
| if (cb.checked) state.selectedPhotoIds.add(id); |
| else state.selectedPhotoIds.delete(id); |
| updateSelectionState(); |
| }); |
| |
| selectAllPhotosBtn.addEventListener('click', () => { |
| photoGrid.querySelectorAll('.photoCheck').forEach(cb => { |
| cb.checked = true; |
| state.selectedPhotoIds.add(cb.dataset.photoId); |
| }); |
| updateSelectionState(); |
| }); |
| |
| clearPhotosBtn.addEventListener('click', () => { |
| photoGrid.querySelectorAll('.photoCheck').forEach(cb => (cb.checked = false)); |
| state.selectedPhotoIds.clear(); |
| updateSelectionState(); |
| }); |
| |
| |
| continueBtn.addEventListener('click', () => { |
| if (state.selectedPhotoIds.size === 0) return; |
| |
| const selectedIndices = Array.from(state.selectedPhotoIds) |
| .map(id => Number(id.replace('photo-', ''))) |
| .filter(n => Number.isFinite(n)); |
| |
| const payload = { |
| |
| selectedPhotoIndices: selectedIndices, |
| uploads: processedUploads, |
| createdAt: new Date().toISOString(), |
| }; |
| |
| localStorage.setItem('repex_report_payload', JSON.stringify(payload)); |
| window.location.href = 'report-viewer.html'; |
| }); |
| }); |
| </script> |
| </body> |
| </html> |
|
|