Migrate root of application to use a basic Vue app

We'll build from here.
This commit is contained in:
Eli Ribble 2026-03-21 20:47:46 +00:00
parent 5779242f22
commit efece7733f
No known key found for this signature in database
4 changed files with 64 additions and 80 deletions

View file

@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nidus Sync</title>
<link rel="icon" href="/static/ico/favicon-sync.ico" type="image/x-icon" />
<link href="{{ bundlePathCSS }}" rel="stylesheet" />
{{ if not .Config.IsProductionEnvironment }}
<script src="/.flogo/injector.js"></script>
{{ end }}
</head>
<body>
<div id="app"></div>
{{ if not .Config.IsProductionEnvironment }}
<div id="flogo"></div>
{{ end }}
<script src="{{ bundlePathJS }}"></script>
</body>
</html>

View file

@ -2,12 +2,10 @@ package sync
import ( import (
"context" "context"
"errors"
"html/template" "html/template"
"net/http" "net/http"
"time" "time"
"github.com/Gleipnir-Technology/nidus-sync/auth"
"github.com/Gleipnir-Technology/nidus-sync/html" "github.com/Gleipnir-Technology/nidus-sync/html"
nhttp "github.com/Gleipnir-Technology/nidus-sync/http" nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
"github.com/Gleipnir-Technology/nidus-sync/platform" "github.com/Gleipnir-Technology/nidus-sync/platform"
@ -56,27 +54,50 @@ func getLayoutTest(ctx context.Context, r *http.Request, user platform.User) (*h
return html.NewResponse("sync/layout-test.html", contentLayoutTest{}), nil return html.NewResponse("sync/layout-test.html", contentLayoutTest{}), nil
} }
func getRoot(w http.ResponseWriter, r *http.Request) { func getRoot(ctx context.Context, r *http.Request, user platform.User) (*html.Response[contentDashboard], *nhttp.ErrorWithStatus) {
ctx := r.Context() var lastSync *time.Time
user, err := auth.GetAuthenticatedUser(r) sync, err := user.Organization.FieldseekerSyncLatest(ctx)
if err != nil { if err != nil {
// No credentials or user not found: go to login return nil, nhttp.NewError("Failed to get syncs: %w", err)
if errors.Is(err, &auth.NoCredentialsError{}) || errors.Is(err, &platform.NoUserError{}) { } else if sync != nil {
http.Redirect(w, r, "/signin", http.StatusFound) lastSync = &sync.Created
return
} else {
respondError(w, "Failed to get root", err, http.StatusInternalServerError)
return
}
} }
if user == nil { is_syncing := user.Organization.IsSyncOngoing()
errorCode := r.URL.Query().Get("error") count_trap, err := user.Organization.CountTrap(ctx)
signin(w, errorCode, "/") if err != nil {
return return nil, nhttp.NewError("Failed to get trap count: %w", err)
} else {
dashboard(ctx, w, *user)
return
} }
count_source, err := user.Organization.CountTrap(ctx)
if err != nil {
return nil, nhttp.NewError("Failed to get source count: %w", err)
}
count_service, err := user.Organization.CountServiceRequest(ctx)
if err != nil {
return nil, nhttp.NewError("Failed to get service count: %w", err)
}
service_request_recent, err := user.Organization.ServiceRequestRecent(ctx)
if err != nil {
return nil, nhttp.NewError("Failed to get recent service: %w", err)
}
requests := make([]ServiceRequestSummary, 0)
for _, r := range service_request_recent {
requests = append(requests, ServiceRequestSummary{
Date: r.Creationdate.MustGet(),
Location: r.Reqaddr1.MustGet(),
Status: "Completed",
})
}
content := contentDashboard{
CountTraps: int(count_trap),
CountMosquitoSources: int(count_source),
CountServiceRequests: int(count_service),
IsSyncOngoing: is_syncing,
LastSync: lastSync,
MapData: ComponentMap{},
RecentRequests: requests,
}
return html.NewResponse("sync/authenticated.html", content), nil
} }
func getSource(ctx context.Context, r *http.Request, user platform.User) (*html.Response[contentSource], *nhttp.ErrorWithStatus) { func getSource(ctx context.Context, r *http.Request, user platform.User) (*html.Response[contentSource], *nhttp.ErrorWithStatus) {
@ -175,62 +196,6 @@ func getTrap(ctx context.Context, r *http.Request, user platform.User) (*html.Re
return html.NewResponse("sync/trap.html", data), nil return html.NewResponse("sync/trap.html", data), nil
} }
func dashboard(ctx context.Context, w http.ResponseWriter, user platform.User) {
var lastSync *time.Time
sync, err := user.Organization.FieldseekerSyncLatest(ctx)
if err != nil {
respondError(w, "Failed to get syncs", err, http.StatusInternalServerError)
} else if sync != nil {
lastSync = &sync.Created
}
is_syncing := user.Organization.IsSyncOngoing()
count_trap, err := user.Organization.CountTrap(ctx)
if err != nil {
respondError(w, "Failed to get trap count", err, http.StatusInternalServerError)
return
}
count_source, err := user.Organization.CountTrap(ctx)
if err != nil {
respondError(w, "Failed to get source count", err, http.StatusInternalServerError)
return
}
count_service, err := user.Organization.CountServiceRequest(ctx)
if err != nil {
respondError(w, "Failed to get service count", err, http.StatusInternalServerError)
return
}
service_request_recent, err := user.Organization.ServiceRequestRecent(ctx)
if err != nil {
respondError(w, "Failed to get recent service", err, http.StatusInternalServerError)
return
}
requests := make([]ServiceRequestSummary, 0)
for _, r := range service_request_recent {
requests = append(requests, ServiceRequestSummary{
Date: r.Creationdate.MustGet(),
Location: r.Reqaddr1.MustGet(),
Status: "Completed",
})
}
content := contentDashboard{
CountTraps: int(count_trap),
CountMosquitoSources: int(count_source),
CountServiceRequests: int(count_service),
IsSyncOngoing: is_syncing,
LastSync: lastSync,
MapData: ComponentMap{},
RecentRequests: requests,
}
html.RenderOrError(w, "sync/dashboard.html", contentAuthenticated[contentDashboard]{
C: content,
Config: html.NewContentConfig(),
Organization: user.Organization,
URL: html.NewContentURL(),
User: user,
})
}
func source(w http.ResponseWriter, r *http.Request, user platform.User, id uuid.UUID) { func source(w http.ResponseWriter, r *http.Request, user platform.User, id uuid.UUID) {
} }

View file

@ -9,8 +9,6 @@ import (
func Router() chi.Router { func Router() chi.Router {
r := chi.NewRouter() r := chi.NewRouter()
// Root is a special endpoint that is neither authenticated nor unauthenticated
r.Get("/", getRoot)
// Unauthenticated endpoints // Unauthenticated endpoints
r.Get("/arcgis/oauth/begin", getArcgisOauthBegin) r.Get("/arcgis/oauth/begin", getArcgisOauthBegin)
@ -42,6 +40,7 @@ func Router() chi.Router {
// Authenticated endpoints // Authenticated endpoints
r.Route("/api", api.AddRoutes) r.Route("/api", api.AddRoutes)
r.Method("GET", "/", authenticatedHandler(getRoot))
r.Method("GET", "/admin", authenticatedHandler(getAdminDash)) r.Method("GET", "/admin", authenticatedHandler(getAdminDash))
r.Method("GET", "/cell/{cell}", authenticatedHandler(getCellDetails)) r.Method("GET", "/cell/{cell}", authenticatedHandler(getCellDetails))
r.Method("GET", "/communication", authenticatedHandler(getCommunicationRoot)) r.Method("GET", "/communication", authenticatedHandler(getCommunicationRoot))

View file

@ -2,7 +2,7 @@ import Alpine from "./vendor/alpinejs-3.15.8.js";
import { createApp } from "vue"; import { createApp } from "vue";
import App from "./app.vue"; import App from "./app.vue";
import { SSEManager } from "./sse-manager"; import { SSEManager } from "./sse-manager";
import { SetupSidebar } from "./sidebar"; //import { SetupSidebar } from "./sidebar";
import "maplibre-gl/dist/maplibre-gl.css"; import "maplibre-gl/dist/maplibre-gl.css";
// Import Bootstrap Icons CSS // Import Bootstrap Icons CSS
@ -37,7 +37,7 @@ window.createAppPlanning = createAppPlanning;
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
Alpine.start(); Alpine.start();
SSEManager.connect("/api/events"); SSEManager.connect("/api/events");
SetupSidebar(); //SetupSidebar();
}); });
document.addEventListener("alpine:init", () => { document.addEventListener("alpine:init", () => {
const user = { const user = {