579 lines
24 KiB
Go
579 lines
24 KiB
Go
// Package dashboard provides dashboard, inbox, activity, and workspace-level git HTTP handlers.
|
|
package dashboard
|
|
|
|
import "github.com/kandev/kandev/internal/office/models"
|
|
|
|
// RunActivityDay holds aggregated run outcome counts for a single calendar day.
|
|
type RunActivityDay struct {
|
|
Date string `json:"date"`
|
|
Succeeded int `json:"succeeded"`
|
|
Failed int `json:"failed"`
|
|
Other int `json:"other"`
|
|
}
|
|
|
|
// TaskBreakdown holds task counts bucketed by status category.
|
|
type TaskBreakdown struct {
|
|
Open int `json:"open"`
|
|
InProgress int `json:"in_progress"`
|
|
Blocked int `json:"blocked"`
|
|
Done int `json:"done"`
|
|
}
|
|
|
|
// RecentTaskDTO represents a single recently-updated task for the dashboard.
|
|
type RecentTaskDTO struct {
|
|
ID string `json:"id"`
|
|
Identifier string `json:"identifier"`
|
|
Title string `json:"title"`
|
|
Status string `json:"status"`
|
|
AssigneeAgentProfileID string `json:"assigneeAgentProfileId,omitempty"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
}
|
|
|
|
// LiveRunDTO represents a single agent run (active or recently finished).
|
|
type LiveRunDTO struct {
|
|
AgentID string `json:"agentId"`
|
|
AgentName string `json:"agentName"`
|
|
TaskID string `json:"taskId"`
|
|
TaskTitle string `json:"taskTitle"`
|
|
TaskIdentifier string `json:"taskIdentifier"`
|
|
Status string `json:"status"`
|
|
DurationMs int64 `json:"durationMs"`
|
|
StartedAt string `json:"startedAt"`
|
|
FinishedAt string `json:"finishedAt,omitempty"`
|
|
}
|
|
|
|
// LiveRunsResponse wraps a list of live run DTOs.
|
|
type LiveRunsResponse struct {
|
|
Runs []LiveRunDTO `json:"runs"`
|
|
}
|
|
|
|
// AgentSummariesResponse wraps the list returned by GET
|
|
// /workspaces/:wsId/agent-summaries. The AgentSummary struct (defined in
|
|
// service.go) already carries snake_case JSON tags so it serialises
|
|
// directly without an extra DTO layer.
|
|
type AgentSummariesResponse struct {
|
|
Agents []AgentSummary `json:"agents"`
|
|
}
|
|
|
|
// DashboardResponse wraps the full dashboard data.
|
|
type DashboardResponse struct {
|
|
AgentCount int `json:"agent_count"`
|
|
RunningCount int `json:"running_count"`
|
|
PausedCount int `json:"paused_count"`
|
|
ErrorCount int `json:"error_count"`
|
|
TasksInProgress int `json:"tasks_in_progress"`
|
|
OpenTasks int `json:"open_tasks"`
|
|
BlockedTasks int `json:"blocked_tasks"`
|
|
MonthSpendSubcents int64 `json:"month_spend_subcents"`
|
|
PendingApprovals int `json:"pending_approvals"`
|
|
RecentActivity []*models.ActivityEntry `json:"recent_activity"`
|
|
TaskCount int `json:"task_count"`
|
|
SkillCount int `json:"skill_count"`
|
|
RoutineCount int `json:"routine_count"`
|
|
RunActivity []RunActivityDay `json:"run_activity"`
|
|
TaskBreakdown TaskBreakdown `json:"task_breakdown"`
|
|
RecentTasks []RecentTaskDTO `json:"recent_tasks"`
|
|
// AgentSummaries is the per-agent card payload, embedded in the
|
|
// dashboard response so the dashboard renders in a single round-trip
|
|
// (Stream A + G of office optimization). Always non-nil; empty array
|
|
// for workspaces with no agents.
|
|
AgentSummaries []AgentSummary `json:"agent_summaries"`
|
|
}
|
|
|
|
// NewDashboardResponse maps the internal dashboard model to the public JSON
|
|
// shape consumed by both the HTTP endpoint and the Go-rendered SPA boot payload.
|
|
func NewDashboardResponse(data *models.DashboardData, summaries []AgentSummary) DashboardResponse {
|
|
if summaries == nil {
|
|
summaries = []AgentSummary{}
|
|
}
|
|
if data == nil {
|
|
return DashboardResponse{AgentSummaries: summaries}
|
|
}
|
|
|
|
runActivity := make([]RunActivityDay, len(data.RunActivity))
|
|
for i, d := range data.RunActivity {
|
|
runActivity[i] = RunActivityDay{
|
|
Date: d.Date,
|
|
Succeeded: d.Succeeded,
|
|
Failed: d.Failed,
|
|
Other: d.Other,
|
|
}
|
|
}
|
|
|
|
recentTasks := make([]RecentTaskDTO, len(data.RecentTasks))
|
|
for i, t := range data.RecentTasks {
|
|
recentTasks[i] = RecentTaskDTO{
|
|
ID: t.ID,
|
|
Identifier: t.Identifier,
|
|
Title: t.Title,
|
|
Status: dbStateToOfficeStatus(t.Status),
|
|
AssigneeAgentProfileID: t.AssigneeAgentProfileID,
|
|
UpdatedAt: t.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
return DashboardResponse{
|
|
AgentCount: data.AgentCount,
|
|
RunningCount: data.RunningCount,
|
|
PausedCount: data.PausedCount,
|
|
ErrorCount: data.ErrorCount,
|
|
TasksInProgress: data.TasksInProgress,
|
|
OpenTasks: data.OpenTasks,
|
|
BlockedTasks: data.BlockedTasks,
|
|
MonthSpendSubcents: data.MonthSpendSubcents,
|
|
PendingApprovals: data.PendingApprovals,
|
|
RecentActivity: data.RecentActivity,
|
|
TaskCount: data.TaskCount,
|
|
SkillCount: data.SkillCount,
|
|
RoutineCount: data.RoutineCount,
|
|
RunActivity: runActivity,
|
|
TaskBreakdown: TaskBreakdown{
|
|
Open: data.TaskBreakdown.Open,
|
|
InProgress: data.TaskBreakdown.InProgress,
|
|
Blocked: data.TaskBreakdown.Blocked,
|
|
Done: data.TaskBreakdown.Done,
|
|
},
|
|
RecentTasks: recentTasks,
|
|
AgentSummaries: summaries,
|
|
}
|
|
}
|
|
|
|
// InboxResponse wraps inbox items. TotalCount mirrors len(Items) and is
|
|
// included so the sidebar badge can read its count from the same payload
|
|
// that drives the inbox page (Stream F of office optimization), avoiding a
|
|
// separate /inbox?count=true round-trip.
|
|
type InboxResponse struct {
|
|
Items []*models.InboxItem `json:"items"`
|
|
TotalCount int `json:"total_count"`
|
|
}
|
|
|
|
// InboxCountResponse wraps the inbox item count.
|
|
type InboxCountResponse struct {
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
// ActivityListResponse wraps a list of activity entries.
|
|
type ActivityListResponse struct {
|
|
Activity []*models.ActivityEntry `json:"activity"`
|
|
}
|
|
|
|
// RunListResponse wraps a list of run requests. RunListItem is used in
|
|
// place of *models.Run so the list surface can expose the task_id that
|
|
// is otherwise buried inside the JSON-encoded payload.
|
|
type RunListResponse struct {
|
|
Runs []RunListItem `json:"runs"`
|
|
}
|
|
|
|
// RunListItem mirrors models.Run with the JSON keys flattened plus a
|
|
// derived task_id pulled from the run payload. The frontend run-queue
|
|
// surfaces (and the approval-flow E2E poll) read task_id directly.
|
|
type RunListItem struct {
|
|
ID string `json:"id"`
|
|
AgentProfileID string `json:"agent_profile_id"`
|
|
Reason string `json:"reason"`
|
|
Payload string `json:"payload"`
|
|
Status string `json:"status"`
|
|
CoalescedCount int `json:"coalesced_count"`
|
|
IdempotencyKey *string `json:"idempotency_key"`
|
|
ContextSnapshot string `json:"context_snapshot"`
|
|
RetryCount int `json:"retry_count"`
|
|
ScheduledRetryAt *string `json:"scheduled_retry_at,omitempty"`
|
|
CancelReason *string `json:"cancel_reason,omitempty"`
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
RequestedAt string `json:"requested_at"`
|
|
ClaimedAt *string `json:"claimed_at,omitempty"`
|
|
FinishedAt *string `json:"finished_at,omitempty"`
|
|
TaskID string `json:"task_id,omitempty"`
|
|
RoutingBlockedStatus string `json:"routing_blocked_status,omitempty"`
|
|
}
|
|
|
|
// AgentRunSummaryDTO is one row in the per-agent paginated runs list.
|
|
// It mirrors models.Run with snake_case JSON keys plus a derived
|
|
// short id (first 8 chars) so the frontend doesn't need to recompute
|
|
// the truncation in every cell.
|
|
type AgentRunSummaryDTO struct {
|
|
ID string `json:"id"`
|
|
IDShort string `json:"id_short"`
|
|
Reason string `json:"reason"`
|
|
Status string `json:"status"`
|
|
CancelReason *string `json:"cancel_reason,omitempty"`
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
TaskID string `json:"task_id,omitempty"`
|
|
// CommentID is set for runs triggered by a task comment so the
|
|
// frontend can deeplink the row to the originating comment.
|
|
CommentID string `json:"comment_id,omitempty"`
|
|
// RoutineID is set for runs triggered by a routine cron fire so
|
|
// the frontend can deeplink the row to the routine.
|
|
RoutineID string `json:"routine_id,omitempty"`
|
|
RequestedAt string `json:"requested_at"`
|
|
ClaimedAt string `json:"claimed_at,omitempty"`
|
|
FinishedAt string `json:"finished_at,omitempty"`
|
|
DurationMs int64 `json:"duration_ms,omitempty"`
|
|
}
|
|
|
|
// AgentRunsListResponse wraps the paginated runs list. NextCursor is
|
|
// the requested_at of the last row in the current page (RFC3339);
|
|
// empty when this is the final page.
|
|
type AgentRunsListResponse struct {
|
|
Runs []AgentRunSummaryDTO `json:"runs"`
|
|
NextCursor string `json:"next_cursor"`
|
|
NextID string `json:"next_id,omitempty"`
|
|
}
|
|
|
|
// RunCostSummaryDTO is the per-run token + cost rollup surfaced in
|
|
// the run detail header. CostSubcents stores hundredths of a cent.
|
|
type RunCostSummaryDTO struct {
|
|
InputTokens int64 `json:"input_tokens"`
|
|
OutputTokens int64 `json:"output_tokens"`
|
|
CachedTokens int64 `json:"cached_tokens"`
|
|
CostSubcents int64 `json:"cost_subcents"`
|
|
}
|
|
|
|
// RunInvocationDTO carries the per-run invocation details: the
|
|
// adapter family + working directory + optional command/env/prompt
|
|
// context. Populated best-effort from the agent instance + payload.
|
|
type RunInvocationDTO struct {
|
|
Adapter string `json:"adapter,omitempty"`
|
|
Model string `json:"model,omitempty"`
|
|
WorkingDir string `json:"working_dir,omitempty"`
|
|
Command string `json:"command,omitempty"`
|
|
Env map[string]string `json:"env,omitempty"`
|
|
}
|
|
|
|
// RunSessionDTO carries the session ids associated with a run.
|
|
// SessionIDBefore / SessionIDAfter are placeholders today (we don't
|
|
// track them explicitly); v1 surfaces only the run's claimed session
|
|
// id.
|
|
type RunSessionDTO struct {
|
|
SessionID string `json:"session_id,omitempty"`
|
|
SessionIDBefore string `json:"session_id_before,omitempty"`
|
|
SessionIDAfter string `json:"session_id_after,omitempty"`
|
|
}
|
|
|
|
// RunEventDTO mirrors models.RunEvent with stable JSON keys for the
|
|
// frontend events log.
|
|
type RunEventDTO struct {
|
|
Seq int `json:"seq"`
|
|
EventType string `json:"event_type"`
|
|
Level string `json:"level"`
|
|
Payload string `json:"payload"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
// RunDetailResponse is the per-run aggregate returned by
|
|
// GET /agents/:id/runs/:runId. It composes the run row with the cost
|
|
// rollup, session ids, invocation panel, tasks-touched ids, and the
|
|
// events log so the run detail page can render in a single
|
|
// round-trip.
|
|
//
|
|
// PR 1 of office-heartbeat-rework added the AssembledPrompt /
|
|
// SummaryInjected / ResultJSON / ContextSnapshot / OutputSummary
|
|
// fields so the run-detail UI can render the exact prompt the agent
|
|
// received plus the structured adapter output. Today these are
|
|
// populated for every dispatch (the persistence is unconditional);
|
|
// the prepended-summary slice (SummaryInjected) only fires on
|
|
// taskless runs and stays empty for the normal task-bound path.
|
|
type RunDetailResponse struct {
|
|
ID string `json:"id"`
|
|
IDShort string `json:"id_short"`
|
|
AgentID string `json:"agent_id"`
|
|
AgentName string `json:"agent_name,omitempty"`
|
|
Reason string `json:"reason"`
|
|
Status string `json:"status"`
|
|
CancelReason *string `json:"cancel_reason,omitempty"`
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
TaskID string `json:"task_id,omitempty"`
|
|
RequestedAt string `json:"requested_at"`
|
|
ClaimedAt string `json:"claimed_at,omitempty"`
|
|
FinishedAt string `json:"finished_at,omitempty"`
|
|
DurationMs int64 `json:"duration_ms,omitempty"`
|
|
Costs RunCostSummaryDTO `json:"costs"`
|
|
Session RunSessionDTO `json:"session"`
|
|
Invocation RunInvocationDTO `json:"invocation"`
|
|
Runtime RunRuntimeDTO `json:"runtime"`
|
|
TasksTouched []string `json:"tasks_touched"`
|
|
Events []RunEventDTO `json:"events"`
|
|
// AssembledPrompt is the final prompt string the agent received,
|
|
// captured at dispatch. Empty for runs that pre-date PR 1.
|
|
AssembledPrompt string `json:"assembled_prompt,omitempty"`
|
|
// SummaryInjected is the continuation-summary slice prepended to
|
|
// the prompt at dispatch (taskless runs only). Empty for task-
|
|
// bound runs.
|
|
SummaryInjected string `json:"summary_injected,omitempty"`
|
|
// ResultJSON is the structured adapter output captured at run
|
|
// completion. Defaults to "{}" for runs that never received one.
|
|
ResultJSON string `json:"result_json,omitempty"`
|
|
// ContextSnapshot mirrors runs.context_snapshot — the runtime
|
|
// context the run dispatcher built at claim time. Surfaced for
|
|
// debugging.
|
|
ContextSnapshot string `json:"context_snapshot,omitempty"`
|
|
// ContinuationScope is the persisted summary scope selected when
|
|
// the run was created. It lets run inspection show which
|
|
// continuation-summary chain the run reads and updates.
|
|
ContinuationScope string `json:"continuation_scope,omitempty"`
|
|
// OutputSummary mirrors runs.output_summary — the free-form
|
|
// agent output captured at run finish. Kept alongside ResultJSON
|
|
// because legacy adapters populate this and not result_json.
|
|
OutputSummary string `json:"output_summary,omitempty"`
|
|
// Routing carries the provider-routing snapshot for the run +
|
|
// every recorded route attempt (skipped + launched). Empty when
|
|
// the run did not go through the routing path.
|
|
Routing *RunRouting `json:"routing,omitempty"`
|
|
}
|
|
|
|
type RunRuntimeDTO struct {
|
|
Capabilities map[string]interface{} `json:"capabilities"`
|
|
InputSnapshot map[string]interface{} `json:"input_snapshot"`
|
|
SessionID string `json:"session_id,omitempty"`
|
|
Skills []RunSkillDTO `json:"skills"`
|
|
}
|
|
|
|
type RunSkillDTO struct {
|
|
SkillID string `json:"skill_id"`
|
|
DisplayName string `json:"display_name,omitempty"`
|
|
Slug string `json:"slug,omitempty"`
|
|
LabelSource string `json:"label_source,omitempty"`
|
|
Version string `json:"version"`
|
|
ContentHash string `json:"content_hash"`
|
|
MaterializedPath string `json:"materialized_path"`
|
|
}
|
|
|
|
// TaskSearchResultDTO represents a single task in search results.
|
|
type TaskSearchResultDTO struct {
|
|
ID string `json:"id"`
|
|
WorkspaceID string `json:"workspaceId"`
|
|
Identifier string `json:"identifier"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description,omitempty"`
|
|
Status string `json:"status"`
|
|
Priority string `json:"priority"`
|
|
ParentID string `json:"parentId,omitempty"`
|
|
ProjectID string `json:"projectId,omitempty"`
|
|
AssigneeAgentProfileID string `json:"assigneeAgentProfileId,omitempty"`
|
|
Labels string `json:"labels,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
}
|
|
|
|
// TaskSearchResponse wraps search results.
|
|
type TaskSearchResponse struct {
|
|
Tasks []*TaskSearchResultDTO `json:"tasks"`
|
|
}
|
|
|
|
// LabelDTO represents a label attached to an issue.
|
|
type LabelDTO struct {
|
|
Name string `json:"name"`
|
|
Color string `json:"color"`
|
|
}
|
|
|
|
// TaskDTO represents an office task for the frontend.
|
|
type TaskDTO struct {
|
|
ID string `json:"id"`
|
|
WorkspaceID string `json:"workspaceId"`
|
|
Identifier string `json:"identifier"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description,omitempty"`
|
|
Status string `json:"status"`
|
|
Priority string `json:"priority"`
|
|
ParentID string `json:"parentId,omitempty"`
|
|
ProjectID string `json:"projectId,omitempty"`
|
|
AssigneeAgentProfileID string `json:"assigneeAgentProfileId,omitempty"`
|
|
AssigneeUserID string `json:"assigneeUserId,omitempty"`
|
|
Labels []LabelDTO `json:"labels"`
|
|
Children []*TaskDTO `json:"children,omitempty"`
|
|
BlockedBy []string `json:"blockedBy,omitempty"`
|
|
Reviewers []string `json:"reviewers"`
|
|
Approvers []string `json:"approvers"`
|
|
Decisions []*DecisionDTO `json:"decisions,omitempty"`
|
|
// StartedAt/CompletedAt are derived from the task's status-change
|
|
// activity log, not a persisted column (there is no started_at /
|
|
// completed_at on the tasks table). Only the detail handler
|
|
// (taskRowToDTO is called for both list and detail responses, but
|
|
// the derivation needs the timeline it doesn't have) sets these; see
|
|
// deriveTaskTimestamps in handler.go.
|
|
StartedAt string `json:"startedAt,omitempty"`
|
|
CompletedAt string `json:"completedAt,omitempty"`
|
|
CreatedAt string `json:"createdAt"`
|
|
UpdatedAt string `json:"updatedAt"`
|
|
// IsSystem flags tasks that live in a kandev-managed system
|
|
// workflow (today: standing coordination; future: routine-fired).
|
|
// The Office Tasks UI hides these by default and surfaces a small
|
|
// "System" badge when the dev toggle reveals them.
|
|
IsSystem bool `json:"isSystem,omitempty"`
|
|
}
|
|
|
|
// TaskListResponse wraps a list of tasks.
|
|
type TaskListResponse struct {
|
|
Tasks []*TaskDTO `json:"tasks"`
|
|
}
|
|
|
|
// TimelineEventDTO represents a single chronological event in a task timeline.
|
|
type TimelineEventDTO struct {
|
|
Type string `json:"type"`
|
|
From string `json:"from,omitempty"`
|
|
To string `json:"to,omitempty"`
|
|
At string `json:"at"`
|
|
}
|
|
|
|
// TaskResponse wraps a single task with its timeline.
|
|
type TaskResponse struct {
|
|
Task *TaskDTO `json:"task"`
|
|
Timeline []TimelineEventDTO `json:"timeline"`
|
|
}
|
|
|
|
// CommentDTO represents a single task comment.
|
|
type CommentDTO struct {
|
|
ID string `json:"id"`
|
|
TaskID string `json:"taskId"`
|
|
AuthorType string `json:"authorType"`
|
|
AuthorID string `json:"authorId"`
|
|
Body string `json:"body"`
|
|
Source string `json:"source"`
|
|
CreatedAt string `json:"createdAt"`
|
|
// Per-comment run lifecycle. Only populated for user comments
|
|
// whose comment_created subscriber queued a task_comment run. Exact
|
|
// same-task wakes map by idempotency key; salted fan-out wakes map by
|
|
// payload.comment_id. The frontend reads these to render a Queued /
|
|
// Working / Failed badge on the triggering comment.
|
|
RunID string `json:"runId,omitempty"`
|
|
RunStatus string `json:"runStatus,omitempty"`
|
|
RunError string `json:"runError,omitempty"`
|
|
}
|
|
|
|
// CommentListResponse wraps a list of comments.
|
|
type CommentListResponse struct {
|
|
Comments []*CommentDTO `json:"comments"`
|
|
}
|
|
|
|
// CommentResponse wraps a single comment.
|
|
type CommentResponse struct {
|
|
Comment *CommentDTO `json:"comment"`
|
|
}
|
|
|
|
// CreateCommentRequest is the request body for creating a comment.
|
|
type CreateCommentRequest struct {
|
|
Body string `json:"body"`
|
|
AuthorType string `json:"author_type"`
|
|
}
|
|
|
|
// UpdateWorkspaceSettingsRequest is the request body for updating workspace settings.
|
|
type UpdateWorkspaceSettingsRequest struct {
|
|
PermissionHandlingMode *string `json:"permission_handling_mode"`
|
|
RecoveryLookbackHours *int `json:"recovery_lookback_hours"`
|
|
RequireApprovalForNewAgents *bool `json:"require_approval_for_new_agents"`
|
|
RequireApprovalForTaskCompletion *bool `json:"require_approval_for_task_completion"`
|
|
RequireApprovalForSkillChanges *bool `json:"require_approval_for_skill_changes"`
|
|
}
|
|
|
|
// WorkspaceSettingsResponse wraps the workspace settings for GET /settings.
|
|
type WorkspaceSettingsResponse struct {
|
|
Settings *WorkspaceSettings `json:"settings"`
|
|
}
|
|
|
|
// GitCloneRequest is the request body for cloning a workspace from a git repo.
|
|
type GitCloneRequest struct {
|
|
RepoURL string `json:"repoUrl"`
|
|
Branch string `json:"branch"`
|
|
WorkspaceName string `json:"workspaceName"`
|
|
}
|
|
|
|
// GitPushRequest is the request body for pushing workspace changes.
|
|
type GitPushRequest struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// GitStatusResponse wraps the git status for a workspace.
|
|
type GitStatusResponse struct {
|
|
IsGit bool `json:"is_git"`
|
|
Branch string `json:"branch,omitempty"`
|
|
IsDirty bool `json:"is_dirty"`
|
|
HasRemote bool `json:"has_remote"`
|
|
Ahead int `json:"ahead"`
|
|
Behind int `json:"behind"`
|
|
CommitCount int `json:"commit_count"`
|
|
}
|
|
|
|
// DocumentDTO represents a task document for the frontend.
|
|
type DocumentDTO struct {
|
|
ID string `json:"id"`
|
|
TaskID string `json:"task_id"`
|
|
Key string `json:"key"`
|
|
Type string `json:"type"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content,omitempty"`
|
|
AuthorKind string `json:"author_kind"`
|
|
AuthorName string `json:"author_name"`
|
|
Filename string `json:"filename,omitempty"`
|
|
MimeType string `json:"mime_type,omitempty"`
|
|
SizeBytes int64 `json:"size_bytes,omitempty"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// DocumentRevisionDTO represents a single revision of a task document.
|
|
type DocumentRevisionDTO struct {
|
|
ID string `json:"id"`
|
|
TaskID string `json:"task_id"`
|
|
DocumentKey string `json:"document_key"`
|
|
RevisionNumber int `json:"revision_number"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
AuthorKind string `json:"author_kind"`
|
|
AuthorName string `json:"author_name"`
|
|
RevertOfRevisionID *string `json:"revert_of_revision_id,omitempty"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// DocumentListResponse wraps a list of task documents.
|
|
type DocumentListResponse struct {
|
|
Documents []*DocumentDTO `json:"documents"`
|
|
}
|
|
|
|
// DocumentResponse wraps a single task document.
|
|
type DocumentResponse struct {
|
|
Document *DocumentDTO `json:"document"`
|
|
}
|
|
|
|
// DocumentRevisionListResponse wraps a list of document revisions.
|
|
type DocumentRevisionListResponse struct {
|
|
Revisions []*DocumentRevisionDTO `json:"revisions"`
|
|
}
|
|
|
|
// DocumentRevisionResponse wraps a single document revision.
|
|
type DocumentRevisionResponse struct {
|
|
Revision *DocumentRevisionDTO `json:"revision"`
|
|
}
|
|
|
|
// CreateOrUpdateDocumentRequest is the request body for creating or updating a document.
|
|
type CreateOrUpdateDocumentRequest struct {
|
|
Type string `json:"type"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
AuthorKind string `json:"author_kind"`
|
|
AuthorName string `json:"author_name"`
|
|
}
|
|
|
|
// PermissionMeta describes a single permission for frontend rendering.
|
|
type PermissionMeta struct {
|
|
Key string `json:"key"`
|
|
Label string `json:"label"`
|
|
Description string `json:"description"`
|
|
Type string `json:"type"` // "bool" or "int"
|
|
}
|
|
|
|
// MetaResponse contains all office metadata for the frontend.
|
|
type MetaResponse struct {
|
|
Statuses []models.StatusMeta `json:"statuses"`
|
|
Priorities []models.PriorityMeta `json:"priorities"`
|
|
Roles []models.RoleMeta `json:"roles"`
|
|
ExecutorTypes []models.ExecutorTypeMeta `json:"executorTypes"`
|
|
SkillSourceTypes []models.SkillSourceTypeMeta `json:"skillSourceTypes"`
|
|
ProjectStatuses []models.ProjectStatusMeta `json:"projectStatuses"`
|
|
AgentStatuses []models.AgentStatusMeta `json:"agentStatuses"`
|
|
RoutineRunStatuses []models.RoutineRunStatusMeta `json:"routineRunStatuses"`
|
|
InboxItemTypes []models.InboxItemTypeMeta `json:"inboxItemTypes"`
|
|
Permissions []PermissionMeta `json:"permissions"`
|
|
PermissionDefaults map[string]map[string]interface{} `json:"permissionDefaults"`
|
|
}
|