From 63031f872c3175b6e9c77e60833eb739ea808954 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 14 Nov 2024 11:55:50 -0600 Subject: [PATCH 1/3] refine users page --- src/lib/common/Loader.svelte | 6 +- src/lib/helpers/http.js | 5 +- src/lib/helpers/types/roleTypes.js | 11 + src/lib/scss/custom/pages/_users.scss | 8 + src/lib/services/api-endpoints.js | 6 + src/lib/services/role-service.js | 26 +++ src/lib/services/user-service.js | 12 ++ src/routes/page/mongodb/+page.svelte | 57 ----- src/routes/page/roles/+page.svelte | 5 + src/routes/page/users/+page.svelte | 18 +- src/routes/page/users/user-item.svelte | 285 ++++++++++++++----------- svelte.config.js | 2 + 12 files changed, 253 insertions(+), 188 deletions(-) create mode 100644 src/lib/helpers/types/roleTypes.js create mode 100644 src/lib/services/role-service.js delete mode 100644 src/routes/page/mongodb/+page.svelte create mode 100644 src/routes/page/roles/+page.svelte 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/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/roleTypes.js b/src/lib/helpers/types/roleTypes.js new file mode 100644 index 0000000..8ff08cc --- /dev/null +++ b/src/lib/helpers/types/roleTypes.js @@ -0,0 +1,11 @@ +/** + * @typedef {Object} RoleModel + * @property {string} id - The user id. + * @property {string} [name] - Role name + * @property {string[]} permissions - Permissions. + * @property {string} [create_date] - The user create date. + * @property {string} [update_date] - The user update date. + */ + + +export default {}; \ 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..84ecd8a 100644 --- a/src/lib/scss/custom/pages/_users.scss +++ b/src/lib/scss/custom/pages/_users.scss @@ -46,6 +46,14 @@ } } + .role-wrapper { + .role-select { + height: 30px; + font-size: 12px; + padding: 0.3rem 1rem; + } + } + .user-permission-container { display: flex; gap: 5px; diff --git a/src/lib/services/api-endpoints.js b/src/lib/services/api-endpoints.js index 1c4a95d..265d761 100644 --- a/src/lib/services/api-endpoints.js +++ b/src/lib/services/api-endpoints.js @@ -2,10 +2,16 @@ 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`, + // 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..0fa8339 --- /dev/null +++ b/src/lib/services/role-service.js @@ -0,0 +1,26 @@ +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 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; +} 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/page/mongodb/+page.svelte b/src/routes/page/mongodb/+page.svelte deleted file mode 100644 index 979a1d3..0000000 --- a/src/routes/page/mongodb/+page.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - -

{$_('Migrate agents from file repository to MongoDB')}

- \ 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..339712a --- /dev/null +++ b/src/routes/page/roles/+page.svelte @@ -0,0 +1,5 @@ + + +
\ No newline at end of file diff --git a/src/routes/page/users/+page.svelte b/src/routes/page/users/+page.svelte index 54aba8f..8fc2034 100644 --- a/src/routes/page/users/+page.svelte +++ b/src/routes/page/users/+page.svelte @@ -21,6 +21,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 +48,9 @@ /** @type {import('$commonTypes').IdName[]} */ let agents = []; + /** @type {string[]} */ + let roleOptions = []; + /** @type {any} */ let unsubscriber; @@ -79,11 +83,14 @@ function init() { isLoading = true; - getPagedAgents().then(() => { - getPagedUsers().then(() => { - isLoading = false; - }); - }); + getRoleOptions().then(roles => { + roleOptions = [...roles]; + getPagedAgents().then(() => { + getPagedUsers().then(() => { + isLoading = false; + }); + }); + }); } function getPagedUsers() { @@ -273,6 +280,7 @@ saveUser(e)} /> diff --git a/src/routes/page/users/user-item.svelte b/src/routes/page/users/user-item.svelte index 0766554..855f93d 100644 --- a/src/routes/page/users/user-item.svelte +++ b/src/routes/page/users/user-item.svelte @@ -2,11 +2,13 @@ import { createEventDispatcher, onMount } from 'svelte'; import { fly } from 'svelte/transition'; import { Button, Input } from '@sveltestrap/sveltestrap'; + import { v4 as uuidv4 } from 'uuid'; import Swal from 'sweetalert2'; import lodash from "lodash"; - import InPlaceEdit from '$lib/common/InPlaceEdit.svelte'; + import Loader from '$lib/common/Loader.svelte'; import { UserAction } from '$lib/helpers/enums'; - import { v4 as uuidv4 } from 'uuid'; + import { getUserDetails } from '$lib/services/user-service'; + const svelteDispatch = createEventDispatcher(); @@ -23,6 +25,12 @@ /** @type {import('$commonTypes').IdName[]} */ export let agents = []; + /** @type {string[]} */ + export let roleOptions = []; + + + /** @type {boolean} */ + let isLoading = false; /** @type {import('$userTypes').UserModel} */ let innerItem = { ...item }; @@ -104,10 +112,26 @@ function toggleUserDetail() { open = !open; if (open) { - initAgentActions(); + isLoading = true; + getUserDetails(item.id).then(res => { + innerItem = { ...res }; + initAgentActions(); + }).finally(() => { + isLoading = false; + }); } } + + /** @param {any} e */ + function changeRole(e) { + const value = e.target.value; + innerItem = { + ...innerItem, + role: value + }; + } + /** * @param {any} e * @param {import('$userTypes').UserAgentInnerAction} agentActionItem @@ -245,143 +269,156 @@ {#if open} + - -
-
- - -
save(item.id)} - > - -
-
-
    -
  • -
    - {'User name:'} - {item.user_name} + {#if isLoading} + + + + {:else} + +
    +
    + + +
    save(item.id)} + > +
    -
  • - {#if item.full_name} -
  • -
    - {'Full name:'} - {item.full_name} -
    -
  • - {/if} - {#if item.role} -
  • -
    -
    {'Role:'}
    -
    -
    -
  • - {/if} - {#if item.source} -
  • -
    - {'Source:'} - {item.source} -
    -
  • - {/if} - {#if item.type} -
  • -
    - {'Type:'} - {item.type} -
    -
  • - {/if} -
-
    -
  • -
    -
    {'Permissions:'}
    -
    - {#each innerItem.permissions as permission, index} -
    - -
    +
    +
      +
    • +
      + {'User name:'} + {item.user_name} +
      +
    • + {#if item.full_name} +
    • +
      + {'Full name:'} + {item.full_name} +
      +
    • + {/if} + {#if item.type} +
    • +
      + {'Type:'} + {item.type} +
      +
    • + {/if} + {#if item.source} +
    • +
      + {'Source:'} + {item.source} +
      +
    • + {/if} + {#if item.role} +
    • +
      +
      {'Role:'}
      +
      + changeRole(e)}> + {#each roleOptions as option} + + {/each} + +
      +
      +
    • + {/if} +
    +
      +
    • +
      +
      {'Permissions:'}
      +
      + {#each innerItem.permissions as permission, index} +
      + +
      + {}} + on:click={() => deletePermission(index)} + /> +
      +
      + {/each} + {#if innerItem.permissions?.length < 5} +
      {}} - on:click={() => deletePermission(index)} + on:click={() => addPermission()} />
      -
      - {/each} - {#if innerItem.permissions?.length < 5} -
      - {}} - on:click={() => addPermission()} - /> -
      - {/if} -
      -
    -
  • -
- - {#if innerActions.length > 0} -
-
-
- {'Agent'} + {/if} +
- {#each allActions as title} + + + + {#if innerActions.length > 0} +
+
-
{title.name}
-
- checkAll(e, title)} - /> -
+ {'Agent'}
- {/each} -
-
- {#each innerActions as agentActionItem} -
-
- {agentActionItem.agent_name} -
- {#each agentActionItem.actions as actionItem} -
+ {#each allActions as title} +
+
{title.name}
+
checkAction(e, agentActionItem, actionItem)} + checked={title.checked} + on:change={e => checkAll(e, title)} />
- {/each} -
- {/each} +
+ {/each} +
+
+ {#each innerActions as agentActionItem} +
+
+ {agentActionItem.agent_name} +
+ {#each agentActionItem.actions as actionItem} +
+ checkAction(e, agentActionItem, actionItem)} + /> +
+ {/each} +
+ {/each} +
-
- {/if} -
- + {/if} +
+ + {/if} {/if} \ No newline at end of file diff --git a/svelte.config.js b/svelte.config.js index 208fd8d..c59ac90 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -14,6 +14,7 @@ const config = { $fileTypes: './src/lib/helpers/types/fileTypes.js', $audioTypes: './src/lib/helpers/types/audioTypes.js', $userTypes: './src/lib/helpers/types/userTypes.js', + $roleTypes: './src/lib/helpers/types/roleTypes.js', $pluginTypes: './src/lib/helpers/types/pluginTypes.js', }, @@ -60,6 +61,7 @@ const config = { "/page/mongodb", "/page/user/me", "/page/users", + "/page/roles", "/chat", "/chat/[agentId]", "/chat/[agentId]/[conversationId]", From 5ad4bf3025d6508f9a54c48f0e5d49f966351704 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 14 Nov 2024 17:33:00 -0600 Subject: [PATCH 2/3] add role manage page --- src/lib/helpers/enums.js | 2 + src/lib/helpers/types/roleTypes.js | 23 ++ src/lib/helpers/types/userTypes.js | 1 + src/lib/scss/app.scss | 1 + src/lib/scss/custom/pages/_roles.scss | 119 ++++++ src/lib/scss/custom/pages/_users.scss | 5 - src/lib/services/api-endpoints.js | 1 + src/lib/services/role-service.js | 21 +- .../[conversationId]/chat-box.svelte | 2 +- .../agent/[agentId]/agent-overview.svelte | 2 +- src/routes/page/agent/card-agent.svelte | 2 +- src/routes/page/roles/+page.svelte | 184 ++++++++- src/routes/page/roles/role-item.svelte | 380 ++++++++++++++++++ src/routes/page/users/+page.svelte | 29 +- src/routes/page/users/user-item.svelte | 15 +- 15 files changed, 752 insertions(+), 35 deletions(-) create mode 100644 src/lib/scss/custom/pages/_roles.scss create mode 100644 src/routes/page/roles/role-item.svelte 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/types/roleTypes.js b/src/lib/helpers/types/roleTypes.js index 8ff08cc..1e036b9 100644 --- a/src/lib/helpers/types/roleTypes.js +++ b/src/lib/helpers/types/roleTypes.js @@ -3,9 +3,32 @@ * @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..076c269 --- /dev/null +++ b/src/lib/scss/custom/pages/_roles.scss @@ -0,0 +1,119 @@ +.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; + + 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 84ecd8a..e948ee1 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; diff --git a/src/lib/services/api-endpoints.js b/src/lib/services/api-endpoints.js index 265d761..7c9929c 100644 --- a/src/lib/services/api-endpoints.js +++ b/src/lib/services/api-endpoints.js @@ -6,6 +6,7 @@ export const endpoints = { roleOptionsUrl: `${host}/role/options`, rolesUrl: `${host}/roles`, roleDetailUrl: `${host}/role/{id}/details`, + roleUpdateUrl: `${host}/role`, // user tokenUrl: `${host}/token`, diff --git a/src/lib/services/role-service.js b/src/lib/services/role-service.js index 0fa8339..15f4011 100644 --- a/src/lib/services/role-service.js +++ b/src/lib/services/role-service.js @@ -11,7 +11,15 @@ export async function getRoleOptions() { } - +/** + * 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; +} /** @@ -24,3 +32,14 @@ export async function getRoleDetails(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/routes/chat/[agentId]/[conversationId]/chat-box.svelte b/src/routes/chat/[agentId]/[conversationId]/chat-box.svelte index 7f42d0e..8b8e4a3 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', { 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} + + + + + +{#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 8fc2034..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 @@ -58,7 +56,7 @@ userName: '', externalId: '', role: '', - source: '', + type: '', }; onMount(async () => { @@ -149,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, @@ -157,7 +155,7 @@ user_names: !!userName ? [userName] : [], external_ids: !!externalId ? [externalId] : [], roles: !!role ? [role] : [], - sources: !!source ? [source] : [] + types: !!type ? [type] : [] }; } @@ -179,7 +177,7 @@ if (res) { isLoading = false; isComplete = true; - postUpdateUser(data); + postUpdate(data); setTimeout(() => { isComplete = false; }, duration); @@ -197,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 }; @@ -219,19 +217,6 @@
{$_('User List')}
-
- - - - - - -
@@ -246,7 +231,7 @@ - +