tracker/workflow.go
package main
import (
"fmt"
"strconv"
)
// Decision metadata belongs to the canonical use case, so generated drafts and
// source fingerprints agree. Persisted, user-edited diagrams are never replaced.
func workflowDiagram(u UseCase) Diagram {
d := Diagram{Nodes: []DiagramNode{}, Edges: []DiagramEdge{}}
gates := map[int]WorkflowDecision{}
for _, g := range u.Workflow.Decisions {
gates[g.Before] = g
}
entry := func(i int) string {
if i > len(u.Flow) {
return "end"
}
if _, ok := gates[i]; ok {
return fmt.Sprintf("decision-%d", i)
}
return fmt.Sprintf("step-%d", i)
}
addNode := func(id, kind, title, description string, x, y float64) {
d.Nodes = append(d.Nodes, DiagramNode{ID: id, Type: kind, Title: title, Description: description, X: x, Y: y})
}
edge := func(from, to, label string) {
d.Edges = append(d.Edges, DiagramEdge{ID: fmt.Sprintf("edge-%d", len(d.Edges)+1), Source: from, Target: to, Label: label, Head: "arrow", Tail: "none"})
}
addNode("start", "start", "เริ่มต้น", u.Start, 100, 1000)
edge("start", entry(1), "")
x := 440.0
for i, f := range u.Flow {
step := i + 1
id := fmt.Sprintf("step-%d", step)
if g, ok := gates[step]; ok {
gid := fmt.Sprintf("decision-%d", step)
addNode(gid, "decision", g.Title, g.Description, x, 1000)
x += 340
for j, b := range g.Branches {
target := id
if b.Outcome != "" {
target = fmt.Sprintf("outcome-%d-%d", step, j+1)
addNode(target, "end", b.Label, b.Outcome, x, float64(100+j*260))
} else if b.Target != step {
target = entry(b.Target)
}
edge(gid, target, b.Label)
}
}
addNode(id, "process", fmt.Sprintf("%d. %s", step, f[0]), f[1], x, 1000)
x += 340
if outcome, ok := u.Workflow.Stop[strconv.Itoa(step)]; ok {
target := fmt.Sprintf("stopped-%d", step)
addNode(target, "end", "สิ้นสุดเส้นทางนี้", outcome, x, 100)
edge(id, target, "")
} else if next, ok := u.Workflow.Next[strconv.Itoa(step)]; ok {
edge(id, entry(next), "")
} else {
edge(id, entry(step+1), "")
}
}
addNode("end", "end", "สิ้นสุด", u.Outcome, x, 1000)
return d
}