Feat v0.3.6 demo workflows + interactive demo
Four example workflow packages proving the engine end-to-end: - Bug Report Triage: public entry, branch routing, SLA timer - Employee Onboarding: Starlark automated stages, signoff gate - Content Approval: multi-party signoff, revision cycle loop - Webhook Notifier: http.post, connections fallback, delivery logging Demo surface at /s/workflow-demo with cards, stage diagrams, Starlark viewer, API examples, active/published status, and copyable public links. Engine fixes: started_by in automated context, sla_seconds in package installer, parseSnapshotStages for wrapped/legacy formats. Platform fixes: extension SDK boot in base.html (Preact globals + boot()), admin teams paginated response extraction, workflow adoption endpoint (POST /teams/:teamId/workflows/:id/adopt), team-admin copyable link. Review pass remains on roadmap for remaining UI polish. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,53 @@ func (h *WorkflowHandler) requireTeamWorkflow(c *gin.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ── Adopt Global Workflow ────────────────────
|
||||
|
||||
// AdoptTeamWorkflow claims a global (team_id=NULL) workflow for this team.
|
||||
// POST /api/v1/teams/:teamId/workflows/:id/adopt
|
||||
func (h *WorkflowHandler) AdoptTeamWorkflow(c *gin.Context) {
|
||||
teamID := c.Param("teamId")
|
||||
wfID := c.Param("id")
|
||||
|
||||
w, err := h.stores.Workflows.GetByID(c.Request.Context(), wfID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "workflow not found"})
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load workflow"})
|
||||
}
|
||||
return
|
||||
}
|
||||
if w.TeamID != nil {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "workflow already belongs to a team"})
|
||||
return
|
||||
}
|
||||
|
||||
patch := models.WorkflowPatch{TeamID: &teamID}
|
||||
if err := h.stores.Workflows.Update(c.Request.Context(), wfID, patch); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to adopt workflow"})
|
||||
return
|
||||
}
|
||||
|
||||
// Return the updated workflow
|
||||
c.Set("id", wfID)
|
||||
h.Get(c)
|
||||
}
|
||||
|
||||
// ListGlobalWorkflows returns unowned workflows available for adoption.
|
||||
// GET /api/v1/teams/:teamId/workflows/available
|
||||
func (h *WorkflowHandler) ListGlobalWorkflows(c *gin.Context) {
|
||||
result, err := h.stores.Workflows.ListGlobal(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list workflows"})
|
||||
return
|
||||
}
|
||||
if result == nil {
|
||||
result = []models.Workflow{}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": result})
|
||||
}
|
||||
|
||||
// ── Workflow CRUD ───────────────────────────
|
||||
|
||||
// ListTeamWorkflows returns workflows owned by the team.
|
||||
|
||||
Reference in New Issue
Block a user