Rename activity->notifications module (#9446)

This commit is contained in:
Rijk van Zanten
2021-11-03 19:09:26 -04:00
committed by GitHub
parent 69d893123e
commit 428e5d4ea9
7 changed files with 35 additions and 35 deletions

View File

@@ -1,132 +0,0 @@
<template>
<v-list nav>
<v-list-item clickable :active="!filterField" @click="clearNavFilter">
<v-list-item-icon>
<v-icon name="access_time" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('all_activity')" />
</v-list-item-content>
</v-list-item>
<v-list-item
clickable
:active="filterField === 'user' && filterValue === currentUserID"
@click="setNavFilter('user', currentUserID)"
>
<v-list-item-icon>
<v-icon name="face" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('my_activity')" />
</v-list-item-content>
</v-list-item>
<v-divider />
<v-list-item
clickable
:active="filterField === 'action' && filterValue === 'create'"
@click="setNavFilter('action', 'create')"
>
<v-list-item-icon>
<v-icon name="add" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('create')" />
</v-list-item-content>
</v-list-item>
<v-list-item
clickable
:active="filterField === 'action' && filterValue === 'update'"
@click="setNavFilter('action', 'update')"
>
<v-list-item-icon>
<v-icon name="check" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('update')" />
</v-list-item-content>
</v-list-item>
<v-list-item
clickable
:active="filterField === 'action' && filterValue === 'delete'"
@click="setNavFilter('action', 'delete')"
>
<v-list-item-icon>
<v-icon name="clear" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('delete_label')" />
</v-list-item-content>
</v-list-item>
<v-list-item
clickable
:active="filterField === 'action' && filterValue === 'comment'"
@click="setNavFilter('action', 'comment')"
>
<v-list-item-icon>
<v-icon name="chat_bubble_outline" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('comment')" />
</v-list-item-content>
</v-list-item>
<v-list-item
clickable
:active="filterField === 'action' && filterValue === 'login'"
@click="setNavFilter('action', 'login')"
>
<v-list-item-icon>
<v-icon name="login" />
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow :text="t('login')" />
</v-list-item-content>
</v-list-item>
</v-list>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed, PropType } from 'vue';
import { useUserStore } from '@/stores/user';
import { Filter } from '@directus/shared/types';
export default defineComponent({
props: {
filter: {
type: Object as PropType<Filter>,
default: null,
},
},
emits: ['update:filter'],
setup(props, { emit }) {
const { t } = useI18n();
const userStore = useUserStore();
const currentUserID = computed(() => userStore.currentUser?.id);
const filterField = computed(() => Object.keys(props.filter ?? {})[0] ?? null);
const filterValue = computed(() => Object.values(props.filter ?? {})[0]?._eq ?? null);
return { t, currentUserID, setNavFilter, clearNavFilter, filterField, filterValue };
function setNavFilter(key: string, value: any) {
emit('update:filter', {
[key]: {
_eq: value,
},
});
}
function clearNavFilter() {
emit('update:filter', null);
}
},
});
</script>

View File

@@ -1,27 +0,0 @@
import { defineModule } from '@directus/shared/utils';
import ActivityCollection from './routes/collection.vue';
import ActivityItem from './routes/item.vue';
export default defineModule({
id: 'activity',
hidden: true,
name: '$t:activity',
icon: 'notifications',
routes: [
{
name: 'activity-collection',
path: '',
component: ActivityCollection,
props: true,
children: [
{
name: 'activity-item',
path: ':primaryKey',
components: {
detail: ActivityItem,
},
},
],
},
],
});

View File

