feat: Update the design of the audit logs page (#9901)

This is continuation of the design update, updates the design for audit logs listing page.

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
Pranav
2024-08-06 20:58:04 -07:00
committed by GitHub
parent e393bcf125
commit aea68f1ecf
5 changed files with 118 additions and 133 deletions

View File

@@ -173,11 +173,10 @@ const settings = accountId => ({
meta: { meta: {
permissions: ['administrator'], permissions: ['administrator'],
}, },
toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`), toState: frontendURL(`accounts/${accountId}/settings/audit-logs/list`),
toStateName: 'auditlogs_list', toStateName: 'auditlogs_list',
isEnterpriseOnly: true, isEnterpriseOnly: true,
featureFlag: FEATURE_FLAGS.AUDIT_LOGS, featureFlag: FEATURE_FLAGS.AUDIT_LOGS,
beta: true,
}, },
{ {
icon: 'document-list-clock', icon: 'document-list-clock',

View File

@@ -1,18 +1,19 @@
const FEATURE_HELP_URLS = { const FEATURE_HELP_URLS = {
agent_bots: 'https://chwt.app/hc/agent-bots',
audit_logs: 'https://chwt.app/hc/audit-logs',
campaigns: 'https://chwt.app/hc/campaigns',
canned_responses: 'https://chwt.app/hc/canned',
channel_email: 'https://chwt.app/hc/email', channel_email: 'https://chwt.app/hc/email',
channel_facebook: 'https://chwt.app/hc/fb', channel_facebook: 'https://chwt.app/hc/fb',
help_center: 'https://chwt.app/hc/help-center',
agent_bots: 'https://chwt.app/hc/agent-bots',
team_management: 'https://chwt.app/hc/teams',
labels: 'https://chwt.app/hc/labels',
custom_attributes: 'https://chwt.app/hc/custom-attributes', custom_attributes: 'https://chwt.app/hc/custom-attributes',
canned_responses: 'https://chwt.app/hc/canned',
integrations: 'https://chwt.app/hc/integrations',
campaigns: 'https://chwt.app/hc/campaigns',
reports: 'https://chwt.app/hc/reports',
message_reply_to: 'https://chwt.app/hc/reply-to',
sla: 'https://chwt.app/hc/sla',
dashboard_apps: 'https://chwt.app/hc/dashboard-apps', dashboard_apps: 'https://chwt.app/hc/dashboard-apps',
help_center: 'https://chwt.app/hc/help-center',
integrations: 'https://chwt.app/hc/integrations',
labels: 'https://chwt.app/hc/labels',
message_reply_to: 'https://chwt.app/hc/reply-to',
reports: 'https://chwt.app/hc/reports',
sla: 'https://chwt.app/hc/sla',
team_management: 'https://chwt.app/hc/teams',
}; };
export function getHelpUrlForFeature(featureName) { export function getHelpUrlForFeature(featureName) {

View File

@@ -3,6 +3,8 @@
"HEADER": "Audit Logs", "HEADER": "Audit Logs",
"HEADER_BTN_TXT": "Add Audit Logs", "HEADER_BTN_TXT": "Add Audit Logs",
"LOADING": "Fetching Audit Logs", "LOADING": "Fetching Audit Logs",
"DESCRIPTION": "Audit Logs maintain a record of activities in your account, allowing you to track and audit your account, team, or services.",
"LEARN_MORE": "Learn more about audit logs",
"SEARCH_404": "There are no items matching this query", "SEARCH_404": "There are no items matching this query",
"SIDEBAR_TXT": "<p><b>Audit Logs</b> </p><p> Audit Logs are trails for events and actions in a Chatwoot System. </p>", "SIDEBAR_TXT": "<p><b>Audit Logs</b> </p><p> Audit Logs are trails for events and actions in a Chatwoot System. </p>",
"LIST": { "LIST": {
@@ -67,5 +69,5 @@
"ACCOUNT": { "ACCOUNT": {
"EDIT": "%{agentName} updated the account configuration (#%{id})" "EDIT": "%{agentName} updated the account configuration (#%{id})"
} }
} }
} }

View File

@@ -1,116 +1,104 @@
<script> <script setup>
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables'; import { useAlert } from 'dashboard/composables';
import { messageTimestamp } from 'shared/helpers/timeHelper'; import { messageTimestamp } from 'shared/helpers/timeHelper';
import { useStoreGetters, useStore } from 'dashboard/composables/store';
import TableFooter from 'dashboard/components/widgets/TableFooter.vue'; import TableFooter from 'dashboard/components/widgets/TableFooter.vue';
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
import { import {
generateTranslationPayload, generateTranslationPayload,
generateLogActionKey, generateLogActionKey,
} from 'dashboard/helper/auditlogHelper'; } from 'dashboard/helper/auditlogHelper';
import { computed, onMounted, watch } from 'vue';
import { useI18n } from 'dashboard/composables/useI18n';
import { useRoute, useRouter } from 'dashboard/composables/route';
export default { const getters = useStoreGetters();
components: { const store = useStore();
TableFooter, const router = useRouter();
}, const records = computed(() => getters['auditlogs/getAuditLogs'].value);
beforeRouteEnter(to, from, next) { const uiFlags = computed(() => getters['auditlogs/getUIFlags'].value);
// Fetch Audit Logs on page load without manual refresh const meta = computed(() => getters['auditlogs/getMeta'].value);
next(vm => { const agentList = computed(() => getters['agents/getAgents'].value);
vm.fetchAuditLogs();
}); const { t } = useI18n();
}, const route = useRoute();
data() {
return { const routerPage = computed(() => Number(route.query.page ?? 1));
loading: {},
auditLogsAPI: { const fetchAuditLogs = page => {
message: '', try {
}, store.dispatch('auditlogs/fetch', { page });
}; } catch (error) {
}, const errorMessage = error?.message || t('AUDIT_LOGS.API.ERROR_MESSAGE');
computed: {
...mapGetters({
records: 'auditlogs/getAuditLogs',
uiFlags: 'auditlogs/getUIFlags',
meta: 'auditlogs/getMeta',
agentList: 'agents/getAgents',
}),
},
mounted() {
// Fetch API Call
this.$store.dispatch('agents/get');
},
methods: {
messageTimestamp,
fetchAuditLogs() {
const page = this.$route.query.page ?? 1;
this.$store.dispatch('auditlogs/fetch', { page }).catch(error => {
const errorMessage =
error?.message || this.$t('AUDIT_LOGS.API.ERROR_MESSAGE');
useAlert(errorMessage); useAlert(errorMessage);
}); }
}, };
generateLogText(auditLogItem) {
const generateLogText = auditLogItem => {
const translationPayload = generateTranslationPayload( const translationPayload = generateTranslationPayload(
auditLogItem, auditLogItem,
this.agentList agentList.value
); );
const translationKey = generateLogActionKey(auditLogItem); const translationKey = generateLogActionKey(auditLogItem);
return this.$t(translationKey, translationPayload); return t(translationKey, translationPayload);
},
onPageChange(page) {
window.history.pushState({}, null, `${this.$route.path}?page=${page}`);
try {
this.$store.dispatch('auditlogs/fetch', { page });
} catch (error) {
const errorMessage =
error?.message || this.$t('AUDIT_LOGS.API.ERROR_MESSAGE');
useAlert(errorMessage);
}
},
},
}; };
const onPageChange = page => {
router.push({ name: 'auditlogs_list', query: { page: page } });
};
onMounted(() => {
store.dispatch('agents/get');
fetchAuditLogs(routerPage.value);
});
watch(routerPage, (newPage, oldPage) => {
if (newPage !== oldPage) {
fetchAuditLogs(newPage);
}
});
</script> </script>
<template> <template>
<div class="flex flex-col justify-between flex-1 p-4 overflow-auto"> <div class="flex-1 overflow-auto">
<!-- List Audit Logs --> <BaseSettingsHeader
<div> :title="$t('AUDIT_LOGS.HEADER')"
<div> :description="$t('AUDIT_LOGS.DESCRIPTION')"
<p :link-text="$t('AUDIT_LOGS.LEARN_MORE')"
v-if="!uiFlags.fetchingList && !records.length" feature-name="audit_logs"
class="flex flex-col items-center justify-center h-full" />
>
{{ $t('AUDIT_LOGS.LIST.404') }} <div class="mt-6 flex-1 text-slate-700 dark:text-slate-300">
</p>
<woot-loading-state <woot-loading-state
v-if="uiFlags.fetchingList" v-if="uiFlags.fetchingList"
:message="$t('AUDIT_LOGS.LOADING')" :message="$t('AUDIT_LOGS.LOADING')"
/> />
<p
<table v-else-if="!records.length"
v-if="!uiFlags.fetchingList && records.length" class="flex flex-col items-center justify-center h-full text-base p-8"
class="w-full woot-table"
> >
<colgroup> {{ $t('AUDIT_LOGS.LIST.404') }}
<col class="w-3/5" /> </p>
<col /> <div v-else class="min-w-full overflow-x-auto">
<col /> <table class="divide-y divide-slate-75 dark:divide-slate-700">
</colgroup>
<thead> <thead>
<!-- Header -->
<th <th
v-for="thHeader in $t('AUDIT_LOGS.LIST.TABLE_HEADER')" v-for="thHeader in $t('AUDIT_LOGS.LIST.TABLE_HEADER')"
:key="thHeader" :key="thHeader"
class="py-4 pr-4 text-left font-semibold text-slate-700 dark:text-slate-300"
> >
{{ thHeader }} {{ thHeader }}
</th> </th>
</thead> </thead>
<tbody> <tbody
class="divide-y divide-slate-50 dark:divide-slate-800 text-slate-700 dark:text-slate-300"
>
<tr v-for="auditLogItem in records" :key="auditLogItem.id"> <tr v-for="auditLogItem in records" :key="auditLogItem.id">
<td class="break-all whitespace-nowrap"> <td class="py-4 pr-4 break-all whitespace-nowrap">
{{ generateLogText(auditLogItem) }} {{ generateLogText(auditLogItem) }}
</td> </td>
<td class="break-all whitespace-nowrap"> <td class="py-4 pr-4 break-all whitespace-nowrap">
{{ {{
messageTimestamp( messageTimestamp(
auditLogItem.created_at, auditLogItem.created_at,
@@ -118,20 +106,20 @@ export default {
) )
}} }}
</td> </td>
<td class="w-[8.75rem]"> <td class="py-4 w-[8.75rem]">
{{ auditLogItem.remote_address }} {{ auditLogItem.remote_address }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</div>
<TableFooter <TableFooter
:current-page="Number(meta.currentPage)" :current-page="Number(meta.currentPage)"
:total-count="meta.totalEntries" :total-count="meta.totalEntries"
:page-size="meta.perPage" :page-size="meta.perPage"
class="!bg-slate-25 dark:!bg-slate-900 border-t border-slate-75 dark:border-slate-700/50" class="border-slate-50 dark:border-slate-800 border-t !px-0 py-4"
@pageChange="onPageChange" @pageChange="onPageChange"
/> />
</div> </div>
</div>
</div>
</template> </template>

View File

@@ -1,18 +1,13 @@
import { frontendURL } from '../../../../helper/URLHelper'; import { frontendURL } from '../../../../helper/URLHelper';
const SettingsContent = () => import('../Wrapper.vue'); const SettingsWrapper = () => import('../SettingsWrapper.vue');
const AuditLogsHome = () => import('./Index.vue'); const AuditLogsHome = () => import('./Index.vue');
export default { export default {
routes: [ routes: [
{ {
path: frontendURL('accounts/:accountId/settings/audit-log'), path: frontendURL('accounts/:accountId/settings/audit-logs'),
component: SettingsContent, component: SettingsWrapper,
props: {
headerTitle: 'AUDIT_LOGS.HEADER',
icon: 'key',
showNewButton: false,
},
children: [ children: [
{ {
path: '', path: '',