feat: Enhance user management and dashboard components

- Updated user management templates to improve user experience with a new layout and success/error message handling.
- Implemented password hashing for user creation and management, enhancing security.
- Refactored dashboard components to include better data presentation and responsiveness.
- Added dark mode support for various components, ensuring a consistent user interface across themes.
- Improved toast notification animations for better user feedback during actions.
This commit is contained in:
StarFleetCPTN
2025-03-23 11:42:16 -07:00
parent 3140d9e143
commit 30d0725331
13 changed files with 1656 additions and 761 deletions
+26
View File
@@ -14,6 +14,7 @@ import (
"github.com/glebarez/sqlite"
"github.com/starfleetcptn/gomft/internal/db/migrations"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
@@ -1681,3 +1682,28 @@ func (db *DB) UnassignRoleFromUser(roleID, userID, unassignedByID uint) error {
return tx.Commit().Error
}
// User struct method to set password with secure hashing
func (u *User) SetPassword(password string) error {
// Validate password length
if len(password) < 8 {
return fmt.Errorf("password must be at least 8 characters long")
}
// Hash the password using bcrypt
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("failed to hash password: %w", err)
}
// Store the hashed password
u.PasswordHash = string(hashedPassword)
u.LastPasswordChange = time.Now()
return nil
}
func (u *User) CheckPassword(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))
return err == nil
}
+704
View File
@@ -571,3 +571,707 @@ func getUserID(c *gin.Context) uint {
}
return 0
}
// HandleUsers renders the user management page
func (h *Handlers) HandleUsers(c *gin.Context) {
ctx := components.CreateTemplateContext(c)
// Fetch users from the database
var users []db.User
if err := h.DB.Find(&users).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "list",
ErrorMessage: "Failed to fetch users: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Preload roles for each user
for i := range users {
if err := h.DB.Model(&users[i]).Association("Roles").Find(&users[i].Roles); err != nil {
// Log error but continue
fmt.Printf("Error loading roles for user %d: %v\n", users[i].ID, err)
}
}
// Get success message from flash if available
successMsg := c.Query("success")
data := components.UserManagementData{
Users: users,
ActiveTab: "list",
SuccessMessage: successMsg,
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
}
// HandleNewUser renders the new user creation page
func (h *Handlers) HandleNewUser(c *gin.Context) {
ctx := components.CreateTemplateContext(c)
// Fetch available roles
var roles []db.Role
if err := h.DB.Find(&roles).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to fetch roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
data := components.UserManagementData{
ActiveTab: "create",
Roles: roles,
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
}
// HandleCreateUser processes user creation
func (h *Handlers) HandleCreateUser(c *gin.Context) {
ctx := components.CreateTemplateContext(c)
email := c.PostForm("email")
password := c.PostForm("password")
passwordConfirm := c.PostForm("password_confirm")
isAdmin := c.PostForm("is_admin") == "on"
roleIDs := c.PostFormArray("roles[]")
// Validate inputs
if email == "" || password == "" {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Email and password are required",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
if password != passwordConfirm {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Passwords do not match",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Create new user
user := &db.User{
Email: email,
}
// Set password (this would use a proper password hashing mechanism)
if err := user.SetPassword(password); err != nil {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to set password: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Set admin status
user.SetIsAdmin(isAdmin)
// Start transaction
tx := h.DB.Begin()
if err := tx.Error; err != nil {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Database error: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Save user
if err := tx.Create(user).Error; err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to create user: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Add roles if not admin and roles are selected
if !isAdmin && len(roleIDs) > 0 {
for _, roleIDStr := range roleIDs {
roleID, err := strconv.ParseUint(roleIDStr, 10, 32)
if err != nil {
continue // Skip invalid IDs
}
// Check if role exists
var role db.Role
if err := tx.First(&role, roleID).Error; err != nil {
continue // Skip if role doesn't exist
}
// Add role to user
if err := tx.Model(user).Association("Roles").Append(&role); err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to assign roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
}
}
// Create audit log entry
creatorID := getUserID(c)
auditLog := &db.AuditLog{
Action: "create",
EntityType: "user",
EntityID: user.ID,
UserID: creatorID,
Timestamp: time.Now(),
Details: map[string]interface{}{
"email": user.Email,
"is_admin": user.GetIsAdmin(),
},
}
if err := tx.Create(auditLog).Error; err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to create audit log: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
data := components.UserManagementData{
ActiveTab: "create",
ErrorMessage: "Failed to commit transaction: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// For HTMX requests, render the user list with success message
if c.GetHeader("HX-Request") == "true" {
// Fetch users from the database
var users []db.User
if err := h.DB.Find(&users).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "list",
ErrorMessage: "Failed to fetch users after creation: " + err.Error(),
}
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
return
}
// Preload roles for each user
for i := range users {
if err := h.DB.Model(&users[i]).Association("Roles").Find(&users[i].Roles); err != nil {
// Log error but continue
fmt.Printf("Error loading roles for user %d: %v\n", users[i].ID, err)
}
}
data := components.UserManagementData{
Users: users,
ActiveTab: "list",
SuccessMessage: "User created successfully",
}
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
// Redirect to user list with success message for regular requests
c.Redirect(http.StatusFound, "/admin/users?success=User+created+successfully")
}
}
// HandleEditUser renders the user edit page
func (h *Handlers) HandleEditUser(c *gin.Context) {
ctx := components.CreateTemplateContext(c)
userID := c.Param("id")
// Fetch user
var user db.User
if err := h.DB.First(&user, userID).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "list",
ErrorMessage: "User not found",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Fetch user's roles
if err := h.DB.Model(&user).Association("Roles").Find(&user.Roles); err != nil {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to fetch user roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Fetch all available roles
var roles []db.Role
if err := h.DB.Find(&roles).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to fetch roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
Roles: roles,
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
}
// HandleUpdateUser processes user updates
func (h *Handlers) HandleUpdateUser(c *gin.Context) {
ctx := components.CreateTemplateContext(c)
userID := c.Param("id")
email := c.PostForm("email")
password := c.PostForm("password")
passwordConfirm := c.PostForm("password_confirm")
isAdmin := c.PostForm("is_admin") == "on"
accountLocked := c.PostForm("account_locked") == "on"
roleIDs := c.PostFormArray("roles[]")
// Fetch existing user
var user db.User
if err := h.DB.First(&user, userID).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "list",
ErrorMessage: "User not found",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Check if trying to update the last admin
if user.GetIsAdmin() && !isAdmin {
// Count how many admins there are
var adminCount int64
if err := h.DB.Model(&db.User{}).Where("metadata->>'is_admin' = 'true'").Count(&adminCount).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to check admin count: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// If this is the last admin, prevent removal of admin status
if adminCount <= 1 {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Cannot remove admin status from the last administrator",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
}
// Start transaction
tx := h.DB.Begin()
if err := tx.Error; err != nil {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Database error: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Update email
if email != "" && email != user.Email {
user.Email = email
}
// Update password if provided
if password != "" {
if password != passwordConfirm {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Passwords do not match",
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
if err := user.SetPassword(password); err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to set password: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
}
// Update admin status and account lock status
user.SetIsAdmin(isAdmin)
user.SetAccountLocked(accountLocked)
// Save user changes
if err := tx.Save(&user).Error; err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to update user: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Update roles if not admin
if !isAdmin {
// First, remove all existing roles
if err := tx.Model(&user).Association("Roles").Clear(); err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to clear existing roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Then add the selected roles
if len(roleIDs) > 0 {
for _, roleIDStr := range roleIDs {
roleID, err := strconv.ParseUint(roleIDStr, 10, 32)
if err != nil {
continue // Skip invalid IDs
}
// Check if role exists
var role db.Role
if err := tx.First(&role, roleID).Error; err != nil {
continue // Skip if role doesn't exist
}
// Add role to user
if err := tx.Model(&user).Association("Roles").Append(&role); err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to assign roles: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
}
}
}
// Create audit log entry
updaterID := getUserID(c)
auditLog := &db.AuditLog{
Action: "update",
EntityType: "user",
EntityID: user.ID,
UserID: updaterID,
Timestamp: time.Now(),
Details: map[string]interface{}{
"email": user.Email,
"is_admin": user.GetIsAdmin(),
"account_locked": user.GetAccountLocked(),
"password_changed": password != "",
},
}
if err := tx.Create(auditLog).Error; err != nil {
tx.Rollback()
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to create audit log: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
data := components.UserManagementData{
ActiveTab: "edit",
EditUser: &user,
ErrorMessage: "Failed to commit transaction: " + err.Error(),
}
if c.GetHeader("HX-Request") == "true" {
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
_ = components.UserManagement(ctx, data).Render(ctx, c.Writer)
}
return
}
// For HTMX requests, render the user list with success message
if c.GetHeader("HX-Request") == "true" {
// Fetch users from the database
var users []db.User
if err := h.DB.Find(&users).Error; err != nil {
data := components.UserManagementData{
ActiveTab: "list",
ErrorMessage: "Failed to fetch users after update: " + err.Error(),
}
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
return
}
// Preload roles for each user
for i := range users {
if err := h.DB.Model(&users[i]).Association("Roles").Find(&users[i].Roles); err != nil {
// Log error but continue
fmt.Printf("Error loading roles for user %d: %v\n", users[i].ID, err)
}
}
data := components.UserManagementData{
Users: users,
ActiveTab: "list",
SuccessMessage: "User updated successfully",
}
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
} else {
// Redirect to user list with success message for regular requests
c.Redirect(http.StatusFound, "/admin/users?success=User+updated+successfully")
}
}
// HandleDeleteUser processes user deletion
func (h *Handlers) HandleDeleteUser(c *gin.Context) {
userID := c.Param("id")
// Fetch user
var user db.User
if err := h.DB.First(&user, userID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
// Check if this is an admin user
if user.GetIsAdmin() {
// Count how many admins there are
var adminCount int64
if err := h.DB.Model(&db.User{}).Where("metadata->>'is_admin' = 'true'").Count(&adminCount).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check admin count"})
return
}
// If this is the last admin, prevent deletion
if adminCount <= 1 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot delete the last administrator"})
return
}
}
// Check if trying to delete yourself
currentUserID := getUserID(c)
if currentUserID == user.ID {
c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot delete your own account"})
return
}
// Start transaction
tx := h.DB.Begin()
if err := tx.Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
return
}
// Create audit log entry before deletion
auditLog := &db.AuditLog{
Action: "delete",
EntityType: "user",
EntityID: user.ID,
UserID: currentUserID,
Timestamp: time.Now(),
Details: map[string]interface{}{
"email": user.Email,
"is_admin": user.GetIsAdmin(),
"account_locked": user.GetAccountLocked(),
},
}
if err := tx.Create(auditLog).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create audit log"})
return
}
// Remove roles association
if err := tx.Model(&user).Association("Roles").Clear(); err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear user roles"})
return
}
// Delete user
if err := tx.Delete(&user).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete user"})
return
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit transaction"})
return
}
// Fetch the updated user list to return
ctx := components.CreateTemplateContext(c)
var users []db.User
if err := h.DB.Find(&users).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch updated user list"})
return
}
// Preload roles for each user
for i := range users {
if err := h.DB.Model(&users[i]).Association("Roles").Find(&users[i].Roles); err != nil {
// Log error but continue
fmt.Printf("Error loading roles for user %d: %v\n", users[i].ID, err)
}
}
// Render the updated user list
data := components.UserManagementData{
Users: users,
ActiveTab: "list",
SuccessMessage: "User deleted successfully",
}
// Always use the partial for HTMX delete requests
_ = components.UserManagementContent(data).Render(ctx, c.Writer)
}
+2 -1
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"net/url"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/starfleetcptn/gomft/components"
@@ -24,7 +25,7 @@ func (h *Handlers) HandleDashboard(c *gin.Context) {
h.DB.Model(&db.JobHistory{}).Where("job_histories.status = 'running' AND job_histories.end_time IS NULL").Count(&totalJobs)
var completedJobs int64
h.DB.Model(&db.JobHistory{}).Where("status = ?", "completed").Count(&completedJobs)
h.DB.Model(&db.JobHistory{}).Where("status = ? AND start_time >= ?", "completed", time.Now().AddDate(0, 0, -1)).Count(&completedJobs)
var failedJobs int64
h.DB.Model(&db.JobHistory{}).Where("status = ?", "failed").Count(&failedJobs)
+3 -3
View File
@@ -99,17 +99,17 @@ func (h *Handlers) RegisterRoutes(router *gin.Engine) {
admin.Use(h.AuthMiddleware())
{
// Main admin dashboard - requires admin role
admin.GET("", h.AdminMiddleware(), h.HandleAdminDashboard)
// admin.GET("", h.AdminMiddleware(), h.HandleAdminDashboard)
// User management routes
userGroup := admin.Group("/users")
userGroup.Use(h.PermissionMiddleware("users.view"))
{
userGroup.GET("", h.HandleUsers)
userGroup.GET("/new", h.PermissionMiddleware("users.create"), h.AdminNewUserPage)
userGroup.GET("/new", h.PermissionMiddleware("users.create"), h.HandleNewUser)
userGroup.POST("", h.PermissionMiddleware("users.create"), h.HandleCreateUser)
userGroup.GET("/:id/edit", h.PermissionMiddleware("users.edit"), h.HandleEditUser)
userGroup.PUT("/:id", h.PermissionMiddleware("users.edit"), h.AdminUpdateUser)
userGroup.PUT("/:id", h.PermissionMiddleware("users.edit"), h.HandleUpdateUser)
userGroup.DELETE("/:id", h.PermissionMiddleware("users.delete"), h.HandleDeleteUser)
userGroup.PUT("/:id/toggle-lock", h.PermissionMiddleware("users.edit"), h.AdminToggleLockUser)
}
+104 -104
View File
@@ -16,100 +16,100 @@ import (
)
// HandleUsers handles the GET /admin/users route
func (h *Handlers) HandleUsers(c *gin.Context) {
var users []db.User
if err := h.DB.Find(&users).Error; err != nil {
c.String(http.StatusInternalServerError, "Failed to retrieve users")
return
}
// func (h *Handlers) HandleUsers(c *gin.Context) {
// var users []db.User
// if err := h.DB.Find(&users).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to retrieve users")
// return
// }
// Create the data for the Users component, not UserManagement
data := components.UsersData{
Users: users,
}
// // Create the data for the Users component, not UserManagement
// data := components.UsersData{
// Users: users,
// }
// Use the Users component from users.templ and ensure context flows through consistently
ctx := h.CreateTemplateContext(c)
components.Users(ctx, data).Render(ctx, c.Writer)
}
// // Use the Users component from users.templ and ensure context flows through consistently
// ctx := h.CreateTemplateContext(c)
// components.Users(ctx, data).Render(ctx, c.Writer)
// }
// HandleNewUser handles the GET /admin/users/new route
func (h *Handlers) HandleNewUser(c *gin.Context) {
// Create the data for the UserForm component
data := components.UserFormData{
IsNew: true,
ErrorMessage: "",
}
// func (h *Handlers) HandleNewUser(c *gin.Context) {
// // Create the data for the UserForm component
// data := components.UserFormData{
// IsNew: true,
// ErrorMessage: "",
// }
// Use consistent context handling
ctx := h.CreateTemplateContext(c)
err := components.UserForm(ctx, data).Render(ctx, c.Writer)
if err != nil {
log.Printf("ERROR rendering UserForm: %v", err)
c.String(http.StatusInternalServerError, "Error rendering form: %v", err)
return
}
}
// // Use consistent context handling
// ctx := h.CreateTemplateContext(c)
// err := components.UserForm(ctx, data).Render(ctx, c.Writer)
// if err != nil {
// log.Printf("ERROR rendering UserForm: %v", err)
// c.String(http.StatusInternalServerError, "Error rendering form: %v", err)
// return
// }
// }
// HandleCreateUser handles the POST /admin/users/new route
func (h *Handlers) HandleCreateUser(c *gin.Context) {
email := c.PostForm("email")
password := c.PostForm("password")
isAdmin := c.PostForm("is_admin") == "on"
// func (h *Handlers) HandleCreateUser(c *gin.Context) {
// email := c.PostForm("email")
// password := c.PostForm("password")
// isAdmin := c.PostForm("is_admin") == "on"
// Check if email already exists
var existingUser db.User
if err := h.DB.Where("email = ?", email).First(&existingUser).Error; err == nil {
c.String(http.StatusBadRequest, "Email already exists")
return
}
// // Check if email already exists
// var existingUser db.User
// if err := h.DB.Where("email = ?", email).First(&existingUser).Error; err == nil {
// c.String(http.StatusBadRequest, "Email already exists")
// return
// }
// Hash the password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
c.String(http.StatusInternalServerError, "Failed to hash password")
return
}
// // Hash the password
// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
// if err != nil {
// c.String(http.StatusInternalServerError, "Failed to hash password")
// return
// }
// Create the user
user := db.User{
Email: email,
PasswordHash: string(hashedPassword),
LastPasswordChange: time.Now(),
}
user.SetIsAdmin(isAdmin)
// // Create the user
// user := db.User{
// Email: email,
// PasswordHash: string(hashedPassword),
// LastPasswordChange: time.Now(),
// }
// user.SetIsAdmin(isAdmin)
if err := h.DB.Create(&user).Error; err != nil {
c.String(http.StatusInternalServerError, "Failed to create user")
return
}
// if err := h.DB.Create(&user).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to create user")
// return
// }
c.Redirect(http.StatusSeeOther, "/admin/users")
}
// c.Redirect(http.StatusSeeOther, "/admin/users")
// }
// HandleDeleteUser handles the POST /admin/users/delete route
func (h *Handlers) HandleDeleteUser(c *gin.Context) {
userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
if err != nil {
c.String(http.StatusBadRequest, "Invalid user ID")
return
}
// func (h *Handlers) HandleDeleteUser(c *gin.Context) {
// userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
// if err != nil {
// c.String(http.StatusBadRequest, "Invalid user ID")
// return
// }
// Don't allow deleting the current user
currentUserID := c.GetUint("userID")
if uint(userID) == currentUserID {
c.String(http.StatusBadRequest, "Cannot delete your own account")
return
}
// // Don't allow deleting the current user
// currentUserID := c.GetUint("userID")
// if uint(userID) == currentUserID {
// c.String(http.StatusBadRequest, "Cannot delete your own account")
// return
// }
// Delete the user
if err := h.DB.Delete(&db.User{}, userID).Error; err != nil {
c.String(http.StatusInternalServerError, "Failed to delete user")
return
}
// // Delete the user
// if err := h.DB.Delete(&db.User{}, userID).Error; err != nil {
// c.String(http.StatusInternalServerError, "Failed to delete user")
// return
// }
c.Redirect(http.StatusSeeOther, "/admin/users")
}
// c.Redirect(http.StatusSeeOther, "/admin/users")
// }
// HandleRegisterPage handles the GET /register route
func (h *Handlers) HandleRegisterPage(c *gin.Context) {
@@ -304,37 +304,37 @@ func (h *Handlers) AdminCreateUser(c *gin.Context) {
}
// HandleEditUser handles the GET /admin/users/:id/edit route
func (h *Handlers) HandleEditUser(c *gin.Context) {
id := c.Param("id")
// func (h *Handlers) HandleEditUser(c *gin.Context) {
// id := c.Param("id")
var user db.User
if err := h.DB.First(&user, id).Error; err != nil {
c.Redirect(http.StatusFound, "/admin/users")
return
}
// var user db.User
// if err := h.DB.First(&user, id).Error; err != nil {
// c.Redirect(http.StatusFound, "/admin/users")
// return
// }
// Get user's roles
userRoles, err := h.DB.GetUserRoles(user.ID)
if err != nil {
log.Printf("Error fetching user roles: %v", err)
}
// // Get user's roles
// userRoles, err := h.DB.GetUserRoles(user.ID)
// if err != nil {
// log.Printf("Error fetching user roles: %v", err)
// }
// Get all available roles
var allRoles []db.Role
if err := h.DB.Find(&allRoles).Error; err != nil {
log.Printf("Error fetching roles: %v", err)
}
// // Get all available roles
// var allRoles []db.Role
// if err := h.DB.Find(&allRoles).Error; err != nil {
// log.Printf("Error fetching roles: %v", err)
// }
// Use UserEdit component to match the form that's being submitted
data := components.UserEditData{
User: &user,
Roles: allRoles,
UserRoles: userRoles,
IsNew: false,
}
ctx := h.CreateTemplateContext(c)
components.UserEdit(ctx, data).Render(ctx, c.Writer)
}
// // Use UserEdit component to match the form that's being submitted
// data := components.UserEditData{
// User: &user,
// Roles: allRoles,
// UserRoles: userRoles,
// IsNew: false,
// }
// ctx := h.CreateTemplateContext(c)
// components.UserEdit(ctx, data).Render(ctx, c.Writer)
// }
// AdminUpdateUser handles the PUT /admin/users/:id route
func (h *Handlers) AdminUpdateUser(c *gin.Context) {