Skip to content

Commit c6862dd

Browse files
author
roary.yao
committed
AII-449
1 parent 890104f commit c6862dd

File tree

6 files changed

+220
-37
lines changed

6 files changed

+220
-37
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<script>
2+
import { Input, Dropdown, DropdownMenu, DropdownItem, Spinner, DropdownToggle } from '@sveltestrap/sveltestrap';
3+
import { debounce } from 'lodash';
4+
5+
/** @type {{id: string, name: string} | null} */
6+
export let selectedValue;
7+
export let disabled = false;
8+
export let placeholder = '';
9+
/**
10+
11+
* @type {(arg0: any) => any[] | PromiseLike<any[]>}
12+
*/
13+
export let onSearch;
14+
export let loading = false;
15+
16+
/** @type {any[]} */
17+
let searchResults = [];
18+
let isOpen = false;
19+
20+
// @ts-ignore
21+
const debouncedSearch = debounce(async (query) => {
22+
if (query.length) {
23+
loading = true;
24+
searchResults = await onSearch(query);
25+
loading = false;
26+
isOpen = true;
27+
} else {
28+
searchResults = [];
29+
isOpen = false;
30+
}
31+
}, 500);
32+
33+
/**
34+
* @param {any} e
35+
*/
36+
async function handleInput(e) {
37+
const query = e.target.value;
38+
selectedValue = { id: query, name: query };
39+
await debouncedSearch(query);
40+
}
41+
42+
/**
43+
* @param {{ id: string; name: string; }} result
44+
*/
45+
function selectResult(result) {
46+
selectedValue = result;
47+
}
48+
49+
export function clearSearchResults() {
50+
searchResults = [];
51+
}
52+
53+
</script>
54+
55+
<div class="position-relative">
56+
<Dropdown class="scrollable-dropdown" isOpen={isOpen && (searchResults.length > 0 || loading)} toggle={() => isOpen = !isOpen}>
57+
<DropdownToggle tag="div">
58+
<Input
59+
type="text"
60+
value={selectedValue?.name}
61+
on:input={handleInput}
62+
{disabled}
63+
{placeholder}
64+
/>
65+
</DropdownToggle>
66+
<DropdownMenu class="w-100">
67+
{#if loading}
68+
<DropdownItem>
69+
<Spinner size="sm" />
70+
</DropdownItem>
71+
{:else}
72+
{#each searchResults as result, index}
73+
<DropdownItem
74+
active={selectedValue?.id === result.id}
75+
on:click={() => selectResult(result)}
76+
title={result.name}
77+
>
78+
{result.name}
79+
</DropdownItem>
80+
{/each}
81+
{/if}
82+
</DropdownMenu>
83+
</Dropdown>
84+
</div>

src/lib/helpers/http.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ function skipLoader(config) {
9090
new RegExp('http(s*)://(.*?)/role/(.*?)/details', 'g'),
9191
new RegExp('http(s*)://(.*?)/user/(.*?)/details', 'g'),
9292
new RegExp('http(s*)://(.*?)/agent/labels', 'g'),
93+
new RegExp('http(s*)://(.*?)/conversation/state-search', 'g'),
9394
];
9495

9596
if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) {

src/lib/scss/custom/common/_common.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,15 @@ button:focus {
180180
.thin-scrollbar {
181181
scrollbar-width: thin;
182182
}
183+
184+
.scrollable-dropdown {
185+
.dropdown-menu {
186+
max-height: 200px;
187+
overflow-y: auto;
188+
}
189+
.dropdown-item {
190+
white-space: nowrap;
191+
overflow: hidden;
192+
text-overflow: ellipsis;
193+
}
194+
}

src/lib/services/api-endpoints.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export const endpoints = {
6767
conversationTagsUpdateUrl: `${host}/conversation/{conversationId}/update-tags`,
6868
fileUploadUrl: `${host}/agent/{agentId}/conversation/{conversationId}/upload`,
6969
pinConversationUrl: `${host}/agent/{agentId}/conversation/{conversationId}/dashboard`,
70+
conversationStateValueUrl: `${host}/conversation/state-search`,
71+
conversationStateKeyListUrl: `${host}/conversation/state-key`,
7072

7173
// LLM provider
7274
llmProvidersUrl: `${host}/llm-providers`,

src/lib/services/conversation-service.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,38 @@ export async function getAddressOptions(text) {
288288
}
289289
});
290290
return response.data;
291+
}
292+
293+
/**
294+
* get conversation state key list
295+
* @returns {Promise<{id: string, name: string, description: string}[]>}
296+
*/
297+
export async function getConversationStateKey() {
298+
let url = endpoints.conversationStateKeyListUrl;
299+
const response = await axios.get(url);
300+
return response.data;
301+
}
302+
303+
/** @type {import('axios').CancelTokenSource | null} */
304+
let getConversationStateValueCancelToken = null;
305+
/**
306+
* get conversation state value
307+
* @param {string} key
308+
* @param {string} query
309+
* @returns {Promise<{id: string, name: string}[]>}
310+
*/
311+
export async function getConversationStateValue(key, query) {
312+
let url = endpoints.conversationStateValueUrl;
313+
if (getConversationStateValueCancelToken) {
314+
getConversationStateValueCancelToken.cancel();
315+
}
316+
getConversationStateValueCancelToken = axios.CancelToken.source();
317+
const response = await axios.get(url, {
318+
params: {
319+
conversatinFilterType: key,
320+
searchKey: query
321+
},
322+
cancelToken: getConversationStateValueCancelToken.token
323+
});
324+
return response.data;
291325
}

src/routes/page/conversation/+page.svelte

Lines changed: 87 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@
2121
import StateModal from '$lib/common/StateModal.svelte';
2222
import { onMount } from 'svelte';
2323
import { getAgents } from '$lib/services/agent-service';
24-
import { getConversations, deleteConversation } from '$lib/services/conversation-service.js';
24+
import { getConversations, deleteConversation, getConversationStateKey, getConversationStateValue } from '$lib/services/conversation-service.js';
2525
import { utcToLocal } from '$lib/helpers/datetime';
2626
import Swal from 'sweetalert2';
2727
import lodash from "lodash";
2828
import MultiSelect from '$lib/common/MultiSelect.svelte';
2929
import { ConversationChannel, ConversationTag } from '$lib/helpers/enums';
30+
import RemoteSearchInput from '$lib/common/RemoteSearchInput.svelte';
3031
3132
let isLoading = false;
3233
let isComplete = false;
@@ -82,11 +83,29 @@
8283
tags: []
8384
};
8485
86+
/** @type {any[]} */
87+
let stateOptions = [];
88+
89+
/** @type {string | null} */
90+
let selectedState = null;
91+
92+
/** @type {{id: string, name: string} | null} */
93+
let selectedValue;
94+
95+
96+
let isValueEditable = false;
97+
98+
/**
99+
* @type {RemoteSearchInput}
100+
*/
101+
let remoteSearchInput;
102+
85103
onMount(async () => {
86104
isLoading = true;
87105
Promise.all([
88106
loadAgentOptions(),
89107
loadSearchOption(),
108+
loadStates(),
90109
loadConversations()])
91110
.finally(() => {
92111
isLoading = false
@@ -221,13 +240,23 @@
221240
};
222241
}
223242
224-
function toggleSearchStateModal() {
225-
isOpenSearchStateModal = !isOpenSearchStateModal;
226-
}
227-
228-
function handleConfirmStateModal() {
243+
/**
244+
* @param {any} e
245+
*/
246+
function handleConfirmStateModal(e) {
247+
if (selectedState && selectedValue?.id) {
248+
searchOption.states = [
249+
{
250+
key: { data: stateOptions.find(x => Number(x.id) === Number(selectedState))?.description, isValid: true },
251+
value: {data: selectedValue.id, isValid: true },
252+
active_rounds: {data: 0, isValid: true},
253+
}
254+
];
255+
} else {
256+
searchOption.states = [];
257+
}
229258
handleSearchStates();
230-
toggleSearchStateModal();
259+
searchConversations(e);
231260
}
232261
233262
function loadSearchOption() {
@@ -333,46 +362,72 @@
333362
};
334363
}
335364
}
365+
366+
async function loadStates() {
367+
const response = await getConversationStateKey();
368+
stateOptions = response;
369+
}
370+
371+
/**
372+
* @param { any } e
373+
*/
374+
function handleStateChange(e) {
375+
selectedState = e.target.value;
376+
isValueEditable = !!selectedState;
377+
selectedValue = null;
378+
remoteSearchInput?.clearSearchResults();
379+
}
380+
381+
/**
382+
* @param {any} query
383+
*/
384+
async function handleValueSearch(query) {
385+
if (!selectedState) return [];
386+
const response = await getConversationStateValue(selectedState, query);
387+
return response;
388+
}
336389
</script>
337390
338391
<HeadTitle title="{$_('Conversation List')}" />
339392
<Breadcrumb title="{$_('Communication')}" pagetitle="{$_('Conversations')}" />
340393
<LoadingToComplete isLoading={isLoading} isComplete={isComplete} isError={isError} successText={'Delete completed!'} />
341-
<StateModal
342-
isOpen={isOpenSearchStateModal}
343-
validateKey={true}
344-
validateValue={false}
345-
bind:states={searchOption.states}
346-
toggleModal={() => toggleSearchStateModal()}
347-
confirm={() => handleConfirmStateModal()}
348-
cancel={() => toggleSearchStateModal()}
349-
/>
350394
351395
<Row>
352396
<Col lg="12">
353397
<Card>
354398
<CardBody class="border-bottom">
355399
<div class="d-flex align-items-center">
356-
<h5 class="mb-0 card-title flex-grow-1">{$_('Conversation List')}</h5>
357-
<div class="flex-shrink-0">
400+
<h5 class="mb-0 card-title flex-grow-0">{$_('Conversation List')}</h5>
401+
<div class="flex-grow-1">
402+
<Row class="g-3">
403+
<Col lg="1"></Col>
404+
<Col lg="2" sm="12">
405+
<select class="form-select" on:change={handleStateChange}>
406+
<option value={null}>Select State</option>
407+
{#each stateOptions as state}
408+
<option value={state.id}>{state.name}</option>
409+
{/each}
410+
</select>
411+
</Col>
412+
<Col lg="2" sm="12">
413+
<RemoteSearchInput
414+
bind:selectedValue={selectedValue}
415+
disabled={!isValueEditable}
416+
onSearch={handleValueSearch}
417+
placeholder="Enter a value"
418+
bind:this={remoteSearchInput}
419+
/>
420+
</Col>
421+
<Col lg="1" sm="12">
422+
<button class="btn btn-primary" on:click={handleConfirmStateModal}>Confirm</button>
423+
</Col>
424+
</Row>
358425
<!-- <Button
359426
class="btn btn-light"
360427
on:click={(e) => searchConversations(e)}
361428
>
362429
<i class="mdi mdi-magnify" />
363430
</Button> -->
364-
<Dropdown class="dropdown d-inline-block">
365-
<DropdownToggle type="menu" class="btn" id="dropdownMenuButton1">
366-
<i class="mdi mdi-dots-vertical" />
367-
</DropdownToggle>
368-
<DropdownMenu class="conv-state-search-menu">
369-
<DropdownItem
370-
on:click={() => toggleSearchStateModal()}
371-
>
372-
{$_('Search States')}
373-
</DropdownItem>
374-
</DropdownMenu>
375-
</Dropdown>
376431
</div>
377432
</div>
378433
</CardBody>
@@ -416,8 +471,8 @@
416471
/>
417472
</Col>
418473
<!-- <Col lg="2">
419-
<Input type="date" class="form-control" />
420-
</Col> -->
474+
<Input type="date" class="form-control" />
475+
</Col> -->
421476
<Col lg="1">
422477
<Button
423478
type="button"
@@ -430,11 +485,6 @@
430485
</Button>
431486
</Col>
432487
</Row>
433-
{#if searchStateStrs?.length > 0}
434-
{#each searchStateStrs as str, idx (idx)}
435-
<Label index={idx} text={str} onClose={(index) => handleCloseLabel(index)} />
436-
{/each}
437-
{/if}
438488
</CardBody>
439489
<CardBody>
440490
<div class="table-responsive thin-scrollbar">

0 commit comments

Comments
 (0)