gitea/web_src/js/components/projects/WorkflowStore.ts

260 lines
9.3 KiB
TypeScript

import {reactive} from 'vue';
import {GET, POST} from '../../modules/fetch.ts';
export function createWorkflowStore(props: { projectLink: string, eventID: string}) {
const store = reactive({
workflowEvents: [],
selectedItem: props.eventID,
selectedWorkflow: null,
projectColumns: [],
projectLabels: [], // Add labels data
saving: false,
loading: false, // Add loading state to prevent rapid clicks
showCreateDialog: false, // For create workflow dialog
selectedEventType: null, // For workflow creation
workflowFilters: {
issue_type: '', // 'issue', 'pull_request', or ''
},
workflowActions: {
column: '', // column ID to move to
labels: [], // selected label IDs
closeIssue: false,
},
async loadEvents() {
const response = await GET(`${props.projectLink}/workflows/events`);
store.workflowEvents = await response.json();
return store.workflowEvents;
},
async loadProjectColumns() {
try {
const response = await GET(`${props.projectLink}/workflows/columns`);
store.projectColumns = await response.json();
} catch (error) {
console.error('Failed to load project columns:', error);
store.projectColumns = [];
}
},
async loadWorkflowData(eventId: string) {
store.loading = true;
try {
// Load project columns and labels for the dropdowns
await store.loadProjectColumns();
await store.loadProjectLabels();
// Find the workflow from existing workflowEvents
const workflow = store.workflowEvents.find((e) => e.event_id === eventId);
if (workflow && workflow.filters && workflow.actions) {
// Load existing configuration from the workflow data
// Convert backend filter format to frontend format
const frontendFilters = {issue_type: ''};
if (workflow.filters && Array.isArray(workflow.filters)) {
for (const filter of workflow.filters) {
if (filter.type === 'issue_type') {
frontendFilters.issue_type = filter.value;
}
}
}
// Convert backend action format to frontend format
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 === 'add_labels') {
frontendActions.labels.push(action.action_value);
} else if (action.action_type === 'close') {
frontendActions.closeIssue = action.action_value === 'true';
}
}
}
store.workflowFilters = frontendFilters;
store.workflowActions = frontendActions;
} else {
// Reset to defaults for new workflow
store.resetWorkflowData();
}
} finally {
store.loading = false;
}
},
async loadProjectLabels() {
try {
const response = await GET(`${props.projectLink}/workflows/labels`);
store.projectLabels = await response.json();
} catch (error) {
console.error('Failed to load project labels:', error);
store.projectLabels = [];
}
},
resetWorkflowData() {
store.workflowFilters = {issue_type: ''};
store.workflowActions = {column: '', add_labels: [], closeIssue: false};
},
async saveWorkflow() {
if (!store.selectedWorkflow) return;
store.saving = true;
try {
// For new workflows, use the base event type
const eventId = store.selectedWorkflow.base_event_type || store.selectedWorkflow.event_id;
// Convert frontend data format to backend form format
const formData = new FormData();
formData.append('event_id', eventId);
// Add filters as form fields
for (const [key, value] of Object.entries(store.workflowFilters)) {
if (value !== '') {
formData.append(`filters[${key}]`, value);
}
}
// Add actions as form fields
for (const [key, value] of Object.entries(store.workflowActions)) {
if (key === 'labels' && Array.isArray(value)) {
// Handle label array
for (const labelId of value) {
if (labelId !== '') {
formData.append(`actions[labels][]`, labelId);
}
}
} else if (key === 'closeIssue') {
// Handle boolean
formData.append(`actions[${key}]`, value.toString());
} else if (value !== '') {
// Handle string fields
formData.append(`actions[${key}]`, value);
}
}
console.log('Saving workflow with FormData');
console.log('URL:', `${props.projectLink}/workflows/${eventId}`);
// Log form data entries
for (const [key, value] of formData.entries()) {
console.log(`${key}: ${value}`);
}
const response = await POST(`${props.projectLink}/workflows/${eventId}`, {
data: formData,
});
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
if (!response.ok) {
const errorText = await response.text();
console.error('Response error:', errorText);
alert(`Failed to save workflow: ${response.status} ${response.statusText}\n${errorText}`);
return;
}
const result = await response.json();
console.log('Response result:', result);
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 wasNewWorkflow = store.selectedWorkflow.id === 0 ||
store.selectedWorkflow.event_id.startsWith('new-') ||
store.selectedWorkflow.event_id.startsWith('clone-');
// Reload events from server to get the correct event structure
await store.loadEvents();
// Update selected workflow and selectedItem
store.selectedWorkflow = result.workflow;
store.selectedItem = result.workflow.event_id;
// Update URL to use the new workflow ID
if (wasNewWorkflow) {
const newUrl = `${props.projectLink}/workflows/${result.workflow.event_id}`;
window.history.replaceState({eventId: result.workflow.event_id}, '', newUrl);
}
alert('Workflow saved successfully!');
} else {
console.error('Unexpected response format:', result);
alert('Failed to save workflow: Unexpected response format');
}
} catch (error) {
console.error('Error saving workflow:', error);
alert(`Error saving workflow: ${error.message}`);
} finally {
store.saving = false;
}
},
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;
}