mirror of
https://github.com/directus/directus.git
synced 2026-01-23 19:18:07 -05:00
@@ -437,6 +437,7 @@ system_collections: System Collections
|
||||
placeholder: Placeholder
|
||||
icon_left: Icon Left
|
||||
icon_right: Icon Right
|
||||
count_other_revisions: '{count} Other Revisions'
|
||||
font: Font
|
||||
sans_serif: Sans Serif
|
||||
serif: Serif
|
||||
|
||||
23
app/src/utils/abbreviate-number.ts
Normal file
23
app/src/utils/abbreviate-number.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export function abbreviateNumber(value: number) {
|
||||
if (value >= 1000) {
|
||||
const suffixes = ['', 'K', 'M', 'B', 'T'];
|
||||
const suffixNum = Math.floor(('' + value).length / 3);
|
||||
let shortValue: number = value;
|
||||
|
||||
for (let precision = 2; precision >= 1; precision--) {
|
||||
shortValue = parseFloat((suffixNum != 0 ? value / Math.pow(1000, suffixNum) : value).toPrecision(precision));
|
||||
const dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g, '');
|
||||
if (dotLessShortValue.length <= 2) break;
|
||||
}
|
||||
|
||||
let valueAsString: string = String(shortValue);
|
||||
|
||||
if (shortValue % 1 != 0) {
|
||||
valueAsString = shortValue.toFixed(1);
|
||||
}
|
||||
|
||||
return shortValue + suffixes[suffixNum];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -2,28 +2,39 @@
|
||||
<sidebar-detail
|
||||
:title="$t('revisions')"
|
||||
icon="change_history"
|
||||
:badge="!loading && revisions ? revisions.length : null"
|
||||
:badge="!loading && revisions ? abbreviateNumber(revisionsCount) : null"
|
||||
>
|
||||
<v-progress-linear indeterminate v-if="loading" />
|
||||
|
||||
<template v-else v-for="group in revisionsByDate">
|
||||
<v-divider :key="group.date.toString()">{{ group.dateFormatted }}</v-divider>
|
||||
<template v-else>
|
||||
<template v-for="group in revisionsByDate">
|
||||
<v-divider :key="group.date.toString()">{{ group.dateFormatted }}</v-divider>
|
||||
|
||||
<template v-for="(item, index) in group.revisions">
|
||||
<revision-item
|
||||
:key="item.id"
|
||||
:revision="item"
|
||||
:last="index === group.revisions.length - 1"
|
||||
@click="openModal(item.id)"
|
||||
/>
|
||||
<template v-for="(item, index) in group.revisions">
|
||||
<revision-item
|
||||
:key="item.id"
|
||||
:revision="item"
|
||||
:last="index === group.revisions.length - 1"
|
||||
@click="openModal(item.id)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-if="loading === false && hasCreate === false">
|
||||
<v-divider v-if="revisionsByDate.length > 0" />
|
||||
<div class="external">
|
||||
{{ $t('revision_delta_created_externally') }}
|
||||
</div>
|
||||
<v-divider class="other" v-if="revisionsCount > 100">
|
||||
{{ $tc('count_other_revisions', revisionsCount - 101) }}
|
||||
</v-divider>
|
||||
|
||||
<template v-if="created">
|
||||
<revision-item :revision="created" last @click="openModal(created.id)" />
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<v-divider v-if="revisionsByDate.length > 0" />
|
||||
|
||||
<div class="external">
|
||||
{{ $t('revision_delta_created_externally') }}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<revisions-drawer
|
||||
@@ -49,6 +60,7 @@ import formatLocalized from '@/utils/localized-format';
|
||||
import RevisionItem from './revision-item.vue';
|
||||
import RevisionsDrawer from './revisions-drawer.vue';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { abbreviateNumber } from '@/utils/abbreviate-number';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RevisionItem, RevisionsDrawer },
|
||||
@@ -63,16 +75,10 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { revisions, revisionsByDate, loading, refresh } = useRevisions(props.collection, props.primaryKey);
|
||||
|
||||
const hasCreate = computed(() => {
|
||||
// We expect the very first revision record to be a creation
|
||||
return (
|
||||
revisions.value &&
|
||||
revisions.value.length > 0 &&
|
||||
revisions.value[revisions.value.length - 1].activity.action === 'create'
|
||||
);
|
||||
});
|
||||
const { revisions, revisionsByDate, loading, refresh, revisionsCount, created } = useRevisions(
|
||||
props.collection,
|
||||
props.primaryKey
|
||||
);
|
||||
|
||||
const modalActive = ref(false);
|
||||
const modalCurrentRevision = ref<number | null>(null);
|
||||
@@ -82,11 +88,13 @@ export default defineComponent({
|
||||
revisionsByDate,
|
||||
loading,
|
||||
refresh,
|
||||
hasCreate,
|
||||
modalActive,
|
||||
modalCurrentRevision,
|
||||
openModal,
|
||||
onRevert,
|
||||
revisionsCount,
|
||||
created,
|
||||
abbreviateNumber,
|
||||
};
|
||||
|
||||
function openModal(id: number) {
|
||||
@@ -98,10 +106,12 @@ export default defineComponent({
|
||||
const revisions = ref<Revision[] | null>(null);
|
||||
const revisionsByDate = ref<RevisionsByDate[] | null>(null);
|
||||
const loading = ref(false);
|
||||
const revisionsCount = ref(0);
|
||||
const created = ref<Revision>();
|
||||
|
||||
getRevisions();
|
||||
|
||||
return { revisions, revisionsByDate, loading, refresh };
|
||||
return { created, revisions, revisionsByDate, loading, refresh, revisionsCount };
|
||||
|
||||
async function getRevisions() {
|
||||
loading.value = true;
|
||||
@@ -117,6 +127,8 @@ export default defineComponent({
|
||||
_eq: primaryKey,
|
||||
},
|
||||
},
|
||||
sort: '-id',
|
||||
limit: 100,
|
||||
fields: [
|
||||
'id',
|
||||
'data',
|
||||
@@ -132,9 +144,48 @@ export default defineComponent({
|
||||
'activity.ip',
|
||||
'activity.user_agent',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
});
|
||||
|
||||
const createdResponse = await api.get(`/revisions`, {
|
||||
params: {
|
||||
filter: {
|
||||
collection: {
|
||||
_eq: collection,
|
||||
},
|
||||
item: {
|
||||
_eq: primaryKey,
|
||||
},
|
||||
activity: {
|
||||
action: {
|
||||
_eq: 'create',
|
||||
},
|
||||
},
|
||||
},
|
||||
sort: '-id',
|
||||
limit: 1,
|
||||
fields: [
|
||||
'id',
|
||||
'data',
|
||||
'delta',
|
||||
'collection',
|
||||
'item',
|
||||
'activity.action',
|
||||
'activity.timestamp',
|
||||
'activity.user.id',
|
||||
'activity.user.email',
|
||||
'activity.user.first_name',
|
||||
'activity.user.last_name',
|
||||
'activity.ip',
|
||||
'activity.user_agent',
|
||||
],
|
||||
meta: ['filter_count'],
|
||||
},
|
||||
});
|
||||
|
||||
created.value = createdResponse.data.data?.[0];
|
||||
|
||||
const revisionsGroupedByDate = groupBy(response.data.data, (revision: Revision) => {
|
||||
// revision's timestamp date is in iso-8601
|
||||
const date = new Date(new Date(revision.activity.timestamp).toDateString());
|
||||
@@ -166,6 +217,7 @@ export default defineComponent({
|
||||
|
||||
revisionsByDate.value = orderBy(revisionsGrouped, ['date'], ['desc']);
|
||||
revisions.value = orderBy(response.data.data, ['activity.timestamp'], ['desc']);
|
||||
revisionsCount.value = response.data.meta.filter_count;
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
@@ -219,4 +271,10 @@ export default defineComponent({
|
||||
color: var(--foreground-subdued);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.other {
|
||||
--v-divider-label-color: var(--foreground-subdued);
|
||||
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user