mirror of
https://github.com/go-gitea/gitea.git
synced 2026-03-05 08:15:13 +01:00
fix
This commit is contained in:
parent
829fa15816
commit
7240b2b144
@ -30,6 +30,13 @@ const isInEditMode = computed(() => {
|
||||
return store.selectedWorkflow._isEditing || false;
|
||||
});
|
||||
|
||||
const showCancelButton = computed(() => {
|
||||
if (!store.selectedWorkflow) return false;
|
||||
if (store.selectedWorkflow.id > 0) return true;
|
||||
const eventId = store.selectedWorkflow.event_id ?? '';
|
||||
return typeof eventId === 'string' && eventId.startsWith('clone-');
|
||||
});
|
||||
|
||||
// Helper to set edit mode for current workflow
|
||||
const setEditMode = (enabled) => {
|
||||
if (store.selectedWorkflow) {
|
||||
@ -43,21 +50,39 @@ const setEditMode = (enabled) => {
|
||||
|
||||
// Store previous selection for cancel functionality
|
||||
|
||||
const isTemporaryWorkflow = (workflow) => {
|
||||
if (!workflow) return false;
|
||||
if (workflow.id > 0) return false;
|
||||
const eventId = typeof workflow.event_id === 'string' ? workflow.event_id : '';
|
||||
return eventId.startsWith('clone-') || eventId.startsWith('new-');
|
||||
};
|
||||
|
||||
const removeTemporaryWorkflow = (workflow) => {
|
||||
if (!isTemporaryWorkflow(workflow)) return;
|
||||
|
||||
const eventId = workflow.event_id;
|
||||
const tempIndex = store.workflowEvents.findIndex((w) => w.event_id === eventId);
|
||||
if (tempIndex >= 0) {
|
||||
store.workflowEvents.splice(tempIndex, 1);
|
||||
}
|
||||
|
||||
if (typeof store.clearDraft === 'function') {
|
||||
store.clearDraft(eventId);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleEditMode = () => {
|
||||
if (isInEditMode.value) {
|
||||
// Canceling edit mode
|
||||
const canceledWorkflow = store.selectedWorkflow;
|
||||
const hadTemporarySelection = isTemporaryWorkflow(canceledWorkflow);
|
||||
|
||||
if (hadTemporarySelection) {
|
||||
removeTemporaryWorkflow(canceledWorkflow);
|
||||
}
|
||||
|
||||
if (previousSelection.value) {
|
||||
// If there was a previous selection, return to it
|
||||
if (store.selectedWorkflow && store.selectedWorkflow.id === 0) {
|
||||
// Remove temporary unsaved workflow (new or cloned) from list
|
||||
const tempIndex = store.workflowEvents.findIndex((w) =>
|
||||
w.event_id === store.selectedWorkflow.event_id,
|
||||
);
|
||||
if (tempIndex >= 0) {
|
||||
store.workflowEvents.splice(tempIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore previous selection
|
||||
store.selectedItem = previousSelection.value.selectedItem;
|
||||
store.selectedWorkflow = previousSelection.value.selectedWorkflow;
|
||||
@ -65,6 +90,21 @@ const toggleEditMode = () => {
|
||||
store.loadWorkflowData(previousSelection.value.selectedWorkflow.event_id);
|
||||
}
|
||||
previousSelection.value = null;
|
||||
} else if (hadTemporarySelection) {
|
||||
// If we removed a temporary item but have no previous selection, fall back to first workflow
|
||||
const fallback = store.workflowEvents.find((w) => {
|
||||
if (!canceledWorkflow) return false;
|
||||
const baseType = canceledWorkflow.base_event_type || canceledWorkflow.workflow_event;
|
||||
return baseType && (w.base_event_type === baseType || w.workflow_event === baseType || w.event_id === baseType);
|
||||
}) || store.workflowEvents[0];
|
||||
if (fallback) {
|
||||
store.selectedItem = fallback.event_id;
|
||||
store.selectedWorkflow = fallback;
|
||||
store.loadWorkflowData(fallback.event_id);
|
||||
} else {
|
||||
store.selectedItem = null;
|
||||
store.selectedWorkflow = null;
|
||||
}
|
||||
}
|
||||
setEditMode(false);
|
||||
} else {
|
||||
@ -187,6 +227,12 @@ const cloneWorkflow = (sourceWorkflow) => {
|
||||
store.workflowEvents.push(clonedWorkflow);
|
||||
}
|
||||
|
||||
// Remember the source so cancel can return to it
|
||||
previousSelection.value = {
|
||||
selectedItem: store.selectedItem,
|
||||
selectedWorkflow: store.selectedWorkflow ? {...store.selectedWorkflow} : {...sourceWorkflow},
|
||||
};
|
||||
|
||||
// Select the cloned workflow and enter edit mode
|
||||
store.selectedItem = tempId;
|
||||
store.selectedWorkflow = clonedWorkflow;
|
||||
@ -195,7 +241,6 @@ const cloneWorkflow = (sourceWorkflow) => {
|
||||
store.loadWorkflowData(tempId);
|
||||
|
||||
// Enter edit mode
|
||||
previousSelection.value = null; // No previous selection for cloned workflow
|
||||
setEditMode(true);
|
||||
|
||||
// Update URL
|
||||
@ -470,6 +515,25 @@ watch(isInEditMode, async (newVal) => {
|
||||
}
|
||||
});
|
||||
|
||||
const getCurrentDraftKey = () => {
|
||||
if (!store.selectedWorkflow) return null;
|
||||
return store.selectedWorkflow.event_id || store.selectedWorkflow.base_event_type;
|
||||
};
|
||||
|
||||
const persistDraftState = () => {
|
||||
const draftKey = getCurrentDraftKey();
|
||||
if (!draftKey) return;
|
||||
store.updateDraft(draftKey, store.workflowFilters, store.workflowActions);
|
||||
};
|
||||
|
||||
watch(() => store.workflowFilters, () => {
|
||||
persistDraftState();
|
||||
}, {deep: true});
|
||||
|
||||
watch(() => store.workflowActions, () => {
|
||||
persistDraftState();
|
||||
}, {deep: true});
|
||||
|
||||
onMounted(async () => {
|
||||
// Load all necessary data
|
||||
store.workflowEvents = await store.loadEvents();
|
||||
@ -666,6 +730,16 @@ onUnmounted(() => {
|
||||
<div class="editor-actions-header">
|
||||
<!-- Edit Mode Buttons -->
|
||||
<template v-if="isInEditMode">
|
||||
<!-- Cancel Button -->
|
||||
<button
|
||||
v-if="showCancelButton"
|
||||
class="btn btn-outline-secondary"
|
||||
@click="toggleEditMode"
|
||||
>
|
||||
<i class="times icon"/>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
<!-- Save Button -->
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@ -676,15 +750,6 @@ onUnmounted(() => {
|
||||
Save
|
||||
</button>
|
||||
|
||||
<!-- Cancel Button -->
|
||||
<button
|
||||
class="btn btn-outline-secondary"
|
||||
@click="toggleEditMode"
|
||||
>
|
||||
<i class="times icon"/>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
<!-- Delete Button (only for configured workflows) -->
|
||||
<button
|
||||
v-if="store.selectedWorkflow && store.selectedWorkflow.id > 0"
|
||||
|
||||
@ -2,7 +2,41 @@ import {reactive} from 'vue';
|
||||
import {GET, POST} from '../../modules/fetch.ts';
|
||||
import {showInfoToast, showErrorToast} from '../../modules/toast.ts';
|
||||
|
||||
export function createWorkflowStore(props: { projectLink: string, eventID: string}) {
|
||||
type WorkflowFiltersState = {
|
||||
issue_type: string;
|
||||
column: string;
|
||||
labels: string[];
|
||||
};
|
||||
|
||||
type WorkflowActionsState = {
|
||||
column: string;
|
||||
add_labels: string[];
|
||||
remove_labels: string[];
|
||||
closeIssue: boolean;
|
||||
};
|
||||
|
||||
type WorkflowDraftState = {
|
||||
filters: WorkflowFiltersState;
|
||||
actions: WorkflowActionsState;
|
||||
};
|
||||
|
||||
const createDefaultFilters = (): WorkflowFiltersState => ({issue_type: '', column: '', labels: []});
|
||||
const createDefaultActions = (): WorkflowActionsState => ({column: '', add_labels: [], remove_labels: [], closeIssue: false});
|
||||
|
||||
const cloneFilters = (filters: WorkflowFiltersState): WorkflowFiltersState => ({
|
||||
issue_type: filters.issue_type,
|
||||
column: filters.column,
|
||||
labels: Array.from(filters.labels),
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
export function createWorkflowStore(props: {projectLink: string, eventID: string}) {
|
||||
const store = reactive({
|
||||
workflowEvents: [],
|
||||
selectedItem: props.eventID,
|
||||
@ -14,17 +48,25 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
showCreateDialog: false, // For create workflow dialog
|
||||
selectedEventType: null, // For workflow creation
|
||||
|
||||
workflowFilters: {
|
||||
issue_type: '', // 'issue', 'pull_request', or ''
|
||||
column: '', // target column ID for item_column_changed event
|
||||
labels: [], // label IDs to filter by
|
||||
workflowFilters: createDefaultFilters(),
|
||||
|
||||
workflowActions: createDefaultActions(),
|
||||
|
||||
workflowDrafts: {} as Record<string, WorkflowDraftState>,
|
||||
|
||||
getDraft(eventId: string): WorkflowDraftState | undefined {
|
||||
return store.workflowDrafts[eventId];
|
||||
},
|
||||
|
||||
workflowActions: {
|
||||
column: '', // column ID to move to
|
||||
add_labels: [], // selected label IDs
|
||||
remove_labels: [], // selected label IDs to remove
|
||||
closeIssue: false,
|
||||
updateDraft(eventId: string, filters: WorkflowFiltersState, actions: WorkflowActionsState) {
|
||||
store.workflowDrafts[eventId] = {
|
||||
filters: cloneFilters(filters),
|
||||
actions: cloneActions(actions),
|
||||
};
|
||||
},
|
||||
|
||||
clearDraft(eventId: string) {
|
||||
delete store.workflowDrafts[eventId];
|
||||
},
|
||||
|
||||
async loadEvents() {
|
||||
@ -54,6 +96,13 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
await store.loadProjectColumns();
|
||||
await store.loadProjectLabels();
|
||||
|
||||
const draft = store.getDraft(eventId);
|
||||
if (draft) {
|
||||
store.workflowFilters = cloneFilters(draft.filters);
|
||||
store.workflowActions = cloneActions(draft.actions);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the workflow from existing workflowEvents
|
||||
const workflow = store.workflowEvents.find((e) => e.event_id === eventId);
|
||||
console.log('[WorkflowStore] loadWorkflowData - eventId:', eventId);
|
||||
@ -65,7 +114,7 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
// Convert backend action format to frontend format
|
||||
const frontendActions = {column: '', add_labels: [], remove_labels: [], closeIssue: false};
|
||||
|
||||
if (workflow) {
|
||||
if (workflow?.filters && Array.isArray(workflow.filters)) {
|
||||
for (const filter of workflow.filters) {
|
||||
if (filter.type === 'issue_type') {
|
||||
frontendFilters.issue_type = filter.value;
|
||||
@ -76,6 +125,23 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
}
|
||||
}
|
||||
|
||||
if (workflow.actions && Array.isArray(workflow.actions)) {
|
||||
for (const action of workflow.actions) {
|
||||
if (action.type === 'column') {
|
||||
// Backend returns string, keep as string to match column.id type
|
||||
frontendActions.column = action.value;
|
||||
} else if (action.type === 'add_labels') {
|
||||
// Backend returns string, keep as string to match label.id type
|
||||
frontendActions.add_labels.push(action.value);
|
||||
} else if (action.type === 'remove_labels') {
|
||||
// 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (workflow?.actions && Array.isArray(workflow.actions)) {
|
||||
for (const action of workflow.actions) {
|
||||
if (action.type === 'column') {
|
||||
// Backend returns string, keep as string to match column.id type
|
||||
@ -94,6 +160,7 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
|
||||
store.workflowFilters = frontendFilters;
|
||||
store.workflowActions = frontendActions;
|
||||
store.updateDraft(eventId, frontendFilters, frontendActions);
|
||||
} finally {
|
||||
store.loading = false;
|
||||
}
|
||||
@ -110,8 +177,13 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
},
|
||||
|
||||
resetWorkflowData() {
|
||||
store.workflowFilters = {issue_type: '', column: '', labels: []};
|
||||
store.workflowActions = {column: '', add_labels: [], remove_labels: [], closeIssue: false};
|
||||
store.workflowFilters = createDefaultFilters();
|
||||
store.workflowActions = createDefaultActions();
|
||||
|
||||
const currentEventId = store.selectedWorkflow?.event_id || store.selectedWorkflow?.base_event_type;
|
||||
if (currentEventId) {
|
||||
store.updateDraft(currentEventId, store.workflowFilters, store.workflowActions);
|
||||
}
|
||||
},
|
||||
|
||||
async saveWorkflow() {
|
||||
@ -121,6 +193,7 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
try {
|
||||
// For new workflows, use the base event type
|
||||
const eventId = store.selectedWorkflow.base_event_type || store.selectedWorkflow.event_id;
|
||||
const previousDraftKey = store.selectedWorkflow.event_id || store.selectedWorkflow.base_event_type;
|
||||
|
||||
// Convert frontend data format to backend JSON format
|
||||
const postData = {
|
||||
@ -151,9 +224,14 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
if (result.success && result.workflow) {
|
||||
// Always reload the events list to get the updated structure
|
||||
// This ensures we have both the base event and the new filtered event
|
||||
const eventKey = typeof store.selectedWorkflow.event_id === 'string' ? store.selectedWorkflow.event_id : '';
|
||||
const wasNewWorkflow = store.selectedWorkflow.id === 0 ||
|
||||
store.selectedWorkflow.event_id.startsWith('new-') ||
|
||||
store.selectedWorkflow.event_id.startsWith('clone-');
|
||||
eventKey.startsWith('new-') ||
|
||||
eventKey.startsWith('clone-');
|
||||
|
||||
if (wasNewWorkflow && previousDraftKey) {
|
||||
store.clearDraft(previousDraftKey);
|
||||
}
|
||||
|
||||
// Reload events from server to get the correct event structure
|
||||
await store.loadEvents();
|
||||
@ -204,6 +282,9 @@ export function createWorkflowStore(props: { projectLink: string, eventID: strin
|
||||
|
||||
store.workflowFilters = frontendFilters;
|
||||
store.workflowActions = frontendActions;
|
||||
if (store.selectedWorkflow?.event_id) {
|
||||
store.updateDraft(store.selectedWorkflow.event_id, frontendFilters, frontendActions);
|
||||
}
|
||||
|
||||
// Update URL to use the new workflow ID
|
||||
if (wasNewWorkflow) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user