mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Do not load sidebar details until they are opened (#20848)
Co-authored-by: ian <licitdev@gmail.com> Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
This commit is contained in:
5
.changeset/thick-spiders-share.md
Normal file
5
.changeset/thick-spiders-share.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@directus/app': minor
|
||||
---
|
||||
|
||||
Prevented loading of sidebar details (revisions, comments, shares etc) until they are opened
|
||||
@@ -27,13 +27,25 @@ export function useRevisions(
|
||||
const revisions = ref<Revision[] | null>(null);
|
||||
const revisionsByDate = ref<RevisionsByDate[] | null>(null);
|
||||
const loading = ref(false);
|
||||
const loadingCount = ref(false);
|
||||
const revisionsCount = ref(0);
|
||||
const created = ref<Revision>();
|
||||
const pagesCount = ref(0);
|
||||
|
||||
watch([collection, primaryKey, version], () => getRevisions(), { immediate: true });
|
||||
watch([collection, primaryKey, version], () => refresh());
|
||||
|
||||
return { created, revisions, revisionsByDate, loading, refresh, revisionsCount, pagesCount };
|
||||
return {
|
||||
created,
|
||||
revisions,
|
||||
revisionsByDate,
|
||||
getRevisions,
|
||||
loading,
|
||||
refresh,
|
||||
loadingCount,
|
||||
revisionsCount,
|
||||
getRevisionsCount,
|
||||
pagesCount,
|
||||
};
|
||||
|
||||
async function getRevisions(page = 0) {
|
||||
if (typeof unref(primaryKey) === 'undefined') return;
|
||||
@@ -98,7 +110,6 @@ export function useRevisions(
|
||||
'activity.user_agent',
|
||||
'activity.origin',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -137,7 +148,6 @@ export function useRevisions(
|
||||
'activity.user_agent',
|
||||
'activity.origin',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -193,7 +203,6 @@ export function useRevisions(
|
||||
|
||||
revisionsByDate.value = orderBy(revisionsGrouped, ['date'], ['desc']);
|
||||
revisions.value = orderBy(response.data.data, ['activity.timestamp'], ['desc']);
|
||||
revisionsCount.value = response.data.meta.filter_count;
|
||||
pagesCount.value = Math.ceil(revisionsCount.value / pageSize);
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
@@ -202,7 +211,63 @@ export function useRevisions(
|
||||
}
|
||||
}
|
||||
|
||||
async function getRevisionsCount() {
|
||||
if (typeof unref(primaryKey) === 'undefined') return;
|
||||
|
||||
loadingCount.value = true;
|
||||
|
||||
try {
|
||||
const filter: Filter = {
|
||||
_and: [
|
||||
{
|
||||
collection: {
|
||||
_eq: unref(collection),
|
||||
},
|
||||
},
|
||||
{
|
||||
item: {
|
||||
_eq: unref(primaryKey),
|
||||
},
|
||||
},
|
||||
{
|
||||
version: version?.value
|
||||
? {
|
||||
_eq: version.value.id,
|
||||
}
|
||||
: { _null: true },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (options?.action) {
|
||||
filter._and.push({
|
||||
activity: {
|
||||
action: {
|
||||
_eq: options?.action,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const response = await api.get(`/revisions`, {
|
||||
params: {
|
||||
filter,
|
||||
aggregate: {
|
||||
count: 'id',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
revisionsCount.value = Number(response.data.data[0].count.id);
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
} finally {
|
||||
loadingCount.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function refresh(page = 0) {
|
||||
await getRevisionsCount();
|
||||
await getRevisions(page);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-d
|
||||
import SaveOptions from '@/views/private/components/save-options.vue';
|
||||
import SharesSidebarDetail from '@/views/private/components/shares-sidebar-detail.vue';
|
||||
import { useCollection } from '@directus/composables';
|
||||
import type { PrimaryKey } from '@directus/types';
|
||||
import { useHead } from '@unhead/vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import LivePreview from '../components/live-preview.vue';
|
||||
@@ -184,11 +185,25 @@ const isFormDisabled = computed(() => {
|
||||
return true;
|
||||
});
|
||||
|
||||
const actualPrimaryKey = computed(() => {
|
||||
if (unref(isSingleton)) {
|
||||
const singleton = unref(item);
|
||||
const pkField = unref(primaryKeyField)?.field;
|
||||
return (singleton && pkField ? singleton[pkField] ?? null : null) as PrimaryKey | null;
|
||||
}
|
||||
|
||||
return props.primaryKey;
|
||||
});
|
||||
|
||||
const internalPrimaryKey = computed(() => {
|
||||
if (unref(loading)) return '+';
|
||||
if (unref(isNew)) return '+';
|
||||
|
||||
if (unref(isSingleton)) return unref(item)?.[unref(primaryKeyField)?.field] ?? '+';
|
||||
if (unref(isSingleton)) {
|
||||
const singleton = unref(item);
|
||||
const pkField = unref(primaryKeyField)?.field;
|
||||
return (singleton && pkField ? singleton[pkField] ?? '+' : '+') as PrimaryKey;
|
||||
}
|
||||
|
||||
return props.primaryKey;
|
||||
});
|
||||
@@ -706,12 +721,12 @@ function revert(values: Record<string, any>) {
|
||||
<sidebar-detail icon="info" :title="t('information')" close>
|
||||
<div v-md="t('page_help_collections_item')" class="page-description" />
|
||||
</sidebar-detail>
|
||||
<template v-if="isNew === false && loading === false && internalPrimaryKey">
|
||||
<template v-if="isNew === false && actualPrimaryKey">
|
||||
<revisions-drawer-detail
|
||||
v-if="revisionsAllowed && accountabilityScope === 'all'"
|
||||
ref="revisionsDrawerDetailRef"
|
||||
:collection="collection"
|
||||
:primary-key="internalPrimaryKey"
|
||||
:primary-key="actualPrimaryKey"
|
||||
:version="currentVersion"
|
||||
:scope="accountabilityScope"
|
||||
@revert="revert"
|
||||
@@ -719,19 +734,19 @@ function revert(values: Record<string, any>) {
|
||||
<comments-sidebar-detail
|
||||
v-if="currentVersion === null"
|
||||
:collection="collection"
|
||||
:primary-key="internalPrimaryKey"
|
||||
:primary-key="actualPrimaryKey"
|
||||
/>
|
||||
<shares-sidebar-detail
|
||||
v-if="currentVersion === null"
|
||||
:collection="collection"
|
||||
:primary-key="internalPrimaryKey"
|
||||
:primary-key="actualPrimaryKey"
|
||||
:allowed="shareAllowed"
|
||||
/>
|
||||
<flow-sidebar-detail
|
||||
v-if="currentVersion === null"
|
||||
location="item"
|
||||
:collection="collection"
|
||||
:primary-key="internalPrimaryKey"
|
||||
:primary-key="actualPrimaryKey"
|
||||
:has-edits="hasEdits"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
|
||||
@@ -3,9 +3,10 @@ import { useRevisions } from '@/composables/use-revisions';
|
||||
import { useExtensions } from '@/extensions';
|
||||
import type { FlowRaw } from '@directus/types';
|
||||
import { Action } from '@directus/constants';
|
||||
import { computed, ref, toRefs, unref, watch } from 'vue';
|
||||
import { computed, ref, toRefs, unref, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { getTriggers } from '../triggers';
|
||||
import { abbreviateNumber } from '@directus/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
flow: FlowRaw;
|
||||
@@ -24,14 +25,15 @@ const usedTrigger = computed(() => {
|
||||
|
||||
const page = ref<number>(1);
|
||||
|
||||
const { revisionsByDate, revisionsCount, loading, pagesCount, refresh } = useRevisions(
|
||||
ref('directus_flows'),
|
||||
computed(() => unref(flow).id),
|
||||
ref(null),
|
||||
{
|
||||
action: Action.RUN,
|
||||
},
|
||||
);
|
||||
const { revisionsByDate, getRevisions, revisionsCount, getRevisionsCount, loading, loadingCount, pagesCount, refresh } =
|
||||
useRevisions(
|
||||
ref('directus_flows'),
|
||||
computed(() => unref(flow).id),
|
||||
ref(null),
|
||||
{
|
||||
action: Action.RUN,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => page.value,
|
||||
@@ -40,6 +42,10 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getRevisionsCount();
|
||||
});
|
||||
|
||||
const previewing = ref();
|
||||
|
||||
const triggerData = computed(() => {
|
||||
@@ -86,10 +92,19 @@ const steps = computed(() => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
function onToggle(open: boolean) {
|
||||
if (open && revisionsByDate.value === null) getRevisions();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sidebar-detail :title="t('logs')" icon="fact_check" :badge="revisionsCount">
|
||||
<sidebar-detail
|
||||
:title="t('logs')"
|
||||
icon="fact_check"
|
||||
:badge="!loadingCount && revisionsCount > 0 ? abbreviateNumber(revisionsCount) : null"
|
||||
@toggle="onToggle"
|
||||
>
|
||||
<v-progress-linear v-if="!revisionsByDate && loading" indeterminate />
|
||||
|
||||
<div v-else-if="revisionsCount === 0" class="empty">{{ t('no_logs') }}</div>
|
||||
|
||||
@@ -3,10 +3,11 @@ import api from '@/api';
|
||||
import { Activity, ActivityByDate } from '@/types/activity';
|
||||
import { localizedFormat } from '@/utils/localized-format';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import type { User } from '@directus/types';
|
||||
import type { PrimaryKey, User } from '@directus/types';
|
||||
import { abbreviateNumber } from '@directus/utils';
|
||||
import { isThisYear, isToday, isYesterday } from 'date-fns';
|
||||
import { flatten, groupBy, orderBy } from 'lodash';
|
||||
import { ref } from 'vue';
|
||||
import { Ref, onMounted, ref, toRefs, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CommentInput from './comment-input.vue';
|
||||
import CommentItem from './comment-item.vue';
|
||||
@@ -20,24 +21,46 @@ type ActivityByDateDisplay = ActivityByDate & {
|
||||
|
||||
const props = defineProps<{
|
||||
collection: string;
|
||||
primaryKey: string | number;
|
||||
primaryKey: PrimaryKey;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { activity, loading, refresh, count, userPreviews } = useActivity(props.collection, props.primaryKey);
|
||||
const { collection, primaryKey } = toRefs(props);
|
||||
|
||||
function useActivity(collection: string, primaryKey: string | number) {
|
||||
const { activity, getActivity, loading, refresh, activityCount, getActivityCount, loadingCount, userPreviews } =
|
||||
useActivity(collection, primaryKey);
|
||||
|
||||
onMounted(() => {
|
||||
getActivityCount();
|
||||
});
|
||||
|
||||
function onToggle(open: boolean) {
|
||||
if (open && activity.value === null) getActivity();
|
||||
}
|
||||
|
||||
function useActivity(collection: Ref<string>, primaryKey: Ref<PrimaryKey>) {
|
||||
const regex = /\s@[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}/gm;
|
||||
const activity = ref<ActivityByDateDisplay[] | null>(null);
|
||||
const count = ref(0);
|
||||
const activityCount = ref(0);
|
||||
const error = ref(null);
|
||||
const loading = ref(false);
|
||||
const loadingCount = ref(false);
|
||||
const userPreviews = ref<Record<string, any>>({});
|
||||
|
||||
getActivity();
|
||||
watch([collection, primaryKey], () => refresh());
|
||||
|
||||
return { activity, error, loading, refresh, count, userPreviews };
|
||||
return {
|
||||
activity,
|
||||
getActivity,
|
||||
error,
|
||||
loading,
|
||||
refresh,
|
||||
activityCount,
|
||||
getActivityCount,
|
||||
loadingCount,
|
||||
userPreviews,
|
||||
};
|
||||
|
||||
async function getActivity() {
|
||||
error.value = null;
|
||||
@@ -46,8 +69,8 @@ function useActivity(collection: string, primaryKey: string | number) {
|
||||
try {
|
||||
const response = await api.get(`/activity`, {
|
||||
params: {
|
||||
'filter[collection][_eq]': collection,
|
||||
'filter[item][_eq]': primaryKey,
|
||||
'filter[collection][_eq]': collection.value,
|
||||
'filter[item][_eq]': primaryKey.value,
|
||||
'filter[action][_eq]': 'comment',
|
||||
sort: '-id', // directus_activity has auto increment and is therefore in chronological order
|
||||
fields: [
|
||||
@@ -65,8 +88,6 @@ function useActivity(collection: string, primaryKey: string | number) {
|
||||
},
|
||||
});
|
||||
|
||||
count.value = response.data.data.length;
|
||||
|
||||
userPreviews.value = await loadUserPreviews(response.data.data, regex);
|
||||
|
||||
const activityWithUsersInComments = (response.data.data as Activity[]).map((comment) => {
|
||||
@@ -120,7 +141,48 @@ function useActivity(collection: string, primaryKey: string | number) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getActivityCount() {
|
||||
error.value = null;
|
||||
loadingCount.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/activity`, {
|
||||
params: {
|
||||
filter: {
|
||||
_and: [
|
||||
{
|
||||
collection: {
|
||||
_eq: collection.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
item: {
|
||||
_eq: primaryKey.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: {
|
||||
_eq: 'comment',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
aggregate: {
|
||||
count: 'id',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
activityCount.value = Number(response.data.data[0].count.id);
|
||||
} catch (error: any) {
|
||||
error.value = error;
|
||||
} finally {
|
||||
loadingCount.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
await getActivityCount();
|
||||
await getActivity();
|
||||
}
|
||||
}
|
||||
@@ -158,7 +220,12 @@ async function loadUserPreviews(comments: Record<string, any>, regex: RegExp) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sidebar-detail :title="t('comments')" icon="chat_bubble_outline" :badge="count || null">
|
||||
<sidebar-detail
|
||||
:title="t('comments')"
|
||||
icon="chat_bubble_outline"
|
||||
:badge="!loadingCount && activityCount > 0 ? abbreviateNumber(activityCount) : null"
|
||||
@toggle="onToggle"
|
||||
>
|
||||
<comment-input :refresh="refresh" :collection="collection" :primary-key="primaryKey" />
|
||||
|
||||
<v-progress-linear v-if="loading" indeterminate />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useRevisions } from '@/composables/use-revisions';
|
||||
import { ContentVersion } from '@directus/types';
|
||||
import { abbreviateNumber } from '@directus/utils';
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
import { onMounted, ref, toRefs, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import RevisionsDateGroup from './revisions-date-group.vue';
|
||||
import RevisionsDrawer from './revisions-drawer.vue';
|
||||
@@ -19,16 +19,27 @@ const { t } = useI18n();
|
||||
|
||||
const { collection, primaryKey, version } = toRefs(props);
|
||||
|
||||
const { revisions, revisionsByDate, loading, refresh, revisionsCount, pagesCount, created } = useRevisions(
|
||||
collection,
|
||||
primaryKey,
|
||||
version,
|
||||
);
|
||||
|
||||
const modalActive = ref(false);
|
||||
const modalCurrentRevision = ref<number | null>(null);
|
||||
const page = ref<number>(1);
|
||||
|
||||
const {
|
||||
revisions,
|
||||
revisionsByDate,
|
||||
loading,
|
||||
refresh,
|
||||
revisionsCount,
|
||||
pagesCount,
|
||||
created,
|
||||
getRevisions,
|
||||
loadingCount,
|
||||
getRevisionsCount,
|
||||
} = useRevisions(collection, primaryKey, version);
|
||||
|
||||
onMounted(() => {
|
||||
getRevisionsCount();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => page.value,
|
||||
(newPage) => {
|
||||
@@ -41,6 +52,10 @@ function openModal(id: number) {
|
||||
modalActive.value = true;
|
||||
}
|
||||
|
||||
function onToggle(open: boolean) {
|
||||
if (open && revisions.value === null) getRevisions();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
refresh,
|
||||
});
|
||||
@@ -50,7 +65,8 @@ defineExpose({
|
||||
<sidebar-detail
|
||||
:title="t('revisions')"
|
||||
icon="change_history"
|
||||
:badge="!loading && revisionsCount > 0 ? abbreviateNumber(revisionsCount) : null"
|
||||
:badge="!loadingCount && revisionsCount > 0 ? abbreviateNumber(revisionsCount) : null"
|
||||
@toggle="onToggle"
|
||||
>
|
||||
<v-progress-linear v-if="!revisions && loading" indeterminate />
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useClipboard } from '@/composables/use-clipboard';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { Share } from '@directus/types';
|
||||
import { useClipboard } from '@/composables/use-clipboard';
|
||||
import { PrimaryKey, Share } from '@directus/types';
|
||||
import { Ref, computed, onMounted, ref, toRefs, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import api from '@/api';
|
||||
import ShareItem from './share-item.vue';
|
||||
import DrawerItem from '@/views/private/components/drawer-item.vue';
|
||||
import { abbreviateNumber } from '@directus/utils';
|
||||
import ShareItem from './share-item.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
collection: string;
|
||||
@@ -18,44 +19,224 @@ const props = defineProps<{
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { collection, primaryKey } = toRefs(props);
|
||||
|
||||
const { copyToClipboard } = useClipboard();
|
||||
|
||||
const shares = ref<Share[] | null>([]);
|
||||
const count = ref(0);
|
||||
const error = ref(null);
|
||||
const loading = ref(false);
|
||||
const deleting = ref(false);
|
||||
const shareToEdit = ref<string | null>(null);
|
||||
const shareToSend = ref<Share | null>(null);
|
||||
const shareToDelete = ref<Share | null>(null);
|
||||
const sending = ref(false);
|
||||
const sendEmails = ref('');
|
||||
const {
|
||||
shares,
|
||||
shareToEdit,
|
||||
shareToSend,
|
||||
shareToDelete,
|
||||
sharesCount,
|
||||
loading,
|
||||
loadingCount,
|
||||
sendPublicLink,
|
||||
sendEmails,
|
||||
error,
|
||||
getShares,
|
||||
getSharesCount,
|
||||
input,
|
||||
send,
|
||||
select,
|
||||
unselect,
|
||||
remove,
|
||||
sending,
|
||||
deleting,
|
||||
} = useShares(collection, primaryKey);
|
||||
|
||||
const sendPublicLink = computed(() => {
|
||||
if (!shareToSend.value) return null;
|
||||
return window.location.origin + getRootPath() + 'admin/shared/' + shareToSend.value.id;
|
||||
onMounted(() => {
|
||||
getSharesCount();
|
||||
});
|
||||
|
||||
refresh();
|
||||
function onToggle(open: boolean) {
|
||||
if (open && shares.value === null) getShares();
|
||||
}
|
||||
|
||||
async function input(data: any) {
|
||||
if (!data) return;
|
||||
function useShares(collection: Ref<string>, primaryKey: Ref<PrimaryKey>) {
|
||||
const shares = ref<Share[] | null>(null);
|
||||
const sharesCount = ref(0);
|
||||
const error = ref(null);
|
||||
const loading = ref(false);
|
||||
const loadingCount = ref(false);
|
||||
const deleting = ref(false);
|
||||
const shareToEdit = ref<string | null>(null);
|
||||
const shareToSend = ref<Share | null>(null);
|
||||
const shareToDelete = ref<Share | null>(null);
|
||||
const sending = ref(false);
|
||||
const sendEmails = ref('');
|
||||
|
||||
data.collection = props.collection;
|
||||
data.item = props.primaryKey;
|
||||
watch([collection, primaryKey], () => refresh());
|
||||
|
||||
try {
|
||||
if (shareToEdit.value === '+') {
|
||||
await api.post('/shares', data);
|
||||
} else {
|
||||
await api.patch(`/shares/${shareToEdit.value}`, data);
|
||||
const sendPublicLink = computed(() => {
|
||||
if (!shareToSend.value) return null;
|
||||
return window.location.origin + getRootPath() + 'admin/shared/' + shareToSend.value.id;
|
||||
});
|
||||
|
||||
return {
|
||||
shares,
|
||||
shareToEdit,
|
||||
shareToSend,
|
||||
shareToDelete,
|
||||
sharesCount,
|
||||
loading,
|
||||
loadingCount,
|
||||
sendPublicLink,
|
||||
sendEmails,
|
||||
error,
|
||||
getShares,
|
||||
getSharesCount,
|
||||
input,
|
||||
send,
|
||||
select,
|
||||
unselect,
|
||||
remove,
|
||||
sending,
|
||||
deleting,
|
||||
};
|
||||
|
||||
async function input(data: any) {
|
||||
if (!data) return;
|
||||
|
||||
data.collection = collection.value;
|
||||
data.item = primaryKey.value;
|
||||
|
||||
try {
|
||||
if (shareToEdit.value === '+') {
|
||||
await api.post('/shares', data);
|
||||
} else {
|
||||
await api.patch(`/shares/${shareToEdit.value}`, data);
|
||||
}
|
||||
|
||||
await refresh();
|
||||
|
||||
shareToEdit.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
}
|
||||
}
|
||||
|
||||
await refresh();
|
||||
function select(id: string) {
|
||||
shareToEdit.value = id;
|
||||
}
|
||||
|
||||
function unselect() {
|
||||
shareToEdit.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
await getSharesCount();
|
||||
await getShares();
|
||||
}
|
||||
|
||||
async function getShares() {
|
||||
error.value = null;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/shares`, {
|
||||
params: {
|
||||
filter: {
|
||||
_and: [
|
||||
{
|
||||
collection: {
|
||||
_eq: collection.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
item: {
|
||||
_eq: primaryKey.value,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
sort: 'name',
|
||||
},
|
||||
});
|
||||
|
||||
shares.value = response.data.data;
|
||||
} catch (error: any) {
|
||||
error.value = error;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getSharesCount() {
|
||||
error.value = null;
|
||||
loadingCount.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/shares`, {
|
||||
params: {
|
||||
filter: {
|
||||
_and: [
|
||||
{
|
||||
collection: {
|
||||
_eq: collection.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
item: {
|
||||
_eq: primaryKey.value,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
aggregate: {
|
||||
count: 'id',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
sharesCount.value = Number(response.data.data[0].count.id);
|
||||
} catch (error: any) {
|
||||
error.value = error;
|
||||
} finally {
|
||||
loadingCount.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
if (!shareToDelete.value) return;
|
||||
|
||||
deleting.value = true;
|
||||
|
||||
try {
|
||||
await api.delete(`/shares/${shareToDelete.value.id}`);
|
||||
await refresh();
|
||||
shareToDelete.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function send() {
|
||||
if (!shareToSend.value) return;
|
||||
|
||||
sending.value = true;
|
||||
|
||||
try {
|
||||
const emailsParsed = sendEmails.value
|
||||
.split(/,|\n/)
|
||||
.filter((e) => e)
|
||||
.map((email) => email.trim());
|
||||
|
||||
await api.post('/shares/invite', {
|
||||
emails: emailsParsed,
|
||||
share: shareToSend.value.id,
|
||||
});
|
||||
|
||||
sendEmails.value = '';
|
||||
|
||||
shareToSend.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
} finally {
|
||||
sending.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,94 +244,15 @@ async function copy(id: string) {
|
||||
const url = window.location.origin + getRootPath() + 'admin/shared/' + id;
|
||||
await copyToClipboard(url, { success: t('share_copy_link_success'), fail: t('share_copy_link_error') });
|
||||
}
|
||||
|
||||
function select(id: string) {
|
||||
shareToEdit.value = id;
|
||||
}
|
||||
|
||||
function unselect() {
|
||||
shareToEdit.value = null;
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
error.value = null;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/shares`, {
|
||||
params: {
|
||||
filter: {
|
||||
_and: [
|
||||
{
|
||||
collection: {
|
||||
_eq: props.collection,
|
||||
},
|
||||
},
|
||||
{
|
||||
item: {
|
||||
_eq: props.primaryKey,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
sort: 'name',
|
||||
},
|
||||
});
|
||||
|
||||
count.value = response.data.data.length;
|
||||
shares.value = response.data.data;
|
||||
} catch (error: any) {
|
||||
error.value = error;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function remove() {
|
||||
if (!shareToDelete.value) return;
|
||||
|
||||
deleting.value = true;
|
||||
|
||||
try {
|
||||
await api.delete(`/shares/${shareToDelete.value.id}`);
|
||||
await refresh();
|
||||
shareToDelete.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function send() {
|
||||
if (!shareToSend.value) return;
|
||||
|
||||
sending.value = true;
|
||||
|
||||
try {
|
||||
const emailsParsed = sendEmails.value
|
||||
.split(/,|\n/)
|
||||
.filter((e) => e)
|
||||
.map((email) => email.trim());
|
||||
|
||||
await api.post('/shares/invite', {
|
||||
emails: emailsParsed,
|
||||
share: shareToSend.value.id,
|
||||
});
|
||||
|
||||
sendEmails.value = '';
|
||||
|
||||
shareToSend.value = null;
|
||||
} catch (error) {
|
||||
unexpectedError(error);
|
||||
} finally {
|
||||
sending.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<sidebar-detail :title="t('shares')" icon="share" :badge="count">
|
||||
<sidebar-detail
|
||||
:title="t('shares')"
|
||||
icon="share"
|
||||
:badge="!loadingCount && sharesCount > 0 ? abbreviateNumber(sharesCount) : null"
|
||||
@toggle="onToggle"
|
||||
>
|
||||
<v-notice v-if="error" type="danger">{{ t('unexpected_error') }}</v-notice>
|
||||
<v-progress-linear v-else-if="loading" indeterminate />
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ const props = defineProps<{
|
||||
close?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggle: [open: boolean];
|
||||
}>();
|
||||
|
||||
const { active, toggle } = useGroupable({
|
||||
value: props.title,
|
||||
group: 'sidebar-detail',
|
||||
@@ -17,11 +21,16 @@ const { active, toggle } = useGroupable({
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { sidebarOpen } = toRefs(appStore);
|
||||
|
||||
function onClick() {
|
||||
emit('toggle', !active.value);
|
||||
toggle();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-detail" :class="{ open: sidebarOpen }">
|
||||
<button v-tooltip.left="!sidebarOpen && title" class="toggle" :class="{ open: active }" @click="toggle">
|
||||
<button v-tooltip.left="!sidebarOpen && title" class="toggle" :class="{ open: active }" @click="onClick">
|
||||
<div class="icon">
|
||||
<v-badge :dot="badge === true" bordered :value="badge" :disabled="!badge">
|
||||
<v-icon :name="icon" />
|
||||
|
||||
Reference in New Issue
Block a user