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,7 @@
import endPoints from 'widget/api/endPoints';
import { API } from 'widget/helpers/axios';
export const getAvailableAgents = async websiteToken => {
const urlData = endPoints.getAvailableAgents(websiteToken);
return API.get(urlData.url, { params: urlData.params });
};

View File

@@ -0,0 +1,7 @@
import endPoints from 'widget/api/endPoints';
import { API } from 'widget/helpers/axios';
export const getMostReadArticles = async (slug, locale) => {
const urlData = endPoints.getMostReadArticles(slug, locale);
return API.get(urlData.url, { params: urlData.params });
};

View File

@@ -0,0 +1,27 @@
import endPoints from 'widget/api/endPoints';
import { API } from 'widget/helpers/axios';
const getCampaigns = async websiteToken => {
const urlData = endPoints.getCampaigns(websiteToken);
return API.get(urlData.url, { params: urlData.params });
};
const triggerCampaign = async ({
campaignId,
websiteToken,
customAttributes,
}) => {
const urlData = endPoints.triggerCampaign({
websiteToken,
campaignId,
customAttributes,
});
await API.post(
urlData.url,
{ ...urlData.data },
{
params: urlData.params,
}
);
};
export { getCampaigns, triggerCampaign };

View File

@@ -0,0 +1,28 @@
import { API } from 'widget/helpers/axios';
const buildUrl = endPoint => `/api/v1/${endPoint}${window.location.search}`;
export default {
get() {
return API.get(buildUrl('widget/contact'));
},
update(userObject) {
return API.patch(buildUrl('widget/contact'), userObject);
},
setUser(identifier, userObject) {
return API.patch(buildUrl('widget/contact/set_user'), {
identifier,
...userObject,
});
},
setCustomAttributes(customAttributes = {}) {
return API.patch(buildUrl('widget/contact'), {
custom_attributes: customAttributes,
});
},
deleteCustomAttribute(customAttribute) {
return API.post(buildUrl('widget/contact/destroy_custom_attributes'), {
custom_attributes: [customAttribute],
});
},
};

View File

@@ -0,0 +1,82 @@
import endPoints from 'widget/api/endPoints';
import { API } from 'widget/helpers/axios';
const createConversationAPI = async content => {
const urlData = endPoints.createConversation(content);
return API.post(urlData.url, urlData.params);
};
const sendMessageAPI = async (content, replyTo = null) => {
const urlData = endPoints.sendMessage(content, replyTo);
return API.post(urlData.url, urlData.params);
};
const sendAttachmentAPI = async (attachment, replyTo = null) => {
const urlData = endPoints.sendAttachment(attachment, replyTo);
return API.post(urlData.url, urlData.params);
};
const getMessagesAPI = async ({ before, after }) => {
const urlData = endPoints.getConversation({ before, after });
return API.get(urlData.url, { params: urlData.params });
};
const getConversationAPI = async () => {
return API.get(`/api/v1/widget/conversations${window.location.search}`);
};
const toggleTyping = async ({ typingStatus }) => {
return API.post(
`/api/v1/widget/conversations/toggle_typing${window.location.search}`,
{ typing_status: typingStatus }
);
};
const setUserLastSeenAt = async ({ lastSeen }) => {
return API.post(
`/api/v1/widget/conversations/update_last_seen${window.location.search}`,
{ contact_last_seen_at: lastSeen }
);
};
const sendEmailTranscript = async () => {
return API.post(
`/api/v1/widget/conversations/transcript${window.location.search}`
);
};
const toggleStatus = async () => {
return API.get(
`/api/v1/widget/conversations/toggle_status${window.location.search}`
);
};
const setCustomAttributes = async customAttributes => {
return API.post(
`/api/v1/widget/conversations/set_custom_attributes${window.location.search}`,
{
custom_attributes: customAttributes,
}
);
};
const deleteCustomAttribute = async customAttribute => {
return API.post(
`/api/v1/widget/conversations/destroy_custom_attributes${window.location.search}`,
{
custom_attribute: [customAttribute],
}
);
};
export {
createConversationAPI,
sendMessageAPI,
getConversationAPI,
getMessagesAPI,
sendAttachmentAPI,
toggleTyping,
setUserLastSeenAt,
sendEmailTranscript,
toggleStatus,
setCustomAttributes,
deleteCustomAttribute,
};

View File

@@ -0,0 +1,12 @@
import { API } from 'widget/helpers/axios';
const buildUrl = endPoint => `/api/v1/${endPoint}${window.location.search}`;
export default {
create(label) {
return API.post(buildUrl('widget/labels'), { label });
},
destroy(label) {
return API.delete(buildUrl(`widget/labels/${label}`));
},
};

View File

