ruby_llm-agents 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +9 -3
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +58 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +59 -16
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +144 -20
- data/app/controllers/ruby_llm/agents/executions_controller.rb +13 -16
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +279 -90
- data/app/helpers/ruby_llm/agents/application_helper.rb +100 -0
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +84 -0
- data/app/mailers/ruby_llm/agents/application_mailer.rb +28 -0
- data/app/models/ruby_llm/agents/execution/analytics.rb +170 -20
- data/app/models/ruby_llm/agents/execution/scopes.rb +0 -31
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -129
- data/app/models/ruby_llm/agents/execution.rb +50 -14
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +277 -0
- data/app/models/ruby_llm/agents/tenant/configurable.rb +135 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +310 -0
- data/app/models/ruby_llm/agents/tenant.rb +146 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +12 -253
- data/app/services/ruby_llm/agents/agent_registry.rb +18 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +72 -76
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -12
- data/app/views/ruby_llm/agents/agents/_sortable_header.html.erb +56 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +5 -15
- data/app/views/ruby_llm/agents/agents/index.html.erb +271 -100
- data/app/views/ruby_llm/agents/agents/show.html.erb +1 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +107 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +18 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +66 -359
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +56 -0
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +115 -0
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +35 -60
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +17 -6
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +373 -72
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +0 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +51 -39
- data/app/views/ruby_llm/agents/executions/_list.html.erb +53 -195
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +5 -20
- data/app/views/ruby_llm/agents/executions/index.html.erb +7 -83
- data/app/views/ruby_llm/agents/executions/show.html.erb +10 -20
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +2 -1
- data/app/views/ruby_llm/agents/shared/_doc_link.html.erb +12 -0
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +3 -15
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_sortable_header.html.erb +53 -0
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +7 -0
- data/app/views/ruby_llm/agents/shared/_status_dot.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +9 -35
- data/app/views/ruby_llm/agents/system_config/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/tenants/index.html.erb +4 -1
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +7 -15
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +539 -0
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +920 -0
- data/app/views/ruby_llm/agents/workflows/index.html.erb +179 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +164 -139
- data/config/routes.rb +1 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +6 -36
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +5 -38
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +8 -41
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +18 -46
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/install_generator.rb +33 -56
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +480 -0
- data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +42 -22
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +2 -2
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +8 -39
- data/lib/generators/ruby_llm_agents/templates/add_tenant_to_executions_migration.rb.tt +13 -2
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +5 -8
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +40 -42
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +24 -26
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +31 -33
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +125 -127
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +20 -18
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +38 -40
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +42 -44
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +48 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/create_tenant_budgets_migration.rb.tt +11 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +72 -0
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +25 -27
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +17 -19
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/rename_tenant_budgets_to_tenants_migration.rb.tt +34 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +87 -24
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +21 -27
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +46 -54
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +31 -39
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +22 -28
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +53 -63
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +46 -56
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +22 -30
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +38 -46
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +7 -7
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +59 -71
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +274 -23
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +29 -31
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +28 -30
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +10 -43
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +26 -0
- data/lib/ruby_llm/agents/core/configuration.rb +55 -43
- data/lib/ruby_llm/agents/core/llm_tenant.rb +60 -60
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +26 -0
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +4 -2
- data/lib/ruby_llm/agents/pipeline.rb +69 -0
- data/lib/ruby_llm/agents/workflow/approval.rb +205 -0
- data/lib/ruby_llm/agents/workflow/approval_store.rb +179 -0
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +467 -0
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +244 -0
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +289 -0
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +107 -0
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +150 -0
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +187 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +352 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +415 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +257 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +317 -0
- data/lib/ruby_llm/agents/workflow/dsl.rb +576 -0
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +2 -7
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +180 -0
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +121 -0
- data/lib/ruby_llm/agents/workflow/notifiers.rb +70 -0
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +190 -23
- data/lib/ruby_llm/agents/workflow/result.rb +202 -0
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +206 -0
- data/lib/ruby_llm/agents/workflow/wait_result.rb +213 -0
- metadata +43 -6
- data/app/views/ruby_llm/agents/dashboard/_execution_item.html.erb +0 -66
- data/lib/ruby_llm/agents/workflow/parallel.rb +0 -299
- data/lib/ruby_llm/agents/workflow/pipeline.rb +0 -306
- data/lib/ruby_llm/agents/workflow/router.rb +0 -429
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
<%
|
|
2
|
+
# DSL-based workflow structure visualization
|
|
3
|
+
# Shows steps with support for routing (branching) and parallel groups
|
|
4
|
+
steps = local_assigns[:steps] || []
|
|
5
|
+
parallel_groups = local_assigns[:parallel_groups] || []
|
|
6
|
+
input_schema_fields = local_assigns[:input_schema_fields] || {}
|
|
7
|
+
%>
|
|
8
|
+
|
|
9
|
+
<% if steps.any? %>
|
|
10
|
+
<!-- Visual DSL Workflow Flow -->
|
|
11
|
+
<div class="py-4 overflow-x-auto">
|
|
12
|
+
<div class="flex items-start justify-center gap-2 min-w-max px-4">
|
|
13
|
+
<%
|
|
14
|
+
# Group steps: sequential steps, parallel groups, and wait steps
|
|
15
|
+
current_parallel_group = nil
|
|
16
|
+
grouped_items = []
|
|
17
|
+
|
|
18
|
+
steps.each do |step|
|
|
19
|
+
# Check if this is a wait step
|
|
20
|
+
if step[:type] == :wait
|
|
21
|
+
current_parallel_group = nil
|
|
22
|
+
grouped_items << { type: :wait, step: step }
|
|
23
|
+
elsif step[:parallel_group].present?
|
|
24
|
+
pg = parallel_groups.find { |g| g[:name] == step[:parallel_group] }
|
|
25
|
+
if current_parallel_group != step[:parallel_group]
|
|
26
|
+
current_parallel_group = step[:parallel_group]
|
|
27
|
+
grouped_items << { type: :parallel_group, group: pg, steps: [step] }
|
|
28
|
+
else
|
|
29
|
+
grouped_items.last[:steps] << step
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
current_parallel_group = nil
|
|
33
|
+
grouped_items << { type: :step, step: step }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
%>
|
|
37
|
+
|
|
38
|
+
<% grouped_items.each_with_index do |item, index| %>
|
|
39
|
+
<% if item[:type] == :wait %>
|
|
40
|
+
<!-- Wait Step Box -->
|
|
41
|
+
<% step = item[:step] %>
|
|
42
|
+
<%
|
|
43
|
+
wait_type = step[:wait_type]
|
|
44
|
+
case wait_type
|
|
45
|
+
when :delay
|
|
46
|
+
border_color = "border-teal-300 dark:border-teal-600"
|
|
47
|
+
bg_color = "bg-teal-50 dark:bg-teal-900/30"
|
|
48
|
+
text_color = "text-teal-700 dark:text-teal-300"
|
|
49
|
+
icon_svg = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>'
|
|
50
|
+
type_label = "delay"
|
|
51
|
+
when :until
|
|
52
|
+
border_color = "border-cyan-300 dark:border-cyan-600"
|
|
53
|
+
bg_color = "bg-cyan-50 dark:bg-cyan-900/30"
|
|
54
|
+
text_color = "text-cyan-700 dark:text-cyan-300"
|
|
55
|
+
icon_svg = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>'
|
|
56
|
+
type_label = "poll"
|
|
57
|
+
when :schedule
|
|
58
|
+
border_color = "border-indigo-300 dark:border-indigo-600"
|
|
59
|
+
bg_color = "bg-indigo-50 dark:bg-indigo-900/30"
|
|
60
|
+
text_color = "text-indigo-700 dark:text-indigo-300"
|
|
61
|
+
icon_svg = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>'
|
|
62
|
+
type_label = "scheduled"
|
|
63
|
+
when :approval
|
|
64
|
+
border_color = "border-orange-300 dark:border-orange-600"
|
|
65
|
+
bg_color = "bg-orange-50 dark:bg-orange-900/30"
|
|
66
|
+
text_color = "text-orange-700 dark:text-orange-300"
|
|
67
|
+
icon_svg = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>'
|
|
68
|
+
type_label = "approval"
|
|
69
|
+
else
|
|
70
|
+
border_color = "border-gray-300 dark:border-gray-600"
|
|
71
|
+
bg_color = "bg-gray-50 dark:bg-gray-900/30"
|
|
72
|
+
text_color = "text-gray-700 dark:text-gray-300"
|
|
73
|
+
icon_svg = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>'
|
|
74
|
+
type_label = "wait"
|
|
75
|
+
end
|
|
76
|
+
%>
|
|
77
|
+
<div class="flex flex-col items-center">
|
|
78
|
+
<div class="w-28 h-16 flex flex-col items-center justify-center rounded-lg border-2 border-dashed <%= border_color %> <%= bg_color %> relative">
|
|
79
|
+
<!-- Wait type icon -->
|
|
80
|
+
<div class="absolute -top-2 -right-2 <%= text_color %>">
|
|
81
|
+
<%= raw icon_svg %>
|
|
82
|
+
</div>
|
|
83
|
+
<span class="text-sm font-semibold <%= text_color %> truncate max-w-24 px-1">
|
|
84
|
+
<%= step[:ui_label] || step[:name].to_s.gsub(/_/, ' ').titleize %>
|
|
85
|
+
</span>
|
|
86
|
+
<% if step[:duration] %>
|
|
87
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400">
|
|
88
|
+
<%= step[:duration].is_a?(Numeric) ? "#{step[:duration]}s" : step[:duration] %>
|
|
89
|
+
</span>
|
|
90
|
+
<% elsif step[:timeout] %>
|
|
91
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400">
|
|
92
|
+
timeout: <%= step[:timeout].is_a?(Numeric) ? "#{step[:timeout]}s" : step[:timeout] %>
|
|
93
|
+
</span>
|
|
94
|
+
<% end %>
|
|
95
|
+
</div>
|
|
96
|
+
<span class="text-xs <%= text_color %> mt-1">
|
|
97
|
+
<%= type_label %>
|
|
98
|
+
</span>
|
|
99
|
+
</div>
|
|
100
|
+
<% elsif item[:type] == :step %>
|
|
101
|
+
<% step = item[:step] %>
|
|
102
|
+
<!-- Single Step Box -->
|
|
103
|
+
<div class="flex flex-col items-center">
|
|
104
|
+
<%
|
|
105
|
+
# Determine step styling based on type
|
|
106
|
+
if step[:routing]
|
|
107
|
+
border_color = "border-amber-300 dark:border-amber-600"
|
|
108
|
+
bg_color = "bg-amber-50 dark:bg-amber-900/30"
|
|
109
|
+
text_color = "text-amber-700 dark:text-amber-300"
|
|
110
|
+
icon_color = "text-amber-400 dark:text-amber-500"
|
|
111
|
+
else
|
|
112
|
+
border_color = "border-indigo-200 dark:border-indigo-700"
|
|
113
|
+
bg_color = "bg-indigo-50 dark:bg-indigo-900/30"
|
|
114
|
+
text_color = "text-indigo-700 dark:text-indigo-300"
|
|
115
|
+
icon_color = "text-indigo-400 dark:text-indigo-500"
|
|
116
|
+
end
|
|
117
|
+
%>
|
|
118
|
+
<div class="w-28 h-16 flex flex-col items-center justify-center rounded-lg border-2 <%= border_color %> <%= bg_color %> <%= step[:optional] ? 'border-dashed' : '' %> relative">
|
|
119
|
+
<% if step[:routing] %>
|
|
120
|
+
<!-- Routing indicator -->
|
|
121
|
+
<div class="absolute -top-2 -right-2">
|
|
122
|
+
<svg class="w-4 h-4 <%= icon_color %>" fill="currentColor" viewBox="0 0 20 20">
|
|
123
|
+
<path fill-rule="evenodd" d="M7.707 3.293a1 1 0 010 1.414L5.414 7H11a7 7 0 017 7v2a1 1 0 11-2 0v-2a5 5 0 00-5-5H5.414l2.293 2.293a1 1 0 11-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
|
124
|
+
</svg>
|
|
125
|
+
</div>
|
|
126
|
+
<% end %>
|
|
127
|
+
<span class="text-sm font-semibold <%= text_color %> truncate max-w-24 px-1">
|
|
128
|
+
<%= step[:name] %>
|
|
129
|
+
</span>
|
|
130
|
+
<% if step[:ui_label].present? %>
|
|
131
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400 truncate max-w-24">
|
|
132
|
+
<%= step[:ui_label] %>
|
|
133
|
+
</span>
|
|
134
|
+
<% end %>
|
|
135
|
+
</div>
|
|
136
|
+
<span class="text-xs text-gray-500 dark:text-gray-400 mt-1 truncate max-w-28" title="<%= step[:agent] %>">
|
|
137
|
+
<%= step[:agent]&.to_s&.gsub(/Agent$/, '') %>
|
|
138
|
+
</span>
|
|
139
|
+
<% if step[:optional] %>
|
|
140
|
+
<span class="text-[10px] text-gray-400 dark:text-gray-500 italic">(optional)</span>
|
|
141
|
+
<% end %>
|
|
142
|
+
</div>
|
|
143
|
+
<% else %>
|
|
144
|
+
<!-- Parallel Group Box -->
|
|
145
|
+
<div class="flex flex-col items-center">
|
|
146
|
+
<div class="relative border-2 border-dashed border-purple-300 dark:border-purple-600 rounded-lg p-3 bg-purple-50/50 dark:bg-purple-900/20">
|
|
147
|
+
<!-- Fork indicator at top -->
|
|
148
|
+
<div class="absolute -top-3 left-1/2 transform -translate-x-1/2">
|
|
149
|
+
<div class="bg-purple-100 dark:bg-purple-800 rounded-full px-2 py-0.5">
|
|
150
|
+
<span class="text-[10px] font-medium text-purple-700 dark:text-purple-300">parallel</span>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div class="flex items-center gap-2 mt-1">
|
|
155
|
+
<% item[:steps].each_with_index do |step, step_idx| %>
|
|
156
|
+
<div class="flex flex-col items-center">
|
|
157
|
+
<div class="w-24 h-14 flex flex-col items-center justify-center rounded-lg border border-purple-200 dark:border-purple-700 bg-white dark:bg-gray-800 <%= step[:optional] ? 'border-dashed' : '' %>">
|
|
158
|
+
<span class="text-xs font-semibold text-purple-700 dark:text-purple-300 truncate max-w-20 px-1">
|
|
159
|
+
<%= step[:name] %>
|
|
160
|
+
</span>
|
|
161
|
+
</div>
|
|
162
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400 mt-0.5 truncate max-w-24">
|
|
163
|
+
<%= step[:agent]&.to_s&.gsub(/Agent$/, '') %>
|
|
164
|
+
</span>
|
|
165
|
+
</div>
|
|
166
|
+
<% end %>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<% if item[:group] %>
|
|
170
|
+
<div class="flex items-center justify-center gap-2 mt-2 text-[10px] text-purple-600 dark:text-purple-400">
|
|
171
|
+
<% if item[:group][:fail_fast] %>
|
|
172
|
+
<span class="bg-purple-100 dark:bg-purple-900/50 px-1.5 py-0.5 rounded">fail_fast</span>
|
|
173
|
+
<% end %>
|
|
174
|
+
<% if item[:group][:concurrency] %>
|
|
175
|
+
<span class="bg-purple-100 dark:bg-purple-900/50 px-1.5 py-0.5 rounded">max: <%= item[:group][:concurrency] %></span>
|
|
176
|
+
<% end %>
|
|
177
|
+
</div>
|
|
178
|
+
<% end %>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<% end %>
|
|
182
|
+
|
|
183
|
+
<!-- Arrow between items -->
|
|
184
|
+
<% unless index == grouped_items.length - 1 %>
|
|
185
|
+
<% next_item = grouped_items[index + 1] %>
|
|
186
|
+
<% current_step = item[:type] == :step ? item[:step] : nil %>
|
|
187
|
+
<% if current_step&.dig(:routing) %>
|
|
188
|
+
<!-- Branching arrow for routing steps -->
|
|
189
|
+
<div class="flex items-center text-amber-400 dark:text-amber-500 mt-4">
|
|
190
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
191
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7"/>
|
|
192
|
+
</svg>
|
|
193
|
+
</div>
|
|
194
|
+
<% else %>
|
|
195
|
+
<div class="flex items-center text-indigo-400 dark:text-indigo-500 mt-4">
|
|
196
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
197
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"/>
|
|
198
|
+
</svg>
|
|
199
|
+
</div>
|
|
200
|
+
<% end %>
|
|
201
|
+
<% end %>
|
|
202
|
+
<% end %>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<!-- Input Schema (if defined) -->
|
|
207
|
+
<% if input_schema_fields.present? %>
|
|
208
|
+
<div class="border-t border-gray-100 dark:border-gray-700 pt-4 mt-4">
|
|
209
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
210
|
+
Input Schema
|
|
211
|
+
</p>
|
|
212
|
+
|
|
213
|
+
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
|
|
214
|
+
<% input_schema_fields.each do |name, field| %>
|
|
215
|
+
<div class="flex items-start gap-2 py-2 px-3 rounded-lg bg-gray-50 dark:bg-gray-900/50">
|
|
216
|
+
<span class="w-2 h-2 rounded-full mt-1.5 <%= field[:required] ? 'bg-red-400' : 'bg-gray-300 dark:bg-gray-600' %>"></span>
|
|
217
|
+
<div class="flex-1 min-w-0">
|
|
218
|
+
<div class="flex items-center gap-1">
|
|
219
|
+
<span class="font-medium text-gray-700 dark:text-gray-300 text-sm truncate">
|
|
220
|
+
<%= name %>
|
|
221
|
+
</span>
|
|
222
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400">
|
|
223
|
+
<%= field[:type] %>
|
|
224
|
+
</span>
|
|
225
|
+
</div>
|
|
226
|
+
<% if field[:default].present? || field[:default] == false %>
|
|
227
|
+
<span class="text-[10px] text-gray-500 dark:text-gray-400">
|
|
228
|
+
default: <%= field[:default].inspect %>
|
|
229
|
+
</span>
|
|
230
|
+
<% end %>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
<% end %>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<% end %>
|
|
237
|
+
|
|
238
|
+
<!-- Step Details Table -->
|
|
239
|
+
<div class="border-t border-gray-100 dark:border-gray-700 pt-4 mt-4">
|
|
240
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
241
|
+
Step Details
|
|
242
|
+
</p>
|
|
243
|
+
|
|
244
|
+
<div class="space-y-2">
|
|
245
|
+
<% steps.each_with_index do |step, index| %>
|
|
246
|
+
<%
|
|
247
|
+
# Determine row styling
|
|
248
|
+
if step[:type] == :wait
|
|
249
|
+
case step[:wait_type]
|
|
250
|
+
when :delay
|
|
251
|
+
badge_bg = "bg-teal-100 dark:bg-teal-900/50"
|
|
252
|
+
badge_text = "text-teal-600 dark:text-teal-300"
|
|
253
|
+
when :until
|
|
254
|
+
badge_bg = "bg-cyan-100 dark:bg-cyan-900/50"
|
|
255
|
+
badge_text = "text-cyan-600 dark:text-cyan-300"
|
|
256
|
+
when :schedule
|
|
257
|
+
badge_bg = "bg-indigo-100 dark:bg-indigo-900/50"
|
|
258
|
+
badge_text = "text-indigo-600 dark:text-indigo-300"
|
|
259
|
+
when :approval
|
|
260
|
+
badge_bg = "bg-orange-100 dark:bg-orange-900/50"
|
|
261
|
+
badge_text = "text-orange-600 dark:text-orange-300"
|
|
262
|
+
else
|
|
263
|
+
badge_bg = "bg-gray-100 dark:bg-gray-900/50"
|
|
264
|
+
badge_text = "text-gray-600 dark:text-gray-300"
|
|
265
|
+
end
|
|
266
|
+
elsif step[:routing]
|
|
267
|
+
badge_bg = "bg-amber-100 dark:bg-amber-900/50"
|
|
268
|
+
badge_text = "text-amber-600 dark:text-amber-300"
|
|
269
|
+
elsif step[:parallel_group]
|
|
270
|
+
badge_bg = "bg-purple-100 dark:bg-purple-900/50"
|
|
271
|
+
badge_text = "text-purple-600 dark:text-purple-300"
|
|
272
|
+
else
|
|
273
|
+
badge_bg = "bg-indigo-100 dark:bg-indigo-900/50"
|
|
274
|
+
badge_text = "text-indigo-600 dark:text-indigo-300"
|
|
275
|
+
end
|
|
276
|
+
%>
|
|
277
|
+
<div class="flex items-start gap-3 py-2 px-3 rounded-lg bg-gray-50 dark:bg-gray-900/50">
|
|
278
|
+
<span class="w-6 h-6 flex items-center justify-center rounded-full <%= badge_bg %> text-xs font-medium <%= badge_text %>">
|
|
279
|
+
<%= index + 1 %>
|
|
280
|
+
</span>
|
|
281
|
+
<div class="flex-1 min-w-0">
|
|
282
|
+
<div class="flex items-center flex-wrap gap-2">
|
|
283
|
+
<span class="font-medium text-gray-700 dark:text-gray-300">
|
|
284
|
+
<%= step[:name] %>
|
|
285
|
+
</span>
|
|
286
|
+
<% if step[:type] == :wait %>
|
|
287
|
+
<span class="text-gray-400 dark:text-gray-500 text-sm">-></span>
|
|
288
|
+
<span class="text-sm <%= badge_text %> italic">
|
|
289
|
+
(<%= step[:wait_type] %> wait)
|
|
290
|
+
</span>
|
|
291
|
+
<% elsif step[:agent] %>
|
|
292
|
+
<span class="text-gray-400 dark:text-gray-500 text-sm">-></span>
|
|
293
|
+
<code class="text-sm font-mono text-gray-600 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded truncate max-w-xs">
|
|
294
|
+
<%= step[:agent] %>
|
|
295
|
+
</code>
|
|
296
|
+
<% else %>
|
|
297
|
+
<span class="text-gray-400 dark:text-gray-500 text-sm">-></span>
|
|
298
|
+
<span class="text-sm text-gray-500 dark:text-gray-400 italic">
|
|
299
|
+
(block)
|
|
300
|
+
</span>
|
|
301
|
+
<% end %>
|
|
302
|
+
|
|
303
|
+
<!-- Badges -->
|
|
304
|
+
<% if step[:routing] %>
|
|
305
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300">
|
|
306
|
+
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
|
307
|
+
<path fill-rule="evenodd" d="M7.707 3.293a1 1 0 010 1.414L5.414 7H11a7 7 0 017 7v2a1 1 0 11-2 0v-2a5 5 0 00-5-5H5.414l2.293 2.293a1 1 0 11-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
|
308
|
+
</svg>
|
|
309
|
+
routing
|
|
310
|
+
</span>
|
|
311
|
+
<% end %>
|
|
312
|
+
|
|
313
|
+
<% if step[:parallel_group] %>
|
|
314
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 dark:bg-purple-900/50 text-purple-700 dark:text-purple-300">
|
|
315
|
+
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
|
316
|
+
<path d="M10 3.5a1.5 1.5 0 013 0V4a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-.5a1.5 1.5 0 000 3h.5a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-.5a1.5 1.5 0 00-3 0v.5a1 1 0 01-1 1H6a1 1 0 01-1-1v-3a1 1 0 00-1-1h-.5a1.5 1.5 0 010-3H4a1 1 0 001-1V6a1 1 0 011-1h3a1 1 0 001-1v-.5z"/>
|
|
317
|
+
</svg>
|
|
318
|
+
<%= step[:parallel_group] %>
|
|
319
|
+
</span>
|
|
320
|
+
<% end %>
|
|
321
|
+
|
|
322
|
+
<% if step[:optional] %>
|
|
323
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
324
|
+
optional
|
|
325
|
+
</span>
|
|
326
|
+
<% end %>
|
|
327
|
+
|
|
328
|
+
<% if step[:timeout] %>
|
|
329
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300">
|
|
330
|
+
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
|
331
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
|
|
332
|
+
</svg>
|
|
333
|
+
<%= step[:timeout].is_a?(Numeric) ? "#{step[:timeout]}s" : step[:timeout] %>
|
|
334
|
+
</span>
|
|
335
|
+
<% end %>
|
|
336
|
+
|
|
337
|
+
<% if step[:type] == :wait %>
|
|
338
|
+
<% if step[:duration] %>
|
|
339
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-teal-100 dark:bg-teal-900/50 text-teal-700 dark:text-teal-300">
|
|
340
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
341
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
342
|
+
</svg>
|
|
343
|
+
<%= step[:duration].is_a?(Numeric) ? "#{step[:duration]}s" : step[:duration] %>
|
|
344
|
+
</span>
|
|
345
|
+
<% end %>
|
|
346
|
+
|
|
347
|
+
<% if step[:poll_interval] %>
|
|
348
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300">
|
|
349
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
350
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
351
|
+
</svg>
|
|
352
|
+
poll: <%= step[:poll_interval].is_a?(Numeric) ? "#{step[:poll_interval]}s" : step[:poll_interval] %>
|
|
353
|
+
</span>
|
|
354
|
+
<% end %>
|
|
355
|
+
|
|
356
|
+
<% if step[:on_timeout] %>
|
|
357
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900/50 text-yellow-700 dark:text-yellow-300">
|
|
358
|
+
on_timeout: <%= step[:on_timeout] %>
|
|
359
|
+
</span>
|
|
360
|
+
<% end %>
|
|
361
|
+
|
|
362
|
+
<% if step[:notify].present? %>
|
|
363
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300">
|
|
364
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
365
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
|
|
366
|
+
</svg>
|
|
367
|
+
notify: <%= Array(step[:notify]).join(", ") %>
|
|
368
|
+
</span>
|
|
369
|
+
<% end %>
|
|
370
|
+
|
|
371
|
+
<% if step[:approvers].present? %>
|
|
372
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300">
|
|
373
|
+
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
374
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
|
|
375
|
+
</svg>
|
|
376
|
+
<%= Array(step[:approvers]).size %> approver(s)
|
|
377
|
+
</span>
|
|
378
|
+
<% end %>
|
|
379
|
+
<% end %>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<% if step[:description].present? %>
|
|
383
|
+
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
|
384
|
+
<%= step[:description] %>
|
|
385
|
+
</p>
|
|
386
|
+
<% end %>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
<% end %>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<!-- Parallel Groups Summary -->
|
|
394
|
+
<% if parallel_groups.present? %>
|
|
395
|
+
<div class="border-t border-gray-100 dark:border-gray-700 pt-4 mt-4">
|
|
396
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
397
|
+
Parallel Groups
|
|
398
|
+
</p>
|
|
399
|
+
|
|
400
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
401
|
+
<% parallel_groups.each do |group| %>
|
|
402
|
+
<div class="flex items-start gap-3 py-2 px-3 rounded-lg bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800">
|
|
403
|
+
<span class="w-6 h-6 flex items-center justify-center rounded-full bg-purple-100 dark:bg-purple-900/50 text-xs font-medium text-purple-600 dark:text-purple-300">
|
|
404
|
+
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
|
405
|
+
<path d="M10 3.5a1.5 1.5 0 013 0V4a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-.5a1.5 1.5 0 000 3h.5a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-.5a1.5 1.5 0 00-3 0v.5a1 1 0 01-1 1H6a1 1 0 01-1-1v-3a1 1 0 00-1-1h-.5a1.5 1.5 0 010-3H4a1 1 0 001-1V6a1 1 0 011-1h3a1 1 0 001-1v-.5z"/>
|
|
406
|
+
</svg>
|
|
407
|
+
</span>
|
|
408
|
+
<div class="flex-1">
|
|
409
|
+
<div class="flex items-center gap-2">
|
|
410
|
+
<span class="font-medium text-purple-700 dark:text-purple-300">
|
|
411
|
+
<%= group[:name] %>
|
|
412
|
+
</span>
|
|
413
|
+
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
414
|
+
(<%= group[:step_names]&.size || 0 %> steps)
|
|
415
|
+
</span>
|
|
416
|
+
</div>
|
|
417
|
+
<div class="flex items-center gap-2 mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
418
|
+
<% if group[:fail_fast] %>
|
|
419
|
+
<span>fail_fast: on</span>
|
|
420
|
+
<% end %>
|
|
421
|
+
<% if group[:concurrency] %>
|
|
422
|
+
<span>concurrency: <%= group[:concurrency] %></span>
|
|
423
|
+
<% end %>
|
|
424
|
+
<% if group[:timeout] %>
|
|
425
|
+
<span>timeout: <%= group[:timeout] %>s</span>
|
|
426
|
+
<% end %>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
|
429
|
+
<% group[:step_names]&.each do |step_name| %>
|
|
430
|
+
<span class="text-xs bg-purple-100 dark:bg-purple-900/50 text-purple-600 dark:text-purple-300 px-1.5 py-0.5 rounded">
|
|
431
|
+
<%= step_name %>
|
|
432
|
+
</span>
|
|
433
|
+
<% end %>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
<% end %>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
<% end %>
|
|
441
|
+
|
|
442
|
+
<!-- Wait Steps Summary -->
|
|
443
|
+
<% wait_steps = steps.select { |s| s[:type] == :wait } %>
|
|
444
|
+
<% if wait_steps.present? %>
|
|
445
|
+
<div class="border-t border-gray-100 dark:border-gray-700 pt-4 mt-4">
|
|
446
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
447
|
+
Wait Steps
|
|
448
|
+
</p>
|
|
449
|
+
|
|
450
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
451
|
+
<% wait_steps.each do |wait_step| %>
|
|
452
|
+
<%
|
|
453
|
+
case wait_step[:wait_type]
|
|
454
|
+
when :delay
|
|
455
|
+
bg_color = "bg-teal-50 dark:bg-teal-900/20"
|
|
456
|
+
border_color = "border-teal-200 dark:border-teal-800"
|
|
457
|
+
icon_color = "text-teal-600 dark:text-teal-300"
|
|
458
|
+
icon_bg = "bg-teal-100 dark:bg-teal-900/50"
|
|
459
|
+
type_label = "Delay"
|
|
460
|
+
when :until
|
|
461
|
+
bg_color = "bg-cyan-50 dark:bg-cyan-900/20"
|
|
462
|
+
border_color = "border-cyan-200 dark:border-cyan-800"
|
|
463
|
+
icon_color = "text-cyan-600 dark:text-cyan-300"
|
|
464
|
+
icon_bg = "bg-cyan-100 dark:bg-cyan-900/50"
|
|
465
|
+
type_label = "Poll Until"
|
|
466
|
+
when :schedule
|
|
467
|
+
bg_color = "bg-indigo-50 dark:bg-indigo-900/20"
|
|
468
|
+
border_color = "border-indigo-200 dark:border-indigo-800"
|
|
469
|
+
icon_color = "text-indigo-600 dark:text-indigo-300"
|
|
470
|
+
icon_bg = "bg-indigo-100 dark:bg-indigo-900/50"
|
|
471
|
+
type_label = "Scheduled"
|
|
472
|
+
when :approval
|
|
473
|
+
bg_color = "bg-orange-50 dark:bg-orange-900/20"
|
|
474
|
+
border_color = "border-orange-200 dark:border-orange-800"
|
|
475
|
+
icon_color = "text-orange-600 dark:text-orange-300"
|
|
476
|
+
icon_bg = "bg-orange-100 dark:bg-orange-900/50"
|
|
477
|
+
type_label = "Approval"
|
|
478
|
+
else
|
|
479
|
+
bg_color = "bg-gray-50 dark:bg-gray-900/20"
|
|
480
|
+
border_color = "border-gray-200 dark:border-gray-800"
|
|
481
|
+
icon_color = "text-gray-600 dark:text-gray-300"
|
|
482
|
+
icon_bg = "bg-gray-100 dark:bg-gray-900/50"
|
|
483
|
+
type_label = "Wait"
|
|
484
|
+
end
|
|
485
|
+
%>
|
|
486
|
+
<div class="flex items-start gap-3 py-2 px-3 rounded-lg <%= bg_color %> border <%= border_color %>">
|
|
487
|
+
<span class="w-6 h-6 flex items-center justify-center rounded-full <%= icon_bg %> text-xs font-medium <%= icon_color %>">
|
|
488
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
489
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
490
|
+
</svg>
|
|
491
|
+
</span>
|
|
492
|
+
<div class="flex-1">
|
|
493
|
+
<div class="flex items-center gap-2">
|
|
494
|
+
<span class="font-medium <%= icon_color %>">
|
|
495
|
+
<%= wait_step[:ui_label] || wait_step[:name] %>
|
|
496
|
+
</span>
|
|
497
|
+
<span class="text-xs px-1.5 py-0.5 rounded <%= icon_bg %> <%= icon_color %>">
|
|
498
|
+
<%= type_label %>
|
|
499
|
+
</span>
|
|
500
|
+
</div>
|
|
501
|
+
<div class="flex flex-wrap items-center gap-2 mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
502
|
+
<% if wait_step[:duration] %>
|
|
503
|
+
<span>duration: <%= wait_step[:duration].is_a?(Numeric) ? "#{wait_step[:duration]}s" : wait_step[:duration] %></span>
|
|
504
|
+
<% end %>
|
|
505
|
+
<% if wait_step[:poll_interval] %>
|
|
506
|
+
<span>poll: <%= wait_step[:poll_interval].is_a?(Numeric) ? "#{wait_step[:poll_interval]}s" : wait_step[:poll_interval] %></span>
|
|
507
|
+
<% end %>
|
|
508
|
+
<% if wait_step[:timeout] %>
|
|
509
|
+
<span>timeout: <%= wait_step[:timeout].is_a?(Numeric) ? "#{wait_step[:timeout]}s" : wait_step[:timeout] %></span>
|
|
510
|
+
<% end %>
|
|
511
|
+
<% if wait_step[:on_timeout] %>
|
|
512
|
+
<span>on_timeout: <%= wait_step[:on_timeout] %></span>
|
|
513
|
+
<% end %>
|
|
514
|
+
</div>
|
|
515
|
+
<% if wait_step[:notify].present? || wait_step[:approvers].present? %>
|
|
516
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
|
517
|
+
<% if wait_step[:notify].present? %>
|
|
518
|
+
<span class="text-xs bg-blue-100 dark:bg-blue-900/50 text-blue-600 dark:text-blue-300 px-1.5 py-0.5 rounded">
|
|
519
|
+
notify: <%= Array(wait_step[:notify]).join(", ") %>
|
|
520
|
+
</span>
|
|
521
|
+
<% end %>
|
|
522
|
+
<% if wait_step[:approvers].present? %>
|
|
523
|
+
<span class="text-xs bg-orange-100 dark:bg-orange-900/50 text-orange-600 dark:text-orange-300 px-1.5 py-0.5 rounded">
|
|
524
|
+
<%= Array(wait_step[:approvers]).size %> approver(s)
|
|
525
|
+
</span>
|
|
526
|
+
<% end %>
|
|
527
|
+
</div>
|
|
528
|
+
<% end %>
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
<% end %>
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
<% end %>
|
|
535
|
+
<% else %>
|
|
536
|
+
<p class="text-gray-500 dark:text-gray-400 italic py-4">
|
|
537
|
+
No steps defined for this DSL workflow.
|
|
538
|
+
</p>
|
|
539
|
+
<% end %>
|