mirror of https://github.com/go-gitea/gitea.git
improvements
parent
dd635586b1
commit
20b4ce5446
|
|
@ -16,27 +16,25 @@ import (
|
|||
type WorkflowEvent string
|
||||
|
||||
const (
|
||||
WorkflowEventItemOpened WorkflowEvent = "item_opened"
|
||||
WorkflowEventItemAddedToProject WorkflowEvent = "item_added_to_project"
|
||||
WorkflowEventItemReopened WorkflowEvent = "item_reopened"
|
||||
WorkflowEventItemClosed WorkflowEvent = "item_closed"
|
||||
WorkflowEventItemColumnChanged WorkflowEvent = "item_column_changed"
|
||||
WorkflowEventCodeChangesRequested WorkflowEvent = "code_changes_requested"
|
||||
WorkflowEventCodeReviewApproved WorkflowEvent = "code_review_approved"
|
||||
WorkflowEventPullRequestMerged WorkflowEvent = "pull_request_merged"
|
||||
WorkflowEventAutoArchiveItems WorkflowEvent = "auto_archive_items"
|
||||
WorkflowEventAutoAddToProject WorkflowEvent = "auto_add_to_project"
|
||||
WorkflowEventAutoCloseIssue WorkflowEvent = "auto_close_issue"
|
||||
)
|
||||
|
||||
var workflowEvents = []WorkflowEvent{
|
||||
WorkflowEventItemOpened,
|
||||
WorkflowEventItemAddedToProject,
|
||||
WorkflowEventItemReopened,
|
||||
WorkflowEventItemClosed,
|
||||
WorkflowEventItemColumnChanged,
|
||||
WorkflowEventCodeChangesRequested,
|
||||
WorkflowEventCodeReviewApproved,
|
||||
WorkflowEventPullRequestMerged,
|
||||
WorkflowEventAutoArchiveItems,
|
||||
WorkflowEventAutoAddToProject,
|
||||
WorkflowEventAutoCloseIssue,
|
||||
}
|
||||
|
||||
func GetWorkflowEvents() []WorkflowEvent {
|
||||
|
|
@ -45,24 +43,22 @@ func GetWorkflowEvents() []WorkflowEvent {
|
|||
|
||||
func (we WorkflowEvent) LangKey() string {
|
||||
switch we {
|
||||
case WorkflowEventItemOpened:
|
||||
return "projects.workflows.event.item_opened"
|
||||
case WorkflowEventItemAddedToProject:
|
||||
return "projects.workflows.event.item_added_to_project"
|
||||
case WorkflowEventItemReopened:
|
||||
return "projects.workflows.event.item_reopened"
|
||||
case WorkflowEventItemClosed:
|
||||
return "projects.workflows.event.item_closed"
|
||||
case WorkflowEventItemColumnChanged:
|
||||
return "projects.workflows.event.item_column_changed"
|
||||
case WorkflowEventCodeChangesRequested:
|
||||
return "projects.workflows.event.code_changes_requested"
|
||||
case WorkflowEventCodeReviewApproved:
|
||||
return "projects.workflows.event.code_review_approved"
|
||||
case WorkflowEventPullRequestMerged:
|
||||
return "projects.workflows.event.pull_request_merged"
|
||||
case WorkflowEventAutoArchiveItems:
|
||||
return "projects.workflows.event.auto_archive_items"
|
||||
case WorkflowEventAutoAddToProject:
|
||||
return "projects.workflows.event.auto_add_to_project"
|
||||
case WorkflowEventAutoCloseIssue:
|
||||
return "projects.workflows.event.auto_close_issue"
|
||||
default:
|
||||
return string(we)
|
||||
}
|
||||
|
|
@ -70,24 +66,22 @@ func (we WorkflowEvent) LangKey() string {
|
|||
|
||||
func (we WorkflowEvent) UUID() string {
|
||||
switch we {
|
||||
case WorkflowEventItemOpened:
|
||||
return "item_opened"
|
||||
case WorkflowEventItemAddedToProject:
|
||||
return "item_added_to_project"
|
||||
case WorkflowEventItemReopened:
|
||||
return "item_reopened"
|
||||
case WorkflowEventItemClosed:
|
||||
return "item_closed"
|
||||
case WorkflowEventItemColumnChanged:
|
||||
return "item_column_changed"
|
||||
case WorkflowEventCodeChangesRequested:
|
||||
return "code_changes_requested"
|
||||
case WorkflowEventCodeReviewApproved:
|
||||
return "code_review_approved"
|
||||
case WorkflowEventPullRequestMerged:
|
||||
return "pull_request_merged"
|
||||
case WorkflowEventAutoArchiveItems:
|
||||
return "auto_archive_items"
|
||||
case WorkflowEventAutoAddToProject:
|
||||
return "auto_add_to_project"
|
||||
case WorkflowEventAutoCloseIssue:
|
||||
return "auto_close_issue"
|
||||
default:
|
||||
return string(we)
|
||||
}
|
||||
|
|
@ -96,20 +90,21 @@ func (we WorkflowEvent) UUID() string {
|
|||
type WorkflowFilterType string
|
||||
|
||||
const (
|
||||
WorkflowFilterTypeScope WorkflowFilterType = "scope" // issue, pull_request, etc.
|
||||
WorkflowFilterTypeIssueType WorkflowFilterType = "issue_type" // issue, pull_request, etc.
|
||||
)
|
||||
|
||||
type WorkflowFilter struct {
|
||||
Type WorkflowFilterType
|
||||
Value string // e.g., "issue", "pull_request", etc.
|
||||
Value string // e.g., "issue", "pull_request", etc. depends on the filter type definition
|
||||
}
|
||||
|
||||
type WorkflowActionType string
|
||||
|
||||
const (
|
||||
WorkflowActionTypeColumn WorkflowActionType = "column" // add the item to the project's column
|
||||
WorkflowActionTypeLabel WorkflowActionType = "label" // choose one or more labels
|
||||
WorkflowActionTypeClose WorkflowActionType = "close" // close the issue
|
||||
WorkflowActionTypeColumn WorkflowActionType = "column" // add the item to the project's column
|
||||
WorkflowActionTypeAddLabels WorkflowActionType = "add_labels" // choose one or more labels
|
||||
WorkflowActionTypeRemoveLabels WorkflowActionType = "remove_labels" // choose one or more labels
|
||||
WorkflowActionTypeClose WorkflowActionType = "close" // close the issue
|
||||
)
|
||||
|
||||
type WorkflowAction struct {
|
||||
|
|
@ -119,48 +114,44 @@ type WorkflowAction struct {
|
|||
|
||||
// WorkflowEventCapabilities defines what filters and actions are available for each event
|
||||
type WorkflowEventCapabilities struct {
|
||||
AvailableFilters []string `json:"available_filters"`
|
||||
AvailableFilters []WorkflowFilterType `json:"available_filters"`
|
||||
AvailableActions []WorkflowActionType `json:"available_actions"`
|
||||
}
|
||||
|
||||
// GetWorkflowEventCapabilities returns the capabilities for each workflow event
|
||||
func GetWorkflowEventCapabilities() map[WorkflowEvent]WorkflowEventCapabilities {
|
||||
return map[WorkflowEvent]WorkflowEventCapabilities{
|
||||
WorkflowEventItemOpened: {
|
||||
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType}, // issue, pull_request
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels},
|
||||
},
|
||||
WorkflowEventItemAddedToProject: {
|
||||
AvailableFilters: []string{"scope"}, // issue, pull_request
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType}, // issue, pull_request
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
WorkflowEventItemReopened: {
|
||||
AvailableFilters: []string{"scope"},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
WorkflowEventItemClosed: {
|
||||
AvailableFilters: []string{"scope"},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
WorkflowEventItemColumnChanged: {
|
||||
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels, WorkflowActionTypeClose},
|
||||
},
|
||||
WorkflowEventCodeChangesRequested: {
|
||||
AvailableFilters: []string{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
WorkflowEventCodeReviewApproved: {
|
||||
AvailableFilters: []string{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
WorkflowEventPullRequestMerged: {
|
||||
AvailableFilters: []string{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel, WorkflowActionTypeClose},
|
||||
},
|
||||
WorkflowEventAutoArchiveItems: {
|
||||
AvailableFilters: []string{"scope"},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn},
|
||||
},
|
||||
WorkflowEventAutoAddToProject: {
|
||||
AvailableFilters: []string{"scope"},
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeLabel},
|
||||
},
|
||||
WorkflowEventAutoCloseIssue: {
|
||||
AvailableFilters: []string{}, // only applies to issues
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeClose, WorkflowActionTypeLabel},
|
||||
AvailableFilters: []WorkflowFilterType{}, // only applies to pull requests
|
||||
AvailableActions: []WorkflowActionType{WorkflowActionTypeColumn, WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3904,15 +3904,14 @@ type-3.display_name = Organization Project
|
|||
enter_fullscreen = Fullscreen
|
||||
workflows = Workflows
|
||||
exit_fullscreen = Exit Fullscreen
|
||||
workflows.event.item_opened = Item opened
|
||||
workflows.event.item_added_to_project = Item added to project
|
||||
workflows.event.item_reopened = Item reopened
|
||||
workflows.event.item_closed = Item closed
|
||||
workflows.event.item_column_changed = Item column changed
|
||||
workflows.event.code_changes_requested = Code changes requested
|
||||
workflows.event.code_review_approved = Code review approved
|
||||
workflows.event.pull_request_merged = Pull request merged
|
||||
workflows.event.auto_archive_items = Auto archive items
|
||||
workflows.event.auto_add_to_project = Auto add to project
|
||||
workflows.event.auto_close_issue = Auto close issue
|
||||
|
||||
[git.filemode]
|
||||
changed_filemode = %[1]s → %[2]s
|
||||
|
|
|
|||
|
|
@ -68,12 +68,23 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc
|
|||
ActionValue: strValue,
|
||||
})
|
||||
}
|
||||
case "labels":
|
||||
case "add_labels":
|
||||
if labels, ok := value.([]string); ok && len(labels) > 0 {
|
||||
for _, label := range labels {
|
||||
if label != "" {
|
||||
actions = append(actions, project_model.WorkflowAction{
|
||||
ActionType: project_model.WorkflowActionTypeLabel,
|
||||
ActionType: project_model.WorkflowActionTypeAddLabels,
|
||||
ActionValue: label,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
case "remove_labels":
|
||||
if labels, ok := value.([]string); ok && len(labels) > 0 {
|
||||
for _, label := range labels {
|
||||
if label != "" {
|
||||
actions = append(actions, project_model.WorkflowAction{
|
||||
ActionType: project_model.WorkflowActionTypeRemoveLabels,
|
||||
ActionValue: label,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func (m *workflowNotifier) IssueChangeStatus(ctx context.Context, doer *user_mod
|
|||
func fireIssueWorkflow(ctx context.Context, workflow *project_model.Workflow, issue *issues_model.Issue) {
|
||||
for _, filter := range workflow.WorkflowFilters {
|
||||
switch filter.Type {
|
||||
case project_model.WorkflowFilterTypeScope:
|
||||
case project_model.WorkflowFilterTypeIssueType:
|
||||
values := strings.Split(filter.Value, ",")
|
||||
if !(slices.Contains(values, "issue") && !issue.IsPull) || (slices.Contains(values, "pull") && issue.IsPull) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import {onMounted, useTemplateRef, computed} from 'vue';
|
||||
import {onMounted, useTemplateRef, computed, ref} from 'vue';
|
||||
import {createWorkflowStore} from './WorkflowStore.ts';
|
||||
import {svg} from '../../svg.ts';
|
||||
|
||||
|
|
@ -12,6 +12,49 @@ const props = defineProps({
|
|||
|
||||
const store = createWorkflowStore(props);
|
||||
|
||||
const editMode = ref(false);
|
||||
|
||||
const toggleEditMode = () => {
|
||||
editMode.value = !editMode.value;
|
||||
};
|
||||
|
||||
const toggleWorkflowStatus = async () => {
|
||||
if (store.selectedWorkflow) {
|
||||
// Toggle the enabled status
|
||||
store.selectedWorkflow.enabled = !store.selectedWorkflow.enabled;
|
||||
await store.saveWorkflowStatus();
|
||||
}
|
||||
};
|
||||
|
||||
const deleteWorkflow = async () => {
|
||||
if (!store.selectedWorkflow || !confirm('Are you sure you want to delete this workflow?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
await store.deleteWorkflow();
|
||||
|
||||
// Refresh workflow list
|
||||
store.workflowEvents = await store.loadEvents();
|
||||
|
||||
// Find workflows for the same base event type
|
||||
const sameEventWorkflows = store.workflowEvents.filter(w =>
|
||||
w.base_event_type === store.selectedWorkflow.base_event_type ||
|
||||
w.workflow_event === store.selectedWorkflow.base_event_type
|
||||
);
|
||||
|
||||
if (sameEventWorkflows.length === 0) {
|
||||
// No workflows left for this event type, create an empty one
|
||||
const baseEventType = store.selectedWorkflow.base_event_type;
|
||||
const displayName = store.selectedWorkflow.display_name.split(' (')[0]; // Remove filter suffix
|
||||
createNewWorkflow(baseEventType, store.selectedWorkflow.capabilities, displayName);
|
||||
} else {
|
||||
// Select the first remaining workflow of the same type
|
||||
selectWorkflowItem(sameEventWorkflows[0]);
|
||||
}
|
||||
|
||||
editMode.value = false;
|
||||
};
|
||||
|
||||
const selectWorkflowEvent = (event) => {
|
||||
// Toggle selection - if already selected, deselect
|
||||
if (store.selectedItem === event.event_id) {
|
||||
|
|
@ -60,11 +103,13 @@ const createNewWorkflow = (baseEventType, capabilities, displayName) => {
|
|||
actions: [],
|
||||
filter_summary: '',
|
||||
base_event_type: baseEventType,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
store.selectedWorkflow = newWorkflow;
|
||||
store.selectedItem = tempId;
|
||||
store.resetWorkflowData();
|
||||
editMode.value = true; // Auto-enter edit mode for new workflows
|
||||
};
|
||||
|
||||
const cloneWorkflow = (sourceWorkflow) => {
|
||||
|
|
@ -78,6 +123,7 @@ const cloneWorkflow = (sourceWorkflow) => {
|
|||
actions: Array.from(sourceWorkflow.actions || []),
|
||||
filter_summary: '',
|
||||
base_event_type: sourceWorkflow.base_event_type || sourceWorkflow.workflow_event,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
store.selectedWorkflow = clonedWorkflow;
|
||||
|
|
@ -85,9 +131,11 @@ const cloneWorkflow = (sourceWorkflow) => {
|
|||
|
||||
// Load the source workflow's data into the form
|
||||
store.loadWorkflowData(sourceWorkflow.event_id);
|
||||
editMode.value = true; // Auto-enter edit mode for cloned workflows
|
||||
};
|
||||
|
||||
const selectWorkflowItem = (item) => {
|
||||
editMode.value = false; // Reset edit mode when switching workflows
|
||||
if (item.isConfigured) {
|
||||
// This is a configured workflow, select it
|
||||
selectWorkflowEvent(item);
|
||||
|
|
@ -121,11 +169,16 @@ const _getActionsSummary = (workflow) => {
|
|||
if (column) {
|
||||
actions.push(`Move to "${column.title}"`);
|
||||
}
|
||||
} else if (action.action_type === 'label') {
|
||||
} else if (action.action_type === 'add_labels') {
|
||||
const label = store.projectLabels.find((l) => l.id === action.action_value);
|
||||
if (label) {
|
||||
actions.push(`Add label "${label.name}"`);
|
||||
}
|
||||
} else if (action.action_type === 'remove_labels') {
|
||||
const label = store.projectLabels.find((l) => l.id === action.action_value);
|
||||
if (label) {
|
||||
actions.push(`Remove label "${label.name}"`);
|
||||
}
|
||||
} else if (action.action_type === 'close') {
|
||||
actions.push('Close issue');
|
||||
}
|
||||
|
|
@ -233,12 +286,41 @@ onMounted(async () => {
|
|||
<h2>
|
||||
<i class="settings icon"/>
|
||||
{{ store.selectedWorkflow.display_name }}
|
||||
<span v-if="store.selectedWorkflow.id > 0 && !editMode"
|
||||
class="workflow-status"
|
||||
:class="store.selectedWorkflow.enabled ? 'status-enabled' : 'status-disabled'">
|
||||
{{ store.selectedWorkflow.enabled ? 'Enabled' : 'Disabled' }}
|
||||
</span>
|
||||
</h2>
|
||||
<p>Configure automated actions for this workflow</p>
|
||||
<p v-if="editMode">Configure automated actions for this workflow</p>
|
||||
<p v-else>View workflow configuration</p>
|
||||
</div>
|
||||
<div class="editor-actions-header">
|
||||
<!-- Edit/Cancel Button -->
|
||||
<button
|
||||
v-if="store.selectedWorkflow && store.selectedWorkflow.id > 0"
|
||||
class="ui button"
|
||||
:class="editMode ? 'basic' : 'primary'"
|
||||
@click="toggleEditMode"
|
||||
>
|
||||
<i :class="editMode ? 'times icon' : 'edit icon'"/>
|
||||
{{ editMode ? 'Cancel' : 'Edit' }}
|
||||
</button>
|
||||
|
||||
<!-- Enable/Disable Button (only for configured workflows) -->
|
||||
<button
|
||||
v-if="store.selectedWorkflow && store.selectedWorkflow.id > 0 && !editMode"
|
||||
class="ui button"
|
||||
:class="store.selectedWorkflow.enabled ? 'red basic' : 'green'"
|
||||
@click="toggleWorkflowStatus"
|
||||
:title="store.selectedWorkflow.enabled ? 'Disable workflow' : 'Enable workflow'"
|
||||
>
|
||||
<i :class="store.selectedWorkflow.enabled ? 'pause icon' : 'play icon'"/>
|
||||
{{ store.selectedWorkflow.enabled ? 'Disable' : 'Enable' }}
|
||||
</button>
|
||||
|
||||
<!-- Clone Button (only for configured workflows) -->
|
||||
<button
|
||||
v-if="store.selectedWorkflow && store.selectedWorkflow.id > 0 && !editMode"
|
||||
class="ui basic button"
|
||||
@click="cloneWorkflow(store.selectedWorkflow)"
|
||||
title="Clone this workflow"
|
||||
|
|
@ -250,7 +332,7 @@ onMounted(async () => {
|
|||
</div>
|
||||
|
||||
<div class="editor-content">
|
||||
<div class="ui form">
|
||||
<div class="ui form" :class="{ 'readonly': !editMode }">
|
||||
<div class="field">
|
||||
<label>When</label>
|
||||
<div class="ui segment">
|
||||
|
|
@ -264,13 +346,22 @@ onMounted(async () => {
|
|||
<div class="field" v-if="hasAvailableFilters">
|
||||
<label>Filters</label>
|
||||
<div class="ui segment">
|
||||
<div class="field" v-if="hasFilter('scope')">
|
||||
<div class="field" v-if="hasFilter('issue_type')">
|
||||
<label>Apply to</label>
|
||||
<select class="ui dropdown" v-model="store.workflowFilters.scope">
|
||||
<select
|
||||
v-if="editMode"
|
||||
class="ui dropdown"
|
||||
v-model="store.workflowFilters.issue_type"
|
||||
>
|
||||
<option value="">Issues And Pull Requests</option>
|
||||
<option value="issue">Issues</option>
|
||||
<option value="pull_request">Pull requests</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.workflowFilters.issue_type === 'issue' ? 'Issues' :
|
||||
store.workflowFilters.issue_type === 'pull_request' ? 'Pull requests' :
|
||||
'Issues And Pull Requests' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -281,41 +372,68 @@ onMounted(async () => {
|
|||
<div class="ui segment">
|
||||
<div class="field" v-if="hasAction('column')">
|
||||
<label>Move to column</label>
|
||||
<select class="ui dropdown" v-model="store.workflowActions.column">
|
||||
<select
|
||||
v-if="editMode"
|
||||
class="ui dropdown"
|
||||
v-model="store.workflowActions.column"
|
||||
>
|
||||
<option value="">Select column...</option>
|
||||
<option v-for="column in store.projectColumns" :key="column.id" :value="column.id">
|
||||
{{ column.title }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.projectColumns.find(c => c.id === store.workflowActions.column)?.title || 'None' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="hasAction('label')">
|
||||
<label>Add labels</label>
|
||||
<select class="ui multiple dropdown" v-model="store.workflowActions.labels">
|
||||
<select
|
||||
v-if="editMode"
|
||||
class="ui multiple dropdown"
|
||||
v-model="store.workflowActions.labels"
|
||||
>
|
||||
<option value="">Select labels...</option>
|
||||
<option v-for="label in store.projectLabels" :key="label.id" :value="label.id">
|
||||
{{ label.name }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.workflowActions.labels?.map(id =>
|
||||
store.projectLabels.find(l => l.id === id)?.name).join(', ') || 'None' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="hasAction('close')">
|
||||
<div class="ui checkbox">
|
||||
<div v-if="editMode" class="ui checkbox">
|
||||
<input type="checkbox" v-model="store.workflowActions.closeIssue" id="close-issue">
|
||||
<label for="close-issue">Close issue</label>
|
||||
</div>
|
||||
<div v-else class="readonly-value">
|
||||
<label>Close issue</label>
|
||||
<div>{{ store.workflowActions.closeIssue ? 'Yes' : 'No' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fixed bottom actions -->
|
||||
<div class="editor-actions">
|
||||
<!-- Fixed bottom actions (only show in edit mode) -->
|
||||
<div v-if="editMode" class="editor-actions">
|
||||
<button class="ui primary button" @click="saveWorkflow" :class="{ loading: store.saving }">
|
||||
<i class="save icon"/>
|
||||
Save Workflow
|
||||
</button>
|
||||
<button
|
||||
v-if="store.selectedWorkflow && store.selectedWorkflow.id > 0"
|
||||
class="ui red button"
|
||||
@click="deleteWorkflow"
|
||||
>
|
||||
<i class="trash icon"/>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -492,6 +610,9 @@ onMounted(async () => {
|
|||
|
||||
.editor-actions-header {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.editor-content {
|
||||
|
|
@ -576,4 +697,51 @@ onMounted(async () => {
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Workflow status styles */
|
||||
.workflow-status {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.workflow-status.status-enabled {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.workflow-status.status-disabled {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
/* Readonly form styles */
|
||||
.ui.form.readonly {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.readonly-value {
|
||||
background: #f6f8fa;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #e1e4e8;
|
||||
border-radius: 4px;
|
||||
color: #24292e;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.readonly-value label {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.readonly-value div {
|
||||
color: #586069;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
|||
selectedEventType: null, // For workflow creation
|
||||
|
||||
workflowFilters: {
|
||||
scope: '', // 'issue', 'pull_request', or ''
|
||||
issue_type: '', // 'issue', 'pull_request', or ''
|
||||
},
|
||||
|
||||
workflowActions: {
|
||||
|
|
@ -48,22 +48,22 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
|||
if (workflow && workflow.filters && workflow.actions) {
|
||||
// Load existing configuration from the workflow data
|
||||
// Convert backend filter format to frontend format
|
||||
const frontendFilters = {scope: ''};
|
||||
const frontendFilters = {issue_type: ''};
|
||||
if (workflow.filters && Array.isArray(workflow.filters)) {
|
||||
for (const filter of workflow.filters) {
|
||||
if (filter.type === 'scope') {
|
||||
frontendFilters.scope = filter.value;
|
||||
if (filter.type === 'issue_type') {
|
||||
frontendFilters.issue_type = filter.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert backend action format to frontend format
|
||||
const frontendActions = {column: '', labels: [], closeIssue: false};
|
||||
const frontendActions = {column: '', add_labels: [], closeIssue: false};
|
||||
if (workflow.actions && Array.isArray(workflow.actions)) {
|
||||
for (const action of workflow.actions) {
|
||||
if (action.action_type === 'column') {
|
||||
frontendActions.column = action.action_value;
|
||||
} else if (action.action_type === 'label') {
|
||||
} else if (action.action_type === 'add_labels') {
|
||||
frontendActions.labels.push(action.action_value);
|
||||
} else if (action.action_type === 'close') {
|
||||
frontendActions.closeIssue = action.action_value === 'true';
|
||||
|
|
@ -90,8 +90,8 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
|||
},
|
||||
|
||||
resetWorkflowData() {
|
||||
store.workflowFilters = {scope: ''};
|
||||
store.workflowActions = {column: '', labels: [], closeIssue: false};
|
||||
store.workflowFilters = {issue_type: ''};
|
||||
store.workflowActions = {column: '', add_labels: [], closeIssue: false};
|
||||
},
|
||||
|
||||
async saveWorkflow() {
|
||||
|
|
@ -189,6 +189,67 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
|||
}
|
||||
},
|
||||
|
||||
async saveWorkflowStatus() {
|
||||
if (!store.selectedWorkflow || store.selectedWorkflow.id === 0) return;
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('enabled', store.selectedWorkflow.enabled.toString());
|
||||
|
||||
const response = await POST(`${props.projectLink}/workflows/${store.selectedWorkflow.event_id}/status`, {
|
||||
data: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to update workflow status:', errorText);
|
||||
alert(`Failed to update workflow status: ${response.status} ${response.statusText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// Update workflow in the list
|
||||
const existingIndex = store.workflowEvents.findIndex((e) => e.event_id === store.selectedWorkflow.event_id);
|
||||
if (existingIndex >= 0) {
|
||||
store.workflowEvents[existingIndex].enabled = store.selectedWorkflow.enabled;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating workflow status:', error);
|
||||
alert(`Error updating workflow status: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async deleteWorkflow() {
|
||||
if (!store.selectedWorkflow || store.selectedWorkflow.id === 0) return;
|
||||
|
||||
try {
|
||||
const response = await POST(`${props.projectLink}/workflows/${store.selectedWorkflow.event_id}/delete`, {
|
||||
data: new FormData(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to delete workflow:', errorText);
|
||||
alert(`Failed to delete workflow: ${response.status} ${response.statusText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// Remove workflow from the list
|
||||
const existingIndex = store.workflowEvents.findIndex((e) => e.event_id === store.selectedWorkflow.event_id);
|
||||
if (existingIndex >= 0) {
|
||||
store.workflowEvents.splice(existingIndex, 1);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting workflow:', error);
|
||||
alert(`Error deleting workflow: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue