Replace soft delete with archive, add unarchive

This commit is contained in:
rijkvanzanten
2020-09-02 13:17:11 -04:00
parent bdeed26502
commit 0e886f4db0
8 changed files with 126 additions and 72 deletions

View File

@@ -13,12 +13,22 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
const loading = ref(false);
const saving = ref(false);
const deleting = ref(false);
const softDeleting = ref(false);
const archiving = ref(false);
const edits = ref({});
const isNew = computed(() => primaryKey.value === '+');
const isBatch = computed(() => typeof primaryKey.value === 'string' && primaryKey.value.includes(','));
const isSingle = computed(() => !!collectionInfo.value?.meta?.singleton);
const isArchived = computed(() => {
if (!collectionInfo.value?.meta?.archive_field) return null;
if (collectionInfo.value.meta.archive_value === 'true') {
return item.value?.[collectionInfo.value.meta.archive_field] === true;
}
return item.value?.[collectionInfo.value.meta.archive_field] === collectionInfo.value.meta.archive_value;
});
const endpoint = computed(() => {
return collection.value.startsWith('directus_')
? `/${collection.value.substring(9)}`
@@ -45,9 +55,10 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
save,
isNew,
remove,
softDelete,
deleting,
softDeleting,
archive,
isArchived,
archiving,
saveAsCopy,
isBatch,
getItem,
@@ -177,21 +188,36 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
}
}
async function softDelete() {
if (!collectionInfo.value?.meta?.soft_delete_field) return;
async function archive() {
if (!collectionInfo.value?.meta?.archive_field) return;
softDeleting.value = true;
archiving.value = true;
const field = collectionInfo.value.meta.soft_delete_field;
const value = collectionInfo.value.meta.soft_delete_value;
const field = collectionInfo.value.meta.archive_field;
let archiveValue: any = collectionInfo.value.meta.archive_value;
if (archiveValue === 'true') archiveValue = true;
if (archiveValue === 'false') archiveValue = false;
let unarchiveValue: any = collectionInfo.value.meta.unarchive_value;
if (unarchiveValue === 'true') unarchiveValue = true;
if (unarchiveValue === 'false') unarchiveValue = false;
try {
let value: any = item.value[field] === archiveValue ? unarchiveValue : archiveValue;
if (value === 'true') value = true;
if (value === 'false') value = false;
item.value = {
...item.value,
[field]: value,
};
await api.patch(itemEndpoint.value, {
[field]: value,
});
item.value = null;
notify({
title: i18n.tc('item_delete_success', isBatch.value ? 2 : 1),
text: i18n.tc('item_in', isBatch.value ? 2 : 1, {
@@ -212,7 +238,7 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
throw err;
} finally {
softDeleting.value = false;
archiving.value = false;
}
}

View File

@@ -24,6 +24,10 @@ function unbind(element: HTMLElement) {
delete handlers[element.dataset.tooltip as string];
}
function update(element: HTMLElement, binding: DirectiveBinding) {
console.log(element);
}
const Tooltip: DirectiveOptions = {
bind,
unbind,
@@ -32,6 +36,9 @@ const Tooltip: DirectiveOptions = {
bind(element, binding);
} else if (!binding.value && binding.oldValue) {
unbind(element);
} else {
unbind(element);
bind(element, binding);
}
},
};

View File

@@ -35,6 +35,10 @@
"archive_confirm": "Are you sure you want to archive this item?",
"archive_confirm_count": "No Items Selected | Are you sure you want to archive this item? | Are you sure you want to archive these {count} items?",
"unarchive": "Unarchive",
"unarchive_confirm": "Are you sure you want to unarchive this item?",
"unarchive_confirm_count": "No Items Selected | Are you sure you want to unarchive this item? | Are you sure you want to unarchive these {count} items?",
"nested_files_folders_will_be_moved": "Nested files and folders will be moved one level up.",
"markdown": "Markdown",

View File

@@ -75,12 +75,12 @@
</v-dialog>
<v-dialog
v-model="confirmSoftDelete"
v-if="selection.length > 0 && currentCollection.meta && currentCollection.meta.soft_delete_field"
v-model="confirmArchive"
v-if="selection.length > 0 && currentCollection.meta && currentCollection.meta.archive_field"
>
<template #activator="{ on }">
<v-button
:disabled="batchSoftDeleteAllowed !== true"
:disabled="batchArchiveAllowed !== true"
rounded
icon
class="action-soft-delete"
@@ -95,10 +95,10 @@
<v-card-title>{{ $tc('archive_confirm_count', selection.length) }}</v-card-title>
<v-card-actions>
<v-button @click="confirmSoftDelete = false" secondary>
<v-button @click="confirmArchive = false" secondary>
{{ $t('cancel') }}
</v-button>
<v-button @click="batchDelete" class="action-soft-delete" :loading="softDeleting">
<v-button @click="batchDelete" class="action-soft-delete" :loading="archiving">
{{ $t('archive') }}
</v-button>
</v-card-actions>
@@ -279,9 +279,9 @@ export default defineComponent({
confirmDelete,
deleting,
batchDelete,
confirmSoftDelete,
softDelete,
softDeleting,
confirmArchive,
archive,
archiving,
error: deleteError,
} = useBatchDelete();
@@ -303,7 +303,7 @@ export default defineComponent({
{ immediate: true }
);
const { batchEditAllowed, batchSoftDeleteAllowed, batchDeleteAllowed, createAllowed } = usePermissions();
const { batchEditAllowed, batchArchiveAllowed, batchDeleteAllowed, createAllowed } = usePermissions();
return {
addNewLink,
@@ -332,11 +332,11 @@ export default defineComponent({
breadcrumb,
marked,
clearFilters,
confirmSoftDelete,
softDelete,
softDeleting,
confirmArchive,
archive,
archiving,
batchEditAllowed,
batchSoftDeleteAllowed,
batchArchiveAllowed,
batchDeleteAllowed,
deleteError,
createAllowed,
@@ -369,12 +369,12 @@ export default defineComponent({
const confirmDelete = ref(false);
const deleting = ref(false);
const confirmSoftDelete = ref(false);
const softDeleting = ref(false);
const confirmArchive = ref(false);
const archiving = ref(false);
const error = ref<any>();
return { confirmDelete, deleting, batchDelete, confirmSoftDelete, softDeleting, softDelete, error };
return { confirmDelete, deleting, batchDelete, confirmArchive, archiving, archive, error };
async function batchDelete() {
deleting.value = true;
@@ -394,10 +394,10 @@ export default defineComponent({
}
}
async function softDelete() {
if (!currentCollection.value?.meta?.soft_delete_field) return;
async function archive() {
if (!currentCollection.value?.meta?.archive_field) return;
softDeleting.value = true;
archiving.value = true;
const batchPrimaryKeys = selection.value;
@@ -406,11 +406,11 @@ export default defineComponent({
await layout.value?.refresh?.();
selection.value = [];
confirmSoftDelete.value = false;
confirmArchive.value = false;
} catch (err) {
error.value = err;
} finally {
softDeleting.value = false;
archiving.value = false;
}
}
}
@@ -486,8 +486,8 @@ export default defineComponent({
return !!updatePermissions;
});
const batchSoftDeleteAllowed = computed(() => {
if (!currentCollection.value?.meta?.soft_delete_field) return false;
const batchArchiveAllowed = computed(() => {
if (!currentCollection.value?.meta?.archive_field) return false;
const admin = userStore.state?.currentUser?.role.admin === true;
if (admin) return true;
@@ -497,7 +497,7 @@ export default defineComponent({
if (!updatePermissions) return false;
if (!updatePermissions.fields) return false;
if (updatePermissions.fields === '*') return true;
return updatePermissions.fields.split(',').includes(currentCollection.value.meta.soft_delete_field);
return updatePermissions.fields.split(',').includes(currentCollection.value.meta.archive_field);
});
const batchDeleteAllowed = computed(() => {
@@ -520,7 +520,7 @@ export default defineComponent({
return !!createPermissions;
});
return { batchEditAllowed, batchSoftDeleteAllowed, batchDeleteAllowed, createAllowed };
return { batchEditAllowed, batchArchiveAllowed, batchDeleteAllowed, createAllowed };
}
},
});

View File

@@ -74,33 +74,33 @@
</v-dialog>
<v-dialog
v-if="collectionInfo.meta && collectionInfo.meta.soft_delete_field && !isNew"
v-model="confirmSoftDelete"
:disabled="softDeleteAllowed === false"
v-if="collectionInfo.meta && collectionInfo.meta.archive_field && !isNew"
v-model="confirmArchive"
:disabled="archiveAllowed === false"
>
<template #activator="{ on }">
<v-button
rounded
icon
class="action-soft-delete"
v-tooltip.bottom="softDeleteAllowed ? $t('archive') : $t('not_allowed')"
class="action-archive"
v-tooltip.bottom="archiveTooltip"
@click="on"
:disabled="item === null || softDeleteAllowed !== true"
:disabled="item === null || archiveAllowed !== true"
v-if="collectionInfo.meta.singleton === false"
>
<v-icon name="archive" outline />
<v-icon :name="isArchived ? 'unarchive' : 'archive'" outline />
</v-button>
</template>
<v-card>
<v-card-title>{{ $t('archive_confirm') }}</v-card-title>
<v-card-title>{{ isArchived ? $t('unarchive_confirm') : $t('archive_confirm') }}</v-card-title>
<v-card-actions>
<v-button @click="confirmSoftDelete = false" secondary>
<v-button @click="confirmArchive = false" secondary>
{{ $t('cancel') }}
</v-button>
<v-button @click="deleteAndQuit(true)" class="action-soft-delete" :loading="softDeleting">
{{ $t('archive') }}
<v-button @click="toggleArchive" class="action-archive" :loading="archiving">
{{ isArchived ? $t('unarchive') : $t('archive') }}
</v-button>
</v-card-actions>
</v-card>
@@ -237,8 +237,9 @@ export default defineComponent({
save,
remove,
deleting,
softDelete,
softDeleting,
archive,
archiving,
isArchived,
saveAsCopy,
isBatch,
refresh,
@@ -247,7 +248,7 @@ export default defineComponent({
const hasEdits = computed<boolean>(() => Object.keys(edits.value).length > 0);
const confirmDelete = ref(false);
const confirmSoftDelete = ref(false);
const confirmArchive = ref(false);
const confirmLeave = ref(false);
const leaveTo = ref<string | null>(null);
@@ -272,6 +273,12 @@ export default defineComponent({
: i18n.t('editing_in', { collection: collectionInfo.value?.name });
});
const archiveTooltip = computed(() => {
if (archiveAllowed.value === false) return i18n.t('not_allowed');
if (isArchived.value === true) return i18n.t('unarchive');
return i18n.t('archive');
});
useShortcut('mod+s', saveAndStay);
useShortcut('mod+shift+s', saveAndAddNew);
@@ -287,7 +294,7 @@ export default defineComponent({
return next();
};
const { deleteAllowed, softDeleteAllowed, saveAllowed, updateAllowed } = usePermissions();
const { deleteAllowed, archiveAllowed, saveAllowed, updateAllowed } = usePermissions();
return {
item,
@@ -302,14 +309,15 @@ export default defineComponent({
saveAndQuit,
deleteAndQuit,
confirmDelete,
confirmSoftDelete,
confirmArchive,
deleting,
softDeleting,
archiving,
saveAndStay,
saveAndAddNew,
saveAsCopyAndNavigate,
isBatch,
templateValues,
archiveTooltip,
breadcrumb,
title,
revisionsDrawerDetail,
@@ -321,8 +329,10 @@ export default defineComponent({
navigationGuard,
deleteAllowed,
saveAllowed,
softDeleteAllowed,
archiveAllowed,
isArchived,
updateAllowed,
toggleArchive,
};
function useBreadcrumb() {
@@ -369,16 +379,22 @@ export default defineComponent({
router.push(`/collections/${props.collection}/${newPrimaryKey}`);
}
async function deleteAndQuit(soft = false) {
if (soft) {
await softDelete();
} else {
await remove();
}
async function deleteAndQuit() {
await remove();
router.push(`/collections/${props.collection}`);
}
async function toggleArchive() {
await archive();
if (isArchived.value === true) {
router.push(`/collections/${props.collection}`);
} else {
confirmArchive.value = false;
}
}
function discardAndLeave() {
if (!leaveTo.value) return;
edits.value = {};
@@ -396,15 +412,15 @@ export default defineComponent({
});
const updateAllowed = computed(() => isAllowed(collection.value, 'update', item.value));
const softDeleteAllowed = computed(() => {
if (!collectionInfo.value?.meta?.soft_delete_field) return false;
const archiveAllowed = computed(() => {
if (!collectionInfo.value?.meta?.archive_field) return false;
return isAllowed(collection.value, 'update', {
[collectionInfo.value.meta.soft_delete_field]: collectionInfo.value.meta.soft_delete_value,
[collectionInfo.value.meta.archive_field]: collectionInfo.value.meta.archive_value,
});
});
return { deleteAllowed, saveAllowed, softDeleteAllowed, updateAllowed };
return { deleteAllowed, saveAllowed, archiveAllowed, updateAllowed };
}
},
});
@@ -420,7 +436,7 @@ export default defineComponent({
--v-button-color-hover: var(--danger);
}
.action-soft-delete {
.action-archive {
--v-button-background-color: var(--warning-25);
--v-button-color: var(--warning);
--v-button-background-color-hover: var(--warning-50);

View File

@@ -15,8 +15,9 @@ export interface CollectionRaw {
translation: Translation[] | null;
display_template: string | null;
sort_field: string | null;
soft_delete_field: string | null;
soft_delete_value: string | null;
archive_field: string | null;
archive_value: string | null;
unarchive_value: string | null;
} | null;
schema: Record<string, any>;
}

View File

@@ -147,7 +147,7 @@ export default defineComponent({
},
});
const showArchiveToggle = computed(() => !!collectionInfo.value?.meta?.soft_delete_field);
const showArchiveToggle = computed(() => !!collectionInfo.value?.meta?.archive_field);
const archived = computed({
get() {
@@ -156,16 +156,16 @@ export default defineComponent({
);
},
set(showArchived: boolean) {
if (!collectionInfo.value?.meta?.soft_delete_field) return;
if (!collectionInfo.value?.meta?.archive_field) return;
if (showArchived === false) {
emit('input', [
...filters.value,
{
key: 'hide-archived',
field: collectionInfo.value.meta.soft_delete_field,
field: collectionInfo.value.meta.archive_field,
operator: 'neq',
value: collectionInfo.value.meta.soft_delete_value!,
value: collectionInfo.value.meta.archive_value!,
locked: true,
},
]);

View File

@@ -42,7 +42,7 @@ export default defineComponent({
default: null,
},
value: {
type: [String, Number, Object, Array],
type: [String, Number, Object, Array, Boolean],
default: null,
},
type: {