From 6c4160dba00abf1ce1920158294c57f5f3d4bcdd Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 23 Oct 2025 15:19:02 -0700 Subject: [PATCH] fix --- routers/web/projects/workflows.go | 40 +++++++++++++------ services/projects/workflow_notifier.go | 20 ++++++++-- .../components/projects/ProjectWorkflow.vue | 22 +++++++--- .../js/components/projects/WorkflowStore.ts | 30 ++++++++++---- 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/routers/web/projects/workflows.go b/routers/web/projects/workflows.go index 80cf363384..1ec8f2940c 100644 --- a/routers/web/projects/workflows.go +++ b/routers/web/projects/workflows.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "strconv" + "strings" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -132,11 +133,26 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc } } } + case "issueState": + if strValue, ok := value.(string); ok { + switch strings.ToLower(strValue) { + case "close", "closed", "true": + actions = append(actions, project_model.WorkflowAction{ + Type: project_model.WorkflowActionTypeClose, + Value: "close", + }) + case "reopen", "open", "false": + actions = append(actions, project_model.WorkflowAction{ + Type: project_model.WorkflowActionTypeClose, + Value: "reopen", + }) + } + } case "closeIssue": if boolValue, ok := value.(bool); ok && boolValue { actions = append(actions, project_model.WorkflowAction{ Type: project_model.WorkflowActionTypeClose, - Value: "true", + Value: "close", }) } } @@ -172,17 +188,17 @@ func WorkflowsEvents(ctx *context.Context) { } type WorkflowConfig struct { - ID int64 `json:"id"` - EventID string `json:"event_id"` - DisplayName string `json:"display_name"` - BaseEventType string `json:"base_event_type"` // Base event type for grouping - WorkflowEvent string `json:"workflow_event"` // The actual workflow event - Capabilities project_model.WorkflowEventCapabilities `json:"capabilities"` - Filters []project_model.WorkflowFilter `json:"filters"` - Actions []project_model.WorkflowAction `json:"actions"` - FilterSummary string `json:"filter_summary"` // Human readable filter description - Enabled bool `json:"enabled"` - IsConfigured bool `json:"isConfigured"` // Whether this workflow is configured/saved + ID int64 `json:"id"` + EventID string `json:"event_id"` + DisplayName string `json:"display_name"` + BaseEventType string `json:"base_event_type"` // Base event type for grouping + WorkflowEvent string `json:"workflow_event"` // The actual workflow event + Capabilities project_model.WorkflowEventCapabilities `json:"capabilities"` + Filters []project_model.WorkflowFilter `json:"filters"` + Actions []project_model.WorkflowAction `json:"actions"` + FilterSummary string `json:"filter_summary"` // Human readable filter description + Enabled bool `json:"enabled"` + IsConfigured bool `json:"isConfigured"` // Whether this workflow is configured/saved } outputWorkflows := make([]*WorkflowConfig, 0) diff --git a/services/projects/workflow_notifier.go b/services/projects/workflow_notifier.go index 614ea87c0a..812c9f5bd2 100644 --- a/services/projects/workflow_notifier.go +++ b/services/projects/workflow_notifier.go @@ -6,6 +6,7 @@ package projects import ( "context" "strconv" + "strings" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" @@ -424,9 +425,22 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo continue } case project_model.WorkflowActionTypeClose: - if err := issue_service.CloseIssue(ctx, issue, user_model.NewProjectWorkflowsUser(), ""); err != nil { - log.Error("CloseIssue: %v", err) - continue + if strings.EqualFold(action.Value, "reopen") || strings.EqualFold(action.Value, "false") { + if issue.IsClosed { + if err := issue_service.ReopenIssue(ctx, issue, user_model.NewProjectWorkflowsUser(), ""); err != nil { + log.Error("ReopenIssue: %v", err) + continue + } + issue.IsClosed = false + } + } else { + if !issue.IsClosed { + if err := issue_service.CloseIssue(ctx, issue, user_model.NewProjectWorkflowsUser(), ""); err != nil { + log.Error("CloseIssue: %v", err) + continue + } + issue.IsClosed = true + } } default: log.Error("Unsupported action type: %s", action.Type) diff --git a/web_src/js/components/projects/ProjectWorkflow.vue b/web_src/js/components/projects/ProjectWorkflow.vue index 9f056b00c1..36b623c726 100644 --- a/web_src/js/components/projects/ProjectWorkflow.vue +++ b/web_src/js/components/projects/ProjectWorkflow.vue @@ -978,13 +978,20 @@ onUnmounted(() => {
-
- - -
+ +
- -
{{ store.workflowActions.closeIssue ? 'Yes' : 'No' }}
+ {{ store.workflowActions.issueState === 'close' ? 'Close issue' : + store.workflowActions.issueState === 'reopen' ? 'Reopen issue' : 'No change' }}
@@ -1024,6 +1031,7 @@ onUnmounted(() => { background: white; display: flex; flex-direction: column; + min-height: 0; } /* Sidebar */ @@ -1164,6 +1172,7 @@ onUnmounted(() => { flex: 1; display: flex; flex-direction: column; + min-height: 0; } .editor-header { @@ -1202,6 +1211,7 @@ onUnmounted(() => { flex: 1; padding: 1.5rem; overflow-y: auto; + min-height: 0; } .editor-content .field { diff --git a/web_src/js/components/projects/WorkflowStore.ts b/web_src/js/components/projects/WorkflowStore.ts index 2cc129cd3c..f7009a8cca 100644 --- a/web_src/js/components/projects/WorkflowStore.ts +++ b/web_src/js/components/projects/WorkflowStore.ts @@ -8,11 +8,13 @@ type WorkflowFiltersState = { labels: string[]; }; +type WorkflowIssueStateAction = '' | 'close' | 'reopen'; + type WorkflowActionsState = { column: string; add_labels: string[]; remove_labels: string[]; - closeIssue: boolean; + issueState: WorkflowIssueStateAction; }; type WorkflowDraftState = { @@ -21,7 +23,7 @@ type WorkflowDraftState = { }; const createDefaultFilters = (): WorkflowFiltersState => ({issue_type: '', column: '', labels: []}); -const createDefaultActions = (): WorkflowActionsState => ({column: '', add_labels: [], remove_labels: [], closeIssue: false}); +const createDefaultActions = (): WorkflowActionsState => ({column: '', add_labels: [], remove_labels: [], issueState: ''}); const cloneFilters = (filters: WorkflowFiltersState): WorkflowFiltersState => ({ issue_type: filters.issue_type, @@ -33,7 +35,7 @@ const cloneActions = (actions: WorkflowActionsState): WorkflowActionsState => ({ column: actions.column, add_labels: Array.from(actions.add_labels), remove_labels: Array.from(actions.remove_labels), - closeIssue: actions.closeIssue, + issueState: actions.issueState, }); export function createWorkflowStore(props: {projectLink: string, eventID: string}) { @@ -112,7 +114,7 @@ export function createWorkflowStore(props: {projectLink: string, eventID: string // Convert backend filter format to frontend format const frontendFilters = {issue_type: '', column: '', labels: []}; // Convert backend action format to frontend format - const frontendActions = {column: '', add_labels: [], remove_labels: [], closeIssue: false}; + const frontendActions: WorkflowActionsState = {column: '', add_labels: [], remove_labels: [], issueState: ''}; if (workflow?.filters && Array.isArray(workflow.filters)) { for (const filter of workflow.filters) { @@ -137,7 +139,11 @@ export function createWorkflowStore(props: {projectLink: string, eventID: string // Backend returns string, keep as string to match label.id type frontendActions.remove_labels.push(action.value); } else if (action.type === 'close') { - frontendActions.closeIssue = action.value === 'true'; + if (action.value === 'reopen' || action.value === 'false') { + frontendActions.issueState = 'reopen'; + } else if (action.value === 'true' || action.value === 'close') { + frontendActions.issueState = 'close'; + } } } } @@ -153,7 +159,11 @@ export function createWorkflowStore(props: {projectLink: string, eventID: string // Backend returns string, keep as string to match label.id type frontendActions.remove_labels.push(action.value); } else if (action.type === 'close') { - frontendActions.closeIssue = action.value === 'true'; + if (action.value === 'reopen' || action.value === 'false') { + frontendActions.issueState = 'reopen'; + } else if (action.value === 'true' || action.value === 'close') { + frontendActions.issueState = 'close'; + } } } } @@ -252,7 +262,7 @@ export function createWorkflowStore(props: {projectLink: string, eventID: string // Convert backend data to frontend format and update form // Use the selectedWorkflow which now points to the reloaded workflow with complete data const frontendFilters = {issue_type: '', column: '', labels: []}; - const frontendActions = {column: '', add_labels: [], remove_labels: [], closeIssue: false}; + const frontendActions: WorkflowActionsState = {column: '', add_labels: [], remove_labels: [], issueState: ''}; if (store.selectedWorkflow.filters && Array.isArray(store.selectedWorkflow.filters)) { for (const filter of store.selectedWorkflow.filters) { @@ -275,7 +285,11 @@ export function createWorkflowStore(props: {projectLink: string, eventID: string } else if (action.type === 'remove_labels') { frontendActions.remove_labels.push(action.value); } else if (action.type === 'close') { - frontendActions.closeIssue = action.value === 'true'; + if (action.value === 'reopen' || action.value === 'false') { + frontendActions.issueState = 'reopen'; + } else if (action.value === 'true' || action.value === 'close') { + frontendActions.issueState = 'close'; + } } } }