Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/lib/common/LoadingToComplete.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
export let isLoading = false;
export let isComplete = false;
export let isError = false;

export let successText = 'Update completed!';
export let errorText = 'Error!';
export let spinnerSize = 50;
</script>

{#if isLoading}
<Loader />
<Loader size={spinnerSize} />
{/if}

{#if isComplete}
<Alert color="success">
<div>Update completed!</div>
<div>{successText}</div>
</Alert>
{/if}

{#if isError}
<Alert color="danger">
<div>Error!</div>
<div>{errorText}</div>
</Alert>
{/if}
2 changes: 1 addition & 1 deletion src/lib/helpers/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function format(datetime, type = 'date') {
* @param {Date} datetime
* @param {string} format - date or time
*/
export function utcToLocal(datetime, format = 'MMM DD YYYY, hh:mm A') {
export function utcToLocal(datetime, format = 'MMM D YYYY, hh:mm A') {
return moment.utc(datetime).local().format(format);
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/helpers/enums.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const userRole = {
System: "system",
User: "user",
Client: "client",
Function: "function",
Assistant: "assistant"
};
Expand Down
22 changes: 16 additions & 6 deletions src/lib/helpers/http.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import axios from 'axios';
import { getUserStore, setGlobalLoad } from '$lib/helpers/store.js';
import { getUserStore, loaderStore } from '$lib/helpers/store.js';

// Add a request interceptor to attach authentication tokens or headers
axios.interceptors.request.use(
(config) => {
// Add your authentication logic here
let user = getUserStore();
setGlobalLoad(true);
if (!skipLoader(config)) {
loaderStore.set(true);
}
// For example, attach an authentication token to the request headers
if (user.token)
config.headers.Authorization = `Bearer ${user.token}`;
return config;
},
(error) => {
setGlobalLoad(false);
loaderStore.set(false);
return Promise.reject(error);
}
);
Expand All @@ -22,12 +24,11 @@ axios.interceptors.request.use(
axios.interceptors.response.use(
(response) => {
// If the request was successful, return the response
setGlobalLoad(false);
loaderStore.set(false);
return response;
},
(error) => {
setGlobalLoad(false);

loaderStore.set(false);
let user = getUserStore();

if (Date.now() / 1000 > user.expires) {
Expand All @@ -46,6 +47,15 @@ axios.interceptors.response.use(
}
);

/** @param {import('axios').InternalAxiosRequestConfig<any>} config */
function skipLoader(config) {
const regex = new RegExp('http(s*)://(.*?)/conversation/(.*?)/(.*?)', 'g');
if (config.method === 'post' && !!config.data && regex.test(config.url || '')) {
return true;
}
return false;
}

/**
* @param {String} url
* @param {Object} args
Expand Down
22 changes: 13 additions & 9 deletions src/lib/helpers/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const userStore = writable({ id: "", full_name: "", expires: 0, token: nu
/**
* @returns {Writable<import('$types').UserModel>}
*/
export function getUserStore () {
export function getUserStore() {
if (browser) {
// Access localStorage only if in the browser context
let json = localStorage.getItem('user');
Expand All @@ -31,14 +31,6 @@ userStore.subscribe(value => {
}
});

export const globalLoaderStore = writable(false);

/**
* @param {boolean} value
*/
export function setGlobalLoad(value){
globalLoaderStore.set(value);
}

/** @type {Writable<import('$types').ConversationModel>}*/
export const conversationStore = writable({});
Expand Down Expand Up @@ -72,6 +64,18 @@ conversationStore.subscribe(value => {
});



const createLoaderStore = () => {
const { subscribe, set } = writable(false);
return {
subscribe,
set
};
}

export const loaderStore = createLoaderStore();


const createConversationUserStateStore = () => {
return {
reset: () => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/helpers/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ IRichContent.prototype.text;
* @typedef {Object} ContentLogModel
* @property {string} conversation_id - The conversation id.
* @property {string} name - The sender name.
* @property {string} role - The sender role.
* @property {string} content - The log content.
* @property {Date} created_at - The log sent time.
* @property {boolean} is_collapsed - For UI display.
Expand Down
4 changes: 4 additions & 0 deletions src/lib/scss/custom/pages/_conversation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
outline: none !important;
box-shadow: none !important;
}
}

.list-title {
width: 40%;
}
4 changes: 2 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import en from '$lib/langs/en.json';
import '$lib/helpers/http';
import { onMount } from 'svelte';
import { globalLoaderStore } from '$lib/helpers/store';
import { loaderStore } from '$lib/helpers/store';
import Loader from '$lib/common/Loader.svelte';

addMessages('en', en);
Expand All @@ -17,7 +17,7 @@
*/
let isLoading;
onMount(() => {
const subscribe = globalLoaderStore.subscribe((value) => {
const subscribe = loaderStore.subscribe(value => {
isLoading = value;
});
})
Expand Down
45 changes: 33 additions & 12 deletions src/routes/chat/[agentId]/[conversationId]/chat-box.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
let isLoadStateLog = false;
let isOpenEditMsgModal = false;
let isOpenStateModal = false;
let isSendingMsg = false;

onMount(async () => {
dialogs = await GetDialogs(params.conversationId);
Expand Down Expand Up @@ -157,16 +158,30 @@
window.location.href = `chat/${params.agentId}/${conversation.id}`;
}

async function sendTextMessage() {
await sendMessageToHub(params.agentId, params.conversationId, text);
function sendTextMessage() {
isSendingMsg = true;
return new Promise((resolve, reject) => {
sendMessageToHub(params.agentId, params.conversationId, text).then(res => {
isSendingMsg = false;
resolve(res);
}).catch(err => {
isSendingMsg = false;
reject(err);
});
});

}

async function startListen() {
microphoneIcon = "microphone";
webSpeech.onSpeechToTextDetected = async (transcript) => {
webSpeech.onSpeechToTextDetected = (transcript) => {
text = transcript;
await sendTextMessage();
microphoneIcon = "microphone-off";
if (!!!_.trim(text) || isSendingMsg) return;
sendTextMessage().then(() => {
microphoneIcon = "microphone-off";
}).catch(() => {
microphoneIcon = "microphone-off";
});
}
webSpeech.start();
}
Expand All @@ -186,12 +201,13 @@
/** @param {import('$types').ChatResponseModel[]} dialogs */
function groupDialogs(dialogs) {
if (!!!dialogs) return [];
const format = 'MMM D, YYYY';
// @ts-ignore
return _.groupBy(dialogs, (x) => {
const createDate = moment.utc(x.created_at).local().format('MMM DD YYYY');
if (createDate == moment.utc().local().format('MMM DD YYYY')) {
const createDate = moment.utc(x.created_at).local().format(format);
if (createDate == moment.utc().local().format(format)) {
return 'Today';
} else if (createDate == moment.utc().local().subtract(1, 'days').format('MMM DD YYYY')) {
} else if (createDate == moment.utc().local().subtract(1, 'days').format(format)) {
return 'Yesterday';
}
return createDate;
Expand Down Expand Up @@ -228,12 +244,17 @@
return;
}

if ((e.key === 'Enter' && (!!e.shiftKey || !!e.ctrlKey)) || e.key !== 'Enter' || !!!_.trim(text)) {
if ((e.key === 'Enter' && (!!e.shiftKey || !!e.ctrlKey)) || e.key !== 'Enter' || !!!_.trim(text) || isSendingMsg) {
return;
}

prevSentMsgs = [...prevSentMsgs, text];
await sendMessageToHub(params.agentId, params.conversationId, text);
isSendingMsg = true;
sendMessageToHub(params.agentId, params.conversationId, text).then(() => {
isSendingMsg = false;
}).catch(() => {
isSendingMsg = false;
});
}

function endChat() {
Expand Down Expand Up @@ -482,7 +503,7 @@
</Dropdown>
{:else}
<div class="cicon-wrap float-start">
{#if message.sender.role == "client"}
{#if message.sender.role == UserRole.Client}
<img src="images/users/user-dummy.jpg" class="rounded-circle avatar-xs" alt="avatar">
{:else}
<img src={PUBLIC_LIVECHAT_ENTRY_ICON} class="rounded-circle avatar-xs" alt="avatar">
Expand Down Expand Up @@ -557,7 +578,7 @@
<button
type="submit"
class="btn btn-primary btn-rounded chat-send waves-effect waves-light"
disabled={!!!_.trim(text)}
disabled={!!!_.trim(text) || isSendingMsg}
on:click={sendTextMessage}
><span class="d-none d-sm-inline-block me-2">Send</span>
<i class="mdi mdi-send" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { Button } from '@sveltestrap/sveltestrap';
import moment from 'moment';
import { replaceNewLine } from '$lib/helpers/http';
import { UserRole } from '$lib/helpers/enums';

/** @type {import('$types').ContentLogModel} */
export let data;
Expand All @@ -21,7 +22,9 @@
<div class="log-content" class:log-collapse={!!data?.is_collapsed}>
{@html replaceNewLine(data?.content)}
</div>
{#if data.role != UserRole.User}
<Button class='toggle-btn' color="link" on:click={(e) => toggleText(e)}>
{`${!!data?.is_collapsed ? 'More +' : 'Less -'}`}
</Button>
{/if}
</div>
12 changes: 9 additions & 3 deletions src/routes/page/conversation/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@
let pager = filter.pager;

onMount(async () => {
await getPagedConversations();
isLoading = true;
getPagedConversations().then(res => {
isLoading = false;
}).catch(error => {
isLoading = false;
isError = true;
});
});

async function getPagedConversations() {
Expand Down Expand Up @@ -217,7 +223,7 @@
<Table class="align-middle nowrap" bordered>
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col" class="list-title">Title</th>
<th scope="col">User Name</th>
<th scope="col">Agent</th>
<th scope="col">Channel</th>
Expand All @@ -230,7 +236,7 @@
<tbody>
{#each conversations.items as conv}
<tr>
<td scope="row">
<td scope="row" class="list-title">
<a href="page/conversation/{conv.id}">{conv.title}</a></td>
<td>{conv.user.full_name}</td>
<td>{conv.agent_name}</td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { GetDialogs } from '$lib/services/conversation-service.js';
import { format } from '$lib/helpers/datetime';
import { onMount } from 'svelte';
import { UserRole } from '$lib/helpers/enums';

/** @type {import('$types').ChatResponseModel[]} */
let dialogs = [];
Expand All @@ -20,7 +21,7 @@
*/
function showInRight(dialog) {
const sender = dialog.sender;
return sender.role != "client" && sender.role != "user";
return sender.role != UserRole.Client && sender.role != UserRole.User;
}
</script>

Expand Down