diff --git a/src/lib/common/Label.svelte b/src/lib/common/Label.svelte new file mode 100644 index 00000000..e9d30b8b --- /dev/null +++ b/src/lib/common/Label.svelte @@ -0,0 +1,63 @@ + + +
+ +
+ + + \ No newline at end of file diff --git a/src/routes/chat/[agentId]/[conversationId]/addStateModal/state-modal.svelte b/src/lib/common/StateModal.svelte similarity index 80% rename from src/routes/chat/[agentId]/[conversationId]/addStateModal/state-modal.svelte rename to src/lib/common/StateModal.svelte index 23a4508a..4dce30eb 100644 --- a/src/routes/chat/[agentId]/[conversationId]/addStateModal/state-modal.svelte +++ b/src/lib/common/StateModal.svelte @@ -1,8 +1,6 @@ toggleModal && toggleModal()}> - Add states + {title}
{#each states as state, idx (idx)} @@ -149,7 +140,7 @@ diff --git a/src/lib/helpers/store.js b/src/lib/helpers/store.js index 197716bd..5d309d6a 100644 --- a/src/lib/helpers/store.js +++ b/src/lib/helpers/store.js @@ -4,6 +4,7 @@ import { browser } from '$app/environment'; const conversationKey = "conversation"; const conversationUserStatesKey = "conversation_user_states"; +const conversationSearchStatesKey = "conversation_search_states"; const conversationUserMessageKey = "conversation_user_messages"; /** @type {Writable} */ @@ -94,6 +95,23 @@ const createConversationUserStateStore = () => { export const conversationUserStateStore = createConversationUserStateStore(); +const createConversationSearchStateStore = () => { + return { + reset: () => { + localStorage.removeItem(conversationSearchStatesKey); + }, + get: () => { + const json = localStorage.getItem(conversationSearchStatesKey); + return json ? JSON.parse(json) : []; + }, + put: (value) => { + localStorage.setItem(conversationSearchStatesKey, JSON.stringify(value)); + } + } +}; + +export const conversationSearchStateStore = createConversationSearchStateStore(); + const createConversationUserMessageStore = () => { return { diff --git a/src/lib/scss/custom/pages/_chat.scss b/src/lib/scss/custom/pages/_chat.scss index 2d7ef0a8..4b96dfd4 100644 --- a/src/lib/scss/custom/pages/_chat.scss +++ b/src/lib/scss/custom/pages/_chat.scss @@ -412,6 +412,10 @@ .state-form { padding-left: 25px; + overflow-y: auto; + overflow-x: hidden; + max-height: 800px; + scrollbar-width: thin; .state-input { flex: 0.45; diff --git a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index d9785356..351db50e 100644 --- a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte @@ -29,11 +29,11 @@ import _ from "lodash"; import { Pane, Splitpanes } from 'svelte-splitpanes'; import StateLog from './stateLogs/state-log.svelte'; - import StateModal from './addStateModal/state-modal.svelte'; import ChatImage from './chatImage/chat-image.svelte'; import Swal from 'sweetalert2/dist/sweetalert2.js'; import "sweetalert2/src/sweetalert2.scss"; import moment from 'moment'; + import StateModal from '$lib/common/StateModal.svelte'; const options = { scrollbars: { @@ -81,13 +81,16 @@ /** @type {import('$types').ConversationStateLogModel[]} */ let stateLogs = []; + /** @type {import('$types').UserStateDetailModel[]} */ + let userAddStates = []; + /** @type {boolean} */ let isLoadContentLog = false; let isLoadStateLog = false; let isContentLogClosed = false; // initial condition let isStateLogClosed = false; // initial condition let isOpenEditMsgModal = false; - let isOpenAddStateModal = false; + let isOpenUserAddStateModal = false; let isSendingMsg = false; let isThinking = false; let isLite = false; @@ -120,7 +123,7 @@ isLoadContentLog = false; isLoadStateLog = false; isOpenEditMsgModal = false; - isOpenAddStateModal = false; + isOpenUserAddStateModal = false; } } @@ -416,8 +419,31 @@ stateLogs = []; } - function toggleAddStateModal() { - isOpenAddStateModal = !isOpenAddStateModal; + function toggleUserAddStateModal() { + isOpenUserAddStateModal = !isOpenUserAddStateModal; + if (isOpenUserAddStateModal) { + loadAddStates(); + } + } + + function loadAddStates() { + const conversationUserStates = conversationUserStateStore.get(); + if (!!conversationUserStates && conversationUserStates.conversationId == params.conversationId && !!conversationUserStates.states) { + userAddStates = [...conversationUserStates.states]; + } + } + + function handleConfirmUserAddStates() { + const cleanStates = userAddStates.map(state => { + state.key.data = _.trim(state.key.data); + state.value.data = _.trim(state.value.data); + return state; + }); + conversationUserStateStore.put({ + conversationId: params.conversationId, + states: cleanStates + }); + toggleUserAddStateModal(); } function clearAddedStates() { @@ -619,7 +645,6 @@ resizeChatWindow()}/> @@ -683,7 +708,7 @@ {#if !isLite && !isLoadContentLog} toggleContentLog()}>View Log {/if} - {#if !isLite && (!isLoadStateLog || !isOpenAddStateModal)} + {#if !isLite && (!isLoadStateLog || !isOpenUserAddStateModal)}
  • @@ -693,8 +718,8 @@ {#if !isLoadStateLog} toggleStateLog()}>View States {/if} - {#if !isOpenAddStateModal} - toggleAddStateModal()}>Add States + {#if !isOpenUserAddStateModal} + toggleUserAddStateModal()}>Add States {/if} clearAddedStates()}>Clear States diff --git a/src/routes/page/agent/[agentId]/+page.svelte b/src/routes/page/agent/[agentId]/+page.svelte index 528165a2..f2d23b2d 100644 --- a/src/routes/page/agent/[agentId]/+page.svelte +++ b/src/routes/page/agent/[agentId]/+page.svelte @@ -6,7 +6,7 @@ } from '@sveltestrap/sveltestrap'; import Breadcrumb from '$lib/common/Breadcrumb.svelte'; import HeadTitle from '$lib/common/HeadTitle.svelte'; - + import LoadingToComplete from '$lib/common/LoadingToComplete.svelte'; import AgentPrompt from './agent-prompt.svelte'; import AgentOverview from './agent-overview.svelte'; import AgentFunction from './agent-function.svelte'; @@ -16,19 +16,39 @@ import { onMount } from 'svelte'; const params = $page.params; import { _ } from 'svelte-i18n' - + /** @type {import('$types').AgentModel} */ let agent; /** @type {any} */ let agentFunctionCmp = null; + /** @type {boolean} */ + let isLoading = false; + let isComplete = false; + let isError = false; + const duration = 3000; + onMount(async () => { agent = await getAgent(params.agentId); }); async function handleAgentUpdate() { fetchJsonContent(); - const result = await saveAgent(agent) + isLoading = true; + saveAgent(agent).then(res => { + isLoading = false; + isComplete = true; + setTimeout(() => { + isComplete = false; + }, duration); + }).catch(err => { + isLoading = false; + isComplete = false; + isError = true; + setTimeout(() => { + isError = false; + }, duration); + }); } function fetchJsonContent() { @@ -46,6 +66,7 @@ + {#if agent} diff --git a/src/routes/page/conversation/+page.svelte b/src/routes/page/conversation/+page.svelte index 216ab13a..b51ce0e5 100644 --- a/src/routes/page/conversation/+page.svelte +++ b/src/routes/page/conversation/+page.svelte @@ -17,6 +17,9 @@ import HeadTitle from '$lib/common/HeadTitle.svelte'; import TablePagination from '$lib/common/TablePagination.svelte'; import LoadingToComplete from '$lib/common/LoadingToComplete.svelte'; + import Label from '$lib/common/Label.svelte'; + import StateModal from '$lib/common/StateModal.svelte'; + import { conversationSearchStateStore } from '$lib/helpers/store'; import { onMount } from 'svelte'; import Link from 'svelte-link'; import { getConversations, deleteConversation } from '$lib/services/conversation-service.js'; @@ -24,10 +27,12 @@ import Swal from 'sweetalert2/dist/sweetalert2.js'; import "sweetalert2/src/sweetalert2.scss"; import lodash from "lodash"; + let isLoading = false; let isComplete = false; let isError = false; + let isOpenSearchStateModal = false; const duration = 3000; const firstPage = 1; const pageSize = 10; @@ -46,10 +51,13 @@ /** @type {import('$types').Pagination} */ let pager = filter.pager; - /** @type {string} */ - let searchStr = ''; + /** @type {string[]} */ + let searchStateStrs = []; + /** @type {import('$types').UserStateDetailModel[]} */ + export let searchStates = []; onMount(async () => { + loadSearchStates(); isLoading = true; getPagedConversations().then(res => { isLoading = false; @@ -150,37 +158,90 @@ */ function searchConversations(e) { e.preventDefault(); - const searchStates = handleSearchString(); + const states = getSearchStates(); filter = { ...filter, pager: { page: firstPage, size: pageSize, count: 0 }, - states: searchStates + states: states }; getPagedConversations(); } - function handleSearchString() { - const splits = searchStr?.split(';') || []; - /** @type {import('$types').KeyValuePair[]} */ - const states = []; + function toggleSearchStateModal() { + isOpenSearchStateModal = !isOpenSearchStateModal; + } + + function handleConfirmStateModal() { + handleSearchStates(); + saveSearchStates(); + toggleSearchStateModal(); + } + + function getSearchStates() { + return searchStates.map(x => { + return { + key: x.key.data, + value: x.value.data + }; + }); + } + + function loadSearchStates() { + searchStates = conversationSearchStateStore.get(); + handleSearchStates(); + } - splits.forEach(pair => { - const sp = pair?.split('=') || []; - if (sp.length > 0) { - states.push({ - key: lodash.trim(sp[0]), - value: lodash.trim(sp[1]) - }); + function handleSearchStates() { + searchStates = searchStates.map(x => { + if (!!x.key) x.key.data = lodash.trim(x.key.data); + if (!!x.value) x.value.data = lodash.trim(x.value.data) + return x; + }).sort((a, b) => { + const stra = `${!!a.key?.data ? a.key.data : ''} ${!!a.value?.data ? b.value.data : ''}`; + const strb = `${!!b.key?.data ? b.key.data : ''} ${!!b.value?.data ? b.value.data : ''}`; + if (stra.length != strb.length) { + return stra.length - strb.length; + } + const keya = a.key?.data?.toLowerCase() || ''; + const keyb = b.key?.data?.toLowerCase() || ''; + return keya < keyb ? -1 : keya == keyb ? 0 : 1; + }); + searchStateStrs = searchStates.map(x => { + let s = ''; + if (x.key?.data?.length > 0) { + s += x.key.data; + } + if (x.value?.data?.length > 0) { + s += '=' + x.value.data; } + return s; }); + } + + function saveSearchStates() { + conversationSearchStateStore.put(searchStates); + } - return states; + /** @param {string | number} index */ + function handleCloseLabel(index) { + searchStates = searchStates.filter((x, idx) => idx != index); + handleSearchStates(); + saveSearchStates(); } + @@ -192,12 +253,13 @@ searchConversations(e)}> - + - {$_('Action')} - {$_('Another action')} - {$_('Something else here')} + toggleSearchStateModal()} + > + {$_('Search States')} + @@ -210,8 +272,7 @@ type="search" class="form-control" id="searchTableList" - bind:value={searchStr} - placeholder="{$_('Search for states, e.g., key1=value1;key2=value2;')}" + placeholder="{$_('Search for ...')}" /> @@ -248,6 +309,11 @@ + {#if searchStateStrs?.length > 0} + {#each searchStateStrs as str, idx (idx)} +