diff --git a/internal/contract/letter_outgoing_contract.go b/internal/contract/letter_outgoing_contract.go index d50dd8f..82c5c3d 100644 --- a/internal/contract/letter_outgoing_contract.go +++ b/internal/contract/letter_outgoing_contract.go @@ -245,7 +245,8 @@ type ApprovalFlowStepResponse struct { type ListApprovalFlowsRequest struct { Limit int `json:"limit"` - Offset int `json:"offset"` + Page int `json:"page"` + Search *string `json:"search"` DepartmentID *uuid.UUID `json:"department_id,omitempty"` IsActive *bool `json:"is_active,omitempty"` } diff --git a/internal/handler/admin_approval_flow_handler.go b/internal/handler/admin_approval_flow_handler.go index a1a682d..6ff6552 100644 --- a/internal/handler/admin_approval_flow_handler.go +++ b/internal/handler/admin_approval_flow_handler.go @@ -159,42 +159,47 @@ func (h *AdminApprovalFlowHandler) DeleteApprovalFlow(c *gin.Context) { } func (h *AdminApprovalFlowHandler) ListApprovalFlows(c *gin.Context) { + // Parse query params page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) - offset := (page - 1) * limit - - departmentIDStr := c.Query("department_id") - isActiveStr := c.Query("is_active") - + + // Parse department_id var departmentID *uuid.UUID - var isActive *bool - - if departmentIDStr != "" { + if departmentIDStr := c.Query("department_id"); departmentIDStr != "" { if id, err := uuid.Parse(departmentIDStr); err == nil { departmentID = &id } } - - if isActiveStr != "" { - if isActiveStr == "true" { - active := true - isActive = &active - } else if isActiveStr == "false" { - active := false + + // Parse is_active + var isActive *bool + if isActiveStr := c.Query("is_active"); isActiveStr != "" { + if active, err := strconv.ParseBool(isActiveStr); err == nil { isActive = &active } } + + // Parse search + var search *string + if searchStr := c.Query("search"); searchStr != "" { + search = &searchStr + } + // Build request - pass PAGE, bukan OFFSET req := &contract.ListApprovalFlowsRequest{ + Page: page, // ✅ Pass page number Limit: limit, - Offset: offset, DepartmentID: departmentID, IsActive: isActive, + Search: search, // tambahkan ini juga } resp, err := h.svc.ListApprovalFlows(c.Request.Context(), req) if err != nil { - c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError}) + c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{ + Error: err.Error(), + Code: http.StatusInternalServerError, + }) return } @@ -210,7 +215,7 @@ func (h *AdminApprovalFlowHandler) ListApprovalFlowsByDepartment(c *gin.Context) req := &contract.ListApprovalFlowsRequest{ Limit: limit, - Offset: offset, + Page: offset, DepartmentID: &appCtx.DepartmentID, } diff --git a/internal/processor/notification_processor.go b/internal/processor/notification_processor.go index f2d19b0..79c04b9 100644 --- a/internal/processor/notification_processor.go +++ b/internal/processor/notification_processor.go @@ -368,7 +368,7 @@ func (p *NotificationProcessorImpl) SendOutgoingLetterNotification(ctx context.C } // Build notification URL for outgoing letters - url := fmt.Sprintf("/en/apps/surat-menyurat/keluar-detail/%s", letterID.String()) + url := fmt.Sprintf("/en/apps/surat-keluar-detail/%s", letterID.String()) // Use workflow ID from config (defaults to "notification-dashbpard") workflowID := p.workflowID diff --git a/internal/repository/approval_flow_repository.go b/internal/repository/approval_flow_repository.go index 43fee08..73b6cab 100644 --- a/internal/repository/approval_flow_repository.go +++ b/internal/repository/approval_flow_repository.go @@ -66,37 +66,64 @@ func (r *ApprovalFlowRepository) Delete(ctx context.Context, id uuid.UUID) error type ListApprovalFlowsFilter struct { DepartmentID *uuid.UUID + Search *string IsActive *bool } func (r *ApprovalFlowRepository) List(ctx context.Context, filter ListApprovalFlowsFilter, limit, offset int) ([]entities.ApprovalFlow, int64, error) { - db := DBFromContext(ctx, r.db) - query := db.WithContext(ctx).Model(&entities.ApprovalFlow{}) + var list []entities.ApprovalFlow + var total int64 + + // Build base query for counting + countQuery := r.db.WithContext(ctx).Model(&entities.ApprovalFlow{}) if filter.DepartmentID != nil { - query = query.Where("department_id = ?", *filter.DepartmentID) - } - if filter.IsActive != nil { - query = query.Where("is_active = ?", *filter.IsActive) + countQuery = countQuery.Where("department_id = ?", *filter.DepartmentID) } - var total int64 - if err := query.Count(&total).Error; err != nil { + if filter.IsActive != nil { + countQuery = countQuery.Where("is_active = ?", *filter.IsActive) + } + + if filter.Search != nil && *filter.Search != "" { + like := "%" + *filter.Search + "%" + countQuery = countQuery.Where("name ILIKE ? OR description ILIKE ?", like, like) + } + + // Get total count + if err := countQuery.Count(&total).Error; err != nil { return nil, 0, err } + + // Build query for fetching data - BUAT QUERY BARU DARI AWAL + dataQuery := r.db.WithContext(ctx).Model(&entities.ApprovalFlow{}) - var list []entities.ApprovalFlow - if err := query. + if filter.DepartmentID != nil { + dataQuery = dataQuery.Where("department_id = ?", *filter.DepartmentID) + } + + if filter.IsActive != nil { + dataQuery = dataQuery.Where("is_active = ?", *filter.IsActive) + } + + if filter.Search != nil && *filter.Search != "" { + like := "%" + *filter.Search + "%" + dataQuery = dataQuery.Where("name ILIKE ? OR description ILIKE ?", like, like) + } + + // Fetch data with pagination and preloads + if err := dataQuery. + Order("created_at DESC"). + Limit(limit). + Offset(offset). Preload("Department"). Preload("Steps", func(db *gorm.DB) *gorm.DB { return db.Order("step_order ASC, parallel_group ASC") }). - Order("created_at DESC"). - Limit(limit). - Offset(offset). Find(&list).Error; err != nil { return nil, 0, err } + return list, total, nil } diff --git a/internal/service/approval_flow_service.go b/internal/service/approval_flow_service.go index 1377d09..23e8b61 100644 --- a/internal/service/approval_flow_service.go +++ b/internal/service/approval_flow_service.go @@ -173,10 +173,26 @@ func (s *ApprovalFlowServiceImpl) DeleteApprovalFlow(ctx context.Context, id uui func (s *ApprovalFlowServiceImpl) ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error) { filter := repository.ListApprovalFlowsFilter{ DepartmentID: req.DepartmentID, + Search: req.Search, IsActive: req.IsActive, } + + page := req.Page + if page <= 0 { + page = 1 + } - flows, total, err := s.flowRepo.List(ctx, filter, req.Limit, req.Offset) + limit := req.Limit + if limit <= 0 { + limit = 10 + } + if limit > 100 { + limit = 100 // Max limit to prevent performance issues + } + + offset := (page - 1) * limit + + flows, total, err := s.flowRepo.List(ctx, filter, limit, offset) if err != nil { return nil, err }