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
11 changes: 11 additions & 0 deletions src/lib/helpers/types/userTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,15 @@
* @property {string} [token]
*/

/**
* @typedef {Object} DashboardModel
* @property {DashboardConversation[]} [conversation_list] - conversation components
*/

/**
* @typedef {Object} DashboardConversation
* @property {string} [name] - The conversation name.
* @property {string} [conversation_id] - The conversation id.
* @property {string} [instruction] - The user input instruction.
*/
export default {};
5 changes: 5 additions & 0 deletions src/lib/services/api-endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const endpoints = {
conversationMessageUpdateUrl: `${host}/conversation/{conversationId}/update-message`,
conversationTagsUpdateUrl: `${host}/conversation/{conversationId}/update-tags`,
fileUploadUrl: `${host}/agent/{agentId}/conversation/{conversationId}/upload`,
pinConversationUrl: `${host}/agent/{agentId}/conversation/{conversationId}/dashboard`,

// LLM provider
llmProvidersUrl: `${host}/llm-providers`,
Expand Down Expand Up @@ -84,6 +85,10 @@ export const endpoints = {
// chathub
chatHubUrl: `${host}/chatHub`,

// dashboard
dashboardSettingUrl: `${host}/dashboard/components`,
dashConversationInstructionUrl: `${host}/dashboard/component/conversation?userId={userId}`,

// Google geocode api
addressUrl: `${host}/address/options`
}
Expand Down
41 changes: 41 additions & 0 deletions src/lib/services/conversation-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,47 @@ export async function sendMessageToHub(agentId, conversationId, text, data = nul
return response.data;
}

/**
* pin a conversation to dashboard
* @param {string} agentId - The agent id
* @param {string} conversationId - The conversation id
*/
export async function pinConversationToDashboard(agentId, conversationId) {
let url = replaceUrl(endpoints.pinConversationUrl, {
agentId: agentId,
conversationId: conversationId
});
const response = await axios.put(url);
return response.data;
}

/**
* unpin a conversation from dashboard
* @param {string} agentId - The agent id
* @param {string} conversationId - The conversation id
*/
export async function unpinConversationFromDashboard(agentId, conversationId) {
let url = replaceUrl(endpoints.pinConversationUrl, {
agentId: agentId,
conversationId: conversationId
});
const response = await axios.delete(url);
return response.data;
}

/**
* update a dashboard conversation instuction
* @param {string} userId - The conversation id
* @param {import('$userTypes').DashboardConversation} dashConv - The instruction
*/
export async function updateDashboardConversation(userId, dashConv) {
let url = replaceUrl(endpoints.dashConversationInstructionUrl, {
userId: userId
});
const response = await axios.post(url, dashConv);
return response.data;
}


/**
* send a notification to conversation
Expand Down
19 changes: 19 additions & 0 deletions src/lib/services/dashboard-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { endpoints } from '$lib/services/api-endpoints.js';
import axios from 'axios';

/**
* Get dashboard settings
* @param {string} userId - The user id
* @returns {Promise<import('$userTypes').DashboardModel>}
*/
export async function getDashboardSettings(userId) {
let userIdParam = userId;
let url = endpoints.dashboardSettingUrl;
console.log(url);
const response = await axios.get(url, {
params: {
"userId" : userId
}
});
return response.data;
}
12 changes: 11 additions & 1 deletion src/routes/chat/[agentId]/[conversationId]/chat-box.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
getConversationUser,
uploadConversationFiles,
getAddressOptions,
pinConversationToDashboard,
} from '$lib/services/conversation-service.js';
import {
PUBLIC_LIVECHAT_ENTRY_ICON,
Expand Down Expand Up @@ -560,6 +561,12 @@
window.location.href = url.toString();
}

function pinDashboard() {
const agentId = params.agentId;
const convId = params.conversationId;
pinConversationToDashboard(agentId, convId).then().finally();
}

function handleSaveKnowledge() {
sendChatMessage("Save knowledge");
}
Expand Down Expand Up @@ -1511,7 +1518,10 @@
{/if}
{#if agent?.id === LERNER_ID && mode === TRAINING_MODE}
<DropdownItem on:click={() => handleSaveKnowledge()}>Save Knowledge</DropdownItem>
{/if}
{/if}
<DropdownItem on:click={() => pinDashboard()}>
Pin to Dashboard
</DropdownItem>
</DropdownMenu>
</Dropdown>
{:else}
Expand Down
34 changes: 32 additions & 2 deletions src/routes/page/dashboard/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,44 @@
} from '$env/static/public';
import { onMount } from 'svelte';
import { getUserStore, userStore } from '$lib/helpers/store';
import Conversation from './Conversation.svelte';
import { getDashboardSettings } from '$lib/services/dashboard-service';

let subscribemodal = false;
let user = {full_name: ""};
let user = {full_name: "", id: ""};

