mirror of
https://github.com/directus/directus.git
synced 2026-01-31 08:27:57 -05:00
Add discard confirmation prompt for project settings (#8373)
This commit is contained in:
@@ -80,6 +80,19 @@
|
||||
<div v-md="t('page_help_settings_datamodel_fields')" class="page-description" />
|
||||
</sidebar-detail>
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="confirmLeave" @esc="confirmLeave = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('unsaved_changes') }}</v-card-title>
|
||||
<v-card-text>{{ t('unsaved_changes_copy') }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="discardAndLeave">
|
||||
{{ t('discard_changes') }}
|
||||
</v-button>
|
||||
<v-button @click="confirmLeave = false">{{ t('keep_editing') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</private-view>
|
||||
</template>
|
||||
|
||||
@@ -91,8 +104,9 @@ import { useCollection } from '@directus/shared/composables';
|
||||
import FieldsManagement from './components/fields-management.vue';
|
||||
|
||||
import useItem from '@/composables/use-item';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useCollectionsStore, useFieldsStore } from '@/stores';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SettingsNavigation, FieldsManagement },
|
||||
@@ -131,6 +145,26 @@ export default defineComponent({
|
||||
|
||||
const confirmDelete = ref(false);
|
||||
|
||||
const isSavable = computed(() => {
|
||||
if (hasEdits.value === true) return true;
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return {
|
||||
t,
|
||||
collectionInfo,
|
||||
@@ -150,6 +184,10 @@ export default defineComponent({
|
||||
deleteAndQuit,
|
||||
saveAndQuit,
|
||||
hasEdits,
|
||||
isSavable,
|
||||
confirmLeave,
|
||||
leaveTo,
|
||||
discardAndLeave,
|
||||
};
|
||||
|
||||
async function deleteAndQuit() {
|
||||
@@ -165,6 +203,13 @@ export default defineComponent({
|
||||
await fieldsStore.hydrate();
|
||||
router.push(`/settings/data-model`);
|
||||
}
|
||||
|
||||
function discardAndLeave() {
|
||||
if (!leaveTo.value) return;
|
||||
edits.value = {};
|
||||
confirmLeave.value = false;
|
||||
router.push(leaveTo.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -113,6 +113,19 @@
|
||||
</sidebar-detail>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="confirmLeave" @esc="confirmLeave = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('unsaved_changes') }}</v-card-title>
|
||||
<v-card-text>{{ t('unsaved_changes_copy') }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="discardAndLeave">
|
||||
{{ t('discard_changes') }}
|
||||
</v-button>
|
||||
<v-button @click="confirmLeave = false">{{ t('keep_editing') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</private-view>
|
||||
</component>
|
||||
</template>
|
||||
@@ -126,9 +139,10 @@ import { Preset, Filter } from '@directus/shared/types';
|
||||
import api from '@/api';
|
||||
import { useCollectionsStore, usePresetsStore } from '@/stores';
|
||||
import { getLayouts } from '@/layouts';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { useLayout } from '@/composables/use-layout';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
|
||||
type FormattedPreset = {
|
||||
id: number;
|
||||
@@ -184,6 +198,26 @@ export default defineComponent({
|
||||
|
||||
const { layoutWrapper } = useLayout(layout);
|
||||
|
||||
const isSavable = computed(() => {
|
||||
if (hasEdits.value === true) return true;
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return {
|
||||
t,
|
||||
backLink,
|
||||
@@ -205,6 +239,10 @@ export default defineComponent({
|
||||
confirmDelete,
|
||||
updateFilters,
|
||||
searchQuery,
|
||||
isSavable,
|
||||
confirmLeave,
|
||||
leaveTo,
|
||||
discardAndLeave,
|
||||
};
|
||||
|
||||
function useSave() {
|
||||
@@ -496,6 +534,13 @@ export default defineComponent({
|
||||
|
||||
return { fields };
|
||||
}
|
||||
|
||||
function discardAndLeave() {
|
||||
if (!leaveTo.value) return;
|
||||
edits.value = {};
|
||||
confirmLeave.value = false;
|
||||
router.push(leaveTo.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -24,6 +24,19 @@
|
||||
<template #sidebar>
|
||||
<project-info-sidebar-detail />
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="confirmLeave" @esc="confirmLeave = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('unsaved_changes') }}</v-card-title>
|
||||
<v-card-text>{{ t('unsaved_changes_copy') }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="discardAndLeave">
|
||||
{{ t('discard_changes') }}
|
||||
</v-button>
|
||||
<v-button @click="confirmLeave = false">{{ t('keep_editing') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</private-view>
|
||||
</template>
|
||||
|
||||
@@ -35,12 +48,16 @@ import { useCollection } from '@directus/shared/composables';
|
||||
import { useSettingsStore, useServerStore } from '@/stores';
|
||||
import ProjectInfoSidebarDetail from './components/project-info-sidebar-detail.vue';
|
||||
import { clone } from 'lodash';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SettingsNavigation, ProjectInfoSidebarDetail },
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const serverStore = useServerStore();
|
||||
|
||||
@@ -54,7 +71,39 @@ export default defineComponent({
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
return { t, fields, initialValues, edits, noEdits, saving, save };
|
||||
const isSavable = computed(() => {
|
||||
if (noEdits.value === true) return false;
|
||||
return noEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (!noEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return {
|
||||
t,
|
||||
fields,
|
||||
initialValues,
|
||||
edits,
|
||||
noEdits,
|
||||
saving,
|
||||
isSavable,
|
||||
confirmLeave,
|
||||
leaveTo,
|
||||
save,
|
||||
discardAndLeave,
|
||||
};
|
||||
|
||||
async function save() {
|
||||
if (edits.value === null) return;
|
||||
@@ -65,6 +114,13 @@ export default defineComponent({
|
||||
saving.value = false;
|
||||
initialValues.value = clone(settingsStore.settings);
|
||||
}
|
||||
|
||||
function discardAndLeave() {
|
||||
if (!leaveTo.value) return;
|
||||
edits.value = {};
|
||||
confirmLeave.value = false;
|
||||
router.push(leaveTo.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -84,6 +84,19 @@
|
||||
<role-info-sidebar-detail :role="item" />
|
||||
<revisions-drawer-detail collection="directus_roles" :primary-key="primaryKey" />
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="confirmLeave" @esc="confirmLeave = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('unsaved_changes') }}</v-card-title>
|
||||
<v-card-text>{{ t('unsaved_changes_copy') }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="discardAndLeave">
|
||||
{{ t('discard_changes') }}
|
||||
</v-button>
|
||||
<v-button @click="confirmLeave = false">{{ t('keep_editing') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</private-view>
|
||||
</template>
|
||||
|
||||
@@ -92,13 +105,14 @@ import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, computed, toRefs, ref } from 'vue';
|
||||
|
||||
import SettingsNavigation from '../../../components/navigation.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
import { useUserStore } from '@/stores/';
|
||||
import RoleInfoSidebarDetail from './components/role-info-sidebar-detail.vue';
|
||||
import PermissionsOverview from './components/permissions-overview.vue';
|
||||
import UsersInvite from '@/views/private/components/users-invite';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RolesItem',
|
||||
@@ -149,6 +163,26 @@ export default defineComponent({
|
||||
return !!values.app_access;
|
||||
});
|
||||
|
||||
const isSavable = computed(() => {
|
||||
if (hasEdits.value === true) return true;
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return {
|
||||
t,
|
||||
item,
|
||||
@@ -165,6 +199,10 @@ export default defineComponent({
|
||||
adminEnabled,
|
||||
userInviteModalActive,
|
||||
appAccess,
|
||||
isSavable,
|
||||
confirmLeave,
|
||||
leaveTo,
|
||||
discardAndLeave,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -184,6 +222,13 @@ export default defineComponent({
|
||||
await remove();
|
||||
router.push(`/settings/roles`);
|
||||
}
|
||||
|
||||
function discardAndLeave() {
|
||||
if (!leaveTo.value) return;
|
||||
edits.value = {};
|
||||
confirmLeave.value = false;
|
||||
router.push(leaveTo.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -64,6 +64,19 @@
|
||||
</sidebar-detail>
|
||||
<revisions-drawer-detail v-if="isNew === false" collection="directus_webhooks" :primary-key="primaryKey" />
|
||||
</template>
|
||||
|
||||
<v-dialog v-model="confirmLeave" @esc="confirmLeave = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('unsaved_changes') }}</v-card-title>
|
||||
<v-card-text>{{ t('unsaved_changes_copy') }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="discardAndLeave">
|
||||
{{ t('discard_changes') }}
|
||||
</v-button>
|
||||
<v-button @click="confirmLeave = false">{{ t('keep_editing') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</private-view>
|
||||
</template>
|
||||
|
||||
@@ -72,10 +85,11 @@ import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, computed, toRefs, ref } from 'vue';
|
||||
|
||||
import SettingsNavigation from '../../components/navigation.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WebhooksItem',
|
||||
@@ -117,6 +131,26 @@ export default defineComponent({
|
||||
return item.value?.name;
|
||||
});
|
||||
|
||||
const isSavable = computed(() => {
|
||||
if (hasEdits.value === true) return true;
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return {
|
||||
t,
|
||||
item,
|
||||
@@ -136,6 +170,10 @@ export default defineComponent({
|
||||
isBatch,
|
||||
title,
|
||||
validationErrors,
|
||||
isSavable,
|
||||
confirmLeave,
|
||||
leaveTo,
|
||||
discardAndLeave,
|
||||
};
|
||||
|
||||
async function saveAndQuit() {
|
||||
@@ -161,6 +199,13 @@ export default defineComponent({
|
||||
await remove();
|
||||
router.push(`/settings/webhooks`);
|
||||
}
|
||||
|
||||
function discardAndLeave() {
|
||||
if (!leaveTo.value) return;
|
||||
edits.value = {};
|
||||
confirmLeave.value = false;
|
||||
router.push(leaveTo.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user