Add the full compliance mocks

This commit is contained in:
Eli Ribble 2026-04-07 00:52:12 +00:00
parent 4faa7fa8c0
commit 6f677b5638
No known key found for this signature in database
19 changed files with 1473 additions and 1463 deletions

View file

@ -2,95 +2,6 @@
{{ define "title" }}Confirm Address{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
.map-placeholder {
width: 100%;
height: 250px;
background-color: #e9ecef;
border: 2px dashed #6c757d;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: #6c757d;
font-size: 14px;
}
#address-input {
font-size: 16px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 2 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 25%;"
aria-valuenow="25"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Confirm the property address</h2>
<p class="text-muted mb-4">
Please enter the address so we can match your response with our records.
</p>
<form id="address-form" method="POST" action="/compliance/address">
<div class="mb-4">
<label for="address-input" class="form-label fw-semibold">
Property Address
</label>
<input
type="text"
class="form-control form-control-lg"
id="address-input"
name="address"
placeholder="Start typing your address..."
autocomplete="off"
data-autocomplete="true"
required
/>
<div class="form-text">
Begin typing and select your address from the suggestions
</div>
</div>
<!-- Map Placeholder -->
<div class="mb-4">
<label class="form-label fw-semibold">Location Preview</label>
<div class="map-placeholder" id="map-container">
<span>Map will appear here after address is selected</span>
</div>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a class="btn btn-outline-secondary" href="../compliance"> Back </a>
<a class="btn btn-primary flex-grow-1" href="concern"> Continue </a>
</div>
</form>
</main>
</div>
{{ end }}

View file

@ -2,273 +2,6 @@
{{ define "title" }}Response Submitted{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.completion-container {
max-width: 500px;
margin: 0 auto;
padding: 32px 16px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.district-branding {
text-align: center;
margin-bottom: 32px;
}
.district-branding img {
height: 80px;
width: auto;
margin-bottom: 16px;
}
.district-branding h1 {
font-size: 1.25rem;
color: #495057;
margin-bottom: 8px;
}
.district-branding .phone {
color: #6c757d;
font-size: 0.9rem;
}
.success-icon {
width: 80px;
height: 80px;
background-color: #d1e7dd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.success-icon i {
font-size: 2.5rem;
color: #0f5132;
}
.warning-icon {
width: 80px;
height: 80px;
background-color: #fff3cd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.warning-icon i {
font-size: 2.5rem;
color: #997404;
}
.alert-icon {
width: 80px;
height: 80px;
background-color: #f8d7da;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.alert-icon i {
font-size: 2.5rem;
color: #842029;
}
.message-card {
background-color: #fff;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
width: 100%;
margin-bottom: 24px;
}
.message-card h2 {
font-size: 1.5rem;
margin-bottom: 16px;
text-align: center;
}
.message-card p {
color: #495057;
line-height: 1.6;
margin-bottom: 12px;
}
.message-card p:last-child {
margin-bottom: 0;
}
.info-box {
background-color: #f8f9fa;
border-left: 4px solid #6c757d;
padding: 16px;
border-radius: 4px;
margin-top: 16px;
}
.warning-box {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
margin-top: 16px;
}
.reference-number {
text-align: center;
color: #6c757d;
font-size: 0.9rem;
margin-top: 24px;
}
.action-buttons {
width: 100%;
}
</style>
{{ end }}
{{ define "content" }}
<div class="completion-container">
<!-- District Branding -->
<div class="district-branding">
<img src="{{ .District.URLLogo }}" alt="{{ .District.Name }} logo" />
<h1>{{ .District.Name }}</h1>
<div class="phone">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</div>
{{ if .HasCompleteResponse }}
<!-- Mode 1: Complete response with contact info -->
<div class="success-icon">
<i class="bi bi-check-circle-fill"></i>
</div>
<div class="message-card">
<h2>Thank you</h2>
<p>
Your response has been submitted successfully. We will review your
submission and contact you if further action is needed.
</p>
<div class="info-box">
<p class="mb-2">
<strong>What you can expect:</strong>
</p>
<ul class="mb-0 small">
<li>Our team will review your photos and information</li>
<li>If we need to schedule a visit, we'll contact you first</li>
<li>
You'll receive updates at the contact information you provided
</li>
</ul>
</div>
</div>
{{ else if .HasUsefulInfo }}
<!-- Mode 2: Useful info but no contact -->
<div class="warning-icon">
<i class="bi bi-exclamation-circle-fill"></i>
</div>
<div class="message-card">
<h2>Response Received</h2>
<p>
Thank you for your submission. We will review your information and let
you know if further action is needed.
</p>
<div class="warning-box">
<p class="mb-2">
<strong><i class="bi bi-info-circle"></i> Important notice:</strong>
</p>
<p class="mb-0 small">
You did not provide contact information. If further action is
needed, the District may need to use warrant authority to enter the
property. We prefer to coordinate access directly, and contact
information makes that much easier.
</p>
</div>
<p class="text-center mt-3 mb-0">
<small class="text-muted">
If you'd like to add contact information, please call our office.
</small>
</p>
</div>
{{ else }}
<!-- Mode 3: Insufficient information -->
<div class="alert-icon">
<i class="bi bi-exclamation-triangle-fill"></i>
</div>
<div class="message-card">
<h2>Response Received</h2>
<p>
Your response has been recorded, but it does not contain enough
information for us to resolve this matter.
</p>
<div class="warning-box">
<p class="mb-2">
<strong
><i class="bi bi-exclamation-triangle"></i> Important:</strong
>
</p>
<p class="mb-2 small">
This response is not likely to resolve the issue and may require
warrant entry on the property. If you want to help avoid that,
please provide contact information or other evidence.
</p>
<p class="mb-0 small">
<strong>You can still:</strong>
</p>
<ul class="mb-0 small">
<li>Call our office to provide additional information</li>
<li>Email photos showing current conditions</li>
<li>Schedule a time for inspection</li>
</ul>
</div>
</div>
{{ end }}
<!-- Reference Number -->
{{ if .ReferenceNumber }}
<div class="reference-number">
<small>
Reference number: <strong>{{ .ReferenceNumber }}</strong>
</small>
</div>
{{ end }}
<!-- Action Buttons -->
<div class="action-buttons mt-4">
<div class="d-grid gap-2">
{{ if .HasCompleteResponse }}
<a class="btn btn-outline-primary" href="?">
<i class="bi bi-telephone"></i>Remove useful and complete
</a>
{{ else if .HasUsefulInfo }}
<a class="btn btn-outline-primary" href="?complete=1">
<i class="bi bi-telephone"></i>Add complete response
</a>
{{ else }}
<a class="btn btn-outline-primary" href="?useful=1">
<i class="bi bi-telephone"></i>Add useful response
</a>
{{ end }}
</div>
</div>
</div>
{{ end }}

View file

@ -54,145 +54,4 @@
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 3 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 37%;"
aria-valuenow="37"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">
District observations indicate a possible mosquito problem at this
property
</h2>
<p class="text-muted mb-4">
Our inspector documented the following conditions during their visit.
Tap any image to view details.
</p>
<!-- Observation Images -->
<div class="mb-4">
<label class="form-label fw-semibold mb-3">Inspection Photos</label>
<div class="row g-3 mb-3">
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal1"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem;"></i>
<br />Photo 1
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal2"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem;"></i>
<br />Photo 2
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal3"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem;"></i>
<br />Photo 3
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal4"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem;"></i>
<br />Photo 4
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
</div>
</div>
<!-- Inspector Comments -->
<div class="mb-4">
<label class="form-label fw-semibold mb-3">Inspector Comments</label>
<div class="inspector-notes">
<div class="mb-2">
<small class="text-muted">
<i class="bi bi-person-badge"></i>
Inspector #1 | 2 days ago
</small>
</div>
<p class="mb-0">some fake comments here</p>
</div>
</div>
<div class="alert alert-info" role="alert">
<small>
<i class="bi bi-info-circle"></i>
These observations were made from outside the property. In the next
steps, you'll have an opportunity to provide your response and any
additional information.
</small>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a href="../compliance/address" class="btn btn-outline-secondary">
Back
</a>
<a href="evidence" class="btn btn-primary flex-grow-1"> Continue </a>
</div>
</main>
</div>
{{ end }}

View file

@ -27,140 +27,5 @@
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 6 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 75%;"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Contact information</h2>
<div class="benefit-box mb-4">
<p class="mb-0">
<i class="bi bi-info-circle"></i>
<strong>Why share your contact information?</strong><br />
<small>
Providing your contact information helps the District review your
response and coordinate with you if a visit is still needed. This
can save time and prevent unnecessary follow-up actions.
</small>
</p>
</div>
<form id="contact-form" method="POST" action="/compliance/contact">
<!-- Name -->
<div class="mb-3">
<label for="contact-name" class="form-label fw-semibold">
Name
<span class="optional-badge">(Optional)</span>
</label>
<input
type="text"
class="form-control"
id="contact-name"
name="name"
placeholder="Enter your name"
/>
</div>
<!-- Phone -->
<div class="mb-3">
<label for="contact-phone" class="form-label fw-semibold">
Phone Number
<span class="optional-badge">(Optional)</span>
</label>
<input
type="tel"
class="form-control"
id="contact-phone"
name="phone"
placeholder="(555) 123-4567"
/>
</div>
<!-- Can we text? -->
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="can-text"
name="can_text"
value="yes"
/>
<label class="form-check-label" for="can-text">
You may send text messages to this number
</label>
</div>
<small class="text-muted ms-4 d-block mt-1">
Text messages allow for faster communication and updates
</small>
</div>
<!-- Email -->
<div class="mb-4">
<label for="contact-email" class="form-label fw-semibold">
Email Address
<span class="optional-badge">(Optional)</span>
</label>
<input
type="email"
class="form-control"
id="contact-email"
name="email"
placeholder="your.email@example.com"
/>
<div class="form-text">
We'll send you a confirmation and any updates about this request
</div>
</div>
<div class="alert alert-light border" role="alert">
<small class="text-muted">
<i class="bi bi-shield-check"></i>
Your contact information will only be used for this compliance
matter and will be kept confidential.
</small>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a href="../compliance/permission" class="btn btn-outline-secondary">
Back
</a>
<a class="btn btn-primary flex-grow-1" href="process"> Continue </a>
</div>
</form>
</main>
</div>
<div class="container-fluid px-3 py-3"></div>
{{ end }}

View file

@ -2,313 +2,6 @@
{{ define "title" }}Upload Photos{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
.upload-area {
border: 2px dashed #0d6efd;
border-radius: 8px;
padding: 32px 16px;
text-align: center;
background-color: #fff;
cursor: pointer;
transition: all 0.2s ease;
}
.upload-area:hover {
background-color: #f8f9fa;
border-color: #0a58ca;
}
.upload-area.dragover {
background-color: #e7f1ff;
border-color: #0a58ca;
}
#file-input {
display: none;
}
.photo-preview {
position: relative;
width: 100%;
height: 120px;
border-radius: 8px;
overflow: hidden;
background-color: #e9ecef;
border: 1px solid #dee2e6;
}
.photo-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-preview .remove-btn {
position: absolute;
top: 4px;
right: 4px;
background-color: rgba(220, 53, 69, 0.9);
color: white;
border: none;
border-radius: 50%;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.photo-preview .remove-btn:hover {
background-color: rgba(220, 53, 69, 1);
transform: scale(1.1);
}
.guidance-list {
list-style: none;
padding-left: 0;
}
.guidance-list li {
padding: 8px 0;
padding-left: 28px;
position: relative;
}
.guidance-list li::before {
content: "✓";
position: absolute;
left: 0;
color: #198754;
font-weight: bold;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 4 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 50%;"
aria-valuenow="50"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Upload photos of the area</h2>
<p class="text-muted mb-3">
Please provide current photos to help us assess the situation.
</p>
<!-- Photo Guidance -->
<div class="card mb-4">
<div class="card-body">
<h6 class="card-subtitle mb-3 text-muted">
<i class="bi bi-lightbulb"></i> Helpful photos are:
</h6>
<ul class="guidance-list mb-0">
<li>Recent (taken within the last 24 hours)</li>
<li>Showing the specific area of concern</li>
<li>Making water conditions clearly visible</li>
</ul>
</div>
</div>
<form
id="photo-form"
method="POST"
action="/compliance/photos"
enctype="multipart/form-data"
>
<!-- Upload Area -->
<div class="mb-4">
<label class="form-label fw-semibold">Photos</label>
<div
class="upload-area"
id="upload-area"
onclick="document.getElementById('file-input').click()"
>
<i
class="bi bi-camera-fill"
style="font-size: 2.5rem; color: #0d6efd;"
></i>
<p class="mb-2 mt-2 fw-semibold">Tap to take photo or upload</p>
<p class="text-muted small mb-0">You can add multiple photos</p>
</div>
<input
type="file"
id="file-input"
name="photos"
accept="image/*"
capture="environment"
multiple
/>
</div>
<!-- Photo Previews -->
<div id="photo-previews" class="mb-4">
<!-- Thumbnails will be dynamically added here -->
<div class="row g-2" id="preview-container" style="display: none;">
<!-- Example preview structure (hidden by default, shown when photos added):
<div class="col-4">
<div class="photo-preview">
<img src="..." alt="Preview">
<button type="button" class="remove-btn" data-index="0">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
-->
</div>
</div>
<!-- Additional Comments -->
<div class="mb-4">
<label for="comments" class="form-label fw-semibold">
Additional Comments
<span class="text-muted fw-normal">(Optional)</span>
</label>
<textarea
class="form-control"
id="comments"
name="comments"
rows="4"
placeholder="Provide any additional information that may be helpful..."
></textarea>
<div class="form-text">
Example: "This standing water appeared after recent rain" or "I've
already taken steps to address this issue"
</div>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a href="../compliance/concern" class="btn btn-outline-secondary">
Back
</a>
<a href="permission" class="btn btn-primary flex-grow-1">
Continue
</a>
</div>
</form>
</main>
</div>
<script>
// File input handling
const fileInput = document.getElementById("file-input");
const uploadArea = document.getElementById("upload-area");
const previewContainer = document.getElementById("preview-container");
let selectedFiles = [];
fileInput.addEventListener("change", handleFiles);
function handleFiles(e) {
const files = Array.from(e.target.files);
files.forEach((file) => {
if (file.type.startsWith("image/")) {
selectedFiles.push(file);
}
});
updatePreviews();
}
function updatePreviews() {
if (selectedFiles.length === 0) {
previewContainer.style.display = "none";
return;
}
previewContainer.style.display = "block";
previewContainer.innerHTML = "";
selectedFiles.forEach((file, index) => {
const reader = new FileReader();
reader.onload = function (e) {
const col = document.createElement("div");
col.className = "col-4";
col.innerHTML = `
<div class="photo-preview">
<img src="${e.target.result}" alt="Preview ${index + 1}">
<button type="button" class="remove-btn" onclick="removePhoto(${index})">
<i class="bi bi-x-lg"></i>
</button>
</div>
`;
previewContainer.appendChild(col);
};
reader.readAsDataURL(file);
});
}
function removePhoto(index) {
selectedFiles.splice(index, 1);
updatePreviews();
// Reset file input
const dt = new DataTransfer();
selectedFiles.forEach((file) => dt.items.add(file));
fileInput.files = dt.files;
}
// Drag and drop support
uploadArea.addEventListener("dragover", (e) => {
e.preventDefault();
uploadArea.classList.add("dragover");
});
uploadArea.addEventListener("dragleave", () => {
uploadArea.classList.remove("dragover");
});
uploadArea.addEventListener("drop", (e) => {
e.preventDefault();
uploadArea.classList.remove("dragover");
const files = Array.from(e.dataTransfer.files);
files.forEach((file) => {
if (file.type.startsWith("image/")) {
selectedFiles.push(file);
}
});
updatePreviews();
});
</script>
{{ end }}

View file

@ -2,114 +2,9 @@
{{ define "title" }}Property Access{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
.access-option {
background-color: #fff;
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.access-option:hover {
border-color: #0d6efd;
background-color: #f8f9fa;
}
.access-option.selected {
border-color: #0d6efd;
background-color: #e7f1ff;
}
.access-option input[type="radio"] {
width: 20px;
height: 20px;
margin-right: 12px;
cursor: pointer;
}
.access-option label {
cursor: pointer;
margin-bottom: 0;
flex-grow: 1;
}
.conditional-section {
display: none;
margin-top: 16px;
padding: 16px;
background-color: #fff;
border-radius: 8px;
border-left: 4px solid #0d6efd;
}
.conditional-section.active {
display: block;
}
.dog-warning {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 12px;
border-radius: 4px;
margin-top: 12px;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 5 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 63%;"
aria-valuenow="63"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Property access permission</h2>

View file

@ -2,189 +2,6 @@
{{ define "title" }}What Happens Next{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
.process-step {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
border-left: 4px solid #0d6efd;
display: flex;
gap: 16px;
}
.step-number {
background-color: #0d6efd;
color: white;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
}
.step-content {
flex-grow: 1;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
.reminder-box {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 7 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 88%;"
aria-valuenow="88"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">What happens next</h2>
<p class="text-muted mb-4">
Understanding the review process helps you know what to expect.
</p>
<!-- Process Steps -->
<div class="mb-4">
<!-- Step 1 -->
<div class="process-step">
<div class="step-number">1</div>
<div class="step-content">
<h3 class="h6 mb-2">We review your response</h3>
<p class="mb-0 small text-muted">
Our team will evaluate the photos, comments, and information
you've provided to assess the current conditions at the property.
</p>
</div>
</div>
<!-- Step 2 -->
<div class="process-step">
<div class="step-number">2</div>
<div class="step-content">
<h3 class="h6 mb-2">Your response may reduce or close follow-up</h3>
<p class="mb-0 small text-muted">
If your photos and information clearly show that the concern has
been addressed, we may be able to reduce or close this matter
without additional inspection.
</p>
</div>
</div>
<!-- Step 3 -->
<div class="process-step">
<div class="step-number">3</div>
<div class="step-content">
<h3 class="h6 mb-2">We'll contact you if inspection is needed</h3>
<p class="mb-0 small text-muted">
If an inspection is still necessary and you provided contact
information, we will try to reach you in advance to coordinate
timing and access.
</p>
</div>
</div>
<!-- Step 4 -->
<div class="process-step">
<div class="step-number">4</div>
<div class="step-content">
<h3 class="h6 mb-2">
The compliance process continues if unresolved
</h3>
<p class="mb-0 small text-muted">
If the mosquito breeding source remains unaddressed, the District
will continue with standard compliance procedures to protect
public health.
</p>
</div>
</div>
</div>
<!-- Encouragement Box -->
<div class="encouragement-box mb-3">
<p class="mb-2">
<strong
><i class="bi bi-check-circle"></i> Your response helps</strong
>
</p>
<p class="mb-0 small">
By providing photos, access information, and contact details, you give
our team the ability to review the situation thoroughly before taking
further action. This can save everyone time and help resolve the
matter more efficiently.
</p>
</div>
<!-- Reminder Box -->
<div class="reminder-box">
<p class="mb-0 small">
<strong><i class="bi bi-exclamation-circle"></i> Important:</strong>
Submitting this form does not automatically close this compliance
request. The District must verify that mosquito breeding conditions
have been corrected to protect community health.
</p>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a href="../compliance/contact" class="btn btn-outline-secondary">
Back
</a>
<a href="submit" class="btn btn-primary flex-grow-1"> Continue </a>
</div>
</main>
</div>
{{ end }}

View file

@ -2,238 +2,7 @@
{{ define "title" }}Submit Response{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
.summary-section {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
border: 1px solid #dee2e6;
}
.summary-section h3 {
font-size: 1rem;
font-weight: 600;
margin-bottom: 12px;
color: #495057;
}
.summary-item {
padding: 8px 0;
border-bottom: 1px solid #f8f9fa;
}
.summary-item:last-child {
border-bottom: none;
}
.summary-label {
font-weight: 500;
color: #6c757d;
font-size: 0.875rem;
}
.summary-value {
color: #212529;
margin-top: 4px;
}
.photo-count {
display: inline-flex;
align-items: center;
gap: 8px;
background-color: #e7f1ff;
padding: 4px 12px;
border-radius: 16px;
font-size: 0.875rem;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 16px;
font-size: 0.875rem;
font-weight: 500;
}
.status-provided {
background-color: #d1e7dd;
color: #0f5132;
}
.status-not-provided {
background-color: #f8d7da;
color: #842029;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
.validation-alert {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
}
.submit-btn {
font-size: 1.125rem;
padding: 14px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="container-fluid px-3 py-3">
<!-- Header -->
<header class="text-center mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center justify-content-center mb-2">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} logo"
class="me-2"
style="height: 40px; width: auto;"
/>
<h1 class="h5 mb-0">{{ .District.Name }}</h1>
</div>
<div class="text-muted small">
<i class="bi bi-telephone"></i> {{ .District.OfficePhone }}
</div>
</header>
<!-- Progress Bar -->
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted">Step 8 of 8</span>
</div>
<div class="progress" style="height: 8px;">
<div
class="progress-bar"
role="progressbar"
style="width: 100%;"
aria-valuenow="100"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Review and submit your response</h2>
<div class="encouragement-box mb-4">
<p class="mb-2">
<strong><i class="bi bi-lightbulb"></i> Before you submit</strong>
</p>
<p class="mb-0 small">
Providing photos, access permissions, and contact information gives
the District the best opportunity to review your response and
potentially close this matter without further action. The more detail
you provide, the better we can assess the situation.
</p>
</div>
<form id="submit-form" method="POST" action="/compliance/submit">
<!-- Response Summary -->
<div class="mb-4">
<h3 class="h6 mb-3 text-muted">Your Response Summary</h3>
<!-- Address -->
<div class="summary-section">
<h3><i class="bi bi-geo-alt"></i> Property Address</h3>
<div class="summary-item">
<div class="summary-value">
123 Main St
<span class="status-badge status-provided ms-2">
<i class="bi bi-check-circle"></i> Provided
</span>
</div>
</div>
</div>
<!-- Photos -->
<div class="summary-section">
<h3><i class="bi bi-camera"></i> Photos</h3>
<div class="summary-item">
<div class="summary-value">
<span class="photo-count">
<i class="bi bi-images"></i>
3 photos uploaded
</span>
</div>
<div class="summary-value mt-2">
<div class="summary-label">Comments:</div>
<small class="text-muted">These are my comments</small>
</div>
</div>
</div>
<!-- Access Permission -->
<div class="summary-section">
<h3><i class="bi bi-door-open"></i> Property Access</h3>
<div class="summary-item">
<div class="summary-value">
<span class="status-badge status-provided">
<i class="bi bi-check-circle"></i> Entry permitted without
owner present
</span>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="summary-section">
<h3><i class="bi bi-person"></i> Contact Information</h3>
<div class="summary-item">
<div class="summary-label">Name</div>
<div class="summary-value">Kai Fu Lee</div>
</div>
<div class="summary-item">
<div class="summary-label">Phone</div>
<div class="summary-value">
123-555-6789
<small class="text-muted">(texting OK)</small>
</div>
</div>
<div class="summary-item">
<div class="summary-label">Email</div>
<div class="summary-value">me@aol.com</div>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="d-grid gap-2 mt-4">
<a
class="btn btn-primary btn-lg submit-btn"
href="../compliance/complete"
>
<i class="bi bi-check-circle"></i>
Submit Response
</a>
<a class="btn btn-outline-secondary" href="process"> Back </a>
</div>
<div class="text-center mt-3">
<small class="text-muted">
By submitting, you confirm the information provided is accurate to
the best of your knowledge.
</small>
</div>
</form>
</main>
</div>
<!-- Main Content -->
{{ end }}

View file

@ -1,3 +1,9 @@
<style scoped>
.progress-bar {
background-color: #0d6efd;
transition: width 0.3s ease;
}
</style>
<template>
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">

View file

@ -1,3 +1,21 @@
<style scoped>
.map-placeholder {
width: 100%;
height: 250px;
background-color: #e9ecef;
border: 2px dashed #6c757d;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: #6c757d;
font-size: 14px;
}
#address-input {
font-size: 16px;
}
</style>
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
@ -40,8 +58,12 @@
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<a class="btn btn-outline-secondary" href="../compliance"> Back </a>
<a class="btn btn-primary flex-grow-1" href="concern"> Continue </a>
<RouterLink class="btn btn-outline-secondary" to="../compliance">
Back
</RouterLink>
<RouterLink class="btn btn-primary flex-grow-1" to="./concern">
Continue
</RouterLink>
</div>
</form>
</main>

View file

@ -0,0 +1,288 @@
<style scoped>
.completion-container {
max-width: 500px;
margin: 0 auto;
padding: 32px 16px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.district-branding {
text-align: center;
margin-bottom: 32px;
}
.district-branding img {
height: 80px;
width: auto;
margin-bottom: 16px;
}
.district-branding h1 {
font-size: 1.25rem;
color: #495057;
margin-bottom: 8px;
}
.district-branding .phone {
color: #6c757d;
font-size: 0.9rem;
}
.success-icon {
width: 80px;
height: 80px;
background-color: #d1e7dd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.success-icon i {
font-size: 2.5rem;
color: #0f5132;
}
.warning-icon {
width: 80px;
height: 80px;
background-color: #fff3cd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.warning-icon i {
font-size: 2.5rem;
color: #997404;
}
.alert-icon {
width: 80px;
height: 80px;
background-color: #f8d7da;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.alert-icon i {
font-size: 2.5rem;
color: #842029;
}
.message-card {
background-color: #fff;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
width: 100%;
margin-bottom: 24px;
}
.message-card h2 {
font-size: 1.5rem;
margin-bottom: 16px;
text-align: center;
}
.message-card p {
color: #495057;
line-height: 1.6;
margin-bottom: 12px;
}
.message-card p:last-child {
margin-bottom: 0;
}
.info-box {
background-color: #f8f9fa;
border-left: 4px solid #6c757d;
padding: 16px;
border-radius: 4px;
margin-top: 16px;
}
.warning-box {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
margin-top: 16px;
}
.reference-number {
text-align: center;
color: #6c757d;
font-size: 0.9rem;
margin-top: 24px;
}
.action-buttons {
width: 100%;
}
</style>
<template>
<div class="completion-container">
<!-- District Branding -->
<div class="district-branding">
<img :src="district.url_logo" :alt="district.name + 'logo'" />
<h1>{{ district.name }}</h1>
<div class="phone">
<i class="bi bi-telephone"></i> {{ district.phone_office }}
</div>
</div>
<template v-if="hasCompleteResponse">
<!-- Mode 1: Complete response with contact info -->
<div class="success-icon">
<i class="bi bi-check-circle-fill"></i>
</div>
<div class="message-card">
<h2>Thank you</h2>
<p>
Your response has been submitted successfully. We will review your
submission and contact you if further action is needed.
</p>
<div class="info-box">
<p class="mb-2">
<strong>What you can expect:</strong>
</p>
<ul class="mb-0 small">
<li>Our team will review your photos and information</li>
<li>If we need to schedule a visit, we'll contact you first</li>
<li>
You'll receive updates at the contact information you provided
</li>
</ul>
</div>
</div>
</template>
<template v-else-if="hasUsefulInfo">
<!-- Mode 2: Useful info but no contact -->
<div class="warning-icon">
<i class="bi bi-exclamation-circle-fill"></i>
</div>
<div class="message-card">
<h2>Response Received</h2>
<p>
Thank you for your submission. We will review your information and let
you know if further action is needed.
</p>
<div class="warning-box">
<p class="mb-2">
<strong><i class="bi bi-info-circle"></i> Important notice:</strong>
</p>
<p class="mb-0 small">
You did not provide contact information. If further action is
needed, the District may need to use warrant authority to enter the
property. We prefer to coordinate access directly, and contact
information makes that much easier.
</p>
</div>
<p class="text-center mt-3 mb-0">
<small class="text-muted">
If you'd like to add contact information, please call our office.
</small>
</p>
</div>
</template>
<template v-else>
<!-- Mode 3: Insufficient information -->
<div class="alert-icon">
<i class="bi bi-exclamation-triangle-fill"></i>
</div>
<div class="message-card">
<h2>Response Received</h2>
<p>
Your response has been recorded, but it does not contain enough
information for us to resolve this matter.
</p>
<div class="warning-box">
<p class="mb-2">
<strong
><i class="bi bi-exclamation-triangle"></i> Important:</strong
>
</p>
<p class="mb-2 small">
This response is not likely to resolve the issue and may require
warrant entry on the property. If you want to help avoid that,
please provide contact information or other evidence.
</p>
<p class="mb-0 small">
<strong>You can still:</strong>
</p>
<ul class="mb-0 small">
<li>Call our office to provide additional information</li>
<li>Email photos showing current conditions</li>
<li>Schedule a time for inspection</li>
</ul>
</div>
</div>
</template>
<!-- Reference Number -->
<div class="reference-number">
<small>
Reference number: <strong>{{ referenceNumber }}</strong>
</small>
</div>
<!-- Action Buttons -->
<div class="action-buttons mt-4">
<div class="d-grid gap-2">
<template v-if="hasCompleteResponse">
<button class="btn btn-outline-primary" @click="changeResponse()">
<i class="bi bi-telephone"></i>Remove useful and complete
</button>
</template>
<template v-else-if="hasUsefulInfo">
<button class="btn btn-outline-primary" @click="changeResponse()">
<i class="bi bi-telephone"></i>Add complete response
</button>
</template>
<template v-else>
<button class="btn btn-outline-primary" @click="changeResponse()">
<i class="bi bi-telephone"></i>Add useful response
</button>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
const referenceNumber = "abc-123";
const hasCompleteResponse = ref<boolean>(false);
const hasUsefulInfo = ref<boolean>(false);
function changeResponse() {
if (hasCompleteResponse.value && hasUsefulInfo.value) {
hasCompleteResponse.value = false;
hasUsefulInfo.value = false;
} else if (hasUsefulInfo.value) {
hasCompleteResponse.value = true;
} else {
hasUsefulInfo.value = true;
}
}
</script>

View file

@ -0,0 +1,125 @@
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="3" />
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">
District observations indicate a possible mosquito problem at this
property
</h2>
<p class="text-muted mb-4">
Our inspector documented the following conditions during their visit.
Tap any image to view details.
</p>
<!-- Observation Images -->
<div class="mb-4">
<label class="form-label fw-semibold mb-3">Inspection Photos</label>
<div class="row g-3 mb-3">
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal1"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem"></i>
<br />Photo 1
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal2"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem"></i>
<br />Photo 2
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal3"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem"></i>
<br />Photo 3
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
<div class="col-6">
<div
class="observation-image"
data-bs-toggle="modal"
data-bs-target="#imageModal4"
>
<span class="text-center">
<i class="bi bi-camera" style="font-size: 2rem"></i>
<br />Photo 4
</span>
<div class="overlay">Tap to view</div>
</div>
</div>
</div>
</div>
<!-- Inspector Comments -->
<div class="mb-4">
<label class="form-label fw-semibold mb-3">Inspector Comments</label>
<div class="inspector-notes">
<div class="mb-2">
<small class="text-muted">
<i class="bi bi-person-badge"></i>
Inspector #1 | 2 days ago
</small>
</div>
<p class="mb-0">some fake comments here</p>
</div>
</div>
<div class="alert alert-info" role="alert">
<small>
<i class="bi bi-info-circle"></i>
These observations were made from outside the property. In the next
steps, you'll have an opportunity to provide your response and any
additional information.
</small>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<RouterLink to="./address" class="btn btn-outline-secondary">
Back
</RouterLink>
<RouterLink to="evidence" class="btn btn-primary flex-grow-1">
Continue
</RouterLink>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
</script>

View file

@ -0,0 +1,119 @@
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="6" />
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Contact information</h2>
<div class="benefit-box mb-4">
<p class="mb-0">
<i class="bi bi-info-circle"></i>
<strong>Why share your contact information?</strong><br />
<small>
Providing your contact information helps the District review your
response and coordinate with you if a visit is still needed. This
can save time and prevent unnecessary follow-up actions.
</small>
</p>
</div>
<form id="contact-form" method="POST" action="/compliance/contact">
<!-- Name -->
<div class="mb-3">
<label for="contact-name" class="form-label fw-semibold">
Name
<span class="optional-badge">(Optional)</span>
</label>
<input
type="text"
class="form-control"
id="contact-name"
name="name"
placeholder="Enter your name"
/>
</div>
<!-- Phone -->
<div class="mb-3">
<label for="contact-phone" class="form-label fw-semibold">
Phone Number
<span class="optional-badge">(Optional)</span>
</label>
<input
type="tel"
class="form-control"
id="contact-phone"
name="phone"
placeholder="(555) 123-4567"
/>
</div>
<!-- Can we text? -->
<div class="mb-3">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="can-text"
name="can_text"
value="yes"
/>
<label class="form-check-label" for="can-text">
You may send text messages to this number
</label>
</div>
<small class="text-muted ms-4 d-block mt-1">
Text messages allow for faster communication and updates
</small>
</div>
<!-- Email -->
<div class="mb-4">
<label for="contact-email" class="form-label fw-semibold">
Email Address
<span class="optional-badge">(Optional)</span>
</label>
<input
type="email"
class="form-control"
id="contact-email"
name="email"
placeholder="your.email@example.com"
/>
<div class="form-text">
We'll send you a confirmation and any updates about this request
</div>
</div>
<div class="alert alert-light border" role="alert">
<small class="text-muted">
<i class="bi bi-shield-check"></i>
Your contact information will only be used for this compliance
matter and will be kept confidential.
</small>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<RouterLink class="btn btn-outline-secondary" to="./permission">
Back
</RouterLink>
<RouterLink class="btn btn-primary flex-grow-1" to="process">
Continue
</RouterLink>
</div>
</form>
</main>
</div>
</template>
<script setup lang="ts">
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
</script>

View file

@ -0,0 +1,198 @@
<style scoped>
.upload-area {
border: 2px dashed #0d6efd;
border-radius: 8px;
padding: 32px 16px;
text-align: center;
background-color: #fff;
cursor: pointer;
transition: all 0.2s ease;
}
.upload-area:hover {
background-color: #f8f9fa;
border-color: #0a58ca;
}
.upload-area.dragover {
background-color: #e7f1ff;
border-color: #0a58ca;
}
#file-input {
display: none;
}
.photo-preview {
position: relative;
width: 100%;
height: 120px;
border-radius: 8px;
overflow: hidden;
background-color: #e9ecef;
border: 1px solid #dee2e6;
}
.photo-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-preview .remove-btn {
position: absolute;
top: 4px;
right: 4px;
background-color: rgba(220, 53, 69, 0.9);
color: white;
border: none;
border-radius: 50%;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.photo-preview .remove-btn:hover {
background-color: rgba(220, 53, 69, 1);
transform: scale(1.1);
}
.guidance-list {
list-style: none;
padding-left: 0;
}
.guidance-list li {
padding: 8px 0;
padding-left: 28px;
position: relative;
}
.guidance-list li::before {
content: "✓";
position: absolute;
left: 0;
color: #198754;
font-weight: bold;
}
</style>
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="4" />
<!-- Main Content -->
<main>
<h2 class="h4 mb-3">Upload photos of the area</h2>
<p class="text-muted mb-3">
Please provide current photos to help us assess the situation.
</p>
<!-- Photo Guidance -->
<div class="card mb-4">
<div class="card-body">
<h6 class="card-subtitle mb-3 text-muted">
<i class="bi bi-lightbulb"></i> Helpful photos are:
</h6>
<ul class="guidance-list mb-0">
<li>Recent (taken within the last 24 hours)</li>
<li>Showing the specific area of concern</li>
<li>Making water conditions clearly visible</li>
</ul>
</div>
</div>
<form
id="photo-form"
method="POST"
action="/compliance/photos"
enctype="multipart/form-data"
>
<!-- Upload Area -->
<div class="mb-4">
<label class="form-label fw-semibold">Photos</label>
<div
class="upload-area"
id="upload-area"
onclick="document.getElementById('file-input').click()"
>
<i
class="bi bi-camera-fill"
style="font-size: 2.5rem; color: #0d6efd"
></i>
<p class="mb-2 mt-2 fw-semibold">Tap to take photo or upload</p>
<p class="text-muted small mb-0">You can add multiple photos</p>
</div>
<input
type="file"
id="file-input"
name="photos"
accept="image/*"
capture="environment"
multiple
/>
</div>
<!-- Photo Previews -->
<div id="photo-previews" class="mb-4">
<!-- Thumbnails will be dynamically added here -->
<div class="row g-2" id="preview-container" style="display: none">
<!-- Example preview structure (hidden by default, shown when photos added):
<div class="col-4">
<div class="photo-preview">
<img src="..." alt="Preview">
<button type="button" class="remove-btn" data-index="0">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
-->
</div>
</div>
<!-- Additional Comments -->
<div class="mb-4">
<label for="comments" class="form-label fw-semibold">
Additional Comments
<span class="text-muted fw-normal">(Optional)</span>
</label>
<textarea
class="form-control"
id="comments"
name="comments"
rows="4"
placeholder="Provide any additional information that may be helpful..."
></textarea>
<div class="form-text">
Example: "This standing water appeared after recent rain" or "I've
already taken steps to address this issue"
</div>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<RouterLink to="./concern" class="btn btn-outline-secondary">
Back
</RouterLink>
<RouterLink to="permission" class="btn btn-primary flex-grow-1">
Continue
</RouterLink>
</div>
</form>
</main>
</div>
</template>
<script setup lang="ts">
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
</script>

View file

@ -1,7 +1,7 @@
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<ProgressBarCompliance :step="2" />
<ProgressBarCompliance :step="1" />
<!-- Main Content -->
<main>

View file

@ -0,0 +1,307 @@
<style scoped>
.access-option {
background-color: #fff;
border: 2px solid #dee2e6;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.access-option:hover {
border-color: #0d6efd;
background-color: #f8f9fa;
}
.access-option.selected {
border-color: #0d6efd;
background-color: #e7f1ff;
}
.access-option input[type="radio"] {
width: 20px;
height: 20px;
margin-right: 12px;
cursor: pointer;
}
.access-option label {
cursor: pointer;
margin-bottom: 0;
flex-grow: 1;
}
.conditional-section {
display: block;
margin-top: 16px;
padding: 16px;
background-color: #fff;
border-radius: 8px;
border-left: 4px solid #0d6efd;
}
.dog-warning {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 12px;
border-radius: 4px;
margin-top: 12px;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
</style>
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="5" />
<main>
<h2 class="h4 mb-3">Property access permission</h2>
<p class="text-muted mb-4">
Granting access allows our technicians to inspect and potentially treat
mosquito sources more quickly, helping protect you and your neighbors.
</p>
<form id="access-form" method="POST" action="/compliance/access">
<!-- Access Options -->
<div class="mb-4">
<label class="form-label fw-semibold mb-3"
>Please select an option:</label
>
<!-- Option 1: Enter without owner present -->
<div class="access-option">
<div class="d-flex align-items-start">
<input
type="radio"
id="access-allowed"
value="allowed"
v-model="selectedPermission"
class="mt-1"
/>
<label for="access-allowed">
<div class="fw-semibold">
A technician may enter even if I am not home
</div>
<small class="text-muted">Fastest resolution</small>
</label>
</div>
</div>
<!-- Conditional fields for Option 1 -->
<div
v-if="selectedPermission == 'allowed'"
class="conditional-section"
>
<div class="mb-3">
<label for="access-instructions" class="form-label">
Access Instructions
<span class="text-muted">(Optional)</span>
</label>
<textarea
class="form-control"
id="access-instructions"
name="access_instructions"
rows="3"
placeholder="Example: Side gate on left, backyard near shed..."
></textarea>
</div>
<div class="mb-3">
<label for="gate-code" class="form-label">
Gate Code
<span class="text-muted">(Optional)</span>
</label>
<input
type="text"
class="form-control"
id="gate-code"
name="gate_code"
placeholder="Enter code if applicable"
/>
</div>
<div class="mb-3">
<label class="form-label">Dog on Property?</label>
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="has_dog"
id="dog-no"
value="no"
checked
onchange="toggleDogWarning()"
/>
<label class="form-check-label" for="dog-no"> No </label>
</div>
<div class="form-check">
<input
class="form-check-input"
id="dog-yes"
type="radio"
name="has_dog"
v-model="hasDog"
onchange="toggleDogWarning()"
/>
<label class="form-check-label" for="dog-yes"> Yes </label>
</div>
</div>
<div class="dog-warning" v-if="hasDog">
<small>
<i class="bi bi-exclamation-triangle"></i>
<strong>Important:</strong> Our staff will only enter if the dog
is secured indoors. Please ensure your pet is safely inside
before a technician arrives.
</small>
</div>
</div>
<!-- Option 2: Enter with owner present -->
<div class="access-option">
<div class="d-flex align-items-start">
<input
type="radio"
id="access-with-owner"
value="with_owner"
v-model="selectedPermission"
class="mt-1"
/>
<label for="access-with-owner">
<div class="fw-semibold">
A technician may enter, but I want to be present
</div>
<small class="text-muted">Requires scheduling</small>
</label>
</div>
</div>
<!-- Conditional fields for Option 2 -->
<div
class="conditional-section"
v-if="selectedPermission == 'with_owner'"
>
<div class="form-check mb-3">
<input
class="form-check-input"
type="checkbox"
id="request-scheduled"
name="request_scheduled"
value="yes"
/>
<label class="form-check-label" for="request-scheduled">
I would like to request a scheduled visit
</label>
</div>
<div class="mb-3">
<label for="availability-notes" class="form-label">
Availability / Access Notes
<span class="text-muted">(Optional)</span>
</label>
<textarea
class="form-control"
id="availability-notes"
name="availability_notes"
rows="3"
placeholder="Example: Available weekday mornings, please call before visiting..."
></textarea>
</div>
</div>
<!-- Option 3: Not granting entry -->
<div class="access-option">
<div class="d-flex align-items-start">
<input
type="radio"
id="access-denied"
value="denied"
v-model="selectedPermission"
class="mt-1"
/>
<label for="access-denied">
<div class="fw-semibold">
I am not granting entry at this time
</div>
<small class="text-muted">May require follow-up</small>
</label>
</div>
</div>
<!-- Conditional message for Option 3 -->
<div
class="conditional-section"
v-if="selectedPermission == 'denied'"
>
<div class="encouragement-box">
<p class="mb-2">
<strong>We understand.</strong> Your cooperation is voluntary,
but mosquito breeding sources can affect the health and comfort
of the entire community.
</p>
<p class="mb-2">
To help us review this situation and avoid unnecessary
escalation, we strongly encourage you to:
</p>
<ul class="mb-2">
<li>Provide detailed photos of the area</li>
<li>Share your contact information</li>
<li>Include any context that may be helpful</li>
</ul>
<p class="mb-0">
<small class="text-muted">
This allows our team to assess whether the concern has been
addressed or if additional steps may be necessary.
</small>
</p>
</div>
</div>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<RouterLink class="btn btn-outline-secondary" to="./evidence">
Back
</RouterLink>
<RouterLink class="btn btn-primary flex-grow-1" to="contact">
Continue
</RouterLink>
</div>
</form>
</main>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
const hasDog = ref<boolean>(false);
const selectedPermission = ref<string>("");
function toggleDogWarning() {
/*
const dogYes = document.getElementById("dog-yes");
const dogWarning = document.getElementById("dog-warning");
if (dogYes.checked) {
dogWarning.style.display = "block";
} else {
dogWarning.style.display = "none";
}
*/
}
</script>

View file

@ -0,0 +1,157 @@
<style scoped>
.process-step {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
border-left: 4px solid #0d6efd;
display: flex;
gap: 16px;
}
.step-number {
background-color: #0d6efd;
color: white;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
flex-shrink: 0;
}
.step-content {
flex-grow: 1;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
.reminder-box {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
}
</style>
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="7" />
<main>
<h2 class="h4 mb-3">What happens next</h2>
<p class="text-muted mb-4">
Understanding the review process helps you know what to expect.
</p>
<!-- Process Steps -->
<div class="mb-4">
<!-- Step 1 -->
<div class="process-step">
<div class="step-number">1</div>
<div class="step-content">
<h3 class="h6 mb-2">We review your response</h3>
<p class="mb-0 small text-muted">
Our team will evaluate the photos, comments, and information
you've provided to assess the current conditions at the property.
</p>
</div>
</div>
<!-- Step 2 -->
<div class="process-step">
<div class="step-number">2</div>
<div class="step-content">
<h3 class="h6 mb-2">Your response may reduce or close follow-up</h3>
<p class="mb-0 small text-muted">
If your photos and information clearly show that the concern has
been addressed, we may be able to reduce or close this matter
without additional inspection.
</p>
</div>
</div>
<!-- Step 3 -->
<div class="process-step">
<div class="step-number">3</div>
<div class="step-content">
<h3 class="h6 mb-2">We'll contact you if inspection is needed</h3>
<p class="mb-0 small text-muted">
If an inspection is still necessary and you provided contact
information, we will try to reach you in advance to coordinate
timing and access.
</p>
</div>
</div>
<!-- Step 4 -->
<div class="process-step">
<div class="step-number">4</div>
<div class="step-content">
<h3 class="h6 mb-2">
The compliance process continues if unresolved
</h3>
<p class="mb-0 small text-muted">
If the mosquito breeding source remains unaddressed, the District
will continue with standard compliance procedures to protect
public health.
</p>
</div>
</div>
</div>
<!-- Encouragement Box -->
<div class="encouragement-box mb-3">
<p class="mb-2">
<strong
><i class="bi bi-check-circle"></i> Your response helps</strong
>
</p>
<p class="mb-0 small">
By providing photos, access information, and contact details, you give
our team the ability to review the situation thoroughly before taking
further action. This can save everyone time and help resolve the
matter more efficiently.
</p>
</div>
<!-- Reminder Box -->
<div class="reminder-box">
<p class="mb-0 small">
<strong><i class="bi bi-exclamation-circle"></i> Important:</strong>
Submitting this form does not automatically close this compliance
request. The District must verify that mosquito breeding conditions
have been corrected to protect community health.
</p>
</div>
<!-- Navigation Buttons -->
<div class="d-flex gap-2 mt-4">
<RouterLink to="contact" class="btn btn-outline-secondary">
Back
</RouterLink>
<RouterLink to="submit" class="btn btn-primary flex-grow-1">
Continue
</RouterLink>
</div>
</main>
</div>
</template>
<script setup lang="ts">
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
</script>

View file

@ -0,0 +1,204 @@
<style scoped>
.summary-section {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
border: 1px solid #dee2e6;
}
.summary-section h3 {
font-size: 1rem;
font-weight: 600;
margin-bottom: 12px;
color: #495057;
}
.summary-item {
padding: 8px 0;
border-bottom: 1px solid #f8f9fa;
}
.summary-item:last-child {
border-bottom: none;
}
.summary-label {
font-weight: 500;
color: #6c757d;
font-size: 0.875rem;
}
.summary-value {
color: #212529;
margin-top: 4px;
}
.photo-count {
display: inline-flex;
align-items: center;
gap: 8px;
background-color: #e7f1ff;
padding: 4px 12px;
border-radius: 16px;
font-size: 0.875rem;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 16px;
font-size: 0.875rem;
font-weight: 500;
}
.status-provided {
background-color: #d1e7dd;
color: #0f5132;
}
.status-not-provided {
background-color: #f8d7da;
color: #842029;
}
.encouragement-box {
background-color: #d1ecf1;
border-left: 4px solid #0dcaf0;
padding: 16px;
border-radius: 4px;
}
.validation-alert {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 16px;
border-radius: 4px;
}
.submit-btn {
font-size: 1.125rem;
padding: 14px;
}
</style>
<template>
<div class="container-fluid px-3 py-3">
<HeaderCompliance :district="district" />
<!-- Progress Bar -->
<ProgressBarCompliance :step="8" />
<main>
<h2 class="h4 mb-3">Review and submit your response</h2>
<div class="encouragement-box mb-4">
<p class="mb-2">
<strong><i class="bi bi-lightbulb"></i> Before you submit</strong>
</p>
<p class="mb-0 small">
Providing photos, access permissions, and contact information gives
the District the best opportunity to review your response and
potentially close this matter without further action. The more detail
you provide, the better we can assess the situation.
</p>
</div>
<form id="submit-form" method="POST" action="/compliance/submit">
<!-- Response Summary -->
<div class="mb-4">
<h3 class="h6 mb-3 text-muted">Your Response Summary</h3>
<!-- Address -->
<div class="summary-section">
<h3><i class="bi bi-geo-alt"></i> Property Address</h3>
<div class="summary-item">
<div class="summary-value">
123 Main St
<span class="status-badge status-provided ms-2">
<i class="bi bi-check-circle"></i> Provided
</span>
</div>
</div>
</div>
<!-- Photos -->
<div class="summary-section">
<h3><i class="bi bi-camera"></i> Photos</h3>
<div class="summary-item">
<div class="summary-value">
<span class="photo-count">
<i class="bi bi-images"></i>
3 photos uploaded
</span>
</div>
<div class="summary-value mt-2">
<div class="summary-label">Comments:</div>
<small class="text-muted">These are my comments</small>
</div>
</div>
</div>
<!-- Access Permission -->
<div class="summary-section">
<h3><i class="bi bi-door-open"></i> Property Access</h3>
<div class="summary-item">
<div class="summary-value">
<span class="status-badge status-provided">
<i class="bi bi-check-circle"></i> Entry permitted without
owner present
</span>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="summary-section">
<h3><i class="bi bi-person"></i> Contact Information</h3>
<div class="summary-item">
<div class="summary-label">Name</div>
<div class="summary-value">Kai Fu Lee</div>
</div>
<div class="summary-item">
<div class="summary-label">Phone</div>
<div class="summary-value">
123-555-6789
<small class="text-muted">(texting OK)</small>
</div>
</div>
<div class="summary-item">
<div class="summary-label">Email</div>
<div class="summary-value">me@aol.com</div>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="d-grid gap-2 mt-4">
<RouterLink class="btn btn-primary btn-lg submit-btn" to="complete">
<i class="bi bi-check-circle"></i>
Submit Response
</RouterLink>
<RouterLink class="btn btn-outline-secondary" to="process">
Back
</RouterLink>
</div>
<div class="text-center mt-3">
<small class="text-muted">
By submitting, you confirm the information provided is accurate to
the best of your knowledge.
</small>
</div>
</form>
</main>
</div>
</template>
<script setup lang="ts">
import type { District } from "@/type/api";
import HeaderCompliance from "@/rmo/components/HeaderCompliance.vue";
import ProgressBarCompliance from "@/rmo/components/ProgressBarCompliance.vue";
interface Props {
district: District;
}
const props = defineProps<Props>();
</script>

View file

@ -2,7 +2,14 @@ import { createRouter, createWebHistory } from "vue-router";
import type { RouteRecordRaw } from "vue-router";
import Compliance from "@/rmo/view/Compliance.vue";
import ComplianceAddress from "@/rmo/content/compliance/Address.vue";
import ComplianceComplete from "@/rmo/content/compliance/Complete.vue";
import ComplianceConcern from "@/rmo/content/compliance/Concern.vue";
import ComplianceContact from "@/rmo/content/compliance/Contact.vue";
import ComplianceEvidence from "@/rmo/content/compliance/Evidence.vue";
import ComplianceIntro from "@/rmo/content/compliance/Intro.vue";
import CompliancePermission from "@/rmo/content/compliance/Permission.vue";
import ComplianceProcess from "@/rmo/content/compliance/Process.vue";
import ComplianceSubmit from "@/rmo/content/compliance/Submit.vue";
import HomeBase from "@/rmo/view/Home.vue";
import HomeDistrict from "@/rmo/view/district/Home.vue";
import NuisanceBase from "@/rmo/view/Nuisance.vue";
@ -40,6 +47,41 @@ const routes: RouteRecordRaw[] = [
name: "ComplianceAddress",
path: "address",
},
{
component: ComplianceComplete,
name: "ComplianceComplete",
path: "complete",
},
{
component: ComplianceConcern,
name: "ComplianceConcern",
path: "concern",
},
{
component: ComplianceContact,
name: "ComplianceContact",
path: "contact",
},
{
component: ComplianceEvidence,
name: "ComplianceEvidence",
path: "evidence",
},
{
component: CompliancePermission,
name: "CompliancePermission",
path: "permission",
},
{
component: ComplianceProcess,
name: "ComplianceProcess",
path: "process",
},
{
component: ComplianceSubmit,
name: "ComplianceSubmit",
path: "submit",
},
],
component: Compliance,
path: "/district/:slug/compliance",