mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 03:57:52 +00:00
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:
@@ -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',
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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": {
|
||||||
@@ -50,22 +52,22 @@
|
|||||||
"ADD": "%{agentName} created a new team (#%{id})",
|
"ADD": "%{agentName} created a new team (#%{id})",
|
||||||
"EDIT": "%{agentName} updated a team (#%{id})",
|
"EDIT": "%{agentName} updated a team (#%{id})",
|
||||||
"DELETE": "%{agentName} deleted a team (#%{id})"
|
"DELETE": "%{agentName} deleted a team (#%{id})"
|
||||||
},
|
},
|
||||||
"MACRO": {
|
"MACRO": {
|
||||||
"ADD": "%{agentName} created a new macro (#%{id})",
|
"ADD": "%{agentName} created a new macro (#%{id})",
|
||||||
"EDIT": "%{agentName} updated a macro (#%{id})",
|
"EDIT": "%{agentName} updated a macro (#%{id})",
|
||||||
"DELETE": "%{agentName} deleted a macro (#%{id})"
|
"DELETE": "%{agentName} deleted a macro (#%{id})"
|
||||||
},
|
},
|
||||||
"INBOX_MEMBER": {
|
"INBOX_MEMBER": {
|
||||||
"ADD": "%{agentName} added %{user} to the inbox(#%{inbox_id})",
|
"ADD": "%{agentName} added %{user} to the inbox(#%{inbox_id})",
|
||||||
"REMOVE": "%{agentName} removed %{user} from the inbox(#%{inbox_id})"
|
"REMOVE": "%{agentName} removed %{user} from the inbox(#%{inbox_id})"
|
||||||
},
|
},
|
||||||
"TEAM_MEMBER": {
|
"TEAM_MEMBER": {
|
||||||
"ADD": "%{agentName} added %{user} to the team(#%{team_id})",
|
"ADD": "%{agentName} added %{user} to the team(#%{team_id})",
|
||||||
"REMOVE": "%{agentName} removed %{user} from the team(#%{team_id})"
|
"REMOVE": "%{agentName} removed %{user} from the team(#%{team_id})"
|
||||||
},
|
},
|
||||||
"ACCOUNT": {
|
"ACCOUNT": {
|
||||||
"EDIT": "%{agentName} updated the account configuration (#%{id})"
|
"EDIT": "%{agentName} updated the account configuration (#%{id})"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: {},
|
|
||||||
auditLogsAPI: {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
generateLogText(auditLogItem) {
|
|
||||||
const translationPayload = generateTranslationPayload(
|
|
||||||
auditLogItem,
|
|
||||||
this.agentList
|
|
||||||
);
|
|
||||||
const translationKey = generateLogActionKey(auditLogItem);
|
|
||||||
|
|
||||||
return this.$t(translationKey, translationPayload);
|
const { t } = useI18n();
|
||||||
},
|
const route = useRoute();
|
||||||
onPageChange(page) {
|
|
||||||
window.history.pushState({}, null, `${this.$route.path}?page=${page}`);
|
const routerPage = computed(() => Number(route.query.page ?? 1));
|
||||||
try {
|
|
||||||
this.$store.dispatch('auditlogs/fetch', { page });
|
const fetchAuditLogs = page => {
|
||||||
} catch (error) {
|
try {
|
||||||
const errorMessage =
|
store.dispatch('auditlogs/fetch', { page });
|
||||||
error?.message || this.$t('AUDIT_LOGS.API.ERROR_MESSAGE');
|
} catch (error) {
|
||||||
useAlert(errorMessage);
|
const errorMessage = error?.message || t('AUDIT_LOGS.API.ERROR_MESSAGE');
|
||||||
}
|
useAlert(errorMessage);
|
||||||
},
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateLogText = auditLogItem => {
|
||||||
|
const translationPayload = generateTranslationPayload(
|
||||||
|
auditLogItem,
|
||||||
|
agentList.value
|
||||||
|
);
|
||||||
|
const translationKey = generateLogActionKey(auditLogItem);
|
||||||
|
|
||||||
|
return t(translationKey, translationPayload);
|
||||||
|
};
|
||||||
|
|
||||||
|
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') }}
|
|
||||||
</p>
|
|
||||||
<woot-loading-state
|
|
||||||
v-if="uiFlags.fetchingList"
|
|
||||||
:message="$t('AUDIT_LOGS.LOADING')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<table
|
<div class="mt-6 flex-1 text-slate-700 dark:text-slate-300">
|
||||||
v-if="!uiFlags.fetchingList && records.length"
|
<woot-loading-state
|
||||||
class="w-full woot-table"
|
v-if="uiFlags.fetchingList"
|
||||||
>
|
:message="$t('AUDIT_LOGS.LOADING')"
|
||||||
<colgroup>
|
/>
|
||||||
<col class="w-3/5" />
|
<p
|
||||||
<col />
|
v-else-if="!records.length"
|
||||||
<col />
|
class="flex flex-col items-center justify-center h-full text-base p-8"
|
||||||
</colgroup>
|
>
|
||||||
|
{{ $t('AUDIT_LOGS.LIST.404') }}
|
||||||
|
</p>
|
||||||
|
<div v-else class="min-w-full overflow-x-auto">
|
||||||
|
<table class="divide-y divide-slate-75 dark:divide-slate-700">
|
||||||
<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>
|
||||||
|
<TableFooter
|
||||||
|
:current-page="Number(meta.currentPage)"
|
||||||
|
:total-count="meta.totalEntries"
|
||||||
|
:page-size="meta.perPage"
|
||||||
|
class="border-slate-50 dark:border-slate-800 border-t !px-0 py-4"
|
||||||
|
@pageChange="onPageChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TableFooter
|
|
||||||
:current-page="Number(meta.currentPage)"
|
|
||||||
:total-count="meta.totalEntries"
|
|
||||||
:page-size="meta.perPage"
|
|
||||||
class="!bg-slate-25 dark:!bg-slate-900 border-t border-slate-75 dark:border-slate-700/50"
|
|
||||||
@pageChange="onPageChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -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: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user