fully implemented timezone list dialog field

This commit is contained in:
2025-10-11 10:32:27 -06:00
parent 5ff7e3e35c
commit 297c1b9157
7 changed files with 56 additions and 8 deletions
+6
View File
@@ -62,6 +62,10 @@ type AmConfig struct {
Driver string `yaml:"driver"` Driver string `yaml:"driver"`
Dsn string `yaml:"dsn"` Dsn string `yaml:"dsn"`
} `yaml:"database"` } `yaml:"database"`
Defaults struct {
Language string `yaml:"language"`
TimeZone string `yaml:"timezone"`
} `yaml:"defaults"`
Email struct { Email struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
@@ -144,6 +148,8 @@ func overlayConfig(dest *AmConfig, loaded *AmConfig, defaults *AmConfig) {
dest.Site.UserAgreement.Text = overlayString(loaded.Site.UserAgreement.Text, defaults.Site.UserAgreement.Text) dest.Site.UserAgreement.Text = overlayString(loaded.Site.UserAgreement.Text, defaults.Site.UserAgreement.Text)
dest.Database.Driver = overlayString(loaded.Database.Driver, defaults.Database.Driver) dest.Database.Driver = overlayString(loaded.Database.Driver, defaults.Database.Driver)
dest.Database.Dsn = overlayString(loaded.Database.Dsn, defaults.Database.Dsn) dest.Database.Dsn = overlayString(loaded.Database.Dsn, defaults.Database.Dsn)
dest.Defaults.Language = overlayString(loaded.Defaults.Language, defaults.Defaults.Language)
dest.Defaults.TimeZone = overlayString(loaded.Defaults.TimeZone, defaults.Defaults.TimeZone)
dest.Email.Host = overlayString(loaded.Email.Host, defaults.Email.Host) dest.Email.Host = overlayString(loaded.Email.Host, defaults.Email.Host)
dest.Email.Port = overlayInt(loaded.Email.Port, defaults.Email.Port) dest.Email.Port = overlayInt(loaded.Email.Port, defaults.Email.Port)
dest.Email.Tls = overlayString(loaded.Email.Tls, defaults.Email.Tls) dest.Email.Tls = overlayString(loaded.Email.Tls, defaults.Email.Tls)
+3
View File
@@ -20,6 +20,9 @@ site:
database: database:
driver: "mysql" driver: "mysql"
dsn: "amsdb:x00yes2k@tcp(localhost)/amsterdam?parseTime=true&loc=Local" dsn: "amsdb:x00yes2k@tcp(localhost)/amsterdam?parseTime=true&loc=Local"
defaults:
language: "en-US"
timezone: "America/Denver"
email: email:
host: localhost host: localhost
port: 1025 port: 1025
+1
View File
@@ -14,6 +14,7 @@ require (
github.com/labstack/echo/v4 v4.13.4 github.com/labstack/echo/v4 v4.13.4
github.com/labstack/gommon v0.4.2 github.com/labstack/gommon v0.4.2
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/tkuchiki/go-timezone v0.2.3
golang.org/x/text v0.30.0 golang.org/x/text v0.30.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
+2
View File
@@ -51,6 +51,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tkuchiki/go-timezone v0.2.3 h1:D3TVdIPrFsu9lxGxqNX2wsZwn1MZtTqTW0mdevMozHc=
github.com/tkuchiki/go-timezone v0.2.3/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+7 -2
View File
@@ -18,6 +18,7 @@ import (
"strings" "strings"
"time" "time"
"git.erbosoft.com/amy/amsterdam/config"
"git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/database"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -173,9 +174,13 @@ func (d *Dialog) Render(ctxt AmContext) (string, any, error) {
switch fld.Type { switch fld.Type {
case "password": // clear all "password" fields as a security measure case "password": // clear all "password" fields as a security measure
d.Fields[i].Value = "" d.Fields[i].Value = ""
case "localelist": // default locale to en-US if we don't have one case "localelist": // default locale if we don't have one
if d.Fields[i].Value == "" { if d.Fields[i].Value == "" {
d.Fields[i].Value = "en-US" d.Fields[i].Value = config.GlobalConfig.Defaults.Language
}
case "tzlist": // default timezone if we don't have any
if d.Fields[i].Value == "" {
d.Fields[i].Value = config.GlobalConfig.Defaults.TimeZone
} }
} }
} }
+33 -5
View File
@@ -22,7 +22,6 @@ import (
"sync" "sync"
"time" "time"
"unicode" "unicode"
_ "unsafe" // HACK HACK HACK
"git.erbosoft.com/amy/amsterdam/config" "git.erbosoft.com/amy/amsterdam/config"
"github.com/CloudyKit/jet/v6" "github.com/CloudyKit/jet/v6"
@@ -31,6 +30,7 @@ import (
"github.com/biter777/countries" "github.com/biter777/countries"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/tkuchiki/go-timezone"
"golang.org/x/text/language" "golang.org/x/text/language"
"golang.org/x/text/language/display" "golang.org/x/text/language/display"
) )
@@ -79,16 +79,42 @@ func internalGetLanguageList() []Language {
slices.SortFunc(cachedLanguageList, func(a Language, b Language) int { slices.SortFunc(cachedLanguageList, func(a Language, b Language) int {
return strings.Compare(a.Name, b.Name) return strings.Compare(a.Name, b.Name)
}) })
log.Infof("internalGetLanguageList() loaded %d languages", len(cachedLanguageList))
} }
return cachedLanguageList return cachedLanguageList
} }
// getLanguageList is a wrapper around the list of languages that can be added to the template context. // getLanguageList is a wrapper around the list of languages that can be added to the template context.
func getLanguageList(a jet.Arguments) reflect.Value { func getLanguageList(a jet.Arguments) reflect.Value {
rc := internalGetLanguageList() return reflect.ValueOf(internalGetLanguageList())
log.Infof("GetLanguageList() provides %d items", len(rc)) }
return reflect.ValueOf(rc)
// cachedTimeZoneList is a wrapper around timezone.Timezones() that produces it by timezone name.
var cachedTimeZoneList []string = nil
// timeZoneListMutex controls access to internalGetTimeZoneList.
var timeZoneListMutex sync.Mutex
// internalGetTimeZoneList is a wrapper around TimeZone.TimeZones() that sorts and compacts the list.
func internalGetTimeZoneList() []string {
timeZoneListMutex.Lock()
defer timeZoneListMutex.Unlock()
if cachedTimeZoneList == nil {
timezones := timezone.New().Timezones()
ilist := make([]string, 0, len(timezones)*5)
for k, v := range timezones {
ilist = append(ilist, k)
ilist = append(ilist, v...)
}
slices.Sort(ilist)
cachedTimeZoneList = slices.Compact(ilist)
}
return cachedTimeZoneList
}
// getTimeZoneList is a wrapper around internalGetTimeZoneList that can be added to the template context.
func getTimeZoneList(a jet.Arguments) reflect.Value {
return reflect.ValueOf(internalGetTimeZoneList())
} }
// cachedCountryList is the cached country list after sorting. // cachedCountryList is the cached country list after sorting.
@@ -224,6 +250,7 @@ func SetupTemplates() {
views.AddGlobal("GlobalConfig", config.GlobalConfig) views.AddGlobal("GlobalConfig", config.GlobalConfig)
views.AddGlobalFunc("GetCountryList", getCountryList) views.AddGlobalFunc("GetCountryList", getCountryList)
views.AddGlobalFunc("GetLanguageList", getLanguageList) views.AddGlobalFunc("GetLanguageList", getLanguageList)
views.AddGlobalFunc("GetTimeZoneList", getTimeZoneList)
views.AddGlobalFunc("GetMonthList", getMonthList) views.AddGlobalFunc("GetMonthList", getMonthList)
views.AddGlobalFunc("MakeIntRange", makeIntRange) views.AddGlobalFunc("MakeIntRange", makeIntRange)
views.AddGlobalFunc("MakeYearRange", makeYearRange) views.AddGlobalFunc("MakeYearRange", makeYearRange)
@@ -236,6 +263,7 @@ func SetupTemplates() {
// preload the lists in the background // preload the lists in the background
go internalGetCountryList() go internalGetCountryList()
go internalGetLanguageList() go internalGetLanguageList()
go internalGetTimeZoneList()
} }
// TemplateRenderer is the Renderer instance set into the Echo context at creation time, to render Jet templates. // TemplateRenderer is the Renderer instance set into the Echo context at creation time, to render Jet templates.
+4 -1
View File
@@ -144,9 +144,12 @@
{{ .Caption }}{{ if .Subcaption != "" }} {{ .Subcaption }}{{ end }} {{ .Caption }}{{ if .Subcaption != "" }} {{ .Subcaption }}{{ end }}
{{ if .Required }}<span class="text-red-600">*</span>{{ end }} {{ if .Required }}<span class="text-red-600">*</span>{{ end }}
</label> </label>
{{ v := .Value }}
<select id="{{ .Name }}" name="{{ .Name }}" {{ if .Required }}required{{ end }} <select id="{{ .Name }}" name="{{ .Name }}" {{ if .Required }}required{{ end }}
class="flex-1 max-w-md px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> class="flex-1 max-w-md px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="XX">Unknown</option>{* TODO *} {{ range GetTimeZoneList() }}
<option value="{{ . }}" {{ if v == . }}selected{{ end }}>{{ . }}</option>
{{ end }}
</select> </select>
</div> </div>
{{ else if .Type == "date" }} {{ else if .Type == "date" }}