admin-insights-pages: dashboard customers+revenue (orders.amount), users->tags relation, pause customer (gates alerts), About/What-is-this/Contact pages
This commit is contained in:
@@ -14,6 +14,7 @@ type Order struct {
|
||||
Status string `json:"status"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
Amount pgtype.Numeric `json:"amount"`
|
||||
}
|
||||
|
||||
type Product struct {
|
||||
@@ -74,4 +75,5 @@ type User struct {
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
RememberToken pgtype.Text `json:"remember_token"`
|
||||
Paused bool `json:"paused"`
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ func (q *Queries) CountTagsByOwner(ctx context.Context, ownerID pgtype.Int8) (in
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (email, password_hash, name, phone)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, email, password_hash, name, phone, created_at, is_admin, remember_token
|
||||
RETURNING id, email, password_hash, name, phone, created_at, is_admin, remember_token, paused
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
@@ -166,6 +166,7 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
&i.RememberToken,
|
||||
&i.Paused,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -221,7 +222,7 @@ func (q *Queries) GetLatestScanByTag(ctx context.Context, tagID int64) (Scan, er
|
||||
}
|
||||
|
||||
const getOrderByID = `-- name: GetOrderByID :one
|
||||
SELECT id, account_id, status, created_at, updated_at FROM orders WHERE id = $1
|
||||
SELECT id, account_id, status, created_at, updated_at, amount FROM orders WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetOrderByID(ctx context.Context, id int64) (Order, error) {
|
||||
@@ -233,6 +234,7 @@ func (q *Queries) GetOrderByID(ctx context.Context, id int64) (Order, error) {
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Amount,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -361,7 +363,7 @@ func (q *Queries) GetTagByID(ctx context.Context, id int64) (Tag, error) {
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token FROM users WHERE email = $1
|
||||
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token, paused FROM users WHERE email = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
@@ -376,12 +378,13 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
&i.RememberToken,
|
||||
&i.Paused,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByID = `-- name: GetUserByID :one
|
||||
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token FROM users WHERE id = $1
|
||||
SELECT id, email, password_hash, name, phone, created_at, is_admin, remember_token, paused FROM users WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||
@@ -396,6 +399,7 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
&i.RememberToken,
|
||||
&i.Paused,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ func LoadTemplates() (Templates, error) {
|
||||
base := dir + "/base.html"
|
||||
pages := map[string][]string{
|
||||
"index": {dir + "/index.html"},
|
||||
"about": {dir + "/about.html"},
|
||||
"what": {dir + "/what.html"},
|
||||
"contact": {dir + "/contact.html"},
|
||||
"register": {dir + "/register.html"},
|
||||
"login": {dir + "/login.html"},
|
||||
"account": {dir + "/account.html", dir + "/account-panel.html", dir + "/tag-list.html", dir + "/tag-edit-inline.html"},
|
||||
|
||||
18
frontend/internal/handlers/pages.go
Normal file
18
frontend/internal/handlers/pages.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import "net/http"
|
||||
|
||||
// About renders the About page.
|
||||
func (a *App) About(w http.ResponseWriter, r *http.Request) {
|
||||
a.render(w, r, "about", "About", nil, "")
|
||||
}
|
||||
|
||||
// WhatIsThis renders the "what is this?" explainer page.
|
||||
func (a *App) WhatIsThis(w http.ResponseWriter, r *http.Request) {
|
||||
a.render(w, r, "what", "What is this?", nil, "")
|
||||
}
|
||||
|
||||
// Contact renders the contact page.
|
||||
func (a *App) Contact(w http.ResponseWriter, r *http.Request) {
|
||||
a.render(w, r, "contact", "Contact", nil, "")
|
||||
}
|
||||
@@ -98,6 +98,13 @@ func (a *App) shouldAlert(ctx context.Context, tag db.Tag, scan db.Scan, hasLoc
|
||||
return false
|
||||
}
|
||||
|
||||
// Customer pause (admin kill-switch): paused owner -> record only.
|
||||
if tag.OwnerID.Valid {
|
||||
if owner, err := a.Queries.GetUserByID(ctx, tag.OwnerID.Int64); err == nil && owner.Paused {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 24 h per-device block: same fingerprint seen recently (excluding this scan) => record only.
|
||||
if fp := scan.Fingerprint.String; fp != "" {
|
||||
if _, err := a.Queries.GetRecentScanByFingerprint(ctx, db.GetRecentScanByFingerprintParams{Fingerprint: pgtype.Text{String: fp, Valid: true}, ID: scan.ID}); err == nil {
|
||||
@@ -279,6 +286,12 @@ func (a *App) FinderContact(w http.ResponseWriter, r *http.Request) {
|
||||
// Should we notify? Skip when the same phone was alerted within the window,
|
||||
// or when this device (fingerprint) has already alerted in the last 24 h.
|
||||
notify := tag.SmsEnabled && tag.Phone.Valid && a.Sender != nil
|
||||
// Customer pause: paused owner -> store only.
|
||||
if notify && tag.OwnerID.Valid {
|
||||
if owner, err := a.Queries.GetUserByID(r.Context(), tag.OwnerID.Int64); err == nil && owner.Paused {
|
||||
notify = false
|
||||
}
|
||||
}
|
||||
// Metering: metered tags must have credits remaining.
|
||||
if notify && tag.SmsAllocated > 0 && tag.SmsUsed >= tag.SmsAllocated {
|
||||
notify = false
|
||||
|
||||
Reference in New Issue
Block a user