Merge branch 'develop' into feat/ui-lib

This commit is contained in:
Muhsin Keloth
2025-06-26 11:07:34 +05:30
committed by GitHub
783 changed files with 12026 additions and 2063 deletions
@@ -123,7 +123,7 @@ const handleDocumentableClick = () => {
@mouseenter="emit('hover', true)"
@mouseleave="emit('hover', false)"
>
<div v-show="selectable" class="absolute top-7 ltr:left-4 rtl:right-4">
<div v-show="selectable" class="absolute top-7 ltr:left-3 rtl:right-3">
<Checkbox v-model="modelValue" />
</div>
<div class="flex relative justify-between w-full gap-1">
@@ -19,6 +19,7 @@ const isConversationRoute = computed(() => {
'conversation_through_mentions',
'conversation_through_unattended',
'conversation_through_participating',
'inbox_view_conversation',
];
return CONVERSATION_ROUTES.includes(route.name);
});
@@ -13,6 +13,7 @@ export function useChannelIcon(inbox) {
'Channel::WebWidget': 'i-ri-global-fill',
'Channel::Whatsapp': 'i-ri-whatsapp-fill',
'Channel::Instagram': 'i-ri-instagram-fill',
'Channel::Voice': 'i-ri-phone-fill',
};
const providerIconMap = {
@@ -19,6 +19,12 @@ describe('useChannelIcon', () => {
expect(icon).toBe('i-ri-whatsapp-fill');
});
it('returns correct icon for Voice channel', () => {
const inbox = { channel_type: 'Channel::Voice' };
const { value: icon } = useChannelIcon(inbox);
expect(icon).toBe('i-ri-phone-fill');
});
describe('Email channel', () => {
it('returns mail icon for generic email channel', () => {
const inbox = { channel_type: 'Channel::Email' };
@@ -1,51 +1,21 @@
<script setup>
import { computed, ref, onMounted, nextTick } from 'vue';
import { computed, ref, onMounted, nextTick, getCurrentInstance } from 'vue';
const props = defineProps({
modelValue: {
type: [String, Number],
default: '',
},
type: {
type: String,
default: 'text',
},
customInputClass: {
type: [String, Object, Array],
default: '',
},
placeholder: {
type: String,
default: '',
},
label: {
type: String,
default: '',
},
id: {
type: String,
default: '',
},
message: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
modelValue: { type: [String, Number], default: '' },
type: { type: String, default: 'text' },
customInputClass: { type: [String, Object, Array], default: '' },
placeholder: { type: String, default: '' },
label: { type: String, default: '' },
id: { type: String, default: '' },
message: { type: String, default: '' },
disabled: { type: Boolean, default: false },
messageType: {
type: String,
default: 'info',
validator: value => ['info', 'error', 'success'].includes(value),
},
min: {
type: String,
default: '',
},
autofocus: {
type: Boolean,
default: false,
},
min: { type: String, default: '' },
autofocus: { type: Boolean, default: false },
});
const emit = defineEmits([
@@ -56,6 +26,10 @@ const emit = defineEmits([
'enter',
]);
// Generate a unique ID per component instance when `id` prop is not provided.
const { uid } = getCurrentInstance();
const uniqueId = computed(() => props.id || `input-${uid}`);
const isFocused = ref(false);
const inputRef = ref(null);
@@ -111,7 +85,7 @@ onMounted(() => {
<div class="relative flex flex-col min-w-0 gap-1">
<label
v-if="label"
:for="id"
:for="uniqueId"
class="mb-0.5 text-sm font-medium text-n-slate-12"
>
{{ label }}
@@ -119,7 +93,7 @@ onMounted(() => {
<!-- Added prefix slot to allow adding icons to the input -->
<slot name="prefix" />
<input
:id="id"
:id="uniqueId"
ref="inputRef"
:value="modelValue"
:class="[
@@ -379,7 +379,7 @@ const shouldRenderMessage = computed(() => {
function openContextMenu(e) {
const shouldSkipContextMenu =
e.target?.classList.contains('skip-context-menu') ||
e.target?.tagName.toLowerCase() === 'a';
['a', 'img'].includes(e.target?.tagName.toLowerCase());
if (shouldSkipContextMenu || getSelection().toString()) {
return;
}
@@ -14,7 +14,8 @@ const fromEmail = computed(() => {
});
const toEmail = computed(() => {
return contentAttributes.value?.email?.to ?? [];
const { toEmails, email } = contentAttributes.value;
return email?.to ?? toEmails ?? [];
});
const ccEmail = computed(() => {
@@ -4,9 +4,11 @@ import BaseBubble from './Base.vue';
import { useI18n } from 'vue-i18n';
import { CONTENT_TYPES } from '../constants.js';
import { useMessageContext } from '../provider.js';
import { useInbox } from 'dashboard/composables/useInbox';
const { content, contentAttributes, contentType } = useMessageContext();
const { t } = useI18n();
const { isAWebWidgetInbox } = useInbox();
const formValues = computed(() => {
if (contentType.value === CONTENT_TYPES.FORM) {
@@ -56,7 +58,7 @@ const formValues = computed(() => {
<dd>{{ item.title }}</dd>
</template>
</dl>
<div v-else class="my-2 font-medium">
<div v-else-if="isAWebWidgetInbox" class="my-2 font-medium">
{{ t('CONVERSATION.NO_RESPONSE') }}
</div>
</BaseBubble>
@@ -87,7 +87,7 @@ const newReportRoutes = () => [
{
name: 'Reports Label',
label: t('SIDEBAR.REPORTS_LABEL'),
to: accountScopedRoute('label_reports'),
to: accountScopedRoute('label_reports_index'),
},
{
name: 'Reports Inbox',