diff --git a/dto/trash_dto.go b/dto/trash_dto.go index 092386d..991ff3d 100644 --- a/dto/trash_dto.go +++ b/dto/trash_dto.go @@ -1,19 +1,23 @@ package dto -import "strings" +import ( + "strings" +) type RequestTrashCategoryDTO struct { - Name string `json:"name"` - Icon string `json:"icon"` + Name string `json:"name"` + EstimatedPrice string `json:"estimatedprice"` + Icon string `json:"icon"` } type ResponseTrashCategoryDTO struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Icon string `json:"icon,omitempty"` - CreatedAt string `json:"createdAt,omitempty"` - UpdatedAt string `json:"updatedAt,omitempty"` - Details []ResponseTrashDetailDTO `json:"details,omitempty"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Icon string `json:"icon,omitempty"` + EstimatedPrice float64 `json:"estimatedprice"` + CreatedAt string `json:"createdAt,omitempty"` + UpdatedAt string `json:"updatedAt,omitempty"` + Details []ResponseTrashDetailDTO `json:"details,omitempty"` } type ResponseTrashDetailDTO struct { @@ -37,6 +41,11 @@ func (r *RequestTrashCategoryDTO) ValidateTrashCategoryInput() (map[string][]str if strings.TrimSpace(r.Name) == "" { errors["name"] = append(errors["name"], "name is required") } + + // if valid, msg := utils.ValidateFloatPrice(fmt.Sprintf("%f", r.EstimatedPrice)); !valid { + // errors["estimated_price"] = append(errors["estimated_price"], msg) + // } + if len(errors) > 0 { return errors, false } @@ -50,9 +59,11 @@ func (r *RequestTrashDetailDTO) ValidateTrashDetailInput() (map[string][]string, if strings.TrimSpace(r.Description) == "" { errors["description"] = append(errors["description"], "description is required") } - if r.Price <= 0 { - errors["price"] = append(errors["price"], "price must be greater than 0") - } + + // if valid, msg := utils.ValidateFloatPrice(fmt.Sprintf("%f", r.Price)); !valid { + // errors["price"] = append(errors["price"], msg) + // } + if len(errors) > 0 { return errors, false } diff --git a/internal/handler/trash_handler.go b/internal/handler/trash_handler.go index e3cace8..3bec83f 100644 --- a/internal/handler/trash_handler.go +++ b/internal/handler/trash_handler.go @@ -19,10 +19,16 @@ func NewTrashHandler(trashService services.TrashService) *TrashHandler { func (h *TrashHandler) CreateCategory(c *fiber.Ctx) error { var request dto.RequestTrashCategoryDTO + if err := c.BodyParser(&request); err != nil { return utils.ValidationErrorResponse(c, map[string][]string{"body": {"Invalid body"}}) } + errors, valid := request.ValidateTrashCategoryInput() + if !valid { + return utils.ValidationErrorResponse(c, errors) + } + iconTrash, err := c.FormFile("icon") if err != nil { log.Printf("Error retrieving card photo from request: %v", err) diff --git a/internal/services/trash_service.go b/internal/services/trash_service.go index bb1cc29..c4601e9 100644 --- a/internal/services/trash_service.go +++ b/internal/services/trash_service.go @@ -6,6 +6,7 @@ import ( "mime/multipart" "os" "path/filepath" + "strconv" "time" "rijig/dto" @@ -101,9 +102,13 @@ func deleteIconTrashFIle(imagePath string) error { } func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconTrash *multipart.FileHeader) (*dto.ResponseTrashCategoryDTO, error) { - errors, valid := request.ValidateTrashCategoryInput() - if !valid { - return nil, fmt.Errorf("validation error: %v", errors) + + parsedPrice, err := strconv.ParseFloat(request.EstimatedPrice, 64) + fmt.Println("Received estimatedprice:", request.EstimatedPrice) + if err != nil { + return nil, fmt.Errorf("gagal memvalidasi harga: %v", err) + } else { + fmt.Printf("hasil parsing%v", parsedPrice) } icontrashPath, err := s.saveIconOfTrash(iconTrash) @@ -113,7 +118,9 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT category := model.TrashCategory{ Name: request.Name, - Icon: icontrashPath, + + EstimatedPrice: parsedPrice, + Icon: icontrashPath, } if err := s.TrashRepo.CreateCategory(&category); err != nil { @@ -124,11 +131,12 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ - ID: category.ID, - Name: category.Name, - Icon: category.Icon, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + ID: category.ID, + Name: category.Name, + EstimatedPrice: float64(category.EstimatedPrice), + Icon: category.Icon, + CreatedAt: createdAt, + UpdatedAt: updatedAt, } if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, nil, time.Hour*6); err != nil { @@ -142,11 +150,12 @@ func (s *trashService) CreateCategory(request dto.RequestTrashCategoryDTO, iconT ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt) cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt) categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ - ID: c.ID, - Name: c.Name, - Icon: c.Icon, - CreatedAt: ccreatedAt, - UpdatedAt: cupdatedAt, + ID: c.ID, + Name: c.Name, + EstimatedPrice: float64(c.EstimatedPrice), + Icon: c.Icon, + CreatedAt: ccreatedAt, + UpdatedAt: cupdatedAt, }) } @@ -227,11 +236,12 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) { for _, category := range cachedCategories["data"].([]interface{}) { categoryData := category.(map[string]interface{}) categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ - ID: categoryData["id"].(string), - Name: categoryData["name"].(string), - Icon: categoryData["icon"].(string), - CreatedAt: categoryData["createdAt"].(string), - UpdatedAt: categoryData["updatedAt"].(string), + ID: categoryData["id"].(string), + Name: categoryData["name"].(string), + EstimatedPrice: categoryData["estimatedprice"].(float64), + Icon: categoryData["icon"].(string), + CreatedAt: categoryData["createdAt"].(string), + UpdatedAt: categoryData["updatedAt"].(string), }) } return categoriesDTO, nil @@ -244,19 +254,19 @@ func (s *trashService) GetCategories() ([]dto.ResponseTrashCategoryDTO, error) { var categoriesDTO []dto.ResponseTrashCategoryDTO for _, category := range categories { - // path := os.Getenv("BASE_URL") + createdAt, _ := utils.FormatDateToIndonesianFormat(category.CreatedAt) updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ - ID: category.ID, - Name: category.Name, - Icon: category.Icon, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + ID: category.ID, + Name: category.Name, + EstimatedPrice: category.EstimatedPrice, + Icon: category.Icon, + CreatedAt: createdAt, + UpdatedAt: updatedAt, }) } - cacheData := map[string]interface{}{ "data": categoriesDTO, } @@ -274,12 +284,13 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO categoryData := cachedCategory["data"].(map[string]interface{}) details := mapDetails(cachedCategory["details"]) return &dto.ResponseTrashCategoryDTO{ - ID: categoryData["id"].(string), - Name: categoryData["name"].(string), - Icon: categoryData["icon"].(string), - CreatedAt: categoryData["createdAt"].(string), - UpdatedAt: categoryData["updatedAt"].(string), - Details: details, + ID: categoryData["id"].(string), + Name: categoryData["name"].(string), + EstimatedPrice: categoryData["estimatedprice"].(float64), + Icon: categoryData["icon"].(string), + CreatedAt: categoryData["createdAt"].(string), + UpdatedAt: categoryData["updatedAt"].(string), + Details: details, }, nil } @@ -292,11 +303,12 @@ func (s *trashService) GetCategoryByID(id string) (*dto.ResponseTrashCategoryDTO updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) categoryDTO := &dto.ResponseTrashCategoryDTO{ - ID: category.ID, - Name: category.Name, - Icon: category.Icon, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + ID: category.ID, + Name: category.Name, + EstimatedPrice: category.EstimatedPrice, + Icon: category.Icon, + CreatedAt: createdAt, + UpdatedAt: updatedAt, } if category.Details != nil { @@ -405,11 +417,12 @@ func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategor updatedAt, _ := utils.FormatDateToIndonesianFormat(category.UpdatedAt) categoryResponseDTO := &dto.ResponseTrashCategoryDTO{ - ID: category.ID, - Name: category.Name, - Icon: category.Icon, - CreatedAt: createdAt, - UpdatedAt: updatedAt, + ID: category.ID, + Name: category.Name, + EstimatedPrice: category.EstimatedPrice, + Icon: category.Icon, + CreatedAt: createdAt, + UpdatedAt: updatedAt, } if err := s.CacheCategoryAndDetails(category.ID, categoryResponseDTO, category.Details, time.Hour*6); err != nil { @@ -423,11 +436,12 @@ func (s *trashService) UpdateCategory(id string, request dto.RequestTrashCategor ccreatedAt, _ := utils.FormatDateToIndonesianFormat(c.CreatedAt) cupdatedAt, _ := utils.FormatDateToIndonesianFormat(c.UpdatedAt) categoriesDTO = append(categoriesDTO, dto.ResponseTrashCategoryDTO{ - ID: c.ID, - Name: c.Name, - Icon: c.Icon, - CreatedAt: ccreatedAt, - UpdatedAt: cupdatedAt, + ID: c.ID, + Name: c.Name, + EstimatedPrice: c.EstimatedPrice, + Icon: c.Icon, + CreatedAt: ccreatedAt, + UpdatedAt: cupdatedAt, }) } @@ -512,6 +526,15 @@ func (s *trashService) DeleteCategory(id string) error { } } + category, err := s.TrashRepo.GetCategoryByID(id) + if err != nil { + return fmt.Errorf("failed to fetch category for deletion: %v", err) + } + + if err := deleteIconTrashFIle(category.Icon); err != nil { + return fmt.Errorf("error deleting icon for category %s: %v", id, err) + } + if err := s.TrashRepo.DeleteCategory(id); err != nil { return fmt.Errorf("failed to delete category: %v", err) } diff --git a/model/trash_model.go b/model/trash_model.go index 7a90d0d..ba44939 100644 --- a/model/trash_model.go +++ b/model/trash_model.go @@ -3,12 +3,13 @@ package model import "time" type TrashCategory struct { - ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` - Name string `gorm:"not null" json:"name"` - Icon string `json:"icon,omitempty"` - Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"` - CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` - UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` + ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id"` + Name string `gorm:"not null" json:"name"` + Icon string `json:"icon,omitempty"` + EstimatedPrice float64 `gorm:"not null" json:"estimated_price"` + Details []TrashDetail `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE;" json:"details"` + CreatedAt time.Time `gorm:"default:current_timestamp" json:"createdAt"` + UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updatedAt"` } type TrashDetail struct { diff --git a/utils/regexp_formatter.go b/utils/regexp_formatter.go index 616675f..3b020ed 100644 --- a/utils/regexp_formatter.go +++ b/utils/regexp_formatter.go @@ -1,7 +1,9 @@ package utils import ( + "fmt" "regexp" + "strconv" "strings" ) @@ -42,3 +44,20 @@ func isSpecialCharacter(char rune) bool { specialChars := "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~" return strings.ContainsRune(specialChars, char) } + +func ValidateFloatPrice(price string) (float64, error) { + + // price = strings.Trim(price, `"`) + // price = strings.TrimSpace(price) + + parsedPrice, err := strconv.ParseFloat(price, 64) + if err != nil { + return 0, fmt.Errorf("harga tidak valid. Format harga harus angka desimal.") + } + + if parsedPrice <= 0 { + return 0, fmt.Errorf("harga harus lebih besar dari 0.") + } + + return parsedPrice, nil +}