Hub: add preferences sync endpoint + notification display on customer page
- POST /api/v1/preferences: accepts {customer_id, email, enabled_events} from controller
- GetRecentNotifications() store method for last N notification log entries
- Customer detail page: new Notifications section (email, events, recent log table)
- joinStrings template function for event list display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,8 @@ func New(store *store.Store, passwordHash string, staleThreshold time.Duration,
|
||||
"statusColor": statusColor,
|
||||
"statusIcon": statusIcon,
|
||||
"formatFloat": func(f float64) string { return fmt.Sprintf("%.0f", f) },
|
||||
"json": func(v interface{}) template.JS {
|
||||
"joinStrings": func(s []string, sep string) string { return strings.Join(s, sep) },
|
||||
"json": func(v interface{}) template.JS {
|
||||
b, _ := json.Marshal(v)
|
||||
return template.JS(b)
|
||||
},
|
||||
@@ -184,11 +185,17 @@ func (s *Server) handleCustomerDetail(w http.ResponseWriter, r *http.Request, cu
|
||||
// Get history (last 24h)
|
||||
history, _ := s.store.GetCustomerHistory(customerID, 24*time.Hour)
|
||||
|
||||
// Get notification preferences and recent log
|
||||
notifPrefs, _ := s.store.GetNotificationPrefs(customerID)
|
||||
recentNotifs, _ := s.store.GetRecentNotifications(customerID, 10)
|
||||
|
||||
type detailData struct {
|
||||
Customer *store.CustomerSummary
|
||||
Report map[string]interface{}
|
||||
History []store.CustomerSummary
|
||||
OverallStatus string
|
||||
Customer *store.CustomerSummary
|
||||
Report map[string]interface{}
|
||||
History []store.CustomerSummary
|
||||
OverallStatus string
|
||||
NotifPrefs *store.NotificationPrefs
|
||||
RecentNotifications []store.NotificationLogEntry
|
||||
}
|
||||
|
||||
overallStatus := "ok"
|
||||
@@ -201,10 +208,12 @@ func (s *Server) handleCustomerDetail(w http.ResponseWriter, r *http.Request, cu
|
||||
}
|
||||
|
||||
data := detailData{
|
||||
Customer: customer,
|
||||
Report: report,
|
||||
History: history,
|
||||
OverallStatus: overallStatus,
|
||||
Customer: customer,
|
||||
Report: report,
|
||||
History: history,
|
||||
OverallStatus: overallStatus,
|
||||
NotifPrefs: notifPrefs,
|
||||
RecentNotifications: recentNotifs,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
@@ -155,6 +155,46 @@
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
<!-- Notifications -->
|
||||
<section class="card">
|
||||
<h2>Notifications</h2>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="label">Email</span>
|
||||
<span class="value">{{if .NotifPrefs}}{{if .NotifPrefs.Email}}{{.NotifPrefs.Email}}{{else}}Not set{{end}}{{else}}Not configured{{end}}</span>
|
||||
</div>
|
||||
{{if .NotifPrefs}}
|
||||
<div class="info-item">
|
||||
<span class="label">Events</span>
|
||||
<span class="value">{{if .NotifPrefs.EnabledEvents}}{{joinStrings .NotifPrefs.EnabledEvents ", "}}{{else}}None{{end}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .RecentNotifications}}
|
||||
<h3>Recent (last 10)</h3>
|
||||
<table class="history-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Event</th>
|
||||
<th>Status</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .RecentNotifications}}
|
||||
<tr>
|
||||
<td>{{.CreatedAt.Format "Jan 02 15:04"}}</td>
|
||||
<td>{{.EventType}}</td>
|
||||
<td><span class="status-badge status-badge-{{.Status}}">{{.Status}}</span></td>
|
||||
<td>{{.Message}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
<!-- Report History (last 24h) -->
|
||||
{{if .History}}
|
||||
<section class="card">
|
||||
|
||||
Reference in New Issue
Block a user