More layout and customisations.

This commit is contained in:
2025-04-01 10:19:56 +01:00
parent aaf90b55da
commit 5aaddf16f1
8 changed files with 218 additions and 126 deletions

View File

@@ -12,6 +12,7 @@ import (
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
) )
// ToDo this should not be in draw for home!
func Home(db *sql.DB) http.HandlerFunc { func Home(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query(` rows, err := db.Query(`
@@ -50,8 +51,9 @@ func Home(db *sql.DB) http.HandlerFunc {
context := helpers.TemplateContext(w, r) context := helpers.TemplateContext(w, r)
context["Data"] = results context["Data"] = results
tmpl := template.Must(template.ParseFiles( tmpl := template.Must(template.New("").Funcs(helpers.TemplateFuncs()).ParseFiles(
"templates/layout.html", "templates/layout.html",
"templates/topbar.html",
"templates/index.html", "templates/index.html",
)) ))
@@ -69,6 +71,7 @@ func NewDraw(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles( tmpl := template.Must(template.ParseFiles(
"templates/layout.html", "templates/layout.html",
"templates/topbar.html",
"templates/new_draw.html", "templates/new_draw.html",
)) ))

View File

@@ -13,6 +13,22 @@ type User struct {
IsAdmin bool IsAdmin bool
} }
type Notification struct {
ID int
Title string
Message string
IsRead bool
CreatedAt time.Time
}
type Message struct {
ID int
Sender string
Subject string
IsRead bool
CreatedAt time.Time
}
var db *sql.DB var db *sql.DB
func SetDB(database *sql.DB) { func SetDB(database *sql.DB) {

View File

@@ -1,3 +1,5 @@
@import "topbar.css";
body { font-family: Arial, sans-serif; margin: 0px; } body { font-family: Arial, sans-serif; margin: 0px; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; } table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { padding: 8px 12px; border: 1px solid #ddd; text-align: center; } th, td { padding: 8px 12px; border: 1px solid #ddd; text-align: center; }
@@ -15,23 +17,6 @@ th { background-color: #f5f5f5; }
} }
.flash { padding: 10px; color: green; background: #e9ffe9; border: 1px solid #c3e6c3; margin-bottom: 15px; } .flash { padding: 10px; color: green; background: #e9ffe9; border: 1px solid #c3e6c3; margin-bottom: 15px; }
.dropdown-admin-box {
min-width: 350px;
}
.dropdown-notification-box {
min-width: 350px;
}
.dropdown-message-box {
min-width: 350px;
}
.btn-xs {
padding: 2px 6px;
font-size: 0.75rem;
line-height: 1;
}
/* Ball Stuff */ /* Ball Stuff */
.ball { .ball {

50
static/css/topbar.css Normal file
View File

@@ -0,0 +1,50 @@
.dropdown-admin-box {
min-width: 350px;
}
.dropdown-notification-box {
min-width: 350px;
}
.dropdown-message-box {
min-width: 350px;
}
.dropdown-with-arrow::before {
content: "";
position: absolute;
top: -8px;
right: 3.5px;
width: 0;
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 9px solid white;
z-index: 1;
}
.dropdown-with-arrow::after {
content: "";
position: absolute;
top: -9px;
right: 3.5px;
width: 0;
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 9px solid rgba(0, 0, 0, 0.15);
z-index: 0;
}
.btn-xs {
padding: 2px 6px;
font-size: 0.75rem;
line-height: 1;
}
.badge-small {
font-size: 0.9rem;
padding: 2px 5px;
line-height: 1;
transform: translate(-50%, -50%);
}

11
storage/badgecounts.go Normal file
View File

@@ -0,0 +1,11 @@
package storage
// "database/sql"
// // Get all for count
// var count int
// db.Get(&count, `SELECT COUNT(*) FROM user_notifications WHERE user_id = ? AND is_read = FALSE`, userID)
// // Then get the top 15 for display
// var notifications []Notification
// db.Select(&notifications, `SELECT * FROM user_notifications WHERE user_id = ? AND is_read = FALSE ORDER BY created_at DESC LIMIT 15`, userID)

View File

@@ -145,6 +145,36 @@ func InitDB(filepath string) *sql.DB {
log.Fatal("❌ Failed to create Users table:", err) log.Fatal("❌ Failed to create Users table:", err)
} }
createUsersMessageTable := `
CREATE TABLE IF NOT EXISTS users_messages (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
title TEXT NOT NULL,
message TEXT,
is_read BOOLEAN DEFAULT FALSE,
type VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
if _, err := db.Exec(createUsersMessageTable); err != nil {
log.Fatal("❌ Failed to create Users messages table:", err)
}
createUsersNotificationTable := `
CREATE TABLE IF NOT EXISTS users_notification (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id),
sender_name VARCHAR(100),
subject TEXT,
body TEXT,
is_read BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
if _, err := db.Exec(createUsersNotificationTable); err != nil {
log.Fatal("❌ Failed to create Users notification table:", err)
}
createAuditLogTable := ` createAuditLogTable := `
CREATE TABLE IF NOT EXISTS auditlog ( CREATE TABLE IF NOT EXISTS auditlog (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,

View File

@@ -10,111 +10,8 @@
</head> </head>
<body class="d-flex flex-column min-vh-100"> <body class="d-flex flex-column min-vh-100">
<!-- Topbar --> <!-- Topbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light px-3"> {{ template "topbar" . }}
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/img/logo.png" alt="Logo" height="30" class="me-2">
<span>SynLotto</span>
</a>
<div class="ms-auto d-flex align-items-center gap-3">
{{ if .User }}
{{ if .IsAdmin }}
<!-- Admin Dropdown -->
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="adminDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-shield-lock fs-5 position-relative"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-admin-box shadow-sm" aria-labelledby="adminDropdown">
<li class="dropdown-header text-center fw-bold">Admin Menu</li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Tools</a></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Audit Logs</a></li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Open Dashboard</a></li>
</ul>
</div>
{{ end }}
<!-- Notification Dropdown -->
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="notificationDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-bell fs-5 position-relative">
<span class="position-absolute top-0 start-0 translate-middle badge rounded-pill bg-warning text-dark" style="transform: translate(-40%, -50%)">
3
</span>
</i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-notification-box shadow-sm" aria-labelledby="notificationDropdown">
<li class="dropdown-header text-center fw-bold">Notifications</li>
<li><hr class="dropdown-divider"></li>
<!-- Example notification -->
<li class="px-3 py-2">
<div class="d-flex align-items-start">
<i class="bi bi-info-circle text-primary me-2 fs-4"></i>
<div>
<div class="fw-semibold">System Update</div>
<small class="text-muted">A new lottery draw has been posted.</small>
</div>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li class="px-3 py-2">
<div class="d-flex align-items-start">
<i class="bi bi-check-circle text-success me-2 fs-4"></i>
<div>
<div class="fw-semibold">Sync Complete</div>
<small class="text-muted">All results are up-to-date.</small>
</div>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/notifications" class="dropdown-item">View all notifications</a></li>
</ul>
</div>
<!-- Message Dropdown -->
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="messageDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-envelope fs-5 position-relative">
<!-- Unread badge (example: 2 messages) -->
<span class="position-absolute top-0 start-0 translate-middle badge rounded-pill bg-danger text-dark" style="transform: translate(-40%, -50%)">
2
</span>
</i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-message-box shadow-sm" aria-labelledby="messageDropdown" style="min-width: 300px;">
<li class="dropdown-header text-center fw-bold">Messages</li>
<li><hr class="dropdown-divider"></li>
<!-- Example message item -->
<li class="px-3 py-2">
<div class="d-flex align-items-start">
<i class="bi bi-person-circle me-2 fs-4 text-secondary"></i>
<div>
<div class="fw-semibold">Admin</div>
<small class="text-muted">Welcome to SynLotto!</small>
</div>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/messages" class="dropdown-item">View all messages</a></li>
</ul>
</div>
<!-- User Greeting -->
<span class="navbar-text">Hello, {{ .User.Username }}</span>
<a class="btn btn-outline-danger btn-xs" href="/logout">Logout</a>
{{ else }}
<a class="btn btn-outline-primary btn-sm" href="/login">Login</a>
{{ end }}
</div>
</nav>
<!-- Main Layout --> <!-- Main Layout -->
<div class="container-fluid flex-grow-1"> <div class="container-fluid flex-grow-1">
<div class="row"> <div class="row">

100
templates/topbar.html Normal file
View File

@@ -0,0 +1,100 @@
{{ define "topbar" }}
<nav class="navbar navbar-expand-lg navbar-light bg-light px-3">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/img/logo.png" alt="Logo" height="30" class="me-2">
<span>SynLotto</span>
</a>
<div class="ms-auto d-flex align-items-center gap-3">
{{ if .User }}
{{ if .IsAdmin }}
<!-- Admin Dropdown -->
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="adminDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-shield-lock fs-5 position-relative"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-admin-box shadow-sm dropdown-with-arrow" aria-labelledby="adminDropdown">
<li class="dropdown-header text-center fw-bold">Admin Menu</li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Tools</a></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Audit Logs</a></li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/admin/dashboard" class="dropdown-item">Open Dashboard</a></li>
</ul>
</div>
{{ end }}
<!-- Notification Dropdown
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="notificationDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-bell fs-5 position-relative">
{{ if gt .NotificationCount 0 }}
<span class="position-absolute top-0 start-0 translate-middle badge rounded-pill bg-warning text-dark badge-small">
{{ if gt .NotificationCount 15 }}15+{{ else }}{{ .NotificationCount }}{{ end }}
</span>
{{ end }}
</i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-notification-box shadow-sm dropdown-with-arrow" aria-labelledby="notificationDropdown">
<li class="dropdown-header text-center fw-bold">Notifications</li>
<li><hr class="dropdown-divider"></li>
{{ $total := len .Notifications }}
{{ range $i, $n := .Notifications }}
<li class="px-3 py-2">
<div class="d-flex align-items-start">
<i class="bi bi-info-circle text-primary me-2 fs-4"></i>
<div>
<div class="fw-semibold">{{ $n.Title }}</div>
<small class="text-muted">{{ $n.Message }}</small>
</div>
</div>
</li>
{{ if lt (add $i 1) $total }}
<li><hr class="dropdown-divider"></li>
{{ end }}
{{ else }}
<li class="text-center text-muted py-2">No notifications</li>
{{ end }}
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/notifications" class="dropdown-item">View all notifications</a></li>
</ul>
</div> -->
<!-- Message Dropdown -->
<div class="dropdown">
<a class="nav-link text-dark" href="#" id="messageDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-envelope fs-5 position-relative">
<!-- Unread badge (example: 2 messages) -->
<span class="position-absolute top-0 start-0 translate-middle badge rounded-pill bg-danger text-dark badge-small">2</span>
</i>
</a>
<ul class="dropdown-menu dropdown-menu-end dropdown-message-box shadow-sm dropdown-with-arrow" aria-labelledby="messageDropdown">
<li class="dropdown-header text-center fw-bold">Messages</li>
<li><hr class="dropdown-divider"></li>
<!-- Example message item -->
<li class="px-3 py-2">
<div class="d-flex align-items-start">
<i class="bi bi-person-circle me-2 fs-4 text-secondary"></i>
<div>
<div class="fw-semibold">Admin</div>
<small class="text-muted">Welcome to SynLotto!</small>
</div>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li class="text-center"><a href="/messages" class="dropdown-item">View all messages</a></li>
</ul>
</div>
<!-- User Greeting -->
<span class="navbar-text">Hello, {{ .User.Username }}</span>
<a class="btn btn-outline-danger btn-xs" href="/logout">Logout</a>
{{ else }}
<a class="btn btn-outline-primary btn-sm" href="/login">Login</a>
{{ end }}
</div>
</nav>
{{ end }}