diff --git a/src/lib/common/Loader.svelte b/src/lib/common/Loader.svelte index e84e9fa..30650a3 100644 --- a/src/lib/common/Loader.svelte +++ b/src/lib/common/Loader.svelte @@ -3,9 +3,13 @@ export let disableDefaultStyles = false; export let containerClasses = ''; + export let containerStyles = ''; export let size = 100; -
+
diff --git a/src/lib/helpers/enums.js b/src/lib/helpers/enums.js index 06af879..af4db40 100644 --- a/src/lib/helpers/enums.js +++ b/src/lib/helpers/enums.js @@ -125,6 +125,8 @@ export const UserPermission = Object.freeze(userPermission); const userAction = { Edit: "edit", + Train: "train", + Evaluate: "evaluate", Chat: "chat" }; export const UserAction = Object.freeze(userAction); diff --git a/src/lib/helpers/http.js b/src/lib/helpers/http.js index 7f53127..d86e1e8 100644 --- a/src/lib/helpers/http.js +++ b/src/lib/helpers/http.js @@ -85,7 +85,10 @@ function skipLoader(config) { new RegExp('http(s*)://(.*?)/conversation/(.*?)/files/(.*?)', 'g'), new RegExp('http(s*)://(.*?)/llm-provider/(.*?)/models', 'g'), new RegExp('http(s*)://(.*?)/knowledge/vector/collections', 'g'), - new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/exist', 'g') + new RegExp('http(s*)://(.*?)/knowledge/vector/(.*?)/exist', 'g'), + new RegExp('http(s*)://(.*?)/role/options', 'g'), + new RegExp('http(s*)://(.*?)/role/(.*?)/details', 'g'), + new RegExp('http(s*)://(.*?)/user/(.*?)/details', 'g'), ]; if (config.method === 'post' && postRegexes.some(regex => regex.test(config.url || ''))) { diff --git a/src/lib/helpers/types/agentTypes.js b/src/lib/helpers/types/agentTypes.js index bbe84e6..fff0d75 100644 --- a/src/lib/helpers/types/agentTypes.js +++ b/src/lib/helpers/types/agentTypes.js @@ -60,6 +60,8 @@ * @property {AgentWelcomeInfo} welcome_info - Welcome information. * @property {boolean} editable * @property {boolean} chatable + * @property {boolean} trainable + * @property {boolean} evaluable */ diff --git a/src/lib/helpers/types/roleTypes.js b/src/lib/helpers/types/roleTypes.js new file mode 100644 index 0000000..1e036b9 --- /dev/null +++ b/src/lib/helpers/types/roleTypes.js @@ -0,0 +1,34 @@ +/** + * @typedef {Object} RoleModel + * @property {string} id - The user id. + * @property {string} [name] - Role name + * @property {string[]} permissions - Permissions. + * @property {RoleAgentAction[]} agent_actions - Agent actions + * @property {string} [create_date] - The user create date. + * @property {string} [update_date] - The user update date. + * @property {boolean} [open_detail] + */ + +/** + * @typedef {Object} RoleAgentAction + * @property {string?} [id] - The id + * @property {string} agent_id - The agent id + * @property {import('$agentTypes').AgentModel} [agent] - The agent details + * @property {string[]} actions - The actions + */ + +/** + * @typedef {Object} RoleAgentInnerAction + * @property {string?} [id] - The id + * @property {string} agent_id - The agent id + * @property {string} [agent_name] - The agent name + * @property {import('$agentTypes').AgentModel} [agent] - The agent details + * @property {{ key: string, value: string, checked: boolean }[]} actions - The actions + */ + +/** + * @typedef {Object} RoleFilter + * @property {string[]} [names] - The role names. + */ + +export default {}; \ No newline at end of file diff --git a/src/lib/helpers/types/userTypes.js b/src/lib/helpers/types/userTypes.js index 529afe2..e48a851 100644 --- a/src/lib/helpers/types/userTypes.js +++ b/src/lib/helpers/types/userTypes.js @@ -44,6 +44,7 @@ * @property {string[]} [user_ids] - The user ids. * @property {string[]} [user_names] - The user names * @property {string[]} [roles] - The roles. + * @property {string[]} [types] - The types. * @property {string[]} [sources] - The sources. * @property {string[]} [external_ids] - The external ids. */ diff --git a/src/lib/scss/app.scss b/src/lib/scss/app.scss index 7d11262..604f0c2 100644 --- a/src/lib/scss/app.scss +++ b/src/lib/scss/app.scss @@ -94,6 +94,7 @@ File: Main Css File @import "custom/pages/agent"; @import "custom/pages/knowledgebase"; @import "custom/pages/users"; +@import "custom/pages/roles"; // Common @import "custom/common/animation"; diff --git a/src/lib/scss/custom/pages/_roles.scss b/src/lib/scss/custom/pages/_roles.scss new file mode 100644 index 0000000..66f1591 --- /dev/null +++ b/src/lib/scss/custom/pages/_roles.scss @@ -0,0 +1,120 @@ +.roles-table { + .role-plain-col { + width: 10%; + max-width: 100px; + } + + .role-permission-col { + width: 40%; + max-width: 300px; + } + + .role-detail { + padding: 2px 5px; + border-radius: 3px; + border-color: var(--#{$prefix}light) !important; + background-color: var(--#{$prefix}light) !important; + position: relative; + + ul { + li { + margin: 2px 0px; + } + } + + .wrappable { + white-space: wrap !important; + } + + .basic-info { + margin: 15px 0px 0px 0px; + display: flex; + flex-wrap: wrap; + + li { + flex: 0 0 50%; + + .inline-edit { + display: flex; + gap: 3px; + } + } + } + + .role-permission-container { + display: flex; + gap: 5px; + + .permission-wrapper { + display: flex; + flex-direction: column; + gap: 3px; + + .edit-wrapper { + display: flex; + gap: 3px; + } + + input[type="text"] { + height: 30px; + font-size: 12px; + } + } + + + .list-add { + font-size: 18px; + } + } + + .role-agent-container { + margin: 20px 0px; + padding: 0px 2rem; + + .action-row-wrapper { + overflow-y: auto; + scrollbar-width: thin; + height: fit-content; + max-height: 300px; + } + + .action-row { + display: flex; + } + + .action-col { + padding: 3px 0px; + border-bottom: 1px dotted var(--bs-primary); + + input[type='checkbox'] { + outline: none !important; + box-shadow: none !important; + } + } + + .action-title { + .action-title-wrapper { + display: flex; + gap: 3px; + justify-content: center; + text-transform: capitalize; + text-align: center; + border-bottom: 2px solid var(--bs-primary); + } + } + + .action-center { + display: flex; + justify-content: center; + } + } + + .edit-btn { + display: flex; + justify-content: flex-end; + font-size: 16px; + margin-top: 3px; + margin-right: 5px; + } + } +} \ No newline at end of file diff --git a/src/lib/scss/custom/pages/_users.scss b/src/lib/scss/custom/pages/_users.scss index e413348..9fe8e64 100644 --- a/src/lib/scss/custom/pages/_users.scss +++ b/src/lib/scss/custom/pages/_users.scss @@ -8,11 +8,6 @@ width: 20%; max-width: 300px; } - - .user-agent-col { - width: 25%; - max-width: 350px; - } .user-detail { padding: 2px 5px; @@ -46,6 +41,14 @@ } } + .role-wrapper { + .role-select { + height: 30px; + font-size: 12px; + padding: 0.3rem 1rem; + } + } + .user-permission-container { display: flex; gap: 5px; @@ -89,6 +92,7 @@ .action-col { padding: 3px 0px; + border-bottom: 1px dotted var(--bs-primary); input[type='checkbox'] { outline: none !important; diff --git a/src/lib/services/api-endpoints.js b/src/lib/services/api-endpoints.js index 1c4a95d..7c9929c 100644 --- a/src/lib/services/api-endpoints.js +++ b/src/lib/services/api-endpoints.js @@ -2,10 +2,17 @@ import { PUBLIC_SERVICE_URL } from '$env/static/public'; export const host = PUBLIC_SERVICE_URL; export const endpoints = { + // role + roleOptionsUrl: `${host}/role/options`, + rolesUrl: `${host}/roles`, + roleDetailUrl: `${host}/role/{id}/details`, + roleUpdateUrl: `${host}/role`, + // user tokenUrl: `${host}/token`, myInfoUrl: `${host}/user/me`, usersUrl: `${host}/users`, + userDetailUrl: `${host}/user/{id}/details`, userUpdateUrl: `${host}/user`, usrCreationUrl: `${host}/user`, userAvatarUrl: `${host}/user/avatar`, diff --git a/src/lib/services/role-service.js b/src/lib/services/role-service.js new file mode 100644 index 0000000..15f4011 --- /dev/null +++ b/src/lib/services/role-service.js @@ -0,0 +1,45 @@ +import { endpoints } from './api-endpoints.js'; +import axios from 'axios'; + +/** + * Get role options + * @returns {Promise} + */ +export async function getRoleOptions() { + const response = await axios.get(endpoints.roleOptionsUrl); + return response.data; +} + + +/** + * Get role list + * @param {import('$roleTypes').RoleFilter?} [filter] + * @returns {Promise} + */ +export async function getRoles(filter = null) { + const response = await axios.post(endpoints.rolesUrl, filter); + return response.data; +} + + +/** + * Get user detail + * @param {string} id + * @returns {Promise} + */ +export async function getRoleDetails(id) { + const url = endpoints.roleDetailUrl.replace("{id}", id); + const response = await axios.get(url); + return response.data; +} + + +/** + * Update role + * @param {import('$roleTypes').RoleModel} model + * @returns {Promise} + */ +export async function updateRole(model) { + const response = await axios.put(endpoints.roleUpdateUrl, { ...model }); + return response.data; +} \ No newline at end of file diff --git a/src/lib/services/user-service.js b/src/lib/services/user-service.js index dacb88d..eadc395 100644 --- a/src/lib/services/user-service.js +++ b/src/lib/services/user-service.js @@ -12,6 +12,18 @@ export async function getUsers(filter) { } +/** + * Get user detail + * @param {string} id + * @returns {Promise} + */ +export async function getUserDetails(id) { + const url = endpoints.userDetailUrl.replace("{id}", id); + const response = await axios.get(url); + return response.data; +} + + /** * Get user list * @param {import('$userTypes').UserModel} model diff --git a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index 7f42d0e..c8292c9 100644 --- a/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte +++ b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte @@ -202,7 +202,7 @@ } $: { - disableAction = !ADMIN_ROLES.includes(currentUser?.role || '') && currentUser?.id !== conversationUser?.id; + disableAction = !ADMIN_ROLES.includes(currentUser?.role || '') && currentUser?.id !== conversationUser?.id || !agent?.chatable; } setContext('chat-window-context', { @@ -1647,7 +1647,7 @@ text={message?.rich_content?.message?.text || message?.text} /> {/if} - {#if PUBLIC_LIVECHAT_ENABLE_TRAINING === 'true'} + {#if PUBLIC_LIVECHAT_ENABLE_TRAINING === 'true' && agent?.trainable} {#if message?.function}
diff --git a/src/routes/page/agent/[agentId]/agent-overview.svelte b/src/routes/page/agent/[agentId]/agent-overview.svelte index 4975286..e4a5e5c 100644 --- a/src/routes/page/agent/[agentId]/agent-overview.svelte +++ b/src/routes/page/agent/[agentId]/agent-overview.svelte @@ -75,7 +75,7 @@ height="50" class="mx-auto d-block" /> - {#if 1} + {#if !!agent.chatable} \ No newline at end of file diff --git a/src/routes/page/roles/+page.svelte b/src/routes/page/roles/+page.svelte new file mode 100644 index 0000000..344ad98 --- /dev/null +++ b/src/routes/page/roles/+page.svelte @@ -0,0 +1,187 @@ + + + + + + + + + + + + +
+
{$_('Role List')}
+
+
+ +
+ + + + + + + + + + + {#each roleItems as item, idx (idx)} + saveRole(e)} + /> + {/each} + +
{$_('Name')}{$_('Permissions')}{$_('Update Date')}{$_('')}
+
+
+
+ +
\ No newline at end of file diff --git a/src/routes/page/roles/role-item.svelte b/src/routes/page/roles/role-item.svelte new file mode 100644 index 0000000..6929fb0 --- /dev/null +++ b/src/routes/page/roles/role-item.svelte @@ -0,0 +1,380 @@ + + + + {item.name} + +
+ {item.permissions?.length > 0 ? item.permissions.join(', ') : 'N/A'} +
+ + + {item.update_date != null ? moment.utc(item.update_date).local().format('MMM D YYYY, hh:mm A') : 'N/A'} + + +
    +
  • + +
  • +
+ + + +{#if open} + + {#if isLoading} + + + + {:else} + +
+
+ + +
save()} + > + +
+
+
    +
  • +
    + {'Name:'} + {item.name} +
    +
  • +
+
    +
  • +
    +
    {'Permissions:'}
    +
    + {#each innerItem.permissions as permission, index} +
    + +
    + {}} + on:click={() => deletePermission(index)} + /> +
    +
    + {/each} + {#if innerItem.permissions?.length < 5} +
    + {}} + on:click={() => addPermission()} + /> +
    + {/if} +
    +
    +
  • +
+ + {#if innerActions.length > 0} +
+
+
+ {'Agent'} +
+ {#each allActions as title} +
+
{title.name}
+
+ checkAll(e, title)} + /> +
+
+ {/each} +
+
+ {#each innerActions as agentActionItem} +
+
+ {agentActionItem.agent_name} +
+ {#each agentActionItem.actions as actionItem} +
+ checkAction(e, agentActionItem, actionItem)} + /> +
+ {/each} +
+ {/each} +
+
+ {/if} +
+ + {/if} + +{/if} \ No newline at end of file diff --git a/src/routes/page/users/+page.svelte b/src/routes/page/users/+page.svelte index 54aba8f..399330c 100644 --- a/src/routes/page/users/+page.svelte +++ b/src/routes/page/users/+page.svelte @@ -5,8 +5,6 @@ Card, CardBody, Col, - Dropdown, - DropdownToggle, Input, Row, Table @@ -21,6 +19,7 @@ import { getAgents } from '$lib/services/agent-service'; import { globalEventStore } from '$lib/helpers/store'; import { GlobalEvent } from '$lib/helpers/enums'; + import { getRoleOptions } from '$lib/services/role-service'; const duration = 3000; const firstPage = 1; @@ -47,6 +46,9 @@ /** @type {import('$commonTypes').IdName[]} */ let agents = []; + /** @type {string[]} */ + let roleOptions = []; + /** @type {any} */ let unsubscriber; @@ -54,7 +56,7 @@ userName: '', externalId: '', role: '', - source: '', + type: '', }; onMount(async () => { @@ -79,11 +81,14 @@ function init() { isLoading = true; - getPagedAgents().then(() => { - getPagedUsers().then(() => { - isLoading = false; - }); - }); + getRoleOptions().then(roles => { + roleOptions = [...roles]; + getPagedAgents().then(() => { + getPagedUsers().then(() => { + isLoading = false; + }); + }); + }); } function getPagedUsers() { @@ -142,7 +147,7 @@ const userName = searchOption.userName?.trim(); const externalId = searchOption.externalId?.trim(); const role = searchOption.role?.trim(); - const source = searchOption.source?.trim(); + const type = searchOption.type?.trim(); filter = { ...filter, @@ -150,7 +155,7 @@ user_names: !!userName ? [userName] : [], external_ids: !!externalId ? [externalId] : [], roles: !!role ? [role] : [], - sources: !!source ? [source] : [] + types: !!type ? [type] : [] }; } @@ -172,7 +177,7 @@ if (res) { isLoading = false; isComplete = true; - postUpdateUser(data); + postUpdate(data); setTimeout(() => { isComplete = false; }, duration); @@ -190,7 +195,7 @@ } /** @param {import('$userTypes').UserModel} data */ - function postUpdateUser(data) { + function postUpdate(data) { userItems = userItems?.map(x => { if (x.id === data.id) { return { ...data, open_detail: true }; @@ -212,19 +217,6 @@
{$_('User List')}
-
- - - - - - -
@@ -239,7 +231,7 @@ - +