Merge branch 'develop' into feat/email-forwarding

This commit is contained in:
iamsivin
2025-09-17 10:19:06 +05:30
2264 changed files with 105291 additions and 20436 deletions
@@ -1,38 +1,33 @@
<script>
import Thumbnail from '../../../components/widgets/Thumbnail.vue';
<script setup>
import Avatar from 'next/avatar/Avatar.vue';
export default {
components: {
Thumbnail,
defineProps({
name: {
type: String,
default: '',
},
props: {
name: {
type: String,
default: '',
},
thumbnail: {
type: String,
default: '',
},
email: {
type: String,
default: '',
},
phoneNumber: {
type: String,
default: '',
},
identifier: {
type: [String, Number],
required: true,
},
thumbnail: {
type: String,
default: '',
},
};
email: {
type: String,
default: '',
},
phoneNumber: {
type: String,
default: '',
},
identifier: {
type: [String, Number],
required: true,
},
});
</script>
<template>
<div class="option-item--user">
<Thumbnail :src="thumbnail" size="28px" :username="name" />
<Avatar :src="thumbnail" :size="28" :name="name" rounded-full />
<div class="option__user-data">
<h5 class="option__title">
{{ name }}
@@ -60,7 +55,7 @@ export default {
@apply flex items-center;
}
.user-identifier {
@apply text-xs ml-0.5 text-slate-700 dark:text-slate-100;
@apply text-xs ml-0.5 text-n-slate-12;
}
.option__user-data {
@apply flex flex-col flex-grow ml-2 mr-2;
@@ -73,10 +68,10 @@ export default {
@apply relative top-px mr-0.5 rtl:mr-0 rtl:ml-0.5;
}
.option__title {
@apply text-slate-800 dark:text-slate-100 font-medium mb-0.5;
@apply text-n-slate-12 font-medium mb-0.5;
}
.option__body {
@apply text-xs text-slate-700 dark:text-slate-100 mt-1;
@apply text-xs text-n-slate-12 mt-1;
}
.option__user-data .option__body {
@@ -130,15 +130,15 @@ export default {
</div>
<div class="flex multiselect-wrap--medium">
<div
class="w-8 relative text-base text-slate-100 dark:text-slate-600 after:content-[''] after:h-12 after:w-0 after:left-4 after:absolute after:border-l after:border-solid after:border-slate-100 after:dark:border-slate-600 before:content-[''] before:h-0 before:w-4 before:left-4 before:top-12 before:absolute before:border-b before:border-solid before:border-slate-100 before:dark:border-slate-600"
class="w-8 relative text-base text-n-strong after:content-[''] after:h-12 after:w-0 ltr:after:left-4 rtl:after:right-4 after:absolute after:border-l after:border-solid after:border-n-strong before:content-[''] before:h-0 before:w-4 ltr:before:left-4 rtl:before:right-4 before:top-12 before:absolute before:border-b before:border-solid before:border-n-strong"
>
<fluent-icon
icon="arrow-up"
class="absolute -top-1 left-2"
class="absolute -top-1 ltr:left-2 rtl:right-2"
size="17"
/>
</div>
<div class="flex flex-col w-full">
<div class="flex flex-col w-full ltr:pl-8 rtl:pr-8">
<label class="multiselect__label">
{{ $t('MERGE_CONTACTS.PRIMARY.TITLE') }}
<woot-label
@@ -17,9 +17,9 @@ export default {
<template>
<div
v-if="parentContactName"
class="my-4 relative p-2.5 border rounded-[4px] text-slate-800 dark:text-slate-100 border-slate-100 dark:border-slate-700 bg-white dark:bg-slate-800"
class="my-4 relative p-2.5 border rounded-[4px] text-n-slate-12 border-n-weak bg-n-background"
>
<h5 class="text-base font-medium text-slate-900 dark:text-white">
<h5 class="text-base font-medium text-n-slate-12">
{{ $t('MERGE_CONTACTS.SUMMARY.TITLE') }}
</h5>
<ul class="ml-0 list-none">
@@ -273,7 +273,7 @@ export default {
<style lang="scss" scoped>
.menu-container {
@apply p-1 bg-white dark:bg-slate-900 shadow-xl rounded-md;
@apply p-1 bg-n-background shadow-xl rounded-md;
hr:first-child {
@apply hidden;
@@ -293,10 +293,6 @@ export default {
@apply font-medium text-base;
}
}
.modal-footer {
@apply pt-4 pb-8 px-8;
}
}
}
</style>
@@ -1,5 +1,5 @@
<script setup>
import { ref, useTemplateRef, onMounted, watch, nextTick } from 'vue';
import { ref, useTemplateRef, onMounted, watch, nextTick, computed } from 'vue';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import ReadMore from './ReadMore.vue';
@@ -8,9 +8,9 @@ const props = defineProps({
type: String,
default: '',
},
content: {
type: String,
default: '',
message: {
type: Object,
default: () => ({}),
},
searchTerm: {
type: String,
@@ -18,6 +18,24 @@ const props = defineProps({
},
});
const messageContent = computed(() => {
// We perform search on either content or email subject or transcribed text
if (props.message.content) {
return props.message.content;
}
const { content_attributes = {} } = props.message;
const { email = {} } = content_attributes || {};
if (email.subject) {
return email.subject;
}
const audioAttachment = props.message.attachments.find(
attachment => attachment.file_type === 'audio'
);
return audioAttachment?.transcribed_text || '';
});
const { highlightContent } = useMessageFormatter();
const messageContainer = useTemplateRef('messageContainer');
@@ -38,7 +56,8 @@ const escapeHtml = html => {
return p.innerText;
};
const prepareContent = (content = '') => {
const prepareContent = () => {
const content = messageContent.value || '';
const escapedText = escapeHtml(content);
return highlightContent(
escapedText,
@@ -65,7 +84,7 @@ onMounted(() => {
{{ $t('SEARCH.WROTE') }}
</p>
<ReadMore :shrink="isOverflowing" @expand="isOverflowing = false">
<div v-dompurify-html="prepareContent(content)" class="message-content" />
<div v-dompurify-html="prepareContent()" class="message-content" />
</ReadMore>
</blockquote>
</template>
@@ -74,6 +93,7 @@ onMounted(() => {
.message {
@apply py-0 px-2 mt-2;
}
.message-content::v-deep p,
.message-content::v-deep li::marker {
@apply text-n-slate-11 mb-1;
@@ -102,7 +102,7 @@ const visibleInfoItems = computed(() =>
>
<InboxName
:inbox="inbox"
class="mr-2 rtl:mr-0 rtl:ml-2 bg-n-slate-3 dark:bg-n-solid-3 text-n-slate-11 dark:text-n-slate-11"
class="mx-2 bg-n-slate-3 dark:bg-n-solid-3 text-n-slate-11 dark:text-n-slate-11"
/>
</div>
</div>
@@ -54,7 +54,7 @@ const getName = message => {
>
<MessageContent
:author="getName(message)"
:content="message.content"
:message="message"
:search-term="query"
/>
</SearchResultConversationItem>
@@ -41,6 +41,7 @@ const onTabChange = index => {
:name="item.name"
:count="item.count"
:show-badge="item.showBadge"
is-compact
/>
</woot-tabs>
</div>
@@ -3,7 +3,7 @@ import WidgetHead from './WidgetHead.vue';
import WidgetBody from './WidgetBody.vue';
import WidgetFooter from './WidgetFooter.vue';
import InputRadioGroup from 'dashboard/routes/dashboard/settings/inbox/components/InputRadioGroup.vue';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
import { useBranding } from 'shared/composables/useBranding';
import { mapGetters } from 'vuex';
export default {
@@ -14,7 +14,6 @@ export default {
WidgetFooter,
InputRadioGroup,
},
mixins: [globalConfigMixin],
props: {
welcomeHeading: {
type: String,
@@ -57,6 +56,12 @@ export default {
default: '',
},
},
setup() {
const { replaceInstallationName } = useBranding();
return {
replaceInstallationName,
};
},
data() {
return {
widgetScreens: [
@@ -140,7 +145,7 @@ export default {
</div>
<div
v-if="isWidgetVisible"
class="widget-wrapper flex flex-col justify-between rounded-lg shadow-md bg-slate-25 dark:bg-slate-800 h-[31.25rem] w-80"
class="widget-wrapper flex flex-col justify-between rounded-lg shadow-md bg-n-slate-2 dark:bg-n-solid-1 h-[31.25rem] w-80"
>
<WidgetHead :config="getWidgetConfig" />
<div>
@@ -151,7 +156,7 @@ export default {
<WidgetFooter :config="getWidgetConfig" />
<div class="py-2.5 flex justify-center">
<a
class="items-center gap-0.5 text-slate-500 dark:text-slate-400 cursor-pointer flex filter grayscale opacity-90 hover:grayscale-0 hover:opacity-100 text-xxs"
class="items-center gap-0.5 text-n-slate-11 cursor-pointer flex filter grayscale opacity-90 hover:grayscale-0 hover:opacity-100 text-xxs"
>
<img
class="max-w-2.5 max-h-2.5"
@@ -159,9 +164,8 @@ export default {
/>
<span>
{{
useInstallationName(
$t('INBOX_MGMT.WIDGET_BUILDER.BRANDING_TEXT'),
globalConfig.installationName
replaceInstallationName(
$t('INBOX_MGMT.WIDGET_BUILDER.BRANDING_TEXT')
)
}}
</span>
@@ -28,7 +28,7 @@ defineProps({
</div>
<div
class="bg-white shadow rounded-[1.25rem] rounded-bl-[0.25rem] px-4 py-3 inline-block text-sm text-slate-900 dark:text-white dark:bg-slate-700"
class="shadow rounded-[1.25rem] rounded-bl-[0.25rem] px-4 py-3 inline-block text-sm text-n-slate-12 bg-n-background dark:bg-n-solid-3"
>
<div>
<p class="m-0">
@@ -2,7 +2,7 @@
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import Avatar from 'next/avatar/Avatar.vue';
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
const props = defineProps({
@@ -27,24 +27,24 @@ const getStatusText = computed(() => {
<div class="relative flex flex-col w-full px-4">
<div
v-if="config.isDefaultScreen"
class="p-4 bg-white rounded-md shadow-sm dark:bg-slate-700"
class="p-4 rounded-md shadow-sm bg-n-background dark:bg-n-solid-2"
>
<div class="flex items-center justify-between">
<div>
<div
class="text-sm font-medium leading-4 text-slate-700 dark:text-slate-50"
class="text-sm font-medium leading-4 text-n-slate-12 dark:text-n-slate-50"
>
{{ getStatusText }}
</div>
<div class="mt-1 text-xs text-slate-500 dark:text-slate-100">
<div class="mt-1 text-xs text-n-slate-11">
{{ config.replyTime }}
</div>
</div>
<Thumbnail username="C" size="34px" />
<Avatar name="C" :size="34" rounded-full />
</div>
<button
v-if="config.isDefaultScreen"
class="inline-flex items-center justify-between px-2 py-1 mt-1 -ml-2 font-medium leading-6 bg-transparent rounded-md text-slate-800 dark:bg-transparent dark:text-slate-50 hover:bg-slate-25 dark:hover:bg-slate-800"
class="inline-flex items-center justify-between px-2 py-1 mt-1 -ml-2 font-medium leading-6 bg-transparent rounded-md text-n-slate-12 dark:bg-transparent"
:style="{ color: config.color }"
>
<span class="pr-2 text-xs">
@@ -59,8 +59,8 @@ const getStatusText = computed(() => {
</div>
<div
v-else
class="flex items-center h-10 bg-white rounded-md dark:bg-slate-700"
:class="{ 'ring-2 ring-woot-500 dark:ring-woot-400': isInputFocused }"
class="flex items-center h-10 bg-white rounded-md dark:!bg-n-slate-3"
:class="{ 'ring-2 ring-n-brand dark:ring-n-brand': isInputFocused }"
>
<ResizableTextArea
id="chat-input"
@@ -68,7 +68,7 @@ const getStatusText = computed(() => {
:placeholder="
$t('INBOX_MGMT.WIDGET_BUILDER.FOOTER.CHAT_INPUT_PLACEHOLDER')
"
class="flex-grow !bg-white border-0 outline-none !outline-0 border-none h-8 text-sm dark:!bg-slate-700 pb-0 !pt-1.5 resize-none px-3 !mb-0 focus:outline-none rounded-md"
class="flex-grow !bg-white border-0 outline-none !outline-0 border-none h-8 text-sm dark:!bg-n-slate-3 pb-0 !pt-1.5 resize-none px-3 !mb-0 focus:outline-none rounded-md"
@focus="isInputFocused = true"
@blur="isInputFocused = false"
/>
@@ -26,8 +26,8 @@ const isDefaultScreen = computed(() => {
class="rounded-t-lg flex-shrink-0 transition-[max-height] duration-300"
:class="
isDefaultScreen
? 'bg-slate-25 dark:bg-slate-800 px-4 py-5'
: 'bg-white dark:bg-slate-900 p-4'
? 'bg-n-slate-2 dark:bg-n-solid-1 px-4 py-5'
: 'bg-n-slate-2 dark:bg-n-solid-1 p-4'
"
>
<div class="relative top-px">
@@ -40,17 +40,15 @@ const isDefaultScreen = computed(() => {
/>
<div v-if="!isDefaultScreen">
<div class="flex items-center justify-start gap-1">
<span
class="text-base font-medium leading-3 text-slate-900 dark:text-white"
>
<span class="text-base font-medium leading-3 text-n-slate-12">
{{ config.websiteName }}
</span>
<div
v-if="config.isOnline"
class="w-2 h-2 bg-green-500 rounded-full"
class="w-2 h-2 bg-n-teal-10 rounded-full"
/>
</div>
<span class="mt-1 text-xs text-slate-600 dark:text-slate-400">
<span class="mt-1 text-xs text-n-slate-11">
{{ config.replyTime }}
</span>
</div>