diff --git a/config/database.go b/config/database.go index 34451f3..9f89033 100644 --- a/config/database.go +++ b/config/database.go @@ -60,6 +60,8 @@ func ConnectDatabase() { &model.Article{}, &model.Banner{}, &model.InitialCoint{}, + &model.About{}, + &model.AboutDetail{}, // =>Trash Model<= &model.TrashCategory{}, diff --git a/dto/about_dto.go b/dto/about_dto.go new file mode 100644 index 0000000..898e647 --- /dev/null +++ b/dto/about_dto.go @@ -0,0 +1,71 @@ +package dto + +import ( + "strings" +) + +type RequestAboutDTO struct { + Title string `json:"title"` + CoverImage string `json:"cover_image"` + // AboutDetail []RequestAboutDetailDTO `json:"about_detail"` +} + +func (r *RequestAboutDTO) ValidateAbout() (map[string][]string, bool) { + errors := make(map[string][]string) + + if strings.TrimSpace(r.Title) == "" { + errors["title"] = append(errors["title"], "Title is required") + } + + if len(errors) > 0 { + return errors, false + } + + return nil, true +} + +type ResponseAboutDTO struct { + ID string `json:"id"` + Title string `json:"title"` + CoverImage string `json:"cover_image"` + AboutDetail *[]ResponseAboutDetailDTO `json:"about_detail"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +type RequestAboutDetailDTO struct { + AboutId string `json:"about_id"` + ImageDetail string `json:"image_detail"` + Description string `json:"description"` +} + +func (r *RequestAboutDetailDTO) ValidateAboutDetail() (map[string][]string, bool) { + errors := make(map[string][]string) + + if strings.TrimSpace(r.AboutId) == "" { + errors["about_id"] = append(errors["about_id"], "About ID is required") + } + + if strings.TrimSpace(r.ImageDetail) == "" { + errors["image_detail"] = append(errors["image_detail"], "Image detail is required") + } + + if strings.TrimSpace(r.Description) == "" { + errors["description"] = append(errors["description"], "Description is required") + } + + if len(errors) > 0 { + return errors, false + } + + return nil, true +} + +type ResponseAboutDetailDTO struct { + ID string `json:"id"` + AboutID string `json:"about_id"` + ImageDetail string `json:"image_detail"` + Description string `json:"description"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} diff --git a/internal/handler/about_handler.go b/internal/handler/about_handler.go new file mode 100644 index 0000000..ca4f581 --- /dev/null +++ b/internal/handler/about_handler.go @@ -0,0 +1,170 @@ +package handler + +import ( + "fmt" + "log" + "rijig/dto" + "rijig/internal/services" + "rijig/utils" + + "github.com/gofiber/fiber/v2" +) + +type AboutHandler struct { + AboutService services.AboutService +} + +func NewAboutHandler(aboutService services.AboutService) *AboutHandler { + return &AboutHandler{ + AboutService: aboutService, + } +} + +func (h *AboutHandler) CreateAbout(c *fiber.Ctx) error { + var request dto.RequestAboutDTO + if err := c.BodyParser(&request); err != nil { + log.Printf("Error parsing request body: %v", err) + return utils.ErrorResponse(c, "Invalid input data") + } + + aboutCoverImage, err := c.FormFile("cover_image") + if err != nil { + log.Printf("Error retrieving cover image about from request: %v", err) + return utils.ErrorResponse(c, "cover_iamge is required") + } + + response, err := h.AboutService.CreateAbout(request, aboutCoverImage) + if err != nil { + log.Printf("Error creating About: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to create About: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully created About") +} + +func (h *AboutHandler) UpdateAbout(c *fiber.Ctx) error { + id := c.Params("id") + + var request dto.RequestAboutDTO + if err := c.BodyParser(&request); err != nil { + log.Printf("Error parsing request body: %v", err) + return utils.ErrorResponse(c, "Invalid input data") + } + + aboutCoverImage, err := c.FormFile("cover_image") + if err != nil { + log.Printf("Error retrieving cover image about from request: %v", err) + return utils.ErrorResponse(c, "cover_iamge is required") + } + + response, err := h.AboutService.UpdateAbout(id, request, aboutCoverImage) + if err != nil { + log.Printf("Error updating About: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to update About: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully updated About") +} + +func (h *AboutHandler) GetAllAbout(c *fiber.Ctx) error { + + response, err := h.AboutService.GetAllAbout() + if err != nil { + log.Printf("Error fetching all About: %v", err) + return utils.ErrorResponse(c, "Failed to fetch About list") + } + + return utils.PaginatedResponse(c, response, 1, len(response), len(response), "Successfully fetched About list") +} + +func (h *AboutHandler) GetAboutByID(c *fiber.Ctx) error { + id := c.Params("id") + + response, err := h.AboutService.GetAboutByID(id) + if err != nil { + log.Printf("Error fetching About by ID: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch About by ID: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully fetched About") +} + +func (h *AboutHandler) GetAboutDetailById(c *fiber.Ctx) error { + id := c.Params("id") + + response, err := h.AboutService.GetAboutDetailById(id) + if err != nil { + log.Printf("Error fetching About detail by ID: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to fetch About by ID: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully fetched About") +} + +func (h *AboutHandler) DeleteAbout(c *fiber.Ctx) error { + id := c.Params("id") + + if err := h.AboutService.DeleteAbout(id); err != nil { + log.Printf("Error deleting About: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to delete About: %v", err)) + } + + return utils.GenericResponse(c, fiber.StatusOK, "Successfully deleted About") +} + +func (h *AboutHandler) CreateAboutDetail(c *fiber.Ctx) error { + var request dto.RequestAboutDetailDTO + if err := c.BodyParser(&request); err != nil { + log.Printf("Error parsing request body: %v", err) + return utils.ErrorResponse(c, "Invalid input data") + } + + aboutDetailImage, err := c.FormFile("image_detail") + if err != nil { + log.Printf("Error retrieving image detail from request: %v", err) + return utils.ErrorResponse(c, "image_detail is required") + } + + response, err := h.AboutService.CreateAboutDetail(request, aboutDetailImage) + if err != nil { + log.Printf("Error creating AboutDetail: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to create AboutDetail: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully created AboutDetail") +} + +func (h *AboutHandler) UpdateAboutDetail(c *fiber.Ctx) error { + id := c.Params("id") + + var request dto.RequestAboutDetailDTO + if err := c.BodyParser(&request); err != nil { + log.Printf("Error parsing request body: %v", err) + return utils.ErrorResponse(c, "Invalid input data") + } + + aboutDetailImage, err := c.FormFile("image_detail") + if err != nil { + log.Printf("Error retrieving image detail from request: %v", err) + return utils.ErrorResponse(c, "image_detail is required") + } + + response, err := h.AboutService.UpdateAboutDetail(id, request, aboutDetailImage) + if err != nil { + log.Printf("Error updating AboutDetail: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to update AboutDetail: %v", err)) + } + + return utils.SuccessResponse(c, response, "Successfully updated AboutDetail") +} + +func (h *AboutHandler) DeleteAboutDetail(c *fiber.Ctx) error { + id := c.Params("id") + + if err := h.AboutService.DeleteAboutDetail(id); err != nil { + log.Printf("Error deleting AboutDetail: %v", err) + return utils.ErrorResponse(c, fmt.Sprintf("Failed to delete AboutDetail: %v", err)) + } + + return utils.GenericResponse(c, fiber.StatusOK, "Successfully deleted AboutDetail") +} diff --git a/internal/repositories/about_repo.go b/internal/repositories/about_repo.go new file mode 100644 index 0000000..3535af9 --- /dev/null +++ b/internal/repositories/about_repo.go @@ -0,0 +1,100 @@ +package repositories + +import ( + "fmt" + "rijig/model" + + "gorm.io/gorm" +) + +type AboutRepository interface { + CreateAbout(about *model.About) error + CreateAboutDetail(aboutDetail *model.AboutDetail) error + GetAllAbout() ([]model.About, error) + GetAboutByID(id string) (*model.About, error) + GetAboutDetailByID(id string) (*model.AboutDetail, error) + UpdateAbout(id string, about *model.About) (*model.About, error) + UpdateAboutDetail(id string, aboutDetail *model.AboutDetail) (*model.AboutDetail, error) + DeleteAbout(id string) error + DeleteAboutDetail(id string) error +} + +type aboutRepository struct { + DB *gorm.DB +} + +func NewAboutRepository(db *gorm.DB) AboutRepository { + return &aboutRepository{DB: db} +} + +func (r *aboutRepository) CreateAbout(about *model.About) error { + if err := r.DB.Create(&about).Error; err != nil { + return fmt.Errorf("failed to create About: %v", err) + } + return nil +} + +func (r *aboutRepository) CreateAboutDetail(aboutDetail *model.AboutDetail) error { + if err := r.DB.Create(&aboutDetail).Error; err != nil { + return fmt.Errorf("failed to create AboutDetail: %v", err) + } + return nil +} + +func (r *aboutRepository) GetAllAbout() ([]model.About, error) { + var abouts []model.About + if err := r.DB.Find(&abouts).Error; err != nil { + return nil, fmt.Errorf("failed to fetch all About records: %v", err) + } + return abouts, nil +} + +func (r *aboutRepository) GetAboutByID(id string) (*model.About, error) { + var about model.About + if err := r.DB.Preload("AboutDetail").Where("id = ?", id).First(&about).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, fmt.Errorf("about with ID %s not found", id) + } + return nil, fmt.Errorf("failed to fetch About by ID: %v", err) + } + return &about, nil +} + +func (r *aboutRepository) GetAboutDetailByID(id string) (*model.AboutDetail, error) { + var aboutDetail model.AboutDetail + if err := r.DB.Where("id = ?", id).First(&aboutDetail).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, fmt.Errorf("aboutdetail with ID %s not found", id) + } + return nil, fmt.Errorf("failed to fetch About by ID: %v", err) + } + return &aboutDetail, nil +} + +func (r *aboutRepository) UpdateAbout(id string, about *model.About) (*model.About, error) { + if err := r.DB.Model(&about).Where("id = ?", id).Updates(about).Error; err != nil { + return nil, fmt.Errorf("failed to update About: %v", err) + } + return about, nil +} + +func (r *aboutRepository) UpdateAboutDetail(id string, aboutDetail *model.AboutDetail) (*model.AboutDetail, error) { + if err := r.DB.Model(&aboutDetail).Where("id = ?", id).Updates(aboutDetail).Error; err != nil { + return nil, fmt.Errorf("failed to update AboutDetail: %v", err) + } + return aboutDetail, nil +} + +func (r *aboutRepository) DeleteAbout(id string) error { + if err := r.DB.Where("id = ?", id).Delete(&model.About{}).Error; err != nil { + return fmt.Errorf("failed to delete About: %v", err) + } + return nil +} + +func (r *aboutRepository) DeleteAboutDetail(id string) error { + if err := r.DB.Where("id = ?", id).Delete(&model.AboutDetail{}).Error; err != nil { + return fmt.Errorf("failed to delete AboutDetail: %v", err) + } + return nil +} diff --git a/internal/services/about_service.go b/internal/services/about_service.go new file mode 100644 index 0000000..1d1fcab --- /dev/null +++ b/internal/services/about_service.go @@ -0,0 +1,426 @@ +package services + +import ( + "fmt" + "log" + "mime/multipart" + "os" + "path/filepath" + "rijig/dto" + "rijig/internal/repositories" + "rijig/model" + "rijig/utils" + + "github.com/google/uuid" +) + +type AboutService interface { + CreateAbout(request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) + UpdateAbout(id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) + GetAllAbout() ([]dto.ResponseAboutDTO, error) + GetAboutByID(id string) (*dto.ResponseAboutDTO, error) + GetAboutDetailById(id string) (*dto.ResponseAboutDetailDTO, error) + DeleteAbout(id string) error + + CreateAboutDetail(request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) + UpdateAboutDetail(id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) + DeleteAboutDetail(id string) error +} + +type aboutService struct { + aboutRepo repositories.AboutRepository +} + +func NewAboutService(aboutRepo repositories.AboutRepository) AboutService { + return &aboutService{aboutRepo: aboutRepo} +} + +func formatResponseAboutDetailDTO(about *model.AboutDetail) (*dto.ResponseAboutDetailDTO, error) { + createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) + + response := &dto.ResponseAboutDetailDTO{ + ID: about.ID, + AboutID: about.AboutID, + ImageDetail: about.ImageDetail, + Description: about.Description, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + return response, nil +} + +func formatResponseAboutDTO(about *model.About) (*dto.ResponseAboutDTO, error) { + createdAt, _ := utils.FormatDateToIndonesianFormat(about.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(about.UpdatedAt) + + response := &dto.ResponseAboutDTO{ + ID: about.ID, + Title: about.Title, + CoverImage: about.CoverImage, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + return response, nil +} + +func (s *aboutService) saveCoverImageAbout(coverImageAbout *multipart.FileHeader) (string, error) { + pathImage := "/uploads/coverabout/" + coverImageAboutDir := "./public" + os.Getenv("BASE_URL") + pathImage + if _, err := os.Stat(coverImageAboutDir); os.IsNotExist(err) { + + if err := os.MkdirAll(coverImageAboutDir, os.ModePerm); err != nil { + return "", fmt.Errorf("gagal membuat direktori untuk cover image about: %v", err) + } + } + + allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true} + extension := filepath.Ext(coverImageAbout.Filename) + if !allowedExtensions[extension] { + return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed") + } + + coverImageFileName := fmt.Sprintf("%s_coverabout%s", uuid.New().String(), extension) + coverImagePath := filepath.Join(coverImageAboutDir, coverImageFileName) + + src, err := coverImageAbout.Open() + if err != nil { + return "", fmt.Errorf("failed to open uploaded file: %v", err) + } + defer src.Close() + + dst, err := os.Create(coverImagePath) + if err != nil { + return "", fmt.Errorf("failed to create cover image about file: %v", err) + } + defer dst.Close() + + if _, err := dst.ReadFrom(src); err != nil { + return "", fmt.Errorf("failed to save cover image about: %v", err) + } + + coverImageAboutUrl := fmt.Sprintf("%s%s", pathImage, coverImageFileName) + + return coverImageAboutUrl, nil +} + +func (s *aboutService) saveCoverImageAboutDetail(coverImageAbout *multipart.FileHeader) (string, error) { + pathImage := "/uploads/coverabout/coveraboutdetail" + coverImageAboutDir := "./public" + os.Getenv("BASE_URL") + pathImage + if _, err := os.Stat(coverImageAboutDir); os.IsNotExist(err) { + + if err := os.MkdirAll(coverImageAboutDir, os.ModePerm); err != nil { + return "", fmt.Errorf("gagal membuat direktori untuk cover image about: %v", err) + } + } + + allowedExtensions := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".svg": true} + extension := filepath.Ext(coverImageAbout.Filename) + if !allowedExtensions[extension] { + return "", fmt.Errorf("invalid file type, only .jpg, .jpeg, and .png are allowed") + } + + coverImageFileName := fmt.Sprintf("%s_coveraboutdetail_%s", uuid.New().String(), extension) + coverImagePath := filepath.Join(coverImageAboutDir, coverImageFileName) + + src, err := coverImageAbout.Open() + if err != nil { + return "", fmt.Errorf("failed to open uploaded file: %v", err) + } + defer src.Close() + + dst, err := os.Create(coverImagePath) + if err != nil { + return "", fmt.Errorf("failed to create cover image about file: %v", err) + } + defer dst.Close() + + if _, err := dst.ReadFrom(src); err != nil { + return "", fmt.Errorf("failed to save cover image about: %v", err) + } + + coverImageAboutUrl := fmt.Sprintf("%s%s", pathImage, coverImageFileName) + + return coverImageAboutUrl, nil +} + +func deleteCoverImageAbout(coverimageAboutPath string) error { + if coverimageAboutPath == "" { + return nil + } + + baseDir := "./public/" + os.Getenv("BASE_URL") + absolutePath := baseDir + coverimageAboutPath + + if _, err := os.Stat(absolutePath); os.IsNotExist(err) { + return fmt.Errorf("image file not found: %v", err) + } + + err := os.Remove(absolutePath) + if err != nil { + return fmt.Errorf("failed to delete image: %v", err) + } + + log.Printf("Image deleted successfully: %s", absolutePath) + return nil +} + +func (s *aboutService) CreateAbout(request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { + errors, valid := request.ValidateAbout() + if !valid { + return nil, fmt.Errorf("validation error: %v", errors) + } + + coverImageAboutPath, err := s.saveCoverImageAbout(coverImageAbout) + if err != nil { + return nil, fmt.Errorf("gagal menyimpan cover image about: %v ", err) + } + + about := model.About{ + Title: request.Title, + CoverImage: coverImageAboutPath, + } + + if err := s.aboutRepo.CreateAbout(&about); err != nil { + return nil, fmt.Errorf("failed to create About: %v", err) + } + + response, err := formatResponseAboutDTO(&about) + if err != nil { + return nil, fmt.Errorf("error formatting About response: %v", err) + } + + return response, nil +} + +func (s *aboutService) UpdateAbout(id string, request dto.RequestAboutDTO, coverImageAbout *multipart.FileHeader) (*dto.ResponseAboutDTO, error) { + + errors, valid := request.ValidateAbout() + if !valid { + return nil, fmt.Errorf("validation error: %v", errors) + } + + about, err := s.aboutRepo.GetAboutByID(id) + if err != nil { + return nil, fmt.Errorf("about not found: %v", err) + } + + if about.CoverImage != "" { + err := deleteCoverImageAbout(about.CoverImage) + if err != nil { + return nil, fmt.Errorf("gagal mengahpus gambar lama: %v", err) + } + } + + var coverImageAboutPath string + if coverImageAbout != nil { + coverImageAboutPath, err = s.saveCoverImageAbout(coverImageAbout) + if err != nil { + return nil, fmt.Errorf("gagal menyimpan gambar baru: %v", err) + } + } + + about.Title = request.Title + if coverImageAboutPath != "" { + about.CoverImage = coverImageAboutPath + } + + updatedAbout, err := s.aboutRepo.UpdateAbout(id, about) + if err != nil { + return nil, fmt.Errorf("failed to update About: %v", err) + } + + response, err := formatResponseAboutDTO(updatedAbout) + if err != nil { + return nil, fmt.Errorf("error formatting About response: %v", err) + } + + return response, nil +} + +func (s *aboutService) GetAllAbout() ([]dto.ResponseAboutDTO, error) { + + aboutList, err := s.aboutRepo.GetAllAbout() + if err != nil { + return nil, fmt.Errorf("failed to get About list: %v", err) + } + + var aboutDTOList []dto.ResponseAboutDTO + for _, about := range aboutList { + response, err := formatResponseAboutDTO(&about) + if err != nil { + log.Printf("Error formatting About response: %v", err) + continue + } + aboutDTOList = append(aboutDTOList, *response) + } + + return aboutDTOList, nil +} + +func (s *aboutService) GetAboutByID(id string) (*dto.ResponseAboutDTO, error) { + + about, err := s.aboutRepo.GetAboutByID(id) + if err != nil { + return nil, fmt.Errorf("about not found: %v", err) + } + + response, err := formatResponseAboutDTO(about) + if err != nil { + return nil, fmt.Errorf("error formatting About response: %v", err) + } + + return response, nil +} + +func (s *aboutService) GetAboutDetailById(id string) (*dto.ResponseAboutDetailDTO, error) { + + about, err := s.aboutRepo.GetAboutDetailByID(id) + if err != nil { + return nil, fmt.Errorf("about not found: %v", err) + } + + response, err := formatResponseAboutDetailDTO(about) + if err != nil { + return nil, fmt.Errorf("error formatting About response: %v", err) + } + + return response, nil +} + +func (s *aboutService) DeleteAbout(id string) error { + about, err := s.aboutRepo.GetAboutByID(id) + if err != nil { + return fmt.Errorf("about not found: %v", err) + } + + if about.CoverImage != "" { + err := deleteCoverImageAbout(about.CoverImage) + if err != nil { + return fmt.Errorf("gagal mengahpus gambar lama: %v", err) + } + } + + if err := s.aboutRepo.DeleteAbout(id); err != nil { + return fmt.Errorf("failed to delete About: %v", err) + } + + return nil +} + +func (s *aboutService) CreateAboutDetail(request dto.RequestAboutDetailDTO, coverImageAboutDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { + + errors, valid := request.ValidateAboutDetail() + if !valid { + return nil, fmt.Errorf("validation error: %v", errors) + } + + _, err := s.aboutRepo.GetAboutByID(request.AboutId) + if err != nil { + + return nil, fmt.Errorf("about_id tidak ditemukan: %v", err) + } + + coverImageAboutDetailPath, err := s.saveCoverImageAboutDetail(coverImageAboutDetail) + if err != nil { + return nil, fmt.Errorf("gagal menyimpan cover image about detail: %v ", err) + } + + aboutDetail := model.AboutDetail{ + AboutID: request.AboutId, + ImageDetail: coverImageAboutDetailPath, + Description: request.Description, + } + + if err := s.aboutRepo.CreateAboutDetail(&aboutDetail); err != nil { + return nil, fmt.Errorf("failed to create AboutDetail: %v", err) + } + + createdAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.UpdatedAt) + + response := &dto.ResponseAboutDetailDTO{ + ID: aboutDetail.ID, + AboutID: aboutDetail.AboutID, + ImageDetail: aboutDetail.ImageDetail, + Description: aboutDetail.Description, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + return response, nil +} + +func (s *aboutService) UpdateAboutDetail(id string, request dto.RequestAboutDetailDTO, imageDetail *multipart.FileHeader) (*dto.ResponseAboutDetailDTO, error) { + + errors, valid := request.ValidateAboutDetail() + if !valid { + return nil, fmt.Errorf("validation error: %v", errors) + } + + aboutDetail, err := s.aboutRepo.GetAboutDetailByID(id) + if err != nil { + return nil, fmt.Errorf("about detail tidakck ditemukan: %v", err) + } + + if aboutDetail.ImageDetail != "" { + err := deleteCoverImageAbout(aboutDetail.ImageDetail) + if err != nil { + return nil, fmt.Errorf("gagal menghapus gambar lama: %v", err) + } + } + + var coverImageAboutDeatilPath string + if imageDetail != nil { + coverImageAboutDeatilPath, err = s.saveCoverImageAbout(imageDetail) + if err != nil { + return nil, fmt.Errorf("gagal menyimpan gambar baru: %v", err) + } + } + + aboutDetail.Description = request.Description + if coverImageAboutDeatilPath != "" { + aboutDetail.ImageDetail = coverImageAboutDeatilPath + } + + aboutDetail, err = s.aboutRepo.UpdateAboutDetail(id, aboutDetail) + if err != nil { + log.Printf("Error updating about detail: %v", err) + return nil, fmt.Errorf("failed to update about detail: %v", err) + } + + createdAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.CreatedAt) + updatedAt, _ := utils.FormatDateToIndonesianFormat(aboutDetail.UpdatedAt) + + response := &dto.ResponseAboutDetailDTO{ + ID: aboutDetail.ID, + AboutID: aboutDetail.AboutID, + ImageDetail: aboutDetail.ImageDetail, + Description: aboutDetail.Description, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + } + + return response, nil +} + +func (s *aboutService) DeleteAboutDetail(id string) error { + aboutDetail, err := s.aboutRepo.GetAboutDetailByID(id) + if err != nil { + return fmt.Errorf("about detail tidakck ditemukan: %v", err) + } + + if aboutDetail.ImageDetail != "" { + err := deleteCoverImageAbout(aboutDetail.ImageDetail) + if err != nil { + return fmt.Errorf("gagal menghapus gambar lama: %v", err) + } + } + + if err := s.aboutRepo.DeleteAboutDetail(id); err != nil { + return fmt.Errorf("failed to delete AboutDetail: %v", err) + } + return nil +} diff --git a/model/about_model.go b/model/about_model.go new file mode 100644 index 0000000..746d2fa --- /dev/null +++ b/model/about_model.go @@ -0,0 +1,23 @@ +package model + +import ( + "time" +) + +type About struct { + ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"` + Title string `gorm:"not null" json:"title"` + CoverImage string `json:"cover_image"` + AboutDetail []AboutDetail `gorm:"foreignKey:AboutID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"about_detail"` + CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` + UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +} + +type AboutDetail struct { + ID string `gorm:"primaryKey;type:uuid;default:uuid_generate_v4();unique;not null" json:"id"` + AboutID string `gorm:"not null" json:"about_id"` + ImageDetail string `json:"image_detail"` + Description string `json:"description"` + CreatedAt time.Time `gorm:"default:current_timestamp" json:"created_at"` + UpdatedAt time.Time `gorm:"default:current_timestamp" json:"updated_at"` +} diff --git a/presentation/about_route.go b/presentation/about_route.go new file mode 100644 index 0000000..6616d4e --- /dev/null +++ b/presentation/about_route.go @@ -0,0 +1,37 @@ +package presentation + +import ( + "rijig/config" + "rijig/internal/handler" + "rijig/internal/repositories" + "rijig/internal/services" + "rijig/middleware" + "rijig/utils" + + "github.com/gofiber/fiber/v2" +) + +func AboutRouter(api fiber.Router) { + + aboutRepo := repositories.NewAboutRepository(config.DB) + aboutService := services.NewAboutService(aboutRepo) + aboutHandler := handler.NewAboutHandler(aboutService) + + aboutRoutes := api.Group("/about") + aboutRoute := api.Group("/about") + aboutRoutes.Use(middleware.AuthMiddleware, middleware.RoleMiddleware(utils.RoleAdministrator)) + + aboutRoute.Get("/", aboutHandler.GetAllAbout) + aboutRoute.Get("/:id", aboutHandler.GetAboutByID) + aboutRoutes.Post("/", aboutHandler.CreateAbout) + aboutRoutes.Put("/:id", aboutHandler.UpdateAbout) + aboutRoutes.Delete("/:id", aboutHandler.DeleteAbout) + + aboutDetailRoutes := api.Group("/about-detail") + aboutDetailRoutes.Use(middleware.AuthMiddleware, middleware.RoleMiddleware(utils.RoleAdministrator)) + aboutDetailRoute := api.Group("/about-detail") + aboutDetailRoute.Get("/:id", aboutHandler.GetAboutDetailById) + aboutDetailRoutes.Post("/", aboutHandler.CreateAboutDetail) + aboutDetailRoutes.Put("/:id", aboutHandler.UpdateAboutDetail) + aboutDetailRoutes.Delete("/:id", aboutHandler.DeleteAboutDetail) +} diff --git a/router/setup_routes.go.go b/router/setup_routes.go.go index 998a2dd..a1f0305 100644 --- a/router/setup_routes.go.go +++ b/router/setup_routes.go.go @@ -34,6 +34,7 @@ func SetupRoutes(app *fiber.App) { presentation.ArticleRouter(api) presentation.BannerRouter(api) presentation.InitialCointRoute(api) + presentation.AboutRouter(api) presentation.TrashRouter(api) presentation.StoreRouter(api) presentation.ProductRouter(api)