diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index b9806b7..16fa9bf 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -87,6 +87,7 @@ type UpdateProfileRequest struct { FirstName string `json:"first_name,omitempty" validate:"omitempty,max=100"` LastName string `json:"last_name,omitempty" validate:"omitempty,max=100"` Theme string `json:"theme,omitempty" validate:"omitempty"` + Timezone string `json:"timezone,omitempty" validate:"omitempty"` } type AdminUpdateUserRequest struct { @@ -564,6 +565,22 @@ func (h *AuthHandler) UpdateProfile(c *echo.Context) error { } } + // Update timezone + if req.Timezone != "" { + if _, err := time.LoadLocation(req.Timezone); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{ + "error": "Invalid timezone", + }) + } + err := h.db.UpdateUserTimezone(ctx, database.UpdateUserTimezoneParams{ + ID: pgtype.UUID{Bytes: userUUID, Valid: true}, + Timezone: pgtype.Text{String: req.Timezone, Valid: true}, + }) + if err != nil { + return err + } + } + // Update email (if provided) if req.Email != "" { existingUser, err := h.db.GetUserByEmail(c.Request().Context(), req.Email) diff --git a/internal/handlers/system_settings.go b/internal/handlers/system_settings.go index 0acde00..f2af571 100644 --- a/internal/handlers/system_settings.go +++ b/internal/handlers/system_settings.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" "strconv" + "time" "github.com/jackc/pgx/v5" "github.com/labstack/echo/v5" @@ -31,6 +32,28 @@ type ScanSettingsResponse struct { Message string `json:"message,omitempty"` } +type UpdateTimezoneSettingsRequest struct { + DefaultTimezone string `json:"default_timezone" validate:"required"` +} + +func (h *SystemSettingsHandler) UpdateTimezoneSettings(c *echo.Context) error { + var req UpdateTimezoneSettingsRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request"}) + } + if _, err := time.LoadLocation(req.DefaultTimezone); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid timezone"}) + } + err := h.db.UpdateSystemSetting(c.Request().Context(), database.UpdateSystemSettingParams{ + SettingKey: "default_timezone", + SettingValue: req.DefaultTimezone, + }) + if err != nil { + return err + } + return c.JSON(http.StatusOK, map[string]string{"message": "Timezone updated"}) +} + func (h *SystemSettingsHandler) UpdateScanSettings(c *echo.Context) error { var req UpdateScanSettingsRequest if err := c.Bind(&req); err != nil { diff --git a/internal/router/helpers.go b/internal/router/helpers.go index d483dc0..449efee 100644 --- a/internal/router/helpers.go +++ b/internal/router/helpers.go @@ -35,6 +35,11 @@ func getTemplateUserWithTheme(c *echo.Context, cfg *Config) (templates.User, err userTheme = userDB.Theme.String } + userTimezone := "UTC" + if userDB.Timezone.Valid { + userTimezone = userDB.Timezone.String + } + // Extract JWT token for WebSocket authentication token := "" if cookie, err := c.Cookie("token"); err == nil { @@ -48,6 +53,7 @@ func getTemplateUserWithTheme(c *echo.Context, cfg *Config) (templates.User, err Role: userRole, Theme: userTheme, Token: token, + Timezone: userTimezone, }, nil } diff --git a/templates/types.go b/templates/types.go index 8ec1371..83448d4 100644 --- a/templates/types.go +++ b/templates/types.go @@ -16,6 +16,7 @@ type User struct { LastName string CreatedAt time.Time Token string + Timezone string } type PageData struct { diff --git a/templates/utils.go b/templates/utils.go index f463f42..9ad94f1 100644 --- a/templates/utils.go +++ b/templates/utils.go @@ -6,6 +6,7 @@ import ( "fmt" "net/url" "strings" + "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgtype" @@ -197,3 +198,25 @@ func formatAlternateInfo(data []byte) string { return strings.Join(parts, " ") } + +// FormatInTimezone formats a time.Time in the specified timezone as MM-DD-YYYY HH:MM +func FormatInTimezone(t time.Time, timezone string) string { + if t.IsZero() { + return "" + } + + loc, err := time.LoadLocation(timezone) + if err != nil { + loc = time.UTC + } + + return t.In(loc).Format("01-02-2006 03:04 PM") +} + +// FormatTimestamptzInTimezone formats a pgtype.Timestamptz in the specified timezone +func FormatTimestamptzInTimezone(t pgtype.Timestamptz, timezone string) string { + if !t.Valid { + return "" + } + return FormatInTimezone(t.Time, timezone) +}