From 9a41a5f8aa8f9563bad85a11169b1c8a17f7f627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Tue, 25 Mar 2025 21:55:54 +0100 Subject: [PATCH] More work on the new people search --- controllers/fields.go | 22 ++++ controllers/members.go | 49 ++++++--- helpers/database/people.go | 75 ++++++++++++++ main.go | 3 + static/search.js | 105 ++++++++++++++++--- views/people.html | 204 ++++++++++++++++++++----------------- 6 files changed, 337 insertions(+), 121 deletions(-) create mode 100644 helpers/database/people.go diff --git a/controllers/fields.go b/controllers/fields.go index 9507880..22f3d43 100644 --- a/controllers/fields.go +++ b/controllers/fields.go @@ -297,3 +297,25 @@ func FieldMoveDown(c *fiber.Ctx) error { return c.Redirect("/admin/fields") } + +func FieldJSON(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + var field models.Field + result := db.Preload("List").Preload("List.ListItems").Find(&field, "id = ?", id) + + if result.Error != nil { + return result.Error + } + + if result.RowsAffected < 1 { + return fiber.NewError(fiber.StatusNotFound, "Not found") + } + + return c.JSON(field) +} diff --git a/controllers/members.go b/controllers/members.go index 0548c70..6fa3aaa 100644 --- a/controllers/members.go +++ b/controllers/members.go @@ -1,6 +1,7 @@ package controllers import ( + "encoding/json" "errors" "fmt" "strconv" @@ -8,6 +9,7 @@ import ( "time" "git.readonly.ch/bouzoure/pop-camarades/helpers" + "git.readonly.ch/bouzoure/pop-camarades/helpers/database" "git.readonly.ch/bouzoure/pop-camarades/models" "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" @@ -59,11 +61,7 @@ func Members(c *fiber.Ctx) error { return err } - filterSection := c.Query("se") - filterArchive := c.Query("a") - filterSearch := c.Query("s") - - sqlFilterSections := allowedSections + /*sqlFilterSections := allowedSections sqlFilterAppend := "IS NULL" if filterArchive == "1" { sqlFilterAppend = "IS NOT NULL" @@ -116,9 +114,6 @@ func Members(c *fiber.Ctx) error { ).Count(&count) } - page, _ := strconv.Atoi(c.Query("p")) - pagination := helpers.Paginate(50, int(count), page) - var people []models.Person if len(filterSearch) > 0 { result := db.Unscoped().Offset( @@ -162,28 +157,50 @@ func Members(c *fiber.Ctx) error { if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) { return err } - } + }*/ - var sections []models.Section + /*var sections []models.Section db.Order( "name collate nocase asc", ).Find( §ions, "contains_members = ? AND id IN ?", true, sqlFilterSections, - ) + )*/ + + searchJSON := c.Query("s") + var params database.PeopleSearchParams + + if len(searchJSON) > 0 { + err = json.Unmarshal([]byte(searchJSON), ¶ms) + if err != nil { + return err + } + } + + page, _ := strconv.Atoi(c.Query("p")) + + results, err := database.PeopleSearch(params, "members", 50, page) + if err != nil { + return err + } + + var sections []models.Section + db.Order("name collate nocase asc").Find(§ions, "contains_members = ?", true) + + var fields []models.Field + db.Order("position asc").Find(&fields, "person_type = ?", "member") return c.Render("people", fiber.Map{ "PageTitle": "Membres", "MembersPage": true, - "People": people, - "Pagination": pagination, + "People": results.Results, + "Pagination": results.Pagination, "PermShow": permShow, "PermShowArchived": permShowArchived, + "SearchJSON": searchJSON, "Sections": sections, - "FilterArchive": filterArchive, - "FilterSection": filterSectionUint, - "FilterSearch": filterSearch, + "Fields": fields, }) } diff --git a/helpers/database/people.go b/helpers/database/people.go new file mode 100644 index 0000000..0080452 --- /dev/null +++ b/helpers/database/people.go @@ -0,0 +1,75 @@ +package database + +import ( + "database/sql" + "fmt" + + "git.readonly.ch/bouzoure/pop-camarades/helpers" + "git.readonly.ch/bouzoure/pop-camarades/models" +) + +type PeopleSearchParams struct { + Advanced bool `json:"advanced"` + Name string `json:"name"` + Section uint `json:"section"` + Active bool `json:"active"` + Archived bool `json:"archived"` + Email string `json:"email"` + Phone string `json:"phone"` + Address string `json:"address"` + PostalCode string `json:"postal_code"` + City string `json:"city"` + Fields map[uint]any `json:"fields"` +} + +type PeopleSearchResults struct { + Results []models.Person + Count int64 + Pagination helpers.Pagination +} + +func PeopleSearch(params PeopleSearchParams, personType string, pageSize int, page int) (PeopleSearchResults, error) { + var results PeopleSearchResults + + db, err := helpers.GetDatabase() + if err != nil { + return results, nil + } + + var sqlQuery string + var sqlParams []any + sqlQuery = `--sql + SELECT people.ID, + people.is_member, + people.is_contact, + people.first_name, + people.last_name, + people.address1, + people.postal_code, + people.city, + people.section_id, + sections.name AS Section__name + FROM people + INNER JOIN sections + ON people.section_id = sections.id + WHERE is_member = @is_member + AND is_contact = @is_contact` + + if personType == "members" { + sqlParams = append(sqlParams, sql.Named("is_member", true)) + sqlParams = append(sqlParams, sql.Named("is_contact", false)) + } else if personType == "contacts" { + sqlParams = append(sqlParams, sql.Named("is_member", false)) + sqlParams = append(sqlParams, sql.Named("is_contact", true)) + } else { + return results, fmt.Errorf("unkown person type") + } + + sqlResult := db.Raw(sqlQuery, sqlParams...).Scan(&results.Results) + + if sqlResult.Error != nil { + return results, sqlResult.Error + } + + return results, nil +} diff --git a/main.go b/main.go index 9d79f3e..fe2b952 100644 --- a/main.go +++ b/main.go @@ -167,6 +167,9 @@ func main() { app.Post("/contacts/:id/restore", controllers.ContactRestore) app.Post("/contacts/:id/purge", controllers.ContactPurge) + // Fields + app.Get("/fields/:id", controllers.FieldJSON) + // Account manage app.Get("/account/manage", controllers.AccountManage) app.Post("/account/manage", controllers.AccountManage) diff --git a/static/search.js b/static/search.js index a6ec53b..5dbe1ff 100644 --- a/static/search.js +++ b/static/search.js @@ -5,13 +5,13 @@ $(document).ready(function() { $("#advanced").on("click", function() { if ($(this).data("state") === "true") { - $(this).find("i").removeClass("bi-chevron-double-up"); - $(this).find("i").addClass("bi-chevron-double-down"); + $(this).find("i").removeClass("bi-arrow-up"); + $(this).find("i").addClass("bi-arrow-down"); $("#advanced-section").addClass("d-none"); $(this).data("state", "false"); } else { - $(this).find("i").removeClass("bi-chevron-double-down"); - $(this).find("i").addClass("bi-chevron-double-up"); + $(this).find("i").removeClass("bi-arrow-down"); + $(this).find("i").addClass("bi-arrow-up"); $("#advanced-section").removeClass("d-none"); $(this).data("state", "true"); } @@ -22,10 +22,7 @@ $(document).ready(function() { var searchData = JSON.parse(json); for (const [key, value] of Object.entries(searchData)) { if (key === "advanced") { - if (value) { - $("#advanced").trigger("click"); - } - + if (value) $("#advanced").trigger("click"); continue; } @@ -34,16 +31,90 @@ $(document).ready(function() { continue } - if (typeof value === "boolean" && value) { - console.log(key, value, typeof(value)); - $("[data-search-field=" + key + "]").prop("checked", true); + if (typeof value === "boolean") { + $("[data-search-field=" + key + "]").prop("checked", value); } else { $("[data-search-field=" + key + "]").val(value); } } } + + createFieldSelection(); + $("body").on("change", ".search-fields-count .field-select", function() { + var lastfield = $(this).attr("data-last-field"); + var field = $(this).val(); + if (field == "") { + $(this).parents(".row").remove(); + return; + } + + if (lastfield.length === 0) { + createFieldSelection(); + } + $(this).attr("data-last-field", field); + + $(this).parents(".row").find(".end-col").find("input").remove(); + $(this).parents(".row").find(".end-col").find("select").remove(); + + $(elem).parents(".row").find(".end-col").append($("", { + class: "form-control", + type: "text", + disabled: true, + })); + + var elem = $(this); + $.getJSON("/fields/" + field, function(data) { + $(this).parents(".row").find(".end-col").find("input").remove(); + + if (data.FieldType === "list") { + var select = $("", { + class: "form-control", + type: "number", + "data-optional-field": field + })); + } else if (data.FieldType === "date") { + $(elem).parents(".row").find(".end-col").append($("", { + class: "form-control", + type: "date", + "data-optional-field": field + })); + } else { + $(elem).parents(".row").find(".end-col").append($("", { + class: "form-control", + type: "text", + "data-optional-field": field + })); + } + }); + }); }); +function createFieldSelection() { + const uuid = crypto.randomUUID(); + + var elem = $("#search-fields-model").clone().appendTo("#search-fields"); + $(elem).attr("id", "search-field-" + uuid); + //$(elem).find(".start-col").find("label").attr("for", "search-field-field-" + uuid); + //$(elem).find(".start-col").find("select").attr("id", "search-field-field-" + uuid); + //$(elem).find(".end-col").find("label").attr("for", "search-field-value-" + uuid); + //$(elem).find(".end-col").find("input").attr("id", "search-field-value-" + uuid); + $(elem).removeClass("d-none"); + $(elem).addClass("search-fields-count"); +} + function search() { var advancedSearch = ($("#advanced").data("state") === "true"); var searchData = { @@ -66,7 +137,17 @@ function search() { searchData[index] = value; }); + fields = {} + $("[data-optional-field]:not(:disabled)").each(function() { + var index = $(this).attr("data-optional-field"); + var value = $(this).val(); + + fields[index] = value; + }); + searchData["fields"] = fields; + var json = JSON.stringify(searchData); $("#search-json").val(json); - $("#search-form").submit(); + console.log(searchData); + //$("#search-form").submit(); } \ No newline at end of file diff --git a/views/people.html b/views/people.html index bea48f5..f6323af 100644 --- a/views/people.html +++ b/views/people.html @@ -25,7 +25,7 @@ Ajouter - + Exporter @@ -36,7 +36,7 @@ Ajouter - + Exporter @@ -44,106 +44,124 @@ {% endif %} -
-
- - - - -
-
- - - - -
-
- - -
- - -
- -
-
- -
+
- - + +
-
+
- - + +
-
+
+ +
+ + {% if MembersPage %} + + {% else %} + + {% endif %} +
- - - -
-
- - - - -
-
- - - +
+ + {% if MembersPage %} + + {% else %} + + {% endif %} +
+ +
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
-
-
- -
-
- -
-
- -
- - -
-
@@ -203,7 +221,7 @@
  • @@ -211,7 +229,7 @@
  • @@ -227,7 +245,7 @@ class="page-link" {% endif %} - href="?se={{ FilterSection }}&s={{ FilterSearch|urlencode }}&a={{ FilterArchive }}&p={{ i }}" + href="?&p={{ i }}&s={{ SearchJSON|urlencode }}" > {{ i }} @@ -249,7 +267,7 @@
  • @@ -257,7 +275,7 @@