add pagination approval flows

This commit is contained in:
efrilm 2025-10-10 19:02:52 +07:00
parent 239f052a9a
commit 3717b0b53c
5 changed files with 83 additions and 34 deletions

View File

@ -245,7 +245,8 @@ type ApprovalFlowStepResponse struct {
type ListApprovalFlowsRequest struct { type ListApprovalFlowsRequest struct {
Limit int `json:"limit"` Limit int `json:"limit"`
Offset int `json:"offset"` Page int `json:"page"`
Search *string `json:"search"`
DepartmentID *uuid.UUID `json:"department_id,omitempty"` DepartmentID *uuid.UUID `json:"department_id,omitempty"`
IsActive *bool `json:"is_active,omitempty"` IsActive *bool `json:"is_active,omitempty"`
} }

View File

@ -159,42 +159,47 @@ func (h *AdminApprovalFlowHandler) DeleteApprovalFlow(c *gin.Context) {
} }
func (h *AdminApprovalFlowHandler) ListApprovalFlows(c *gin.Context) { func (h *AdminApprovalFlowHandler) ListApprovalFlows(c *gin.Context) {
// Parse query params
page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
offset := (page - 1) * limit
// Parse department_id
departmentIDStr := c.Query("department_id")
isActiveStr := c.Query("is_active")
var departmentID *uuid.UUID var departmentID *uuid.UUID
var isActive *bool if departmentIDStr := c.Query("department_id"); departmentIDStr != "" {
if departmentIDStr != "" {
if id, err := uuid.Parse(departmentIDStr); err == nil { if id, err := uuid.Parse(departmentIDStr); err == nil {
departmentID = &id departmentID = &id
} }
} }
if isActiveStr != "" { // Parse is_active
if isActiveStr == "true" { var isActive *bool
active := true if isActiveStr := c.Query("is_active"); isActiveStr != "" {
isActive = &active if active, err := strconv.ParseBool(isActiveStr); err == nil {
} else if isActiveStr == "false" {
active := false
isActive = &active isActive = &active
} }
} }
// Parse search
var search *string
if searchStr := c.Query("search"); searchStr != "" {
search = &searchStr
}
// Build request - pass PAGE, bukan OFFSET
req := &contract.ListApprovalFlowsRequest{ req := &contract.ListApprovalFlowsRequest{
Page: page, // ✅ Pass page number
Limit: limit, Limit: limit,
Offset: offset,
DepartmentID: departmentID, DepartmentID: departmentID,
IsActive: isActive, IsActive: isActive,
Search: search, // tambahkan ini juga
} }
resp, err := h.svc.ListApprovalFlows(c.Request.Context(), req) resp, err := h.svc.ListApprovalFlows(c.Request.Context(), req)
if err != nil { 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 return
} }
@ -210,7 +215,7 @@ func (h *AdminApprovalFlowHandler) ListApprovalFlowsByDepartment(c *gin.Context)
req := &contract.ListApprovalFlowsRequest{ req := &contract.ListApprovalFlowsRequest{
Limit: limit, Limit: limit,
Offset: offset, Page: offset,
DepartmentID: &appCtx.DepartmentID, DepartmentID: &appCtx.DepartmentID,
} }

View File

@ -368,7 +368,7 @@ func (p *NotificationProcessorImpl) SendOutgoingLetterNotification(ctx context.C
} }
// Build notification URL for outgoing letters // 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") // Use workflow ID from config (defaults to "notification-dashbpard")
workflowID := p.workflowID workflowID := p.workflowID

View File

@ -66,37 +66,64 @@ func (r *ApprovalFlowRepository) Delete(ctx context.Context, id uuid.UUID) error
type ListApprovalFlowsFilter struct { type ListApprovalFlowsFilter struct {
DepartmentID *uuid.UUID DepartmentID *uuid.UUID
Search *string
IsActive *bool IsActive *bool
} }
func (r *ApprovalFlowRepository) List(ctx context.Context, filter ListApprovalFlowsFilter, limit, offset int) ([]entities.ApprovalFlow, int64, error) { func (r *ApprovalFlowRepository) List(ctx context.Context, filter ListApprovalFlowsFilter, limit, offset int) ([]entities.ApprovalFlow, int64, error) {
db := DBFromContext(ctx, r.db) var list []entities.ApprovalFlow
query := db.WithContext(ctx).Model(&entities.ApprovalFlow{}) var total int64
// Build base query for counting
countQuery := r.db.WithContext(ctx).Model(&entities.ApprovalFlow{})
if filter.DepartmentID != nil { if filter.DepartmentID != nil {
query = query.Where("department_id = ?", *filter.DepartmentID) countQuery = countQuery.Where("department_id = ?", *filter.DepartmentID)
}
if filter.IsActive != nil {
query = query.Where("is_active = ?", *filter.IsActive)
} }
var total int64 if filter.IsActive != nil {
if err := query.Count(&total).Error; err != 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 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 filter.DepartmentID != nil {
if err := query. 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("Department").
Preload("Steps", func(db *gorm.DB) *gorm.DB { Preload("Steps", func(db *gorm.DB) *gorm.DB {
return db.Order("step_order ASC, parallel_group ASC") return db.Order("step_order ASC, parallel_group ASC")
}). }).
Order("created_at DESC").
Limit(limit).
Offset(offset).
Find(&list).Error; err != nil { Find(&list).Error; err != nil {
return nil, 0, err return nil, 0, err
} }
return list, total, nil return list, total, nil
} }

View File

@ -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) { func (s *ApprovalFlowServiceImpl) ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error) {
filter := repository.ListApprovalFlowsFilter{ filter := repository.ListApprovalFlowsFilter{
DepartmentID: req.DepartmentID, DepartmentID: req.DepartmentID,
Search: req.Search,
IsActive: req.IsActive, 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 { if err != nil {
return nil, err return nil, err
} }