@@ -0,0 +1,120 @@
import { buildSearchParamsWithLocale } from '../helpers/urlParamsHelper';
import { generateEventParams } from './events';
const createConversation = params => {
const referrerURL = window.referrerURL || '';
const search = buildSearchParamsWithLocale(window.location.search);
return {
url: `/api/v1/widget/conversations${search}`,
params: {
contact: {
name: params.fullName,
email: params.emailAddress,
phone_number: params.phoneNumber,
},
message: {
content: params.message,
timestamp: new Date().toString(),
referer_url: referrerURL,
},
custom_attributes: params.customAttributes,
},
};
};
const sendMessage = (content, replyTo) => {
const referrerURL = window.referrerURL || '';
const search = buildSearchParamsWithLocale(window.location.search);
return {
url: `/api/v1/widget/messages${search}`,
params: {
message: {
content,
reply_to: replyTo,
timestamp: new Date().toString(),
referer_url: referrerURL,
},
},
};
};
const sendAttachment = ({ attachment, replyTo = null }) => {
const { referrerURL = '' } = window;
const timestamp = new Date().toString();
const { file } = attachment;
const formData = new FormData();
if (typeof file === 'string') {
formData.append('message[attachments][]', file);
} else {
formData.append('message[attachments][]', file, file.name);
}
formData.append('message[referer_url]', referrerURL);
formData.append('message[timestamp]', timestamp);
if (replyTo !== null) {
formData.append('message[reply_to]', replyTo);
}
return {
url: `/api/v1/widget/messages${window.location.search}`,
params: formData,
};
};
const getConversation = ({ before, after }) => ({
url: `/api/v1/widget/messages${window.location.search}`,
params: { before, after },
});
const updateMessage = id => ({
url: `/api/v1/widget/messages/${id}${window.location.search}`,
});
const getAvailableAgents = token => ({
url: '/api/v1/widget/inbox_members',
params: {
website_token: token,
},
});
const getCampaigns = token => ({
url: '/api/v1/widget/campaigns',
params: {
website_token: token,
},
});
const triggerCampaign = ({ websiteToken, campaignId, customAttributes }) => ({
url: '/api/v1/widget/events',
data: {
name: 'campaign.triggered',
event_info: {
campaign_id: campaignId,
custom_attributes: customAttributes,
...generateEventParams(),
},
},
params: {
website_token: websiteToken,
},
});
const getMostReadArticles = (slug, locale) => ({
url: `/hc/${slug}/${locale}/articles.json`,
params: {
page: 1,
sort: 'views',
status: 1,
per_page: 6,
},
});
export default {
createConversation,
sendMessage,
sendAttachment,
getConversation,
updateMessage,
getAvailableAgents,
getCampaigns,
triggerCampaign,
getMostReadArticles,
};

View File

@@ -0,0 +1,19 @@
import { API } from 'widget/helpers/axios';
import { buildSearchParamsWithLocale } from '../helpers/urlParamsHelper';
export const generateEventParams = () => ({
initiated_at: {
timestamp: new Date().toString(),
},
referer: window.referrerURL || '',
});
export default {
create(name) {
const search = buildSearchParamsWithLocale(window.location.search);
return API.post(`/api/v1/widget/events${search}`, {
name,
event_info: generateEventParams(),
});
},
};

View File

@@ -0,0 +1,12 @@
import { API } from 'widget/helpers/axios';
import { buildSearchParamsWithLocale } from '../helpers/urlParamsHelper';
export default {
addParticipantToDyteMeeting: messageId => {
const search = buildSearchParamsWithLocale(window.location.search);
const urlData = {
url: `/api/v1/widget/integrations/dyte/add_participant_to_meeting${search}`,
};
return API.post(urlData.url, { message_id: messageId });
},
};

View File

@@ -0,0 +1,12 @@
import authEndPoint from 'widget/api/endPoints';
import { API } from 'widget/helpers/axios';
export default {
update: ({ messageId, email, values }) => {
const urlData = authEndPoint.updateMessage(messageId);
return API.patch(urlData.url, {
contact: { email },
message: { submitted_values: values },
});
},
};

View File

@@ -0,0 +1,110 @@
import endPoints from '../endPoints';
describe('#sendMessage', () => {
it('returns correct payload', () => {
const spy = vi.spyOn(global, 'Date').mockImplementation(() => ({
toString: () => 'mock date',
}));
vi.spyOn(window, 'location', 'get').mockReturnValue({
...window.location,
search: '?param=1',
});
window.WOOT_WIDGET = {
$root: {
$i18n: {
locale: 'ar',
},
},
};
expect(endPoints.sendMessage('hello')).toEqual({
url: `/api/v1/widget/messages?param=1&locale=ar`,
params: {
message: {
content: 'hello',
referer_url: '',
timestamp: 'mock date',
},
},
});
spy.mockRestore();
});
});
describe('#getConversation', () => {
it('returns correct payload', () => {
vi.spyOn(window, 'location', 'get').mockReturnValue({
...window.location,
search: '',
});
expect(endPoints.getConversation({ before: 123 })).toEqual({
url: `/api/v1/widget/messages`,
params: {
before: 123,
},
});
});
});
describe('#triggerCampaign', () => {
it('should returns correct payload', () => {
const spy = vi.spyOn(global, 'Date').mockImplementation(() => ({
toString: () => 'mock date',
}));
vi.spyOn(window, 'location', 'get').mockReturnValue({
...window.location,
search: '',
});
const websiteToken = 'ADSDJ2323MSDSDFMMMASDM';
const campaignId = 12;
expect(
endPoints.triggerCampaign({
websiteToken,
campaignId,
})
).toEqual({
url: `/api/v1/widget/events`,
data: {
name: 'campaign.triggered',
event_info: {
campaign_id: campaignId,
referer: '',
initiated_at: {
timestamp: 'mock date',
},
},
},
params: {
website_token: websiteToken,
},
});
spy.mockRestore();
});
});
describe('#getConversation', () => {
it('should returns correct payload', () => {
const spy = vi.spyOn(global, 'Date').mockImplementation(() => ({
toString: () => 'mock date',
}));
vi.spyOn(window, 'location', 'get').mockReturnValue({
...window.location,
search: '',
});
expect(
endPoints.getConversation({
after: 123,
})
).toEqual({
url: `/api/v1/widget/messages`,
params: {
after: 123,
before: undefined,
},
});
spy.mockRestore();
});
});