Changeset 0.28.0.1 (#173)

This commit is contained in:
2026-03-11 14:45:37 +00:00
parent 93c72daadf
commit 58313f7e31
57 changed files with 5139 additions and 3206 deletions

View File

@@ -1,21 +1,29 @@
package handlers
import (
"encoding/json"
"net/http"
"time"
"github.com/gin-gonic/gin"
"git.gobha.me/xcaliber/chat-switchboard/database"
"git.gobha.me/xcaliber/chat-switchboard/events"
)
// ── Workflow Assignment Handler ─────────────
// Manages the assignment queue for human review stages.
type WorkflowAssignmentHandler struct{}
type WorkflowAssignmentHandler struct {
hub *events.Hub
}
func NewWorkflowAssignmentHandler() *WorkflowAssignmentHandler {
return &WorkflowAssignmentHandler{}
func NewWorkflowAssignmentHandler(hub ...*events.Hub) *WorkflowAssignmentHandler {
h := &WorkflowAssignmentHandler{}
if len(hub) > 0 {
h.hub = hub[0]
}
return h
}
type assignmentRow struct {
@@ -53,7 +61,7 @@ func (h *WorkflowAssignmentHandler) ListForTeam(c *gin.Context) {
for rows.Next() {
var a assignmentRow
if err := rows.Scan(&a.ID, &a.ChannelID, &a.Stage, &a.TeamID,
&a.AssignedTo, &a.Status, &a.CreatedAt, &a.ClaimedAt, &a.CompletedAt); err != nil {
&a.AssignedTo, &a.Status, database.ST(&a.CreatedAt), database.SNT(&a.ClaimedAt), database.SNT(&a.CompletedAt)); err != nil {
continue
}
result = append(result, a)
@@ -64,17 +72,20 @@ func (h *WorkflowAssignmentHandler) ListForTeam(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": result})
}
// ListForUser returns assignments claimed by the current user.
// ListForUser returns assignments claimed by the current user plus
// unassigned assignments for teams the user belongs to.
// GET /api/v1/workflow-assignments/mine
func (h *WorkflowAssignmentHandler) ListMine(c *gin.Context) {
userID := c.GetString("user_id")
rows, err := database.DB.QueryContext(c.Request.Context(), database.Q(`
SELECT id, channel_id, stage, team_id, assigned_to, status,
created_at, claimed_at, completed_at
FROM workflow_assignments
WHERE assigned_to = $1 AND status = 'claimed'
ORDER BY claimed_at DESC
`), userID)
SELECT DISTINCT wa.id, wa.channel_id, wa.stage, wa.team_id, wa.assigned_to, wa.status,
wa.created_at, wa.claimed_at, wa.completed_at
FROM workflow_assignments wa
LEFT JOIN team_members tm ON tm.team_id = wa.team_id AND tm.user_id = $1
WHERE (wa.assigned_to = $2 AND wa.status = 'claimed')
OR (wa.status = 'unassigned' AND tm.user_id IS NOT NULL)
ORDER BY wa.created_at DESC
`), userID, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list assignments"})
return
@@ -85,7 +96,7 @@ func (h *WorkflowAssignmentHandler) ListMine(c *gin.Context) {
for rows.Next() {
var a assignmentRow
if err := rows.Scan(&a.ID, &a.ChannelID, &a.Stage, &a.TeamID,
&a.AssignedTo, &a.Status, &a.CreatedAt, &a.ClaimedAt, &a.CompletedAt); err != nil {
&a.AssignedTo, &a.Status, database.ST(&a.CreatedAt), database.SNT(&a.ClaimedAt), database.SNT(&a.CompletedAt)); err != nil {
continue
}
result = append(result, a)
@@ -117,6 +128,21 @@ func (h *WorkflowAssignmentHandler) Claim(c *gin.Context) {
c.JSON(http.StatusConflict, gin.H{"error": "assignment already claimed or not found"})
return
}
// Emit workflow.claimed WS event
if h.hub != nil {
payload, _ := json.Marshal(map[string]any{
"assignment_id": assignmentID,
"claimed_by": userID,
})
h.hub.GetBus().Publish(events.Event{
Label: "workflow.claimed",
Room: assignmentID,
Payload: payload,
Ts: now.UnixMilli(),
})
}
c.JSON(http.StatusOK, gin.H{"claimed": true, "assignment_id": assignmentID})
}