durable_flow 0.1.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.
@@ -0,0 +1,74 @@
1
+ <div class="df-header">
2
+ <div>
3
+ <div class="df-kicker">DurableFlow</div>
4
+ <h1>Workflow Runs</h1>
5
+ <p class="df-muted">Recent executions, waits, sleeps, and terminal states across your Rails workflows.</p>
6
+ </div>
7
+ <%= link_to "Refresh", workflow_runs_path, class: "df-button" %>
8
+ </div>
9
+
10
+ <div class="df-stats">
11
+ <div class="df-stat">
12
+ <div class="df-stat-label">Total runs</div>
13
+ <div class="df-stat-value"><%= @workflow_runs.size %></div>
14
+ </div>
15
+ <div class="df-stat">
16
+ <div class="df-stat-label">Active</div>
17
+ <div class="df-stat-value"><%= @active_count %></div>
18
+ </div>
19
+ <div class="df-stat">
20
+ <div class="df-stat-label">Completed</div>
21
+ <div class="df-stat-value"><%= @status_counts.fetch("completed", 0) %></div>
22
+ </div>
23
+ <div class="df-stat">
24
+ <div class="df-stat-label">Failed</div>
25
+ <div class="df-stat-value"><%= @status_counts.fetch("failed", 0) %></div>
26
+ </div>
27
+ </div>
28
+
29
+ <section class="df-panel">
30
+ <div class="df-panel-header">
31
+ <div>
32
+ <div class="df-panel-title">Runs</div>
33
+ <div class="df-panel-subtitle">Showing latest <%= @workflow_runs.size %> workflow runs</div>
34
+ </div>
35
+ <div class="df-filter">Filter by run, workflow, status</div>
36
+ </div>
37
+
38
+ <table>
39
+ <thead>
40
+ <tr>
41
+ <th style="width: 34%">Run</th>
42
+ <th style="width: 18%">Status</th>
43
+ <th class="df-hide-sm" style="width: 15%">Duration</th>
44
+ <th class="df-hide-sm" style="width: 17%">Started</th>
45
+ <th style="width: 16%">Updated</th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ <% @workflow_runs.each do |run| %>
50
+ <% finished_at = run.completed_at || run.failed_at %>
51
+ <tr>
52
+ <td>
53
+ <%= link_to run.workflow_class, workflow_run_path(run.run_id), class: "df-run-link" %>
54
+ <div class="df-run-id df-mono"><%= run.run_id %></div>
55
+ </td>
56
+ <td>
57
+ <span class="df-status <%= durable_flow_status_class(run.status) %>"><%= run.status %></span>
58
+ </td>
59
+ <td class="df-hide-sm"><%= durable_flow_duration(run.started_at || run.created_at, finished_at) %></td>
60
+ <td class="df-hide-sm"><%= durable_flow_format_time(run.started_at || run.created_at) %></td>
61
+ <td><%= durable_flow_format_time(run.updated_at) %></td>
62
+ </tr>
63
+ <% end %>
64
+
65
+ <% if @workflow_runs.empty? %>
66
+ <tr>
67
+ <td colspan="5">
68
+ <div class="df-empty">No workflow runs yet.</div>
69
+ </td>
70
+ </tr>
71
+ <% end %>
72
+ </tbody>
73
+ </table>
74
+ </section>
@@ -0,0 +1,180 @@
1
+ <div class="df-header">
2
+ <div>
3
+ <div class="df-kicker"><%= link_to "Workflow Runs", workflow_runs_path %> / Run</div>
4
+ <h1><%= @workflow_run.workflow_class %></h1>
5
+ <p class="df-muted df-mono"><%= @workflow_run.run_id %></p>
6
+ </div>
7
+ <span class="df-status <%= durable_flow_status_class(@workflow_run.status) %>"><%= @workflow_run.status %></span>
8
+ </div>
9
+
10
+ <div class="df-meta-grid">
11
+ <div class="df-meta">
12
+ <div class="df-meta-label">Duration</div>
13
+ <div class="df-meta-value"><%= durable_flow_duration(@workflow_run.started_at || @workflow_run.created_at, @workflow_run.completed_at || @workflow_run.failed_at) %></div>
14
+ </div>
15
+ <div class="df-meta">
16
+ <div class="df-meta-label">Started</div>
17
+ <div class="df-meta-value"><%= durable_flow_format_time(@workflow_run.started_at || @workflow_run.created_at) %></div>
18
+ </div>
19
+ <div class="df-meta">
20
+ <div class="df-meta-label">Interrupted</div>
21
+ <div class="df-meta-value"><%= durable_flow_format_time(@workflow_run.interrupted_at) %></div>
22
+ </div>
23
+ <div class="df-meta">
24
+ <div class="df-meta-label">Updated</div>
25
+ <div class="df-meta-value"><%= durable_flow_format_time(@workflow_run.updated_at) %></div>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="df-detail-grid">
30
+ <section class="df-panel">
31
+ <div class="df-panel-header">
32
+ <div>
33
+ <div class="df-panel-title">Timeline</div>
34
+ <div class="df-panel-subtitle"><%= pluralize(@workflow_timeline.step_entries.size, "step") %> recorded for this run</div>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="df-timeline">
39
+ <% @workflow_timeline.step_entries.each do |entry| %>
40
+ <% step = entry.step %>
41
+ <article class="df-step">
42
+ <div class="df-step-rail">
43
+ <span class="df-step-dot <%= durable_flow_status_class(step.status) %>"></span>
44
+ </div>
45
+ <div class="df-step-body">
46
+ <div class="df-step-top">
47
+ <div>
48
+ <div class="df-step-name"><%= step.name %></div>
49
+ <div class="df-step-meta">
50
+ Attempt <%= step.attempts %>
51
+ · Started <%= durable_flow_format_time(step.started_at) %>
52
+ <% if step.completed_at %>
53
+ · Completed <%= durable_flow_format_time(step.completed_at) %>
54
+ <% end %>
55
+ </div>
56
+ </div>
57
+ <span class="df-status <%= durable_flow_status_class(step.status) %>"><%= step.status %></span>
58
+ </div>
59
+
60
+ <% if step.metadata.present? %>
61
+ <pre class="df-code"><%= durable_flow_json(step.metadata) %></pre>
62
+ <% end %>
63
+
64
+ <% if step.result.present? %>
65
+ <pre class="df-code"><%= durable_flow_json(step.result) %></pre>
66
+ <% end %>
67
+
68
+ <% if entry.logs.any? %>
69
+ <div class="df-step-logs">
70
+ <div class="df-step-logs-label">Logs</div>
71
+ <% entry.logs.each do |log| %>
72
+ <div class="df-step-log">
73
+ <div class="df-step-log-top">
74
+ <span class="df-status <%= durable_flow_status_class(log.level) %>"><%= log.level %></span>
75
+ <span class="df-step-log-message"><%= log.message %></span>
76
+ </div>
77
+ <% if log.data.present? %>
78
+ <pre class="df-code"><%= durable_flow_json(log.data_value) %></pre>
79
+ <% end %>
80
+ </div>
81
+ <% end %>
82
+ </div>
83
+ <% end %>
84
+ </div>
85
+ </article>
86
+ <% end %>
87
+
88
+ <% if @workflow_timeline.step_entries.empty? %>
89
+ <div class="df-empty">No steps recorded yet.</div>
90
+ <% end %>
91
+ </div>
92
+ </section>
93
+
94
+ <aside class="df-panel">
95
+ <div class="df-panel-header">
96
+ <div>
97
+ <div class="df-panel-title">Run Details</div>
98
+ <div class="df-panel-subtitle">Queue, arguments, waits, and errors</div>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="df-side-section">
103
+ <h3>Metadata</h3>
104
+ <div class="df-kv">
105
+ <div class="df-kv-key">Job ID</div>
106
+ <div class="df-kv-value df-mono"><%= @workflow_run.job_id %></div>
107
+ </div>
108
+ <div class="df-kv">
109
+ <div class="df-kv-key">Queue</div>
110
+ <div class="df-kv-value"><%= @workflow_run.queue_name.presence || "default" %></div>
111
+ </div>
112
+ <div class="df-kv">
113
+ <div class="df-kv-key">Priority</div>
114
+ <div class="df-kv-value"><%= @workflow_run.priority || "—" %></div>
115
+ </div>
116
+ <div class="df-kv">
117
+ <div class="df-kv-key">Lock</div>
118
+ <div class="df-kv-value"><%= @workflow_run.execution_locked? ? "active until #{durable_flow_format_time(@workflow_run.execution_lock_expires_at)}" : "not locked" %></div>
119
+ </div>
120
+ </div>
121
+
122
+ <% if @workflow_timeline.waits.any? %>
123
+ <div class="df-side-section">
124
+ <h3>Waits</h3>
125
+ <% @workflow_timeline.waits.each do |wait| %>
126
+ <div class="df-kv">
127
+ <div class="df-kv-key">Event</div>
128
+ <div class="df-kv-value"><%= wait.event_name %></div>
129
+ </div>
130
+ <div class="df-kv">
131
+ <div class="df-kv-key">Status</div>
132
+ <div class="df-kv-value">
133
+ <span class="df-status <%= durable_flow_status_class(wait.status) %>"><%= wait.status %></span>
134
+ </div>
135
+ </div>
136
+ <div class="df-kv">
137
+ <div class="df-kv-key">Timeout</div>
138
+ <div class="df-kv-value"><%= durable_flow_format_time(wait.timeout_at) %></div>
139
+ </div>
140
+ <pre class="df-code"><%= durable_flow_json(wait.match) %></pre>
141
+ <% if wait.workflow_event %>
142
+ <pre class="df-code"><%= durable_flow_json(wait.workflow_event.payload) %></pre>
143
+ <% end %>
144
+ <% end %>
145
+ </div>
146
+ <% end %>
147
+
148
+ <% if @workflow_timeline.run_logs.any? %>
149
+ <div class="df-side-section">
150
+ <h3>Run Logs</h3>
151
+ <% @workflow_timeline.run_logs.each do |log| %>
152
+ <div class="df-kv">
153
+ <div class="df-kv-key"><%= durable_flow_format_time(log.created_at) %></div>
154
+ <div class="df-kv-value">
155
+ <span class="df-status <%= durable_flow_status_class(log.level) %>"><%= log.level %></span>
156
+ <div style="margin-top: 8px; font-weight: 650;"><%= log.message %></div>
157
+ <% if log.data.present? %>
158
+ <pre class="df-code"><%= durable_flow_json(log.data_value) %></pre>
159
+ <% end %>
160
+ </div>
161
+ </div>
162
+ <% end %>
163
+ </div>
164
+ <% end %>
165
+
166
+ <% if @workflow_run.arguments.present? %>
167
+ <div class="df-side-section">
168
+ <h3>Arguments</h3>
169
+ <pre class="df-code"><%= durable_flow_json(@workflow_run.arguments) %></pre>
170
+ </div>
171
+ <% end %>
172
+
173
+ <% if @workflow_run.last_error.present? %>
174
+ <div class="df-side-section">
175
+ <h3>Error</h3>
176
+ <pre class="df-code"><%= durable_flow_json(@workflow_run.last_error) %></pre>
177
+ </div>
178
+ <% end %>
179
+ </aside>
180
+ </div>