+import { useExpandableContent } from 'shared/composables/useExpandableContent';
+
+defineProps({
+ text: {
+ type: String,
+ default: '',
+ },
+});
+
+const { contentElement, showReadMore, showReadLess, toggleExpanded } =
+ useExpandableContent({ useResizeObserverForCheck: true });
+
+
+
+
+ {{ $t('SEARCH.TRANSCRIPT') }}
+
+
+
+ {{ text }}
+
+
+
+
+
diff --git a/app/javascript/dashboard/modules/search/helpers/searchHelper.js b/app/javascript/dashboard/modules/search/helpers/searchHelper.js
new file mode 100644
index 000000000..ebdb3e579
--- /dev/null
+++ b/app/javascript/dashboard/modules/search/helpers/searchHelper.js
@@ -0,0 +1,75 @@
+import ContactAPI from 'dashboard/api/contacts';
+
+export const DATE_RANGE_TYPES = {
+ LAST_7_DAYS: 'last_7_days',
+ LAST_30_DAYS: 'last_30_days',
+ LAST_60_DAYS: 'last_60_days',
+ LAST_90_DAYS: 'last_90_days',
+ CUSTOM: 'custom',
+ BETWEEN: 'between',
+};
+
+export const generateURLParams = (
+ { from, in: inboxId, dateRange },
+ isAdvancedSearchEnabled = true
+) => {
+ const params = {};
+
+ // Only include filter params if advanced search is enabled
+ if (isAdvancedSearchEnabled) {
+ if (from) params.from = from;
+ if (inboxId) params.inbox_id = inboxId;
+
+ if (dateRange?.type) {
+ const { type, from: dateFrom, to: dateTo } = dateRange;
+ params.range = type;
+
+ if (dateFrom) params.since = dateFrom;
+ if (dateTo) params.until = dateTo;
+ }
+ }
+
+ return params;
+};
+
+export const parseURLParams = (query, isAdvancedSearchEnabled = true) => {
+ // If advanced search is disabled, return empty filters
+ if (!isAdvancedSearchEnabled) {
+ return {
+ from: null,
+ in: null,
+ dateRange: {
+ type: null,
+ from: null,
+ to: null,
+ },
+ };
+ }
+
+ const { from, inbox_id, since, until, range } = query;
+
+ let type = range;
+ if (!type && (since || until)) {
+ type = DATE_RANGE_TYPES.BETWEEN;
+ }
+
+ return {
+ from: from || null,
+ in: inbox_id ? Number(inbox_id) : null,
+ dateRange: {
+ type,
+ from: since ? Number(since) : null,
+ to: until ? Number(until) : null,
+ },
+ };
+};
+
+export const fetchContactDetails = async id => {
+ try {
+ const response = await ContactAPI.show(id);
+ return response.data.payload;
+ } catch (error) {
+ // error
+ return null;
+ }
+};
diff --git a/app/javascript/dashboard/modules/search/helpers/specs/searchHelper.spec.js b/app/javascript/dashboard/modules/search/helpers/specs/searchHelper.spec.js
new file mode 100644
index 000000000..5f34e32f2
--- /dev/null
+++ b/app/javascript/dashboard/modules/search/helpers/specs/searchHelper.spec.js
@@ -0,0 +1,379 @@
+import ContactAPI from 'dashboard/api/contacts';
+import {
+ DATE_RANGE_TYPES,
+ generateURLParams,
+ parseURLParams,
+ fetchContactDetails,
+} from '../searchHelper';
+
+// Mock ContactAPI
+vi.mock('dashboard/api/contacts', () => ({
+ default: {
+ show: vi.fn(),
+ },
+}));
+
+describe('#generateURLParams', () => {
+ it('returns empty object when no parameters provided', () => {
+ expect(generateURLParams({})).toEqual({});
+ });
+
+ it('generates params with from parameter', () => {
+ const result = generateURLParams({ from: 'agent:123' });
+ expect(result).toEqual({ from: 'agent:123' });
+ });
+
+ it('generates params with inbox_id parameter', () => {
+ const result = generateURLParams({ in: 456 });
+ expect(result).toEqual({ inbox_id: 456 });
+ });
+
+ it('generates params with all basic parameters', () => {
+ const result = generateURLParams({
+ from: 'contact:789',
+ in: 123,
+ });
+ expect(result).toEqual({
+ from: 'contact:789',
+ inbox_id: 123,
+ });
+ });
+
+ describe('with date range', () => {
+ it('generates params with date range type only', () => {
+ const result = generateURLParams({
+ dateRange: { type: DATE_RANGE_TYPES.LAST_7_DAYS },
+ });
+ expect(result).toEqual({
+ range: 'last_7_days',
+ });
+ });
+
+ it('generates params with BETWEEN date range', () => {
+ const result = generateURLParams({
+ dateRange: {
+ type: DATE_RANGE_TYPES.BETWEEN,
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ expect(result).toEqual({
+ range: 'between',
+ since: 1640995200,
+ until: 1672531199,
+ });
+ });
+
+ it('generates params with CUSTOM date range', () => {
+ const result = generateURLParams({
+ dateRange: {
+ type: DATE_RANGE_TYPES.CUSTOM,
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ expect(result).toEqual({
+ range: 'custom',
+ since: 1640995200,
+ until: 1672531199,
+ });
+ });
+
+ it('handles date range with missing from/to values', () => {
+ const result = generateURLParams({
+ dateRange: {
+ type: DATE_RANGE_TYPES.BETWEEN,
+ from: null,
+ to: undefined,
+ },
+ });
+ expect(result).toEqual({
+ range: 'between',
+ });
+ });
+ });
+
+ it('generates params with all parameters combined', () => {
+ const result = generateURLParams({
+ from: 'agent:456',
+ in: 789,
+ dateRange: {
+ type: DATE_RANGE_TYPES.BETWEEN,
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ expect(result).toEqual({
+ from: 'agent:456',
+ inbox_id: 789,
+ range: 'between',
+ since: 1640995200,
+ until: 1672531199,
+ });
+ });
+
+ describe('when advanced search is disabled', () => {
+ it('returns empty object when isAdvancedSearchEnabled is false', () => {
+ const result = generateURLParams(
+ {
+ from: 'agent:123',
+ in: 456,
+ dateRange: {
+ type: DATE_RANGE_TYPES.BETWEEN,
+ from: 1640995200,
+ to: 1672531199,
+ },
+ },
+ false
+ );
+ expect(result).toEqual({});
+ });
+
+ it('strips all filter params when feature flag is disabled', () => {
+ const result = generateURLParams(
+ {
+ from: 'contact:789',
+ in: 123,
+ },
+ false
+ );
+ expect(result).toEqual({});
+ });
+
+ it('strips date range params when feature flag is disabled', () => {
+ const result = generateURLParams(
+ {
+ dateRange: {
+ type: DATE_RANGE_TYPES.LAST_7_DAYS,
+ from: 1640995200,
+ to: 1672531199,
+ },
+ },
+ false
+ );
+ expect(result).toEqual({});
+ });
+ });
+});
+
+describe('#parseURLParams', () => {
+ it('returns default values for empty query', () => {
+ const result = parseURLParams({});
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: undefined,
+ from: null,
+ to: null,
+ },
+ });
+ });
+
+ it('parses from parameter', () => {
+ const result = parseURLParams({ from: 'agent:123' });
+ expect(result).toEqual({
+ from: 'agent:123',
+ in: null,
+ dateRange: {
+ type: undefined,
+ from: null,
+ to: null,
+ },
+ });
+ });
+
+ it('parses inbox_id parameter as number', () => {
+ const result = parseURLParams({ inbox_id: '456' });
+ expect(result).toEqual({
+ from: null,
+ in: 456,
+ dateRange: {
+ type: undefined,
+ from: null,
+ to: null,
+ },
+ });
+ });
+
+ it('parses explicit range parameter', () => {
+ const result = parseURLParams({
+ range: 'last_7_days',
+ since: '1640995200',
+ until: '1672531199',
+ });
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: 'last_7_days',
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ });
+
+ describe('inferred date range types', () => {
+ it('infers BETWEEN type when both since and until are present', () => {
+ const result = parseURLParams({
+ since: '1640995200',
+ until: '1672531199',
+ });
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: 'between',
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ });
+
+ it('prioritizes explicit range over inferred type', () => {
+ const result = parseURLParams({
+ range: 'custom',
+ since: '1640995200',
+ until: '1672531199',
+ });
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: 'custom',
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ });
+ });
+
+ it('parses all parameters combined', () => {
+ const result = parseURLParams({
+ from: 'contact:789',
+ inbox_id: '123',
+ range: 'between',
+ since: '1640995200',
+ until: '1672531199',
+ });
+ expect(result).toEqual({
+ from: 'contact:789',
+ in: 123,
+ dateRange: {
+ type: 'between',
+ from: 1640995200,
+ to: 1672531199,
+ },
+ });
+ });
+
+ describe('when advanced search is disabled', () => {
+ it('returns empty filters when isAdvancedSearchEnabled is false', () => {
+ const result = parseURLParams(
+ {
+ from: 'agent:123',
+ inbox_id: '456',
+ range: 'between',
+ since: '1640995200',
+ until: '1672531199',
+ },
+ false
+ );
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: null,
+ from: null,
+ to: null,
+ },
+ });
+ });
+
+ it('ignores all filter params from URL when feature flag is disabled', () => {
+ const result = parseURLParams(
+ {
+ from: 'contact:789',
+ inbox_id: '123',
+ },
+ false
+ );
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: null,
+ from: null,
+ to: null,
+ },
+ });
+ });
+
+ it('ignores date range params from URL when feature flag is disabled', () => {
+ const result = parseURLParams(
+ {
+ range: 'last_7_days',
+ since: '1640995200',
+ until: '1672531199',
+ },
+ false
+ );
+ expect(result).toEqual({
+ from: null,
+ in: null,
+ dateRange: {
+ type: null,
+ from: null,
+ to: null,
+ },
+ });
+ });
+ });
+});
+
+describe('#fetchContactDetails', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('returns contact data on successful API call', async () => {
+ const mockContactData = {
+ id: 123,
+ name: 'John Doe',
+ email: 'john@example.com',
+ };
+
+ ContactAPI.show.mockResolvedValue({
+ data: {
+ payload: mockContactData,
+ },
+ });
+
+ const result = await fetchContactDetails(123);
+ expect(result).toEqual(mockContactData);
+ expect(ContactAPI.show).toHaveBeenCalledWith(123);
+ });
+
+ it('returns null on API error', async () => {
+ ContactAPI.show.mockRejectedValue(new Error('API Error'));
+
+ const result = await fetchContactDetails(123);
+ expect(result).toBeNull();
+ expect(ContactAPI.show).toHaveBeenCalledWith(123);
+ });
+
+ it('handles different contact ID types', async () => {
+ const mockContactData = { id: 456, name: 'Jane Doe' };
+ ContactAPI.show.mockResolvedValue({
+ data: { payload: mockContactData },
+ });
+
+ // Test with string ID
+ await fetchContactDetails('456');
+ expect(ContactAPI.show).toHaveBeenCalledWith('456');
+
+ // Test with number ID
+ await fetchContactDetails(456);
+ expect(ContactAPI.show).toHaveBeenCalledWith(456);
+ });
+});
diff --git a/app/javascript/dashboard/routes/dashboard/companies/pages/CompaniesIndex.vue b/app/javascript/dashboard/routes/dashboard/companies/pages/CompaniesIndex.vue
index cab05b8f4..236de594a 100644
--- a/app/javascript/dashboard/routes/dashboard/companies/pages/CompaniesIndex.vue
+++ b/app/javascript/dashboard/routes/dashboard/companies/pages/CompaniesIndex.vue
@@ -9,7 +9,7 @@ import { useCompaniesStore } from 'dashboard/stores/companies';
import CompaniesListLayout from 'dashboard/components-next/Companies/CompaniesListLayout.vue';
import CompaniesCard from 'dashboard/components-next/Companies/CompaniesCard/CompaniesCard.vue';
-const DEFAULT_SORT_FIELD = 'created_at';
+const DEFAULT_SORT_FIELD = 'name';
const DEBOUNCE_DELAY = 300;
const companiesStore = useCompaniesStore();
@@ -37,7 +37,7 @@ const parseSortSettings = (sortString = '') => {
};
};
-const { companies_sort_by: companySortBy = `-${DEFAULT_SORT_FIELD}` } =
+const { companies_sort_by: companySortBy = DEFAULT_SORT_FIELD } =
uiSettings.value ?? {};
const { sort: initialSort, order: initialOrder } =
parseSortSettings(companySortBy);
diff --git a/app/javascript/dashboard/routes/dashboard/settings/canned/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/canned/Index.vue
index 216aa1923..0071eab03 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/canned/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/canned/Index.vue
@@ -6,6 +6,7 @@ import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
import { computed, onMounted, ref, defineOptions } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStoreGetters, useStore } from 'dashboard/composables/store';
+import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import Button from 'dashboard/components-next/button/Button.vue';
@@ -17,6 +18,8 @@ const getters = useStoreGetters();
const store = useStore();
const { t } = useI18n();
+const { getPlainText } = useMessageFormatter();
+
const showAddPopup = ref(false);
const loading = ref({});
const showEditPopup = ref(false);
@@ -182,7 +185,7 @@ const tableHeaders = computed(() => {
{{ cannedItem.short_code }}
- {{ cannedItem.content }}
+ {{ getPlainText(cannedItem.content) }}
|
|