fix: Fix issues with contact routes in old navigation sidebar (#10547)
This commit is contained in:
@@ -20,7 +20,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['goToContactsList']);
|
const emit = defineEmits(['goToContactsList', 'resetTab']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
@@ -74,6 +74,9 @@ const onContactSearch = debounce(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const resetState = () => {
|
const resetState = () => {
|
||||||
|
if (state.primaryContactId === null) {
|
||||||
|
emit('resetTab');
|
||||||
|
}
|
||||||
state.primaryContactId = null;
|
state.primaryContactId = null;
|
||||||
searchResults.value = [];
|
searchResults.value = [];
|
||||||
isSearching.value = false;
|
isSearching.value = false;
|
||||||
|
|||||||
@@ -130,7 +130,19 @@ const handleAvatarDelete = async () => {
|
|||||||
<h3 class="text-base font-medium text-n-slate-12">
|
<h3 class="text-base font-medium text-n-slate-12">
|
||||||
{{ selectedContact?.name }}
|
{{ selectedContact?.name }}
|
||||||
</h3>
|
</h3>
|
||||||
<span class="text-sm text-n-slate-11">
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<span
|
||||||
|
v-if="selectedContact?.identifier"
|
||||||
|
class="inline-flex items-center gap-1 text-sm text-n-slate-11"
|
||||||
|
>
|
||||||
|
<span class="i-ph-user-gear text-n-slate-10 size-4" />
|
||||||
|
{{ selectedContact?.identifier }}
|
||||||
|
</span>
|
||||||
|
<span class="inline-flex items-center gap-1 text-sm text-n-slate-11">
|
||||||
|
<span
|
||||||
|
v-if="selectedContact?.identifier"
|
||||||
|
class="i-ph-activity text-n-slate-10 size-4"
|
||||||
|
/>
|
||||||
{{ $t('CONTACTS_LAYOUT.DETAILS.CREATED_AT', { date: createdAt }) }}
|
{{ $t('CONTACTS_LAYOUT.DETAILS.CREATED_AT', { date: createdAt }) }}
|
||||||
•
|
•
|
||||||
{{
|
{{
|
||||||
@@ -140,6 +152,7 @@ const handleAvatarDelete = async () => {
|
|||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<ContactLabels :contact-id="selectedContact?.id" />
|
<ContactLabels :contact-id="selectedContact?.id" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-start gap-6">
|
<div class="flex flex-col items-start gap-6">
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { INBOX_TYPES } from 'dashboard/helper/inbox';
|
import { INBOX_TYPES } from 'dashboard/helper/inbox';
|
||||||
|
import { getInboxIconByType } from 'dashboard/helper/inbox';
|
||||||
import camelcaseKeys from 'camelcase-keys';
|
import camelcaseKeys from 'camelcase-keys';
|
||||||
import ContactAPI from 'dashboard/api/contacts';
|
import ContactAPI from 'dashboard/api/contacts';
|
||||||
|
|
||||||
export const convertChannelTypeToLabel = channelType => {
|
|
||||||
const [, type] = channelType.split('::');
|
|
||||||
return type ? type.charAt(0).toUpperCase() + type.slice(1) : channelType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateLabelForContactableInboxesList = ({
|
export const generateLabelForContactableInboxesList = ({
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
@@ -22,7 +18,7 @@ export const generateLabelForContactableInboxesList = ({
|
|||||||
) {
|
) {
|
||||||
return `${name} (${phoneNumber})`;
|
return `${name} (${phoneNumber})`;
|
||||||
}
|
}
|
||||||
return `${name} (${convertChannelTypeToLabel(channelType)})`;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildContactableInboxesList = contactInboxes => {
|
export const buildContactableInboxesList = contactInboxes => {
|
||||||
@@ -30,6 +26,7 @@ export const buildContactableInboxesList = contactInboxes => {
|
|||||||
return contactInboxes.map(
|
return contactInboxes.map(
|
||||||
({ name, id, email, channelType, phoneNumber, ...rest }) => ({
|
({ name, id, email, channelType, phoneNumber, ...rest }) => ({
|
||||||
id,
|
id,
|
||||||
|
icon: getInboxIconByType(channelType, phoneNumber, 'line'),
|
||||||
label: generateLabelForContactableInboxesList({
|
label: generateLabelForContactableInboxesList({
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
|
|||||||
@@ -6,19 +6,6 @@ import * as helpers from '../composeConversationHelper';
|
|||||||
vi.mock('dashboard/api/contacts');
|
vi.mock('dashboard/api/contacts');
|
||||||
|
|
||||||
describe('composeConversationHelper', () => {
|
describe('composeConversationHelper', () => {
|
||||||
describe('convertChannelTypeToLabel', () => {
|
|
||||||
it('converts channel type with namespace to capitalized label', () => {
|
|
||||||
expect(helpers.convertChannelTypeToLabel('Channel::Email')).toBe('Email');
|
|
||||||
expect(helpers.convertChannelTypeToLabel('Channel::Whatsapp')).toBe(
|
|
||||||
'Whatsapp'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns original value if no namespace found', () => {
|
|
||||||
expect(helpers.convertChannelTypeToLabel('email')).toBe('email');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('generateLabelForContactableInboxesList', () => {
|
describe('generateLabelForContactableInboxesList', () => {
|
||||||
const contact = {
|
const contact = {
|
||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
@@ -59,7 +46,7 @@ describe('composeConversationHelper', () => {
|
|||||||
...contact,
|
...contact,
|
||||||
channelType: 'Channel::Api',
|
channelType: 'Channel::Api',
|
||||||
})
|
})
|
||||||
).toBe('John Doe (Api)');
|
).toBe('John Doe');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -83,6 +70,7 @@ describe('composeConversationHelper', () => {
|
|||||||
const result = helpers.buildContactableInboxesList(inboxes);
|
const result = helpers.buildContactableInboxesList(inboxes);
|
||||||
expect(result[0]).toMatchObject({
|
expect(result[0]).toMatchObject({
|
||||||
id: 1,
|
id: 1,
|
||||||
|
icon: 'i-ri-mail-line',
|
||||||
label: 'Email Inbox (support@example.com)',
|
label: 'Email Inbox (support@example.com)',
|
||||||
action: 'inbox',
|
action: 'inbox',
|
||||||
value: 1,
|
value: 1,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const contacts = accountId => ({
|
|||||||
icon: 'contact-card-group',
|
icon: 'contact-card-group',
|
||||||
label: 'ALL_CONTACTS',
|
label: 'ALL_CONTACTS',
|
||||||
hasSubMenu: false,
|
hasSubMenu: false,
|
||||||
toState: frontendURL(`accounts/${accountId}/contacts`),
|
toState: frontendURL(`accounts/${accountId}/contacts?page=1`),
|
||||||
toStateName: 'contacts_dashboard_index',
|
toStateName: 'contacts_dashboard_index',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default {
|
|||||||
icon: 'number-symbol',
|
icon: 'number-symbol',
|
||||||
label: 'TAGGED_WITH',
|
label: 'TAGGED_WITH',
|
||||||
hasSubMenu: true,
|
hasSubMenu: true,
|
||||||
key: 'label',
|
key: 'labels',
|
||||||
newLink: this.showNewLink(FEATURE_FLAGS.TEAM_MANAGEMENT),
|
newLink: this.showNewLink(FEATURE_FLAGS.TEAM_MANAGEMENT),
|
||||||
newLinkTag: 'NEW_LABEL',
|
newLinkTag: 'NEW_LABEL',
|
||||||
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
|
toState: frontendURL(`accounts/${this.accountId}/settings/labels`),
|
||||||
@@ -147,7 +147,7 @@ export default {
|
|||||||
color: label.color,
|
color: label.color,
|
||||||
truncateLabel: true,
|
truncateLabel: true,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/labels/${label.title}/contacts`
|
`accounts/${this.accountId}/contacts/labels/${label.title}`
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -194,7 +194,7 @@ export default {
|
|||||||
icon: 'folder',
|
icon: 'folder',
|
||||||
label: 'CUSTOM_VIEWS_SEGMENTS',
|
label: 'CUSTOM_VIEWS_SEGMENTS',
|
||||||
hasSubMenu: true,
|
hasSubMenu: true,
|
||||||
key: 'custom_view',
|
key: 'segments',
|
||||||
children: this.customViews
|
children: this.customViews
|
||||||
.filter(view => view.filter_type === 'contact')
|
.filter(view => view.filter_type === 'contact')
|
||||||
.map(view => ({
|
.map(view => ({
|
||||||
@@ -202,7 +202,7 @@ export default {
|
|||||||
label: view.name,
|
label: view.name,
|
||||||
truncateLabel: true,
|
truncateLabel: true,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/contacts/custom_view/${view.id}`
|
`accounts/${this.accountId}/contacts/segments/${view.id}`
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -247,7 +247,7 @@ export default {
|
|||||||
<transition-group
|
<transition-group
|
||||||
name="menu-list"
|
name="menu-list"
|
||||||
tag="ul"
|
tag="ul"
|
||||||
class="pt-2 reset-base list-none"
|
class="pt-2 list-none reset-base"
|
||||||
>
|
>
|
||||||
<SecondaryNavItem
|
<SecondaryNavItem
|
||||||
v-for="menuItem in accessibleMenuItems"
|
v-for="menuItem in accessibleMenuItems"
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ export const INBOX_TYPES = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const INBOX_ICON_MAP = {
|
const INBOX_ICON_MAP = {
|
||||||
[INBOX_TYPES.WEB]: 'i-ri-global-fill',
|
[INBOX_TYPES.WEB]: 'i-ri-global',
|
||||||
[INBOX_TYPES.FB]: 'i-ri-messenger-fill',
|
[INBOX_TYPES.FB]: 'i-ri-messenger',
|
||||||
[INBOX_TYPES.TWITTER]: 'i-ri-twitter-x-fill',
|
[INBOX_TYPES.TWITTER]: 'i-ri-twitter-x',
|
||||||
[INBOX_TYPES.WHATSAPP]: 'i-ri-whatsapp-fill',
|
[INBOX_TYPES.WHATSAPP]: 'i-ri-whatsapp',
|
||||||
[INBOX_TYPES.API]: 'i-ri-cloudy-fill',
|
[INBOX_TYPES.API]: 'i-ri-cloudy',
|
||||||
[INBOX_TYPES.EMAIL]: 'i-ri-mail-fill',
|
[INBOX_TYPES.EMAIL]: 'i-ri-mail',
|
||||||
[INBOX_TYPES.TELEGRAM]: 'i-ri-telegram-fill',
|
[INBOX_TYPES.TELEGRAM]: 'i-ri-telegram',
|
||||||
[INBOX_TYPES.LINE]: 'i-ri-line-fill',
|
[INBOX_TYPES.LINE]: 'i-ri-line',
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_ICON = 'i-ri-chat-1-fill';
|
const DEFAULT_ICON = 'i-ri-chat-1';
|
||||||
|
|
||||||
export const getInboxSource = (type, phoneNumber, inbox) => {
|
export const getInboxSource = (type, phoneNumber, inbox) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -110,15 +110,16 @@ export const getInboxClassByType = (type, phoneNumber) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInboxIconByType = (type, phoneNumber) => {
|
export const getInboxIconByType = (type, phoneNumber, variant = 'fill') => {
|
||||||
// Special case for Twilio (whatsapp and sms)
|
// Special case for Twilio (whatsapp and sms)
|
||||||
if (type === INBOX_TYPES.TWILIO) {
|
if (type === INBOX_TYPES.TWILIO) {
|
||||||
return phoneNumber?.startsWith('whatsapp')
|
return phoneNumber?.startsWith('whatsapp')
|
||||||
? 'i-ri-whatsapp-fill'
|
? `i-ri-whatsapp-${variant}`
|
||||||
: 'i-ri-chat-1-fill';
|
: `i-ri-chat-1-${variant}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return INBOX_ICON_MAP[type] ?? DEFAULT_ICON;
|
const baseIcon = INBOX_ICON_MAP[type] ?? DEFAULT_ICON;
|
||||||
|
return `${baseIcon}-${variant}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInboxWarningIconClass = (type, reauthorizationRequired) => {
|
export const getInboxWarningIconClass = (type, reauthorizationRequired) => {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ describe('#Inbox Helpers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getInboxIconByType', () => {
|
describe('getInboxIconByType', () => {
|
||||||
|
describe('fill variant (default)', () => {
|
||||||
it('returns correct icon for web widget', () => {
|
it('returns correct icon for web widget', () => {
|
||||||
expect(getInboxIconByType(INBOX_TYPES.WEB)).toBe('i-ri-global-fill');
|
expect(getInboxIconByType(INBOX_TYPES.WEB)).toBe('i-ri-global-fill');
|
||||||
});
|
});
|
||||||
@@ -55,26 +56,6 @@ describe('#Inbox Helpers', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Twilio cases', () => {
|
|
||||||
it('returns WhatsApp icon for Twilio WhatsApp number', () => {
|
|
||||||
expect(
|
|
||||||
getInboxIconByType(INBOX_TYPES.TWILIO, 'whatsapp:+1234567890')
|
|
||||||
).toBe('i-ri-whatsapp-fill');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns SMS icon for regular Twilio number', () => {
|
|
||||||
expect(getInboxIconByType(INBOX_TYPES.TWILIO, '+1234567890')).toBe(
|
|
||||||
'i-ri-chat-1-fill'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns SMS icon when phone number is undefined', () => {
|
|
||||||
expect(getInboxIconByType(INBOX_TYPES.TWILIO, undefined)).toBe(
|
|
||||||
'i-ri-chat-1-fill'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns correct icon for WhatsApp', () => {
|
it('returns correct icon for WhatsApp', () => {
|
||||||
expect(getInboxIconByType(INBOX_TYPES.WHATSAPP)).toBe(
|
expect(getInboxIconByType(INBOX_TYPES.WHATSAPP)).toBe(
|
||||||
'i-ri-whatsapp-fill'
|
'i-ri-whatsapp-fill'
|
||||||
@@ -108,6 +89,67 @@ describe('#Inbox Helpers', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('line variant', () => {
|
||||||
|
it('returns correct line icon for web widget', () => {
|
||||||
|
expect(getInboxIconByType(INBOX_TYPES.WEB, null, 'line')).toBe(
|
||||||
|
'i-ri-global-line'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct line icon for Facebook', () => {
|
||||||
|
expect(getInboxIconByType(INBOX_TYPES.FB, null, 'line')).toBe(
|
||||||
|
'i-ri-messenger-line'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct line icon for unknown type', () => {
|
||||||
|
expect(getInboxIconByType('UNKNOWN_TYPE', null, 'line')).toBe(
|
||||||
|
'i-ri-chat-1-line'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Twilio cases', () => {
|
||||||
|
describe('fill variant', () => {
|
||||||
|
it('returns WhatsApp icon for Twilio WhatsApp number', () => {
|
||||||
|
expect(
|
||||||
|
getInboxIconByType(INBOX_TYPES.TWILIO, 'whatsapp:+1234567890')
|
||||||
|
).toBe('i-ri-whatsapp-fill');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns SMS icon for regular Twilio number', () => {
|
||||||
|
expect(getInboxIconByType(INBOX_TYPES.TWILIO, '+1234567890')).toBe(
|
||||||
|
'i-ri-chat-1-fill'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns SMS icon when phone number is undefined', () => {
|
||||||
|
expect(getInboxIconByType(INBOX_TYPES.TWILIO, undefined)).toBe(
|
||||||
|
'i-ri-chat-1-fill'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('line variant', () => {
|
||||||
|
it('returns WhatsApp line icon for Twilio WhatsApp number', () => {
|
||||||
|
expect(
|
||||||
|
getInboxIconByType(
|
||||||
|
INBOX_TYPES.TWILIO,
|
||||||
|
'whatsapp:+1234567890',
|
||||||
|
'line'
|
||||||
|
)
|
||||||
|
).toBe('i-ri-whatsapp-line');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns SMS line icon for regular Twilio number', () => {
|
||||||
|
expect(
|
||||||
|
getInboxIconByType(INBOX_TYPES.TWILIO, '+1234567890', 'line')
|
||||||
|
).toBe('i-ri-chat-1-line');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getInboxWarningIconClass', () => {
|
describe('getInboxWarningIconClass', () => {
|
||||||
it('should return correct class for warning', () => {
|
it('should return correct class for warning', () => {
|
||||||
expect(getInboxWarningIconClass('Channel::FacebookPage', true)).toEqual(
|
expect(getInboxWarningIconClass('Channel::FacebookPage', true)).toEqual(
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ onMounted(() => {
|
|||||||
ref="contactMergeRef"
|
ref="contactMergeRef"
|
||||||
:selected-contact="selectedContact"
|
:selected-contact="selectedContact"
|
||||||
@go-to-contacts-list="goToContactsList"
|
@go-to-contacts-list="goToContactsList"
|
||||||
|
@reset-tab="handleTabChange(CONTACT_TABS_OPTIONS[0])"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user