makersclaw 0.8.0 → 0.8.1
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.
- package/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1627 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// What starts an automation. Eight ways, the platform tells which it runs:
|
|
4
|
+
// by hand; on a schedule in the team's time zone; when an app sends an
|
|
5
|
+
// event; when one of an app's actions finishes; when a record is added or
|
|
6
|
+
// changed; when the automation's own web address is called; when another
|
|
7
|
+
// automation finishes. A connection's own events (a new mail) are listed as
|
|
8
|
+
// "soon", with what to use until then.
|
|
9
|
+
//
|
|
10
|
+
// The schedule is offered in the shapes people ask for ("every weekday at
|
|
11
|
+
// 07:00") and kept as a five-field cron; anything the picker cannot read
|
|
12
|
+
// back stays the custom cron it was.
|
|
13
|
+
//
|
|
14
|
+
// Props in, callbacks out; the definition's trigger is one of
|
|
15
|
+
// { type: "manual" }
|
|
16
|
+
// { type: "schedule", cron, tz }
|
|
17
|
+
// { type: "event", topic, app? }
|
|
18
|
+
// { type: "action", action, app? }
|
|
19
|
+
// { type: "record", kinds?, ops?, app? }
|
|
20
|
+
// { type: "webhook" }
|
|
21
|
+
// { type: "automation", key, when }
|
|
22
|
+
|
|
23
|
+
import { useId, useState } from "react";
|
|
24
|
+
import { Button, Checkbox, Field, Input, Select, Switch, TimeInput, cx } from "../../ui";
|
|
25
|
+
import { Glyph, SidePanel, WEEKDAYS, cronFromParts, cronWords, parseTime, partsFromCron, timeText, triggerGlyph, triggerTitle, triggerWords } from "../automation-model";
|
|
26
|
+
|
|
27
|
+
const FREQS = [
|
|
28
|
+
{ value: "daily", label: "Every day" },
|
|
29
|
+
{ value: "weekdays", label: "Every weekday" },
|
|
30
|
+
{ value: "weekly", label: "Every week" },
|
|
31
|
+
{ value: "hourly", label: "Every hour" },
|
|
32
|
+
{ value: "custom", label: "Another schedule" },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** What a platform without the newer triggers offers: two ways, and the rest said honestly. */
|
|
36
|
+
const FALLBACK_CHOICES = [
|
|
37
|
+
{ type: "manual", label: "By hand", hint: "Someone presses Run, or the agent runs it.", available: true },
|
|
38
|
+
{ type: "schedule", label: "On a schedule", hint: "Every day, every weekday, every hour.", available: true },
|
|
39
|
+
];
|
|
40
|
+
const LATER = [
|
|
41
|
+
{ id: "record", label: "When something is added", hint: "A new person, a new row, a new message." },
|
|
42
|
+
{ id: "automation", label: "From another automation", hint: "One flow starts the next." },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** A fresh trigger of a type, with what the platform lists first filled in. */
|
|
46
|
+
function startOf(type, { app, other }) {
|
|
47
|
+
switch (type) {
|
|
48
|
+
case "schedule":
|
|
49
|
+
return { type, cron: "0 7 * * 1-5", tz: "team" };
|
|
50
|
+
case "event":
|
|
51
|
+
return { type, topic: app?.topics?.[0] ?? "", ...(app && !app.own ? { app: app.name } : {}) };
|
|
52
|
+
case "action":
|
|
53
|
+
return { type, action: app?.actions?.[0]?.name ?? "", ...(app && !app.own ? { app: app.name } : {}) };
|
|
54
|
+
case "record":
|
|
55
|
+
return { type, ...(app && !app.own ? { app: app.name } : {}) };
|
|
56
|
+
case "automation":
|
|
57
|
+
return { type, key: other?.[0]?.key ?? "", when: "done" };
|
|
58
|
+
default:
|
|
59
|
+
return { type };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The ways, as a list of options for a Select: what is available, and the current one even if it is not. */
|
|
64
|
+
function optionsOf(choices, current) {
|
|
65
|
+
const list = choices.filter((c) => c.available);
|
|
66
|
+
if (current && !list.some((c) => c.type === current)) list.push({ type: current, label: triggerTitle(current), hint: "", available: true });
|
|
67
|
+
return list.map((c) => ({ value: c.type, label: c.label }));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Schedule({ uid, trigger, readOnly, onChange }) {
|
|
71
|
+
const parts = partsFromCron(trigger.cron ?? "0 7 * * 1-5");
|
|
72
|
+
const apply = (next) => onChange({ type: "schedule", cron: cronFromParts({ ...parts, ...next }), tz: trigger.tz ?? "team" });
|
|
73
|
+
const words = cronWords(trigger.cron);
|
|
74
|
+
const team = !trigger.tz || trigger.tz === "team";
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<Field id={`${uid}-freq`} label="How often">
|
|
78
|
+
<Select id={`${uid}-freq`} value={parts.freq} disabled={readOnly} options={FREQS} onChange={(e) => apply({ freq: e.target.value })} />
|
|
79
|
+
</Field>
|
|
80
|
+
{parts.freq === "weekly" ? (
|
|
81
|
+
<Field id={`${uid}-day`} label="On">
|
|
82
|
+
<Select id={`${uid}-day`} value={String(parts.weekday)} disabled={readOnly} options={WEEKDAYS.map((d, i) => ({ value: String(i), label: d }))} onChange={(e) => apply({ weekday: Number(e.target.value) })} />
|
|
83
|
+
</Field>
|
|
84
|
+
) : null}
|
|
85
|
+
{parts.freq === "daily" || parts.freq === "weekdays" || parts.freq === "weekly" ? (
|
|
86
|
+
<Field id={`${uid}-time`} label="At">
|
|
87
|
+
<TimeInput
|
|
88
|
+
id={`${uid}-time`}
|
|
89
|
+
value={timeText(parts.hour, parts.minute)}
|
|
90
|
+
disabled={readOnly}
|
|
91
|
+
onChange={(e) => {
|
|
92
|
+
const t = parseTime(e.target.value);
|
|
93
|
+
if (t) apply(t);
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
</Field>
|
|
97
|
+
) : null}
|
|
98
|
+
{parts.freq === "hourly" ? (
|
|
99
|
+
<Field id={`${uid}-min`} label="Minutes past the hour">
|
|
100
|
+
<Input id={`${uid}-min`} type="number" min={0} max={59} inputMode="numeric" value={parts.minute} disabled={readOnly} onChange={(e) => apply({ minute: Number(e.target.value) || 0 })} />
|
|
101
|
+
</Field>
|
|
102
|
+
) : null}
|
|
103
|
+
{parts.freq === "custom" ? (
|
|
104
|
+
<Field id={`${uid}-cron`} label="Schedule" hint="Five fields: minute, hour, day, month, weekday.">
|
|
105
|
+
<Input id={`${uid}-cron`} className="font-mono text-[13px]" value={trigger.cron} disabled={readOnly} placeholder="0 7 * * 1-5" onChange={(e) => onChange({ type: "schedule", cron: e.target.value, tz: trigger.tz })} />
|
|
106
|
+
</Field>
|
|
107
|
+
) : null}
|
|
108
|
+
<Switch id={`${uid}-team`} checked={team} disabled={readOnly} label="In the team's time zone" onChange={(e) => onChange({ type: "schedule", cron: trigger.cron, tz: e.target.checked ? "team" : "UTC" })} />
|
|
109
|
+
<p className="m-0 -mt-2 text-xs text-muted-foreground">07:00 stays 07:00 through daylight saving. Turn off to name another zone.</p>
|
|
110
|
+
{!team ? (
|
|
111
|
+
<Field id={`${uid}-tz`} label="Time zone">
|
|
112
|
+
<Input id={`${uid}-tz`} className="font-mono text-[13px]" value={trigger.tz} disabled={readOnly} placeholder="America/New_York" onChange={(e) => onChange({ type: "schedule", cron: trigger.cron, tz: e.target.value })} />
|
|
113
|
+
</Field>
|
|
114
|
+
) : null}
|
|
115
|
+
<p className="m-0 text-xs text-muted-foreground">{words ? `Runs ${words.charAt(0).toLowerCase()}${words.slice(1)}${team ? ", team time" : ""}.` : "That schedule cannot be read yet."}</p>
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Which app a team automation hears: one picked from the platform's list. An app's own has no choice. */
|
|
121
|
+
function AppField({ uid, trigger, apps, ownedByApp, readOnly, onApp, required }) {
|
|
122
|
+
if (ownedByApp || apps.length === 0) return null;
|
|
123
|
+
return (
|
|
124
|
+
<Field id={`${uid}-app`} label="In which app" hint={required ? undefined : "Leave it open to hear any app."}>
|
|
125
|
+
<Select
|
|
126
|
+
id={`${uid}-app`}
|
|
127
|
+
value={trigger.app ?? ""}
|
|
128
|
+
disabled={readOnly}
|
|
129
|
+
options={[{ value: "", label: required ? "Pick an app" : "Any app" }, ...apps.map((a) => ({ value: a.name, label: a.name }))]}
|
|
130
|
+
onChange={(e) => onApp(e.target.value)}
|
|
131
|
+
/>
|
|
132
|
+
</Field>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function Webhook({ uid, hook, onRotate, readOnly, origin }) {
|
|
137
|
+
const [shown, setShown] = useState(null);
|
|
138
|
+
const [asking, setAsking] = useState(false);
|
|
139
|
+
const [busy, setBusy] = useState(false);
|
|
140
|
+
const [note, setNote] = useState(null);
|
|
141
|
+
if (!hook) {
|
|
142
|
+
return <p className="m-0 text-xs text-muted-foreground">Save it first. Its web address is made when it exists, and the secret after that.</p>;
|
|
143
|
+
}
|
|
144
|
+
const base = (origin ?? (typeof window !== "undefined" ? window.location.origin : "")).replace(/\/$/, "");
|
|
145
|
+
const address = `${base}${hook.path}`;
|
|
146
|
+
const make = async () => {
|
|
147
|
+
setBusy(true);
|
|
148
|
+
setNote(null);
|
|
149
|
+
try {
|
|
150
|
+
const r = await onRotate?.();
|
|
151
|
+
if (r && r.ok === false) setNote(r.error || "That did not work.");
|
|
152
|
+
else if (r?.hook?.secret) setShown(r.hook.secret);
|
|
153
|
+
setAsking(false);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
setNote(e?.message || "That did not work.");
|
|
156
|
+
} finally {
|
|
157
|
+
setBusy(false);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
return (
|
|
161
|
+
<>
|
|
162
|
+
<Field id={`${uid}-addr`} label="Its web address" hint="Post to it with JSON. What it receives is on the run as trigger.payload.body.">
|
|
163
|
+
<Input id={`${uid}-addr`} readOnly className="font-mono text-[12.5px]" value={address} onFocus={(e) => e.target.select()} />
|
|
164
|
+
</Field>
|
|
165
|
+
{shown ? (
|
|
166
|
+
<Field id={`${uid}-secret`} label="Its secret" hint="Shown once: copy it now. Sign the body with it (x-makersclaw-signature), or add ?token=… to the address when a sender cannot sign.">
|
|
167
|
+
<Input id={`${uid}-secret`} readOnly className="font-mono text-[12.5px]" value={shown} onFocus={(e) => e.target.select()} />
|
|
168
|
+
</Field>
|
|
169
|
+
) : (
|
|
170
|
+
<p className="m-0 text-xs text-muted-foreground">
|
|
171
|
+
{hook.has_secret ? "It has a secret, and only the sender who holds it can start a run." : "It has no secret yet, so nothing can start it."}
|
|
172
|
+
{hook.last_received_at ? " Last called " + new Date(hook.last_received_at).toLocaleString() + "." : ""}
|
|
173
|
+
</p>
|
|
174
|
+
)}
|
|
175
|
+
{!readOnly ? (
|
|
176
|
+
asking ? (
|
|
177
|
+
<div className="grid gap-2 rounded-md border border-border p-2.5">
|
|
178
|
+
<p className="m-0 text-xs">The old secret stops working at once. Anything that still uses it will be refused.</p>
|
|
179
|
+
<div className="flex gap-2">
|
|
180
|
+
<Button size="sm" variant="danger" onClick={make} disabled={busy}>
|
|
181
|
+
Make a new secret
|
|
182
|
+
</Button>
|
|
183
|
+
<Button size="sm" variant="secondary" onClick={() => setAsking(false)} disabled={busy}>
|
|
184
|
+
Keep the old one
|
|
185
|
+
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
) : (
|
|
189
|
+
<Button size="sm" variant="secondary" className="justify-self-start" icon="key" onClick={() => (hook.has_secret ? setAsking(true) : make())} disabled={busy}>
|
|
190
|
+
{hook.has_secret ? "Make a new secret" : "Make a secret"}
|
|
191
|
+
</Button>
|
|
192
|
+
)
|
|
193
|
+
) : null}
|
|
194
|
+
{note ? (
|
|
195
|
+
<p role="alert" className="m-0 text-xs text-destructive">
|
|
196
|
+
{note}
|
|
197
|
+
</p>
|
|
198
|
+
) : null}
|
|
199
|
+
</>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* trigger the definition's trigger (see the list at the top)
|
|
205
|
+
* onChange(trigger)
|
|
206
|
+
* readOnly shown, not editable
|
|
207
|
+
* onClose draws the close button
|
|
208
|
+
* catalog what the platform offers (the list door's `triggers`):
|
|
209
|
+
* { choices: [{ type, label, hint, available, why? }],
|
|
210
|
+
* apps: [{ name, actions: [{ name, description }], kinds, topics }],
|
|
211
|
+
* automations: [{ key, name }] }. Without it: by hand and a
|
|
212
|
+
* schedule, and `later` listed as "soon".
|
|
213
|
+
* ownedByApp the automation belongs to an app: its actions, kinds and
|
|
214
|
+
* topics are that app's (the first of catalog.apps)
|
|
215
|
+
* selfKey this automation's key: it cannot follow itself
|
|
216
|
+
* hook a webhook trigger's address once the automation is saved:
|
|
217
|
+
* { path, has_secret, rotated_at, last_received_at }
|
|
218
|
+
* onRotateHook async () => { ok, hook: { secret } } | { ok: false, error }
|
|
219
|
+
* origin where the address lives (default: this page's origin)
|
|
220
|
+
* later the not-yet ways to start without a catalog, [{ id, label, hint }]
|
|
221
|
+
*/
|
|
222
|
+
export function TriggerPicker({ trigger, onChange, readOnly = false, onClose, catalog = null, ownedByApp = false, selfKey = null, hook = null, onRotateHook, origin, later }) {
|
|
223
|
+
const uid = useId();
|
|
224
|
+
const current = trigger ?? { type: "manual" };
|
|
225
|
+
const type = current.type;
|
|
226
|
+
const choices = catalog?.choices?.length ? catalog.choices : FALLBACK_CHOICES;
|
|
227
|
+
const apps = catalog?.apps ?? [];
|
|
228
|
+
const others = (catalog?.automations ?? []).filter((a) => a.key !== selfKey);
|
|
229
|
+
const scope = ownedByApp ? apps[0] : apps.find((a) => a.name === current.app);
|
|
230
|
+
const scoped = scope ? { ...scope, own: ownedByApp } : null;
|
|
231
|
+
const soon = catalog ? choices.filter((c) => !c.available) : (later ?? LATER).map((l) => ({ type: l.id, label: l.label, hint: l.hint, available: false }));
|
|
232
|
+
const switchTo = (next) => onChange(startOf(next, { app: ownedByApp ? scoped : apps.length === 1 ? { ...apps[0], own: false } : null, other: others }));
|
|
233
|
+
const setApp = (name) => {
|
|
234
|
+
const { app: _drop, ...rest } = current;
|
|
235
|
+
onChange(name ? { ...rest, app: name } : rest);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<SidePanel glyph={<Glyph name={triggerGlyph(type)} size={20} />} title="What starts it" subtitle={triggerWords(current)} onClose={onClose} label="What starts it">
|
|
240
|
+
<Field id={`${uid}-type`} label="It starts">
|
|
241
|
+
<Select id={`${uid}-type`} value={type} disabled={readOnly} options={optionsOf(choices, type)} onChange={(e) => switchTo(e.target.value)} />
|
|
242
|
+
</Field>
|
|
243
|
+
<p className="m-0 -mt-2 text-xs text-muted-foreground">{choices.find((c) => c.type === type)?.hint ?? ""}</p>
|
|
244
|
+
|
|
245
|
+
{type === "manual" ? <p className="m-0 text-xs text-muted-foreground">Nothing starts it on its own. Press Run when you want it to go, or let the agent run it.</p> : null}
|
|
246
|
+
|
|
247
|
+
{type === "schedule" ? <Schedule uid={uid} trigger={current} readOnly={readOnly} onChange={onChange} /> : null}
|
|
248
|
+
|
|
249
|
+
{type === "event" ? (
|
|
250
|
+
<>
|
|
251
|
+
<AppField uid={uid} trigger={current} apps={apps} ownedByApp={ownedByApp} readOnly={readOnly} onApp={setApp} required={false} />
|
|
252
|
+
<Field id={`${uid}-topic`} label="Event" hint="The name an app sends, like rows.imported. Keyed events (with a key) run once per item.">
|
|
253
|
+
<Input id={`${uid}-topic`} className="font-mono text-[13px]" list={`${uid}-topics`} value={current.topic ?? ""} disabled={readOnly} placeholder="rows.imported" onChange={(e) => onChange({ ...current, topic: e.target.value.trim().toLowerCase() })} />
|
|
254
|
+
<datalist id={`${uid}-topics`}>{(scoped?.topics ?? apps.flatMap((a) => a.topics ?? [])).map((t) => <option key={t} value={t} />)}</datalist>
|
|
255
|
+
</Field>
|
|
256
|
+
</>
|
|
257
|
+
) : null}
|
|
258
|
+
|
|
259
|
+
{type === "action" ? (
|
|
260
|
+
<>
|
|
261
|
+
<AppField uid={uid} trigger={current} apps={apps} ownedByApp={ownedByApp} readOnly={readOnly} onApp={(n) => onChange({ type: "action", action: "", ...(n ? { app: n } : {}) })} required />
|
|
262
|
+
<Field id={`${uid}-action`} label="Action" hint="Runs when a person or the app's agent has done it. An action another automation ran does not start this one.">
|
|
263
|
+
{scoped?.actions?.length ? (
|
|
264
|
+
<Select
|
|
265
|
+
id={`${uid}-action`}
|
|
266
|
+
value={current.action ?? ""}
|
|
267
|
+
disabled={readOnly}
|
|
268
|
+
options={[{ value: "", label: "Pick an action" }, ...scoped.actions.map((a) => ({ value: a.name, label: a.name.replace(/_/g, " ") }))]}
|
|
269
|
+
onChange={(e) => onChange({ ...current, action: e.target.value })}
|
|
270
|
+
/>
|
|
271
|
+
) : (
|
|
272
|
+
<Input id={`${uid}-action`} className="font-mono text-[13px]" value={current.action ?? ""} disabled={readOnly} placeholder="add_people" onChange={(e) => onChange({ ...current, action: e.target.value.trim() })} />
|
|
273
|
+
)}
|
|
274
|
+
</Field>
|
|
275
|
+
</>
|
|
276
|
+
) : null}
|
|
277
|
+
|
|
278
|
+
{type === "record" ? (
|
|
279
|
+
<>
|
|
280
|
+
<AppField uid={uid} trigger={current} apps={apps} ownedByApp={ownedByApp} readOnly={readOnly} onApp={(n) => onChange({ type: "record", ...(n ? { app: n } : {}) })} required />
|
|
281
|
+
<fieldset className="m-0 grid min-w-0 gap-1 border-0 p-0">
|
|
282
|
+
<legend className="mb-1 p-0 text-sm font-medium">Which records</legend>
|
|
283
|
+
{(scoped?.kinds ?? []).map((k) => {
|
|
284
|
+
const picked = current.kinds ?? [];
|
|
285
|
+
return (
|
|
286
|
+
<Checkbox
|
|
287
|
+
key={k}
|
|
288
|
+
id={`${uid}-k-${k}`}
|
|
289
|
+
label={k.replace(/_/g, " ")}
|
|
290
|
+
checked={picked.includes(k)}
|
|
291
|
+
disabled={readOnly}
|
|
292
|
+
onChange={(e) => {
|
|
293
|
+
const next = e.target.checked ? [...picked, k] : picked.filter((x) => x !== k);
|
|
294
|
+
const { kinds: _drop, ...rest } = current;
|
|
295
|
+
onChange(next.length ? { ...rest, kinds: next } : rest);
|
|
296
|
+
}}
|
|
297
|
+
/>
|
|
298
|
+
);
|
|
299
|
+
})}
|
|
300
|
+
<p className="m-0 text-xs text-muted-foreground">{(current.kinds ?? []).length ? "Only these." : "None picked: every kind of record the owner can read."}</p>
|
|
301
|
+
</fieldset>
|
|
302
|
+
<fieldset className="m-0 grid min-w-0 gap-1 border-0 p-0">
|
|
303
|
+
<legend className="mb-1 p-0 text-sm font-medium">When it is</legend>
|
|
304
|
+
{[
|
|
305
|
+
["create", "Added"],
|
|
306
|
+
["update", "Changed"],
|
|
307
|
+
].map(([op, label]) => {
|
|
308
|
+
const ops = current.ops ?? ["create", "update"];
|
|
309
|
+
return (
|
|
310
|
+
<Checkbox
|
|
311
|
+
key={op}
|
|
312
|
+
id={`${uid}-op-${op}`}
|
|
313
|
+
label={label}
|
|
314
|
+
checked={ops.includes(op)}
|
|
315
|
+
disabled={readOnly || (ops.length === 1 && ops[0] === op)}
|
|
316
|
+
onChange={(e) => {
|
|
317
|
+
const next = e.target.checked ? [...ops, op] : ops.filter((x) => x !== op);
|
|
318
|
+
const { ops: _drop, ...rest } = current;
|
|
319
|
+
onChange(next.length === 2 ? rest : { ...rest, ops: next });
|
|
320
|
+
}}
|
|
321
|
+
/>
|
|
322
|
+
);
|
|
323
|
+
})}
|
|
324
|
+
</fieldset>
|
|
325
|
+
<p className="m-0 text-xs text-muted-foreground">A person, a screen or the app's agent made the change; the app's own jobs do not start it. One run for each record, at most once a minute.</p>
|
|
326
|
+
</>
|
|
327
|
+
) : null}
|
|
328
|
+
|
|
329
|
+
{type === "webhook" ? <Webhook uid={uid} hook={hook} onRotate={onRotateHook} readOnly={readOnly} origin={origin} /> : null}
|
|
330
|
+
|
|
331
|
+
{type === "automation" ? (
|
|
332
|
+
<>
|
|
333
|
+
<Field id={`${uid}-key`} label="Another automation" hint="It runs after that one has finished, with how it went.">
|
|
334
|
+
<Select
|
|
335
|
+
id={`${uid}-key`}
|
|
336
|
+
value={current.key ?? ""}
|
|
337
|
+
disabled={readOnly}
|
|
338
|
+
options={[{ value: "", label: others.length ? "Pick one" : "No other automation yet" }, ...others.map((a) => ({ value: a.key, label: a.name }))]}
|
|
339
|
+
onChange={(e) => onChange({ ...current, key: e.target.value })}
|
|
340
|
+
/>
|
|
341
|
+
</Field>
|
|
342
|
+
<Field id={`${uid}-when`} label="When it">
|
|
343
|
+
<Select
|
|
344
|
+
id={`${uid}-when`}
|
|
345
|
+
value={current.when ?? "done"}
|
|
346
|
+
disabled={readOnly}
|
|
347
|
+
options={[
|
|
348
|
+
{ value: "done", label: "finishes well" },
|
|
349
|
+
{ value: "failed", label: "fails" },
|
|
350
|
+
{ value: "any", label: "ends either way" },
|
|
351
|
+
]}
|
|
352
|
+
onChange={(e) => onChange({ ...current, when: e.target.value })}
|
|
353
|
+
/>
|
|
354
|
+
</Field>
|
|
355
|
+
<p className="m-0 text-xs text-muted-foreground">Two automations cannot start each other: the platform refuses a loop when you save.</p>
|
|
356
|
+
</>
|
|
357
|
+
) : null}
|
|
358
|
+
|
|
359
|
+
{soon.length ? (
|
|
360
|
+
<ul className={cx("m-0 grid list-none gap-1.5 border-t border-border p-0 pt-3")} aria-label="Coming later">
|
|
361
|
+
{soon.map((l) => (
|
|
362
|
+
<li key={l.type} className="flex items-start justify-between gap-2 text-sm text-muted-foreground">
|
|
363
|
+
<span className="grid gap-0.5">
|
|
364
|
+
<span>{l.label}</span>
|
|
365
|
+
{l.why || l.hint ? <span className="text-xs">{l.why || l.hint}</span> : null}
|
|
366
|
+
</span>
|
|
367
|
+
<span className="shrink-0 rounded-full border border-border px-1.5 text-2xs">soon</span>
|
|
368
|
+
</li>
|
|
369
|
+
))}
|
|
370
|
+
</ul>
|
|
371
|
+
) : null}
|
|
372
|
+
</SidePanel>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# WorkflowView
|
|
2
|
+
|
|
3
|
+
> **Superseded** (0.8.1, the automations engine, D26). One job drawn top to bottom, from `useJobs`, `useRuns` and `useRun`. For an app whose jobs run on the automations engine, use `automation-editor` (a node canvas a member can edit, test and switch on) and `run-view` (one run on the same canvas, the path taken lit). Keep using this block for an app that has not moved: it works as it did.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
One job of the app's agent, drawn as a graph on a dotted ground. Part of
|
|
7
|
+
the agent kit (0.8.1): the app shows its agent's work itself; the
|
|
8
|
+
window's Runs tab stays as the full history.
|
|
9
|
+
|
|
10
|
+
- **The graph**: the Trigger ("Every weekday at 07:00", "When a deal is
|
|
11
|
+
created", "Run by Owen") → each step of the chosen run, with how it
|
|
12
|
+
went as a mark and a word → the result (what it wrote, what needs
|
|
13
|
+
you).
|
|
14
|
+
- **Over it**: the run picker ("Run #7 · Tue 29 Sep, 07:02") and Re-run.
|
|
15
|
+
- **In its head**: the Live switch and "Trigger manually · about 700 cr".
|
|
16
|
+
- **Beside it**: the run history and the overview (the `run-history`
|
|
17
|
+
block's RunHistory and RunOverview).
|
|
18
|
+
- **Before any run**: the job's declared steps (`plan`) drawn in pencil,
|
|
19
|
+
dashed, with an honest line ("No runs yet. The first runs Mon 12 Oct,
|
|
20
|
+
07:00, once a mailbox is connected."). A job not set up still has
|
|
21
|
+
something to look at.
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { WorkflowView } from "../components/blocks/workflow-view"; // a Makersclaw app
|
|
25
|
+
import { WorkflowView } from "@/components/makersclaw/blocks/workflow-view"; // a shadcn project
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
A client component. `makersclaw add workflow-view` lays `run-history`
|
|
29
|
+
with it. Its own width decides its layout: under about 768 px the
|
|
30
|
+
history and the overview stack under the graph. The graph always runs
|
|
31
|
+
top to bottom, every step a full-width node with its whole title (a
|
|
32
|
+
left-to-right strip scrolled sideways and cut titles at a desk window's
|
|
33
|
+
width, 2026-10-11). It never scrolls sideways. It formats nothing.
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
- `title` — the job's name.
|
|
38
|
+
- `trigger` — `{ label, detail }`: what starts it, in words.
|
|
39
|
+
- `steps` — the chosen run's steps, `[{ label, status, detail? }]`.
|
|
40
|
+
`status` is `done` · `failed` · `running` · `needs_you` · `stopped` ·
|
|
41
|
+
`skipped` · `waiting` (not reached yet). useRun's `steps` pass as
|
|
42
|
+
they are.
|
|
43
|
+
- `result` — `{ label, detail, status }`: what the run wrote, or what
|
|
44
|
+
waits on the member.
|
|
45
|
+
- `runs` — RunHistory's rows (`{ id, status, title, line, when, cost }`),
|
|
46
|
+
newest first. `selected`, `onSelect(run)` — the run drawn; the picker
|
|
47
|
+
and the history both choose it.
|
|
48
|
+
- `overview` — RunOverview's props (`{ completed, failed, running, avg,
|
|
49
|
+
credits }`), or null.
|
|
50
|
+
- `live` — `{ on, onToggle(next), busy }`: the app's Live switch. Null:
|
|
51
|
+
no switch.
|
|
52
|
+
- `run` — `{ label, price, onRun, loading, disabled, why }`: "Trigger
|
|
53
|
+
manually" with its price on the button. Null: no button.
|
|
54
|
+
- `rerun` — `{ onRun, loading, label? }`: Re-run beside the picker.
|
|
55
|
+
- `plan` — the job's declared steps (useJobs' `job.plan`), drawn in
|
|
56
|
+
pencil before any run.
|
|
57
|
+
- `empty` — the honest line before any run: when it will first run, and
|
|
58
|
+
what it waits on.
|
|
59
|
+
- `className` — layout around it. Never colour.
|
|
60
|
+
|
|
61
|
+
## Hooks (lib/hooks.js)
|
|
62
|
+
|
|
63
|
+
- `useJobs()` — the job (`words`, `plan`, `enabled`), the app's `live`,
|
|
64
|
+
`may`, `runNow(id)`, `pause()`, `resume()`.
|
|
65
|
+
- `useRuns({ job })` — the runs and the overview.
|
|
66
|
+
- `useRun(id)` — the chosen run's `steps`, `text` and `needs`.
|
|
67
|
+
|
|
68
|
+
## Who may do what
|
|
69
|
+
|
|
70
|
+
- Any member reads the graph, the runs and the overview (a teammate's
|
|
71
|
+
ask is never among the runs: the door leaves it out).
|
|
72
|
+
- **Trigger manually**, **Re-run** and **Live**: the app's owner, a
|
|
73
|
+
workspace owner or an admin — `may.run` and `may.pause`. Pass `run`,
|
|
74
|
+
`rerun` and `live` only when they are true. The door refuses the
|
|
75
|
+
others anyway; a hidden button is never the guard.
|
|
76
|
+
|
|
77
|
+
## Never
|
|
78
|
+
|
|
79
|
+
- Never a press that spends without its price on it.
|
|
80
|
+
- Never an Approve: a step that needs the member says "Needs you" and
|
|
81
|
+
the screen opens the desk (`openRequest`).
|
|
82
|
+
- Never a status by colour alone: each comes with its mark and its
|
|
83
|
+
word.
|
|
84
|
+
- Never a steps list made up when the run has none: pass the `plan`, or
|
|
85
|
+
nothing, and the empty line.
|
|
86
|
+
|
|
87
|
+
## Example
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
"use client";
|
|
91
|
+
import { useState } from "react";
|
|
92
|
+
import { useJobs, useRun, useRuns } from "../lib/hooks";
|
|
93
|
+
import { WorkflowView } from "../components/blocks/workflow-view";
|
|
94
|
+
|
|
95
|
+
const at = (iso) => new Date(iso).toLocaleString("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
|
|
96
|
+
|
|
97
|
+
export default function MorningReadFlow({ jobId }) {
|
|
98
|
+
const { jobs, app, may, busy, appBusy, runNow, pause, resume } = useJobs();
|
|
99
|
+
const job = jobs.find((j) => j.id === jobId);
|
|
100
|
+
const { runs, overview } = useRuns({ job: jobId });
|
|
101
|
+
const [picked, setPicked] = useState(null);
|
|
102
|
+
const selected = picked ?? runs[0]?.id ?? null;
|
|
103
|
+
const { run, steps } = useRun(selected);
|
|
104
|
+
if (!job) return null;
|
|
105
|
+
return (
|
|
106
|
+
<WorkflowView
|
|
107
|
+
title={job.name}
|
|
108
|
+
trigger={{ label: job.words ?? "On its schedule", detail: job.does }}
|
|
109
|
+
steps={steps}
|
|
110
|
+
result={run ? { label: run.state === "needs_you" ? "Waiting for your yes on the desk" : run.report ?? "Done", status: run.state } : null}
|
|
111
|
+
runs={runs.map((r) => ({ id: r.id, status: r.state, title: `Run #${r.number}`, when: at(r.started_at) }))}
|
|
112
|
+
selected={selected}
|
|
113
|
+
onSelect={(r) => setPicked(r.id)}
|
|
114
|
+
overview={overview && { completed: overview.completed, failed: overview.failed, running: overview.running, avg: overview.avg_ms == null ? null : `${Math.round(overview.avg_ms / 1000)} s` }}
|
|
115
|
+
live={may.pause ? { on: Boolean(app?.live), busy: Boolean(appBusy), onToggle: (on) => (on ? resume() : pause()) } : null}
|
|
116
|
+
run={may.run ? { label: "Trigger manually", price: "about 700 cr", onRun: () => runNow(job.id), loading: busy[job.id] === "run" } : null}
|
|
117
|
+
rerun={may.run ? { onRun: () => runNow(job.id), loading: busy[job.id] === "run" } : null}
|
|
118
|
+
plan={job.plan}
|
|
119
|
+
empty={job.next_run_at ? `No runs yet. The first runs ${at(job.next_run_at)}.` : "No runs yet. It starts once it is switched on."}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "workflow-view",
|
|
3
|
+
"title": "WorkflowView",
|
|
4
|
+
"description": "One job of the app's agent drawn as a graph on a dotted ground: the Trigger (\"Every weekday at 07:00\", \"When a deal is created\") → each step of the chosen run with how it went, as a mark and a word → the result. Over it a run picker with Re-run; in its head the Live switch and \"Trigger manually\" with its price; beside it the run history and the overview (run-history). Before any run it draws the job's declared steps in pencil, with an honest empty line. Fed by useJobs, useRuns and useRun (lib/hooks.js). Under about 768 px of its own width the history stacks under the graph and the graph runs top to bottom.",
|
|
5
|
+
"useWhen": "Superseded by automation-editor and run-view for an app whose jobs run on the automations engine; still the block for an app that has not moved (its jobs are the agent's wakes). A job's own page: the founder opens an automation to see what it does, how its last runs went and to run it now — a morning read, an inbox sweep, a nightly enrichment, an event-triggered workflow. For the job as a card beside the day's work use schedule-card; for a plain list of runs use run-history.",
|
|
6
|
+
"tags": ["workflow", "automation", "graph", "pipeline", "steps", "trigger", "runs", "run history", "re-run", "live", "schedule", "agent", "nodes", "kit"],
|
|
7
|
+
"exports": ["WorkflowView"],
|
|
8
|
+
"files": ["index.js", "workflow-view.jsx"],
|
|
9
|
+
"requires": ["run-history"]
|
|
10
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// WorkflowView with a pretend job — the gallery's and the smoke test's
|
|
4
|
+
// surface. Not installed into apps. A job with runs (one going, one
|
|
5
|
+
// failed): pick a run, Re-run, Trigger manually, switch Live. Then the
|
|
6
|
+
// same job before any run, its plan in pencil. A width picker shows the
|
|
7
|
+
// layout at 1280, 800, 368 and 320 px of the block's own width.
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { Button, ButtonGroup } from "../components/ui";
|
|
10
|
+
import { WorkflowView } from "../components/blocks/workflow-view";
|
|
11
|
+
|
|
12
|
+
const STEPS = {
|
|
13
|
+
r14: [
|
|
14
|
+
{ label: "Read mail", status: "running", detail: "12 of 40 emails since yesterday" },
|
|
15
|
+
{ label: "Find people", status: "waiting" },
|
|
16
|
+
{ label: "Draft follow-ups", status: "waiting" },
|
|
17
|
+
],
|
|
18
|
+
r13: [
|
|
19
|
+
{ label: "Read mail", status: "done", detail: "38 emails, 3 meetings" },
|
|
20
|
+
{ label: "Find people", status: "done", detail: "4 new, 3 details" },
|
|
21
|
+
{ label: "Draft follow-ups", status: "failed", detail: "The model timed out after 60 s; nothing was sent" },
|
|
22
|
+
],
|
|
23
|
+
r12: [
|
|
24
|
+
{ label: "Read mail", status: "done", detail: "22 emails" },
|
|
25
|
+
{ label: "Find people", status: "done", detail: "1 new" },
|
|
26
|
+
{ label: "Draft follow-ups", status: "needs_you", detail: "3 drafts wait for your yes" },
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
const RESULTS = {
|
|
30
|
+
r14: { label: "Writing as it reads", detail: "People and deals appear on Today as they are found", status: "running" },
|
|
31
|
+
r13: { label: "Wrote 4 people, 1 deal move", detail: "Follow-ups not drafted", status: "failed" },
|
|
32
|
+
r12: { label: "Waiting for your yes on the desk", detail: "3 follow-ups to send", status: "needs_you" },
|
|
33
|
+
};
|
|
34
|
+
const RUNS = [
|
|
35
|
+
{ id: "r14", status: "running", title: "Run #14 · Wed 30 Sep, 07:00", when: "now" },
|
|
36
|
+
{ id: "r13", status: "failed", title: "Run #13 · Tue 29 Sep, 07:02", line: "Drafting failed: the model timed out", when: "yesterday", cost: "640 cr" },
|
|
37
|
+
{ id: "r12", status: "needs_you", title: "Run #12 · Mon 28 Sep, 16:10", line: "Run now, by Owen", when: "2 days ago", cost: "420 cr" },
|
|
38
|
+
];
|
|
39
|
+
const PLAN = ["Read mail since the last read", "Find people and details", "Draft follow-ups owed"];
|
|
40
|
+
const WIDTHS = [1280, 800, 368, 320];
|
|
41
|
+
|
|
42
|
+
export default function WorkflowViewDemo() {
|
|
43
|
+
const [width, setWidth] = useState(800);
|
|
44
|
+
const [selected, setSelected] = useState("r13");
|
|
45
|
+
const [live, setLive] = useState(true);
|
|
46
|
+
const [busy, setBusy] = useState(null);
|
|
47
|
+
const press = (what, then) => {
|
|
48
|
+
setBusy(what);
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
then?.();
|
|
51
|
+
setBusy(null);
|
|
52
|
+
}, 800);
|
|
53
|
+
};
|
|
54
|
+
return (
|
|
55
|
+
<div className="grid gap-6 p-4">
|
|
56
|
+
<ButtonGroup aria-label="The block's width">
|
|
57
|
+
{WIDTHS.map((w) => (
|
|
58
|
+
<Button key={w} size="sm" variant={w === width ? "primary" : "secondary"} onClick={() => setWidth(w)}>
|
|
59
|
+
{w} px
|
|
60
|
+
</Button>
|
|
61
|
+
))}
|
|
62
|
+
</ButtonGroup>
|
|
63
|
+
<div className="w-full" style={{ maxWidth: width }}>
|
|
64
|
+
<WorkflowView
|
|
65
|
+
title="Morning read"
|
|
66
|
+
trigger={{ label: "Every weekday at 07:00", detail: "Starts on its own; Owen can run it now" }}
|
|
67
|
+
steps={STEPS[selected]}
|
|
68
|
+
result={RESULTS[selected]}
|
|
69
|
+
runs={RUNS}
|
|
70
|
+
selected={selected}
|
|
71
|
+
onSelect={(r) => setSelected(r.id)}
|
|
72
|
+
overview={{ completed: 149, failed: 2, running: 1, avg: "1 min 52 s", credits: { used: 4180, of: 10000, words: "4,180 of 10,000 this week" } }}
|
|
73
|
+
live={{ on: live, busy: busy === "live", onToggle: (on) => press("live", () => setLive(on)) }}
|
|
74
|
+
run={{ label: "Trigger manually", price: "about 700 cr", onRun: () => press("run"), loading: busy === "run" }}
|
|
75
|
+
rerun={{ onRun: () => press("rerun"), loading: busy === "rerun" }}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="w-full" style={{ maxWidth: width }}>
|
|
79
|
+
<WorkflowView
|
|
80
|
+
title="Friday review"
|
|
81
|
+
trigger={{ label: "Fridays at 16:00" }}
|
|
82
|
+
plan={PLAN}
|
|
83
|
+
empty="No runs yet. The first runs Fri 9 Oct, 16:00, once a mailbox is connected."
|
|
84
|
+
live={{ on: false, onToggle: () => {} }}
|
|
85
|
+
run={{ label: "Trigger manually", price: "about 900 cr", disabled: true, why: "Connect a mailbox first" }}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|