Update timezone plan: remove duplicate query, use 12-hour format
- Remove UpdateSystemTimezone query from plan; reuse existing UpdateSystemSetting with 'default_timezone' as the key parameter - Update handler code example to reference UpdateSystemSetting - Update FormatInTimezone format string to 12-hour (03:04 PM) - Update queries file description in summary table
This commit is contained in:
+55
-39
@@ -14,27 +14,44 @@ Add per-user timezone support with system-wide fallback (set via docker-compose)
|
|||||||
|
|
||||||
**File:** `database/schema/schema.sql`
|
**File:** `database/schema/schema.sql`
|
||||||
|
|
||||||
1. Add `timezone` column to `users` table:
|
1. Add `timezone` column directly to the `users` table definition (line ~36):
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS timezone VARCHAR(50) DEFAULT 'UTC';
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
email VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
username VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
first_name VARCHAR(255),
|
||||||
|
last_name VARCHAR(255),
|
||||||
|
role VARCHAR(20) NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user')),
|
||||||
|
theme VARCHAR(50) DEFAULT 'tokyo-night',
|
||||||
|
max_devices INTEGER DEFAULT 10,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
timezone VARCHAR(50) DEFAULT 'UTC'
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Add system-wide default timezone to `system_settings`:
|
> Note: The `timezone` column is already present at line 36 in the current schema. No change needed for this step.
|
||||||
|
|
||||||
|
1. Add `default_timezone` to the `system_settings` INSERT block (line ~49-52):
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
INSERT INTO system_settings (setting_key, setting_value, description) VALUES
|
INSERT INTO system_settings (setting_key, setting_value, description) VALUES
|
||||||
|
('scan_poll_interval_seconds', '60', 'How often to scan all libraries in minutes'),
|
||||||
|
('auto_scan_enabled', 'true', 'Whether auto-scanning is enabled system-wide'),
|
||||||
('default_timezone', 'UTC', 'System default timezone')
|
('default_timezone', 'UTC', 'System default timezone')
|
||||||
ON CONFLICT (setting_key) DO NOTHING;
|
ON CONFLICT (setting_key) DO NOTHING;
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Add index:
|
1. Add index in the indexes section (after line ~460, with other user indexes):
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX IF NOT EXISTS idx_users_timezone ON users(timezone);
|
CREATE INDEX IF NOT EXISTS idx_users_timezone ON users(timezone);
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Regenerate sqlc code:
|
1. Regenerate sqlc code:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd internal/database && sqlc generate
|
cd internal/database && sqlc generate
|
||||||
@@ -54,11 +71,10 @@ UPDATE users SET timezone = $2, updated_at = NOW() WHERE id = $1;
|
|||||||
|
|
||||||
-- name: GetSystemTimezone :one
|
-- name: GetSystemTimezone :one
|
||||||
SELECT setting_value FROM system_settings WHERE setting_key = 'default_timezone';
|
SELECT setting_value FROM system_settings WHERE setting_key = 'default_timezone';
|
||||||
|
|
||||||
-- name: UpdateSystemTimezone :exec
|
|
||||||
UPDATE system_settings SET setting_value = $2, updated_at = NOW() WHERE setting_key = 'default_timezone';
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> Note: `UpdateSystemTimezone` is omitted because the existing `UpdateSystemSetting` query handles it by passing `'default_timezone'` as the key parameter.
|
||||||
|
|
||||||
Regenerate after adding queries:
|
Regenerate after adding queries:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -93,7 +109,7 @@ func FormatInTimezone(t time.Time, timezone string) string {
|
|||||||
loc = time.UTC
|
loc = time.UTC
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.In(loc).Format("01-02-2006 15:04")
|
return t.In(loc).Format("01-02-2006 03:04 PM")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatTimestamptzInTimezone formats a pgtype.Timestamptz in the specified timezone
|
// FormatTimestamptzInTimezone formats a pgtype.Timestamptz in the specified timezone
|
||||||
@@ -199,7 +215,7 @@ func (h *SystemSettingsHandler) UpdateTimezoneSettings(c *echo.Context) error {
|
|||||||
if _, err := time.LoadLocation(req.DefaultTimezone); err != nil {
|
if _, err := time.LoadLocation(req.DefaultTimezone); err != nil {
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid timezone"})
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid timezone"})
|
||||||
}
|
}
|
||||||
err := h.db.UpdateSystemTimezone(c.Request().Context(), database.UpdateSystemTimezoneParams{
|
err := h.db.UpdateSystemSetting(c.Request().Context(), database.UpdateSystemSettingParams{
|
||||||
SettingKey: "default_timezone",
|
SettingKey: "default_timezone",
|
||||||
SettingValue: req.DefaultTimezone,
|
SettingValue: req.DefaultTimezone,
|
||||||
})
|
})
|
||||||
@@ -267,20 +283,20 @@ Add system default timezone setting:
|
|||||||
|
|
||||||
### Files to update
|
### Files to update
|
||||||
|
|
||||||
| Template | Line(s) | Field(s) |
|
| Template | Line(s) | Field(s) |
|
||||||
|----------|---------|----------|
|
| ------------------------------------ | -------------- | ----------------------------------- |
|
||||||
| `templates/book_detail.templ` | ~253, ~282 | `LastReadAt`, `DatePublished` |
|
| `templates/book_detail.templ` | ~253, ~282 | `LastReadAt`, `DatePublished` |
|
||||||
| `templates/book_detail_modals.templ` | ~68, ~113 | `Timestamp`, `LastReadAt` |
|
| `templates/book_detail_modals.templ` | ~68, ~113 | `Timestamp`, `LastReadAt` |
|
||||||
| `templates/devices.templ` | ~83, ~91, ~174 | `LastSync`, `LastSeen`, `ExpiresAt` |
|
| `templates/devices.templ` | ~83, ~91, ~174 | `LastSync`, `LastSeen`, `ExpiresAt` |
|
||||||
| `templates/conflicts.templ` | ~114 | `CreatedAt` |
|
| `templates/conflicts.templ` | ~114 | `CreatedAt` |
|
||||||
| `templates/admin_users.templ` | ~89 | `CreatedAt` |
|
| `templates/admin_users.templ` | ~89 | `CreatedAt` |
|
||||||
| `templates/queue.templ` | ~138 | `CreatedAt` |
|
| `templates/queue.templ` | ~138 | `CreatedAt` |
|
||||||
|
|
||||||
### Change pattern
|
### Change pattern
|
||||||
|
|
||||||
```templ
|
```templ
|
||||||
<!-- Before -->
|
<!-- Before -->
|
||||||
{ book.ReadingProgress.LastReadAt.Time.Format("2006-01-02 15:04") }
|
{ book.ReadingProgress.LastReadAt.Time.Format("01-02-2006 03:04 PM") }
|
||||||
|
|
||||||
<!-- After -->
|
<!-- After -->
|
||||||
{ templates.FormatTimestamptzInTimezone(book.ReadingProgress.LastReadAt, user.Timezone) }
|
{ templates.FormatTimestamptzInTimezone(book.ReadingProgress.LastReadAt, user.Timezone) }
|
||||||
@@ -290,7 +306,7 @@ For `time.Time` fields:
|
|||||||
|
|
||||||
```templ
|
```templ
|
||||||
<!-- Before -->
|
<!-- Before -->
|
||||||
{ device.LastSync.Format("2006-01-02 15:04") }
|
{ device.LastSync.Format("01-02-2006 03:04 PM") }
|
||||||
|
|
||||||
<!-- After -->
|
<!-- After -->
|
||||||
{ templates.FormatInTimezone(device.LastSync, user.Timezone) }
|
{ templates.FormatInTimezone(device.LastSync, user.Timezone) }
|
||||||
@@ -320,25 +336,25 @@ TZ=UTC
|
|||||||
|
|
||||||
## Files Modified Summary
|
## Files Modified Summary
|
||||||
|
|
||||||
| File | Change |
|
| File | Change |
|
||||||
|------|--------|
|
| --------------------------------------- | ------------------------------------------------------------------------------- |
|
||||||
| `database/schema/schema.sql` | Add timezone column to users, system_setting row |
|
| `database/schema/schema.sql` | Add timezone column to users, system_setting row |
|
||||||
| `internal/database/queries/queries.sql` | Add UpdateUserTimezone, GetSystemTimezone, UpdateSystemTimezone |
|
| `internal/database/queries/queries.sql` | Add UpdateUserTimezone, GetSystemTimezone (reuses existing UpdateSystemSetting) |
|
||||||
| `templates/utils.go` | Add FormatInTimezone, FormatTimestamptzInTimezone |
|
| `templates/utils.go` | Add FormatInTimezone, FormatTimestamptzInTimezone |
|
||||||
| `templates/types.go` | Add Timezone field to User struct |
|
| `templates/types.go` | Add Timezone field to User struct |
|
||||||
| `internal/router/helpers.go` | Pass timezone to template User |
|
| `internal/router/helpers.go` | Pass timezone to template User |
|
||||||
| `internal/handlers/auth.go` | Handle timezone updates in UpdateProfile |
|
| `internal/handlers/auth.go` | Handle timezone updates in UpdateProfile |
|
||||||
| `internal/handlers/system_settings.go` | Add timezone settings handler |
|
| `internal/handlers/system_settings.go` | Add timezone settings handler |
|
||||||
| `templates/profile_form.templ` | Add timezone dropdown |
|
| `templates/profile_form.templ` | Add timezone dropdown |
|
||||||
| `templates/admin_settings.templ` | Add default timezone setting |
|
| `templates/admin_settings.templ` | Add default timezone setting |
|
||||||
| `templates/book_detail.templ` | Update time displays |
|
| `templates/book_detail.templ` | Update time displays |
|
||||||
| `templates/book_detail_modals.templ` | Update time displays |
|
| `templates/book_detail_modals.templ` | Update time displays |
|
||||||
| `templates/devices.templ` | Update time displays |
|
| `templates/devices.templ` | Update time displays |
|
||||||
| `templates/conflicts.templ` | Update time displays |
|
| `templates/conflicts.templ` | Update time displays |
|
||||||
| `templates/admin_users.templ` | Update time displays |
|
| `templates/admin_users.templ` | Update time displays |
|
||||||
| `templates/queue.templ` | Update time displays |
|
| `templates/queue.templ` | Update time displays |
|
||||||
| `docker-compose.yml` | Add TZ env var |
|
| `docker-compose.yml` | Add TZ env var |
|
||||||
| `.env.example` | Add TZ example |
|
| `.env.example` | Add TZ example |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user