From 76cc8679de406ad21e11f2a44391c306dde1f805 Mon Sep 17 00:00:00 2001 From: Mark Burvill Date: Tue, 4 Nov 2025 11:01:42 +0000 Subject: [PATCH] Added option to automatically refresh current item after Flow completes. --- contributors.yml | 1 + packages/flow-trigger-bundle/package.json | 3 +- .../src/composables/use-flow-triggers.ts | 96 +++++++++++++++++++ .../src/flow-triggers-interface/index.ts | 15 +++ .../src/flow-triggers-interface/interface.vue | 38 +++++++- .../src/flow-triggers-panel/panel.vue | 4 +- 6 files changed, 152 insertions(+), 5 deletions(-) diff --git a/contributors.yml b/contributors.yml index 6a1e1233..3678af60 100644 --- a/contributors.yml +++ b/contributors.yml @@ -6,3 +6,4 @@ - timio23 - Dominic-Marcelino - ukmadlz +- MarkBurvs diff --git a/packages/flow-trigger-bundle/package.json b/packages/flow-trigger-bundle/package.json index a99da17f..acd3c9ef 100644 --- a/packages/flow-trigger-bundle/package.json +++ b/packages/flow-trigger-bundle/package.json @@ -46,7 +46,8 @@ "dependencies": { "@directus/format-title": "11.0.0", "@directus/types": "11.1.1", - "vue-i18n": "9.13.1" + "vue-i18n": "9.13.1", + "lodash-es": "^4.17.21" }, "devDependencies": { "@directus/extensions-sdk": "12.0.1", diff --git a/packages/flow-trigger-bundle/src/composables/use-flow-triggers.ts b/packages/flow-trigger-bundle/src/composables/use-flow-triggers.ts index c94b3ee3..33578167 100644 --- a/packages/flow-trigger-bundle/src/composables/use-flow-triggers.ts +++ b/packages/flow-trigger-bundle/src/composables/use-flow-triggers.ts @@ -4,10 +4,13 @@ import { useApi, useStores } from '@directus/extensions-sdk'; import formatTitle from '@directus/format-title'; import { computed, ref, unref } from 'vue'; import { useI18n } from 'vue-i18n'; +import { isEqual } from 'lodash-es'; interface FlowTriggerContext { collection: (trigger: Trigger) => string | undefined; keys: (trigger: Trigger) => PrimaryKey[] | undefined; + autoRefresh?: () => boolean; + formValues?: () => Record; } export function useFlowTriggers(context: FlowTriggerContext) { @@ -54,6 +57,10 @@ export function useFlowTriggers(context: FlowTriggerContext) { fields: Record[]; } | null>(null); + const showUnsavedWarning = ref(false); + + const checkingUnsavedChanges = ref([]); + const isConfirmButtonDisabled = computed(() => { if (!selectedTrigger.value) { return true; @@ -161,6 +168,15 @@ export function useFlowTriggers(context: FlowTriggerContext) { title: t('run_flow_success', { flow: flow.name }), }); + // Refresh the current page to show updated data (if enabled) + const shouldAutoRefresh = context.autoRefresh?.() ?? true; + if (shouldAutoRefresh) { + // Use a small delay to ensure the notification is visible before refresh + setTimeout(() => { + window.location.reload(); + }, 500); + } + resetConfirm(); } catch (error) { @@ -195,15 +211,92 @@ export function useFlowTriggers(context: FlowTriggerContext) { }); } + async function hasUnsavedChanges(trigger: Trigger): Promise { + try { + const collection = context.collection(trigger); + const keys = context.keys(trigger); + + // Can't detect unsaved changes if no collection, keys, or form values + if (!collection || !keys || keys.length === 0 || keys[0] === '+') { + return false; + } + + // Can't detect if no form values are available + if (!context.formValues) { + return false; + } + + // Fetch the saved item from API + const response = await api.get(`/items/${collection}/${keys[0]}`); + const savedValues = response.data.data; + const currentValues = context.formValues(); + + // Deep compare each field to detect differences + for (const key in currentValues) { + if (key in savedValues && !isEqual(currentValues[key], savedValues[key])) { + return true; // Found a difference + } + } + + return false; // No differences found + } + catch (error) { + // If we can't fetch the item or compare, assume no unsaved changes + console.warn('Could not detect unsaved changes:', error); + return false; + } + } + async function onTriggerClick(trigger: Trigger) { selectedTrigger.value = trigger; + + // Check if we should warn about unsaved changes + const shouldAutoRefresh = context.autoRefresh?.() ?? true; + const keys = context.keys(trigger); + const isItemDetailPage = keys && keys.length > 0 && keys[0] !== '+'; + + if (shouldAutoRefresh && isItemDetailPage) { + // Add to checking state + const flowId = trigger.flowId; + checkingUnsavedChanges.value = [...checkingUnsavedChanges.value, flowId]; + + try { + // Check for actual unsaved changes + const hasChanges = await hasUnsavedChanges(trigger); + + if (hasChanges) { + // Show unsaved changes warning + showUnsavedWarning.value = true; + return; + } + } + finally { + // Remove from checking state + checkingUnsavedChanges.value = checkingUnsavedChanges.value.filter( + (id) => id !== flowId, + ); + } + } + + // No unsaved changes or no auto-refresh, proceed directly + confirmRunFlow(); + } + + function proceedAfterWarning() { + showUnsavedWarning.value = false; confirmRunFlow(); } + function cancelWarning() { + showUnsavedWarning.value = false; + selectedTrigger.value = null; + } + return { getFlow, runFlow, runningFlows, + checkingUnsavedChanges, onTriggerClick, getButtonText, getButtonIcon, @@ -213,5 +306,8 @@ export function useFlowTriggers(context: FlowTriggerContext) { isConfirmButtonDisabled, getConfirmButtonText, resetConfirm, + showUnsavedWarning, + proceedAfterWarning, + cancelWarning, }; } diff --git a/packages/flow-trigger-bundle/src/flow-triggers-interface/index.ts b/packages/flow-trigger-bundle/src/flow-triggers-interface/index.ts index 352dd08d..b4757b09 100644 --- a/packages/flow-trigger-bundle/src/flow-triggers-interface/index.ts +++ b/packages/flow-trigger-bundle/src/flow-triggers-interface/index.ts @@ -43,6 +43,21 @@ export default defineInterface({ }, }, }, + { + field: 'autoRefresh', + name: 'Auto Refresh', + type: 'boolean', + meta: { + interface: 'boolean', + options: { + label: 'Automatically refresh item data after flow completes', + }, + width: 'full', + }, + schema: { + default_value: true, + }, + }, ]; }, }); diff --git a/packages/flow-trigger-bundle/src/flow-triggers-interface/interface.vue b/packages/flow-trigger-bundle/src/flow-triggers-interface/interface.vue index 0e41766a..12996cf3 100644 --- a/packages/flow-trigger-bundle/src/flow-triggers-interface/interface.vue +++ b/packages/flow-trigger-bundle/src/flow-triggers-interface/interface.vue @@ -1,7 +1,7 @@