Class: LcpRuby::Kanban::DefaultProvider

Inherits:
HostProvider show all
Defined in:
lib/lcp_ruby/kanban/default_provider.rb

Overview

Default config-driven kanban provider. Resolves ‘kanban.group_by` to one of three modes — workflow / enum / belongs_to — and implements the full HostProvider contract on top of LCP’s existing workflow engine, enum metadata, and AR scope querying.

Instance Attribute Summary

Attributes inherited from HostProvider

#config, #model_definition

Instance Method Summary collapse

Methods inherited from HostProvider

#archive_column, #column_aggregate, #create_column, #create_in_column, #delete_column, #fetch_records_2d, #initialize, #rename_column, #swimlanes

Constructor Details

This class inherits a constructor from LcpRuby::Kanban::HostProvider

Instance Method Details

#allowed_targets(record:, user:) ⇒ Object



74
75
76
77
78
79
80
81
# File 'lib/lcp_ruby/kanban/default_provider.rb', line 74

def allowed_targets(record:, user:)
  return nil unless mode == :workflow
  evaluator = permission_evaluator_for(user)
  return nil if evaluator.nil?
  return nil unless record.respond_to?(:available_transitions)

  record.available_transitions(user: user, evaluator: evaluator).map { |t| t.to.to_s }
end

#can_move?(record:, user:) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/lcp_ruby/kanban/default_provider.rb', line 58

def can_move?(record:, user:)
  # An active approval request locks the workflow regardless of role; check first.
  if record.respond_to?(:active_approval_request) && record.active_approval_request.present?
    return false
  end
  # Workflow readonly_fields lock is enforced even without a permission
  # evaluator (e.g. no permission definition for the model).
  if Workflow::Registry.readonly_fields_for(record, model_name: model_name)
                       .map(&:to_s).include?(group_by_column.to_s)
    return false
  end
  evaluator = permission_evaluator_for(user)
  return true if evaluator.nil?
  evaluator.field_writable?(group_by_column.to_s, record)
end

#columns(scope:, user:, context:) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/lcp_ruby/kanban/default_provider.rb', line 8

def columns(scope:, user:, context:)
  @column_counts = scope.group(group_by_column).count

  base = case mode
  when :workflow then workflow_columns
  when :enum then enum_columns
  when :belongs_to then belongs_to_columns
  else
           raise ArgumentError, "DefaultProvider: kanban.group_by '#{group_by}' " \
                                "does not resolve on model '#{model_name}'. The validator " \
                                "should have caught this at boot."
  end

  # `_null` is always first per spec (kanban_view.md edge case #2). `_other` is appended.
  leading = null_column_or_nil
  trailing = other_column_or_nil
  [ leading, *base, trailing ].compact
end

#fetch_records(scope:, columns:, user:, context:) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/lcp_ruby/kanban/default_provider.rb', line 27

def fetch_records(scope:, columns:, user:, context:)
  page = (context.is_a?(Hash) ? context[:kanban_page] : nil).to_i
  page = 1 if page < 1
  cards = (config["cards_per_column"] || 20).to_i

  # Cumulative pagination: page=1 returns the first N cards, page=2
  # returns the first 2N cards, etc. The "Load more" Turbo Frame swap
  # replaces the column body with the cumulative slice — semantically
  # "show me one more page worth", which is the standard Load-more UX.
  # Avoids client-side accumulation logic.
  limit = page * cards

  columns.each_with_object({}) do |col, h|
    h[col.value] = column_query(scope, col)
                   .order(within_column_order_clause)
                   .limit(limit)
                   .to_a
  end
end

#move(record:, target_column:, target_swimlane: nil, target_position: nil, user:, comment: nil) ⇒ Object



47
48
49
50
51
52
53
54
55
56
# File 'lib/lcp_ruby/kanban/default_provider.rb', line 47

def move(record:, target_column:, target_swimlane: nil, target_position: nil, user:, comment: nil)
  case mode
  when :workflow
    workflow_move(record: record, target_column: target_column, user: user, comment: comment)
  when :enum, :belongs_to
    field_move(record: record, target_column: target_column, target_position: target_position)
  else
    MoveResult.error(code: :unknown, message: "DefaultProvider could not resolve kanban.group_by mode")
  end
end