feat: change custom attribute screen
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
import AttributeBadge from 'dashboard/components-next/ConversationWorkflow/AttributeBadge.vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
attribute: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
badges: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['edit', 'delete']);
|
||||
|
||||
const iconByType = {
|
||||
text: 'i-lucide-align-justify',
|
||||
checkbox: 'i-lucide-circle-check-big',
|
||||
list: 'i-lucide-list',
|
||||
date: 'i-lucide-calendar',
|
||||
link: 'i-lucide-link',
|
||||
number: 'i-lucide-hash',
|
||||
};
|
||||
|
||||
const attributeIcon = computed(() => {
|
||||
const typeKey = props.attribute.type?.toLowerCase();
|
||||
return iconByType[typeKey] || 'i-lucide-align-justify';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col gap-2 p-4 bg-white rounded-2xl border border-n-weak dark:bg-n-solid-1"
|
||||
>
|
||||
<div class="flex flex-wrap gap-2 justify-between items-center">
|
||||
<div class="flex flex-wrap gap-2 items-center min-w-0">
|
||||
<h4 class="text-sm font-medium truncate text-n-slate-12">
|
||||
{{ attribute.label }}
|
||||
</h4>
|
||||
<div class="flex gap-2 items-center text-sm text-n-slate-11">
|
||||
<Icon :icon="attributeIcon" class="size-4" />
|
||||
<span class="text-sm text-n-slate-11">{{ attribute.type }}</span>
|
||||
<div class="w-px h-3 bg-n-weak" />
|
||||
<Icon icon="i-lucide-key-round" class="size-4" />
|
||||
<span class="line-clamp-1">{{ attribute.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<AttributeBadge
|
||||
v-for="badge in badges"
|
||||
:key="badge.label || badge"
|
||||
:label="badge.label || badge"
|
||||
:icon="badge.icon"
|
||||
:color="badge.color"
|
||||
/>
|
||||
<div class="w-px h-3 bg-n-strong" />
|
||||
<Button
|
||||
icon="i-lucide-pencil-line"
|
||||
size="sm"
|
||||
color="slate"
|
||||
ghost
|
||||
@click="emit('edit', attribute)"
|
||||
/>
|
||||
<div class="w-px h-3 bg-n-strong" />
|
||||
<Button
|
||||
icon="i-lucide-trash"
|
||||
size="sm"
|
||||
color="slate"
|
||||
ghost
|
||||
@click="emit('delete', attribute)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0 text-sm text-n-slate-11">
|
||||
{{ attribute.attribute_description || attribute.description || '' }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -20,7 +20,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['tabChanged']);
|
||||
const emit = defineEmits(['change', 'tabChanged']);
|
||||
|
||||
const activeTab = computed(() => props.initialActiveTab);
|
||||
|
||||
@@ -51,6 +51,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const selectTab = index => {
|
||||
emit('change', index);
|
||||
emit('tabChanged', props.tabs[index]);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
|
||||
import AddAttribute from './AddAttribute.vue';
|
||||
import CustomAttribute from './CustomAttribute.vue';
|
||||
import EditAttribute from './EditAttribute.vue';
|
||||
import SettingsLayout from '../SettingsLayout.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import TabBar from 'dashboard/components-next/tabbar/TabBar.vue';
|
||||
import AttributeListItem from 'dashboard/components-next/ConversationWorkflow/AttributeListItem.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
|
||||
@@ -16,6 +18,9 @@ const store = useStore();
|
||||
const showAddPopup = ref(false);
|
||||
const selectedTabIndex = ref(0);
|
||||
const uiFlags = computed(() => getters['attributes/getUIFlags'].value);
|
||||
const showEditPopup = ref(false);
|
||||
const showDeletePopup = ref(false);
|
||||
const selectedAttribute = ref({});
|
||||
|
||||
const openAddPopup = () => {
|
||||
showAddPopup.value = true;
|
||||
@@ -23,6 +28,14 @@ const openAddPopup = () => {
|
||||
const hideAddPopup = () => {
|
||||
showAddPopup.value = false;
|
||||
};
|
||||
const hideEditPopup = () => {
|
||||
showEditPopup.value = false;
|
||||
selectedAttribute.value = {};
|
||||
};
|
||||
const closeDelete = () => {
|
||||
showDeletePopup.value = false;
|
||||
selectedAttribute.value = {};
|
||||
};
|
||||
|
||||
const tabs = computed(() => {
|
||||
return [
|
||||
@@ -49,9 +62,23 @@ const attributes = computed(() =>
|
||||
getters['attributes/getAttributesByModel'].value(attributeModel.value)
|
||||
);
|
||||
|
||||
const onClickTabChange = index => {
|
||||
selectedTabIndex.value = index;
|
||||
const onClickTabChange = payload => {
|
||||
selectedTabIndex.value = typeof payload === 'number' ? payload : payload?.key;
|
||||
};
|
||||
|
||||
const handleEditAttribute = attribute => {
|
||||
selectedAttribute.value = attribute;
|
||||
showEditPopup.value = true;
|
||||
};
|
||||
|
||||
const handleDeleteAttribute = attribute => {
|
||||
selectedAttribute.value = attribute;
|
||||
showDeletePopup.value = true;
|
||||
};
|
||||
|
||||
const badges = computed(() => {
|
||||
return ['pre-chat', 'resolution'];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -77,27 +104,30 @@ const onClickTabChange = index => {
|
||||
</template>
|
||||
</BaseSettingsHeader>
|
||||
</template>
|
||||
<template #preBody>
|
||||
<woot-tabs
|
||||
class="font-medium [&_ul]:p-0 mb-4"
|
||||
:index="selectedTabIndex"
|
||||
@change="onClickTabChange"
|
||||
>
|
||||
<woot-tabs-item
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="tab.key"
|
||||
:index="index"
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
is-compact
|
||||
/>
|
||||
</woot-tabs>
|
||||
</template>
|
||||
<template #body>
|
||||
<CustomAttribute
|
||||
:key="attributeModel"
|
||||
:attribute-model="attributeModel"
|
||||
/>
|
||||
<div class="flex flex-col gap-6">
|
||||
<TabBar
|
||||
:tabs="tabs.map(tab => ({ label: tab.name, key: tab.key }))"
|
||||
:initial-active-tab="selectedTabIndex"
|
||||
class="max-w-xl"
|
||||
@change="onClickTabChange"
|
||||
/>
|
||||
<div class="grid gap-3">
|
||||
<AttributeListItem
|
||||
v-for="attribute in attributes"
|
||||
:key="attribute.id"
|
||||
:attribute="{
|
||||
...attribute,
|
||||
label: attribute.attribute_display_name,
|
||||
type: attribute.attribute_display_type,
|
||||
value: attribute.attribute_key,
|
||||
}"
|
||||
:badges="badges"
|
||||
@edit="handleEditAttribute"
|
||||
@delete="handleDeleteAttribute"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<AddAttribute
|
||||
v-if="showAddPopup"
|
||||
@@ -105,5 +135,39 @@ const onClickTabChange = index => {
|
||||
:on-close="hideAddPopup"
|
||||
:selected-attribute-model-tab="selectedTabIndex"
|
||||
/>
|
||||
<woot-modal v-model:show="showEditPopup" :on-close="hideEditPopup">
|
||||
<EditAttribute
|
||||
:selected-attribute="selectedAttribute"
|
||||
:is-updating="uiFlags.isUpdating"
|
||||
@on-close="hideEditPopup"
|
||||
/>
|
||||
</woot-modal>
|
||||
<woot-confirm-delete-modal
|
||||
v-if="showDeletePopup"
|
||||
v-model:show="showDeletePopup"
|
||||
:title="
|
||||
$t('ATTRIBUTES_MGMT.DELETE.CONFIRM.TITLE', {
|
||||
attributeName: selectedAttribute.attribute_display_name,
|
||||
})
|
||||
"
|
||||
:message="$t('ATTRIBUTES_MGMT.DELETE.CONFIRM.MESSAGE')"
|
||||
:confirm-text="`${$t('ATTRIBUTES_MGMT.DELETE.CONFIRM.YES')} ${
|
||||
selectedAttribute.attribute_display_name || ''
|
||||
}`"
|
||||
:reject-text="$t('ATTRIBUTES_MGMT.DELETE.CONFIRM.NO')"
|
||||
:confirm-value="selectedAttribute.attribute_display_name"
|
||||
:confirm-place-holder-text="
|
||||
$t('ATTRIBUTES_MGMT.DELETE.CONFIRM.PLACE_HOLDER', {
|
||||
attributeName: selectedAttribute.attribute_display_name,
|
||||
})
|
||||
"
|
||||
@on-confirm="
|
||||
() => {
|
||||
store.dispatch('attributes/delete', selectedAttribute.id);
|
||||
closeDelete();
|
||||
}
|
||||
"
|
||||
@on-close="closeDelete"
|
||||
/>
|
||||
</SettingsLayout>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user