Merge branch 'feat/contacts-redesign' into feat/compose-new-conversation

This commit is contained in:
Sivin Varghese
2024-11-22 10:30:45 +05:30
committed by GitHub
9 changed files with 53 additions and 192 deletions
@@ -3,8 +3,8 @@ import { computed, watch, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { useMapGetter, useStore } from 'dashboard/composables/store';
import LabelItem from 'dashboard/components-next/label/LabelItem.vue';
import AddLabel from 'dashboard/components-next/label/AddLabel.vue';
import LabelItem from 'dashboard/components-next/Label/LabelItem.vue';
import AddLabel from 'dashboard/components-next/Label/AddLabel.vue';
const props = defineProps({
contactId: {
@@ -27,6 +27,7 @@ const props = defineProps({
const emit = defineEmits([
'saveArticle',
'saveArticleAsync',
'goBack',
'setAuthor',
'setCategory',
@@ -35,19 +36,37 @@ const emit = defineEmits([
const { t } = useI18n();
const saveArticle = debounce(value => emit('saveArticle', value), 600, false);
const saveAndSync = value => {
emit('saveArticle', value);
};
// this will only send the data to the backend
// but will not update the local state preventing unnecessary re-renders
// since the data is already saved and we keep the editor text as the source of truth
const quickSave = debounce(
value => emit('saveArticleAsync', value),
400,
false
);
// 2.5 seconds is enough to know that the user has stopped typing and is taking a pause
// so we can save the data to the backend and retrieve the updated data
// this will update the local state with response data
const saveAndSyncDebounced = debounce(saveAndSync, 2500, false);
const articleTitle = computed({
get: () => props.article.title,
set: value => {
saveArticle({ title: value });
quickSave({ title: value });
saveAndSyncDebounced({ title: value });
},
});
const articleContent = computed({
get: () => props.article.content,
set: content => {
saveArticle({ content });
quickSave({ content });
saveAndSyncDebounced({ content });
},
});
@@ -93,7 +112,7 @@ const previewArticle = () => {
/>
<ArticleEditorControls
:article="article"
@save-article="saveArticle"
@save-article="saveAndSync"
@set-author="setAuthorId"
@set-category="setCategoryId"
/>
@@ -1,49 +0,0 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
defineProps({
labelMenuItems: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['updateLabel']);
const { t } = useI18n();
const showDropdown = ref(false);
</script>
<template>
<div class="relative">
<button
class="flex items-center gap-1 px-2 py-1 rounded-md outline-dashed h-[26px] outline-1 outline-n-slate-6 hover:bg-n-alpha-2"
:class="{ 'bg-n-alpha-2': showDropdown }"
@click="showDropdown = !showDropdown"
>
<span class="i-lucide-plus" />
<span class="text-sm text-n-slate-11">
{{ t('LABEL.TAG_BUTTON') }}
</span>
</button>
<DropdownMenu
v-if="showDropdown"
v-on-clickaway="() => (showDropdown = false)"
:menu-items="labelMenuItems"
show-search
class="z-[100] w-48 mt-2 overflow-y-auto ltr:left-0 rtl:right-0 top-full max-h-52"
@action="emit('updateLabel', $event)"
>
<template #thumbnail="{ item }">
<div
class="rounded-sm size-2"
:style="{ backgroundColor: item.thumbnail.color }"
/>
</template>
</DropdownMenu>
</div>
</template>
@@ -1,22 +0,0 @@
<script setup>
defineProps({
label: {
type: Object,
default: null,
},
});
</script>
<template>
<div
class="bg-n-alpha-2 rounded-md flex items-center h-7 w-fit py-1 ltr:pl-1 rtl:pr-1 ltr:pr-1.5 rtl:pl-1.5"
>
<div
class="w-2 h-2 m-1 rounded-sm"
:style="{ backgroundColor: label.color }"
/>
<span class="text-sm text-n-slate-12">
{{ label.title }}
</span>
</div>
</template>
@@ -1,27 +0,0 @@
<script setup>
import AddLabel from '../AddLabel.vue';
import { labelMenuItems } from './fixtures';
function onUpdateLabel(label) {
console.log('Label updated:', label);
}
</script>
<template>
<Story title="Components/Label/Add Label">
<Variant title="Default (button with label menu items with active state)">
<div class="h-[300px] p-4">
<AddLabel
:label-menu-items="labelMenuItems"
@update-label="onUpdateLabel"
/>
</div>
</Variant>
<Variant title="Empty List (button with empty label menu)">
<div class="h-[300px] p-4">
<AddLabel :label-menu-items="[]" @update-label="onUpdateLabel" />
</div>
</Variant>
</Story>
</template>
@@ -1,21 +0,0 @@
<script setup>
import Label from '../LabelItem.vue';
import { label } from './fixtures';
</script>
<template>
<Story title="Components/Label/Label item">
<Variant title="Default">
<Label :label="label" />
</Variant>
<Variant title="Custom Label">
<Label
:label="{
title: 'Custom Label',
color: '#FF5733',
}"
/>
</Variant>
</Story>
</template>
@@ -1,62 +0,0 @@
export const label = {
id: 1,
title: 'delivery',
color: '#A2FDD5',
};
export const labelMenuItems = [
{
label: 'delivery',
value: 3,
thumbnail: {
color: '#A2FDD5',
},
isSelected: true,
action: 'addLabel',
},
{
label: 'lead',
value: 6,
thumbnail: {
color: '#F161C8',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'ops-handover',
value: 4,
thumbnail: {
color: '#A53326',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'billing',
value: 1,
thumbnail: {
color: '#28AD21',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'premium-customer',
value: 5,
thumbnail: {
color: '#6FD4EF',
},
isSelected: false,
action: 'addLabel',
},
{
label: 'software',
value: 2,
thumbnail: {
color: '#8F6EF2',
},
isSelected: false,
action: 'addLabel',
},
];
@@ -34,10 +34,11 @@ const portalLink = computed(() => {
);
});
const saveArticle = async ({ ...values }) => {
const saveArticle = async ({ ...values }, isAsync = false) => {
const actionToDispatch = isAsync ? 'articles/updateAsync' : 'articles/update';
isUpdating.value = true;
try {
await store.dispatch('articles/update', {
await store.dispatch(actionToDispatch, {
portalSlug,
articleId: articleSlug,
...values,
@@ -55,6 +56,10 @@ const saveArticle = async ({ ...values }) => {
}
};
const saveArticleAsync = async ({ ...values }) => {
saveArticle({ ...values }, true);
};
const isCategoryArticles = computed(() => {
return (
route.name === 'portals_categories_articles_index' ||
@@ -92,9 +97,7 @@ const previewArticle = () => {
});
};
onMounted(() => {
fetchArticleDetails();
});
onMounted(fetchArticleDetails);
</script>
<template>
@@ -103,6 +106,7 @@ onMounted(() => {
:is-updating="isUpdating"
:is-saved="isSaved"
@save-article="saveArticle"
@save-article-async="saveArticleAsync"
@preview-article="previewArticle"
@go-back="goBackToArticles"
/>
@@ -69,6 +69,25 @@ export const actions = {
}
},
updateAsync: async ({ commit }, { portalSlug, articleId, ...articleObj }) => {
commit(types.UPDATE_ARTICLE_FLAG, {
uiFlags: { isUpdating: true },
articleId,
});
try {
await articlesAPI.updateArticle({ portalSlug, articleId, articleObj });
return articleId;
} catch (error) {
return throwErrorMessage(error);
} finally {
commit(types.UPDATE_ARTICLE_FLAG, {
uiFlags: { isUpdating: false },
articleId,
});
}
},
update: async ({ commit }, { portalSlug, articleId, ...articleObj }) => {
commit(types.UPDATE_ARTICLE_FLAG, {
uiFlags: {