Add simple compliance landing page

This commit is contained in:
Eli Ribble 2026-04-03 00:12:52 +00:00
parent 4b87c74f41
commit 457f123f69
No known key found for this signature in database
4 changed files with 181 additions and 23 deletions

View file

@ -0,0 +1,144 @@
{{ template "rmo/layout/base.html" . }}
{{ define "title" }}Compliance Request{{ end }}
{{ define "extraheader" }}
<style>
body {
background-color: #f8f9fa;
}
.page-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.content-card {
background-color: white;
border-radius: 15px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 25px;
margin-bottom: 20px;
}
.logo-area {
text-align: center;
margin-bottom: 20px;
}
.logo {
height: 50px;
max-width: 200px;
margin: 0 auto;
}
.map-container {
height: 300px;
background-color: #e9ecef;
border-radius: 10px;
margin-bottom: 20px;
position: relative;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-image: url("https://placehold.co/600x300/e9ecef/adb5bd?text=Map+View");
background-size: cover;
background-position: center;
}
.map-pin {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -100%);
color: #dc3545;
font-size: 30px;
}
.address-container {
background-color: #f8f9fa;
border-radius: 10px;
padding: 15px;
margin-bottom: 20px;
border-left: 4px solid #0d6efd;
}
.action-buttons {
display: flex;
gap: 10px;
}
.progress-container {
margin: 30px 0 20px;
}
.progress {
height: 8px;
}
</style>
{{ end }}
{{ define "content" }}
<div class="page-container">
<div class="logo-area">
<img
src="{{ .District.URLLogo }}"
alt="{{ .District.Name }} Logo"
class="logo"
/>
</div>
<div class="progress-container">
<div class="d-flex justify-content-between mb-1">
<span class="text-muted small">Location</span>
<span class="text-muted small">1 of 4</span>
</div>
<div class="progress">
<div
class="progress-bar"
role="progressbar"
style="width: 25%"
aria-valuenow="25"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
</div>
<div class="content-card">
<h1 class="h4 mb-4">Confirm Property Location</h1>
<div class="map-container">
<div class="map-placeholder"></div>
<div class="map-pin">
<i class="bi bi-geo-alt-fill"></i>
</div>
</div>
<div class="address-container">
<div class="mb-2"><strong>Detected Address:</strong></div>
<h5>123 Maple Street, Riverside, CA 92501</h5>
</div>
<div class="mb-4">
<p>Is this the correct location of the property in question?</p>
</div>
<div class="action-buttons">
<a
href="/mock/report/%7bcode%7d/evidence"
class="btn btn-success flex-grow-1"
>
<i class="bi bi-check-circle me-2"></i> Correct
</a>
<a
href="/mock/report/%7bcode%7d/update"
class="btn btn-outline-primary flex-grow-1"
>
<i class="bi bi-geo-alt me-2"></i> Update Location
</a>
</div>
</div>
<div class="text-center text-muted small">
<p>
If you need assistance, please contact Vector Control at (555) 123-4567
</p>
</div>
</div>
{{ end }}

23
rmo/compliance.go Normal file
View file

@ -0,0 +1,23 @@
package rmo
import (
"net/http"
"github.com/Gleipnir-Technology/nidus-sync/html"
)
func getDistrictCompliance(w http.ResponseWriter, r *http.Request) {
district, err := districtBySlug(r)
if err != nil {
respondError(w, "Failed to lookup organization", err, http.StatusBadRequest)
return
}
html.RenderOrError(
w,
"rmo/district-compliance.html",
ContentNuisance{
District: newContentDistrict(district),
URL: makeContentURL(nil),
},
)
}

View file

