From a89a9776c3c7ce447de796c181901790c3e9cfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Mon, 12 May 2025 21:10:40 +0200 Subject: [PATCH 01/19] Migrate to PostgreSQL and fix related issues --- compose.yml | 14 ++++ controllers/contacts.go | 26 +++--- controllers/debug.go | 4 +- controllers/fields.go | 5 +- controllers/lists.go | 4 +- controllers/members.go | 24 +++--- controllers/roles.go | 2 +- controllers/sections.go | 22 ++--- controllers/users.go | 8 +- go.mod | 14 ++-- go.sum | 49 +++-------- helpers/config.go | 1 + helpers/database.go | 4 +- helpers/database/people.go | 161 ++++++++++++++++++++----------------- helpers/users.go | 2 +- models/fields.go | 8 +- models/sections.go | 2 +- 17 files changed, 176 insertions(+), 174 deletions(-) create mode 100644 compose.yml diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..b1e57b0 --- /dev/null +++ b/compose.yml @@ -0,0 +1,14 @@ +services: + postgres: + image: postgres:latest + container_name: camarades-postgres + ports: + - "127.0.0.1:5432:5432" + environment: + POSTGRES_USER: camarades + POSTGRES_PASSWORD: camarades + POSTGRES_DB: camarades + volumes: + - postgres_data:/var/lib/postgresql/data +volumes: + postgres_data: diff --git a/controllers/contacts.go b/controllers/contacts.go index a7d104c..5f4b8a8 100644 --- a/controllers/contacts.go +++ b/controllers/contacts.go @@ -69,7 +69,7 @@ func Contacts(c *fiber.Ctx) error { params.PersonType = "contacts" var sections []models.Section - db.Order("name collate nocase asc").Find(§ions, "contains_contacts = ? AND id IN ?", true, allowedSections) + db.Order("name asc").Find(§ions, "contains_contacts = ? AND id IN ?", true, allowedSections) params.AllowedSections = allowedSections // Security for active contacts @@ -156,7 +156,7 @@ func ContactsExport(c *fiber.Ctx) error { params.PersonType = "contacts" var sections []models.Section - db.Order("name collate nocase asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) + db.Order("name asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) params.AllowedSections = allowedSections // Security for active contacts @@ -280,7 +280,7 @@ func ContactsExport(c *fiber.Ctx) error { } else if field.FieldType == "list" { - if field.List.Multi { + if field.List != nil && field.List.Multi { if countMulti == 0 { c.Writef("\"") @@ -323,7 +323,7 @@ func ContactShow(c *fiber.Ctx) error { var person models.Person result := db.Unscoped().Preload("Section").Find( - &person, "id = ? AND is_contact", id, true, + &person, "id = ? AND is_contact = ?", id, true, ) if result.Error != nil { @@ -416,7 +416,7 @@ func ContactAdd(c *fiber.Ctx) error { } var sections []models.Section - db.Order("name collate nocase asc").Find( + db.Order("name asc").Find( §ions, "contains_contacts = ?", true, @@ -511,7 +511,7 @@ func ContactAdd(c *fiber.Ctx) error { } for _, field := range fields { - if field.List.Multi { + if field.List != nil && field.List.Multi { for _, listItem := range field.List.ListItems { key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) value := c.FormValue(key) @@ -520,7 +520,7 @@ func ContactAdd(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = listItem.ID + fieldValue.ListItemID = &listItem.ID db.Create(&fieldValue) } } @@ -594,7 +594,8 @@ func ContactAdd(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = uint(valueInt) + valueUint := uint(valueInt) + fieldValue.ListItemID = &valueUint db.Create(&fieldValue) } } @@ -659,7 +660,7 @@ func ContactEdit(c *fiber.Ctx) error { } var sections []models.Section - db.Order("name collate nocase asc").Find( + db.Order("name asc").Find( §ions, "contains_contacts = ?", true, @@ -760,7 +761,7 @@ func ContactEdit(c *fiber.Ctx) error { field.ID, ) - if field.List.Multi { + if field.List != nil && field.List.Multi { for _, listItem := range field.List.ListItems { key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) value := c.FormValue(key) @@ -769,7 +770,7 @@ func ContactEdit(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = listItem.ID + fieldValue.ListItemID = &listItem.ID db.Create(&fieldValue) } } @@ -843,7 +844,8 @@ func ContactEdit(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = uint(valueInt) + valueUint := uint(valueInt) + fieldValue.ListItemID = &valueUint db.Create(&fieldValue) } } diff --git a/controllers/debug.go b/controllers/debug.go index 30a0624..1a4d93e 100644 --- a/controllers/debug.go +++ b/controllers/debug.go @@ -110,7 +110,7 @@ func DebugFixFieldsOrder(c *fiber.Ctx) error { var memberFields []models.Field result := db.Order( - "name collate nocase asc", + "name asc", ).Find( &memberFields, "person_type = ?", "member", ) @@ -130,7 +130,7 @@ func DebugFixFieldsOrder(c *fiber.Ctx) error { var contactFields []models.Field result2 := db.Order( - "name collate nocase asc", + "name asc", ).Find( &contactFields, "person_type = ?", "contact", ) diff --git a/controllers/fields.go b/controllers/fields.go index 22f3d43..17c5a8a 100644 --- a/controllers/fields.go +++ b/controllers/fields.go @@ -74,7 +74,7 @@ func FieldAdd(c *fiber.Ctx) error { } var lists []models.List - db.Order("name collate nocase asc").Find(&lists) + db.Order("name asc").Find(&lists) if c.Method() == "POST" { field.Name = c.FormValue("name") @@ -107,7 +107,8 @@ func FieldAdd(c *fiber.Ctx) error { if err != nil || listID < 1 { errors = append(errors, "Liste incorrecte") } else { - field.ListID = uint(listID) + listID2 := uint(listID) + field.ListID = &listID2 } } diff --git a/controllers/lists.go b/controllers/lists.go index a947723..36b1b14 100644 --- a/controllers/lists.go +++ b/controllers/lists.go @@ -17,7 +17,7 @@ func Lists(c *fiber.Ctx) error { } var lists []models.List - result := db.Order("name collate nocase asc").Find(&lists) + result := db.Order("name asc").Find(&lists) if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { return err @@ -54,7 +54,7 @@ func ListShow(c *fiber.Ctx) error { ) var listItems []models.ListItem - db.Order("value collate nocase asc").Find(&listItems, "list_id = ?", id) + db.Order("value asc").Find(&listItems, "list_id = ?", id) return c.Render("list", fiber.Map{ "PageTitle": title, diff --git a/controllers/members.go b/controllers/members.go index f263fec..c26381a 100644 --- a/controllers/members.go +++ b/controllers/members.go @@ -84,7 +84,7 @@ func Members(c *fiber.Ctx) error { params.PersonType = "members" var sections []models.Section - db.Order("name collate nocase asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) + db.Order("name asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) params.AllowedSections = allowedSections // Security for active contacts @@ -171,7 +171,7 @@ func MembersExport(c *fiber.Ctx) error { params.PersonType = "members" var sections []models.Section - db.Order("name collate nocase asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) + db.Order("name asc").Find(§ions, "contains_members = ? AND id IN ?", true, allowedSections) params.AllowedSections = allowedSections // Security for active contacts @@ -338,7 +338,7 @@ func MemberShow(c *fiber.Ctx) error { var person models.Person result := db.Unscoped().Preload("Section").Find( - &person, "id = ? AND is_member", id, true, + &person, "id = ? AND is_member = ?", id, true, ) if result.Error != nil { @@ -431,7 +431,7 @@ func MemberAdd(c *fiber.Ctx) error { } var sections []models.Section - db.Order("name collate nocase asc").Find( + db.Order("name asc").Find( §ions, "contains_members = ?", true, @@ -526,7 +526,7 @@ func MemberAdd(c *fiber.Ctx) error { } for _, field := range fields { - if field.List.Multi { + if field.List != nil && field.List.Multi { for _, listItem := range field.List.ListItems { key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) value := c.FormValue(key) @@ -535,7 +535,7 @@ func MemberAdd(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = listItem.ID + fieldValue.ListItemID = &listItem.ID db.Create(&fieldValue) } } @@ -609,7 +609,8 @@ func MemberAdd(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = uint(valueInt) + valueUint := uint(valueInt) + fieldValue.ListItemID = &valueUint db.Create(&fieldValue) } } @@ -674,7 +675,7 @@ func MemberEdit(c *fiber.Ctx) error { } var sections []models.Section - db.Order("name collate nocase asc").Find( + db.Order("name asc").Find( §ions, "contains_members = ?", true, @@ -775,7 +776,7 @@ func MemberEdit(c *fiber.Ctx) error { field.ID, ) - if field.List.Multi { + if field.List != nil && field.List.Multi { for _, listItem := range field.List.ListItems { key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) value := c.FormValue(key) @@ -784,7 +785,7 @@ func MemberEdit(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = listItem.ID + fieldValue.ListItemID = &listItem.ID db.Create(&fieldValue) } } @@ -858,7 +859,8 @@ func MemberEdit(c *fiber.Ctx) error { var fieldValue models.FieldValue fieldValue.FieldID = field.ID fieldValue.PersonID = person.ID - fieldValue.ListItemID = uint(valueInt) + valueUint := uint(valueInt) + fieldValue.ListItemID = &valueUint db.Create(&fieldValue) } } diff --git a/controllers/roles.go b/controllers/roles.go index 575675a..21a0bba 100644 --- a/controllers/roles.go +++ b/controllers/roles.go @@ -17,7 +17,7 @@ func Roles(c *fiber.Ctx) error { } var roles []models.Role - result := db.Order("name collate nocase asc").Find(&roles) + result := db.Order("name asc").Find(&roles) if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { return err diff --git a/controllers/sections.go b/controllers/sections.go index 4c13af9..1632c54 100644 --- a/controllers/sections.go +++ b/controllers/sections.go @@ -18,7 +18,7 @@ func Sections(c *fiber.Ctx) error { } var sections []models.Section - result := db.Order("name collate nocase asc").Preload("ParentSection").Find(§ions) + result := db.Order("name asc").Preload("ParentSection").Find(§ions) if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { return err @@ -70,8 +70,8 @@ func SectionAdd(c *fiber.Ctx) error { } var sections []models.Section - db.Order("name collate nocase asc").Find( - §ions, "id <> ? AND parent_section_id = 0", section.ID, + db.Order("name asc").Find( + §ions, "id <> ? AND parent_section_id IS NULL", section.ID, ) if c.Method() == "POST" { @@ -106,12 +106,12 @@ func SectionAdd(c *fiber.Ctx) error { section.Name = name section.ShortName = shortName - section.ParentSectionID = 0 parentSectionID, err := strconv.ParseUint(parentSection, 10, 0) if err == nil { for _, parentSection := range sections { - if parentSection.ID == uint(parentSectionID) { - section.ParentSectionID = uint(parentSectionID) + parentSectionID2 := uint(parentSectionID) + if parentSection.ID == parentSectionID2 { + section.ParentSectionID = &parentSectionID2 break } } @@ -177,8 +177,8 @@ func SectionEdit(c *fiber.Ctx) error { ) var sections []models.Section - db.Order("name collate nocase asc").Find( - §ions, "id <> ? AND parent_section_id = 0", section.ID, + db.Order("name asc").Find( + §ions, "id <> ? AND parent_section_id IS NULL", section.ID, ) var errors []string @@ -200,12 +200,12 @@ func SectionEdit(c *fiber.Ctx) error { section.Name = name section.ShortName = shortName - section.ParentSectionID = 0 parentSectionID, err := strconv.ParseUint(parentSection, 10, 0) if err == nil { for _, parentSection := range sections { - if parentSection.ID == uint(parentSectionID) { - section.ParentSectionID = uint(parentSectionID) + parentSectionID2 := uint(parentSectionID) + if parentSection.ID == parentSectionID2 { + section.ParentSectionID = &parentSectionID2 break } } diff --git a/controllers/users.go b/controllers/users.go index 126a779..4c15889 100644 --- a/controllers/users.go +++ b/controllers/users.go @@ -25,7 +25,7 @@ func Users(c *fiber.Ctx) error { } var users []models.User - result := db.Order("name collate nocase asc").Find(&users) + result := db.Order("name asc").Find(&users) if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { return err @@ -62,7 +62,7 @@ func UserShow(c *fiber.Ctx) error { ) var userRoles []models.UserRole - db.Joins("Role").Joins("Section").Order("Section__name collate nocase asc").Find( + db.Joins("Role").Joins("Section").Order("\"Section\".\"name\" asc").Find( &userRoles, "user_id = ?", id, ) @@ -284,10 +284,10 @@ func UserPermissions(c *fiber.Ctx) error { ) var roles []models.Role - db.Order("name collate nocase asc").Find(&roles) + db.Order("name asc").Find(&roles) var sections []models.Section - db.Order("name collate nocase asc").Find(§ions) + db.Order("name asc").Find(§ions) var errors []string if c.Method() == "POST" { diff --git a/go.mod b/go.mod index 90d626e..1392525 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/brianvoe/gofakeit/v7 v7.2.1 github.com/charmbracelet/log v0.4.1 github.com/flosch/pongo2/v6 v6.0.0 - github.com/glebarez/sqlite v1.11.0 github.com/go-playground/validator/v10 v10.26.0 github.com/gofiber/fiber/v2 v2.52.6 github.com/gofiber/helmet/v2 v2.2.26 @@ -17,6 +16,7 @@ require ( github.com/pquerna/otp v1.4.0 github.com/yuin/goldmark v1.7.11 golang.org/x/crypto v0.38.0 + gorm.io/driver/postgres v1.5.11 gorm.io/gorm v1.26.1 ) @@ -36,7 +36,6 @@ require ( github.com/dgraph-io/ristretto v0.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -49,6 +48,10 @@ require ( github.com/golang/snappy v1.0.0 // indirect github.com/golobby/cast v1.3.3 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.4 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -58,10 +61,8 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/muesli/termenv v0.16.0 // indirect - github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.62.0 // indirect @@ -69,11 +70,8 @@ require ( go.opencensus.io v0.24.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/net v0.40.0 // indirect + golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - modernc.org/libc v1.65.4 // indirect - modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.10.0 // indirect - modernc.org/sqlite v1.37.0 // indirect ) diff --git a/go.sum b/go.sum index c5aad78..dbe47bd 100644 --- a/go.sum +++ b/go.sum @@ -63,10 +63,6 @@ github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vt github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= -github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= -github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= -github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -130,13 +126,19 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -169,8 +171,6 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -181,8 +181,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -204,6 +202,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -242,8 +241,6 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -286,8 +283,6 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -323,31 +318,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= +gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= -modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= -modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= -modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8= -modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= -modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= -modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.65.4 h1:8oVL/29p3e+ZvMv4nE1pryq5p8grHiFsU8bN8Eah/rs= -modernc.org/libc v1.65.4/go.mod h1:MOiGAM9lrMBT9L8xT1nO41qYl5eg9gCp9/kWhz5L7WA= -modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= -modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4= -modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= -modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= -modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= -modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= -modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= -modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= -modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= -modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/helpers/config.go b/helpers/config.go index 7e1b931..835b7a1 100644 --- a/helpers/config.go +++ b/helpers/config.go @@ -16,6 +16,7 @@ type Config struct { } Database struct { Location string `env:"DATABASE_LOCATION"` + DSN string `env:"DATABASE_DSN"` } Sessions struct { Location string `env:"SESSIONS_LOCATION"` diff --git a/helpers/database.go b/helpers/database.go index 5e9d0c7..e4bc052 100644 --- a/helpers/database.go +++ b/helpers/database.go @@ -2,7 +2,7 @@ package helpers import ( "git.readonly.ch/bouzoure/pop-camarades/models" - "github.com/glebarez/sqlite" + "gorm.io/driver/postgres" "gorm.io/gorm" gormLogger "gorm.io/gorm/logger" ) @@ -30,7 +30,7 @@ func GetDatabase() (*gorm.DB, error) { } database, err = gorm.Open( - sqlite.Open(config.Database.Location), + postgres.Open(config.Database.DSN), &gormConfig, ) if err != nil { diff --git a/helpers/database/people.go b/helpers/database/people.go index e22677e..90310cf 100644 --- a/helpers/database/people.go +++ b/helpers/database/people.go @@ -44,7 +44,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } // SQL qeury for results - sqlQuery := `--sql + sqlQuery := ` SELECT people.*, sections.name AS Section__name FROM people @@ -52,7 +52,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { ON people.section_id = sections.id` // SQL query to count results - sqlQueryCount := `--sql + sqlQueryCount := ` SELECT people.id FROM people ` @@ -62,7 +62,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { var sqlFilters string // Filter: member or contact - sqlFilters = `--sql + sqlFilters = ` WHERE is_member = @is_member AND is_contact = @is_contact ` @@ -78,11 +78,11 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filter: name -> first_name + last_name if len(params.Name) > 0 { - nameFilter := `--sql - people.first_name LIKE @name - OR people.last_name LIKE @name - OR CONCAT(people.first_name, ' ', people.last_name) LIKE @name - OR CONCAT(people.last_name, ' ', people.first_name) LIKE @name + nameFilter := ` + LOWER(people.first_name) LIKE LOWER(@name) + OR LOWER(people.last_name) LIKE LOWER(@name) + OR LOWER(CONCAT(people.first_name, ' ', people.last_name)) LIKE LOWER(@name) + OR LOWER(CONCAT(people.last_name, ' ', people.first_name)) LIKE LOWER(@name) ` sqlParams = append(sqlParams, sql.Named( "name", @@ -92,10 +92,10 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { if strings.Contains(params.Name, " ") { names := strings.Split(params.Name, " ") for index, name := range names { - nameFilter = fmt.Sprintf(`--sql + nameFilter = fmt.Sprintf(` %s - OR people.first_name LIKE @name_%d - OR people.last_name LIKE @name_%d + OR LOWER(people.first_name) LIKE LOWER(@name_%d) + OR LOWER(people.last_name) LIKE LOWER(@name_%d) `, nameFilter, index, index) sqlParams = append(sqlParams, sql.Named( @@ -105,7 +105,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } } - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND (%s) `, sqlFilters, nameFilter) @@ -113,7 +113,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filter: section if params.Section > 0 { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.section_id = @section `, sqlFilters) @@ -122,7 +122,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filter: active (only apply if archived is false) if params.Active && !params.Archive { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.deleted_at IS NULL `, sqlFilters) @@ -130,7 +130,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filter: archived (only apply if active is false) if params.Archive && !params.Active { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.deleted_at IS NOT NULL `, sqlFilters) @@ -138,7 +138,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filter: if both active and archived are turned off, return nothing if !params.Archive && !params.Active { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND 0=1 `, sqlFilters) @@ -146,9 +146,9 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filters: email if len(params.Email) > 0 { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s - AND people.email LIKE @email + AND LOWER(people.email) LIKE LOWER(@email) `, sqlFilters) sqlParams = append(sqlParams, sql.Named( "email", @@ -158,35 +158,29 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filters: phone if len(params.Phone) > 0 { - params.Phone = strings.ReplaceAll(params.Phone, " ", "") - params.Phone = strings.ReplaceAll(params.Phone, "-", "") - params.Phone = strings.ReplaceAll(params.Phone, ".", "") - params.Phone = strings.ReplaceAll(params.Phone, "/", "") - params.Phone = strings.ReplaceAll(params.Phone, "+", "") + var phoneWithoutZeroFilter string + if string(params.Phone[0]) == "0" { + phoneWithoutZeroFilter = ` + OR + TRANSLATE(people.phone, ' ,-,.,/,+', '') LIKE TRANSLATE(@phone2, ' ,-,.,/,+', '') OR + TRANSLATE(people.mobile, ' ,-,.,/,+', '') LIKE TRANSLATE(@phone2, ' ,-,.,/,+', '') + ` - sqlFilters = fmt.Sprintf(`--sql + phone2 := params.Phone[1:] + sqlParams = append(sqlParams, sql.Named( + "phone2", + fmt.Sprintf("%%%s%%", phone2), + )) + } + + sqlFilters = fmt.Sprintf(` %s AND ( - REPLACE( - REPLACE( - REPLACE( - REPLACE( - REPLACE(people.phone, ' ', '') - , '-', '') - , '.', '') - , '/', '') - , '+', '') LIKE @phone - OR REPLACE( - REPLACE( - REPLACE( - REPLACE( - REPLACE(people.mobile, ' ', '') - , '-', '') - , '.', '') - , '/', '') - , '+', '') LIKE @phone + TRANSLATE(people.phone, ' ,-,.,/,+', '') LIKE TRANSLATE(@phone, ' ,-,.,/,+', '') OR + TRANSLATE(people.mobile, ' ,-,.,/,+', '') LIKE TRANSLATE(@phone, ' ,-,.,/,+', '') + %s ) - `, sqlFilters) + `, sqlFilters, phoneWithoutZeroFilter) sqlParams = append(sqlParams, sql.Named( "phone", fmt.Sprintf("%%%s%%", params.Phone), @@ -195,7 +189,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filters: address if len(params.Address) > 0 { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND ( people.address1 LIKE @address @@ -210,7 +204,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filters: postal code if len(params.PostalCode) > 0 { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.postal_code = @postal_code `, sqlFilters) @@ -222,7 +216,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // Filters: city if len(params.City) > 0 { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.city LIKE @city `, sqlFilters) @@ -233,7 +227,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } // Security filter: only show results in allowed secitons - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND people.section_id IN @allowed_sections `, sqlFilters) @@ -250,7 +244,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { continue } - sqlFieldJoins = fmt.Sprintf(`--sql + sqlFieldJoins = fmt.Sprintf(` %s LEFT JOIN field_values AS field_%d @@ -271,36 +265,53 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } switch v := value.(type) { - default: - fmt.Println(v) - continue case string: - filter = fmt.Sprintf(`--sql - %s - field_%d.value_string LIKE @field_%d_%d - OR field_%d.value_date LIKE @field_%d_%d - `, filter, field.ID, field.ID, index, field.ID, field.ID, index) - sqlParams = append(sqlParams, sql.Named( - fmt.Sprintf("field_%d_%d", field.ID, index), - fmt.Sprintf("%%%s%%", v), - )) + if field.FieldType == "date" { + filter = fmt.Sprintf(` + %s + TO_CHAR(field_%d.value_date, 'YYYY-MM-DD') = @field_%d_%d + `, filter, field.ID, field.ID, index) + sqlParams = append(sqlParams, sql.Named( + fmt.Sprintf("field_%d_%d", field.ID, index), + v, + )) + } else { + filter = fmt.Sprintf(` + %s + LOWER(field_%d.value_string) LIKE LOWER(@field_%d_%d) + `, filter, field.ID, field.ID, index) + sqlParams = append(sqlParams, sql.Named( + fmt.Sprintf("field_%d_%d", field.ID, index), + fmt.Sprintf("%%%s%%", v), + )) + } case float64: - filter = fmt.Sprintf(`--sql - %s - field_%d.value_int = @field_%d_%d - OR field_%d.list_item_id = @field_%d_%d - `, filter, field.ID, field.ID, index, field.ID, field.ID, index) - sqlParams = append(sqlParams, sql.Named( - fmt.Sprintf("field_%d_%d", field.ID, index), - v, - )) + if field.FieldType == "list" { + filter = fmt.Sprintf(` + %s + field_%d.list_item_id = @field_%d_%d + `, filter, field.ID, field.ID, index) + sqlParams = append(sqlParams, sql.Named( + fmt.Sprintf("field_%d_%d", field.ID, index), + v, + )) + } else { + filter = fmt.Sprintf(` + %s + field_%d.value_int = @field_%d_%d + `, filter, field.ID, field.ID, index) + sqlParams = append(sqlParams, sql.Named( + fmt.Sprintf("field_%d_%d", field.ID, index), + v, + )) + } } fieldFilter = fmt.Sprintf("%s %s", fieldFilter, filter) } if fieldFilter != "" { - sqlFilters = fmt.Sprintf(`--sql + sqlFilters = fmt.Sprintf(` %s AND (%s) `, sqlFilters, fieldFilter) @@ -308,7 +319,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } // Build and run count query - sqlQueryCount = fmt.Sprintf(`--sql + sqlQueryCount = fmt.Sprintf(` %s %s %s @@ -330,7 +341,7 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { var sqlPagination string if params.PageSize > 0 { - sqlPagination = `--sql + sqlPagination = ` LIMIT @pagination_limit OFFSET @pagination_offset ` @@ -339,12 +350,12 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { } // Build and run paginated result query - sqlQuery = fmt.Sprintf(`--sql + sqlQuery = fmt.Sprintf(` %s %s %s - GROUP BY people.id - ORDER BY CONCAT(people.last_name, people.first_name) COLLATE NOCASE ASC + GROUP BY people.id, Section__name + ORDER BY CONCAT(people.last_name, people.first_name) ASC %s `, sqlQuery, sqlFieldJoins, sqlFilters, sqlPagination) diff --git a/helpers/users.go b/helpers/users.go index 9ebcb6d..35625f0 100644 --- a/helpers/users.go +++ b/helpers/users.go @@ -16,7 +16,7 @@ func FirstAccountCheck() (bool, error) { } var user models.User - result := db.First(&user, "is_admin = 1") + result := db.First(&user, "is_admin = ?", true) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return false, nil diff --git a/models/fields.go b/models/fields.go index 0df335a..6c18471 100644 --- a/models/fields.go +++ b/models/fields.go @@ -11,8 +11,8 @@ type Field struct { Name string PersonType string FieldType string - ListID uint - List List + ListID *uint + List *List Position int } @@ -25,8 +25,8 @@ type FieldValue struct { ValueString sql.NullString ValueInt sql.NullInt64 ValueDate sql.NullTime - ListItemID uint - ListItem ListItem + ListItemID *uint + ListItem *ListItem } var PersonTypes = map[string]string{ diff --git a/models/sections.go b/models/sections.go index a1ea7e2..18537a4 100644 --- a/models/sections.go +++ b/models/sections.go @@ -6,7 +6,7 @@ type Section struct { gorm.Model Name string ShortName string - ParentSectionID uint + ParentSectionID *uint ParentSection *Section ContainsMembers bool ContainsContacts bool From e2de88b25b5bb7624f81981fbc2e83f96f4f9d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Tue, 13 May 2025 13:04:58 +0200 Subject: [PATCH 02/19] PostgreSQL: update config & connection pool --- .env.example | 2 +- helpers/config.go | 3 +-- helpers/database.go | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index da20ebe..b8e1c1f 100644 --- a/.env.example +++ b/.env.example @@ -3,5 +3,5 @@ DEV_MODE=false APP_LISTEN_ADDRESS=127.0.0.1 APP_LISTEN_PORT=3000 APP_BEHIND_PROXY=false -DATABASE_LOCATION=./people.db +DATABASE_DSN="host=localhost user=camarades password=camarades dbname=camarades port=5432 sslmode=disable TimeZone=Europe/Zurich" SESSIONS_LOCATION=./sessions.db diff --git a/helpers/config.go b/helpers/config.go index 835b7a1..e87443d 100644 --- a/helpers/config.go +++ b/helpers/config.go @@ -15,8 +15,7 @@ type Config struct { BehindProxy bool `env:"APP_BEHIND_PROXY"` } Database struct { - Location string `env:"DATABASE_LOCATION"` - DSN string `env:"DATABASE_DSN"` + DSN string `env:"DATABASE_DSN"` } Sessions struct { Location string `env:"SESSIONS_LOCATION"` diff --git a/helpers/database.go b/helpers/database.go index e4bc052..00ed2f8 100644 --- a/helpers/database.go +++ b/helpers/database.go @@ -42,7 +42,8 @@ func GetDatabase() (*gorm.DB, error) { return database, err } - db.SetMaxOpenConns(1) + db.SetMaxIdleConns(10) + db.SetMaxOpenConns(50) err = database.AutoMigrate( &models.User{}, From 183415912f0d85cb61864a797e4b3b7c46935280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Tue, 13 May 2025 15:01:12 +0200 Subject: [PATCH 03/19] Bootstrap theme + rework some views --- frontend/bootstrap.scss | 6 + frontend/index.css | 2 +- package.json | 1 + pnpm-lock.yaml | 54 +++++ views/people.html | 11 +- views/role_form.html | 450 ++++++++++++++++++---------------------- 6 files changed, 267 insertions(+), 257 deletions(-) create mode 100644 frontend/bootstrap.scss diff --git a/frontend/bootstrap.scss b/frontend/bootstrap.scss new file mode 100644 index 0000000..45d1cf3 --- /dev/null +++ b/frontend/bootstrap.scss @@ -0,0 +1,6 @@ +$red: #cb0000; + +@use "npm:bootstrap/scss/bootstrap.scss" with ( + $enable-rounded: false, + $danger: $red +); diff --git a/frontend/index.css b/frontend/index.css index d60456d..a4e9322 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -1,4 +1,4 @@ -@import "npm:bootstrap/dist/css/bootstrap.css"; +@import "bootstrap.scss"; @import "npm:bootstrap-icons/font/bootstrap-icons.css"; img#header-logo { diff --git a/package.json b/package.json index 1771197..849758f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "url": "https://git.readonly.ch/bouzoure/pop-camarades" }, "devDependencies": { + "@parcel/transformer-sass": "2.15.0", "parcel": "^2.15.0", "prettier": "^3.5.3", "prettier-plugin-jinja-template": "^2.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 233a827..15ee3fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,9 @@ importers: specifier: ^3.7.1 version: 3.7.1 devDependencies: + '@parcel/transformer-sass': + specifier: 2.15.0 + version: 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) parcel: specifier: ^2.15.0 version: 2.15.0(@swc/helpers@0.5.17) @@ -370,6 +373,10 @@ packages: resolution: {integrity: sha512-I108zq+ZwQrGXgkbdIXLW3VbUQhW0gjACiHVEXM380wWm/44bbrGLbD6VMupq5svP2Y5sKkopI9zzjuYUHplHw==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} + '@parcel/transformer-sass@2.15.0': + resolution: {integrity: sha512-upcwFGc7fd0QlHSReLc4OTZTcwDGlHlDT70cQOCbAPRRE6YDGIJYqhAeqTWShEDsZBets4GJyu6SN1lDsXjTIQ==} + engines: {node: '>= 16.0.0', parcel: ^2.15.0} + '@parcel/transformer-svg@2.15.0': resolution: {integrity: sha512-pbhbkxM4mWjH4kpg8F+0xmHbXNCTavJ4DzrCoYgLZszZKYMhOYQZZ/uHkx4wOZ+b3n4iPe4QDlByYkh0QACxbw==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} @@ -584,6 +591,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -643,6 +654,9 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + immutable@5.1.2: + resolution: {integrity: sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -794,17 +808,30 @@ packages: resolution: {integrity: sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==} engines: {node: '>=0.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + sass@1.88.0: + resolution: {integrity: sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==} + engines: {node: '>=14.0.0'} + hasBin: true + semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} hasBin: true + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1426,6 +1453,15 @@ snapshots: - '@parcel/core' - napi-wasm + '@parcel/transformer-sass@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': + dependencies: + '@parcel/plugin': 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) + '@parcel/source-map': 2.1.1 + sass: 1.88.0 + transitivePeerDependencies: + - '@parcel/core' + - napi-wasm + '@parcel/transformer-svg@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/diagnostic': 2.15.0 @@ -1626,6 +1662,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chrome-trace-event@1.0.4: {} clone@2.1.2: {} @@ -1664,6 +1704,8 @@ snapshots: has-flag@4.0.0: {} + immutable@5.1.2: {} + is-extglob@2.1.1: {} is-glob@4.0.3: @@ -1811,12 +1853,24 @@ snapshots: react-refresh@0.16.0: {} + readdirp@4.1.2: {} + regenerator-runtime@0.14.1: {} safe-buffer@5.2.1: {} + sass@1.88.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.2 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + semver@7.7.1: {} + source-map-js@1.2.1: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 diff --git a/views/people.html b/views/people.html index e287c7a..10506bb 100644 --- a/views/people.html +++ b/views/people.html @@ -49,10 +49,7 @@ {% endif %} -
+
@@ -83,11 +80,10 @@
{% if PermShow %} -
+
+
-
- Permissions membres +
+
+
Permissions membres
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
-
Permissions membres
+
Permissions contacts
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
+
+ + +
-
-
- - -
-
- -
- Permissions contacts -
- -
-
-
Permissions contacts
-
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
- -
-
- - -
-
- -
-
- - +
+ + +
From 56fd3c09523f519762c7543c0c02500e9c39fc02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Fri, 27 Jun 2025 11:43:06 +0200 Subject: [PATCH 04/19] Fix section preload in people search --- helpers/database/people.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/helpers/database/people.go b/helpers/database/people.go index 90310cf..e5ad054 100644 --- a/helpers/database/people.go +++ b/helpers/database/people.go @@ -45,11 +45,9 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { // SQL qeury for results sqlQuery := ` - SELECT people.*, - sections.name AS Section__name + SELECT people.* FROM people - INNER JOIN sections - ON people.section_id = sections.id` + ` // SQL query to count results sqlQueryCount := ` @@ -354,12 +352,12 @@ func PeopleSearch(params PeopleSearchParams) (PeopleSearchResults, error) { %s %s %s - GROUP BY people.id, Section__name + GROUP BY people.id ORDER BY CONCAT(people.last_name, people.first_name) ASC %s `, sqlQuery, sqlFieldJoins, sqlFilters, sqlPagination) - sqlResult := db.Raw(sqlQuery, sqlParams...).Scan(&results.Results) + sqlResult := db.Raw(sqlQuery, sqlParams...).Preload("Section").Find(&results.Results) if sqlResult.Error != nil { return results, sqlResult.Error } From d52372929881429d22ccb4ed5563d12687a8b63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Fri, 27 Jun 2025 11:43:37 +0200 Subject: [PATCH 05/19] Fix parcel --- package.json | 2 +- pnpm-lock.yaml | 230 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 224 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 849758f..6051002 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://git.readonly.ch/bouzoure/pop-camarades" }, "devDependencies": { - "@parcel/transformer-sass": "2.15.0", + "@parcel/transformer-sass": "2.15.4", "parcel": "^2.15.0", "prettier": "^3.5.3", "prettier-plugin-jinja-template": "^2.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15ee3fc..a9676de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,8 +19,8 @@ importers: version: 3.7.1 devDependencies: '@parcel/transformer-sass': - specifier: 2.15.0 - version: 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) + specifier: 2.15.4 + version: 2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17)) parcel: specifier: ^2.15.0 version: 2.15.0(@swc/helpers@0.5.17) @@ -117,6 +117,10 @@ packages: resolution: {integrity: sha512-zpZCf5W+npiSkdCUC7izjdUUoWM2M++XWRwbqgwWBUCKrXC4vVJoOYMzLbyfaF/zkVo5iQenSGlsD0olBd7V1w==} engines: {node: '>= 16.0.0'} + '@parcel/codeframe@2.15.4': + resolution: {integrity: sha512-ErAPEQaJIpB+ocNZ3rl8AEK6piA7JBInwZLNU0eHMthm01Ssb10JkpAadyn1w9IVfCey+kqQcEeWv47Yh6mL1Q==} + engines: {node: '>= 16.0.0'} + '@parcel/compressor-raw@2.15.0': resolution: {integrity: sha512-AoShbbqNCkzTkNMygmcCazV6iFj9nLVwBPZZCAyJ57ooRlxPszMtLO1RIw/cVU9PJg/NlYGg0uEGVt/N56SzWA==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} @@ -134,6 +138,10 @@ packages: resolution: {integrity: sha512-Bzg7AJu10muQ793p2MPlZnYvbqZXOJw/YBIOCFjbwRKiYUb06+sZyfntU7e7YecV6im0IGqkIfMD0F4MT+v7Rg==} engines: {node: '>= 16.0.0'} + '@parcel/diagnostic@2.15.4': + resolution: {integrity: sha512-8MAqefwzBKceNN3364OLm+p4HRD7AfimfFW3MntLxPB6bnelc9UBg5c9zEm34zYEctbmky8gqYgAUSDjqYC5Hw==} + engines: {node: '>= 16.0.0'} + '@parcel/error-overlay@2.15.0': resolution: {integrity: sha512-Tsq0q4Lv3aDn/nXWuzH1x/pgzYQYCt17qOejAANfNYIBIrLs7BRsGyT63vP39i7IXI+MvulMl5nDXQDAqDwujw==} engines: {node: '>= 16.0.0'} @@ -142,10 +150,18 @@ packages: resolution: {integrity: sha512-iCoFGsZTAlh3ewp6KYseUC16OHbZi2n6vAl4Rr8Uw7yxvwCC3iHT9acLwhO7bP/YKkdGri3d78+UwPl8LmbIwA==} engines: {node: '>= 16.0.0'} + '@parcel/events@2.15.4': + resolution: {integrity: sha512-SBq4zstaFr7XQaXNaQmUuVh1swCUHrhtPCOSofvkJoQGhjsuhQlh4t0NmUikyKNdj7C1j40xCS1kGHuUO29b0g==} + engines: {node: '>= 16.0.0'} + '@parcel/feature-flags@2.15.0': resolution: {integrity: sha512-gtAC30G2QlIwTlLM2tI7AB0JBKEiX4nNOL/qh+or9wD9fuk53O4QHJwPtiy49YGSPYrYnIR2EXWOOV+3Br9CCw==} engines: {node: '>= 16.0.0'} + '@parcel/feature-flags@2.15.4': + resolution: {integrity: sha512-DJqZVtbfjWJseM0gk7yyDkAuOhP7/FVwZ/YVqjozIqXBhmQm07xctiqNQyZX2vBbQsxmVbjpqyq+DOj45WPEzQ==} + engines: {node: '>= 16.0.0'} + '@parcel/fs@2.15.0': resolution: {integrity: sha512-ecWIbIhwdnvJc/PY+l3TFOcRtr8W3X6M1yfhNQLmYs/3kETIxDK8s+vTva/qPBFEiW0amMBhbkhKZEXFrxL1GQ==} engines: {node: '>= 16.0.0'} @@ -160,10 +176,18 @@ packages: resolution: {integrity: sha512-WCYtSweM7Iol/lE7HhU5cLsSNuGQ1T4xTIYvG16tGHCsjybWF1H9yqkL90WU2JHjhSsvNGjvwrVxWjfO304fqQ==} engines: {node: '>= 16.0.0'} + '@parcel/logger@2.15.4': + resolution: {integrity: sha512-rQ7F5+FMQ7t+w5NGFRT8CWHhym0aunduufCjlafvRzUSKEN/5/nwTfCe9I5QsthGlXJWs+ZTy4zQ+wLtZQRBKQ==} + engines: {node: '>= 16.0.0'} + '@parcel/markdown-ansi@2.15.0': resolution: {integrity: sha512-TJOSg/y2P1Rp199+osSFd4jtt8M4iyBQwgC4gdAARcraIwLa/wYRt6RVnOIsN3nz1r1CPLvHHPfuIwwFjRNw9A==} engines: {node: '>= 16.0.0'} + '@parcel/markdown-ansi@2.15.4': + resolution: {integrity: sha512-u5Lwcr4ZVBSLFbKYht+mJqJ3ZMXvJdmDMU5eDtrIEKPpu9LrIDdPpDEXBoyO6pDsoV/2AqyXUUMzBRyCatkkoQ==} + engines: {node: '>= 16.0.0'} + '@parcel/namer-default@2.15.0': resolution: {integrity: sha512-JkfrvBcMmZ4DvI9VGUWSir3Nwlh224MsKUMqfXs9zc9Xq484v+p+bSaEoAwZIyfUwXXDz1sXk9NffNuLSa5ivA==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} @@ -228,10 +252,18 @@ packages: resolution: {integrity: sha512-OT+W5t70+VZbcg2P30QahF4YjRu+9ywG5NSMj0SYvS6PCZa+IAYB9589KuavcAp+Tq2FV7MgYtrBKPh9b0VAtg==} engines: {node: '>= 16.0.0'} + '@parcel/plugin@2.15.4': + resolution: {integrity: sha512-XVehjmzk8ZDOFf/BXo26L76ZqCGNKIQcN2ngxAnq0KRY/WFanL8yLaL0qQq+c9whlu09hkGz1CuhFBLAIjJMYQ==} + engines: {node: '>= 16.0.0'} + '@parcel/profiler@2.15.0': resolution: {integrity: sha512-/Bw10pCISHbSzpdmuxg1GjSh+GuvqmUYA9bAmb69dkzWLIEk3uU05ba4xoju2mwoSeNb50LRcYPcirLB0Z61wA==} engines: {node: '>= 16.0.0'} + '@parcel/profiler@2.15.4': + resolution: {integrity: sha512-ezVZlttUmQ1MQD5e8yVb07vSGYEFOB59Y/jaxL9mGSLZkVhMIIHe/7SuA+4qVAH8dlg6bslXRqlsunLMPEgPsg==} + engines: {node: '>= 16.0.0'} + '@parcel/reporter-cli@2.15.0': resolution: {integrity: sha512-p8nNpX53A7OLFpqZravxzanExPyk0/zNFTYJO/rdGToOqe/m3V/uK8XWb45fVf9OKNZmB13sr/DRdeYHMbXyYg==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} @@ -270,48 +302,96 @@ packages: cpu: [arm64] os: [darwin] + '@parcel/rust-darwin-arm64@2.15.4': + resolution: {integrity: sha512-cEpNDeEtvM5Nhj0QLN95QbcZ9yY6Z5W3+2OeHvnojEAP8Rp1XGzqVTTZdlyKyN1KTiyfzIOiQJCiEcr+kMc5Nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@parcel/rust-darwin-x64@2.15.0': resolution: {integrity: sha512-l6tD0nNvmtpwuNSCP9Q5jPpPeY45NwmRNiuDoYMzfNT3iFKs/i48/3JM1vZvYO3HiW6V0xGfWa1b+HQsSKQRYg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@parcel/rust-darwin-x64@2.15.4': + resolution: {integrity: sha512-jL9i13sXKeBXXz8Z3BNYoScPOi+ljBA0ubAE3PN5DCoAA6wS4/FsAiRSIUw+3uxqASBD7+JvaT5sDUga1Xft5g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@parcel/rust-linux-arm-gnueabihf@2.15.0': resolution: {integrity: sha512-MZNL/UV20kBaTYMos/IcJPZvzYzlYWjuBZh2EI3OHYBGMR9QdpeJuwgTAy2WUYbevXm7nemdGHGccGcdiNf/Xg==} engines: {node: '>= 10'} cpu: [arm] os: [linux] + '@parcel/rust-linux-arm-gnueabihf@2.15.4': + resolution: {integrity: sha512-c8HpVdDugCutlMILoOlkTioih9HGJpQrzS2G3cg/O1a5ZTacooGf3eGJGoh6dUBEv9WEaEb6zsTRwFv2BgtZcA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + '@parcel/rust-linux-arm64-gnu@2.15.0': resolution: {integrity: sha512-u/rndCWjmQgFJi/2NFVWV1snlF/souO8UYZR+ZG6goo/sik5WgrACtCucgOrskogE50WU1+JGmP0TBNqOX27Uw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@parcel/rust-linux-arm64-gnu@2.15.4': + resolution: {integrity: sha512-Wcfs/JY4FnuLxQaU+VX2rI4j376Qo2LkZmq4zp9frnsajaAqmloVQfnbUkdnQPEL4I38eHXerzBX3LoXSxnZKA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@parcel/rust-linux-arm64-musl@2.15.0': resolution: {integrity: sha512-uTSOZJkZKh/x/IfaGkbmqqdUaK1S61Kw3ZW8yj+EtteHvfZgk1SQMgI51Gg5hwaZ5wuZx0nOJGLuOxPMGx7z6w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@parcel/rust-linux-arm64-musl@2.15.4': + resolution: {integrity: sha512-xf9HxosEn3dU5M0zDSXqBaG8rEjLThRdTYqpkxHW/qQGzy0Se+/ntg8PeDHsSG5E9OK8xrcKH46Lhaw0QBF/Zw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@parcel/rust-linux-x64-gnu@2.15.0': resolution: {integrity: sha512-8SIwgM+bpiodJemNaEuUgZQk4hV/3pgJnPBRjGse1F7SHeTp9UoABLSF3V5Sc79Hi8fzECoRimk44krzSCaynw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@parcel/rust-linux-x64-gnu@2.15.4': + resolution: {integrity: sha512-RigXVCFj6h0AXmkuxU61rfgYuW+PXBR6qSkR2I20yKnAXoMfxLaZy9YJ3sAPMEjT9zXgzGAX+3syItMF+bRjaw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@parcel/rust-linux-x64-musl@2.15.0': resolution: {integrity: sha512-pMFQ7bdaBeFY+qfHE8Oor8yZLkXDl5PmnKICuFiGETnbClV9xfWmZdTnqjEw2XU9gGQ49DkWJcGW975d3IlksA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@parcel/rust-linux-x64-musl@2.15.4': + resolution: {integrity: sha512-tHlRgonSr5ca8OvhbGzZUggCgCOirRz5dHhPSCm4ajMxeDMamwprq6lKy0sCNTXht4TXIEyugBcfEuRKEeVIBw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@parcel/rust-win32-x64-msvc@2.15.0': resolution: {integrity: sha512-UXjPkWbavwGIHi/R1uPd4CZDhAUUfOGpvIMRdq0ImihoRUnUxyTCIsqRhwh8flOO2RCuU6rteeGOeT9undSX7Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@parcel/rust-win32-x64-msvc@2.15.4': + resolution: {integrity: sha512-YsX6vMl/bfyxqZSN7yiaZQKLoJKELSZYcvg8gIv4CF1xkaTdmfr6gvq2iCyoV+bwrodNohN4Xfl8r7Wniu1/UA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@parcel/rust@2.15.0': resolution: {integrity: sha512-ERRO4q14g6nD5mr1S/kEDSsmis/mll9JLxzyub0vTgobywrUq/azJ6Un7XwhCXCaU7lO7ihD+HJvjmNLVULCXg==} engines: {node: '>= 16.0.0'} @@ -321,6 +401,15 @@ packages: napi-wasm: optional: true + '@parcel/rust@2.15.4': + resolution: {integrity: sha512-OxOux8z8YEYg23+15uMmYaloFp3x1RwcliBay6HqxUW7RTmtI1/z+xd8AtienCckACD60gvDGy04LjgbEGdJVg==} + engines: {node: '>= 16.0.0'} + peerDependencies: + napi-wasm: ^1.1.2 + peerDependenciesMeta: + napi-wasm: + optional: true + '@parcel/source-map@2.1.1': resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} engines: {node: ^12.18.3 || >=14} @@ -373,9 +462,9 @@ packages: resolution: {integrity: sha512-I108zq+ZwQrGXgkbdIXLW3VbUQhW0gjACiHVEXM380wWm/44bbrGLbD6VMupq5svP2Y5sKkopI9zzjuYUHplHw==} engines: {node: '>= 16.0.0', parcel: ^2.15.0} - '@parcel/transformer-sass@2.15.0': - resolution: {integrity: sha512-upcwFGc7fd0QlHSReLc4OTZTcwDGlHlDT70cQOCbAPRRE6YDGIJYqhAeqTWShEDsZBets4GJyu6SN1lDsXjTIQ==} - engines: {node: '>= 16.0.0', parcel: ^2.15.0} + '@parcel/transformer-sass@2.15.4': + resolution: {integrity: sha512-o7rPDdId5tpx/riTzu0O3zpyt24ORrVdAxW6k2YN2xGrvStfnQHxKjrSiEFDUMMzuikcW7Ed+uqPk046DK3m8w==} + engines: {node: '>= 16.0.0', parcel: ^2.15.4} '@parcel/transformer-svg@2.15.0': resolution: {integrity: sha512-pbhbkxM4mWjH4kpg8F+0xmHbXNCTavJ4DzrCoYgLZszZKYMhOYQZZ/uHkx4wOZ+b3n4iPe4QDlByYkh0QACxbw==} @@ -384,13 +473,23 @@ packages: '@parcel/types-internal@2.15.0': resolution: {integrity: sha512-N0p622dZx84OPoxSoz5YfnVJMXAoQfcHI+qp535J/Uv6UAbcsimKl9NPKefrLOHgSKlTTJPiDbWSMOVrPvyr6w==} + '@parcel/types-internal@2.15.4': + resolution: {integrity: sha512-kl5QEZ8PTWRvMkwmk7IG3VpP/5/MSGwt9Nrj9ctXLdZkDdXZpK7IbXAthLQ4zrByMaqZULL2IyDuBqBgfuAqlQ==} + '@parcel/types@2.15.0': resolution: {integrity: sha512-BtAeK/mTQMjbgyo8r1jM1d+dcnEowErHH/Eb/95Agxi7YHpfnNP2oR8cC2yZbevU9FCXnSJ2f6vZc4NGT+nqlA==} + '@parcel/types@2.15.4': + resolution: {integrity: sha512-fS3UMMinLtzn/NTSx/qx38saBgRniylldh0XZEUcGeME4D2Llu/QlLv+YZ/LJqrFci3fPRM+YAn2K+JT/u+/0w==} + '@parcel/utils@2.15.0': resolution: {integrity: sha512-Xir0/9UvUvMF8iRnARDdzzlEokDAcrsxj6aQUbYP3ZXV/l6/6eMRuSXZ32x6lUzOTHxukKMJA42imWUg6x38qg==} engines: {node: '>= 16.0.0'} + '@parcel/utils@2.15.4': + resolution: {integrity: sha512-29m09sfPx0GHnmy1kkZ5XezprepdFGKKKUEJkyiYA4ERf55jjdnU2/GP4sWlZXxjh2Y+JFoCAFlCamEClq/8eA==} + engines: {node: '>= 16.0.0'} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -479,6 +578,12 @@ packages: peerDependencies: '@parcel/core': ^2.15.0 + '@parcel/workers@2.15.4': + resolution: {integrity: sha512-wZ/5/mfjs5aeqhXY0c6fwuaBFeNpOXoOq2CKPSMDXt+GX2u/9/1bpVxN9XeGTAJO+ZD++CLq0hyzTnIHy58nyw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + '@parcel/core': ^2.15.4 + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -940,6 +1045,10 @@ snapshots: dependencies: chalk: 4.1.2 + '@parcel/codeframe@2.15.4': + dependencies: + chalk: 4.1.2 + '@parcel/compressor-raw@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/plugin': 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) @@ -1022,12 +1131,21 @@ snapshots: '@mischnic/json-sourcemap': 0.1.1 nullthrows: 1.1.1 + '@parcel/diagnostic@2.15.4': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + nullthrows: 1.1.1 + '@parcel/error-overlay@2.15.0': {} '@parcel/events@2.15.0': {} + '@parcel/events@2.15.4': {} + '@parcel/feature-flags@2.15.0': {} + '@parcel/feature-flags@2.15.4': {} + '@parcel/fs@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/core': 2.15.0(@swc/helpers@0.5.17) @@ -1050,10 +1168,19 @@ snapshots: '@parcel/diagnostic': 2.15.0 '@parcel/events': 2.15.0 + '@parcel/logger@2.15.4': + dependencies: + '@parcel/diagnostic': 2.15.4 + '@parcel/events': 2.15.4 + '@parcel/markdown-ansi@2.15.0': dependencies: chalk: 4.1.2 + '@parcel/markdown-ansi@2.15.4': + dependencies: + chalk: 4.1.2 + '@parcel/namer-default@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/diagnostic': 2.15.0 @@ -1214,6 +1341,13 @@ snapshots: - '@parcel/core' - napi-wasm + '@parcel/plugin@2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17))': + dependencies: + '@parcel/types': 2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17)) + transitivePeerDependencies: + - '@parcel/core' + - napi-wasm + '@parcel/profiler@2.15.0': dependencies: '@parcel/diagnostic': 2.15.0 @@ -1221,6 +1355,13 @@ snapshots: '@parcel/types-internal': 2.15.0 chrome-trace-event: 1.0.4 + '@parcel/profiler@2.15.4': + dependencies: + '@parcel/diagnostic': 2.15.4 + '@parcel/events': 2.15.4 + '@parcel/types-internal': 2.15.4 + chrome-trace-event: 1.0.4 + '@parcel/reporter-cli@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/plugin': 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) @@ -1300,27 +1441,51 @@ snapshots: '@parcel/rust-darwin-arm64@2.15.0': optional: true + '@parcel/rust-darwin-arm64@2.15.4': + optional: true + '@parcel/rust-darwin-x64@2.15.0': optional: true + '@parcel/rust-darwin-x64@2.15.4': + optional: true + '@parcel/rust-linux-arm-gnueabihf@2.15.0': optional: true + '@parcel/rust-linux-arm-gnueabihf@2.15.4': + optional: true + '@parcel/rust-linux-arm64-gnu@2.15.0': optional: true + '@parcel/rust-linux-arm64-gnu@2.15.4': + optional: true + '@parcel/rust-linux-arm64-musl@2.15.0': optional: true + '@parcel/rust-linux-arm64-musl@2.15.4': + optional: true + '@parcel/rust-linux-x64-gnu@2.15.0': optional: true + '@parcel/rust-linux-x64-gnu@2.15.4': + optional: true + '@parcel/rust-linux-x64-musl@2.15.0': optional: true + '@parcel/rust-linux-x64-musl@2.15.4': + optional: true + '@parcel/rust-win32-x64-msvc@2.15.0': optional: true + '@parcel/rust-win32-x64-msvc@2.15.4': + optional: true + '@parcel/rust@2.15.0': optionalDependencies: '@parcel/rust-darwin-arm64': 2.15.0 @@ -1332,6 +1497,17 @@ snapshots: '@parcel/rust-linux-x64-musl': 2.15.0 '@parcel/rust-win32-x64-msvc': 2.15.0 + '@parcel/rust@2.15.4': + optionalDependencies: + '@parcel/rust-darwin-arm64': 2.15.4 + '@parcel/rust-darwin-x64': 2.15.4 + '@parcel/rust-linux-arm-gnueabihf': 2.15.4 + '@parcel/rust-linux-arm64-gnu': 2.15.4 + '@parcel/rust-linux-arm64-musl': 2.15.4 + '@parcel/rust-linux-x64-gnu': 2.15.4 + '@parcel/rust-linux-x64-musl': 2.15.4 + '@parcel/rust-win32-x64-msvc': 2.15.4 + '@parcel/source-map@2.1.1': dependencies: detect-libc: 1.0.3 @@ -1453,9 +1629,9 @@ snapshots: - '@parcel/core' - napi-wasm - '@parcel/transformer-sass@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': + '@parcel/transformer-sass@2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: - '@parcel/plugin': 2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17)) + '@parcel/plugin': 2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17)) '@parcel/source-map': 2.1.1 sass: 1.88.0 transitivePeerDependencies: @@ -1478,6 +1654,13 @@ snapshots: '@parcel/source-map': 2.1.1 utility-types: 3.11.0 + '@parcel/types-internal@2.15.4': + dependencies: + '@parcel/diagnostic': 2.15.4 + '@parcel/feature-flags': 2.15.4 + '@parcel/source-map': 2.1.1 + utility-types: 3.11.0 + '@parcel/types@2.15.0(@parcel/core@2.15.0(@swc/helpers@0.5.17))': dependencies: '@parcel/types-internal': 2.15.0 @@ -1486,6 +1669,14 @@ snapshots: - '@parcel/core' - napi-wasm + '@parcel/types@2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17))': + dependencies: + '@parcel/types-internal': 2.15.4 + '@parcel/workers': 2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17)) + transitivePeerDependencies: + - '@parcel/core' + - napi-wasm + '@parcel/utils@2.15.0': dependencies: '@parcel/codeframe': 2.15.0 @@ -1499,6 +1690,19 @@ snapshots: transitivePeerDependencies: - napi-wasm + '@parcel/utils@2.15.4': + dependencies: + '@parcel/codeframe': 2.15.4 + '@parcel/diagnostic': 2.15.4 + '@parcel/logger': 2.15.4 + '@parcel/markdown-ansi': 2.15.4 + '@parcel/rust': 2.15.4 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + nullthrows: 1.1.1 + transitivePeerDependencies: + - napi-wasm + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -1571,6 +1775,18 @@ snapshots: transitivePeerDependencies: - napi-wasm + '@parcel/workers@2.15.4(@parcel/core@2.15.0(@swc/helpers@0.5.17))': + dependencies: + '@parcel/core': 2.15.0(@swc/helpers@0.5.17) + '@parcel/diagnostic': 2.15.4 + '@parcel/logger': 2.15.4 + '@parcel/profiler': 2.15.4 + '@parcel/types-internal': 2.15.4 + '@parcel/utils': 2.15.4 + nullthrows: 1.1.1 + transitivePeerDependencies: + - napi-wasm + '@popperjs/core@2.11.8': {} '@swc/core-darwin-arm64@1.11.24': From 898b9bbb632cc4315ed4e342ebb9678039781bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Fri, 27 Jun 2025 11:44:16 +0200 Subject: [PATCH 06/19] People search: use responsive bs cols instead of a table --- frontend/index.css | 4 ++ views/people.html | 116 +++++++++++++++++++++++++++++---------------- 2 files changed, 79 insertions(+), 41 deletions(-) diff --git a/frontend/index.css b/frontend/index.css index a4e9322..bdb46b3 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -53,3 +53,7 @@ a { #licenses p { margin: 0; } + +.fs-7 { + font-size: .75rem !important; +} \ No newline at end of file diff --git a/views/people.html b/views/people.html index 10506bb..a6c6d02 100644 --- a/views/people.html +++ b/views/people.html @@ -1,7 +1,7 @@ {% extends "layouts/main.html" %} {% block main %} -
+
-
- - - - - - - - - - {% for Person in People %} - - - - - - {% endfor %} - -
NomAdresseSection
- {% if Person.IsMember %} - - {{ Person.LastName }} {{ Person.FirstName }} - - {% else %} - - {{ Person.LastName }} {{ Person.FirstName }} - - {% endif %} - - {{ Person.Address1 }} - {% if Person.Address1 and (Person.PostalCode or Person.City) %} - – - {% endif %} - {{ Person.PostalCode }} - {{ Person.City }} - - {% if Person.SectionID %} - {{ Person.Section.Name }} - {% endif %} -
+
+ + + {% for Person in People %} +
+
+
+
+ Nom +
+ +
+
+
+ Adresse +
+
+ {{ Person.Address1 }} +
+
+
+
+ Lieu +
+
+ {{ Person.PostalCode }} {{ Person.City }} +
+
+
+
+ Section +
+
+ {{ Person.Section.Name }} +
+
+
+
+ Création +
+
+ {{ Person.CreatedAt|date:"02.01.2006 - 15:04" }} +
+
+
+
+ Mise à jour +
+
+ {{ Person.UpdatedAt|date:"02.01.2006 - 15:04" }} +
+
+
+
+ {% endfor %}