From d40bc511016cfd94f5948819cf892ce5d5883f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Bouzour=C3=A8ne?= Date: Thu, 16 Jan 2025 16:48:17 +0100 Subject: [PATCH] Manage contacts --- controllers/contacts.go | 606 ++++++++++++++++++++++++++++++++++++++++ main.go | 9 + views/person.html | 69 ++++- 3 files changed, 670 insertions(+), 14 deletions(-) diff --git a/controllers/contacts.go b/controllers/contacts.go index f920b8f..1cf623e 100644 --- a/controllers/contacts.go +++ b/controllers/contacts.go @@ -2,9 +2,13 @@ package controllers import ( "errors" + "fmt" + "strconv" + "time" "git.readonly.ch/bouzoure/pop-camarades/helpers" "git.readonly.ch/bouzoure/pop-camarades/models" + "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "gorm.io/gorm" ) @@ -32,3 +36,605 @@ func Contacts(c *fiber.Ctx) error { "People": people, }) } + +func ContactShow(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + var person models.Person + result := db.Unscoped().Preload("Section").Find( + &person, "id = ? AND is_contact", id, true, + ) + + if result.RowsAffected < 1 { + return fiber.NewError(fiber.StatusNotFound, "Not found") + } + + if result.Error != nil { + return result.Error + } + + title := fmt.Sprintf( + "%s %s | Contact", + person.LastName, + person.FirstName, + ) + + var fields []models.Field + db.Order("name collate nocase asc").Preload( + "List", + ).Find( + &fields, "person_type = ?", "contact", + ) + + var fieldValues []models.FieldValue + db.Preload("ListItem").Find( + &fieldValues, + "person_id = ?", + person.ID, + ) + + return c.Render("person", fiber.Map{ + "PageTitle": title, + "Person": person, + "Fields": fields, + "FieldValues": fieldValues, + }) +} + +func ContactAdd(c *fiber.Ctx) error { + var person models.Person + var errors []string + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + var sections []models.Section + db.Order("name collate nocase asc").Find( + §ions, + "contains_contacts = ?", + true, + ) + + var fields []models.Field + db.Preload("List").Preload("List.ListItems").Order( + "name collate nocase asc", + ).Find( + &fields, "person_type = ?", "contact", + ) + + if c.Method() == "POST" { + data := PersonValidation{ + LastName: c.FormValue("last_name"), + FirstName: c.FormValue("first_name"), + Email: c.FormValue("email"), + Phone: c.FormValue("phone"), + Mobile: c.FormValue("mobile"), + Address1: c.FormValue("address1"), + Address2: c.FormValue("address2"), + PostalCode: c.FormValue("postal_code"), + City: c.FormValue("city"), + Section: c.FormValue("section"), + } + + validate := validator.New() + validErrs := validate.Struct(data) + + if validErrs != nil { + for _, validErr := range validErrs.(validator.ValidationErrors) { + switch validErr.Field() { + case "LastName": + errors = append(errors, "Le nom de famille est requis et ne peut pas contenir plus de 100 caractères") + case "FirstName": + errors = append(errors, "Le prénom est requis et ne peut pas contenir plus de 100 caractères") + case "Email": + if len(data.Email) > 0 { + errors = append(errors, "L'adresse email doit être valide") + } + case "Phone": + errors = append(errors, "Le numéro de téléphone fixe est trop long") + case "Mobile": + errors = append(errors, "Le numéro de téléphone mobile est trop long") + case "Address1": + errors = append(errors, "La ligne 1 de l'adresse est trop longue") + case "Address2": + errors = append(errors, "La ligne 2 de l'adresse est trop longue") + case "PostalCode": + if len(data.PostalCode) > 0 { + errors = append(errors, "Le code postal n'est pas valide") + } + case "City": + errors = append(errors, "Le lieu est trop long") + case "Section": + errors = append(errors, "La section n'est pas valide") + } + } + } + + person.IsContact = true + person.IsMember = false + person.LastName = data.LastName + person.FirstName = data.FirstName + person.Email = data.Email + person.Phone = data.Phone + person.Mobile = data.Mobile + person.Address1 = data.Address1 + person.Address2 = data.Address2 + person.PostalCode = data.PostalCode + person.City = data.City + + sectionID, err := strconv.ParseUint(data.Section, 10, 0) + if err == nil { + for _, section := range sections { + if section.ID == uint(sectionID) { + person.SectionID = uint(sectionID) + break + } + } + } + + if person.SectionID == 0 { + errors = append(errors, "La section est introuvable") + } + + if len(errors) == 0 { + result := db.Create(&person) + if result.Error != nil { + return result.Error + } + + for _, field := range fields { + if field.List.Multi { + for _, listItem := range field.List.ListItems { + key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) + value := c.FormValue(key) + + if value == "on" { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + fieldValue.ListItemID = listItem.ID + db.Create(&fieldValue) + } + } + } else { + key := fmt.Sprintf("field-%d", field.ID) + value := c.FormValue(key) + + if (field.FieldType == "text" || field.FieldType == "longtext") && len(value) > 0 { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueString.Scan(value) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "number" && len(value) > 0 { + valueInt, err := strconv.ParseInt(value, 10, 0) + if err != nil { + continue + } + + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueInt.Scan(valueInt) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "date" && len(value) > 0 { + valueDate, err := time.Parse("2006-01-02", value) + if err != nil { + continue + } + + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueDate.Scan(valueDate) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "list" && len(value) > 0 { + valueInt, err := strconv.ParseUint(value, 10, 0) + if err != nil { + continue + } + + found := false + for _, listItem := range field.List.ListItems { + if listItem.ID == uint(valueInt) { + found = true + } + } + + if found { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + fieldValue.ListItemID = uint(valueInt) + db.Create(&fieldValue) + } + } + } + } + + c.Redirect(fmt.Sprintf( + "/contacts/%d", + person.ID, + )) + } + } + + return c.Render("person_form", fiber.Map{ + "PageTitle": "Ajouter un contact", + "Person": person, + "Sections": sections, + "Fields": fields, + "Errors": errors, + }) +} + +func ContactEdit(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + var person models.Person + result := db.Find(&person, "id = ?", id) + + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusNotFound, "Not found") + } + + if result.Error != nil { + return result.Error + } + + title := fmt.Sprintf( + "%s %s | Modifier contact", + person.LastName, + person.FirstName, + ) + + var sections []models.Section + db.Order("name collate nocase asc").Find( + §ions, + "contains_contacts = ?", + true, + ) + + var fields []models.Field + db.Preload("List").Preload("List.ListItems").Order( + "name collate nocase asc", + ).Find( + &fields, "person_type = ?", "contact", + ) + + var fieldValues []models.FieldValue + db.Preload("ListItem").Find( + &fieldValues, + "person_id = ?", + person.ID, + ) + + var errors []string + if c.Method() == "POST" { + data := PersonValidation{ + LastName: c.FormValue("last_name"), + FirstName: c.FormValue("first_name"), + Email: c.FormValue("email"), + Phone: c.FormValue("phone"), + Mobile: c.FormValue("mobile"), + Address1: c.FormValue("address1"), + Address2: c.FormValue("address2"), + PostalCode: c.FormValue("postal_code"), + City: c.FormValue("city"), + Section: c.FormValue("section"), + } + + validate := validator.New() + validErrs := validate.Struct(data) + + if validErrs != nil { + for _, validErr := range validErrs.(validator.ValidationErrors) { + switch validErr.Field() { + case "LastName": + errors = append(errors, "Le nom de famille est requis et ne peut pas contenir plus de 100 caractères") + case "FirstName": + errors = append(errors, "Le prénom est requis et ne peut pas contenir plus de 100 caractères") + case "Email": + if len(data.Email) > 0 { + errors = append(errors, "L'adresse email doit être valide") + } + case "Phone": + errors = append(errors, "Le numéro de téléphone fixe est trop long") + case "Mobile": + errors = append(errors, "Le numéro de téléphone mobile est trop long") + case "Address1": + errors = append(errors, "La ligne 1 de l'adresse est trop longue") + case "Address2": + errors = append(errors, "La ligne 2 de l'adresse est trop longue") + case "PostalCode": + if len(data.PostalCode) > 0 { + errors = append(errors, "Le code postal n'est pas valide") + } + case "City": + errors = append(errors, "Le lieu est trop long") + case "Section": + errors = append(errors, "La section n'est pas valide") + } + } + } + + person.IsContact = true + person.IsMember = false + person.LastName = data.LastName + person.FirstName = data.FirstName + person.Email = data.Email + person.Phone = data.Phone + person.Mobile = data.Mobile + person.Address1 = data.Address1 + person.Address2 = data.Address2 + person.PostalCode = data.PostalCode + person.City = data.City + + sectionID, err := strconv.ParseUint(data.Section, 10, 0) + if err == nil { + for _, section := range sections { + if section.ID == uint(sectionID) { + person.SectionID = uint(sectionID) + break + } + } + } + + if person.SectionID == 0 { + errors = append(errors, "La section est introuvable") + } + + for _, field := range fields { + db.Delete( + &models.FieldValue{}, + "person_id = ? AND field_id = ?", + person.ID, + field.ID, + ) + + if field.List.Multi { + for _, listItem := range field.List.ListItems { + key := fmt.Sprintf("field-%d-%d", field.ID, listItem.ID) + value := c.FormValue(key) + + if value == "on" { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + fieldValue.ListItemID = listItem.ID + db.Create(&fieldValue) + } + } + } else { + key := fmt.Sprintf("field-%d", field.ID) + value := c.FormValue(key) + + if (field.FieldType == "text" || field.FieldType == "longtext") && len(value) > 0 { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueString.Scan(value) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "number" && len(value) > 0 { + valueInt, err := strconv.ParseInt(value, 10, 0) + if err != nil { + continue + } + + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueInt.Scan(valueInt) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "date" && len(value) > 0 { + valueDate, err := time.Parse("2006-01-02", value) + if err != nil { + continue + } + + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + + err = fieldValue.ValueDate.Scan(valueDate) + if err != nil { + continue + } + + db.Create(&fieldValue) + } + + if field.FieldType == "list" && len(value) > 0 { + valueInt, err := strconv.ParseUint(value, 10, 0) + if err != nil { + continue + } + + found := false + for _, listItem := range field.List.ListItems { + if listItem.ID == uint(valueInt) { + found = true + } + } + + if found { + var fieldValue models.FieldValue + fieldValue.FieldID = field.ID + fieldValue.PersonID = person.ID + fieldValue.ListItemID = uint(valueInt) + db.Create(&fieldValue) + } + } + } + } + + if len(errors) == 0 { + result := db.Save(&person) + if result.Error != nil { + return result.Error + } + + c.Redirect(fmt.Sprintf( + "/contacts/%d", + person.ID, + )) + } + } + + return c.Render("person_form", fiber.Map{ + "PageTitle": title, + "Person": person, + "Sections": sections, + "Fields": fields, + "FieldValues": fieldValues, + "Errors": errors, + }) +} + +func ContactConvert(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + var person models.Person + result := db.Find(&person, "id = ?", id) + + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusNotFound, "Not found") + } + + if result.Error != nil { + return result.Error + } + + person.IsContact = false + person.IsMember = true + + result = db.Save(&person) + if result.Error != nil { + return result.Error + } + + return c.Redirect(fmt.Sprintf( + "/members/%d", + person.ID, + )) +} + +func ContactArchive(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + result := db.Delete(&models.Person{}, id) + if result.Error != nil { + return result.Error + } + + return c.Redirect(fmt.Sprintf( + "/contacts/%s", + id, + )) +} + +func ContactRestore(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + result := db.Unscoped().Model(&models.Person{}).Where( + "id = ?", id, + ).Update("DeletedAt", nil) + + if result.Error != nil { + return result.Error + } + + return c.Redirect(fmt.Sprintf( + "/contacts/%s", + id, + )) +} + +func ContactPurge(c *fiber.Ctx) error { + id := c.Params("id") + + db, err := helpers.GetDatabase() + if err != nil { + return err + } + + result := db.Unscoped().Delete( + &models.FieldValue{}, "person_id = ?", id, + ) + + if result.Error != nil { + return result.Error + } + + result = db.Unscoped().Delete( + &models.Person{}, id, + ) + + if result.Error != nil { + return result.Error + } + + return c.Redirect("/contacts") +} diff --git a/main.go b/main.go index 0596f0b..a43a06d 100644 --- a/main.go +++ b/main.go @@ -130,6 +130,15 @@ func main() { // Contacts app.Get("/contacts", controllers.Contacts) + app.Get("/contacts/:id", controllers.ContactShow) + app.Get("/contacts/add", controllers.ContactAdd) + app.Post("/contacts/add", controllers.ContactAdd) + app.Get("/contacts/:id/edit", controllers.ContactEdit) + app.Post("/contacts/:id/edit", controllers.ContactEdit) + app.Post("/contacts/:id/convert", controllers.ContactConvert) + app.Post("/contacts/:id/archive", controllers.ContactArchive) + app.Post("/contacts/:id/restore", controllers.ContactRestore) + app.Post("/contacts/:id/purge", controllers.ContactPurge) // Account manage app.Get("/account/manage", controllers.AccountManage) diff --git a/views/person.html b/views/person.html index 0b22809..3135695 100644 --- a/views/person.html +++ b/views/person.html @@ -316,20 +316,61 @@ {% else %} - - - Modifier - -
- -
+
+
+ {% if !Person.DeletedAt.Valid %} + + + Modifier + +
+ +
+ {% endif %} +
+
+ {% if Person.DeletedAt.Valid %} +
+ +
+ {% else %} +
+ +
+ {% endif %} +
+ +
+
+
{% endif %}