package http import ( "net/http" "strconv" "strings" "time" "unicode/utf8" "primaya-api/cpone-middleware/internal/repository" ) type TarifKerjasamaPaginationResponse struct { CurrentPage int `json:"current_page"` Data []repository.TarifKerjasamaSummary `json:"data"` FirstPageURL string `json:"first_page_url"` From *int `json:"from"` LastPage int `json:"last_page"` LastPageURL string `json:"last_page_url"` Links map[string]any `json:"links"` NextPageURL *string `json:"next_page_url"` Total int `json:"total"` PerPage int `json:"per_page"` } type TarifKerjasamaResponse struct { Success bool `json:"success"` Status string `json:"status"` Message string `json:"message"` Data any `json:"data"` } func (h Handler) ListTarifKerjasama(w http.ResponseWriter, r *http.Request) { params, message := parseTarifKerjasamaListParams(r) if message != "" { writeTarifKerjasamaValidationError(w, message) return } page, err := h.tarifRepository(r.Context()).ListTarifKerjasama(r.Context(), params) if err != nil { writeTarifKerjasamaInternalError(w) return } lastPage := (page.Total + params.PerPage - 1) / params.PerPage if lastPage < 1 { lastPage = 1 } var from *int if len(page.Items) > 0 { value := (params.Page-1)*params.PerPage + 1 from = &value } var nextPageURL *string if params.Page < lastPage { value := currentRequestURL(r, params.Page+1) nextPageURL = &value } WriteJSON(w, http.StatusOK, TarifKerjasamaPaginationResponse{ CurrentPage: params.Page, Data: page.Items, FirstPageURL: "", From: from, LastPage: lastPage, LastPageURL: "", Links: map[string]any{}, NextPageURL: nextPageURL, Total: page.Total, PerPage: params.PerPage, }) } func (h Handler) GetTarifKerjasama(w http.ResponseWriter, r *http.Request) { id, params, message := parseTarifKerjasamaDetailParams(r) if message != "" { writeTarifKerjasamaValidationError(w, message) return } detail, err := h.tarifRepository(r.Context()).GetTarifKerjasama(r.Context(), id, params) if err != nil { writeTarifKerjasamaInternalError(w) return } if detail == nil { WriteJSON(w, http.StatusNotFound, TarifKerjasamaResponse{ Success: false, Status: "tarif_kerjasama_not_found", Message: "Master tarif kerja sama tidak ditemukan", Data: nil, }) return } WriteJSON(w, http.StatusOK, TarifKerjasamaResponse{ Success: true, Status: "tarif_kerjasama_found", Message: "Data tarif kerja sama berhasil diambil", Data: detail, }) } func parseTarifKerjasamaListParams(r *http.Request) (repository.TarifKerjasamaListParams, string) { query := r.URL.Query() params := repository.TarifKerjasamaListParams{ Search: strings.TrimSpace(query.Get("search")), PerusahaanID: strings.TrimSpace(query.Get("perusahaan_id")), Page: 1, PerPage: 15, } if utf8.RuneCountInString(params.Search) > 255 { return params, "The search may not be greater than 255 characters." } if utf8.RuneCountInString(params.PerusahaanID) > 50 { return params, "The perusahaan id may not be greater than 50 characters." } if raw, exists := firstQueryValue(r, "tarif_id"); exists && raw != "" { value, err := positiveInt64(raw) if err != nil { return params, "The tarif id must be a positive integer." } params.TarifID = &value } if raw, exists := firstQueryValue(r, "page"); exists && raw != "" { value, err := strconv.Atoi(raw) if err != nil || value < 1 { return params, "The page must be at least 1." } params.Page = value } if raw, exists := firstQueryValue(r, "per_page"); exists && raw != "" { value, err := strconv.Atoi(raw) if err != nil || value < 1 || value > 100 { return params, "The per page must be between 1 and 100." } params.PerPage = value } return params, "" } func parseTarifKerjasamaDetailParams(r *http.Request) (int64, repository.TarifKerjasamaDetailParams, string) { params := repository.TarifKerjasamaDetailParams{Active: "N"} id, err := positiveInt64(strings.TrimSpace(r.PathValue("id"))) if err != nil { return 0, params, "The id must be a positive integer." } if raw, exists := firstQueryValue(r, "group_tarif_id"); exists && raw != "" { value, err := positiveInt64(raw) if err != nil { return 0, params, "The group tarif id must be a positive integer." } params.GroupTarifID = &value } if raw, exists := firstQueryValue(r, "active"); exists && raw != "" { if raw != "N" && raw != "Y" { return 0, params, "The selected active is invalid." } params.Active = raw } params.EffectiveDate = strings.TrimSpace(r.URL.Query().Get("effective_date")) if params.EffectiveDate != "" { if _, err := time.Parse("2006-01-02", params.EffectiveDate); err != nil { return 0, params, "The effective date does not match the format Y-m-d." } } return id, params, "" } func positiveInt64(raw string) (int64, error) { value, err := strconv.ParseInt(strings.TrimSpace(raw), 10, 64) if err != nil || value < 1 { return 0, strconv.ErrSyntax } return value, nil } func writeTarifKerjasamaValidationError(w http.ResponseWriter, message string) { WriteJSON(w, http.StatusUnprocessableEntity, TarifKerjasamaResponse{ Success: false, Status: "validation_error", Message: message, Data: nil, }) } func writeTarifKerjasamaInternalError(w http.ResponseWriter) { WriteJSON(w, http.StatusInternalServerError, Response{ Success: false, Message: "internal server error", Data: nil, }) }