feat: Show popular articles on widget home (#7604)

This commit is contained in:
Nithin David Thomas
2023-08-01 21:32:44 +05:30
committed by GitHub
parent 9efadf8804
commit e052a061f4
22 changed files with 299 additions and 64 deletions

View File

@@ -10,7 +10,7 @@
<div
v-dompurify-html="formatMessage(message, false)"
class="message-content"
:class="$dm('text-black-900', 'dark:text-slate-50')"
:class="$dm('text-slate-900', 'dark:text-slate-50')"
/>
<email-input
v-if="isTemplateEmail"

View File

@@ -1,21 +1,27 @@
<template>
<div class="py-2">
<h3 class="text-sm font-semibold text-slate-900 mb-0">{{ title }}</h3>
<article-list :articles="articles" />
<div>
<h3 class="text-base font-medium text-slate-900 dark:text-slate-50 mb-0">
{{ title }}
</h3>
<article-list :articles="articles" @click="onArticleClick" />
<button
class="inline-flex text-sm font-medium rounded-md px-2 py-1 -ml-2 leading-6 text-slate-800 justify-between items-center hover:bg-slate-25 see-articles"
@click="$emit('view-all-articles')"
class="inline-flex text-sm font-medium rounded-md px-2 py-1 -ml-2 leading-6 text-slate-800 dark:text-slate-50 justify-between items-center hover:bg-slate-25 see-articles"
:style="{ color: widgetColor }"
@click="$emit('view-all')"
>
<span class="pr-2">{{ $t('PORTAL.VIEW_ALL_ARTICLES') }}</span>
<span class="pr-2 text-sm">{{ $t('PORTAL.VIEW_ALL_ARTICLES') }}</span>
<fluent-icon icon="arrow-right" size="14" />
</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import ArticleList from './ArticleList.vue';
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
export default {
components: { ArticleList },
components: { FluentIcon, ArticleList },
props: {
title: {
type: String,
@@ -25,9 +31,13 @@ export default {
type: Array,
default: () => [],
},
categoryPath: {
type: String,
default: '',
},
computed: {
...mapGetters({ widgetColor: 'appConfig/getWidgetColor' }),
},
methods: {
onArticleClick(link) {
this.$emit('view', link);
},
},
};

View File

@@ -1,13 +1,10 @@
<template>
<div>
<h2 class="text-base font-bold leading-6 text-slate-800 mb-0">
{{ $t('PORTAL.POPULAR_ARTICLES') }}
</h2>
<category-card
:articles="articles.slice(0, 4)"
@view-all-articles="$emit('view-all-articles')"
/>
</div>
<category-card
:title="$t('PORTAL.POPULAR_ARTICLES')"
:articles="articles.slice(0, 4)"
@view-all="$emit('view-all')"
@view="onArticleClick"
/>
</template>
<script>
@@ -24,6 +21,11 @@ export default {
default: '',
},
},
methods: {
onArticleClick(link) {
this.$emit('view', link);
},
},
};
</script>

View File

@@ -2,7 +2,7 @@
<ul role="list" class="py-2">
<article-list-item
v-for="article in articles"
:key="article.id"
:key="article.slug"
:link="article.link"
:title="article.title"
@click="onClick"

View File

@@ -1,20 +1,24 @@
<template>
<li
class="py-1 flex items-center justify-between -mx-1 px-1 hover:bg-slate-25"
class="py-1 flex items-center justify-between -mx-1 px-1 hover:bg-slate-75 dark:hover:bg-slate-600 rounded cursor-pointer text-slate-700 dark:text-slate-50 dark:hover:text-slate-25 hover:text-slate-900 "
@click="onClick"
>
<button
class="text-slate-700 hover:text-slate-900 underline-offset-2 text-sm leading-6"
class="underline-offset-2 text-sm leading-6 text-left"
@click="onClick"
>
{{ title }}
</button>
<span class="pl-1 text-slate-700 arrow">
<span class="pl-1 first-letter arrow">
<fluent-icon icon="arrow-right" size="14" />
</span>
</li>
</template>
<script>
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
export default {
components: { FluentIcon },
props: {
link: {
type: String,

View File

@@ -4,7 +4,11 @@
:class="$dm('bg-white', 'dark:bg-slate-900')"
>
<div class="flex items-center">
<button v-if="showBackButton" @click="onBackButtonClick">
<button
v-if="showBackButton"
class="-ml-3 px-2"
@click="onBackButtonClick"
>
<fluent-icon
icon="chevron-left"
size="24"

View File

@@ -1,7 +1,7 @@
<template>
<header
class="header-expanded py-6 px-5 relative box-border w-full"
:class="$dm('bg-white', 'dark:bg-slate-900')"
:class="showBg ? 'bg-transparent' : 'bg-white dark:bg-slate-900'"
>
<div
class="flex items-start"
@@ -13,23 +13,25 @@
:src="avatarUrl"
alt="Avatar"
/>
<header-actions :show-popout-button="showPopoutButton" />
<header-actions
:show-popout-button="showPopoutButton"
:show-end-conversation-button="false"
/>
</div>
<h2
v-dompurify-html="introHeading"
class="mt-5 text-3xl mb-3 leading-8 font-normal"
class="mt-4 text-2xl mb-2 font-normal"
:class="$dm('text-slate-900', 'dark:text-slate-50')"
/>
<p
v-dompurify-html="introBody"
class="text-lg leading-normal"
class="text-base leading-normal"
:class="$dm('text-slate-700', 'dark:text-slate-200')"
/>
</header>
</template>
<script>
import { mapGetters } from 'vuex';
import HeaderActions from './HeaderActions';
import darkModeMixin from 'widget/mixins/darkModeMixin.js';
@@ -56,11 +58,10 @@ export default {
type: Boolean,
default: false,
},
},
computed: {
...mapGetters({
widgetColor: 'appConfig/getWidgetColor',
}),
showBg: {
type: Boolean,
default: true,
},
},
};
</script>

View File

@@ -1,7 +1,11 @@
<template>
<div v-if="showHeaderActions" class="actions flex items-center">
<button
v-if="canLeaveConversation && hasEndConversationEnabled"
v-if="
canLeaveConversation &&
hasEndConversationEnabled &&
showEndConversationButton
"
class="button transparent compact"
:title="$t('END_CONVERSATION')"
@click="resolveConversation"
@@ -56,6 +60,10 @@ export default {
type: Boolean,
default: false,
},
showEndConversationButton: {
type: Boolean,
default: true,
},
},
computed: {
...mapGetters({

View File

@@ -1,18 +1,18 @@
<template>
<div class="px-5">
<div class="p-4 shadow rounded-md bg-white dark:bg-slate-700">
<div class="flex items-center justify-between mb-4">
<div
class="max-w-xs"
:class="$dm('text-black-700', 'dark:text-slate-50')"
:class="$dm('text-slate-700', 'dark:text-slate-50')"
>
<div class="text-base leading-5 font-medium mb-1">
<div class="text-sm font-medium mb-1">
{{
isOnline
? $t('TEAM_AVAILABILITY.ONLINE')
: $t('TEAM_AVAILABILITY.OFFLINE')
}}
</div>
<div class="text-xs leading-3 mt-1">
<div class="text-xs mt-1">
{{ replyWaitMessage }}
</div>
</div>

View File

@@ -1,14 +1,16 @@
<template>
<div
class="w-full h-full flex flex-col"
:class="$dm('bg-slate-50', 'dark:bg-slate-800')"
class="w-full h-full flex flex-col relative bg-slate-50 dark:bg-slate-800"
:class="{ 'overflow-auto': isOnHomeView }"
:style="portal ? { backgroundColor: backgroundColor } : {}"
@keydown.esc="closeWindow"
>
<div
class="header-wrap"
class="header-wrap sticky top-0 z-40"
:class="{
expanded: !isHeaderCollapsed,
collapsed: isHeaderCollapsed,
'custom-header-shadow': (isOnHomeView && !portal) || !isOnArticleViewer,
}"
>
<transition
@@ -25,6 +27,7 @@
:intro-body="channelConfig.welcomeTagline"
:avatar-url="channelConfig.avatarUrl"
:show-popout-button="appConfig.showPopoutButton"
:show-bg="!!portal"
/>
<chat-header
v-if="isHeaderCollapsed"
@@ -32,6 +35,7 @@
:avatar-url="channelConfig.avatarUrl"
:show-popout-button="appConfig.showPopoutButton"
:available-agents="availableAgents"
:show-back-button="showBackButton"
/>
</transition>
</div>
@@ -46,7 +50,7 @@
>
<router-view />
</transition>
<branding :disable-branding="disableBranding" />
<branding v-if="!isOnArticleViewer" :disable-branding="disableBranding" />
</div>
</template>
<script>
@@ -75,20 +79,42 @@ export default {
},
computed: {
...mapGetters({
availableAgents: 'agent/availableAgents',
appConfig: 'appConfig/getAppConfig',
availableAgents: 'agent/availableAgents',
widgetColor: 'appConfig/getWidgetColor',
}),
portal() {
return window.chatwootWebChannel.portal;
},
isHeaderCollapsed() {
if (!this.hasIntroText) {
return true;
}
return this.$route.name !== 'home';
return !this.isOnHomeView;
},
backgroundColor() {
const color = this.widgetColor.replace('#', '');
const r = parseInt(color.slice(0, 2), 16);
const g = parseInt(color.slice(2, 4), 16);
const b = parseInt(color.slice(4, 6), 16);
return `rgba(${r},${g},${b}, 0.02)`;
},
hasIntroText() {
return (
this.channelConfig.welcomeTitle || this.channelConfig.welcomeTagline
);
},
showBackButton() {
return ['article-viewer', 'messages', 'prechat-form'].includes(
this.$route.name
);
},
isOnArticleViewer() {
return ['article-viewer'].includes(this.$route.name);
},
isOnHomeView() {
return ['home'].includes(this.$route.name);
},
},
methods: {
closeWindow() {
@@ -102,11 +128,13 @@ export default {
@import '~widget/assets/scss/variables';
@import '~widget/assets/scss/mixins';
.custom-header-shadow {
@include shadow-large;
}
.header-wrap {
flex-shrink: 0;
transition: max-height 300ms;
z-index: 99;
@include shadow-large;
&.expanded {
max-height: 16rem;