Add page for showing notifications

This commit is contained in:
Eli Ribble 2026-02-10 16:24:37 +00:00
parent c3e8bba822
commit 396cf5c586
No known key found for this signature in database
6 changed files with 128 additions and 3 deletions

View file

@ -0,0 +1,71 @@
{{ template "sync/layout/authenticated.html" . }}
{{ define "title" }}Layout Test{{ end }}
{{ define "extraheader" }}
{{ end }}
{{ define "content" }}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div
class="card-header bg-white d-flex justify-content-between align-items-center"
>
<h5 class="mb-0">Notifications</h5>
<span class="badge bg-primary">{{ len .Notifications }}</span>
</div>
<div class="list-group list-group-flush">
{{ range .Notifications }}
<a
href="{{ .Link }}"
class="list-group-item list-group-item-action notification-item"
>
<div class="d-flex align-items-center">
<!-- Icon based on notification type -->
{{ if eq .Type "oauth-token-invalid" }}
<div class="me-3 fs-4 text-primary">
<i class="bi bi-circle-square"></i>
</div>
{{ else if eq .Type "success" }}
<div class="me-3 fs-4 text-success">
<i class="bi bi-check-circle-fill"></i>
</div>
{{ else if eq .Type "warning" }}
<div class="me-3 fs-4 text-warning">
<i class="bi bi-exclamation-triangle-fill"></i>
</div>
{{ else if eq .Type "danger" }}
<div class="me-3 fs-4 text-danger">
<i class="bi bi-x-circle-fill"></i>
</div>
{{ else if eq .Type "message" }}
<div class="me-3 fs-4 text-info">
<i class="bi bi-envelope-fill"></i>
</div>
{{ else }}
<div class="me-3 fs-4 text-secondary">
<i class="bi bi-bell-fill"></i>
</div>
{{ end }}
<div class="flex-grow-1" data-notification-type="{{ .Type }}">
<div>{{ .Message }}</div>
<div class="notification-time">{{ .Time|timeSince }}</div>
</div>
<div class="ms-2">
<i class="bi bi-chevron-right text-muted"></i>
</div>
</div>
</a>
{{ else }}
<div class="list-group-item text-center text-muted py-4">
<i class="bi bi-bell-slash fs-4 mb-2"></i>
<p>No notifications yet</p>
</div>
{{ end }}
</div>
</div>
</div>
</div>
</div>
{{ end }}

View file

@ -85,7 +85,7 @@ func ForUser(ctx context.Context, u *models.User) ([]Notification, error) {
func notificationTypeName(t enums.Notificationtype) string {
switch t {
case enums.NotificationtypeOauthTokenInvalidated:
return "alert"
return "oauth-token-invalid"
default:
return "unknown-type"
}

View file

@ -51,5 +51,6 @@ $theme-colors: map-merge(
@import "./rmo/root.scss";
@import "./rmo/status.scss";
@import "./sync/dashboard.scss";
@import "./sync/notification.scss";
@import "./sync/pool-csv-upload.scss";
@import "./sync/pool-by-id.scss";

View file

@ -0,0 +1,10 @@
.notification-item {
transition: all 0.2s ease;
}
.notification-item:hover {
background-color: rgba(0,0,0,0.05);
}
.notification-time {
font-size: 0.8rem;
color: #6c757d;
}

42
sync/notification.go Normal file
View file

@ -0,0 +1,42 @@
package sync
import (
//"context"
//"fmt"
"net/http"
//"strings"
//"time"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/html"
"github.com/Gleipnir-Technology/nidus-sync/notification"
//"github.com/Gleipnir-Technology/bob"
//"github.com/Gleipnir-Technology/bob/dialect/psql"
//"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
//"github.com/Gleipnir-Technology/nidus-sync/db"
//"github.com/Gleipnir-Technology/nidus-sync/db/sql"
//"github.com/google/uuid"
//"github.com/uber/h3-go/v4"
)
type contentNotificationList struct {
Notifications []notification.Notification
User User
}
func getNotificationList(w http.ResponseWriter, r *http.Request, u *models.User) {
userContent, err := contentForUser(r.Context(), u)
if err != nil {
respondError(w, "Failed to get user", err, http.StatusInternalServerError)
return
}
ctx := r.Context()
notifications, err := notification.ForUser(ctx, u)
if err != nil {
respondError(w, "Failed to get notifications", err, http.StatusInternalServerError)
}
html.RenderOrError(w, "sync/notification-list.html", &contentNotificationList{
Notifications: notifications,
User: userContent,
})
}

View file

@ -17,6 +17,7 @@ func Router() chi.Router {
r.Get("/arcgis/oauth/callback", getArcgisOauthCallback)
r.Get("/district", getDistrict)
// Mock endpoints
r.Get("/mock", renderMock("mock-root.html"))
r.Get("/mock/admin", renderMock("admin.html"))
r.Get("/mock/admin/service-request", renderMock("admin-service-request.html"))
@ -46,10 +47,9 @@ func Router() chi.Router {
r.Get("/mock/setting/user", renderMock("setting-user.html"))
r.Get("/mock/setting/user/add", renderMock("setting-user-add.html"))
// Utility endpoints
r.Get("/oauth/refresh", getOAuthRefresh)
r.Get("/privacy", getPrivacy)
r.Get("/qr-code/report/{code}", getQRCodeReport)
r.Get("/signin", getSignin)
r.Post("/signin", postSignin)
@ -61,6 +61,7 @@ func Router() chi.Router {
r.Route("/api", api.AddRoutes)
r.Method("GET", "/cell/{cell}", auth.NewEnsureAuth(getCellDetails))
r.Method("GET", "/layout-test", auth.NewEnsureAuth(getLayoutTest))
r.Method("GET", "/notification", auth.NewEnsureAuth(getNotificationList))
r.Method("GET", "/pool", auth.NewEnsureAuth(getPoolList))
r.Method("GET", "/pool/upload", auth.NewEnsureAuth(getPoolUpload))
r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID))