diff --git a/apps/console/src/_locales/en-US.json b/apps/console/src/_locales/en-US.json index 32b4325473..32d92afcd2 100644 --- a/apps/console/src/_locales/en-US.json +++ b/apps/console/src/_locales/en-US.json @@ -766,14 +766,14 @@ "timeEstimate": { "label": "Time estimate" }, "deadline": { "label": "Deadline" } }, - "states": { "todo": "To do", "inProgress": "In progress", "done": "Done" }, + "states": { "backlog": "Backlog", "todo": "To do", "inProgress": "In progress", "done": "Done", "canceled": "Canceled", "duplicate": "Duplicate" }, "priorities": { "urgent": "Urgent", "high": "High", "medium": "Medium", "low": "Low" }, "messages": { "created": "Task created successfully.", "updated": "Task updated successfully." }, "errors": { "create": "Failed to create task", "update": "Failed to update task" }, "actions": { "create": "Create task", "update": "Update task" } }, "tasksCard": { - "states": { "all": "All", "todo": "To do", "inProgress": "In progress", "done": "Done" }, + "states": { "all": "All", "backlog": "Backlog", "todo": "To do", "inProgress": "In progress", "done": "Done", "canceled": "Canceled", "duplicate": "Duplicate" }, "empty": "No tasks", "error": { "title": "Error", "reorder": "Failed to reorder task." }, "dragInstructions": { @@ -781,7 +781,7 @@ "state": "Drag and drop to reorder tasks" }, "deleteConfirmation": "Are you sure you want to delete this task?", - "actions": { "moveToInProgress": "Move to In progress", "moveToDone": "Move to Done", "edit": "Edit", "delete": "Delete" } + "actions": { "moveToTodo": "Move to To do", "moveToInProgress": "Move to In progress", "moveToDone": "Move to Done", "edit": "Edit", "delete": "Delete" } }, "deleteTrustCenterReferenceDialog": { "title": "Delete Reference", diff --git a/apps/console/src/_locales/fr-FR.json b/apps/console/src/_locales/fr-FR.json index 749917fd96..cfd950984b 100644 --- a/apps/console/src/_locales/fr-FR.json +++ b/apps/console/src/_locales/fr-FR.json @@ -1297,7 +1297,10 @@ "states": { "todo": "À faire", "inProgress": "En cours", - "done": "Terminé" + "done": "Terminé", + "backlog": "Backlog", + "canceled": "Annulé", + "duplicate": "Doublon" }, "priorities": { "urgent": "Urgente", @@ -1323,7 +1326,10 @@ "all": "Toutes", "todo": "À faire", "inProgress": "En cours", - "done": "Terminé" + "done": "Terminé", + "backlog": "Backlog", + "canceled": "Annulé", + "duplicate": "Doublon" }, "empty": "Aucune tâche", "error": { @@ -1336,6 +1342,7 @@ }, "deleteConfirmation": "Voulez-vous vraiment supprimer cette tâche ?", "actions": { + "moveToTodo": "Déplacer vers À faire", "moveToInProgress": "Déplacer vers En cours", "moveToDone": "Déplacer vers Terminé", "edit": "Modifier", diff --git a/apps/console/src/_locales/nl-NL.json b/apps/console/src/_locales/nl-NL.json index 1c48236e30..a4e8f2edff 100644 --- a/apps/console/src/_locales/nl-NL.json +++ b/apps/console/src/_locales/nl-NL.json @@ -1297,7 +1297,10 @@ "states": { "todo": "Te doen", "inProgress": "In uitvoering", - "done": "Voltooid" + "done": "Voltooid", + "backlog": "Backlog", + "canceled": "Geannuleerd", + "duplicate": "Duplicaat" }, "priorities": { "urgent": "Urgent", @@ -1323,7 +1326,10 @@ "all": "Alle", "todo": "Te doen", "inProgress": "In uitvoering", - "done": "Voltooid" + "done": "Voltooid", + "backlog": "Backlog", + "canceled": "Geannuleerd", + "duplicate": "Duplicaat" }, "empty": "Geen taken", "error": { @@ -1336,6 +1342,7 @@ }, "deleteConfirmation": "Weet u zeker dat u deze taak wilt verwijderen?", "actions": { + "moveToTodo": "Verplaatsen naar Te doen", "moveToInProgress": "Verplaatsen naar In uitvoering", "moveToDone": "Verplaatsen naar Voltooid", "edit": "Bewerken", diff --git a/apps/console/src/components/tasks/TaskFormDialog.tsx b/apps/console/src/components/tasks/TaskFormDialog.tsx index 70a85f0b38..f441757fc0 100644 --- a/apps/console/src/components/tasks/TaskFormDialog.tsx +++ b/apps/console/src/components/tasks/TaskFormDialog.tsx @@ -99,12 +99,13 @@ export const taskUpdateMutation = graphql` } `; -export const taskStates = ["TODO", "IN_PROGRESS", "DONE"] as const; +export const taskStates = ["BACKLOG", "TODO", "IN_PROGRESS", "DONE", "CANCELED", "DUPLICATE"] as const; export const taskPriorities = ["URGENT", "HIGH", "MEDIUM", "LOW"] as const; const createTaskSchema = z.object({ name: z.string().min(1), description: z.string().optional().nullable(), + state: z.enum(taskStates), priority: z.enum(taskPriorities), timeEstimate: z.string().optional().nullable(), assignedToId: z.string().optional().nullable(), @@ -217,6 +218,7 @@ export default function TaskFormDialog(props: Props) { organizationId, name: data.name, description: data.description || null, + state: "state" in data ? data.state : undefined, priority: data.priority, timeEstimate: data.timeEstimate || null, deadline: formatDatetime(data.deadline) ?? null, @@ -271,42 +273,58 @@ export default function TaskFormDialog(props: Props) { {/* Properties form */}
- {isUpdating && ( - - ( - - )} - /> - - )} + + ( + + )} + /> + { + const renderTaskRow = (node: (typeof tasks)[number]["node"], sectionState?: TaskState) => { const task = readTask(node); return ( = { + BACKLOG: { state: "TODO", label: t("tasksCard.actions.moveToTodo"), icon: IconRadioUnchecked, className: "text-txt-quaternary" }, TODO: { state: "IN_PROGRESS", label: t("tasksCard.actions.moveToInProgress"), icon: IconCircleProgress, className: "text-txt-warning" }, IN_PROGRESS: { state: "DONE", label: t("tasksCard.actions.moveToDone"), icon: IconCircleCheck, className: "text-txt-accent" }, }; diff --git a/e2e/console/task_test.go b/e2e/console/task_test.go index 97d602f613..2d57114e57 100644 --- a/e2e/console/task_test.go +++ b/e2e/console/task_test.go @@ -41,6 +41,7 @@ func TestTask_Create(t *testing.T) { node { id name + state } } } @@ -51,8 +52,9 @@ func TestTask_Create(t *testing.T) { CreateTask struct { TaskEdge struct { Node struct { - ID string `json:"id"` - Name string `json:"name"` + ID string `json:"id"` + Name string `json:"name"` + State string `json:"state"` } `json:"node"` } `json:"taskEdge"` } `json:"createTask"` @@ -72,6 +74,7 @@ func TestTask_Create(t *testing.T) { task := result.CreateTask.TaskEdge.Node assert.NotEmpty(t, task.ID) assert.Equal(t, "Owner Task", task.Name) + assert.Equal(t, "TODO", task.State) } func TestTask_CreateWithoutMeasure(t *testing.T) { @@ -327,12 +330,53 @@ func TestTask_StateEnum(t *testing.T) { Create() states := []string{ + "BACKLOG", "TODO", "IN_PROGRESS", "DONE", + "CANCELED", + "DUPLICATE", } for _, state := range states { + t.Run("create with state "+state, func(t *testing.T) { + query := ` + mutation CreateTask($input: CreateTaskInput!) { + createTask(input: $input) { + taskEdge { + node { + id + state + } + } + } + } + ` + + var result struct { + CreateTask struct { + TaskEdge struct { + Node struct { + ID string `json:"id"` + State string `json:"state"` + } `json:"node"` + } `json:"taskEdge"` + } `json:"createTask"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "measureId": measureID, + "name": "Create State " + state, + "priority": "MEDIUM", + "state": state, + }, + }, &result) + require.NoError(t, err, "State %s should be valid on create", state) + assert.Equal(t, state, result.CreateTask.TaskEdge.Node.State) + }) + t.Run("update to state "+state, func(t *testing.T) { taskID := factory.NewTask(owner, measureID). WithName("State Test " + state). diff --git a/packages/n8n-node/nodes/Probo/actions/task/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/task/create.operation.ts index c81140148d..c826321f8e 100644 --- a/packages/n8n-node/nodes/Probo/actions/task/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/task/create.operation.ts @@ -76,6 +76,45 @@ export const description: INodeProperties[] = [ default: '', description: 'The description of the task', }, + { + displayName: 'State', + name: 'state', + type: 'options', + displayOptions: { + show: { + resource: ['task'], + operation: ['create'], + }, + }, + options: [ + { + name: 'Backlog', + value: 'BACKLOG', + }, + { + name: 'Canceled', + value: 'CANCELED', + }, + { + name: 'Done', + value: 'DONE', + }, + { + name: 'Duplicate', + value: 'DUPLICATE', + }, + { + name: 'In Progress', + value: 'IN_PROGRESS', + }, + { + name: 'Todo', + value: 'TODO', + }, + ], + default: 'TODO', + description: 'The state of the task', + }, { displayName: 'Priority', name: 'priority', @@ -156,6 +195,7 @@ export async function execute( const measureId = this.getNodeParameter('measureId', itemIndex, '') as string; const name = this.getNodeParameter('name', itemIndex) as string; const description = this.getNodeParameter('description', itemIndex, '') as string; + const state = this.getNodeParameter('state', itemIndex, '') as string; const priority = this.getNodeParameter('priority', itemIndex, '') as string; const timeEstimate = this.getNodeParameter('timeEstimate', itemIndex, '') as string; const assignedToId = this.getNodeParameter('assignedToId', itemIndex, '') as string; @@ -187,6 +227,7 @@ export async function execute( name, ...(measureId && { measureId }), ...(description && { description }), + ...(state && { state }), ...(priority && { priority }), ...(timeEstimate && { timeEstimate }), ...(assignedToId && { assignedToId }), diff --git a/packages/n8n-node/nodes/Probo/actions/task/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/task/update.operation.ts index 75de5bc97f..081dc6be38 100644 --- a/packages/n8n-node/nodes/Probo/actions/task/update.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/task/update.operation.ts @@ -78,17 +78,29 @@ export const description: INodeProperties[] = [ value: '', }, { - name: 'Todo', - value: 'TODO', + name: 'Backlog', + value: 'BACKLOG', }, { - name: 'In Progress', - value: 'IN_PROGRESS', + name: 'Canceled', + value: 'CANCELED', }, { name: 'Done', value: 'DONE', }, + { + name: 'Duplicate', + value: 'DUPLICATE', + }, + { + name: 'In Progress', + value: 'IN_PROGRESS', + }, + { + name: 'Todo', + value: 'TODO', + }, ], default: '', description: 'The state of the task', diff --git a/packages/ui/src/Atoms/Icons/IconCircleSlashes.tsx b/packages/ui/src/Atoms/Icons/IconCircleSlashes.tsx new file mode 100644 index 0000000000..745823ef38 --- /dev/null +++ b/packages/ui/src/Atoms/Icons/IconCircleSlashes.tsx @@ -0,0 +1,33 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import type { IconProps } from "./type"; + +export function IconCircleSlashes({ size = 24, className }: IconProps) { + return ( + + + + ); +} diff --git a/packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx b/packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx index 5d9c4bc3f4..8038f0c1b4 100644 --- a/packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx +++ b/packages/ui/src/Atoms/Icons/TaskStateIcon.stories.tsx @@ -30,6 +30,12 @@ export default { type Story = StoryObj; +export const Backlog: Story = { + args: { + state: "BACKLOG", + }, +}; + export const Default: Story = { args: { state: "TODO", @@ -47,3 +53,15 @@ export const Done: Story = { state: "DONE", }, }; + +export const Canceled: Story = { + args: { + state: "CANCELED", + }, +}; + +export const Duplicate: Story = { + args: { + state: "DUPLICATE", + }, +}; diff --git a/packages/ui/src/Atoms/Icons/TaskStateIcon.tsx b/packages/ui/src/Atoms/Icons/TaskStateIcon.tsx index 652cd208e7..85ec72a283 100644 --- a/packages/ui/src/Atoms/Icons/TaskStateIcon.tsx +++ b/packages/ui/src/Atoms/Icons/TaskStateIcon.tsx @@ -18,21 +18,33 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import { CircleDashedIcon } from "@phosphor-icons/react"; + import { IconCircleCheck } from "./IconCircleCheck"; import { IconCircleProgress } from "./IconCircleProgress"; +import { IconCircleSlashes } from "./IconCircleSlashes"; +import { IconCircleX } from "./IconCircleX"; import { IconRadioUnchecked } from "./IconRadioUnchecked"; +export type TaskState = "BACKLOG" | "TODO" | "IN_PROGRESS" | "DONE" | "CANCELED" | "DUPLICATE"; + type Props = { - state: "TODO" | "IN_PROGRESS" | "DONE"; + state: TaskState; }; export function TaskStateIcon({ state }: Props) { switch (state) { + case "BACKLOG": + return ; case "TODO": return ; case "IN_PROGRESS": return ; case "DONE": return ; + case "CANCELED": + return ; + case "DUPLICATE": + return ; } } diff --git a/packages/ui/src/Atoms/Icons/index.tsx b/packages/ui/src/Atoms/Icons/index.tsx index 94733e4785..0e85193682 100644 --- a/packages/ui/src/Atoms/Icons/index.tsx +++ b/packages/ui/src/Atoms/Icons/index.tsx @@ -25,6 +25,7 @@ export { IconBell2 } from "./IconBell2"; export { IconMedal } from "./IconMedal"; export { IconMail } from "./IconMail"; export { IconArrowInbox1 } from "./IconArrowInbox1"; +export { IconCircleSlashes } from "./IconCircleSlashes"; export { IconCircleX } from "./IconCircleX"; export { IconFire3 } from "./IconFire3"; export { IconMagnifyingGlass } from "./IconMagnifyingGlass"; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 98ba8fe972..b095d7c569 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -67,6 +67,7 @@ export { ControlItem } from "./Atoms/ControlItem/ControlItem"; export { InfiniteScrollTrigger } from "./Atoms/InfiniteScrollTrigger/InfiniteScrollTrigger"; export { PriorityLevel } from "./Atoms/PriorityLevel/PriorityLevel"; export { TaskStateIcon } from "./Atoms/Icons/TaskStateIcon"; +export type { TaskState } from "./Atoms/Icons/TaskStateIcon"; export { Checkbox } from "./Atoms/Checkbox/Checkbox"; export { Toggle } from "./Atoms/Toggle/Toggle"; export { diff --git a/pkg/cmd/cmdutil/flags.go b/pkg/cmd/cmdutil/flags.go index d24eb52ec5..c075b52f18 100644 --- a/pkg/cmd/cmdutil/flags.go +++ b/pkg/cmd/cmdutil/flags.go @@ -33,6 +33,21 @@ const ( OutputTable = "table" ) +var ( + TaskStates = []string{ + "BACKLOG", + "TODO", + "IN_PROGRESS", + "DONE", + "CANCELED", + "DUPLICATE", + } +) + +func TaskStateFlagUsage() string { + return "Task state: " + strings.Join(TaskStates, ", ") +} + // AddOutputFlag registers --output / -o on cmd and returns a pointer to the // value. The default is "table". Callers should call ValidateOutputFlag early // in RunE, then branch on *p. diff --git a/pkg/cmd/task/create/create.go b/pkg/cmd/task/create/create.go index 99be758da9..7a65e3932b 100644 --- a/pkg/cmd/task/create/create.go +++ b/pkg/cmd/task/create/create.go @@ -63,6 +63,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { flagOrg string flagName string flagDescription string + flagState string flagPriority string flagMeasure string flagTimeEstimate string @@ -146,6 +147,14 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { input["description"] = flagDescription } + if flagState != "" { + if err := cmdutil.ValidateEnum("state", flagState, cmdutil.TaskStates); err != nil { + return err + } + + input["state"] = flagState + } + if flagPriority != "" { input["priority"] = flagPriority } @@ -194,6 +203,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID") cmd.Flags().StringVar(&flagName, "name", "", "Task name (required)") cmd.Flags().StringVar(&flagDescription, "description", "", "Task description") + cmd.Flags().StringVar(&flagState, "state", "", cmdutil.TaskStateFlagUsage()) cmd.Flags().StringVar(&flagPriority, "priority", "", "Task priority: URGENT, HIGH, MEDIUM, LOW") cmd.Flags().StringVar(&flagMeasure, "measure", "", "Measure ID") cmd.Flags().StringVar(&flagTimeEstimate, "time-estimate", "", "Time estimate") diff --git a/pkg/cmd/task/update/update.go b/pkg/cmd/task/update/update.go index 1fe54841fe..01725c2a5e 100644 --- a/pkg/cmd/task/update/update.go +++ b/pkg/cmd/task/update/update.go @@ -101,6 +101,10 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { } if cmd.Flags().Changed("state") { + if err := cmdutil.ValidateEnum("state", flagState, cmdutil.TaskStates); err != nil { + return err + } + input["state"] = flagState } @@ -163,7 +167,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().StringVar(&flagName, "name", "", "Task name") cmd.Flags().StringVar(&flagDescription, "description", "", "Task description") - cmd.Flags().StringVar(&flagState, "state", "", "Task state: TODO, IN_PROGRESS, DONE") + cmd.Flags().StringVar(&flagState, "state", "", cmdutil.TaskStateFlagUsage()) cmd.Flags().StringVar(&flagPriority, "priority", "", "Task priority: URGENT, HIGH, MEDIUM, LOW") cmd.Flags().StringVar(&flagTimeEstimate, "time-estimate", "", "Time estimate") cmd.Flags().StringVar(&flagDeadline, "deadline", "", "Deadline") diff --git a/pkg/coredata/migrations/20260828T133808Z.sql b/pkg/coredata/migrations/20260828T133808Z.sql new file mode 100644 index 0000000000..badb3e3fe4 --- /dev/null +++ b/pkg/coredata/migrations/20260828T133808Z.sql @@ -0,0 +1,23 @@ +-- Copyright (c) 2026 Probo Inc . +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +ALTER TYPE task_state ADD VALUE 'BACKLOG' BEFORE 'TODO'; +ALTER TYPE task_state ADD VALUE 'CANCELED'; +ALTER TYPE task_state ADD VALUE 'DUPLICATE'; diff --git a/pkg/coredata/task_state.go b/pkg/coredata/task_state.go index c590da85c5..fc76b54449 100644 --- a/pkg/coredata/task_state.go +++ b/pkg/coredata/task_state.go @@ -30,9 +30,12 @@ type ( ) const ( + TaskStateBacklog TaskState = "BACKLOG" TaskStateTodo TaskState = "TODO" TaskStateInProgress TaskState = "IN_PROGRESS" TaskStateDone TaskState = "DONE" + TaskStateCanceled TaskState = "CANCELED" + TaskStateDuplicate TaskState = "DUPLICATE" ) var ( @@ -43,18 +46,24 @@ var ( func TaskStates() []TaskState { return []TaskState{ + TaskStateBacklog, TaskStateTodo, TaskStateInProgress, TaskStateDone, + TaskStateCanceled, + TaskStateDuplicate, } } func (v TaskState) IsValid() bool { switch v { case + TaskStateBacklog, TaskStateTodo, TaskStateInProgress, - TaskStateDone: + TaskStateDone, + TaskStateCanceled, + TaskStateDuplicate: return true } diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go index 7450a91b0a..b601a25103 100644 --- a/pkg/probo/task_service.go +++ b/pkg/probo/task_service.go @@ -44,6 +44,7 @@ type ( MeasureID *gid.GID Name string Description *string + State *coredata.TaskState Priority coredata.TaskPriority TimeEstimate *time.Duration AssignedToID *gid.GID @@ -71,6 +72,7 @@ func (ctr *CreateTaskRequest) Validate() error { v.Check(ctr.MeasureID, "measure_id", validator.GID(coredata.MeasureEntityType)) v.Check(ctr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(ctr.Description, "description", validator.SafeText(ContentMaxLength)) + v.Check(ctr.State, "state", validator.OneOfSlice(coredata.TaskStates())) v.Check(ctr.Priority, "priority", validator.Required(), validator.OneOfSlice(coredata.TaskPriorities())) v.Check(ctr.TimeEstimate, "time_estimate", validator.RangeDuration(0, 1000*time.Hour)) v.Check(ctr.AssignedToID, "assigned_to_id", validator.GID(coredata.MembershipProfileEntityType)) @@ -110,6 +112,11 @@ func (s TaskService) Create( return nil, fmt.Errorf("cannot generate reference id: %w", err) } + state := coredata.TaskStateTodo + if req.State != nil { + state = *req.State + } + task := &coredata.Task{ ID: taskID, OrganizationID: req.OrganizationID, @@ -120,7 +127,7 @@ func (s TaskService) Create( TimeEstimate: req.TimeEstimate, AssignedToID: req.AssignedToID, Deadline: req.Deadline, - State: coredata.TaskStateTodo, + State: state, ReferenceID: "custom-task-" + referenceID.String(), CreatedAt: now, UpdatedAt: now, diff --git a/pkg/server/api/console/v1/graphql/task.graphql b/pkg/server/api/console/v1/graphql/task.graphql index 80d41524d1..730f3f0309 100644 --- a/pkg/server/api/console/v1/graphql/task.graphql +++ b/pkg/server/api/console/v1/graphql/task.graphql @@ -1,7 +1,10 @@ enum TaskState @goModel(model: "go.probo.inc/probo/pkg/coredata.TaskState") { + BACKLOG @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateBacklog") TODO @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateTodo") IN_PROGRESS @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateInProgress") DONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateDone") + CANCELED @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateCanceled") + DUPLICATE @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateDuplicate") } enum TaskPriority @@ -91,6 +94,7 @@ input CreateTaskInput { measureId: ID name: String! description: String + state: TaskState priority: TaskPriority! timeEstimate: Duration assignedToId: ID diff --git a/pkg/server/api/console/v1/task_resolvers.go b/pkg/server/api/console/v1/task_resolvers.go index 96169e5517..e0001e768e 100644 --- a/pkg/server/api/console/v1/task_resolvers.go +++ b/pkg/server/api/console/v1/task_resolvers.go @@ -36,6 +36,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, + State: input.State, Priority: input.Priority, TimeEstimate: input.TimeEstimate, AssignedToID: input.AssignedToID, diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index ad44e35dc6..e9d62ac7f1 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -2133,6 +2133,7 @@ func (r *Resolver) AddTaskTool(ctx context.Context, req *mcp.CallToolRequest, in MeasureID: input.MeasureID, Name: input.Name, Description: input.Description, + State: input.State, Priority: priority, TimeEstimate: input.TimeEstimate, Deadline: input.Deadline, diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index 54b9c8ca6b..cadb503a0e 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -6871,9 +6871,12 @@ components: TaskState: type: string enum: + - BACKLOG - TODO - IN_PROGRESS - DONE + - CANCELED + - DUPLICATE go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.TaskState TaskOrderField: @@ -7069,6 +7072,10 @@ components: - type: "null" description: Not assigned description: Assigned to person ID + state: + $ref: "#/components/schemas/TaskState" + description: Task state (defaults to TODO if not specified) + default: TODO priority: $ref: "#/components/schemas/TaskPriority" description: Task priority level (defaults to MEDIUM if not specified)