@ -16,6 +16,7 @@ func Router(r *mux.Router) {
r.HandleFunc("/district", getDistrictList).Methods("GET") r.HandleFunc("/district", getDistrictList).Methods("GET")
r.HandleFunc("/district/{slug}", getRootDistrict).Methods("GET") r.HandleFunc("/district/{slug}", getRootDistrict).Methods("GET")
r.HandleFunc("/district/{slug}/compliance", getDistrictCompliance).Methods("GET")
r.HandleFunc("/district/{slug}/nuisance", getNuisanceDistrict).Methods("GET") r.HandleFunc("/district/{slug}/nuisance", getNuisanceDistrict).Methods("GET")
//r.HandleFunc("/district/{slug}/nuisance-submit-complete", renderMock(mockNuisanceSubmitCompleteT)).Methods("GET") //r.HandleFunc("/district/{slug}/nuisance-submit-complete", renderMock(mockNuisanceSubmitCompleteT)).Methods("GET")
//r.HandleFunc("/district/{slug}/status", renderMock(mockStatusT)).Methods("GET") //r.HandleFunc("/district/{slug}/status", renderMock(mockStatusT)).Methods("GET")

View file

@ -35,28 +35,18 @@ func AddStaticRoute(r *mux.Router, path string) {
}) })
} }
} }
fileServer(r, "/static", localFS, embeddedStaticFS) fileServer(r, "/static/", localFS, embeddedStaticFS)
} }
func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS embed.FS) { func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS embed.FS) {
if strings.ContainsAny(path, "{}*") { log.Debug().Str("path", path).Msg("adding file server")
panic("FileServer does not permit any URL parameters.") r.PathPrefix(path).HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
}
if path != "/" && path[len(path)-1] != '/' {
r.HandleFunc(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
}
path += "*"
r.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
//rctx := chi.RouteContext(r.Context()) //rctx := chi.RouteContext(r.Context())
//pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*") //pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
pathPrefix := strings.TrimPrefix(r.URL.Path, "/static") //pathPrefix := strings.TrimPrefix(r.URL.Path, "/static")
path := strings.TrimPrefix(r.URL.Path, "/static/")
// Determine the actual file path //log.Debug().Str("path", path).Msg("handling request")
requestedPath := strings.TrimPrefix(r.URL.Path, pathPrefix+"/")
var err error var err error
var fileToServe http.File var fileToServe http.File
@ -65,9 +55,9 @@ func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS emb
// For dev, try the current filesystem // For dev, try the current filesystem
if !config.IsProductionEnvironment() { if !config.IsProductionEnvironment() {
// Try to open from local filesystem for development // Try to open from local filesystem for development
fileToServe, err = root.Open(requestedPath) fileToServe, err = root.Open(path)
if err != nil { if err != nil {
log.Warn().Str("path", requestedPath).Msg("Failed to read static file for dev") log.Warn().Err(err).Str("path", path).Msg("Failed to read static file for dev")
found = false found = false
} else { } else {
found = true found = true
@ -76,10 +66,10 @@ func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS emb
// For production use the embedded filesystem // For production use the embedded filesystem
if !found { if !found {
// Requested paths start with // Requested paths start with
embeddedFile, err := embeddedFS.Open(requestedPath) embeddedFile, err := embeddedFS.Open(path)
if err != nil { if err != nil {
log.Debug().Err(err).Str("requested path", requestedPath).Msg("Failed to find resource") log.Debug().Err(err).Str("embedded path", path).Msg("Failed to find resource")
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
@ -93,14 +83,14 @@ func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS emb
// Add caching headers // Add caching headers
if config.IsProductionEnvironment() { if config.IsProductionEnvironment() {
ext := filepath.Ext(requestedPath) ext := filepath.Ext(path)
switch ext { switch ext {
case ".css", ".jpg", ".jpeg", ".png", ".gif", ".svg", ".woff", ".woff2", ".ttf": case ".css", ".jpg", ".jpeg", ".png", ".gif", ".svg", ".woff", ".woff2", ".ttf":
// Cache for 1 week (604800 seconds) // Cache for 1 week (604800 seconds)
crw.Header().Set("Cache-Control", "public, max-age=604800, stale-while-revalidate=86400") crw.Header().Set("Cache-Control", "public, max-age=604800, stale-while-revalidate=86400")
default: default:
// If it's a generated file, cache it essentially forever (1 year) // If it's a generated file, cache it essentially forever (1 year)
if strings.HasPrefix(requestedPath, "gen/") { if strings.HasPrefix(path, "/static/gen/") {
crw.Header().Set("Cache-Control", "public, max-age=31536000, immutable") crw.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
} else { } else {
// Other files, 1 hour // Other files, 1 hour
@ -109,7 +99,7 @@ func fileServer(r *mux.Router, path string, root http.FileSystem, embeddedFS emb
} }
} }
// Serve the file // Serve the file
http.ServeContent(crw, r, requestedPath, startedTime, fileToServe) http.ServeContent(crw, r, path, startedTime, fileToServe)
// Close the file // Close the file
fileToServe.Close() fileToServe.Close()