mirror of
https://github.com/go-gitea/gitea.git
synced 2026-02-23 10:45:28 +01:00
Use camel case in typescript
This commit is contained in:
parent
a6eb361a23
commit
1a045fa62e
@ -22,6 +22,7 @@
|
||||
"add-asset-webpack-plugin": "3.1.1",
|
||||
"ansi_up": "6.0.6",
|
||||
"asciinema-player": "3.13.5",
|
||||
"camelcase-keys": "10.0.1",
|
||||
"chart.js": "4.5.1",
|
||||
"chartjs-adapter-dayjs-4": "1.0.4",
|
||||
"chartjs-plugin-zoom": "2.2.0",
|
||||
|
||||
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
@ -74,6 +74,9 @@ importers:
|
||||
asciinema-player:
|
||||
specifier: 3.13.5
|
||||
version: 3.13.5
|
||||
camelcase-keys:
|
||||
specifier: 10.0.1
|
||||
version: 10.0.1
|
||||
chart.js:
|
||||
specifier: 4.5.1
|
||||
version: 4.5.1
|
||||
@ -1917,6 +1920,14 @@ packages:
|
||||
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
camelcase-keys@10.0.1:
|
||||
resolution: {integrity: sha512-kIH5nQUKB8ORZrwjk40GUgnhzuzxwKb6n5L+anOVmVSrjgix1pfNqVNl0tEcOP4AaFYdke3NNpNiGDSD112lcA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
camelcase@8.0.0:
|
||||
resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
caniuse-lite@1.0.30001760:
|
||||
resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==}
|
||||
|
||||
@ -3194,6 +3205,10 @@ packages:
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
map-obj@5.0.2:
|
||||
resolution: {integrity: sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
markdown-it@14.1.0:
|
||||
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
|
||||
hasBin: true
|
||||
@ -3709,6 +3724,10 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
quick-lru@7.3.0:
|
||||
resolution: {integrity: sha512-k9lSsjl36EJdK7I06v7APZCbyGT2vMTsYSRX1Q2nbYmnkBqgUhRkAuzH08Ciotteu/PLJmIF2+tti7o3C/ts2g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
|
||||
@ -4159,6 +4178,10 @@ packages:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
typescript-eslint@8.50.0:
|
||||
resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@ -5868,6 +5891,15 @@ snapshots:
|
||||
|
||||
camelcase-css@2.0.1: {}
|
||||
|
||||
camelcase-keys@10.0.1:
|
||||
dependencies:
|
||||
camelcase: 8.0.0
|
||||
map-obj: 5.0.2
|
||||
quick-lru: 7.3.0
|
||||
type-fest: 4.41.0
|
||||
|
||||
camelcase@8.0.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001760: {}
|
||||
|
||||
chai@6.2.1: {}
|
||||
@ -7221,6 +7253,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
map-obj@5.0.2: {}
|
||||
|
||||
markdown-it@14.1.0:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
@ -7818,6 +7852,8 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
quick-lru@7.3.0: {}
|
||||
|
||||
randombytes@2.1.0:
|
||||
dependencies:
|
||||
safe-buffer: '@nolyfill/safe-buffer@1.0.44'
|
||||
@ -8331,6 +8367,8 @@ snapshots:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
typescript-eslint@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
|
||||
@ -176,7 +176,7 @@ type WorkflowConfig struct {
|
||||
Actions []project_model.WorkflowAction `json:"actions"`
|
||||
Summary string `json:"summary"` // Human readable filter description
|
||||
Enabled bool `json:"enabled"`
|
||||
IsConfigured bool `json:"isConfigured"` // Whether this workflow is configured/saved
|
||||
IsConfigured bool `json:"is_configured"` // Whether this workflow is configured/saved
|
||||
}
|
||||
|
||||
func WorkflowsEvents(ctx *context.Context, project *project_model.Project) {
|
||||
@ -447,7 +447,7 @@ func WorkflowsPost(ctx *context.Context) {
|
||||
workflowSummary := project_service.GetWorkflowSummary(ctx, wf)
|
||||
ctx.JSON(http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"workflows": WorkflowConfig{
|
||||
"workflow": WorkflowConfig{
|
||||
ID: wf.ID,
|
||||
EventID: strconv.FormatInt(wf.ID, 10),
|
||||
DisplayName: string(ctx.Tr(wf.WorkflowEvent.LangKey())),
|
||||
|
||||
@ -84,22 +84,22 @@ const setEditMode = (enabled: boolean) => {
|
||||
const showCancelButton = computed(() => {
|
||||
if (!store.selectedWorkflow) return false;
|
||||
if (store.selectedWorkflow.id > 0) return true;
|
||||
const eventId = store.selectedWorkflow.event_id ?? '';
|
||||
const eventId = store.selectedWorkflow.eventId ?? '';
|
||||
return typeof eventId === 'string' && eventId.startsWith('clone-');
|
||||
});
|
||||
|
||||
const isTemporaryWorkflow = (workflow?: WorkflowEvent | null) => {
|
||||
if (!workflow) return false;
|
||||
if (workflow.id > 0) return false;
|
||||
const eventId = typeof workflow.event_id === 'string' ? workflow.event_id : '';
|
||||
const eventId = typeof workflow.eventId === 'string' ? workflow.eventId : '';
|
||||
return eventId.startsWith('clone-') || eventId.startsWith('new-');
|
||||
};
|
||||
|
||||
const removeTemporaryWorkflow = (workflow?: WorkflowEvent | null) => {
|
||||
if (!workflow || !isTemporaryWorkflow(workflow)) return;
|
||||
|
||||
const eventId = workflow.event_id;
|
||||
const tempIndex = store.workflowEvents.findIndex((w: WorkflowEvent) => w.event_id === eventId);
|
||||
const eventId = workflow.eventId;
|
||||
const tempIndex = store.workflowEvents.findIndex((w: WorkflowEvent) => w.eventId === eventId);
|
||||
if (tempIndex >= 0) {
|
||||
store.workflowEvents.splice(tempIndex, 1);
|
||||
}
|
||||
@ -125,20 +125,20 @@ const toggleEditMode = () => {
|
||||
store.selectedItem = previousSelection.value.selectedItem;
|
||||
store.selectedWorkflow = previousSelection.value.selectedWorkflow;
|
||||
if (previousSelection.value.selectedWorkflow) {
|
||||
store.loadWorkflowData(previousSelection.value.selectedWorkflow.event_id);
|
||||
store.loadWorkflowData(previousSelection.value.selectedWorkflow.eventId);
|
||||
}
|
||||
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: WorkflowEvent) => {
|
||||
if (!canceledWorkflow) return false;
|
||||
const baseType = canceledWorkflow.workflow_event;
|
||||
return baseType && (w.workflow_event === baseType || w.event_id === baseType);
|
||||
const baseType = canceledWorkflow.workflowEvent;
|
||||
return baseType && (w.workflowEvent === baseType || w.eventId === baseType);
|
||||
}) || store.workflowEvents[0];
|
||||
if (fallback) {
|
||||
store.selectedItem = fallback.event_id;
|
||||
store.selectedItem = fallback.eventId;
|
||||
store.selectedWorkflow = fallback;
|
||||
store.loadWorkflowData(fallback.event_id);
|
||||
store.loadWorkflowData(fallback.eventId);
|
||||
} else {
|
||||
store.selectedItem = null;
|
||||
store.selectedWorkflow = null;
|
||||
@ -174,7 +174,7 @@ const deleteWorkflow = async () => {
|
||||
// If deleting a temporary workflow (new or cloned, unsaved), just remove from list
|
||||
if (currentSelection.id === 0) {
|
||||
const tempIndex = store.workflowEvents.findIndex((w: WorkflowEvent) =>
|
||||
w.event_id === currentSelection.event_id,
|
||||
w.eventId === currentSelection.eventId,
|
||||
);
|
||||
if (tempIndex >= 0) {
|
||||
store.workflowEvents.splice(tempIndex, 1);
|
||||
@ -188,7 +188,7 @@ const deleteWorkflow = async () => {
|
||||
|
||||
// Find workflows for the same base event type
|
||||
const sameEventWorkflows = store.workflowEvents.filter((w: WorkflowEvent) =>
|
||||
(w.workflow_event === currentSelection.workflow_event)
|
||||
(w.workflowEvent === currentSelection.workflowEvent)
|
||||
);
|
||||
|
||||
let workflowToSelect: WorkflowListItem | null = null;
|
||||
@ -231,18 +231,18 @@ const cloneWorkflow = (sourceWorkflow?: WorkflowEvent | null) => {
|
||||
if (!sourceWorkflow) return;
|
||||
|
||||
// Generate a unique temporary ID for the cloned workflow
|
||||
const tempId = `${sourceWorkflow.workflow_event}`;
|
||||
const tempId = `${sourceWorkflow.workflowEvent}`;
|
||||
|
||||
// Extract base name without any parenthetical descriptions
|
||||
const baseName = (sourceWorkflow.display_name || sourceWorkflow.workflow_event || sourceWorkflow.event_id)
|
||||
const baseName = (sourceWorkflow.displayName || sourceWorkflow.workflowEvent || sourceWorkflow.eventId)
|
||||
.replace(/\s*\([^)]*\)\s*/g, '');
|
||||
|
||||
// Create a new workflow object based on the source
|
||||
const clonedWorkflow = {
|
||||
id: 0, // New workflow
|
||||
event_id: tempId,
|
||||
display_name: `${baseName} (Copy)`,
|
||||
workflow_event: sourceWorkflow.workflow_event,
|
||||
eventId: tempId,
|
||||
displayName: `${baseName} (Copy)`,
|
||||
workflowEvent: sourceWorkflow.workflowEvent,
|
||||
capabilities: sourceWorkflow.capabilities,
|
||||
filters: JSON.parse(JSON.stringify(sourceWorkflow.filters || [])), // Deep clone
|
||||
actions: JSON.parse(JSON.stringify(sourceWorkflow.actions || [])), // Deep clone
|
||||
@ -251,7 +251,7 @@ const cloneWorkflow = (sourceWorkflow?: WorkflowEvent | null) => {
|
||||
};
|
||||
|
||||
// Insert cloned workflow right after the source workflow (keep same type together)
|
||||
const sourceIndex = store.workflowEvents.findIndex((w: WorkflowEvent) => w.event_id === sourceWorkflow.event_id);
|
||||
const sourceIndex = store.workflowEvents.findIndex((w: WorkflowEvent) => w.eventId === sourceWorkflow.eventId);
|
||||
if (sourceIndex >= 0) {
|
||||
store.workflowEvents.splice(sourceIndex + 1, 0, clonedWorkflow);
|
||||
} else {
|
||||
@ -285,22 +285,22 @@ const selectWorkflowEvent = async (event: WorkflowEvent) => {
|
||||
if (store.loading) return;
|
||||
|
||||
// If already selected, do nothing (keep selection active)
|
||||
if (store.selectedItem === event.event_id) {
|
||||
if (store.selectedItem === event.eventId) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
store.selectedItem = event.event_id;
|
||||
store.selectedItem = event.eventId;
|
||||
store.selectedWorkflow = event;
|
||||
|
||||
// Wait for DOM update before proceeding
|
||||
await nextTick();
|
||||
|
||||
await store.loadWorkflowData(event.event_id);
|
||||
await store.loadWorkflowData(event.eventId);
|
||||
|
||||
// Update URL without page reload
|
||||
const newUrl = `${props.projectLink}/workflows/${event.event_id}`;
|
||||
window.history.pushState({eventId: event.event_id}, '', newUrl);
|
||||
const newUrl = `${props.projectLink}/workflows/${event.eventId}`;
|
||||
window.history.pushState({eventId: event.eventId}, '', newUrl);
|
||||
} catch (error) {
|
||||
console.error('Error selecting workflow event:', error);
|
||||
// On error, try to select the first available workflow instead of clearing
|
||||
@ -322,7 +322,7 @@ const saveWorkflow = async () => {
|
||||
|
||||
const isWorkflowConfigured = (event: WorkflowEvent) => {
|
||||
// Check if the event_id is a number (saved workflow ID) or if it has id > 0
|
||||
return !Number.isNaN(parseInt(event.event_id)) || (event.id !== undefined && event.id > 0);
|
||||
return !Number.isNaN(parseInt(event.eventId)) || (event.id !== undefined && event.id > 0);
|
||||
};
|
||||
|
||||
// Get flat list of all workflows - use cached data to prevent frequent recomputation
|
||||
@ -336,7 +336,7 @@ const workflowList = computed<WorkflowListItem[]>(() => {
|
||||
return workflows.map((workflow: WorkflowEvent) => ({
|
||||
...workflow,
|
||||
isConfigured: isWorkflowConfigured(workflow),
|
||||
display_name: workflow.display_name || workflow.workflow_event || workflow.event_id,
|
||||
displayName: workflow.displayName || workflow.workflowEvent || workflow.eventId,
|
||||
}));
|
||||
});
|
||||
|
||||
@ -363,28 +363,28 @@ const selectWorkflowItem = async (item: WorkflowListItem) => {
|
||||
} else {
|
||||
// This is an unconfigured event - check if we already have a workflow object for it
|
||||
const existingWorkflow = store.workflowEvents.find((w: WorkflowEvent) =>
|
||||
w.id === 0 && w.workflow_event === item.workflow_event,
|
||||
w.id === 0 && w.workflowEvent === item.workflowEvent,
|
||||
);
|
||||
|
||||
const workflowToSelect = existingWorkflow || item;
|
||||
await selectWorkflowEvent(workflowToSelect);
|
||||
|
||||
// Update URL for workflow
|
||||
const newUrl = `${props.projectLink}/workflows/${item.workflow_event}`;
|
||||
window.history.pushState({eventId: item.workflow_event}, '', newUrl);
|
||||
const newUrl = `${props.projectLink}/workflows/${item.workflowEvent}`;
|
||||
window.history.pushState({eventId: item.workflowEvent}, '', newUrl);
|
||||
}
|
||||
};
|
||||
|
||||
const hasAvailableFilters = computed(() => {
|
||||
return (store.selectedWorkflow?.capabilities?.available_filters?.length ?? 0) > 0;
|
||||
return (store.selectedWorkflow?.capabilities?.availableFilters?.length ?? 0) > 0;
|
||||
});
|
||||
|
||||
const hasFilter = (filterType: any) => {
|
||||
return store.selectedWorkflow?.capabilities?.available_filters?.includes(filterType);
|
||||
return store.selectedWorkflow?.capabilities?.availableFilters?.includes(filterType);
|
||||
};
|
||||
|
||||
const hasAction = (actionType: any) => {
|
||||
return store.selectedWorkflow?.capabilities?.available_actions?.includes(actionType);
|
||||
return store.selectedWorkflow?.capabilities?.availableActions?.includes(actionType);
|
||||
};
|
||||
|
||||
// Toggle label selection for add_labels, remove_labels, or filter_labels
|
||||
@ -392,8 +392,10 @@ const toggleLabel = (type: string, labelId: any) => {
|
||||
let labels;
|
||||
if (type === 'filter_labels') {
|
||||
labels = store.workflowFilters.labels;
|
||||
} else {
|
||||
labels = (store.workflowActions as any)[type];
|
||||
} else if (type === 'add_labels') {
|
||||
labels = (store.workflowActions as any)['addLabels'];
|
||||
} else if (type === 'remove_labels') {
|
||||
labels = (store.workflowActions as any)['removeLabels'];
|
||||
}
|
||||
const index = labels.indexOf(labelId);
|
||||
if (index > -1) {
|
||||
@ -426,20 +428,20 @@ const isItemSelected = (item: WorkflowListItem) => {
|
||||
|
||||
if (item.isConfigured || item.id === 0) {
|
||||
// For configured workflows or temporary workflows (new), match by event_id
|
||||
return store.selectedItem === item.event_id;
|
||||
return store.selectedItem === item.eventId;
|
||||
}
|
||||
// For unconfigured events, match by workflow_event
|
||||
return store.selectedItem === item.workflow_event;
|
||||
return store.selectedItem === item.workflowEvent;
|
||||
};
|
||||
|
||||
// Get display name for workflow with numbering for same types
|
||||
const getWorkflowDisplayName = (item: WorkflowListItem, _index: number) => {
|
||||
const list = workflowList.value;
|
||||
const displayName = item.display_name || item.workflow_event || item.event_id || '';
|
||||
const displayName = item.displayName || item.workflowEvent || item.eventId || '';
|
||||
|
||||
// Find all workflows of the same type
|
||||
const sameTypeWorkflows = list.filter((w: WorkflowListItem) =>
|
||||
w.workflow_event === item.workflow_event &&
|
||||
w.workflowEvent === item.workflowEvent &&
|
||||
(w.isConfigured || w.id === 0) // Only count configured workflows
|
||||
);
|
||||
|
||||
@ -449,7 +451,7 @@ const getWorkflowDisplayName = (item: WorkflowListItem, _index: number) => {
|
||||
}
|
||||
|
||||
// Find the index of this workflow among same-type workflows
|
||||
const sameTypeIndex = sameTypeWorkflows.findIndex((w: WorkflowListItem) => w.event_id === item.event_id);
|
||||
const sameTypeIndex = sameTypeWorkflows.findIndex((w: WorkflowListItem) => w.eventId === item.eventId);
|
||||
|
||||
// Extract base name without filter summary (remove anything in parentheses)
|
||||
const baseName = displayName.replace(/\s*\([^)]*\)\s*$/g, '');
|
||||
@ -461,7 +463,7 @@ const getWorkflowDisplayName = (item: WorkflowListItem, _index: number) => {
|
||||
|
||||
const getCurrentDraftKey = () => {
|
||||
if (!store.selectedWorkflow) return null;
|
||||
return store.selectedWorkflow.event_id || store.selectedWorkflow.workflow_event;
|
||||
return store.selectedWorkflow.eventId || store.selectedWorkflow.workflowEvent;
|
||||
};
|
||||
|
||||
const persistDraftState = () => {
|
||||
@ -533,7 +535,7 @@ onMounted(async () => {
|
||||
// Auto-select logic
|
||||
if (props.eventID) {
|
||||
// If eventID is provided in URL, try to find and select it
|
||||
const selectedEvent = store.workflowEvents.find((e: WorkflowEvent) => e.event_id === props.eventID);
|
||||
const selectedEvent = store.workflowEvents.find((e: WorkflowEvent) => e.eventId === props.eventID);
|
||||
if (selectedEvent) {
|
||||
// Found existing configured workflow
|
||||
store.selectedItem = props.eventID;
|
||||
@ -543,7 +545,7 @@ onMounted(async () => {
|
||||
// Check if eventID matches a base event type (unconfigured workflow)
|
||||
const items = workflowList.value;
|
||||
const matchingUnconfigured = items.find((item: WorkflowListItem) =>
|
||||
!item.isConfigured && (item.workflow_event === props.eventID || item.event_id === props.eventID),
|
||||
!item.isConfigured && (item.workflowEvent === props.eventID || item.eventId === props.eventID),
|
||||
);
|
||||
if (matchingUnconfigured) {
|
||||
// Select the placeholder workflow for this base event type
|
||||
@ -587,14 +589,14 @@ onMounted(async () => {
|
||||
const popstateHandler = (e: PopStateEvent) => {
|
||||
if (e.state?.eventId) {
|
||||
// Handle browser back/forward navigation
|
||||
const event = store.workflowEvents.find((ev: WorkflowEvent) => ev.event_id === e.state.eventId);
|
||||
const event = store.workflowEvents.find((ev: WorkflowEvent) => ev.eventId === e.state.eventId);
|
||||
if (event) {
|
||||
void selectWorkflowEvent(event);
|
||||
} else {
|
||||
// Check if it's a base event type
|
||||
const items = workflowList.value;
|
||||
const matchingUnconfigured = items.find((item: WorkflowListItem) =>
|
||||
!item.isConfigured && (item.workflow_event === e.state.eventId || item.event_id === e.state.eventId),
|
||||
!item.isConfigured && (item.workflowEvent === e.state.eventId || item.eventId === e.state.eventId),
|
||||
);
|
||||
if (matchingUnconfigured) {
|
||||
void selectWorkflowEvent(matchingUnconfigured);
|
||||
@ -635,7 +637,7 @@ onUnmounted(() => {
|
||||
<div class="workflow-items">
|
||||
<div
|
||||
v-for="(item, index) in workflowList"
|
||||
:key="`workflow-${item.event_id}-${item.isConfigured ? 'configured' : 'unconfigured'}`"
|
||||
:key="`workflow-${item.eventId}-${item.isConfigured ? 'configured' : 'unconfigured'}`"
|
||||
class="workflow-item"
|
||||
:class="{ active: isItemSelected(item) }"
|
||||
:data-workflow-item="JSON.stringify(item)"
|
||||
@ -678,7 +680,7 @@ onUnmounted(() => {
|
||||
<div class="editor-title">
|
||||
<h2>
|
||||
<i class="settings icon"/>
|
||||
{{ store.selectedWorkflow.display_name }}
|
||||
{{ store.selectedWorkflow.displayName }}
|
||||
<span
|
||||
v-if="store.selectedWorkflow.id > 0 && !isInEditMode"
|
||||
class="workflow-status"
|
||||
@ -765,7 +767,7 @@ onUnmounted(() => {
|
||||
<label>{{ locale.when }}</label>
|
||||
<div class="segment">
|
||||
<div class="description">
|
||||
{{ locale.runWhen }}<strong>{{ store.selectedWorkflow.display_name }}</strong>
|
||||
{{ locale.runWhen }}<strong>{{ store.selectedWorkflow.displayName }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -779,15 +781,15 @@ onUnmounted(() => {
|
||||
<select
|
||||
v-if="isInEditMode"
|
||||
class="column-select"
|
||||
v-model="store.workflowFilters.issue_type"
|
||||
v-model="store.workflowFilters.issueType"
|
||||
>
|
||||
<option value="">{{ locale.issuesAndPullRequests }}</option>
|
||||
<option value="issue">{{ locale.issuesOnly }}</option>
|
||||
<option value="pull_request">{{ locale.pullRequestsOnly }}</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.workflowFilters.issue_type === 'issue' ? locale.issuesOnly :
|
||||
store.workflowFilters.issue_type === 'pull_request' ? locale.pullRequestsOnly :
|
||||
{{ store.workflowFilters.issueType === 'issue' ? locale.issuesOnly :
|
||||
store.workflowFilters.issueType === 'pull_request' ? locale.pullRequestsOnly :
|
||||
locale.issuesAndPullRequests }}
|
||||
</div>
|
||||
</div>
|
||||
@ -796,7 +798,7 @@ onUnmounted(() => {
|
||||
<label>{{ locale.whenMovedFromColumn }}</label>
|
||||
<select
|
||||
v-if="isInEditMode"
|
||||
v-model="store.workflowFilters.source_column"
|
||||
v-model="store.workflowFilters.sourceColumn"
|
||||
class="column-select"
|
||||
>
|
||||
<option value="">{{ locale.anyColumn }}</option>
|
||||
@ -805,7 +807,7 @@ onUnmounted(() => {
|
||||
</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.projectColumns.find(c => String(c.id) === store.workflowFilters.source_column)?.title || locale.anyColumn }}
|
||||
{{ store.projectColumns.find(c => String(c.id) === store.workflowFilters.sourceColumn)?.title || locale.anyColumn }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -813,7 +815,7 @@ onUnmounted(() => {
|
||||
<label>{{ locale.whenMovedToColumn }}</label>
|
||||
<select
|
||||
v-if="isInEditMode"
|
||||
v-model="store.workflowFilters.target_column"
|
||||
v-model="store.workflowFilters.targetColumn"
|
||||
class="column-select"
|
||||
>
|
||||
<option value="">{{ locale.anyColumn }}</option>
|
||||
@ -822,7 +824,7 @@ onUnmounted(() => {
|
||||
</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.projectColumns.find(c => String(c.id) === store.workflowFilters.target_column)?.title || locale.anyColumn }}
|
||||
{{ store.projectColumns.find(c => String(c.id) === store.workflowFilters.targetColumn)?.title || locale.anyColumn }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -894,13 +896,13 @@ onUnmounted(() => {
|
||||
<div class="field" v-if="hasAction('add_labels')">
|
||||
<label>{{ locale.addLabels }}</label>
|
||||
<div v-if="isInEditMode" class="ui fluid multiple search selection dropdown label-dropdown">
|
||||
<input type="hidden" :value="store.workflowActions.add_labels.join(',')">
|
||||
<input type="hidden" :value="store.workflowActions.addLabels.join(',')">
|
||||
<i class="dropdown icon"/>
|
||||
<div class="text" :class="{ default: !store.workflowActions.add_labels?.length }">
|
||||
<span v-if="!store.workflowActions.add_labels?.length">{{ locale.none }}</span>
|
||||
<div class="text" :class="{ default: !store.workflowActions.addLabels?.length }">
|
||||
<span v-if="!store.workflowActions.addLabels?.length">{{ locale.none }}</span>
|
||||
<template v-else>
|
||||
<span
|
||||
v-for="labelId in store.workflowActions.add_labels" :key="labelId"
|
||||
v-for="labelId in store.workflowActions.addLabels" :key="labelId"
|
||||
class="ui label"
|
||||
:style="`background-color: ${store.projectLabels.find(l => String(l.id) === labelId)?.color}; color: ${getLabelTextColor(store.projectLabels.find(l => String(l.id) === labelId)?.color)}`"
|
||||
>
|
||||
@ -913,7 +915,7 @@ onUnmounted(() => {
|
||||
class="item" v-for="label in store.projectLabels" :key="label.id"
|
||||
:data-value="String(label.id)"
|
||||
@click.prevent="toggleLabel('add_labels', String(label.id))"
|
||||
:class="{ active: store.workflowActions.add_labels.includes(String(label.id)), selected: store.workflowActions.add_labels.includes(String(label.id)) }"
|
||||
:class="{ active: store.workflowActions.addLabels.includes(String(label.id)), selected: store.workflowActions.addLabels.includes(String(label.id)) }"
|
||||
>
|
||||
<span class="ui label" :style="`background-color: ${label.color}; color: ${getLabelTextColor(label.color)}`">
|
||||
{{ label.name }}
|
||||
@ -922,9 +924,9 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ui labels">
|
||||
<span v-if="!store.workflowActions.add_labels?.length" class="text-muted">{{ locale.none }}</span>
|
||||
<span v-if="!store.workflowActions.addLabels?.length" class="text-muted">{{ locale.none }}</span>
|
||||
<span
|
||||
v-for="labelId in store.workflowActions.add_labels" :key="labelId"
|
||||
v-for="labelId in store.workflowActions.addLabels" :key="labelId"
|
||||
class="ui label"
|
||||
:style="`background-color: ${store.projectLabels.find(l => String(l.id) === labelId)?.color}; color: ${getLabelTextColor(store.projectLabels.find(l => String(l.id) === labelId)?.color)}`"
|
||||
>
|
||||
@ -936,13 +938,13 @@ onUnmounted(() => {
|
||||
<div class="field" v-if="hasAction('remove_labels')">
|
||||
<label>{{ locale.removeLabels }}</label>
|
||||
<div v-if="isInEditMode" class="ui fluid multiple search selection dropdown label-dropdown">
|
||||
<input type="hidden" :value="store.workflowActions.remove_labels.join(',')">
|
||||
<input type="hidden" :value="store.workflowActions.removeLabels.join(',')">
|
||||
<i class="dropdown icon"/>
|
||||
<div class="text" :class="{ default: !store.workflowActions.remove_labels?.length }">
|
||||
<span v-if="!store.workflowActions.remove_labels?.length">{{ locale.none }}</span>
|
||||
<div class="text" :class="{ default: !store.workflowActions.removeLabels?.length }">
|
||||
<span v-if="!store.workflowActions.removeLabels?.length">{{ locale.none }}</span>
|
||||
<template v-else>
|
||||
<span
|
||||
v-for="labelId in store.workflowActions.remove_labels" :key="labelId"
|
||||
v-for="labelId in store.workflowActions.removeLabels" :key="labelId"
|
||||
class="ui label"
|
||||
:style="`background-color: ${store.projectLabels.find(l => String(l.id) === labelId)?.color}; color: ${getLabelTextColor(store.projectLabels.find(l => String(l.id) === labelId)?.color)}`"
|
||||
>
|
||||
@ -955,7 +957,7 @@ onUnmounted(() => {
|
||||
class="item" v-for="label in store.projectLabels" :key="label.id"
|
||||
:data-value="String(label.id)"
|
||||
@click.prevent="toggleLabel('remove_labels', String(label.id))"
|
||||
:class="{ active: store.workflowActions.remove_labels.includes(String(label.id)), selected: store.workflowActions.remove_labels.includes(String(label.id)) }"
|
||||
:class="{ active: store.workflowActions.removeLabels.includes(String(label.id)), selected: store.workflowActions.removeLabels.includes(String(label.id)) }"
|
||||
>
|
||||
<span class="ui label" :style="`background-color: ${label.color}; color: ${getLabelTextColor(label.color)}`">
|
||||
{{ label.name }}
|
||||
@ -964,9 +966,9 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ui labels">
|
||||
<span v-if="!store.workflowActions.remove_labels?.length" class="text-muted">{{ locale.none }}</span>
|
||||
<span v-if="!store.workflowActions.removeLabels?.length" class="text-muted">{{ locale.none }}</span>
|
||||
<span
|
||||
v-for="labelId in store.workflowActions.remove_labels" :key="labelId"
|
||||
v-for="labelId in store.workflowActions.removeLabels" :key="labelId"
|
||||
class="ui label"
|
||||
:style="`background-color: ${store.projectLabels.find(l => String(l.id) === labelId)?.color}; color: ${getLabelTextColor(store.projectLabels.find(l => String(l.id) === labelId)?.color)}`"
|
||||
>
|
||||
@ -981,15 +983,15 @@ onUnmounted(() => {
|
||||
v-if="isInEditMode"
|
||||
id="issue-state-action"
|
||||
class="column-select"
|
||||
v-model="store.workflowActions.issue_state"
|
||||
v-model="store.workflowActions.issueState"
|
||||
>
|
||||
<option value="">{{ locale.noChange }}</option>
|
||||
<option value="close">{{ locale.closeIssue }}</option>
|
||||
<option value="reopen">{{ locale.reopenIssue }}</option>
|
||||
</select>
|
||||
<div v-else class="readonly-value">
|
||||
{{ store.workflowActions.issue_state === 'close' ? locale.closeIssue :
|
||||
store.workflowActions.issue_state === 'reopen' ? locale.reopenIssue : locale.noChange }}
|
||||
{{ store.workflowActions.issueState === 'close' ? locale.closeIssue :
|
||||
store.workflowActions.issueState === 'reopen' ? locale.reopenIssue : locale.noChange }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import {reactive} from 'vue';
|
||||
import {GET, POST} from '../../modules/fetch.ts';
|
||||
import {showErrorToast} from '../../modules/toast.ts';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
|
||||
type WorkflowFilters = {
|
||||
issue_type: string;
|
||||
source_column: string;
|
||||
target_column: string;
|
||||
issueType: string;
|
||||
sourceColumn: string;
|
||||
targetColumn: string;
|
||||
labels: string[];
|
||||
};
|
||||
|
||||
@ -13,9 +14,9 @@ type WorkflowIssueStateAction = '' | 'close' | 'reopen';
|
||||
|
||||
type WorkflowActions = {
|
||||
column: string;
|
||||
add_labels: string[];
|
||||
remove_labels: string[];
|
||||
issue_state: WorkflowIssueStateAction;
|
||||
addLabels: string[];
|
||||
removeLabels: string[];
|
||||
issueState: WorkflowIssueStateAction;
|
||||
};
|
||||
|
||||
type WorkflowDraftState = {
|
||||
@ -35,15 +36,15 @@ export type ProjectLabel = {
|
||||
};
|
||||
|
||||
type WorkflowCapabilities = {
|
||||
available_filters?: string[];
|
||||
available_actions?: string[];
|
||||
availableFilters?: string[];
|
||||
availableActions?: string[];
|
||||
};
|
||||
|
||||
export type WorkflowEvent = {
|
||||
id: number;
|
||||
event_id: string;
|
||||
workflow_event?: string;
|
||||
display_name?: string;
|
||||
eventId: string;
|
||||
workflowEvent?: string;
|
||||
displayName?: string;
|
||||
summary?: string;
|
||||
enabled?: boolean;
|
||||
capabilities?: WorkflowCapabilities;
|
||||
@ -79,19 +80,29 @@ type WorkflowStoreState = {
|
||||
deleteWorkflow(): Promise<void>;
|
||||
};
|
||||
|
||||
const createDefaultFilters = (): WorkflowFilters => ({issue_type: '', source_column: '', target_column: '', labels: []});
|
||||
const createDefaultActions = (): WorkflowActions => ({column: '', add_labels: [], remove_labels: [], issue_state: ''});
|
||||
const createDefaultFilters = (): WorkflowFilters => ({issueType: '', sourceColumn: '', targetColumn: '', labels: []});
|
||||
const createDefaultActions = (): WorkflowActions => ({column: '', addLabels: [], removeLabels: [], issueState: ''});
|
||||
|
||||
const camelToSnake = (key: string): string => key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
||||
|
||||
function convertKeysToSnakeCase<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
result[camelToSnake(key)] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertFilters(workflow: any): WorkflowFilters {
|
||||
const filters = createDefaultFilters();
|
||||
if (workflow?.filters && Array.isArray(workflow.filters)) {
|
||||
for (const filter of workflow.filters) {
|
||||
if (filter.type === 'issue_type') {
|
||||
filters.issue_type = filter.value;
|
||||
filters.issueType = filter.value;
|
||||
} else if (filter.type === 'source_column') {
|
||||
filters.source_column = filter.value;
|
||||
filters.sourceColumn = filter.value;
|
||||
} else if (filter.type === 'target_column') {
|
||||
filters.target_column = filter.value;
|
||||
filters.targetColumn = filter.value;
|
||||
} else if (filter.type === 'labels') {
|
||||
filters.labels.push(filter.value);
|
||||
}
|
||||
@ -110,12 +121,12 @@ function convertActions(workflow: any): WorkflowActions {
|
||||
actions.column = action.value;
|
||||
} else if (action.type === 'add_labels') {
|
||||
// Backend returns string, keep as string to match label.id type
|
||||
actions.add_labels.push(action.value);
|
||||
actions.addLabels.push(action.value);
|
||||
} else if (action.type === 'remove_labels') {
|
||||
// Backend returns string, keep as string to match label.id type
|
||||
actions.remove_labels.push(action.value);
|
||||
actions.removeLabels.push(action.value);
|
||||
} else if (action.type === 'issue_state') {
|
||||
actions.issue_state = action.value as WorkflowIssueStateAction;
|
||||
actions.issueState = action.value as WorkflowIssueStateAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,17 +134,17 @@ function convertActions(workflow: any): WorkflowActions {
|
||||
}
|
||||
|
||||
const cloneFilters = (filters: WorkflowFilters): WorkflowFilters => ({
|
||||
issue_type: filters.issue_type,
|
||||
source_column: filters.source_column,
|
||||
target_column: filters.target_column,
|
||||
issueType: filters.issueType,
|
||||
sourceColumn: filters.sourceColumn,
|
||||
targetColumn: filters.targetColumn,
|
||||
labels: Array.from(filters.labels),
|
||||
});
|
||||
|
||||
const cloneActions = (actions: WorkflowActions): WorkflowActions => ({
|
||||
column: actions.column,
|
||||
add_labels: Array.from(actions.add_labels),
|
||||
remove_labels: Array.from(actions.remove_labels),
|
||||
issue_state: actions.issue_state,
|
||||
addLabels: Array.from(actions.addLabels),
|
||||
removeLabels: Array.from(actions.removeLabels),
|
||||
issueState: actions.issueState,
|
||||
});
|
||||
|
||||
export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
@ -170,7 +181,8 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
|
||||
async loadEvents(): Promise<WorkflowEvent[]> {
|
||||
const response = await GET(`${props.projectLink}/workflows/events`);
|
||||
store.workflowEvents = await response.json() as WorkflowEvent[];
|
||||
const data = await response.json();
|
||||
store.workflowEvents = camelcaseKeys(data, {deep: true}) as WorkflowEvent[];
|
||||
return store.workflowEvents;
|
||||
},
|
||||
|
||||
@ -199,7 +211,7 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
}
|
||||
|
||||
// Find the workflow from existing workflowEvents
|
||||
const workflow = store.workflowEvents.find((e: WorkflowEvent) => e.event_id === eventId);
|
||||
const workflow = store.workflowEvents.find((e: WorkflowEvent) => e.eventId === eventId);
|
||||
|
||||
store.workflowFilters = convertFilters(workflow);
|
||||
store.workflowActions = convertActions(workflow);
|
||||
@ -223,7 +235,7 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
store.workflowFilters = createDefaultFilters();
|
||||
store.workflowActions = createDefaultActions();
|
||||
|
||||
const currentEventId = store.selectedWorkflow?.event_id;
|
||||
const currentEventId = store.selectedWorkflow?.eventId;
|
||||
if (currentEventId) {
|
||||
store.updateDraft(currentEventId, store.workflowFilters, store.workflowActions);
|
||||
}
|
||||
@ -235,9 +247,9 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
// Validate: at least one action must be configured
|
||||
const hasAtLeastOneAction = Boolean(
|
||||
store.workflowActions.column ||
|
||||
store.workflowActions.add_labels.length > 0 ||
|
||||
store.workflowActions.remove_labels.length > 0 ||
|
||||
store.workflowActions.issue_state,
|
||||
store.workflowActions.addLabels.length > 0 ||
|
||||
store.workflowActions.removeLabels.length > 0 ||
|
||||
store.workflowActions.issueState,
|
||||
);
|
||||
|
||||
if (!hasAtLeastOneAction) {
|
||||
@ -248,13 +260,13 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
store.saving = true;
|
||||
try {
|
||||
// For new workflows, use the base event type
|
||||
const eventId = store.selectedWorkflow.event_id;
|
||||
const eventId = store.selectedWorkflow.eventId;
|
||||
|
||||
// Convert frontend data format to backend JSON format
|
||||
const postData = {
|
||||
event_id: eventId,
|
||||
filters: store.workflowFilters,
|
||||
actions: store.workflowActions,
|
||||
filters: convertKeysToSnakeCase(store.workflowFilters),
|
||||
actions: convertKeysToSnakeCase(store.workflowActions),
|
||||
};
|
||||
|
||||
const response = await POST(`${props.projectLink}/workflows/${eventId}`, {
|
||||
@ -280,45 +292,46 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const data = await response.json();
|
||||
const result = camelcaseKeys(data, {deep: true});
|
||||
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 eventKey = typeof store.selectedWorkflow.eventId === 'string' ? store.selectedWorkflow.eventId : '';
|
||||
const wasNewWorkflow = store.selectedWorkflow.id === 0 ||
|
||||
eventKey.startsWith('new-') ||
|
||||
eventKey.startsWith('clone-');
|
||||
|
||||
if (wasNewWorkflow && store.selectedWorkflow.workflow_event) {
|
||||
store.clearDraft(store.selectedWorkflow.workflow_event);
|
||||
if (wasNewWorkflow && store.selectedWorkflow.workflowEvent) {
|
||||
store.clearDraft(store.selectedWorkflow.workflowEvent);
|
||||
}
|
||||
|
||||
// Reload events from server to get the correct event structure
|
||||
await store.loadEvents();
|
||||
|
||||
// Find the reloaded workflow which has complete data including capabilities
|
||||
const reloadedWorkflow = store.workflowEvents.find((w: WorkflowEvent) => w.event_id === result.workflow.event_id);
|
||||
const reloadedWorkflow = store.workflowEvents.find((w: WorkflowEvent) => w.eventId === result.workflow.eventId);
|
||||
|
||||
if (reloadedWorkflow) {
|
||||
// Use the reloaded workflow as it has all the necessary fields
|
||||
store.selectedWorkflow = reloadedWorkflow;
|
||||
store.selectedItem = reloadedWorkflow.event_id;
|
||||
store.selectedItem = reloadedWorkflow.eventId;
|
||||
} else {
|
||||
// Fallback: use the result from backend (shouldn't normally happen)
|
||||
store.selectedWorkflow = result.workflow;
|
||||
store.selectedItem = result.workflow.event_id;
|
||||
store.selectedItem = result.workflow.eventId;
|
||||
}
|
||||
|
||||
store.workflowFilters = convertFilters(store.selectedWorkflow);
|
||||
store.workflowActions = convertActions(store.selectedWorkflow);
|
||||
if (store.selectedWorkflow?.event_id) {
|
||||
store.updateDraft(store.selectedWorkflow.event_id, store.workflowFilters, store.workflowActions);
|
||||
if (store.selectedWorkflow?.eventId) {
|
||||
store.updateDraft(store.selectedWorkflow.eventId, store.workflowFilters, store.workflowActions);
|
||||
}
|
||||
|
||||
// Update URL to use the new workflow ID
|
||||
if (wasNewWorkflow && store.selectedWorkflow?.event_id) {
|
||||
const newUrl = `${props.projectLink}/workflows/${store.selectedWorkflow.event_id}`;
|
||||
window.history.replaceState({eventId: store.selectedWorkflow.event_id}, '', newUrl);
|
||||
if (wasNewWorkflow && store.selectedWorkflow?.eventId) {
|
||||
const newUrl = `${props.projectLink}/workflows/${store.selectedWorkflow.eventId}`;
|
||||
window.history.replaceState({eventId: store.selectedWorkflow.eventId}, '', newUrl);
|
||||
}
|
||||
} else {
|
||||
console.error('Unexpected response format:', result);
|
||||
@ -361,7 +374,7 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// Update workflow in the list
|
||||
const existingIndex = store.workflowEvents.findIndex((e: WorkflowEvent) => e.event_id === selected.event_id);
|
||||
const existingIndex = store.workflowEvents.findIndex((e: WorkflowEvent) => e.eventId === selected.eventId);
|
||||
if (existingIndex >= 0) {
|
||||
store.workflowEvents[existingIndex].enabled = desiredEnabled;
|
||||
}
|
||||
@ -399,7 +412,7 @@ export function createWorkflowStore(props: any): WorkflowStoreState {
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
// Remove workflow from the list
|
||||
const existingIndex = store.workflowEvents.findIndex((e: WorkflowEvent) => e.event_id === selected.event_id);
|
||||
const existingIndex = store.workflowEvents.findIndex((e: WorkflowEvent) => e.eventId === selected.eventId);
|
||||
if (existingIndex >= 0) {
|
||||
store.workflowEvents.splice(existingIndex, 1);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user