Restructure omni services and add Chatwoot research snapshot

This commit is contained in:
Ruslan Bakiev
2026-02-21 11:11:27 +07:00
parent edea7a0034
commit b73babbbf6
7732 changed files with 978203 additions and 32 deletions

View File

@@ -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;
}
};

View File

@@ -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);
});
});