@@ -1,128 +0,0 @@
<template>
<component
:is="layoutWrapper"
v-slot="{ layoutState }"
v-model:layout-options="layoutOptions"
v-model:layout-query="layoutQuery"
:filter="mergeFilters(filter, roleFilter)"
:filter-user="filter"
:filter-system="roleFilter"
:search="search"
collection="directus_activity"
>
<private-view :title="t('activity_feed')">
<template #title-outer:prepend>
<v-button class="header-icon" rounded disabled icon secondary>
<v-icon name="access_time" />
</v-button>
</template>
<template #actions:prepend>
<component :is="`layout-actions-${layout}`" v-bind="layoutState" />
</template>
<template #actions>
<search-input v-model="search" v-model:filter="filter" collection="directus_activity" />
</template>
<template #navigation>
<activity-navigation v-model:filter="roleFilter" />
</template>
<component :is="`layout-${layout}`" v-bind="layoutState" class="layout">
<template #no-results>
<v-info :title="t('no_results')" icon="search" center>
{{ t('no_results_copy') }}
</v-info>
</template>
<template #no-items>
<v-info :title="t('item_count', 0)" icon="access_time" center>
{{ t('no_items_copy') }}
</v-info>
</template>
</component>
<router-view name="detail" :primary-key="primaryKey" />
<template #sidebar>
<sidebar-detail icon="info_outline" :title="t('information')" close>
<div v-md="t('page_help_activity_collection')" class="page-description" />
</sidebar-detail>
<layout-sidebar-detail v-model="layout">
<component :is="`layout-options-${layout}`" v-bind="layoutState" />
</layout-sidebar-detail>
<component :is="`layout-sidebar-${layout}`" v-bind="layoutState" />
</template>
</private-view>
</component>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed, ref } from 'vue';
import ActivityNavigation from '../components/navigation.vue';
import usePreset from '@/composables/use-preset';
import { useLayout } from '@/composables/use-layout';
import LayoutSidebarDetail from '@/views/private/components/layout-sidebar-detail';
import SearchInput from '@/views/private/components/search-input';
import { Filter } from '@directus/shared/types';
import { mergeFilters } from '@directus/shared/utils';
export default defineComponent({
name: 'ActivityCollection',
components: { ActivityNavigation, LayoutSidebarDetail, SearchInput },
props: {
primaryKey: {
type: String,
default: null,
},
},
setup() {
const { t } = useI18n();
const { layout, layoutOptions, layoutQuery, filter, search } = usePreset(ref('directus_activity'));
const { breadcrumb } = useBreadcrumb();
const { layoutWrapper } = useLayout(layout);
const roleFilter = ref<Filter | null>(null);
return {
t,
breadcrumb,
layout,
layoutWrapper,
layoutOptions,
layoutQuery,
search,
filter,
roleFilter,
mergeFilters,
};
function useBreadcrumb() {
const breadcrumb = computed(() => {
return [
{
name: t('collection', 2),
to: `/content`,
},
];
});
return { breadcrumb };
}
},
});
</script>
<style lang="scss" scoped>
.content {
padding: var(--content-padding);
}
.header-icon {
--v-button-color-disabled: var(--foreground-normal);
}
</style>

View File

@@ -1,151 +0,0 @@
<template>
<v-drawer :model-value="isOpen" :title="t('activity_item')" @update:model-value="close" @cancel="close">
<v-progress-circular v-if="loading" indeterminate />
<div v-else-if="error" class="content">
<v-notice type="danger">
{{ error }}
</v-notice>
</div>
<div v-else class="content">
<!-- @TODO add final design -->
<p class="type-label">{{ t('user') }}:</p>
<user-popover v-if="item.user" :user="item.user.id">
{{ userName(item.user) }}
</user-popover>
<p class="type-label">{{ t('action') }}:</p>
<p>{{ item.action }}</p>
<p class="type-label">{{ t('date') }}:</p>
<p>{{ item.timestamp }}</p>
<p class="type-label">{{ t('ip_address') }}:</p>
<p>{{ item.ip }}</p>
<p class="type-label">{{ t('user_agent') }}:</p>
<p>{{ item.user_agent }}</p>
<p class="type-label">{{ t('collection') }}:</p>
<p>{{ item.collection }}</p>
<p class="type-label">{{ t('item') }}:</p>
<p>{{ item.item }}</p>
</div>
<template #actions>
<v-button v-if="openItemLink" v-tooltip.bottom="t('open')" :to="openItemLink" icon rounded>
<v-icon name="launch" />
</v-button>
<v-button v-tooltip.bottom="t('done')" to="/activity" icon rounded>
<v-icon name="check" />
</v-button>
</template>
</v-drawer>
</template>
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { i18n } from '@/lang';
import { defineComponent, computed, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import api from '@/api';
import { userName } from '@/utils/user-name';
import { useDialogRoute } from '@/composables/use-dialog-route';
type ActivityRecord = {
user: {
email: string;
first_name: string;
last_name: string;
} | null;
action: string;
timestamp: string;
ip: string;
user_agent: string;
collection: string;
item: string;
};
export default defineComponent({
name: 'ActivityDetail',
props: {
primaryKey: {
type: String,
required: true,
},
},
setup(props) {
const { t, te } = useI18n();
const router = useRouter();
const isOpen = useDialogRoute();
const item = ref<ActivityRecord>();
const loading = ref(false);
const error = ref<any>(null);
const openItemLink = computed(() => {
if (!item.value || item.value.collection.startsWith('directus_')) return;
return `/content/${item.value.collection}/${encodeURIComponent(item.value.item)}`;
});
watch(() => props.primaryKey, loadActivity, { immediate: true });
return { t, isOpen, item, loading, error, close, openItemLink, userName };
async function loadActivity() {
loading.value = true;
try {
const response = await api.get(`/activity/${props.primaryKey}`, {
params: {
fields: [
'user.id',
'user.email',
'user.first_name',
'user.last_name',
'action',
'timestamp',
'ip',
'user_agent',
'collection',
'item',
],
},
});
item.value = response.data.data;
if (item.value) {
if (te(`field_options.directus_activity.${item.value.action}`))
item.value.action = t(`field_options.directus_activity.${item.value.action}`);
item.value.timestamp = new Date(item.value.timestamp).toLocaleString(i18n.global.locale.value);
}
} catch (err: any) {
error.value = err;
} finally {
loading.value = false;
}
}
function close() {
router.push('/activity');
}
},
});
</script>
<style lang="scss" scoped>
.type-label:not(:first-child) {
margin-top: 24px;
}
.content {
padding: var(--content-padding);
padding-top: 0;
padding-bottom: var(--content-padding);
}
</style>