/**
* @type {import("../../../lib/helpers/types/userTypes").DashboardModel}
*/
let dashboard_model ;
const togglesubscribemodal = (() => {
subscribemodal = !subscribemodal;
})

onMount(() => {
const userModelSubscribe = userStore.subscribe((/** @type {{ full_name: string; }} */ value) => {
const userModelSubscribe = userStore.subscribe((/** @type {{ full_name: string; id: string }} */ value) => {
user = value;
})
user = getUserStore();
loadDashboardComponents(user.id);
setTimeout(() => {
subscribemodal = true;
}, 1000);
})

/**
* delete a message in conversation
* @param {string} userId The user input
*/
async function loadDashboardComponents(userId) {
getDashboardSettings(userId)
.then(
response => {
dashboard_model = response
}
)
.catch();
}
</script>

<HeadTitle title={$_('Dashboard')} />
Expand Down Expand Up @@ -207,6 +229,14 @@
</Col>
</Row>

<Row>
{#each dashboard_model?.conversation_list || [] as conv, index (conv.conversation_id)}
{#if conv?.conversation_id}
<Conversation conversationId={conv.conversation_id} instruction={conv.instruction} userId={user.id}/>
{/if}
{/each}
</Row>

<Row>
<SocialSource />
<Activity />
Expand Down
155 changes: 155 additions & 0 deletions src/routes/page/dashboard/Conversation.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<script>
import { Card, CardBody, CardTitle, Col, Row } from '@sveltestrap/sveltestrap';
import { _ } from 'svelte-i18n';
import { onMount } from 'svelte';
import ChatTextArea from '../../chat/[agentId]/[conversationId]/chat-util/chat-text-area.svelte';
import { getConversation, updateDashboardConversation, unpinConversationFromDashboard } from '$lib/services/conversation-service';

/** @type {string}*/
export let conversationId;
/** @type {string}*/
export let instruction;
/** @type {string}*/
export let userId;

/** @type {import('$conversationTypes').ConversationModel}*/
let conversationModel;

let agent = {
name: "Loading",
icon_url: "https://botsharp.azurewebsites.net/images/users/bot.png"
};

let isLoading = true;

/** @type {string} */
let hide = '';

/** @type {string} */
let text;
/** @type {boolean} */
let loadUtils;

/** @type {number} */
let messageInputTimeout;

/** @type {string[]} */
let chatUtilOptions = [];

onMount(() => {
if (conversationId) {
loadDashboardComponents(conversationId);
}
if (instruction) {
text = instruction;
}
}
);

/**
* delete a message in conversation
* @param {string} id The user input
*/
async function loadDashboardComponents(id) {
getConversation(id)
.then(
response => {
conversationModel = response;
isLoading = false;
}
)
.catch();
}

/** @param {any} e */
function handleMessageInput(e) {
const value = e.target.value;

clearTimeout(messageInputTimeout);
chatUtilOptions = [];
}
</script>

<Col xl={4}>
<Card bind:class={hide}>
<CardBody>
{#if isLoading}
<CardTitle class="mb-0">{"Loading..."} </CardTitle>
<p> Loading ... </p>
{:else}
<div class="row">
<div class="col-10">
<CardTitle class="mb-0">{conversationModel.title} </CardTitle>
</div>
<div class="col-2 ">
<button
class={`btn btn-rounded btn-sm btn-light`}
on:click={() => {
hide = 'hide';
unpinConversationFromDashboard(conversationModel.agent_id, conversationId);
}}
>
<i
class="mdi mdi-pin-off"
style="font-size: 12px;"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Unpin"
/>
</button>
</div>
</div>
<div class="card mb-2">
<div class="chat-head">
<div class="row chat-row">
<div class="col-md-0 col-7 chat-head-info">
<div class="chat-head-agent">
{#if agent?.icon_url}
<div class="line-align-center">
<img class="chat-head-agent-icon" src={agent.icon_url} alt="">
</div>
{/if}
<div class="chat-head-agent-name line-align-center ellipsis">{conversationModel.agent_id}</div>
</div>
</div>
</div>
</div>

<div class={`chat-input-section css-animation fade-in-from-none mb-2`}>
<div class="row">
<div class="col-10">
<div class="position-relative">
<ChatTextArea
className={`chat-input`}
maxLength={1024}
disabled={false}
bind:text={text}
bind:loadUtils={loadUtils}
bind:options={chatUtilOptions}
onFocus={e => chatUtilOptions = []}
>
</ChatTextArea>
</div>
</div>
<div class="col-auto">
<button
type="submit"
class={`btn btn-rounded chat-send waves-effect waves-light btn-primary`}
disabled={!!!(text)}
on:click={() => updateDashboardConversation(userId, {
conversation_id: conversationId,
name: '',
instruction: text
})}
>
<i class="mdi mdi-send" />
</button>
</div>
</div>
</div>
</div>
{/if}

</CardBody>
</Card>
</Col>