Merge branch 'develop' into report-rewrite

This commit is contained in:
Pranav
2024-03-28 16:00:01 -07:00
1120 changed files with 20061 additions and 6509 deletions
@@ -1,7 +1,7 @@
<template>
<div
class="w-[25%] bg-white dark:bg-slate-900 contact--panel"
:class="{ 'border-left': showAvatar }"
class="w-1/4 bg-white dark:bg-slate-900 border-slate-50 dark:border-slate-800/50 h-full text-sm overflow-y-auto relative"
:class="showAvatar ? 'border-l border-solid ' : 'border-r border-solid'"
>
<contact-info
:show-close-button="showCloseButton"
@@ -151,9 +151,6 @@ export default {
@apply pb-3 mb-4;
}
}
.contact--panel {
@apply border-r border-slate-50 dark:border-slate-800/50 h-full text-sm overflow-y-auto relative;
}
.list-group {
.list-group-item {
@@ -243,7 +243,7 @@ export default {
attr.attribute_display_type === 'checkbox'
);
});
if (isCustomAttributeCheckbox) {
if (isCustomAttributeCheckbox || type === 'blocked') {
return [
{
id: true,
@@ -135,7 +135,7 @@ export default {
status={row.availability_status}
/>
<div class="user-block">
<h6 class="sub-block-title overflow-hidden whitespace-nowrap text-ellipsis">
<h6 class="text-base overflow-hidden whitespace-nowrap text-ellipsis">
<router-link
to={`/app/accounts/${this.$route.params.accountId}/contacts/${row.id}`}
class="user-name"
@@ -113,6 +113,13 @@
</woot-button>
</div>
</div>
<woot-confirm-modal
ref="confirmExportContactsDialog"
:title="$t('EXPORT_CONTACTS.CONFIRM.TITLE')"
:description="$t('EXPORT_CONTACTS.CONFIRM.MESSAGE')"
:confirm-label="$t('EXPORT_CONTACTS.CONFIRM.YES')"
:cancel-label="$t('EXPORT_CONTACTS.CONFIRM.NO')"
/>
</header>
</template>
@@ -175,8 +182,13 @@ export default {
toggleImport() {
this.$emit('on-toggle-import');
},
submitExport() {
this.$emit('on-export-submit');
async submitExport() {
const ok =
await this.$refs.confirmExportContactsDialog.showConfirmation();
if (ok) {
this.$emit('on-export-submit');
}
},
submitSearch() {
this.$emit('on-search-submit');
@@ -1,7 +1,7 @@
<template>
<div class="wrap">
<div class="header">
<h5 class="block-title text-black-900 dark:text-slate-200">
<h5 class="text-lg text-black-900 dark:text-slate-200">
{{ $t('EVENTS.HEADER.TITLE') }}
</h5>
</div>
@@ -6,7 +6,7 @@
<div class="card-wrap">
<div class="header">
<div class="text-wrap">
<h6 class="text-block-title">
<h6 class="text-sm">
{{ eventType }}
</h6>
<span class="event-path">on {{ eventPath }}</span>
@@ -76,6 +76,14 @@ const filterTypes = [
filterOperators: OPERATOR_TYPES_5,
attributeModel: 'standard',
},
{
attributeKey: 'blocked',
attributeI18nKey: 'BLOCKED',
inputType: 'search_select',
dataType: 'text',
filterOperators: OPERATOR_TYPES_1,
attributeModel: 'standard',
},
];
export const filterAttributeGroups = [
@@ -115,6 +123,10 @@ export const filterAttributeGroups = [
key: 'last_activity_at',
i18nKey: 'LAST_ACTIVITY',
},
{
key: 'blocked',
i18nKey: 'BLOCKED',
},
],
},
];
@@ -30,7 +30,7 @@
:show-avatar="false"
:contact="contact"
/>
<div class="w-[75%] h-full">
<div class="w-3/4 h-full">
<woot-tabs :index="selectedTabIndex" @change="onClickTabChange">
<woot-tabs-item
v-for="tab in tabs"
@@ -7,7 +7,7 @@
<spinner v-if="watchersUiFlas.isFetching" size="tiny" />
{{ totalWatchersText }}
</p>
<p v-else class="text-muted m-0 text-sm">
<p v-else class="text-slate-400 dark:text-slate-700 m-0 text-sm">
{{ $t('CONVERSATION_PARTICIPANTS.NO_PARTICIPANTS_TEXT') }}
</p>
</div>
@@ -3,14 +3,13 @@
<span class="overflow-hidden whitespace-nowrap text-ellipsis">{{
macro.name
}}</span>
<div class="macros-actions">
<div class="macros-actions flex items-center gap-1">
<woot-button
v-tooltip.left-start="$t('MACROS.EXECUTE.PREVIEW')"
size="tiny"
variant="smooth"
color-scheme="secondary"
icon="info"
class="margin-right-smaller"
@click="toggleMacroPreview(macro)"
/>
<woot-button
@@ -1,6 +1,6 @@
<template>
<form
class="contact--form w-full pt-6 px-8 pb-8"
class="w-full px-8 pt-6 pb-8 contact--form"
@submit.prevent="handleSubmit"
>
<div>
@@ -76,7 +76,7 @@
</label>
<div
v-if="isPhoneNumberNotValid || !phoneNumber"
class="callout small warning text-sm dark:bg-yellow-200/20 dark:text-yellow-400"
class="relative mx-0 mt-0 mb-2.5 p-2 rounded-md text-sm border border-solid border-yellow-500 text-yellow-700 dark:border-yellow-700 bg-yellow-200/60 dark:bg-yellow-200/20 dark:text-yellow-400"
>
{{ $t('CONTACT_FORM.FORM.PHONE_NUMBER.HELP') }}
</div>
@@ -121,17 +121,20 @@
<div
v-for="socialProfile in socialProfileKeys"
:key="socialProfile.key"
class="input-group"
class="flex items-stretch w-full mb-4"
>
<span class="input-group-label">{{ socialProfile.prefixURL }}</span>
<span
class="flex items-center h-10 px-2 text-sm border-solid bg-slate-50 border-y ltr:border-l rtl:border-r ltr:rounded-l-md rtl:rounded-r-md dark:bg-slate-700 text-slate-800 dark:text-slate-100 border-slate-200 dark:border-slate-600"
>{{ socialProfile.prefixURL }}</span
>
<input
v-model="socialProfileUserNames[socialProfile.key]"
class="input-group-field"
class="input-group-field ltr:rounded-l-none rtl:rounded-r-none !mb-0"
type="text"
/>
</div>
</div>
<div class="flex flex-row justify-end gap-2 py-2 px-0 w-full">
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
<div class="w-full">
<woot-submit-button
:loading="inProgress"
@@ -416,10 +419,6 @@ export default {
</script>
<style scoped lang="scss">
.input-group-label {
@apply text-sm bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100 border-slate-200 dark:border-slate-600;
}
::v-deep {
.multiselect .multiselect__tags .multiselect__single {
@apply pl-0;
@@ -1,6 +1,6 @@
<template>
<div class="relative items-center p-4 bg-white dark:bg-slate-900">
<div class="text-left rtl:text-right">
<div class="relative items-center p-4 bg-white dark:bg-slate-900 w-full">
<div class="text-left rtl:text-right flex flex-col gap-2 w-full">
<div class="flex justify-between flex-row">
<thumbnail
v-if="showAvatar"
@@ -17,10 +17,10 @@
/>
</div>
<div class="mt-2 w-full">
<div v-if="showAvatar" class="flex items-start mb-2 gap-1.5">
<div class="flex flex-col items-start gap-1.5 min-w-0 w-full">
<div v-if="showAvatar" class="flex items-start gap-2 min-w-0 w-full">
<h3
class="text-base text-slate-800 dark:text-slate-100 capitalize whitespace-normal my-0"
class="flex-shrink min-w-0 text-base text-slate-800 dark:text-slate-100 capitalize my-0 max-w-full break-words"
>
{{ contact.name }}
</h3>
@@ -38,7 +38,7 @@
/>
<a
:href="contactProfileLink"
class="fs-default"
class="text-base"
target="_blank"
rel="noopener nofollow noreferrer"
>
@@ -52,10 +52,10 @@
</div>
</div>
<p v-if="additionalAttributes.description" class="break-words">
<p v-if="additionalAttributes.description" class="break-words mb-0.5">
{{ additionalAttributes.description }}
</p>
<div class="mb-3">
<div class="flex flex-col gap-2 items-start w-full">
<contact-info-row
:href="contact.email ? `mailto:${contact.email}` : ''"
:value="contact.email"
@@ -95,7 +95,7 @@
<social-icons :social-profiles="socialProfiles" />
</div>
</div>
<div class="flex items-center w-full mt-2 gap-2">
<div class="flex items-center w-full mt-0.5 gap-2">
<woot-button
v-tooltip="$t('CONTACT_PANEL.NEW_MESSAGE')"
title="$t('CONTACT_PANEL.NEW_MESSAGE')"
@@ -1,15 +1,15 @@
<template>
<div class="-ml-1">
<div class="ltr:-ml-1 rtl:-mr-1 h-5 w-full">
<a
v-if="href"
:href="href"
class="flex items-center mb-2 text-slate-800 dark:text-slate-100 hover:underline"
class="flex items-center gap-2 text-slate-800 dark:text-slate-100 hover:underline"
>
<emoji-or-icon
:icon="icon"
:emoji="emoji"
icon-size="14"
class="mr-2 ml-1 rtl:mr-1 rtl:ml-2"
class="ltr:ml-1 rtl:mr-1 flex-shrink-0"
/>
<span
v-if="value"
@@ -29,20 +29,20 @@
size="tiny"
color-scheme="secondary"
icon="clipboard"
class-names="copy-button"
class-names="p-0"
@click="onCopy"
/>
</a>
<div
v-else
class="flex items-center mb-2 text-slate-800 dark:text-slate-100"
class="flex items-center gap-2 text-slate-800 dark:text-slate-100"
>
<emoji-or-icon
:icon="icon"
:emoji="emoji"
icon-size="14"
class="mr-2 ml-1 rtl:mr-1 rtl:ml-2"
class="ltr:ml-1 rtl:mr-1 flex-shrink-0"
/>
<span
v-if="value"
@@ -97,8 +97,3 @@ export default {
},
};
</script>
<style scoped>
.copy-button {
@apply ml-1 rtl:ml-0 rtl:mr-1;
}
</style>
@@ -1,7 +1,10 @@
<template>
<form class="conversation--form w-full" @submit.prevent="onFormSubmit">
<div v-if="showNoInboxAlert" class="callout warning">
<p>
<div
v-if="showNoInboxAlert"
class="relative mx-0 mt-0 mb-2.5 p-2 rounded-none text-sm border border-solid border-yellow-500 dark:border-yellow-700 bg-yellow-200/60 dark:bg-yellow-200/20 text-slate-700 dark:text-yellow-400"
>
<p class="mb-0">
{{ $t('NEW_CONVERSATION.NO_INBOX') }}
</p>
</div>
@@ -226,7 +229,7 @@
class="flex top-0 bottom-0 z-30 gap-2 right-0 left-0 items-center justify-center flex-col absolute w-full h-full bg-white/80 dark:bg-slate-700/80"
>
<fluent-icon icon="cloud-backup" size="40" />
<h4 class="page-sub-title text-slate-600 dark:text-slate-200">
<h4 class="text-2xl break-words text-slate-600 dark:text-slate-200">
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
</h4>
</div>
@@ -1,28 +1,9 @@
/* eslint arrow-body-style: 0 */
import { frontendURL } from '../../../helper/URLHelper';
const ConversationView = () => import('./ConversationView');
const InboxView = () => import('../inbox/InboxView.vue');
export default {
routes: [
{
path: frontendURL('accounts/:accountId/inbox-view'),
name: 'inbox_view',
roles: ['administrator', 'agent'],
component: InboxView,
props: () => {
return { inboxId: 0 };
},
},
{
path: frontendURL('accounts/:accountId/inbox-view/:conversation_id'),
name: 'inbox_view_conversation',
roles: ['administrator', 'agent'],
component: InboxView,
props: route => {
return { inboxId: 0, conversationId: route.params.conversation_id };
},
},
{
path: frontendURL('accounts/:accountId/dashboard'),
name: 'home',
@@ -18,6 +18,7 @@
:show-close="true"
:color="label.color"
variant="smooth"
class="max-w-[calc(100%-0.5rem)]"
@click="removeLabelFromConversation"
/>
@@ -3,6 +3,7 @@ import conversation from './conversation/conversation.routes';
import { routes as searchRoutes } from '../../modules/search/search.routes';
import { routes as contactRoutes } from './contacts/routes';
import { routes as notificationRoutes } from './notifications/routes';
import { routes as inboxRoutes } from './inbox/routes';
import { frontendURL } from '../../helper/URLHelper';
import helpcenterRoutes from './helpcenter/helpcenter.routes';
@@ -16,6 +17,7 @@ export default {
path: frontendURL('accounts/:account_id'),
component: AppContainer,
children: [
...inboxRoutes,
...conversation.routes,
...settings.routes,
...contactRoutes,
@@ -81,11 +81,11 @@ export default {
<style lang="scss" scoped>
.edit-article--container {
@apply my-8 mx-auto py-0 px-6 max-w-[56rem] w-full;
@apply my-8 mx-auto py-0 max-w-[56rem] w-full;
}
.article-heading {
@apply text-[2.5rem] font-semibold w-full text-slate-900 dark:text-slate-75 p-4 hover:bg-slate-25 dark:hover:bg-slate-800 hover:rounded-md resize-none min-h-[4rem] max-h-[40rem] h-auto mb-2 border-0 border-solid border-transparent dark:border-transparent;
@apply text-[2.5rem] font-semibold leading-normal w-full text-slate-900 dark:text-slate-75 p-4 hover:bg-slate-25 dark:hover:bg-slate-800 hover:rounded-md resize-none min-h-[4rem] max-h-[40rem] h-auto mb-2 border-0 border-solid border-transparent dark:border-transparent;
}
.article-content {
@@ -1,12 +1,19 @@
<template>
<div class="article-container--row">
<span class="article-column article-title">
<emoji-or-icon class="icon-grab" icon="grab-handle" />
<div class="article-block">
<div
class="text-slate-700 dark:text-slate-100 last:border-b-0 bg-white dark:bg-slate-900 my-0 -mx-4 grid grid-cols-1 lg:grid-cols-12 gap-4 border-b border-slate-50 dark:border-slate-800 px-6 py-3"
>
<span class="items-start flex gap-2 col-span-6 text-left">
<fluent-icon
v-if="showDragIcon"
size="20"
class="block cursor-move flex-shrink-0 h-4 mt-1 w-4 text-slate-200 dark:text-slate-700 hover:text-slate-400 hover:dark:text-slate-200"
icon="grab-handle"
/>
<div class="flex flex-col truncate">
<router-link :to="articleUrl(id)">
<h6
:title="title"
class="text-base ltr:text-left rtl:text-right text-slate-800 dark:text-slate-100 mb-0 leading-6 h-6 hover:underline overflow-hidden whitespace-nowrap text-ellipsis"
class="text-base ltr:text-left rtl:text-right text-slate-800 dark:text-slate-100 mb-0.5 leading-6 font-medium hover:underline overflow-hidden whitespace-nowrap text-ellipsis"
>
{{ title }}
</h6>
@@ -16,14 +23,14 @@
v-if="author"
:src="author.thumbnail"
:username="author.name"
size="16px"
size="14px"
/>
<div
v-else
v-tooltip.right="
$t('HELP_CENTER.TABLE.COLUMNS.AUTHOR_NOT_AVAILABLE')
"
class="flex items-center justify-center rounded w-4 h-4 bg-woot-100 dark:bg-woot-700"
class="flex items-center justify-center rounded w-3.5 h-3.5 bg-woot-100 dark:bg-woot-700"
>
<fluent-icon
icon="person"
@@ -32,61 +39,64 @@
class="text-woot-300 dark:text-woot-300"
/>
</div>
<span class="font-normal text-slate-500 dark:text-slate-200 text-sm">
<span class="font-normal text-slate-700 dark:text-slate-200 text-sm">
{{ articleAuthorName }}
</span>
</div>
</div>
</span>
<span class="article-column article-category">
<span class="flex items-center col-span-2">
<router-link
class="fs-small button clear link secondary"
class="text-sm hover:underline p-0.5 truncate hover:bg-slate-25 hover:rounded-md"
:to="getCategoryRoute(category.slug)"
>
<span
:title="category.name"
class="category-link-content overflow-hidden whitespace-nowrap text-ellipsis"
>
<span :title="category.name">
{{ category.name }}
</span>
</router-link>
</span>
<span class="article-column article-read-count">
<span class="fs-small" :title="formattedViewCount">
{{ readableViewCount }}
<span
class="flex items-center text-xs lg:text-sm"
:title="formattedViewCount"
>
{{ readableViewCount }}
<span class="lg:hidden ml-1">
{{ ` ${$t('HELP_CENTER.TABLE.HEADERS.READ_COUNT')}` }}
</span>
</span>
<span class="article-column article-status">
<div>
<woot-label
:title="status"
size="small"
variant="smooth"
:color-scheme="labelColor"
/>
</div>
<span class="flex items-center capitalize">
<woot-label
class="!mb-0"
:title="status"
size="small"
variant="smooth"
:color-scheme="labelColor"
/>
</span>
<span class="article-column article-last-edited">
<span class="fs-small">
{{ lastUpdatedAt }}
</span>
<span
class="flex items-center justify-end col-span-2 first-letter:uppercase text-slate-700 dark:text-slate-100 text-xs"
>
{{ lastUpdatedAt }}
</span>
</div>
</template>
<script>
import timeMixin from 'dashboard/mixins/time';
import portalMixin from '../mixins/portalMixin';
import { frontendURL } from 'dashboard/helper/URLHelper';
import EmojiOrIcon from '../../../../../shared/components/EmojiOrIcon.vue';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
export default {
components: {
EmojiOrIcon,
Thumbnail,
},
mixins: [timeMixin, portalMixin],
props: {
showDragIcon: {
type: Boolean,
default: false,
},
id: {
type: Number,
required: true,
@@ -156,65 +166,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container--row {
@apply bg-white dark:bg-slate-900 my-0 -mx-4 py-0 px-4 grid grid-cols-8 gap-4 border-b border-slate-50 dark:border-slate-800;
@media (max-width: 1024px) {
@apply grid-cols-7;
}
@media (max-width: 768px) {
@apply grid-cols-6;
}
&.draggable {
span.article-column.article-title {
@apply -ml-2;
.icon-grab {
@apply block cursor-move h-4 mt-1 w-4 text-slate-100 dark:text-slate-700;
&:hover {
@apply text-slate-300 dark:text-slate-200;
}
}
}
}
span.article-column {
@apply text-slate-700 dark:text-slate-100 text-sm font-semibold py-2 px-0 text-left capitalize last:text-right;
&.article-title {
@apply items-start flex gap-2 col-span-4 text-left;
.icon-grab {
@apply hidden;
}
}
// for screen sizes smaller than 1024px
@media (max-width: 63.9375em) {
&.article-read-count {
@apply hidden;
}
}
@media (max-width: 47.9375em) {
&.article-read-count,
&.article-last-edited {
@apply hidden;
}
}
}
.article-block {
@apply min-w-0;
}
}
span {
@apply font-normal text-slate-700 dark:text-slate-100 text-sm pl-0;
}
</style>
@@ -4,14 +4,14 @@
@click="handlePreview"
>
<h4
class="text-block-title text-left mb-0 text-slate-900 dark:text-slate-25 px-1 -mx-1 rounded-sm width-auto hover:underline group-hover:underline"
class="text-sm ltr:text-left rtl:text-right w-full mb-0 text-slate-900 dark:text-slate-25 -mx-1 rounded-sm hover:underline group-hover:underline"
>
{{ title }}
</h4>
<div class="flex content-between items-center gap-0.5 w-full">
<p
class="text-sm text-left text-slate-600 dark:text-slate-300 mb-0 w-full"
class="text-sm ltr:text-left rtl:text-right text-slate-600 dark:text-slate-300 mb-0 w-full"
>
{{ locale }}
{{ ` / ` }}
@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col py-1">
<div class="flex-container align-middle align-justify py-2">
<h3 class="sub-block-title text-slate-900 dark:text-slate-25">
<div class="flex items-center justify-between py-2 mb-1">
<h3 class="text-base text-slate-900 dark:text-slate-25">
{{ title }}
</h3>
<woot-button
@@ -14,14 +14,16 @@
</div>
<div class="relative">
<div class="absolute left-0 w-8 h-8 flex justify-center items-center">
<fluent-icon icon="search" class="" size="16" />
<div
class="absolute ltr:left-0 rtl:right-0 w-8 top-0.5 h-8 flex justify-center items-center"
>
<fluent-icon icon="search" class="" size="18" />
</div>
<input
ref="searchInput"
type="text"
:placeholder="$t('HELP_CENTER.ARTICLE_SEARCH.PLACEHOLDER')"
class="block w-full pl-8 h-8 text-sm dark:bg-slate-700 bg-slate-25 rounded-md leading-8 py-1 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-woot-200 mb-0 focus:bg-slate-25 dark:focus:bg-slate-700 dark:focus:ring-woot-700"
class="block w-full !h-9 ltr:!pl-8 rtl:!pr-8 dark:!bg-slate-700 !bg-slate-25 text-sm rounded-md leading-8 text-slate-700 shadow-sm ring-2 ring-transparent ring-slate-300 border border-solid border-slate-300 placeholder:text-slate-400 focus:border-woot-600 focus:ring-woot-200 !mb-0 focus:bg-slate-25 dark:focus:bg-slate-700 dark:focus:ring-woot-700"
:value="searchQuery"
@focus="onFocus"
@blur="onBlur"
@@ -1,38 +1,38 @@
<template>
<div class="w-full">
<div class="flex-1">
<div
class="my-0 py-0 px-4 grid grid-cols-6 z-10 md:grid-cols-7 lg:grid-cols-8 gap-4 border-b border-slate-100 dark:border-slate-700 sticky top-16 bg-white dark:bg-slate-900"
class="hidden lg:grid py-0 px-6 h-12 content-center grid-cols-12 z-10 gap-4 border-b border-slate-50 dark:border-slate-700 sticky top-16 bg-white dark:bg-slate-900"
:class="{ draggable: onCategoryPage }"
>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 ltr:text-left rtl:text-right col-span-4"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right col-span-6"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.TITLE') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right col-span-2"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.CATEGORY') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left hidden lg:block"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right hidden lg:block"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.READ_COUNT') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-left rtl:text-right"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.STATUS') }}
</div>
<div
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-right hidden md:block"
class="font-semibold capitalize text-sm py-2 px-0 text-slate-700 dark:text-slate-100 text-right rtl:text-left hidden md:block col-span-2"
>
{{ $t('HELP_CENTER.TABLE.HEADERS.LAST_EDITED') }}
</div>
</div>
<draggable
tag="div"
class="border-t-0 px-4"
class="border-t-0 px-4 pb-4"
:disabled="!dragEnabled"
:list="localArticles"
ghost-class="article-ghost-class"
@@ -46,6 +46,7 @@
:class="{ draggable: onCategoryPage }"
:title="article.title"
:author="article.author"
:show-drag-icon="dragEnabled"
:category="article.category"
:views="article.views"
:status="article.status"
@@ -58,7 +59,7 @@
:current-page="currentPage"
:total-count="totalCount"
:page-size="pageSize"
class="dark:bg-slate-900 sticky bottom-0 border-t-0 px-4"
class="dark:bg-slate-900 sticky bottom-0"
@page-change="onPageChange"
/>
</div>
@@ -78,7 +79,7 @@ export default {
props: {
articles: {
type: Array,
default: () => {},
default: () => [],
},
totalCount: {
type: Number,
@@ -95,7 +96,7 @@ export default {
},
data() {
return {
localArticles: [],
localArticles: this.articles || [],
};
},
computed: {
@@ -155,6 +156,10 @@ export default {
};
</script>
<style lang="scss" scoped>
/*
The .article-ghost-class class is maintained as the vueDraggable doesn't allow multiple classes
to be passed in the ghost-class prop.
*/
.article-ghost-class {
@apply opacity-50 bg-slate-50 dark:bg-slate-800;
}
@@ -1,14 +1,14 @@
<template>
<div
class="flex px-4 items-center justify-between w-full h-16 pt-2 sticky top-0 z-50 bg-white dark:bg-slate-900"
class="flex p-6 items-center justify-between w-full h-16 sticky top-0 z-50 bg-white dark:bg-slate-900"
>
<div class="flex items-center">
<woot-sidemenu-icon />
<div class="flex items-center my-0 mx-2">
<h3 class="text-2xl text-slate-800 dark:text-slate-100 mb-0">
<h3 class="text-xl text-slate-800 dark:text-slate-100 font-medium mb-0">
{{ headerTitle }}
</h3>
<span class="text-sm text-slate-600 dark:text-slate-300 my-0 mx-2">{{
<span class="text-sm text-slate-600 dark:text-slate-300 mx-2 mt-0.5">{{
`(${count})`
}}</span>
</div>
@@ -232,6 +232,7 @@ export default {
.dropdown-pane--open {
@apply absolute top-10 right-0 z-50 min-w-[8rem];
}
.dropdown-arrow {
@apply ml-1 rtl:ml-0 rtl:mr-1;
}
@@ -1,5 +1,5 @@
<template>
<div class="flex items-center justify-between w-full h-16 pt-2">
<div class="flex items-center justify-between w-full h-16">
<div class="flex items-center">
<woot-button
icon="chevron-left"
@@ -240,12 +240,15 @@ export default {
@apply absolute right-0;
}
}
.draft-status {
animation: fadeIn 1s;
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
@@ -85,7 +85,7 @@
size="tiny"
variant="smooth"
icon="delete"
color-scheme="secondary"
color-scheme="alert"
:disabled="locale.code === selectedLocaleCode"
@click="deleteLocale(locale.code)"
/>
@@ -1,7 +1,9 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<div class="w-full">
<h3 class="block-title text-black-900 dark:text-slate-200">
<h3 class="text-lg text-black-900 dark:text-slate-200">
{{
$t(
'HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.BASIC_SETTINGS_PAGE.TITLE'
@@ -75,7 +77,7 @@
</div>
</div>
</div>
<div class="flex-end">
<div class="flex justify-end">
<woot-button
:is-loading="isSubmitting"
:is-disabled="$v.$invalid"
@@ -243,10 +245,6 @@ export default {
};
</script>
<style lang="scss" scoped>
.wizard-body {
@apply pt-3 border border-solid border-transparent dark:border-transparent;
}
::v-deep {
input {
@apply mb-1;
@@ -1,7 +1,9 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%] h-auto">
<div
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<div class="w-full">
<h3 class="block-title text-black-900 dark:text-slate-200">
<h3 class="text-lg text-black-900 dark:text-slate-200">
{{
$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.CUSTOMIZATION_PAGE.TITLE')
}}
@@ -57,7 +59,7 @@
</div>
</div>
</div>
<div class="flex-end">
<div class="flex justify-end">
<woot-button
:is-loading="isSubmitting"
:is-disabled="$v.$invalid"
@@ -150,10 +152,6 @@ export default {
};
</script>
<style lang="scss" scoped>
.wizard-body {
@apply pt-3 border border-solid border-transparent dark:border-transparent;
}
::v-deep {
input {
@apply mb-1;
@@ -44,7 +44,7 @@
</span>
<span
v-if="isLocaleDefault(locale.code)"
class="fs-small text-slate-300 dark:text-slate-200"
class="text-sm text-slate-300 dark:text-slate-200"
>
{{ `(${$t('HELP_CENTER.PORTAL.DEFAULT')})` }}
</span>
@@ -1,18 +1,19 @@
<template>
<div
class="h-full overflow-auto w-60 flex flex-col bg-white dark:bg-slate-900 border-r dark:border-slate-700 rtl:border-r-0 rtl:border-l border-slate-50 text-sm px-2 pb-8"
class="h-full overflow-auto w-60 flex flex-col bg-white dark:bg-slate-900 border-r dark:border-slate-700 rtl:border-r-0 rtl:border-l border-slate-50 text-sm"
>
<sidebar-header
:thumbnail-src="thumbnailSrc"
:header-title="headerTitle"
:sub-title="subTitle"
:portal-link="portalLink"
class="px-4"
@open-popover="openPortalPopover"
/>
<transition-group
name="menu-list"
tag="ul"
class="pt-2 list-none ml-0 mb-0"
class="py-2 px-4 list-none ml-0 mb-0"
>
<secondary-nav-item
v-for="menuItem in accessibleMenuItems"
@@ -75,9 +76,6 @@ export default {
default: () => [],
},
},
data() {
return {};
},
computed: {
hasCategory() {
return (
@@ -1,10 +1,12 @@
<template>
<transition name="popover-animation">
<div class="article-settings--container">
<div
class="min-w-[15rem] max-w-[22.5rem] p-6 overflow-y-auto border-l rtl:border-r rtl:border-l-0 border-solid border-slate-50 dark:border-slate-700"
>
<h3 class="text-base text-slate-800 dark:text-slate-100">
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.TITLE') }}
</h3>
<div class="form-wrap">
<div class="mt-4 mb-6">
<label>
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.FORM.CATEGORY.LABEL') }}
<multiselect-dropdown
@@ -64,6 +66,7 @@
{{ $t('HELP_CENTER.ARTICLE_SETTINGS.FORM.META_DESCRIPTION.LABEL') }}
<textarea
v-model="metaDescription"
class="text-sm"
rows="3"
type="text"
:placeholder="
@@ -95,7 +98,7 @@
/>
</label>
</div>
<div class="action-buttons">
<div class="flex flex-col">
<woot-button
icon="archive"
size="small"
@@ -241,35 +244,24 @@ export default {
</script>
<style lang="scss" scoped>
.article-settings--container {
@apply flex-[0.3] min-w-[15rem] max-w-[22.5rem] py-2 pl-4 rtl:pl-0 rtl:pr-4 ml-4 rtl:ml-0 rtl:mr-4 overflow-y-auto border-l rtl:border-r rtl:border-l-0 border-solid border-slate-50 dark:border-slate-700;
.form-wrap {
@apply mt-4 mb-6;
textarea {
@apply text-sm;
}
}
.action-buttons {
@apply flex flex-col;
}
}
::v-deep {
.multiselect {
@apply mb-0;
}
.multiselect__content-wrapper {
@apply hidden;
}
.multiselect--active .multiselect__tags {
padding-right: var(--space-small) !important;
@apply rounded-md;
}
.multiselect__placeholder {
@apply text-slate-300 dark:text-slate-200 pt-2 mb-0;
}
.multiselect__select {
@apply hidden;
}
@@ -5,31 +5,58 @@
Loading...
</div>
</template>
<script>
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import { mapGetters } from 'vuex';
export default {
mixins: [uiSettingsMixin],
computed: {
...mapGetters({ portals: 'portals/allPortals' }),
},
mounted() {
const {
last_active_portal_slug: lastActivePortalSlug,
last_active_locale_code: lastActiveLocaleCode,
} = this.uiSettings || {};
this.performRouting();
},
methods: {
isPortalPresent(portalSlug) {
return !!this.portals.find(portal => portal.slug === portalSlug);
},
async performRouting() {
await this.$store.dispatch('portals/index');
this.$nextTick(() => this.routeToLastActivePortal());
},
routeToView(name, params) {
this.$router.replace({ name, params, replace: true });
},
async routeToLastActivePortal() {
// TODO: This method should be written as a navigation guard rather than
// a method in the component.
const {
last_active_portal_slug: lastActivePortalSlug,
last_active_locale_code: lastActiveLocaleCode,
} = this.uiSettings || {};
if (lastActivePortalSlug)
this.$router.replace({
name: 'list_all_locale_articles',
params: {
if (this.isPortalPresent(lastActivePortalSlug)) {
// Check if the last active portal from the user preferences is available in the current
// list of portals. If it is, navigate there. The last active portal is saved in the user's
// UI settings, regardless of the account. Consequently, it's possible that the saved portal
// slug is not available in the current account.
this.routeToView('list_all_locale_articles', {
portalSlug: lastActivePortalSlug,
locale: lastActiveLocaleCode,
},
replace: true,
});
else
this.$router.replace({
name: 'list_all_portals',
replace: true,
});
});
} else if (this.portals.length > 0) {
// If the last active portal is available, check for the exisiting list of portals and
// navigate to the first available portal.
const { slug: portalSlug, meta: { default_locale: locale } = {} } =
this.portals[0];
this.routeToView('list_all_locale_articles', { portalSlug, locale });
} else {
// If no portals are available, navigate to the portal list page to prompt creation.
this.$router.replace({ name: 'list_all_portals', replace: true });
}
},
},
};
</script>
@@ -1,8 +1,8 @@
<template>
<div class="article-container">
<div class="article-container flex w-full overflow-auto">
<div
class="edit-article--container"
:class="{ 'is-sidebar-open': showArticleSettings }"
class="flex-1 flex-shrink-0 overflow-auto px-6"
:class="{ 'flex-grow-1 flex-shrink-0': showArticleSettings }"
>
<edit-article-header
:back-button-label="$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES')"
@@ -15,7 +15,7 @@
@show="showArticleInPortal"
@update-meta="updateMeta"
/>
<div v-if="isFetching" class="text-center p-normal fs-default h-full">
<div v-if="isFetching" class="text-center p-4 text-base h-full">
<spinner size="" />
<span>{{ $t('HELP_CENTER.EDIT_ARTICLE.LOADING') }}</span>
</div>
@@ -45,6 +45,7 @@
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditArticleHeader from '../../components/Header/EditArticleHeader.vue';
@@ -107,7 +108,11 @@ export default {
},
methods: {
onClickGoBack() {
this.$router.push({ name: 'list_all_locale_articles' });
if (window.history.length > 2) {
this.$router.go(-1);
} else {
this.$router.push({ name: 'list_all_locale_articles' });
}
},
fetchArticleDetails() {
this.$store.dispatch('articles/show', {
@@ -210,24 +215,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container {
display: flex;
padding: 0 var(--space-normal);
width: 100%;
flex: 1;
overflow: auto;
.edit-article--container {
flex: 1;
flex-shrink: 0;
overflow: auto;
}
.is-sidebar-open {
flex-grow: 1;
flex-shrink: 0;
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<div
class="py-0 px-0 w-full max-w-full overflow-auto bg-white dark:bg-slate-900"
class="py-0 px-0 w-full max-w-full overflow-auto bg-white dark:bg-slate-900 flex flex-col"
>
<article-header
:header-title="headerTitle"
@@ -8,31 +8,35 @@
:selected-locale="activeLocaleName"
:all-locales="allowedLocales"
selected-value="Published"
class="border-b border-slate-50 dark:border-slate-700"
@new-article-page="newArticlePage"
@change-locale="onChangeLocale"
/>
<article-table
:articles="articles"
:current-page="Number(meta.currentPage)"
:total-count="Number(meta.count)"
@page-change="onPageChange"
@reorder="onReorder"
/>
<div
v-if="shouldShowLoader"
v-if="isFetching"
class="items-center flex text-base justify-center py-6 px-4 text-slate-600 dark:text-slate-200"
>
<spinner />
<span class="text-slate-600 dark:text-slate-200">{{
$t('HELP_CENTER.TABLE.LOADING_MESSAGE')
}}</span>
<span class="text-slate-600 dark:text-slate-200">
{{ $t('HELP_CENTER.TABLE.LOADING_MESSAGE') }}
</span>
</div>
<empty-state
v-else-if="shouldShowEmptyState"
:title="$t('HELP_CENTER.TABLE.NO_ARTICLES')"
/>
<div v-else class="flex flex-1">
<article-table
:articles="articles"
:current-page="Number(meta.currentPage)"
:total-count="Number(meta.count)"
@page-change="onPageChange"
@reorder="onReorder"
/>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import allLocales from 'shared/constants/locales.js';
@@ -72,9 +76,6 @@ export default {
shouldShowEmptyState() {
return !this.isFetching && !this.articles.length;
},
shouldShowLoader() {
return this.isFetching && !this.articles.length;
},
selectedPortalSlug() {
return this.$route.params.portalSlug;
},
@@ -1,8 +1,8 @@
<template>
<div class="article-container">
<div class="flex flex-1 overflow-auto">
<div
class="new-article--container"
:class="{ 'is-sidebar-open': showArticleSettings }"
class="flex-1 overflow-y-auto flex-shrink-0 px-6"
:class="{ 'flex-grow-1': showArticleSettings }"
>
<edit-article-header
:back-button-label="$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES')"
@@ -22,6 +22,7 @@
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditArticleHeader from 'dashboard/routes/dashboard/helpcenter/components/Header/EditArticleHeader.vue';
@@ -116,22 +117,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article-container {
display: flex;
padding: 0 var(--space-normal);
width: 100%;
flex: 1;
overflow: auto;
}
.new-article--container {
flex: 1;
flex-shrink: 0;
overflow-y: auto;
}
.is-sidebar-open {
flex: 0.7;
flex-grow: 1;
}
</style>
@@ -2,7 +2,7 @@
<div class="flex items-center relative">
<woot-button
variant="hollow"
class="absolute [&>span]:flex [&>span]:items-center [&>span]:justify-center z-10 top-[25px] h-[2.45rem] w-[2.45rem] !text-slate-400 dark:!text-slate-600 dark:!bg-slate-900 !p-0"
class="absolute [&>span]:flex [&>span]:items-center [&>span]:justify-center z-10 top-[28px] h-[2.5rem] w-[2.45rem] !text-slate-400 dark:!text-slate-600 dark:!bg-slate-900 !p-0"
color-scheme="secondary"
@click="toggleEmojiPicker"
>
@@ -3,7 +3,9 @@
<div class="flex justify-between items-center mt-0 mb-2 mx-0 h-12">
<div class="flex items-center">
<woot-sidemenu-icon />
<h1 class="my-0 mx-2 text-2xl text-slate-800 dark:text-slate-100">
<h1
class="my-0 mx-2 text-2xl text-slate-800 dark:text-slate-100 font-medium"
>
{{ $t('HELP_CENTER.PORTAL.HEADER') }}
</h1>
</div>
@@ -13,7 +13,7 @@
class="flex flex-row overflow-auto py-4 pl-4 rtl:pl-0 rtl:pr-4 h-full bg-slate-50 dark:bg-slate-800"
>
<woot-wizard
class="hide-for-small-only w-[25%]"
class="hidden md:block w-1/4"
:global-config="globalConfig"
:items="items"
/>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%] h-auto">
<div
class="pt-3 bg-white dark:bg-slate-900 h-full border border-solid border-transparent px-6 pb-6 dark:border-transparent w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<empty-state
:title="$t('HELP_CENTER.PORTAL.ADD.CREATE_FLOW_PAGE.FINISH_PAGE.TITLE')"
:message="
@@ -8,7 +10,7 @@
>
<div class="w-full text-center">
<router-link
class="button success nice"
class="button success nice rounded"
:to="{
name: 'list_all_portals',
}"
@@ -35,8 +37,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.wizard-body {
@apply pb-3;
}
</style>
@@ -0,0 +1,40 @@
<template>
<div
class="text-center bg-slate-25 dark:bg-slate-800 justify-center w-full h-full hidden md:flex items-center"
>
<span v-if="uiFlags.isFetching" class="spinner my-4" />
<div v-else class="flex flex-col items-center gap-2">
<fluent-icon
icon="mail-inbox"
size="40"
class="text-slate-600 dark:text-slate-400"
/>
<span class="text-slate-500 text-sm font-medium dark:text-slate-300">
{{ emptyMessage }}
</span>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
props: {
emptyStateMessage: {
type: String,
default: '',
},
},
computed: {
...mapGetters({
uiFlags: 'notifications/getUIFlags',
}),
emptyMessage() {
if (this.emptyStateMessage) {
return this.emptyStateMessage;
}
return this.$t('INBOX.LIST.NOTE');
},
},
};
</script>
@@ -1,43 +1,47 @@
<template>
<div
class="flex flex-col h-full w-full ltr:border-r border-slate-50 dark:border-slate-800/50"
:class="isOnExpandedLayout ? '' : 'min-w-[360px] max-w-[360px]'"
>
<inbox-list-header @filter="onFilterChange" />
<section class="flex w-full h-full bg-white dark:bg-slate-900">
<div
ref="notificationList"
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
class="flex flex-col h-full w-full md:min-w-[360px] md:max-w-[360px] ltr:border-r border-slate-50 dark:border-slate-800/50"
:class="!currentNotificationId ? 'flex' : 'hidden md:flex'"
>
<inbox-card
v-for="notificationItem in notifications"
:key="notificationItem.id"
:notification-item="notificationItem"
@mark-notification-as-read="markNotificationAsRead"
@mark-notification-as-unread="markNotificationAsUnRead"
@delete-notification="deleteNotification"
<inbox-list-header
:is-context-menu-open="isInboxContextMenuOpen"
@filter="onFilterChange"
@redirect="redirectToInbox"
/>
<div v-if="uiFlags.isFetching" class="text-center">
<span class="spinner mt-4 mb-4" />
<div
ref="notificationList"
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
>
<inbox-card
v-for="notificationItem in notifications"
:key="notificationItem.id"
:active="currentNotificationId === notificationItem.id"
:notification-item="notificationItem"
@mark-notification-as-read="markNotificationAsRead"
@mark-notification-as-unread="markNotificationAsUnRead"
@delete-notification="deleteNotification"
@context-menu-open="isInboxContextMenuOpen = true"
@context-menu-close="isInboxContextMenuOpen = false"
/>
<div v-if="uiFlags.isFetching" class="text-center">
<span class="spinner mt-4 mb-4" />
</div>
<p
v-if="showEmptyState"
class="text-center text-slate-400 text-sm dark:text-slate-400 p-4 font-medium"
>
{{ $t('INBOX.LIST.NO_NOTIFICATIONS') }}
</p>
<intersection-observer
v-if="!showEndOfList && !uiFlags.isFetching"
:options="infiniteLoaderOptions"
@observed="loadMoreNotifications"
/>
</div>
<p
v-if="showEmptyState"
class="text-center text-slate-400 text-sm dark:text-slate-400 p-4 font-medium"
>
{{ $t('INBOX.LIST.NO_NOTIFICATIONS') }}
</p>
<p
v-if="showEndOfListMessage"
class="text-center text-slate-400 dark:text-slate-400 p-4"
>
{{ $t('INBOX.LIST.EOF') }}
</p>
<intersection-observer
v-if="!showEndOfList && !uiFlags.isFetching"
:options="infiniteLoaderOptions"
@observed="loadMoreNotifications"
/>
</div>
</div>
<router-view />
</section>
</template>
<script>
@@ -49,6 +53,7 @@ import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
import alertMixin from 'shared/mixins/alertMixin';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
export default {
components: {
InboxCard,
@@ -56,16 +61,6 @@ export default {
IntersectionObserver,
},
mixins: [alertMixin, uiSettingsMixin],
props: {
conversationId: {
type: [String, Number],
default: 0,
},
isOnExpandedLayout: {
type: Boolean,
default: false,
},
},
data() {
return {
infiniteLoaderOptions: {
@@ -76,6 +71,8 @@ export default {
status: '',
type: '',
sortOrder: wootConstants.INBOX_SORT_BY.NEWEST,
isInboxContextMenuOpen: false,
notificationIdToSnooze: null,
};
},
computed: {
@@ -85,6 +82,9 @@ export default {
uiFlags: 'notifications/getUIFlags',
notification: 'notifications/getFilteredNotifications',
}),
currentNotificationId() {
return Number(this.$route.params.notification_id);
},
inboxFilters() {
return {
page: this.page,
@@ -102,9 +102,6 @@ export default {
showEmptyState() {
return !this.uiFlags.isFetching && !this.notifications.length;
},
showEndOfListMessage() {
return this.showEndOfList && this.notifications.length;
},
},
mounted() {
this.setSavedFilter();
@@ -115,13 +112,11 @@ export default {
this.page = 1;
this.$store.dispatch('notifications/clear');
const filter = this.inboxFilters;
this.$store.dispatch('notifications/index', filter);
},
redirectToInbox() {
if (!this.conversationId) return;
if (this.$route.name === 'inbox_view') return;
this.$router.push({ name: 'inbox_view' });
this.$router.replace({ name: 'inbox_view' });
},
loadMoreNotifications() {
if (this.uiFlags.isAllNotificationsLoaded) return;
@@ -177,13 +172,14 @@ export default {
});
},
onFilterChange(option) {
if (option.type === wootConstants.INBOX_FILTER_TYPE.STATUS) {
const { STATUS, TYPE, SORT_ORDER } = wootConstants.INBOX_FILTER_TYPE;
if (option.type === STATUS) {
this.status = option.selected ? option.key : '';
}
if (option.type === wootConstants.INBOX_FILTER_TYPE.TYPE) {
if (option.type === TYPE) {
this.type = option.selected ? option.key : '';
}
if (option.type === wootConstants.INBOX_FILTER_TYPE.SORT_ORDER) {
if (option.type === SORT_ORDER) {
this.sortOrder = option.key;
}
this.fetchNotifications();
@@ -1,127 +1,102 @@
<template>
<section class="flex w-full h-full bg-white dark:bg-slate-900">
<inbox-list
v-show="showConversationList"
:conversation-id="conversationId"
:is-on-expanded-layout="isOnExpandedLayout"
/>
<div
v-if="showInboxMessageView"
class="flex flex-col h-full"
:class="isOnExpandedLayout ? 'w-full' : 'w-[calc(100%-360px)]'"
>
<div class="h-full w-full md:w-[calc(100%-360px)]">
<div v-if="showEmptyState" class="flex w-full h-full">
<inbox-empty-state
:empty-state-message="$t('INBOX.LIST.NO_MESSAGES_AVAILABLE')"
/>
</div>
<div v-else class="flex flex-col h-full w-full">
<inbox-item-header
:total-length="totalNotifications"
class="flex-1"
:total-length="totalNotificationCount"
:current-index="activeNotificationIndex"
:active-notification="activeNotification"
@next="onClickNext"
@prev="onClickPrev"
/>
<div
v-if="isConversationLoading"
class="flex items-center h-[calc(100%-56px)] justify-center bg-slate-25 dark:bg-slate-800"
>
<span class="spinner my-4" />
</div>
<conversation-box
v-else
class="h-[calc(100%-56px)]"
is-inbox-view
:inbox-id="inboxId"
:is-contact-panel-open="isContactPanelOpen"
:is-on-expanded-layout="isOnExpandedLayout"
:is-on-expanded-layout="false"
@contact-panel-toggle="onToggleContactPanel"
/>
</div>
<div
v-if="!showInboxMessageView && !isOnExpandedLayout"
class="text-center bg-slate-25 dark:bg-slate-800 justify-center w-full h-full flex items-center"
>
<span v-if="uiFlags.isFetching" class="spinner mt-4 mb-4" />
<div v-else class="flex flex-col items-center gap-2">
<fluent-icon
icon="mail-inbox"
size="40"
class="text-slate-600 dark:text-slate-400"
/>
<span class="text-slate-500 text-sm font-medium dark:text-slate-300">
{{ $t('INBOX.LIST.NOTE') }}
</span>
</div>
</div>
</section>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import InboxList from './InboxList.vue';
import InboxItemHeader from './components/InboxItemHeader.vue';
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
import InboxEmptyState from './InboxEmptyState.vue';
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
import wootConstants from 'dashboard/constants/globals';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import { BUS_EVENTS } from 'shared/constants/busEvents';
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
export default {
components: {
InboxList,
InboxItemHeader,
InboxEmptyState,
ConversationBox,
},
mixins: [uiSettingsMixin],
props: {
inboxId: {
type: [String, Number],
default: 0,
},
conversationId: {
type: [String, Number],
default: 0,
},
data() {
return {
isConversationLoading: false,
};
},
computed: {
...mapGetters({
currentAccountId: 'getCurrentAccountId',
notifications: 'notifications/getNotifications',
notification: 'notifications/getFilteredNotifications',
currentChat: 'getSelectedChat',
allConversation: 'getAllConversations',
activeNotificationById: 'notifications/getNotificationById',
conversationById: 'getConversationById',
uiFlags: 'notifications/getUIFlags',
meta: 'notifications/getMeta',
}),
notifications() {
return this.notification({
sortOrder: this.activeSortOrder,
});
},
inboxId() {
return Number(this.$route.params.inboxId);
},
notificationId() {
return Number(this.$route.params.notification_id);
},
activeNotification() {
return this.notifications.find(
n => n.primary_actor.id === Number(this.conversationId)
);
return this.activeNotificationById(this.notificationId);
},
isInboxViewEnabled() {
return this.$store.getters['accounts/isFeatureEnabledGlobally'](
this.currentAccountId,
FEATURE_FLAGS.INBOX_VIEW
);
conversationId() {
return this.activeNotification?.primary_actor?.id;
},
showConversationList() {
return this.isOnExpandedLayout ? !this.conversationId : true;
totalNotificationCount() {
return this.meta.count;
},
isFetchingInitialData() {
return this.uiFlags.isFetching && !this.notifications.length;
},
showInboxMessageView() {
showEmptyState() {
return (
Boolean(this.conversationId) &&
Boolean(this.currentChat.id) &&
!this.isFetchingInitialData
!this.conversationId ||
(!this.notifications?.length && this.uiFlags.isFetching)
);
},
totalNotifications() {
return this.notifications?.length ?? 0;
},
activeNotificationIndex() {
const conversationId = Number(this.conversationId);
const notificationIndex = this.notifications.findIndex(
n => n.primary_actor.id === conversationId
);
return notificationIndex >= 0 ? notificationIndex + 1 : 0;
return this.notifications?.findIndex(n => n.id === this.notificationId);
},
isOnExpandedLayout() {
const {
LAYOUT_TYPES: { CONDENSED },
} = wootConstants;
const { conversation_display_type: conversationDisplayType = CONDENSED } =
this.uiSettings;
return conversationDisplayType !== CONDENSED;
activeSortOrder() {
const { inbox_filter_by: filterBy = {} } = this.uiSettings;
const { sort_by: sortBy } = filterBy;
return sortBy || 'desc';
},
isContactPanelOpen() {
if (this.currentChat.id) {
@@ -135,29 +110,29 @@ export default {
watch: {
conversationId: {
immediate: true,
handler() {
this.fetchConversationById();
handler(newVal, oldVal) {
if (newVal !== oldVal) {
this.fetchConversationById();
}
},
},
},
mounted() {
// Open inbox view if inbox view feature is enabled, else redirect to dashboard
// TODO: Remove this code once inbox view feature is enabled for all accounts
if (!this.isInboxViewEnabled) {
this.$router.push({
name: 'home',
});
}
this.$store.dispatch('agents/get');
},
methods: {
async fetchConversationById() {
if (!this.conversationId) return;
const chat = this.findConversation();
if (!chat) {
await this.$store.dispatch('getConversation', this.conversationId);
if (!this.notificationId || !this.conversationId) return;
this.$store.dispatch('clearSelectedState');
const existingChat = this.findConversation();
if (existingChat) {
this.setActiveChat(existingChat);
return;
}
this.isConversationLoading = true;
await this.$store.dispatch('getConversation', this.conversationId);
this.setActiveChat();
this.isConversationLoading = false;
},
setActiveChat() {
const selectedConversation = this.findConversation();
@@ -169,37 +144,47 @@ export default {
});
},
findConversation() {
const conversationId = Number(this.conversationId);
return this.allConversation.find(c => c.id === conversationId);
return this.conversationById(this.conversationId);
},
navigateToConversation(activeIndex, direction) {
const indexOffset = direction === 'next' ? 0 : -2;
const targetNotification = this.notifications[activeIndex + indexOffset];
if (targetNotification) {
const {
id,
primary_actor_id: primaryActorId,
primary_actor_type: primaryActorType,
primary_actor: { id: conversationId, meta: { unreadCount } = {} },
notification_type: notificationType,
} = targetNotification;
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
notificationType,
});
this.$store.dispatch('notifications/read', {
id,
primaryActorId,
primaryActorType,
unreadCount,
});
this.$router.push({
name: 'inbox_view_conversation',
params: { conversation_id: conversationId },
});
let updatedIndex;
if (direction === 'prev' && activeIndex) {
updatedIndex = activeIndex - 1;
} else if (
direction === 'next' &&
activeIndex < this.totalNotificationCount
) {
updatedIndex = activeIndex + 1;
}
const targetNotification = this.notifications[updatedIndex];
if (targetNotification) {
this.openNotification(targetNotification);
}
},
openNotification(notification) {
const {
id,
primary_actor_id: primaryActorId,
primary_actor_type: primaryActorType,
primary_actor: { meta: { unreadCount } = {} },
notification_type: notificationType,
} = notification;
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
notificationType,
});
this.$store.dispatch('notifications/read', {
id,
primaryActorId,
primaryActorType,
unreadCount,
});
this.$router.push({
name: 'inbox_view_conversation',
params: { notification_id: id },
});
},
onClickNext() {
this.navigateToConversation(this.activeNotificationIndex, 'next');
@@ -3,7 +3,7 @@
role="button"
class="flex flex-col ltr:pl-5 rtl:pl-3 rtl:pr-5 ltr:pr-3 gap-2.5 py-3 w-full border-b border-slate-50 dark:border-slate-800/50 hover:bg-slate-25 dark:hover:bg-slate-800 cursor-pointer"
:class="
isInboxCardActive
active
? 'bg-slate-25 dark:bg-slate-800 click-animation'
: 'bg-white dark:bg-slate-900'
"
@@ -23,26 +23,21 @@
</div>
</div>
<div class="flex flex-row justify-between items-center w-full">
<div class="flex gap-1.5 items-center max-w-[calc(100%-70px)]">
<Thumbnail
v-if="assigneeMeta"
:src="assigneeMeta.thumbnail"
:username="assigneeMeta.name"
size="16px"
class="relative bottom-0.5"
/>
<div class="flex min-w-0">
<span
class="text-slate-800 dark:text-slate-50 text-sm overflow-hidden text-ellipsis whitespace-nowrap"
:class="isUnread ? 'font-medium' : 'font-normal'"
>
{{ pushTitle }}
</span>
</div>
</div>
<div class="flex flex-row justify-between items-center w-full gap-2">
<Thumbnail
v-if="assigneeMeta"
:src="assigneeMeta.thumbnail"
:username="assigneeMeta.name"
size="16px"
/>
<span
class="font-medium max-w-[60px] text-slate-600 dark:text-slate-300 text-xs whitespace-nowrap"
class="flex-1 text-slate-800 dark:text-slate-50 text-sm overflow-hidden text-ellipsis whitespace-nowrap"
:class="isUnread ? 'font-medium' : 'font-normal'"
>
{{ pushTitle }}
</span>
<span
class="font-medium text-slate-600 dark:text-slate-300 text-xs whitespace-nowrap"
>
{{ lastActivityAt }}
</span>
@@ -61,6 +56,7 @@
/>
</div>
</template>
<script>
import PriorityIcon from './PriorityIcon.vue';
import StatusIcon from './StatusIcon.vue';
@@ -84,6 +80,10 @@ export default {
type: Object,
default: () => {},
},
active: {
type: Boolean,
default: false,
},
},
data() {
return {
@@ -95,9 +95,6 @@ export default {
primaryActor() {
return this.notificationItem?.primary_actor;
},
isInboxCardActive() {
return this.$route.params.conversation_id === this.primaryActor?.id;
},
inbox() {
return this.$store.getters['inboxes/getInbox'](
this.primaryActor.inbox_id
@@ -166,11 +163,11 @@ export default {
id,
primary_actor_id: primaryActorId,
primary_actor_type: primaryActorType,
primary_actor: { id: conversationId, inbox_id: inboxId },
primary_actor: { inbox_id: inboxId },
notification_type: notificationType,
} = notification;
if (this.$route.params.conversation_id !== conversationId) {
if (this.$route.params.notification_id !== id) {
this.$track(INBOX_EVENTS.OPEN_CONVERSATION_VIA_INBOX, {
notificationType,
});
@@ -184,13 +181,14 @@ export default {
this.$router.push({
name: 'inbox_view_conversation',
params: { inboxId, conversation_id: conversationId },
params: { inboxId, notification_id: id },
});
}
},
closeContextMenu() {
this.isContextMenuOpen = false;
this.contextMenuPosition = { x: null, y: null };
this.$emit('context-menu-close');
},
openContextMenu(e) {
this.closeContextMenu();
@@ -200,6 +198,7 @@ export default {
y: e.pageY || e.clientY,
};
this.isContextMenuOpen = true;
this.$emit('context-menu-open');
},
handleAction(key) {
switch (key) {
@@ -218,17 +217,21 @@ export default {
},
};
</script>
<style scoped>
.click-animation {
animation: click-animation 0.3s ease-in-out;
}
@keyframes click-animation {
0% {
transform: scale(1);
}
50% {
transform: scale(0.99);
}
100% {
transform: scale(1);
}
@@ -2,10 +2,20 @@
<div
class="flex gap-2 py-2 ltr:pl-4 rtl:pl-2 h-14 ltr:pr-2 rtl:pr-4 rtl:border-r justify-between items-center w-full border-b border-slate-50 dark:border-slate-800/50"
>
<woot-button
variant="clear link"
class="flex md:hidden !pt-1 !pb-1 rounded-md ltr:pr-1 rtl:pl-1 !no-underline"
size="medium"
color-scheme="primary"
icon="chevron-left"
@click="onClickGoToInboxList"
>
{{ $t('INBOX.ACTION_HEADER.BACK') }}
</woot-button>
<pagination-button
v-if="totalLength > 1"
:total-length="totalLength"
:current-index="currentIndex"
:current-index="currentIndex + 1"
@next="onClickNext"
@prev="onClickPrev"
/>
@@ -16,6 +26,7 @@
size="small"
color-scheme="secondary"
icon="snooze"
class="[&>span]:hidden md:[&>span]:inline-flex"
@click="openSnoozeNotificationModal"
>
{{ $t('INBOX.ACTION_HEADER.SNOOZE') }}
@@ -25,6 +36,7 @@
size="small"
color-scheme="secondary"
variant="hollow"
class="[&>span]:hidden md:[&>span]:inline-flex"
@click="deleteNotification"
>
{{ $t('INBOX.ACTION_HEADER.DELETE') }}
@@ -41,6 +53,7 @@
</woot-modal>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { getUnixTime } from 'date-fns';
@@ -73,14 +86,10 @@ export default {
},
},
data() {
return {
showCustomSnoozeModal: false,
};
return { showCustomSnoozeModal: false };
},
computed: {
...mapGetters({
meta: 'notifications/getMeta',
}),
...mapGetters({ meta: 'notifications/getMeta' }),
},
mounted() {
bus.$on(CMD_SNOOZE_NOTIFICATION, this.onCmdSnoozeNotification);
@@ -96,15 +105,17 @@ export default {
hideCustomSnoozeModal() {
this.showCustomSnoozeModal = false;
},
snoozeNotification(snoozedUntil) {
this.$store
.dispatch('notifications/snooze', {
async snoozeNotification(snoozedUntil) {
try {
await this.$store.dispatch('notifications/snooze', {
id: this.activeNotification?.id,
snoozedUntil,
})
.then(() => {
this.showAlert(this.$t('INBOX.ALERTS.SNOOZE'));
});
this.showAlert(this.$t('INBOX.ALERTS.SNOOZE'));
} catch (error) {
// Silently fail without any change in the UI
}
},
onCmdSnoozeNotification(snoozeType) {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
@@ -132,7 +143,7 @@ export default {
.then(() => {
this.showAlert(this.$t('INBOX.ALERTS.DELETE'));
});
this.$router.push({ name: 'inbox_view' });
this.$router.replace({ name: 'inbox_view' });
},
onClickNext() {
this.$emit('next');
@@ -140,6 +151,9 @@ export default {
onClickPrev() {
this.$emit('prev');
},
onClickGoToInboxList() {
this.$router.replace({ name: 'inbox_view' });
},
},
};
</script>
@@ -26,7 +26,7 @@
<inbox-display-menu
v-if="showInboxDisplayMenu"
v-on-clickaway="openInboxDisplayMenu"
class="absolute top-8"
class="absolute top-9 ltr:left-0 rtl:right-0"
@filter="onFilterChange"
/>
</div>
@@ -49,7 +49,7 @@
<inbox-option-menu
v-if="showInboxOptionMenu"
v-on-clickaway="openInboxOptionsMenu"
class="absolute top-9"
class="absolute top-9 ltr:right-0 ltr:md:right-[unset] rtl:left-0 rtl:md:left-[unset]"
@option-click="onInboxOptionMenuClick"
/>
</div>
@@ -69,12 +69,29 @@ export default {
InboxDisplayMenu,
},
mixins: [clickaway, alertMixin],
props: {
isContextMenuOpen: {
type: Boolean,
default: false,
},
},
data() {
return {
showInboxDisplayMenu: false,
showInboxOptionMenu: false,
};
},
watch: {
isContextMenuOpen: {
handler(val) {
if (val) {
this.showInboxDisplayMenu = false;
this.showInboxOptionMenu = false;
}
},
immediate: true,
},
},
methods: {
markAllRead() {
this.$track(INBOX_EVENTS.MARK_ALL_NOTIFICATIONS_AS_READ);
@@ -99,22 +116,19 @@ export default {
this.showInboxOptionMenu = !this.showInboxOptionMenu;
},
onInboxOptionMenuClick(key) {
this.showInboxOptionMenu = false;
if (key === 'mark_all_read') {
this.markAllRead();
}
if (key === 'delete_all') {
this.deleteAll();
}
if (key === 'delete_all_read') {
this.deleteAllRead();
}
const actions = {
mark_all_read: () => this.markAllRead(),
delete_all: () => this.deleteAll(),
delete_all_read: () => this.deleteAllRead(),
};
const action = actions[key];
if (action) action();
this.$emit('redirect');
},
onFilterChange(option) {
this.$emit('filter', option);
this.showInboxDisplayMenu = false;
if (this.$route.name === 'inbox_view') return;
this.$router.push({ name: 'inbox_view' });
this.$emit('redirect');
},
},
};
@@ -1,6 +1,4 @@
<script setup>
import { defineProps, defineEmits } from 'vue';
defineProps({
label: {
type: String,
@@ -19,7 +19,7 @@
/>
</div>
<div class="flex items-center gap-1 whitespace-nowrap">
<span class="text-sm font-medium text-gray-600">
<span class="text-sm font-medium text-gray-600 tabular-nums">
{{ totalLength <= 1 ? '1' : currentIndex }}
</span>
<span
@@ -28,7 +28,7 @@
>
/
</span>
<span v-if="totalLength > 1" class="text-sm text-slate-400">
<span v-if="totalLength > 1" class="text-sm text-slate-400 tabular-nums">
{{ totalLength }}
</span>
</div>
@@ -0,0 +1,25 @@
import { frontendURL } from 'dashboard/helper/URLHelper';
const InboxListView = () => import('./InboxList.vue');
const InboxDetailView = () => import('./InboxView.vue');
const InboxEmptyStateView = () => import('./InboxEmptyState.vue');
export const routes = [
{
path: frontendURL('accounts/:accountId/inbox-view'),
component: InboxListView,
children: [
{
path: '',
name: 'inbox_view',
component: InboxEmptyStateView,
roles: ['administrator', 'agent'],
},
{
path: ':notification_id',
name: 'inbox_view_conversation',
component: InboxDetailView,
roles: ['administrator', 'agent'],
},
],
},
];
@@ -139,9 +139,7 @@ export default {
uiFlags: 'notifications/getUIFlags',
}),
notificationPanelFooterIconClass() {
return this.isRTLView
? 'margin-right-minus-slab'
: 'margin-left-minus-slab';
return this.isRTLView ? '-mr-3' : '-ml-3';
},
totalUnreadNotifications() {
return this.meta.unreadCount;
@@ -1,9 +1,10 @@
<template>
<div>
<div class="w-full">
<woot-button
size="expanded"
color-scheme="secondary"
variant="link"
class="w-full"
@click="onClickOpenNotification()"
>
<div
@@ -1,5 +1,7 @@
<template>
<section class="notification--table-wrap bg-white dark:bg-slate-900">
<section
class="h-full flex-shrink flex-grow overflow-hidden py-8 px-4 bg-white dark:bg-slate-900"
>
<woot-submit-button
v-if="notificationMetadata.unreadCount"
class="button nice success button--fixed-top"
@@ -136,10 +138,6 @@ export default {
@apply text-sm m-0 text-slate-800 dark:text-slate-100;
}
.notification--table-wrap {
@apply overflow-hidden hover:overflow-y-auto flex-shrink flex-grow h-full py-8 px-12;
}
.notifications-table {
> tbody {
> tr {
@@ -1,6 +1,6 @@
<template>
<div class="columns notification--page">
<div class="notification--content medium-12">
<div class="overflow-y-auto h-full">
<div class="flex flex-col h-full">
<notification-table
:notifications="records"
:is-loading="uiFlags.isFetching"
@@ -15,15 +15,17 @@
v-if="icon"
:icon="icon"
:class="iconClass"
class="mr-2 ml-4 rtl:ml-2 rtl:mr-4"
class="mr-2 ml-1 rtl:ml-2 rtl:mr-1 hidden md:block"
/>
<slot />
<span class="text-slate-900 dark:text-slate-100">{{ headerTitle }}</span>
<span class="text-slate-900 font-medium text-2xl dark:text-slate-100">
{{ headerTitle }}
</span>
</h1>
<router-link
v-if="showNewButton && isAdmin"
:to="buttonRoute"
class="button success button--fixed-top"
class="button success button--fixed-top px-3.5 py-1 rounded-[5px] flex gap-2"
>
<fluent-icon icon="add-circle" />
<span class="button__content">
@@ -0,0 +1,23 @@
<script setup>
defineProps({
isLoading: {
type: Boolean,
default: false,
},
loadingMessage: {
type: String,
default: '',
},
});
</script>
<template>
<div class="flex flex-col w-full h-full gap-10 font-inter">
<slot name="header" />
<div>
<slot v-if="isLoading" name="loading">
<woot-loading-state :message="loadingMessage" />
</slot>
<slot v-else name="body" />
</div>
</div>
</template>
@@ -1,6 +1,8 @@
<template>
<div class="flex flex-col w-full items-start">
<h2 class="text-xl text-slate-800 dark:text-slate-100 break-words">
<h2
class="text-xl font-medium mb-1 text-slate-800 dark:text-slate-100 break-words"
>
{{ headerTitle }}
</h2>
<p
@@ -0,0 +1,21 @@
<script setup>
defineProps({
keepAlive: {
type: Boolean,
default: true,
},
});
</script>
<template>
<div
class="flex flex-col w-full h-full px-5 pt-8 pb-3 m-0 overflow-auto bg-white sm:px-16 sm:pt-16 dark:bg-slate-900"
>
<div class="flex items-start max-w-[900px] w-full">
<keep-alive v-if="keepAlive">
<router-view />
</keep-alive>
<router-view v-else />
</div>
</div>
</template>
@@ -1,6 +1,6 @@
<template>
<div
class="flex flex-1 h-full justify-between flex-col m-0 bg-slate-25 dark:bg-slate-900"
class="flex flex-1 h-full justify-between flex-col m-0 bg-slate-25 dark:bg-slate-900 overflow-auto"
>
<settings-header
button-route="new"
@@ -7,7 +7,7 @@
<div
class="flex-grow-0 flex-shrink-0 flex-[25%] min-w-0 py-4 pr-6 pl-0"
>
<h4 class="block-title text-black-900 dark:text-slate-200">
<h4 class="text-lg font-medium text-black-900 dark:text-slate-200">
{{ $t('GENERAL_SETTINGS.FORM.GENERAL_SECTION.TITLE') }}
</h4>
<p>{{ $t('GENERAL_SETTINGS.FORM.GENERAL_SECTION.NOTE') }}</p>
@@ -87,12 +87,12 @@
</div>
<div
class="p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300 row"
class="p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300 flex flex-row"
>
<div
class="flex-grow-0 flex-shrink-0 flex-[25%] min-w-0 py-4 pr-6 pl-0"
>
<h4 class="block-title text-black-900 dark:text-slate-200">
<h4 class="text-lg font-medium text-black-900 dark:text-slate-200">
{{ $t('GENERAL_SETTINGS.FORM.ACCOUNT_ID.TITLE') }}
</h4>
<p>
@@ -1,7 +1,7 @@
<template>
<div class="flex-1 overflow-auto p-4">
<div class="flex flex-row gap-4">
<div class="w-[60%]">
<div class="w-full lg:w-3/5">
<woot-loading-state
v-if="uiFlags.isFetching"
:message="$t('AGENT_BOTS.LIST.LOADING')"
@@ -23,7 +23,7 @@
</p>
</div>
<div class="w-[34%]">
<div class="hidden lg:block w-1/3">
<p v-html="$t('AGENT_BOTS.SIDEBAR_TXT')" />
</div>
</div>
@@ -94,9 +94,11 @@ export default {
.bots-list {
list-style: none;
}
.nowrap {
white-space: nowrap;
}
.white-text {
color: white;
}
@@ -1,5 +1,5 @@
<template>
<tr>
<tr class="space-x-2">
<td class="agent-bot--details">
<div class="agent-bot--link">
{{ agentBot.name }}
@@ -9,7 +9,7 @@
<show-more :text="agentBot.description" :limit="120" />
</div>
</td>
<td class="button-wrapper">
<td class="flex justify-end gap-1">
<woot-button
v-if="isACSMLTypeBot"
v-tooltip.top="$t('AGENT_BOTS.EDIT.BUTTON_TEXT')"
@@ -75,13 +75,4 @@ export default {
.agent-bot--details {
width: 90%;
}
.button-wrapper {
max-width: var(--space-mega);
min-width: auto;
button:nth-child(2) {
margin-left: var(--space-normal);
}
}
</style>
@@ -1,5 +1,5 @@
<template>
<span>
<span class="inline-flex items-center gap-1">
<img
v-tooltip="botTypeConfig[botType].label"
class="agent-bot-type--thumbnail"
@@ -11,7 +11,7 @@
<!-- List Agents -->
<div class="flex flex-row gap-4">
<div class="w-[60%]">
<div class="w-full lg:w-3/5">
<woot-loading-state
v-if="uiFlags.isFetching"
:message="$t('AGENT_MGMT.LOADING')"
@@ -27,7 +27,6 @@
<td>
<thumbnail
:src="agent.thumbnail"
class="columns"
:username="agent.name"
size="40px"
:status="agent.availability_status"
@@ -85,7 +84,7 @@
</table>
</div>
</div>
<div class="w-[34%]">
<div class="w-1/3 hidden lg:block">
<span
v-dompurify-html="
useInstallationName(
@@ -1,6 +1,6 @@
<template>
<div class="flex flex-row gap-4 pl-2">
<div class="w-[60%]">
<div class="flex flex-row gap-4 p-8">
<div class="w-full lg:w-3/5">
<woot-tabs :index="selectedTabIndex" @change="onClickTabChange">
<woot-tabs-item
v-for="tab in tabs"
@@ -81,7 +81,7 @@
</table>
</div>
</div>
<div class="w-[34%]">
<div class="hidden lg:block w-1/3">
<span v-dompurify-html="$t('ATTRIBUTES_MGMT.SIDEBAR_TXT')" />
</div>
<woot-modal :show.sync="showEditPopup" :on-close="hideEditPopup">
@@ -224,6 +224,7 @@ export default {
@apply p-0;
}
}
.tabs-title a {
font-weight: var(--font-weight-medium);
padding-top: 0;
@@ -1,5 +1,5 @@
<template>
<div class="flex-1 overflow-auto p-4">
<div class="flex-1 overflow-auto">
<woot-button
color-scheme="success"
class-names="button--fixed-top"
@@ -19,7 +19,7 @@
class="woot-table w-full"
>
<colgroup>
<col class="w-[60%]" />
<col class="w-3/5" />
<col />
<col />
</colgroup>
@@ -144,6 +144,7 @@
</template>
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
@@ -173,7 +174,6 @@ export default {
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
automationRuleEvents: AUTOMATION_RULE_EVENTS,
automationActionTypes: AUTOMATION_ACTION_TYPES,
automationMutated: false,
show: true,
automation: {
@@ -202,6 +202,10 @@ export default {
};
},
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
}),
hasAutomationMutated() {
if (
this.automation.conditions[0].values ||
@@ -210,6 +214,12 @@ export default {
return true;
return false;
},
automationActionTypes() {
const isSLAEnabled = this.isFeatureEnabled('sla');
return isSLAEnabled
? AUTOMATION_ACTION_TYPES
: AUTOMATION_ACTION_TYPES.filter(action => action.key !== 'add_sla');
},
},
mounted() {
this.$store.dispatch('inboxes/get');
@@ -221,6 +231,11 @@ export default {
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
this.manifestCustomAttributes();
},
methods: {
isFeatureEnabled(flag) {
return this.isFeatureEnabledonAccount(this.accountId, flag);
},
},
};
</script>
<style lang="scss" scoped>
@@ -142,6 +142,7 @@
</template>
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
@@ -175,7 +176,6 @@ export default {
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
automationRuleEvents: AUTOMATION_RULE_EVENTS,
automationActionTypes: AUTOMATION_ACTION_TYPES,
automationMutated: false,
show: true,
automation: null,
@@ -185,6 +185,10 @@ export default {
};
},
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
}),
hasAutomationMutated() {
if (
this.automation.conditions[0].values ||
@@ -193,12 +197,23 @@ export default {
return true;
return false;
},
automationActionTypes() {
const isSLAEnabled = this.isFeatureEnabled('sla');
return isSLAEnabled
? AUTOMATION_ACTION_TYPES
: AUTOMATION_ACTION_TYPES.filter(action => action.key !== 'add_sla');
},
},
mounted() {
this.manifestCustomAttributes();
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
this.formatAutomation(this.selectedResponse);
},
methods: {
isFeatureEnabled(flag) {
return this.isFeatureEnabledonAccount(this.accountId, flag);
},
},
};
</script>
<style lang="scss" scoped>
@@ -9,7 +9,7 @@
{{ $t('AUTOMATION.HEADER_BTN_TXT') }}
</woot-button>
<div class="flex flex-row gap-4">
<div class="w-[60%]">
<div class="w-full lg:w-3/5">
<p
v-if="!uiFlags.isFetching && !records.length"
class="flex h-full items-center flex-col justify-center"
@@ -77,7 +77,7 @@
</table>
</div>
<div class="w-[34%]">
<div class="hidden lg:block w-1/3">
<span v-dompurify-html="$t('AUTOMATION.SIDEBAR_TXT')" />
</div>
</div>
@@ -153,6 +153,8 @@ export default {
...mapGetters({
records: ['automations/getAutomations'],
uiFlags: 'automations/getUIFlags',
accountId: 'getCurrentAccountId',
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
}),
// Delete Modal
deleteConfirmText() {
@@ -168,6 +170,9 @@ export default {
deleteMessage() {
return ` ${this.selectedResponse.name}?`;
},
isSLAEnabled() {
return this.isFeatureEnabledonAccount(this.accountId, 'sla');
},
},
mounted() {
this.$store.dispatch('inboxes/get');
@@ -177,6 +182,7 @@ export default {
this.$store.dispatch('labels/get');
this.$store.dispatch('campaigns/get');
this.$store.dispatch('automations/get');
if (this.isSLAEnabled) this.$store.dispatch('sla/get');
},
methods: {
openAddPopup() {
@@ -611,4 +611,9 @@ export const AUTOMATION_ACTION_TYPES = [
label: 'Change Priority',
inputType: 'search_select',
},
{
key: 'add_sla',
label: 'Add SLA',
inputType: 'search_select',
},
];
@@ -61,7 +61,7 @@
</label>
<label
v-if="isOnOffType"
v-if="isOneOffType"
class="multiselect-wrap--small"
:class="{ error: $v.selectedAudience.$error }"
>
@@ -106,7 +106,7 @@
</span>
</label>
<label v-if="isOnOffType">
<label v-if="isOneOffType">
{{ $t('CAMPAIGN.ADD.FORM.SCHEDULED_AT.LABEL') }}
<woot-date-time-picker
:value="scheduledAt"
@@ -1,10 +1,12 @@
<template>
<div
class="bg-white dark:bg-slate-800 mb-2 border border-slate-50 dark:border-slate-900 rounded-md px-5 py-4"
class="px-5 py-4 mb-2 bg-white border rounded-md dark:bg-slate-800 border-slate-50 dark:border-slate-900"
>
<div class="flex flex-row items-start justify-between">
<div class="flex flex-col">
<div class="text-base font-medium -mt-1 mb-1">
<div
class="mb-1 -mt-1 text-base font-medium text-slate-900 dark:text-slate-100"
>
{{ campaign.title }}
</div>
<div
@@ -35,14 +37,14 @@
</div>
</div>
<div class="flex flex-row mt-5 items-center space-x-3">
<div class="flex flex-row items-center mt-5 space-x-3">
<woot-label
small
:title="campaignStatus"
:color-scheme="colorScheme"
class="text-xs mr-3"
class="mr-3 text-xs"
/>
<inbox-name :inbox="campaign.inbox" class="ltr:ml-0 rtl:mr-0 mb-1" />
<inbox-name :inbox="campaign.inbox" class="mb-1 ltr:ml-0 rtl:mr-0" />
<user-avatar-with-name
v-if="campaign.sender"
:user="campaign.sender"
@@ -50,13 +52,13 @@
/>
<div
v-if="campaign.trigger_rules.url"
class="text-xs text-woot-600 mb-1 text-truncate w-1/4"
class="w-1/4 mb-1 text-xs text-woot-600 text-truncate"
>
{{ campaign.trigger_rules.url }}
</div>
<div
v-if="campaign.scheduled_at"
class="text-xs text-slate-700 dark:text-slate-500 mb-1"
class="mb-1 text-xs text-slate-700 dark:text-slate-500"
>
{{ messageStamp(new Date(campaign.scheduled_at), 'LLL d, h:mm a') }}
</div>
@@ -18,7 +18,7 @@ export default {
},
{
path: 'ongoing',
name: 'settings_account_campaigns',
name: 'ongoing_campaigns',
roles: ['administrator'],
component: Index,
},
@@ -25,7 +25,7 @@
<div class="editor-wrap">
<woot-message-editor
v-model="content"
class="message-editor"
class="message-editor [&>div]:px-1"
:class="{ editor_warning: $v.content.$error }"
:enable-variables="true"
:enable-canned-responses="false"
@@ -22,7 +22,7 @@
<div class="editor-wrap">
<woot-message-editor
v-model="content"
class="message-editor"
class="message-editor [&>div]:px-1"
:class="{ editor_warning: $v.content.$error }"
:enable-variables="true"
:enable-canned-responses="false"
@@ -1,5 +1,5 @@
<template>
<div class="flex-1 overflow-auto p-4">
<div class="flex-1 overflow-auto">
<woot-button
color-scheme="success"
class-names="button--fixed-top"
@@ -10,11 +10,11 @@
</woot-button>
<!-- List Canned Response -->
<div class="flex flex-row gap-4">
<div class="w-[60%]">
<div class="flex flex-row gap-4 p-8">
<div class="w-full xl:w-3/5">
<p
v-if="!uiFlags.fetchingList && !records.length"
class="flex h-full items-center flex-col justify-center"
class="flex flex-col items-center justify-center h-full"
>
{{ $t('CANNED_MGMT.LIST.404') }}
</p>
@@ -32,6 +32,7 @@
<th
v-for="thHeader in $t('CANNED_MGMT.LIST.TABLE_HEADER')"
:key="thHeader"
class="last:text-right"
>
{{ thHeader }}
</th>
@@ -42,7 +43,10 @@
:key="cannedItem.short_code"
>
<!-- Short Code -->
<td class="w-[8.75rem]">
<td
class="w-[8.75rem] truncate max-w-[8.75rem]"
:title="cannedItem.short_code"
>
{{ cannedItem.short_code }}
</td>
<!-- Content -->
@@ -50,7 +54,7 @@
{{ cannedItem.content }}
</td>
<!-- Action Buttons -->
<td class="button-wrapper">
<td class="flex justify-end gap-1 min-w-[12.5rem]">
<woot-button
v-tooltip.top="$t('CANNED_MGMT.EDIT.BUTTON_TEXT')"
variant="smooth"
@@ -75,7 +79,7 @@
</table>
</div>
<div class="w-[34%]">
<div class="hidden w-1/3 xl:block">
<span v-dompurify-html="$t('CANNED_MGMT.SIDEBAR_TXT')" />
</div>
</div>
@@ -0,0 +1,102 @@
<script setup>
defineProps({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
iconName: {
type: String,
required: true,
},
href: {
type: String,
default: '',
},
linkText: {
type: String,
default: '',
},
});
const openInNewTab = url => {
if (!url) return;
window.open(url, '_blank', 'noopener noreferrer');
};
</script>
<template>
<div class="flex flex-col items-start w-full gap-4">
<!-- Header section with icon, title and action button -->
<div class="flex items-center justify-between w-full gap-4">
<!-- Icon and title container -->
<div class="flex items-center gap-3">
<div
class="flex items-center w-10 h-10 p-1 rounded-full bg-woot-25/60 dark:bg-woot-900/60"
>
<div
class="flex items-center justify-center w-full h-full rounded-full bg-woot-75/70 dark:bg-woot-800/40"
>
<fluent-icon
size="14"
:icon="iconName"
type="outline"
class="flex-shrink-0 text-woot-500 dark:text-woot-500"
/>
</div>
</div>
<h1
class="text-2xl font-medium tracking-[-1.5%] text-slate-900 dark:text-slate-25"
>
{{ title }}
</h1>
</div>
<!-- Slot for additional actions on larger screens -->
<div class="hidden gap-2 sm:flex">
<slot name="actions" />
</div>
</div>
<!-- Description and optional link -->
<div
class="flex flex-col gap-2 text-slate-600 dark:text-slate-300 max-w-[721px] w-full"
>
<p
class="mb-0 text-sm font-normal tracking-[0.5%] line-clamp-5 sm:line-clamp-none"
>
<slot name="description">{{ description }}</slot>
</p>
<!-- Conditional link -->
<a
v-if="href && linkText"
:href="href"
target="_blank"
rel="noopener noreferrer"
class="sm:inline-flex hidden tracking-[-0.6%] gap-1 w-fit items-center text-woot-500 dark:text-woot-500 text-sm font-medium tracking=[-0.6%] hover:underline"
>
{{ linkText }}
<fluent-icon
size="16"
icon="chevron-right"
type="outline"
class="flex-shrink-0 text-woot-500 dark:text-woot-500"
/>
</a>
</div>
<!-- Mobile view for actions and link -->
<div class="flex items-start justify-start w-full gap-3 sm:hidden">
<slot name="actions" />
<woot-button
v-if="href && linkText"
color-scheme="secondary"
icon="arrow-outwards"
class="flex-row-reverse rounded-xl min-w-0 !bg-slate-50 !text-slate-900 dark:!text-white dark:!bg-slate-800"
@click="openInNewTab(href)"
>
{{ linkText }}
</woot-button>
</div>
</div>
</template>
@@ -0,0 +1,53 @@
<script setup>
defineProps({
title: {
type: String,
default: '',
},
description: {
type: String,
default: '',
},
});
</script>
<template>
<div
class="flex relative flex-col sm:flex-row p-4 gap-4 sm:p-6 justify-between shadow-sm group bg-white border border-solid rounded-xl dark:bg-slate-800 border-slate-75 dark:border-slate-700/50 max-w-[900px] w-full"
>
<!-- left side section -->
<slot name="leftSection">
<div class="flex flex-col min-w-0 items-start gap-3 max-w-[480px] w-full">
<div
class="flex items-center justify-between w-full gap-3 sm:justify-normal whitespace-nowrap"
>
<h3
class="justify-between text-sm tracking-[-0.6%] font-medium truncate w-fit sm:justify-normal text-slate-900 dark:text-slate-25"
>
<slot name="title">
{{ title }}
</slot>
</h3>
<slot name="label" />
</div>
<p
class="text-sm text-slate-600 tracking-[0.5%] dark:text-slate-300 max-w-[400px] w-full line-clamp-2"
>
<slot name="description">
{{ description }}
</slot>
</p>
</div>
</slot>
<!-- right side section -->
<slot name="rightSection" />
<!-- actions section -->
<div
v-if="$slots.actions"
class="absolute flex-col items-center hidden gap-1 border-none ltr:-right-3 rtl:-left-3 top-3 group-hover:flex"
>
<slot name="actions" />
</div>
</div>
</template>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<form class="mx-0 flex flex-wrap" @submit.prevent="addAgents()">
<div class="w-full">
<page-header
@@ -7,7 +9,7 @@
:header-content="$t('INBOX_MGMT.ADD.AGENTS.DESC')"
/>
</div>
<div class="w-[60%]">
<div class="w-3/5">
<div class="w-full">
<label :class="{ error: $v.selectedAgents.$error }">
{{ $t('INBOX_MGMT.ADD.AGENTS.TITLE') }}
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%] h-auto">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.AUTH.TITLE')"
:header-content="
@@ -82,12 +84,3 @@ export default {
},
};
</script>
<style scoped>
.height-auto {
height: auto;
}
.channel-list {
margin-top: var(--space-medium);
}
</style>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<empty-state
:title="$t('INBOX_MGMT.FINISH.TITLE')"
:message="message"
@@ -58,7 +60,7 @@
</div>
<div class="flex justify-center gap-2 mt-4">
<router-link
class="button hollow primary"
class="button hollow primary rounded"
:to="{
name: 'settings_inbox_show',
params: { inboxId: $route.params.inbox_id },
@@ -67,7 +69,7 @@
{{ $t('INBOX_MGMT.FINISH.MORE_SETTINGS') }}
</router-link>
<router-link
class="button success"
class="button success rounded"
:to="{
name: 'inbox_dashboard',
params: { inboxId: $route.params.inbox_id },
@@ -1,5 +1,5 @@
<template>
<div class="settings--content">
<div class="mx-8">
<settings-section
:title="$t('INBOX_MGMT.IMAP.TITLE')"
:sub-title="$t('INBOX_MGMT.IMAP.SUBTITLE')"
@@ -10,16 +10,17 @@
<input
v-model="isIMAPEnabled"
type="checkbox"
class="ltr:mr-2 rtl:ml-2"
name="toggle-imap-enable"
/>
{{ $t('INBOX_MGMT.IMAP.TOGGLE_AVAILABILITY') }}
</label>
<p>{{ $t('INBOX_MGMT.IMAP.TOGGLE_HELP') }}</p>
<div v-if="isIMAPEnabled" class="imap-details-wrap">
<div v-if="isIMAPEnabled" class="mb-6">
<woot-input
v-model.trim="address"
:class="{ error: $v.address.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.IMAP.ADDRESS.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.ADDRESS.PLACE_HOLDER')"
@blur="$v.address.$touch"
@@ -28,7 +29,7 @@
v-model="port"
type="number"
:class="{ error: $v.port.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.IMAP.PORT.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.PORT.PLACE_HOLDER')"
@blur="$v.port.$touch"
@@ -36,7 +37,7 @@
<woot-input
v-model="login"
:class="{ error: $v.login.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.IMAP.LOGIN.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.LOGIN.PLACE_HOLDER')"
@blur="$v.login.$touch"
@@ -44,7 +45,7 @@
<woot-input
v-model="password"
:class="{ error: $v.password.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.IMAP.PASSWORD.LABEL')"
:placeholder="$t('INBOX_MGMT.IMAP.PASSWORD.PLACE_HOLDER')"
type="password"
@@ -54,6 +55,7 @@
<input
v-model="isSSLEnabled"
type="checkbox"
class="ltr:mr-2 rtl:ml-2"
name="toggle-enable-ssl"
/>
{{ $t('INBOX_MGMT.IMAP.ENABLE_SSL') }}
@@ -159,8 +161,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.imap-details-wrap {
margin-bottom: var(--space-medium);
}
</style>
@@ -3,7 +3,7 @@
class="flex flex-row overflow-auto p-4 h-full bg-slate-25 dark:bg-slate-800"
>
<woot-wizard
class="hide-for-small-only w-[25%]"
class="hidden md:block w-1/4"
:global-config="globalConfig"
:items="items"
/>
@@ -1,8 +1,7 @@
<template>
<div class="flex-1 overflow-auto p-4">
<!-- List Canned Response -->
<div class="flex flex-row gap-4">
<div class="w-[60%]">
<div class="flex-1 overflow-auto">
<div class="flex flex-row gap-4 p-8">
<div class="w-full lg:w-3/5">
<p
v-if="!inboxesList.length"
class="flex h-full items-center flex-col justify-center"
@@ -99,7 +98,7 @@
</table>
</div>
<div class="w-[34%]">
<div class="w-1/3 hidden lg:block">
<span
v-dompurify-html="
useInstallationName(
@@ -4,7 +4,7 @@
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.DESCRIPTION') }}
</div>
<form class="flex flex-col" @submit.prevent="updateInbox">
<label class="w-[25%]">
<label class="w-1/4">
{{ $t('INBOX_MGMT.PRE_CHAT_FORM.ENABLE.LABEL') }}
<select v-model="preChatFormEnabled">
<option :value="true">
@@ -21,7 +21,7 @@
</woot-tabs>
</setting-intro-banner>
<div v-if="selectedTabKey === 'inbox_settings'" class="settings--content">
<div v-if="selectedTabKey === 'inbox_settings'" class="mx-8">
<settings-section
:title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_UPDATE_TITLE')"
:sub-title="$t('INBOX_MGMT.SETTINGS_POPUP.INBOX_UPDATE_SUB_TEXT')"
@@ -37,7 +37,7 @@
/>
<woot-input
v-model.trim="selectedInboxName"
class="w-[75%] pb-4"
class="w-3/4 pb-4"
:class="{ error: $v.selectedInboxName.$error }"
:label="inboxNameLabel"
:placeholder="inboxNamePlaceHolder"
@@ -51,7 +51,7 @@
<woot-input
v-if="isAPIInbox"
v-model.trim="webhookUrl"
class="w-[75%] pb-4"
class="w-3/4 pb-4"
:class="{ error: $v.webhookUrl.$error }"
:label="
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WEBHOOK_URL.LABEL')
@@ -69,7 +69,7 @@
<woot-input
v-if="isAWebWidgetInbox"
v-model.trim="channelWebsiteUrl"
class="w-[75%] pb-4"
class="w-3/4 pb-4"
:label="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.LABEL')"
:placeholder="
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_DOMAIN.PLACEHOLDER')
@@ -78,7 +78,7 @@
<woot-input
v-if="isAWebWidgetInbox"
v-model.trim="channelWelcomeTitle"
class="w-[75%] pb-4"
class="w-3/4 pb-4"
:label="
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TITLE.LABEL')
"
@@ -92,7 +92,7 @@
<woot-input
v-if="isAWebWidgetInbox"
v-model.trim="channelWelcomeTagline"
class="w-[75%] pb-4"
class="w-3/4 pb-4"
:label="
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_WELCOME_TAGLINE.LABEL')
"
@@ -103,17 +103,17 @@
"
/>
<label v-if="isAWebWidgetInbox" class="w-[75%] pb-4">
<label v-if="isAWebWidgetInbox" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.WIDGET_COLOR.LABEL') }}
<woot-color-picker v-model="inbox.widget_color" />
</label>
<label v-if="isAWhatsAppChannel" class="w-[75%] pb-4">
<label v-if="isAWhatsAppChannel" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.ADD.WHATSAPP.PROVIDERS.LABEL') }}
<input v-model="whatsAppAPIProviderName" type="text" disabled />
</label>
<label class="w-[75%] pb-4">
<label class="w-3/4 pb-4">
{{
$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.CHANNEL_GREETING_TOGGLE.LABEL')
}}
@@ -157,7 +157,7 @@
:richtext="!textAreaChannels"
/>
</div>
<label v-if="isAWebWidgetInbox" class="w-[75%] pb-4">
<label v-if="isAWebWidgetInbox" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.REPLY_TIME.TITLE') }}
<select v-model="replyTime">
<option key="in_a_few_minutes" value="in_a_few_minutes">
@@ -180,7 +180,7 @@
</p>
</label>
<label v-if="isAWebWidgetInbox" class="w-[75%] pb-4">
<label v-if="isAWebWidgetInbox" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ENABLE_EMAIL_COLLECT_BOX') }}
<select v-model="emailCollectEnabled">
<option :value="true">
@@ -197,7 +197,7 @@
</p>
</label>
<label class="w-[75%] pb-4">
<label class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ENABLE_CSAT') }}
<select v-model="csatSurveyEnabled">
<option :value="true">
@@ -212,7 +212,7 @@
</p>
</label>
<label v-if="isAWebWidgetInbox" class="w-[75%] pb-4">
<label v-if="isAWebWidgetInbox" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ALLOW_MESSAGES_AFTER_RESOLVED') }}
<select v-model="allowMessagesAfterResolved">
<option :value="true">
@@ -231,7 +231,7 @@
</p>
</label>
<label v-if="isAWebWidgetInbox" class="w-[75%] pb-4">
<label v-if="isAWebWidgetInbox" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ENABLE_CONTINUITY_VIA_EMAIL') }}
<select v-model="continuityViaEmail">
<option :value="true">
@@ -249,7 +249,7 @@
}}
</p>
</label>
<div class="w-[75%] pb-4">
<div class="w-3/4 pb-4">
<label>
{{ $t('INBOX_MGMT.HELP_CENTER.LABEL') }}
</label>
@@ -265,7 +265,7 @@
{{ $t('INBOX_MGMT.HELP_CENTER.SUB_TEXT') }}
</p>
</div>
<label v-if="canLocktoSingleConversation" class="w-[75%] pb-4">
<label v-if="canLocktoSingleConversation" class="w-3/4 pb-4">
{{ $t('INBOX_MGMT.SETTINGS_POPUP.LOCK_TO_SINGLE_CONVERSATION') }}
<select v-model="locktoSingleConversation">
<option :value="true">
@@ -287,7 +287,7 @@
<label v-if="isAWebWidgetInbox">
{{ $t('INBOX_MGMT.FEATURES.LABEL') }}
</label>
<div v-if="isAWebWidgetInbox" class="pt-2 pb-4">
<div v-if="isAWebWidgetInbox" class="pt-2 pb-4 flex gap-2">
<input
v-model="selectedFeatureFlags"
type="checkbox"
@@ -298,7 +298,7 @@
{{ $t('INBOX_MGMT.FEATURES.DISPLAY_FILE_PICKER') }}
</label>
</div>
<div v-if="isAWebWidgetInbox" class="pb-4">
<div v-if="isAWebWidgetInbox" class="pb-4 flex gap-2">
<input
v-model="selectedFeatureFlags"
type="checkbox"
@@ -309,7 +309,7 @@
{{ $t('INBOX_MGMT.FEATURES.DISPLAY_EMOJI_PICKER') }}
</label>
</div>
<div v-if="isAWebWidgetInbox" class="pb-4">
<div v-if="isAWebWidgetInbox" class="pb-4 flex gap-2">
<input
v-model="selectedFeatureFlags"
type="checkbox"
@@ -320,7 +320,7 @@
{{ $t('INBOX_MGMT.FEATURES.ALLOW_END_CONVERSATION') }}
</label>
</div>
<div v-if="isAWebWidgetInbox" class="pb-4">
<div v-if="isAWebWidgetInbox" class="pb-4 flex gap-2">
<input
v-model="selectedFeatureFlags"
type="checkbox"
@@ -338,7 +338,7 @@
:sub-title="$t('INBOX_MGMT.EDIT.SENDER_NAME_SECTION.SUB_TEXT')"
:show-border="false"
>
<div class="w-[75%] pb-4">
<div class="w-3/4 pb-4">
<sender-name-example-preview
:sender-name-type="senderNameType"
:business-name="businessName"
@@ -400,7 +400,7 @@
<facebook-reauthorize v-if="isAFacebookInbox" :inbox-id="inbox.id" />
</div>
<div v-if="selectedTabKey === 'collaborators'" class="settings--content">
<div v-if="selectedTabKey === 'collaborators'" class="mx-8">
<collaborators-page :inbox="inbox" />
</div>
<div v-if="selectedTabKey === 'configuration'">
@@ -757,11 +757,9 @@ export default {
</script>
<style scoped lang="scss">
.settings {
.settings--tabs {
::v-deep .tabs {
@apply p-0;
}
.settings--tabs {
::v-deep .tabs {
@apply p-0;
}
}
</style>
@@ -1,5 +1,5 @@
<template>
<div class="settings--content">
<div class="mx-8">
<settings-section
:title="$t('INBOX_MGMT.SMTP.TITLE')"
:sub-title="$t('INBOX_MGMT.SMTP.SUBTITLE')"
@@ -14,11 +14,11 @@
{{ $t('INBOX_MGMT.SMTP.TOGGLE_AVAILABILITY') }}
</label>
<p>{{ $t('INBOX_MGMT.SMTP.TOGGLE_HELP') }}</p>
<div v-if="isSMTPEnabled" class="smtp-details-wrap">
<div v-if="isSMTPEnabled" class="mb-6">
<woot-input
v-model.trim="address"
:class="{ error: $v.address.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.ADDRESS.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.ADDRESS.PLACE_HOLDER')"
@blur="$v.address.$touch"
@@ -27,7 +27,7 @@
v-model="port"
type="number"
:class="{ error: $v.port.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.PORT.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.PORT.PLACE_HOLDER')"
@blur="$v.port.$touch"
@@ -35,7 +35,7 @@
<woot-input
v-model="login"
:class="{ error: $v.login.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.LOGIN.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.LOGIN.PLACE_HOLDER')"
@blur="$v.login.$touch"
@@ -43,7 +43,7 @@
<woot-input
v-model="password"
:class="{ error: $v.password.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.PASSWORD.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.PASSWORD.PLACE_HOLDER')"
type="password"
@@ -52,7 +52,7 @@
<woot-input
v-model.trim="domain"
:class="{ error: $v.domain.$error }"
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.DOMAIN.LABEL')"
:placeholder="$t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')"
@blur="$v.domain.$touch"
@@ -63,14 +63,14 @@
:action="handleEncryptionChange"
/>
<single-select-dropdown
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.OPEN_SSL_VERIFY_MODE')"
:selected="openSSLVerifyMode"
:options="openSSLVerifyModes"
:action="handleSSLModeChange"
/>
<single-select-dropdown
class="medium-9 columns"
class="max-w-[75%] w-full"
:label="$t('INBOX_MGMT.SMTP.AUTH_MECHANISM')"
:selected="authMechanism"
:options="authMechanisms"
@@ -237,8 +237,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.smtp-details-wrap {
margin-bottom: var(--space-medium);
}
</style>
@@ -1,5 +1,5 @@
<template>
<div class="settings--content">
<div class="mx-8">
<div class="widget-builder-container">
<div class="settings-container w-100 lg:w-[40%]">
<div class="settings-content">
@@ -123,7 +123,7 @@
</form>
</div>
</div>
<div class="widget-container w-100 lg:w-[60%]">
<div class="widget-container w-100 lg:w-3/5">
<input-radio-group
name="widget-view-options"
:items="getWidgetViewOptions"
@@ -422,9 +422,9 @@ export default {
display: flex;
flex-direction: row;
padding: var(--space-one);
@include breakpoint(900px down) {
flex-direction: column;
}
// @include breakpoint(900px down) {
// flex-direction: column;
// }
}
.settings-container {
@@ -450,10 +450,11 @@ export default {
padding: var(--space-one) var(--space-one) var(--space-one) var(--space-one);
@apply bg-slate-50 dark:bg-slate-700;
@include breakpoint(500px down) {
background: none;
}
// @include breakpoint(500px down) {
// background: none;
// }
}
.widget-script {
@apply mx-5 p-2.5 bg-slate-50 dark:bg-slate-700;
}
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.API_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.API_CHANNEL.DESC')"
@@ -1,13 +1,13 @@
<template>
<div
v-if="!provider"
class="wizard-body small-12 medium-9 columns height-auto"
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full md:w-full max-w-full md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_PROVIDER.DESCRIPTION')"
/>
<div class="row channel-list">
<div class="flex flex-row flex-wrap mx-0 mt-6">
<channel-selector
v-for="emailProvider in emailProviderList"
:key="emailProvider.key"
@@ -63,9 +63,3 @@ export default {
},
};
</script>
<style scoped>
.channel-list {
margin-top: var(--space-medium);
}
</style>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<div v-if="!hasLoginStarted" class="login-init h-full">
<a href="#" @click="startLogin()">
<img
@@ -41,7 +43,7 @@
"
/>
</div>
<div class="w-[60%]">
<div class="w-3/5">
<div class="w-full">
<div class="input-wrap" :class="{ error: $v.selectedPage.$error }">
{{ $t('INBOX_MGMT.ADD.FB.CHOOSE_PAGE') }}
@@ -283,3 +285,16 @@ export default {
},
};
</script>
<style scoped lang="scss">
.login-init {
@apply pt-[30%] text-center;
p {
@apply p-6;
}
> a > img {
@apply w-60;
}
}
</style>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.LINE_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.LINE_CHANNEL.DESC')"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.SMS.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.SMS.DESC')"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.DESC')"
@@ -54,7 +54,7 @@
</label>
</div>
<div class="medium-8 columns messagingServiceHelptext">
<div class="max-w-[65%] w-full messagingServiceHelptext">
<label for="useMessagingService">
<input
id="useMessagingService"
@@ -84,7 +84,7 @@
}}</span>
</label>
</div>
<div class="medium-8 columns messagingServiceHelptext">
<div class="max-w-[65%] w-full messagingServiceHelptext">
<label for="useAPIKey">
<input
id="useAPIKey"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<div class="login-init h-full text-center">
<form @submit.prevent="requestAuthorization">
<woot-submit-button
@@ -40,3 +42,14 @@ export default {
},
};
</script>
<style scoped lang="scss">
.login-init {
@apply pt-[30%] text-center;
p {
@apply p-6;
}
> a > img {
@apply w-60;
}
}
</style>
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%] h-auto">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.WEBSITE_CHANNEL.DESC')"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.WHATSAPP.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.WHATSAPP.DESC')"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<page-header
:header-title="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')"
@@ -1,5 +1,7 @@
<template>
<div class="wizard-body w-[75%] flex-shrink-0 flex-grow-0 max-w-[75%]">
<div
class="border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6 w-full max-w-full md:w-3/4 md:max-w-[75%] flex-shrink-0 flex-grow-0"
>
<settings-sub-page-header
:header-title="$t('INBOX_MGMT.ADD.MICROSOFT.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.MICROSOFT.DESCRIPTION')"
@@ -1,10 +1,10 @@
<template>
<div class="settings--content">
<div class="mx-8">
<settings-section
:title="$t('INBOX_MGMT.MICROSOFT.TITLE')"
:sub-title="$t('INBOX_MGMT.MICROSOFT.SUBTITLE')"
>
<div class="smtp-details-wrap">
<div class="mb-6">
<form @submit.prevent="requestAuthorization">
<woot-submit-button
icon="brand-twitter"
@@ -1,5 +1,5 @@
<template>
<div class="settings--content">
<div class="mx-8">
<loading-state v-if="uiFlags.isFetching || uiFlags.isFetchingAgentBot" />
<form
v-else
@@ -10,7 +10,7 @@
:title="$t('AGENT_BOTS.BOT_CONFIGURATION.TITLE')"
:sub-title="$t('AGENT_BOTS.BOT_CONFIGURATION.DESC')"
>
<div class="w-[60%]">
<div class="w-3/5">
<label>
<select v-model="selectedAgentBotId">
<option value="" disabled selected>

Some files were not shown because too many files have changed in this diff Show More