mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 03:57:52 +00:00
feat: Update the design for macros design page (#9999)
This is the continuation of the design update for settings page. This PR updates the design for the macros page. Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
@@ -1,117 +1,112 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
<script setup>
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import MacrosTableRow from './MacrosTableRow.vue';
|
||||
export default {
|
||||
components: {
|
||||
MacrosTableRow,
|
||||
},
|
||||
setup() {
|
||||
const { accountScopedUrl } = useAccount();
|
||||
return {
|
||||
accountScopedUrl,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDeleteConfirmationPopup: false,
|
||||
selectedResponse: {},
|
||||
loading: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
records: ['macros/getMacros'],
|
||||
uiFlags: 'macros/getUIFlags',
|
||||
}),
|
||||
deleteMessage() {
|
||||
return ` ${this.selectedResponse.name}?`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('macros/get');
|
||||
},
|
||||
methods: {
|
||||
openDeletePopup(response) {
|
||||
this.showDeleteConfirmationPopup = true;
|
||||
this.selectedResponse = response;
|
||||
},
|
||||
closeDeletePopup() {
|
||||
this.showDeleteConfirmationPopup = false;
|
||||
},
|
||||
confirmDeletion() {
|
||||
this.loading[this.selectedResponse.id] = true;
|
||||
this.closeDeletePopup();
|
||||
this.deleteMacro(this.selectedResponse.id);
|
||||
},
|
||||
async deleteMacro(id) {
|
||||
try {
|
||||
await this.$store.dispatch('macros/delete', id);
|
||||
useAlert(this.$t('MACROS.DELETE.API.SUCCESS_MESSAGE'));
|
||||
this.loading[this.selectedResponse.id] = false;
|
||||
} catch (error) {
|
||||
useAlert(this.$t('MACROS.DELETE.API.ERROR_MESSAGE'));
|
||||
}
|
||||
},
|
||||
},
|
||||
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
|
||||
import SettingsLayout from '../SettingsLayout.vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const showDeleteConfirmationPopup = ref(false);
|
||||
const selectedMacro = ref({});
|
||||
|
||||
const records = computed(() => getters['macros/getMacros'].value);
|
||||
const uiFlags = computed(() => getters['macros/getUIFlags'].value);
|
||||
|
||||
const deleteMessage = computed(() => ` ${selectedMacro.value.name}?`);
|
||||
|
||||
onMounted(() => {
|
||||
store.dispatch('macros/get');
|
||||
});
|
||||
|
||||
const deleteMacro = async id => {
|
||||
try {
|
||||
await store.dispatch('macros/delete', id);
|
||||
useAlert(t('MACROS.DELETE.API.SUCCESS_MESSAGE'));
|
||||
} catch (error) {
|
||||
useAlert(t('MACROS.DELETE.API.ERROR_MESSAGE'));
|
||||
}
|
||||
};
|
||||
|
||||
const openDeletePopup = response => {
|
||||
showDeleteConfirmationPopup.value = true;
|
||||
selectedMacro.value = response;
|
||||
};
|
||||
|
||||
const closeDeletePopup = () => {
|
||||
showDeleteConfirmationPopup.value = false;
|
||||
};
|
||||
|
||||
const confirmDeletion = () => {
|
||||
closeDeletePopup();
|
||||
deleteMacro(selectedMacro.value.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-1 overflow-auto">
|
||||
<router-link
|
||||
:to="accountScopedUrl('settings/macros/new')"
|
||||
class="button success button--fixed-top button success button--fixed-top px-3.5 py-1 rounded-[5px] flex gap-2"
|
||||
>
|
||||
<fluent-icon icon="add-circle" />
|
||||
<span class="button__content">
|
||||
{{ $t('MACROS.HEADER_BTN_TXT') }}
|
||||
</span>
|
||||
</router-link>
|
||||
<div class="flex flex-row gap-4 p-8">
|
||||
<div class="w-full lg:w-3/5">
|
||||
<div v-if="!uiFlags.isFetching && !records.length" class="p-3">
|
||||
<p class="flex flex-col items-center justify-center h-full">
|
||||
{{ $t('MACROS.LIST.404') }}
|
||||
</p>
|
||||
</div>
|
||||
<woot-loading-state
|
||||
v-if="uiFlags.isFetching"
|
||||
:message="$t('MACROS.LOADING')"
|
||||
/>
|
||||
<table v-if="!uiFlags.isFetching && records.length" class="woot-table">
|
||||
<thead>
|
||||
<th
|
||||
v-for="thHeader in $t('MACROS.LIST.TABLE_HEADER')"
|
||||
:key="thHeader"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<MacrosTableRow
|
||||
v-for="(macro, index) in records"
|
||||
:key="index"
|
||||
:macro="macro"
|
||||
@delete="openDeletePopup(macro, index)"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="hidden w-1/3 lg:block">
|
||||
<span v-dompurify-html="$t('MACROS.SIDEBAR_TXT')" />
|
||||
</div>
|
||||
</div>
|
||||
<woot-delete-modal
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('LABEL_MGMT.DELETE.CONFIRM.TITLE')"
|
||||
:message="$t('MACROS.DELETE.CONFIRM.MESSAGE')"
|
||||
:message-value="deleteMessage"
|
||||
:confirm-text="$t('MACROS.DELETE.CONFIRM.YES')"
|
||||
:reject-text="$t('MACROS.DELETE.CONFIRM.NO')"
|
||||
/>
|
||||
</div>
|
||||
<SettingsLayout
|
||||
:no-records-message="$t('MACROS.LIST.404')"
|
||||
:no-records-found="!records.length"
|
||||
:is-loading="uiFlags.isFetching"
|
||||
:loading-message="$t('MACROS.LOADING')"
|
||||
feature-name="macros"
|
||||
>
|
||||
<template #header>
|
||||
<BaseSettingsHeader
|
||||
:title="$t('MACROS.HEADER')"
|
||||
:description="$t('MACROS.DESCRIPTION')"
|
||||
:link-text="$t('MACROS.LEARN_MORE')"
|
||||
feature-name="macros"
|
||||
>
|
||||
<template #actions>
|
||||
<router-link
|
||||
:to="{ name: 'macros_new' }"
|
||||
class="button rounded-md primary"
|
||||
>
|
||||
<fluent-icon icon="add-circle" />
|
||||
<span class="button__content">
|
||||
{{ $t('MACROS.HEADER_BTN_TXT') }}
|
||||
</span>
|
||||
</router-link>
|
||||
</template>
|
||||
</BaseSettingsHeader>
|
||||
</template>
|
||||
<template #body>
|
||||
<table class="min-w-full divide-y divide-slate-75 dark:divide-slate-700">
|
||||
<thead>
|
||||
<th
|
||||
v-for="thHeader in $t('MACROS.LIST.TABLE_HEADER')"
|
||||
:key="thHeader"
|
||||
class="py-4 ltr:pr-4 rtl:pl-4 text-left font-semibold text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
{{ thHeader }}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody
|
||||
class="divide-y divide-slate-50 dark:divide-slate-800 text-slate-700 dark:text-slate-300"
|
||||
>
|
||||
<MacrosTableRow
|
||||
v-for="(macro, index) in records"
|
||||
:key="index"
|
||||
:macro="macro"
|
||||
@delete="openDeletePopup(macro)"
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
<woot-delete-modal
|
||||
:show.sync="showDeleteConfirmationPopup"
|
||||
:on-close="closeDeletePopup"
|
||||
:on-confirm="confirmDeletion"
|
||||
:title="$t('LABEL_MGMT.DELETE.CONFIRM.TITLE')"
|
||||
:message="$t('MACROS.DELETE.CONFIRM.MESSAGE')"
|
||||
:message-value="deleteMessage"
|
||||
:confirm-text="$t('MACROS.DELETE.CONFIRM.YES')"
|
||||
:reject-text="$t('MACROS.DELETE.CONFIRM.NO')"
|
||||
/>
|
||||
</template>
|
||||
</SettingsLayout>
|
||||
</template>
|
||||
|
||||
@@ -1,59 +1,56 @@
|
||||
<script>
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
export default {
|
||||
components: {
|
||||
Thumbnail,
|
||||
},
|
||||
props: {
|
||||
macro: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { accountScopedUrl } = useAccount();
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
|
||||
return {
|
||||
accountScopedUrl,
|
||||
};
|
||||
const props = defineProps({
|
||||
macro: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
computed: {
|
||||
createdByName() {
|
||||
const createdBy = this.macro.created_by;
|
||||
return createdBy.available_name ?? createdBy.email ?? '';
|
||||
},
|
||||
updatedByName() {
|
||||
const updatedBy = this.macro.updated_by;
|
||||
return updatedBy.available_name ?? updatedBy.email ?? '';
|
||||
},
|
||||
visibilityLabel() {
|
||||
return this.macro.visibility === 'global'
|
||||
? this.$t('MACROS.EDITOR.VISIBILITY.GLOBAL.LABEL')
|
||||
: this.$t('MACROS.EDITOR.VISIBILITY.PERSONAL.LABEL');
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
defineEmits(['delete']);
|
||||
const { t } = useI18n();
|
||||
|
||||
const createdByName = computed(() => {
|
||||
const createdBy = props.macro.created_by;
|
||||
return createdBy.available_name ?? createdBy.email ?? '';
|
||||
});
|
||||
|
||||
const updatedByName = computed(() => {
|
||||
const updatedBy = props.macro.updated_by;
|
||||
return updatedBy.available_name ?? updatedBy.email ?? '';
|
||||
});
|
||||
|
||||
const visibilityLabel = computed(() => {
|
||||
const i18nKey =
|
||||
props.macro.visibility === 'global'
|
||||
? 'MACROS.EDITOR.VISIBILITY.GLOBAL.LABEL'
|
||||
: 'MACROS.EDITOR.VISIBILITY.PERSONAL.LABEL';
|
||||
return t(i18nKey);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tr>
|
||||
<td>{{ macro.name }}</td>
|
||||
<td>
|
||||
<div v-if="macro.created_by" class="avatar-container">
|
||||
<td class="py-4 ltr:pr-4 rtl:pl-4 truncate">{{ macro.name }}</td>
|
||||
<td class="py-4 ltr:pr-4 rtl:pl-4">
|
||||
<div v-if="macro.created_by" class="flex items-center">
|
||||
<Thumbnail :username="createdByName" size="24px" />
|
||||
<span>{{ createdByName }}</span>
|
||||
<span class="mx-2">{{ createdByName }}</span>
|
||||
</div>
|
||||
<div v-else>--</div>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="macro.updated_by" class="avatar-container">
|
||||
<td class="py-4 ltr:pr-4 rtl:pl-4">
|
||||
<div v-if="macro.updated_by" class="flex items-center">
|
||||
<Thumbnail :username="updatedByName" size="24px" />
|
||||
<span>{{ updatedByName }}</span>
|
||||
<span class="mx-2">{{ updatedByName }}</span>
|
||||
</div>
|
||||
<div v-else>--</div>
|
||||
</td>
|
||||
<td>{{ visibilityLabel }}</td>
|
||||
<td class="button-wrapper">
|
||||
<router-link :to="accountScopedUrl(`settings/macros/${macro.id}/edit`)">
|
||||
<td class="py-4 ltr:pr-4 rtl:pl-4">{{ visibilityLabel }}</td>
|
||||
<td class="py-4 flex justify-end gap-1">
|
||||
<router-link :to="{ name: 'macros_edit', params: { macroId: macro.id } }">
|
||||
<woot-button
|
||||
v-tooltip.top="$t('MACROS.EDIT.TOOLTIP')"
|
||||
variant="smooth"
|
||||
@@ -75,15 +72,3 @@ export default {
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
margin-left: var(--space-small);
|
||||
margin-right: var(--space-small);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||
|
||||
const SettingsContent = () => import('../Wrapper.vue');
|
||||
const SettingsWrapper = () => import('../SettingsWrapper.vue');
|
||||
const Macros = () => import('./Index.vue');
|
||||
const MacroEditor = () => import('./MacroEditor.vue');
|
||||
|
||||
@@ -8,16 +9,7 @@ export default {
|
||||
routes: [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/macros'),
|
||||
component: SettingsContent,
|
||||
props: params => {
|
||||
const showBackButton = params.name !== 'macros_wrapper';
|
||||
return {
|
||||
headerTitle: 'MACROS.HEADER',
|
||||
headerButtonText: 'MACROS.HEADER_BTN_TXT',
|
||||
icon: 'flash-settings',
|
||||
showBackButton,
|
||||
};
|
||||
},
|
||||
component: SettingsWrapper,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
@@ -27,6 +19,27 @@ export default {
|
||||
permissions: ['administrator', 'agent'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/settings/macros'),
|
||||
component: SettingsContent,
|
||||
props: () => {
|
||||
return {
|
||||
headerTitle: 'MACROS.HEADER',
|
||||
icon: 'flash-settings',
|
||||
showBackButton: true,
|
||||
};
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':macroId/edit',
|
||||
name: 'macros_edit',
|
||||
component: MacroEditor,
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
name: 'macros_new',
|
||||
@@ -35,14 +48,6 @@ export default {
|
||||
permissions: ['administrator', 'agent'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':macroId/edit',
|
||||
name: 'macros_edit',
|
||||
component: MacroEditor,
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user