154 lines
4.9 KiB
Vue
154 lines
4.9 KiB
Vue
<script setup>
|
|
import { ref, computed, watch, onMounted } from 'vue';
|
|
import AppListShell from '../_shared/AppListShell.vue';
|
|
import AppListItem from '../_shared/AppListItem.vue';
|
|
|
|
const props = defineProps({
|
|
serverData: { type: Object, required: true },
|
|
});
|
|
|
|
const api = (path) =>
|
|
fetch(`${props.serverData.restUrl}aurora-admin/v1${path}`, {
|
|
headers: { 'X-WP-Nonce': props.serverData.restNonce },
|
|
}).then((r) => {
|
|
if (!r.ok) throw new Error('Request failed');
|
|
return r.json();
|
|
});
|
|
|
|
const ACTION_TYPES = [
|
|
['', 'All Actions'],
|
|
['post_created', 'Post created'],
|
|
['post_updated', 'Post updated'],
|
|
['post_deleted', 'Post deleted'],
|
|
['user_registered', 'User registered'],
|
|
['user_updated', 'User updated'],
|
|
['login', 'Login'],
|
|
['plugin_activated', 'Plugin activated'],
|
|
['plugin_deactivated', 'Plugin deactivated'],
|
|
['theme_switched', 'Theme switched'],
|
|
['settings_updated', 'Settings updated'],
|
|
];
|
|
const OBJECT_TYPES = [
|
|
['', 'All Types'],
|
|
['post', 'Post'],
|
|
['user', 'User'],
|
|
['plugin', 'Plugin'],
|
|
['theme', 'Theme'],
|
|
['settings', 'Settings'],
|
|
];
|
|
|
|
const items = ref([]);
|
|
const loading = ref(true);
|
|
const total = ref(0);
|
|
const page = ref(1);
|
|
const totalPages = ref(1);
|
|
const actionFilter = ref('');
|
|
const objectFilter = ref('');
|
|
const search = ref('');
|
|
const selectedId = ref(null);
|
|
|
|
const load = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (actionFilter.value) params.set('action_type', actionFilter.value);
|
|
if (objectFilter.value) params.set('object_type', objectFilter.value);
|
|
if (search.value.trim()) params.set('search', search.value.trim());
|
|
params.set('page', page.value);
|
|
const data = await api(`/activity-log?${params.toString()}`);
|
|
items.value = data.items;
|
|
total.value = data.total;
|
|
totalPages.value = data.totalPages;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
onMounted(load);
|
|
watch([actionFilter, objectFilter, search], () => {
|
|
page.value = 1;
|
|
load();
|
|
});
|
|
watch(page, load);
|
|
|
|
const selected = computed(() => items.value.find((i) => i.id === selectedId.value) || null);
|
|
|
|
const formatDate = (s) => (s ? new Date(s.replace(' ', 'T')).toLocaleString() : '');
|
|
</script>
|
|
|
|
<template>
|
|
<AppListShell
|
|
v-model:search="search"
|
|
title="Activity Log"
|
|
search-placeholder="Search activities…"
|
|
:count-label="`${total} ITEMS`"
|
|
:page="page"
|
|
:has-prev="page > 1"
|
|
:has-next="page < totalPages"
|
|
detail-icon="dashicons-clock"
|
|
detail-title="Activity Details"
|
|
detail-text="Select an activity from the list to view detailed information."
|
|
:has-selection="!!selected"
|
|
@prev-page="page--"
|
|
@next-page="page++"
|
|
>
|
|
<template #header-actions>
|
|
<button type="button" @click="load">
|
|
<span class="dashicons dashicons-update" />
|
|
</button>
|
|
</template>
|
|
|
|
<template #filters>
|
|
<select v-model="actionFilter" class="activity-log__select">
|
|
<option v-for="[v, l] in ACTION_TYPES" :key="v" :value="v">{{ l }}</option>
|
|
</select>
|
|
<select v-model="objectFilter" class="activity-log__select">
|
|
<option v-for="[v, l] in OBJECT_TYPES" :key="v" :value="v">{{ l }}</option>
|
|
</select>
|
|
</template>
|
|
|
|
<template #list>
|
|
<p v-if="!loading && !items.length" style="font-size: 0.8125rem; color: var(--aurora-text-muted); padding: 20px 4px; text-align: center;">
|
|
No activities found
|
|
</p>
|
|
<AppListItem
|
|
v-for="i in items"
|
|
:key="i.id"
|
|
icon="dashicons-clock"
|
|
:title="i.objectLabel || i.actionType"
|
|
:subtitle="`${i.userName || 'System'} · ${formatDate(i.createdAt)}`"
|
|
:active="i.id === selectedId"
|
|
@click="selectedId = i.id"
|
|
/>
|
|
</template>
|
|
|
|
<template #detail>
|
|
<div v-if="selected" class="activity-detail">
|
|
<h2>{{ selected.objectLabel || selected.actionType }}</h2>
|
|
<dl>
|
|
<dt>Action</dt><dd>{{ selected.actionType }}</dd>
|
|
<dt>Object type</dt><dd>{{ selected.objectType }}</dd>
|
|
<dt>User</dt><dd>{{ selected.userName || 'System' }}</dd>
|
|
<dt>Date</dt><dd>{{ formatDate(selected.createdAt) }}</dd>
|
|
</dl>
|
|
<pre v-if="selected.details">{{ JSON.stringify(selected.details, null, 2) }}</pre>
|
|
</div>
|
|
</template>
|
|
</AppListShell>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.activity-log__select {
|
|
padding: 5px 10px; border-radius: 8px; border: 1px solid var(--aurora-border);
|
|
background: var(--aurora-bg-subtle); color: var(--aurora-text); font-size: 0.75rem;
|
|
}
|
|
.activity-detail h2 { margin: 0 0 16px; font-size: 1.1rem; color: var(--aurora-text); }
|
|
.activity-detail dl { display: grid; grid-template-columns: 120px 1fr; gap: 8px 12px; font-size: 0.8125rem; margin: 0; }
|
|
.activity-detail dt { color: var(--aurora-text-muted); }
|
|
.activity-detail dd { margin: 0; color: var(--aurora-text); }
|
|
.activity-detail pre {
|
|
margin-top: 18px; padding: 12px; background: var(--aurora-bg-subtle);
|
|
border-radius: 8px; font-size: 0.75rem; overflow-x: auto; color: var(--aurora-text);
|
|
}
|
|
</style>
|