| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <!doctype html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Planning Report – MVP Demo</title>
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
- <script defer src="https://unpkg.com/html2pdf.js@0.10.1/dist/html2pdf.bundle.min.js"></script>
- <style>
- body { background: #0f172a0d; }
- header { background: #0f172a; color: #fff; }
- .report-card { background:#fff; border-radius:12px; box-shadow: 0 10px 30px rgba(0,0,0,.05); }
- .mono { font-family: ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace; }
- textarea { tab-size: 2; }
- </style>
- </head>
- <body>
- <header class="py-4 mb-4">
- <div class="container">
- <h1 class="h3 mb-0">Planning Report Generator — MVP</h1>
- <div class="text-white-50">Standalone demo UI posting to the PHP endpoint</div>
- </div>
- </header>
- <main class="container pb-5">
- <div class="row g-4">
- <div class="col-lg-6">
- <div class="card h-100">
- <div class="card-header d-flex align-items-center justify-content-between">
- <strong>Payload</strong>
- <div class="d-flex gap-2">
- <button id="btnSample" class="btn btn-outline-secondary btn-sm">Load sample payload</button>
- <button id="btnPretty" class="btn btn-outline-secondary btn-sm">Pretty JSON</button>
- </div>
- </div>
- <div class="card-body">
- <textarea id="payload" class="form-control mono" rows="24" spellcheck="false" placeholder='Paste your JSON payload here...'></textarea>
- <div class="mt-3 d-flex gap-2">
- <button id="btnGenerate" class="btn btn-primary">Generate report</button>
- <div id="status" class="small text-muted"></div>
- </div>
- <div class="form-text mt-2">Endpoint: <code id="endpointTxt"></code></div>
- </div>
- </div>
- </div>
- <div class="col-lg-6">
- <div class="card report-card">
- <div class="card-header d-flex justify-content-between align-items-center">
- <strong>Report preview</strong>
- <div class="d-flex gap-2">
- <button id="btnCopyMd" class="btn btn-outline-secondary btn-sm" disabled>Copy Markdown</button>
- <button id="btnSavePdf" class="btn btn-outline-secondary btn-sm" disabled>Save as PDF</button>
- </div>
- </div>
- <div class="card-body" id="reportWrap">
- <div id="reportHtml" class="mb-3"></div>
- <details id="mdBlock" class="mt-3" open>
- <summary class="mb-2">Markdown</summary>
- <textarea id="reportMd" class="form-control mono" rows="16" readonly></textarea>
- </details>
- </div>
- </div>
- </div>
- </div>
- </main>
- <script>
- const REPORT_ENDPOINT = "generate_planning_report.php";
- document.getElementById('endpointTxt').textContent = REPORT_ENDPOINT;
- function loadSample() {
- const sample = {
- address: "24 Clifton Drive, Sorell TAS 7172",
- lat: -42.781, lng: 147.560,
- pid: "1234567",
- title_id: "CT 12345/1",
- total_area: { sqm_label: "1,652 m²", ha_label: "0.1652 ha" },
- area_sqm: "1652", area_ha: "0.1652",
- tenure: "Freehold",
- lpi: "LIST LPI XYZ",
- list_guid: "abcde-12345-fghi-67890",
- locality: "Sorell",
- council: "Sorell Council",
- planning_scheme: "Tasmanian Planning Scheme",
- planning_zones: ["General Residential Zone"],
- planning_codes: ["Parking and Sustainable Transport", "Road and Railway Assets", "Signs"],
- use_class: "Health Services",
- proposal_summary: "The proposal seeks approval for the use and development of an allied-health clinic within an existing building. Works include internal reconfiguration, accessible entry upgrade, and compliant parking layout.",
- operations: { hours: "7:30am–6:00pm Mon–Fri", staff: 8, children: 0 },
- parking: { cars: 12, bikes: 4, accessible: 1, motorcycle: 0 },
- signage: [{ type: "Wall Sign", desc: "Non-illuminated, 1.5 m² near entry." }],
- consultants: { TIA: "Midson Traffic (2025)", Acoustic: "DDEG (2025)" },
- overlays: { bushfire: false, airport_noise: false, road_rail_attenuation: true },
- standards: [
- { clause: "8.3.1", standard: "Discretionary uses", acceptable: "A3", relies_on_pc: ["P1","P4"], notes: "" },
- { clause: "8.5.1", standard: "Non-residential development", acceptable: "A1,A2,A6", relies_on_pc: [], notes: "Complies as proposed." },
- { clause: "C2.5.1", standard: "Car parking numbers", acceptable: "A1", relies_on_pc: [], notes: "" },
- { clause: "C2.6.1", standard: "Bicycle parking", acceptable: "A1", relies_on_pc: [], notes: "" },
- { clause: "C3.5.1", standard: "Road and railway assets", acceptable: "", relies_on_pc: ["P1"], notes: "TIA supports performance solution." }
- ],
- appendices: ["Application Form","Certificate of Title","Architectural Plans","Traffic Impact Assessment","Acoustic Report"],
- map_png: "",
- prepared_for: "Client / Applicant Name",
- prepared_by: "Modulos Design",
- author: "Benjamin Harris",
- job_number: "PRJ-001",
- version: "Draft",
- prepared_date: "5 September 2025"
- };
- document.getElementById('payload').value = JSON.stringify(sample, null, 2);
- }
- function pretty() {
- const ta = document.getElementById('payload');
- try {
- const obj = JSON.parse(ta.value);
- ta.value = JSON.stringify(obj, null, 2);
- } catch {}
- }
- async function generate() {
- const ta = document.getElementById('payload');
- const status = document.getElementById('status');
- const btnMd = document.getElementById('btnCopyMd');
- const btnPdf = document.getElementById('btnSavePdf');
- let payload;
- btnMd.disabled = true; btnPdf.disabled = true;
- try { payload = JSON.parse(ta.value); }
- catch (e) { alert("Invalid JSON in payload."); return; }
- status.textContent = "Generating…";
- try {
- const resp = await fetch("generate_planning_report.php", {
- method: "POST",
- headers: { "Content-Type": "application/json", "Accept": "application/json" },
- body: JSON.stringify(payload)
- });
- const raw = await resp.text(); // <-- read raw
- let out = null;
- try { out = JSON.parse(raw); } catch (_) {}
- if (!resp.ok || !out || out.ok !== true) {
- // Show a helpful snippet of what we actually got back
- const snippet = (raw || '').slice(0, 300).replace(/\s+/g, ' ').trim();
- throw new Error(out && out.error ? out.error : `HTTP ${resp.status} – non-JSON or error body: ${snippet || "<empty>"}`);
- }
- document.getElementById('reportHtml').innerHTML = out.html || "<p>No HTML returned.</p>";
- const md = out.markdown || "";
- const mdTa = document.getElementById('reportMd');
- mdTa.value = md;
- btnMd.disabled = !md;
- btnPdf.disabled = !(out.html && out.html.length);
- status.textContent = "Done.";
- } catch (e) {
- console.error(e);
- status.textContent = "Error.";
- alert("Report generation error: " + e.message);
- }
- }
- async function copyMd() {
- const mdTa = document.getElementById('reportMd');
- try {
- await navigator.clipboard.writeText(mdTa.value);
- const btn = document.getElementById('btnCopyMd');
- btn.textContent = "Copied!";
- setTimeout(()=>btn.textContent="Copy Markdown", 1200);
- } catch {
- mdTa.select(); document.execCommand('copy');
- }
- }
- function savePdf() {
- const container = document.querySelector('#reportHtml');
- if (!container || !container.innerHTML.trim()) { alert("Generate a report first."); return; }
- const opts = {
- margin: [10,10,10,10],
- filename: "Planning Report.pdf",
- image: { type: "jpeg", quality: 0.95 },
- html2canvas: { scale: 2, useCORS: true, logging: false },
- jsPDF: { unit: "mm", format: "a4", orientation: "portrait" }
- };
- html2pdf().from(container).set(opts).save();
- }
- document.getElementById('btnSample').addEventListener('click', loadSample);
- document.getElementById('btnPretty').addEventListener('click', pretty);
- document.getElementById('btnGenerate').addEventListener('click', generate);
- document.getElementById('btnCopyMd').addEventListener('click', copyMd);
- document.getElementById('btnSavePdf').addEventListener('click', savePdf);
- // Preload sample for convenience
- loadSample();
- </script>
- </body>
- </html>
|