package httpapi import ( "fmt" "net/http" "strconv" "strings" "inbox/internal/base/httpx" "inbox/internal/domain/topic" "inbox/internal/domain/workspace" ) func (h *Handler) dashboardMessages(w http.ResponseWriter, r *http.Request) { h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.Messages(r.Context(), ws) }) } func (h *Handler) dashboardTopics(w http.ResponseWriter, r *http.Request) { h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.Topics(r.Context(), ws) }) } func (h *Handler) dashboardTopicRecords(w http.ResponseWriter, r *http.Request) { spaceFilter := strings.TrimSpace(r.URL.Query().Get("space")) h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.TopicRecords(r.Context(), ws, spaceFilter) }) } func (h *Handler) dashboardSpaceTopics(w http.ResponseWriter, r *http.Request) { space, err := parseTopicSpace(r.PathValue("space")) if err != nil { httpx.WriteError(w, http.StatusBadRequest, err.Error()) return } h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.SpaceTopics(r.Context(), ws, space) }) } func (h *Handler) dashboardSpaceMessages(w http.ResponseWriter, r *http.Request) { space, err := parseTopicSpace(r.PathValue("space")) if err != nil { httpx.WriteError(w, http.StatusBadRequest, err.Error()) return } topicSlug := strings.TrimSpace(r.URL.Query().Get("topic")) if topicSlug == "" { httpx.WriteError(w, http.StatusBadRequest, "topic is required") return } h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.SpaceMessages(r.Context(), ws, space, topicSlug) }) } func (h *Handler) dashboardDispatch(w http.ResponseWriter, r *http.Request) { h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.Dispatch(r.Context(), ws) }) } func (h *Handler) dashboardDispatchLive(w http.ResponseWriter, r *http.Request) { topicSlug := strings.TrimSpace(r.URL.Query().Get("topic")) roleName := strings.TrimSpace(r.URL.Query().Get("role")) if topicSlug == "" || roleName == "" { httpx.WriteError(w, http.StatusBadRequest, "topic and role are required") return } afterSeq, err := parseIntQuery(r, "offset") if err != nil { httpx.WriteError(w, http.StatusBadRequest, "offset must be an integer") return } h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.DispatchLive(r.Context(), ws, topicSlug, roleName, afterSeq) }) } func (h *Handler) dashboardRoles(w http.ResponseWriter, r *http.Request) { ws, ok := h.optionalWorkspace(w, r) if !ok { return } payload, err := h.Dashboard.Roles(r.Context(), ws) writePayload(w, http.StatusOK, payload, err) } func (h *Handler) dashboardWorkflowBoard(w http.ResponseWriter, r *http.Request) { activeTopic := strings.TrimSpace(r.URL.Query().Get("topic")) h.writeWorkspacePayload(w, r, http.StatusOK, func(ws workspace.Workspace) (any, error) { return h.Dashboard.WorkflowBoard(r.Context(), ws, activeTopic) }) } func (h *Handler) resolveWorkspace(r *http.Request) (workspace.Workspace, error) { return h.Workspaces.Resolve( r.Context(), r.URL.Query().Get("workspace_id"), firstNonEmpty(r.URL.Query().Get("workspace"), r.URL.Query().Get("workspace_slug")), ) } func (h *Handler) requireWorkspace(w http.ResponseWriter, r *http.Request) (workspace.Workspace, bool) { ws, err := h.resolveWorkspace(r) if err != nil { writeStoreError(w, err) return workspace.Workspace{}, false } return ws, true } func (h *Handler) optionalWorkspace(w http.ResponseWriter, r *http.Request) (*workspace.Workspace, bool) { if strings.TrimSpace(firstNonEmpty( r.URL.Query().Get("workspace_id"), r.URL.Query().Get("workspace"), r.URL.Query().Get("workspace_slug"), )) == "" { return nil, true } ws, err := h.resolveWorkspace(r) if err != nil { writeStoreError(w, err) return nil, false } return &ws, true } func (h *Handler) writeWorkspacePayload(w http.ResponseWriter, r *http.Request, status int, load func(workspace.Workspace) (any, error)) { ws, ok := h.requireWorkspace(w, r) if !ok { return } payload, err := load(ws) writePayload(w, status, payload, err) } func writePayload(w http.ResponseWriter, status int, payload any, err error) { if err != nil { writeStoreError(w, err) return } httpx.WriteJSON(w, status, payload) } func parseTopicSpace(value string) (topic.Space, error) { switch topic.Space(value) { case topic.SpaceClarify, topic.SpaceWorkflow: return topic.Space(value), nil default: return "", fmt.Errorf("invalid space %q", value) } } func parseIntQuery(r *http.Request, key string) (int, error) { value := strings.TrimSpace(r.URL.Query().Get(key)) if value == "" { return 0, nil } return strconv.Atoi(value) } func firstNonEmpty(values ...string) string { for _, value := range values { if strings.TrimSpace(value) != "" { return value } } return "" }