@swiss-ai-hub/web 0.290.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +479 -0
- package/app.config.ts +1 -0
- package/app.vue +52 -0
- package/assets/css/main.css +4 -0
- package/assets/images/logo.png +0 -0
- package/components/Agent/Avatar.vue +40 -0
- package/components/Agent/Card.vue +139 -0
- package/components/Agent/Configuration.vue +20 -0
- package/components/Agent/CreateModal.vue +287 -0
- package/components/Agent/EmptyCard.vue +35 -0
- package/components/Agent/List.vue +85 -0
- package/components/Agent/TemplateCard.vue +58 -0
- package/components/AppLoader.vue +55 -0
- package/components/Chat/Message.vue +90 -0
- package/components/Chat/SourceNodes.vue +120 -0
- package/components/Chat/Thread.vue +134 -0
- package/components/Costs/Table.vue +56 -0
- package/components/Dashboard/Component/BarChart.vue +46 -0
- package/components/Dashboard/Component/LineChart.vue +138 -0
- package/components/Dashboard/Component/Number.vue +31 -0
- package/components/Dashboard/Grid.vue +214 -0
- package/components/Dashboard/Item.vue +75 -0
- package/components/Dashboard/Trend.vue +34 -0
- package/components/Display/List/DisplayList.vue +93 -0
- package/components/Evaluation/Dataset/Card.vue +70 -0
- package/components/Evaluation/Dataset/Create.vue +81 -0
- package/components/Evaluation/Dataset/Edit.vue +132 -0
- package/components/Event/Display/AddMemoryToChatHistoryEvent.vue +60 -0
- package/components/Event/Display/AgentInTheLoopRequestEvent.vue +56 -0
- package/components/Event/Display/AgentInTheLoopResponseEvent.vue +17 -0
- package/components/Event/Display/Base.vue +125 -0
- package/components/Event/Display/BaseRetrieveMemoryEvent.vue +101 -0
- package/components/Event/Display/BaseStoreMemoryEvent.vue +182 -0
- package/components/Event/Display/ChunkEvent.vue +40 -0
- package/components/Event/Display/EmbeddingEvent.vue +25 -0
- package/components/Event/Display/ExceptionEvent.vue +21 -0
- package/components/Event/Display/GuardAcceptEvent.vue +25 -0
- package/components/Event/Display/GuardEvent.vue +17 -0
- package/components/Event/Display/GuardRejectionEvent.vue +25 -0
- package/components/Event/Display/HumanInTheLoopRequestEvent.vue +53 -0
- package/components/Event/Display/HumanInTheLoopResponseEvent.vue +40 -0
- package/components/Event/Display/LLMCostEvent.vue +25 -0
- package/components/Event/Display/LLMEvent.vue +92 -0
- package/components/Event/Display/LimitChatHistoryEvent.vue +60 -0
- package/components/Event/Display/RAGFailureStopEvent.vue +28 -0
- package/components/Event/Display/RAGStartEvent.vue +77 -0
- package/components/Event/Display/RAGSuccessStopEvent.vue +16 -0
- package/components/Event/Display/RawDataContent.vue +69 -0
- package/components/Event/Display/RerankerEvent.vue +39 -0
- package/components/Event/Display/RetrieverEvent.vue +22 -0
- package/components/Event/Display/RouterEvent.vue +54 -0
- package/components/Event/Display/StandaloneQuestionCondenserEvent.vue +38 -0
- package/components/Event/Display/StopEvent.vue +16 -0
- package/components/Event/Display/ThoughtEvent.vue +20 -0
- package/components/Event/Display/ToolEvent.vue +38 -0
- package/components/Event/Display/UnknownEvent.vue +17 -0
- package/components/Event/Display/UserMessageEvent.vue +35 -0
- package/components/Event/List/EventList.vue +249 -0
- package/components/Event/Statistics.vue +49 -0
- package/components/Event/Timeseries.vue +224 -0
- package/components/FormKit/AgentSelector.vue +307 -0
- package/components/FormKit/ChipsInput.vue +62 -0
- package/components/FormKit/DynamicConfiguration.vue +155 -0
- package/components/FormKit/IconSelector.vue +72 -0
- package/components/FormKit/KnowledgeDatabaseSelector.vue +92 -0
- package/components/FormKit/LocaleInput.vue +150 -0
- package/components/FormKit/ModelSelect.vue +110 -0
- package/components/FormKit/Repeater.vue +93 -0
- package/components/FormKit/VectorStoreInput.vue +247 -0
- package/components/Knowledge/Document/List.vue +140 -0
- package/components/Knowledge/Document/Overview.vue +28 -0
- package/components/Knowledge/Document/UploadModal.vue +298 -0
- package/components/Knowledge/Document/WithNodes.vue +105 -0
- package/components/Knowledge/Namespace/Card.vue +108 -0
- package/components/Knowledge/Namespace/CreateModal.vue +203 -0
- package/components/Knowledge/Namespace/EditModal.vue +134 -0
- package/components/Knowledge/Namespace/EmptyCard.vue +35 -0
- package/components/Knowledge/Node/Content.vue +71 -0
- package/components/Markdown/Renderer.vue +87 -0
- package/components/Memory/DetailPage.vue +48 -0
- package/components/Memory/Edit.vue +241 -0
- package/components/Memory/Graph.vue +318 -0
- package/components/Memory/List.vue +155 -0
- package/components/Memory/MemoryManagementPage.vue +178 -0
- package/components/Memory/OpenWebUIContent.vue +96 -0
- package/components/Memory/PageLayout.vue +72 -0
- package/components/Models/ModelDetailsPanel.vue +250 -0
- package/components/Models/NamespaceCard.vue +79 -0
- package/components/Navigation/Left.vue +85 -0
- package/components/Navigation/Top.vue +31 -0
- package/components/Notification/Item.vue +88 -0
- package/components/Notification/NotificationsOverlay.vue +164 -0
- package/components/Process/Card.vue +119 -0
- package/components/Process/Configuration.vue +20 -0
- package/components/Process/CreateModal.vue +276 -0
- package/components/Process/EmptyCard.vue +35 -0
- package/components/Process/Form.vue +153 -0
- package/components/Process/Starts.vue +44 -0
- package/components/Process/Walkthrough/List.vue +162 -0
- package/components/Role/AccessRulesEditor.vue +132 -0
- package/components/Role/Card.vue +68 -0
- package/components/Role/Create.vue +55 -0
- package/components/Role/Edit.vue +82 -0
- package/components/Role/UsageLimitsEditor.vue +225 -0
- package/components/Service/Selection.vue +148 -0
- package/components/Structural/Column.vue +74 -0
- package/components/Structural/Screen.vue +10 -0
- package/components/Structural/Substructure.vue +5 -0
- package/components/Tenant/Switcher.vue +102 -0
- package/components/Thread/Details.vue +135 -0
- package/components/Thread/Hierarchy.vue +136 -0
- package/components/Thread/Info.vue +41 -0
- package/components/Thread/List.vue +136 -0
- package/components/User/Bar.vue +74 -0
- package/components/User/List.vue +86 -0
- package/components/User/RoleChips.vue +83 -0
- package/components/User/Settings.vue +79 -0
- package/components/Workflow/Modal.vue +39 -0
- package/components/Workflow/NodeCard.vue +41 -0
- package/components/Workflow/StartNode.vue +24 -0
- package/components/Workflow/StepNode.vue +27 -0
- package/components/Workflow/StopNode.vue +24 -0
- package/components/Workflow/Visualization.vue +265 -0
- package/components/mdc/MarkdownFigure.vue +9 -0
- package/components/mdc/MarkdownTable.vue +9 -0
- package/components/mdc/ResolveImageComponent.vue +58 -0
- package/composables/agent/useAgentClass.ts +27 -0
- package/composables/agent/useAgentClassInstances.ts +27 -0
- package/composables/agent/useAgentClasses.ts +27 -0
- package/composables/agent/useAgentIconFromThread.ts +8 -0
- package/composables/agent/useAgentInstance.ts +28 -0
- package/composables/agent/useAgentInstanceThreads.ts +76 -0
- package/composables/agent/useAgentInstances.ts +25 -0
- package/composables/agent/useAgentNavigation.ts +35 -0
- package/composables/agent/useCreateAgentInstance.ts +33 -0
- package/composables/agent/useDeleteAgentInstance.ts +31 -0
- package/composables/agent/useUpdateAgentInstance.ts +40 -0
- package/composables/auth/useAuth.ts +54 -0
- package/composables/auth/useAuthProviders.ts +14 -0
- package/composables/chat/useChatCompletions.ts +30 -0
- package/composables/dashboard/useAgentNameFromDashboardWidget.ts +27 -0
- package/composables/dashboard/useDashboardComponent.ts +27 -0
- package/composables/dashboard/useSaveDashboard.ts +21 -0
- package/composables/document/useCreateNamespace.ts +26 -0
- package/composables/document/useDatabases.ts +23 -0
- package/composables/document/useDocument.ts +29 -0
- package/composables/document/useDocumentUrl.ts +20 -0
- package/composables/document/useDocuments.ts +107 -0
- package/composables/document/useNodes.ts +29 -0
- package/composables/document/useSummaryNodes.ts +32 -0
- package/composables/document/useUpdateNamespace.ts +22 -0
- package/composables/evaluation/useCreateDataset.ts +19 -0
- package/composables/evaluation/useDataset.ts +26 -0
- package/composables/evaluation/useDatasets.ts +25 -0
- package/composables/evaluation/useUpdateDataset.ts +23 -0
- package/composables/event/useBasicEventStatistics.ts +83 -0
- package/composables/event/useEventColor.ts +25 -0
- package/composables/event/useEventComponent.ts +87 -0
- package/composables/event/useEventTimeseries.ts +39 -0
- package/composables/event/useEventTimeseriesStats.ts +26 -0
- package/composables/file/useFileUpload.ts +91 -0
- package/composables/file/useSupportedFileTypes.ts +22 -0
- package/composables/form/useCreateInstanceForm.ts +251 -0
- package/composables/form/useFormKitTransform.ts +753 -0
- package/composables/memory/useMemoryCRUD.ts +88 -0
- package/composables/memory/useMemoryFactory.ts +319 -0
- package/composables/memory/useMemorySearchFilter.ts +74 -0
- package/composables/models/useModelsList.ts +24 -0
- package/composables/models/useSingleModel.ts +30 -0
- package/composables/notification/useNotificationPoller.ts +58 -0
- package/composables/notification/useNotifications.ts +57 -0
- package/composables/notification/useUpdateMultipleNotifications.ts +17 -0
- package/composables/notification/useUpdateNotification.ts +17 -0
- package/composables/process/useCreateProcessInstance.ts +32 -0
- package/composables/process/useDeleteProcessInstance.ts +31 -0
- package/composables/process/useProcessClasses.ts +27 -0
- package/composables/process/useProcessInstance.ts +28 -0
- package/composables/process/useProcessInstances.ts +27 -0
- package/composables/process/useProcessWalkthroughs.ts +73 -0
- package/composables/process/useSendProcessStartForm.ts +43 -0
- package/composables/process/useUpdateProcessInstance.ts +40 -0
- package/composables/role/useCreateRole.ts +19 -0
- package/composables/role/useDeleteRole.ts +21 -0
- package/composables/role/useRole.ts +30 -0
- package/composables/role/useRoles.ts +25 -0
- package/composables/role/useUpdateRole.ts +22 -0
- package/composables/suite/useApps.ts +31 -0
- package/composables/suite/useSuite.ts +26 -0
- package/composables/tenant/useActiveTenant.ts +27 -0
- package/composables/tenant/useSysadminNavigation.ts +19 -0
- package/composables/tenant/useTenant.ts +38 -0
- package/composables/tenant/useTenantMemberships.ts +15 -0
- package/composables/tenant/useTenantPath.ts +20 -0
- package/composables/tenant/useTenantPolling.ts +30 -0
- package/composables/tenant/useTenantReady.ts +12 -0
- package/composables/theme/useDarkMode.ts +5 -0
- package/composables/thread/useThread.ts +27 -0
- package/composables/thread/useThreadEvents.ts +91 -0
- package/composables/thread/useThreadUtils.ts +49 -0
- package/composables/thread/useThreads.ts +64 -0
- package/composables/thread/useThreadsInfinite.ts +56 -0
- package/composables/translation/useTranslate.ts +20 -0
- package/composables/useRouteReady.ts +21 -0
- package/composables/useTimeAgo.ts +40 -0
- package/composables/user/useAssignRoleToUser.ts +22 -0
- package/composables/user/useMyUser.ts +25 -0
- package/composables/user/useRevokeRoleFromUser.ts +21 -0
- package/composables/user/useUser.ts +30 -0
- package/composables/user/useUsers.ts +63 -0
- package/composables/utils/useJsonTree.ts +138 -0
- package/formkit.config.ts +44 -0
- package/i18n/locales/de.yaml +815 -0
- package/i18n/locales/en.yaml +804 -0
- package/i18n/locales/fr.yaml +812 -0
- package/i18n/locales/it.yaml +808 -0
- package/layouts/anonymous.vue +8 -0
- package/layouts/default.vue +116 -0
- package/middleware/auth.global.ts +62 -0
- package/nuxt.config.ts +145 -0
- package/package.json +114 -0
- package/pages/[tenant]/index.vue +31 -0
- package/pages/[tenant]/notifications/index.vue +235 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/chat.vue +67 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/configuration.vue +122 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/memories/[memory_id].vue +3 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/memories.vue +20 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/overview.vue +72 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/threads.vue +52 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id]/workflow.vue +19 -0
- package/pages/[tenant]/service/agents/[agent_class]-[agent_id].vue +63 -0
- package/pages/[tenant]/service/agents/templates.vue +102 -0
- package/pages/[tenant]/service/agents.vue +185 -0
- package/pages/[tenant]/service/datasets/[dataset_id].vue +81 -0
- package/pages/[tenant]/service/datasets.vue +53 -0
- package/pages/[tenant]/service/health/index.vue +3 -0
- package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/nodes.vue +20 -0
- package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/overview.vue +40 -0
- package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id]/summary.vue +88 -0
- package/pages/[tenant]/service/knowledge/[db]/[namespace]/[document_id].vue +48 -0
- package/pages/[tenant]/service/knowledge/[db]/[namespace].vue +144 -0
- package/pages/[tenant]/service/knowledge.vue +126 -0
- package/pages/[tenant]/service/models/[model_name].vue +84 -0
- package/pages/[tenant]/service/models.vue +61 -0
- package/pages/[tenant]/service/my-account.vue +117 -0
- package/pages/[tenant]/service/openai/[thread_id]/[display_id]/memories.vue +66 -0
- package/pages/[tenant]/service/openai/[thread_id]/[display_id]/sources.vue +100 -0
- package/pages/[tenant]/service/openai/[thread_id]/[display_id]/tracing.vue +49 -0
- package/pages/[tenant]/service/openai.vue +101 -0
- package/pages/[tenant]/service/organization-memories/graph.vue +97 -0
- package/pages/[tenant]/service/organization-memories/list/[memory_id].vue +3 -0
- package/pages/[tenant]/service/organization-memories/list.vue +150 -0
- package/pages/[tenant]/service/organization-memories.vue +3 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/[process_walkthrough_id].vue +7 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/configuration.vue +106 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/overview.vue +67 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/start.vue +26 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/walkthroughs/[process_walkthrough_id]/overview.vue +14 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id]/walkthroughs.vue +54 -0
- package/pages/[tenant]/service/processes/[process_class]-[process_id].vue +60 -0
- package/pages/[tenant]/service/processes.vue +129 -0
- package/pages/[tenant]/service/roles/[role_id].vue +54 -0
- package/pages/[tenant]/service/roles.vue +84 -0
- package/pages/[tenant]/service/threads/[thread_id]/chat.vue +51 -0
- package/pages/[tenant]/service/threads/[thread_id]/display/[display_id].vue +21 -0
- package/pages/[tenant]/service/threads/[thread_id]/display.vue +29 -0
- package/pages/[tenant]/service/threads/[thread_id]/hierarchy.vue +14 -0
- package/pages/[tenant]/service/threads/[thread_id]/memories/[memory_id].vue +3 -0
- package/pages/[tenant]/service/threads/[thread_id]/memories.vue +19 -0
- package/pages/[tenant]/service/threads/[thread_id]/overview.vue +100 -0
- package/pages/[tenant]/service/threads/[thread_id].vue +54 -0
- package/pages/[tenant]/service/threads.vue +52 -0
- package/pages/[tenant]/service/user-memories/graph.vue +97 -0
- package/pages/[tenant]/service/user-memories/list/[memory_id].vue +3 -0
- package/pages/[tenant]/service/user-memories/list.vue +150 -0
- package/pages/[tenant]/service/user-memories.vue +3 -0
- package/pages/[tenant]/service/users/[user_id].vue +117 -0
- package/pages/[tenant]/service/users.vue +88 -0
- package/pages/auth/callback.vue +52 -0
- package/pages/auth/login.vue +80 -0
- package/pages/auth/renew.vue +24 -0
- package/pages/index.vue +59 -0
- package/pages/select-tenant.vue +76 -0
- package/plugins/0.runtime-config.client.ts +55 -0
- package/plugins/apexcharts.client.ts +5 -0
- package/plugins/api-client.client.ts +38 -0
- package/plugins/dark-mode.client.ts +12 -0
- package/plugins/keycloak-client.ts +41 -0
- package/plugins/oidc-client.ts +78 -0
- package/sdk/client/client/client.gen.ts +237 -0
- package/sdk/client/client/index.ts +24 -0
- package/sdk/client/client/types.gen.ts +213 -0
- package/sdk/client/client/utils.gen.ts +407 -0
- package/sdk/client/client.gen.ts +25 -0
- package/sdk/client/core/auth.gen.ts +42 -0
- package/sdk/client/core/bodySerializer.gen.ts +96 -0
- package/sdk/client/core/params.gen.ts +181 -0
- package/sdk/client/core/pathSerializer.gen.ts +180 -0
- package/sdk/client/core/queryKeySerializer.gen.ts +136 -0
- package/sdk/client/core/serverSentEvents.gen.ts +265 -0
- package/sdk/client/core/types.gen.ts +118 -0
- package/sdk/client/core/utils.gen.ts +143 -0
- package/sdk/client/index.ts +1013 -0
- package/sdk/client/schemas.gen.ts +35395 -0
- package/sdk/client/sdk.gen.ts +3438 -0
- package/sdk/client/transformers.gen.ts +143 -0
- package/sdk/client/types.gen.ts +27567 -0
- package/tailwind.config.mjs +27 -0
- package/themes/aihub-theme.ts +125 -0
- package/types/DashboardWidget.ts +13 -0
- package/types/EventChartInput.ts +7 -0
- package/types/NavItem.ts +6 -0
- package/types/TimeseriesInput.ts +7 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col gap-4">
|
|
3
|
+
<!-- Agent Class Selection -->
|
|
4
|
+
<div>
|
|
5
|
+
<label
|
|
6
|
+
for="agent-class-select"
|
|
7
|
+
class="mb-1 block text-sm font-medium"
|
|
8
|
+
>
|
|
9
|
+
{{ t('agent.selector.class.label') }}
|
|
10
|
+
</label>
|
|
11
|
+
<Select
|
|
12
|
+
v-model="selectedClass"
|
|
13
|
+
input-id="agent-class-select"
|
|
14
|
+
:options="filteredClassOptions"
|
|
15
|
+
option-label="displayName"
|
|
16
|
+
option-value="name"
|
|
17
|
+
:placeholder="classPlaceholder ?? t('agent.selector.class.placeholder')"
|
|
18
|
+
:filter="filter"
|
|
19
|
+
:loading="isLoading"
|
|
20
|
+
class="w-full"
|
|
21
|
+
>
|
|
22
|
+
<template #option="{ option }">
|
|
23
|
+
<div class="flex items-center gap-2">
|
|
24
|
+
<Icon
|
|
25
|
+
:name="option.icon || 'mage:robot'"
|
|
26
|
+
size="1.2em"
|
|
27
|
+
/>
|
|
28
|
+
<span>{{ option.displayName }}</span>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
<template #value="{ value }">
|
|
32
|
+
<div
|
|
33
|
+
v-if="value"
|
|
34
|
+
class="flex items-center gap-2"
|
|
35
|
+
>
|
|
36
|
+
<Icon
|
|
37
|
+
:name="getClassIcon(value)"
|
|
38
|
+
size="1.2em"
|
|
39
|
+
/>
|
|
40
|
+
<span>{{ getClassDisplayName(value) }}</span>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
</Select>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Agent ID Selection (shown when class selected) -->
|
|
47
|
+
<div v-if="selectedClass">
|
|
48
|
+
<label
|
|
49
|
+
for="agent-id-select"
|
|
50
|
+
class="mb-1 block text-sm font-medium"
|
|
51
|
+
>
|
|
52
|
+
{{ t('agent.selector.id.label') }}
|
|
53
|
+
</label>
|
|
54
|
+
<Select
|
|
55
|
+
v-model="selectedId"
|
|
56
|
+
input-id="agent-id-select"
|
|
57
|
+
:options="idOptions"
|
|
58
|
+
option-label="displayName"
|
|
59
|
+
option-value="id"
|
|
60
|
+
:placeholder="idPlaceholder ?? t('agent.selector.id.placeholder')"
|
|
61
|
+
:filter="filter"
|
|
62
|
+
:loading="isLoadingInstances"
|
|
63
|
+
class="w-full"
|
|
64
|
+
>
|
|
65
|
+
<template #option="{ option }">
|
|
66
|
+
<div class="flex items-center gap-2">
|
|
67
|
+
<Icon
|
|
68
|
+
:name="option.icon || 'mage:user'"
|
|
69
|
+
size="1.2em"
|
|
70
|
+
/>
|
|
71
|
+
<span>{{ option.displayName }}</span>
|
|
72
|
+
</div>
|
|
73
|
+
</template>
|
|
74
|
+
<template #value="{ value }">
|
|
75
|
+
<div
|
|
76
|
+
v-if="value"
|
|
77
|
+
class="flex items-center gap-2"
|
|
78
|
+
>
|
|
79
|
+
<Icon
|
|
80
|
+
:name="getIdIcon(value)"
|
|
81
|
+
size="1.2em"
|
|
82
|
+
/>
|
|
83
|
+
<span>{{ getIdDisplayName(value) }}</span>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
</Select>
|
|
87
|
+
<small class="mt-1 text-surface-500">
|
|
88
|
+
{{ t('agent.selector.id.help') }}
|
|
89
|
+
</small>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
import { getAgentClasses, getAgentClassInstances } from '@core/sdk/client'
|
|
96
|
+
import { useChangeCase } from '@vueuse/integrations/useChangeCase'
|
|
97
|
+
|
|
98
|
+
import type { AgentClassDto, FullAgentInstanceDto, LocaleString } from '@core/sdk/client'
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extracts the localized string from a LocaleString object.
|
|
102
|
+
* Falls back to first available value if the requested locale is not available.
|
|
103
|
+
*/
|
|
104
|
+
function getLocalizedValue(localeString: LocaleString | string | undefined, locale: string): string {
|
|
105
|
+
if (!localeString) return ''
|
|
106
|
+
if (typeof localeString === 'string') return localeString
|
|
107
|
+
|
|
108
|
+
// LocaleString object - try to get the current locale, fallback to en, then any
|
|
109
|
+
return (localeString as Record<string, string>)[locale]
|
|
110
|
+
|| (localeString as Record<string, string>).en
|
|
111
|
+
|| Object.values(localeString)[0]
|
|
112
|
+
|| ''
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface AgentSelectorValue {
|
|
116
|
+
agent_class: string
|
|
117
|
+
agent_id: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface ClassOption {
|
|
121
|
+
name: string
|
|
122
|
+
displayName: string
|
|
123
|
+
icon: string
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface IdOption {
|
|
127
|
+
id: string
|
|
128
|
+
displayName: string
|
|
129
|
+
icon: string
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface AgentSelectorProps {
|
|
133
|
+
context: {
|
|
134
|
+
node: {
|
|
135
|
+
input: (value: AgentSelectorValue | null) => void
|
|
136
|
+
}
|
|
137
|
+
value?: AgentSelectorValue | null
|
|
138
|
+
attrs: Record<string, unknown>
|
|
139
|
+
startEvent?: string
|
|
140
|
+
classPlaceholder?: string
|
|
141
|
+
idPlaceholder?: string
|
|
142
|
+
filter?: boolean
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const props = defineProps<AgentSelectorProps>()
|
|
147
|
+
const { t, locale } = useI18n()
|
|
148
|
+
const { tenantId } = useTenant()
|
|
149
|
+
|
|
150
|
+
// Get custom props from context (FormKit passes them there, not as direct props)
|
|
151
|
+
const startEvent = computed(() => props.context.startEvent)
|
|
152
|
+
const classPlaceholder = computed(() => props.context.classPlaceholder)
|
|
153
|
+
const idPlaceholder = computed(() => props.context.idPlaceholder)
|
|
154
|
+
const filter = computed(() => props.context.filter ?? true)
|
|
155
|
+
|
|
156
|
+
// State
|
|
157
|
+
const agentClasses = ref<AgentClassDto[]>([])
|
|
158
|
+
const agentInstances = ref<FullAgentInstanceDto[]>([])
|
|
159
|
+
const isLoading = ref(false)
|
|
160
|
+
const isLoadingInstances = ref(false)
|
|
161
|
+
|
|
162
|
+
// Get current value from context
|
|
163
|
+
const currentValue = computed(() => props.context.value ?? null)
|
|
164
|
+
|
|
165
|
+
// Selected class (computed property that syncs with the form value)
|
|
166
|
+
const selectedClass = computed({
|
|
167
|
+
get: () => currentValue.value?.agent_class ?? null,
|
|
168
|
+
set: (value: string | null) => {
|
|
169
|
+
if (value) {
|
|
170
|
+
// When class changes, reset agent ID and fetch instances
|
|
171
|
+
emitValue(value, '')
|
|
172
|
+
fetchInstances(value)
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
props.context.node.input(null)
|
|
176
|
+
agentInstances.value = []
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// Selected agent ID (computed property that syncs with the form value)
|
|
182
|
+
const selectedId = computed({
|
|
183
|
+
get: () => currentValue.value?.agent_id ?? null,
|
|
184
|
+
set: (value: string | null) => {
|
|
185
|
+
if (selectedClass.value && value) {
|
|
186
|
+
emitValue(selectedClass.value, value)
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// Emit complete value object
|
|
192
|
+
function emitValue(agentClass: string, agentId: string) {
|
|
193
|
+
props.context.node.input({
|
|
194
|
+
agent_class: agentClass,
|
|
195
|
+
agent_id: agentId,
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Class options for select
|
|
200
|
+
const classOptions = computed<ClassOption[]>(() =>
|
|
201
|
+
agentClasses.value.map(cls => ({
|
|
202
|
+
name: cls.agent_class,
|
|
203
|
+
displayName: getLocalizedValue(cls.name, locale.value) || useChangeCase(cls.agent_class, 'capitalCase'),
|
|
204
|
+
icon: cls.icon || 'mage:robot',
|
|
205
|
+
})),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
// Filtered class options based on startEvent
|
|
209
|
+
const filteredClassOptions = computed<ClassOption[]>(() => {
|
|
210
|
+
if (!startEvent.value) return classOptions.value // No filter = show all
|
|
211
|
+
|
|
212
|
+
return classOptions.value.filter((classOption) => {
|
|
213
|
+
const agentClass = agentClasses.value.find(c => c.agent_class === classOption.name)
|
|
214
|
+
if (!agentClass) return false
|
|
215
|
+
|
|
216
|
+
// Check if any start_event matches the filter
|
|
217
|
+
return agentClass.start_events.some(event =>
|
|
218
|
+
event.event_name === startEvent.value
|
|
219
|
+
|| event.event_parents.includes(startEvent.value!),
|
|
220
|
+
)
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// ID options based on selected class
|
|
225
|
+
const idOptions = computed<IdOption[]>(() => {
|
|
226
|
+
return agentInstances.value.map(instance => ({
|
|
227
|
+
id: instance.agent_config.agent_id,
|
|
228
|
+
displayName: instance.agent_config.name || instance.agent_config.agent_id,
|
|
229
|
+
icon: instance.agent_config.icon || 'mage:user',
|
|
230
|
+
}))
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
function getClassIcon(name: string): string {
|
|
234
|
+
const option = classOptions.value.find(opt => opt.name === name)
|
|
235
|
+
return option?.icon ?? 'mage:robot'
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function getClassDisplayName(name: string): string {
|
|
239
|
+
const option = classOptions.value.find(opt => opt.name === name)
|
|
240
|
+
return option?.displayName ?? name
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function getIdIcon(id: string): string {
|
|
244
|
+
const option = idOptions.value.find(opt => opt.id === id)
|
|
245
|
+
return option?.icon ?? 'mage:user'
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function getIdDisplayName(id: string): string {
|
|
249
|
+
const option = idOptions.value.find(opt => opt.id === id)
|
|
250
|
+
return option?.displayName ?? id
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function fetchClasses() {
|
|
254
|
+
isLoading.value = true
|
|
255
|
+
try {
|
|
256
|
+
const response = await getAgentClasses({
|
|
257
|
+
composable: '$fetch',
|
|
258
|
+
path: { tenant_id: tenantId.value! },
|
|
259
|
+
})
|
|
260
|
+
agentClasses.value = response
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
console.error('Failed to fetch agent classes:', error)
|
|
264
|
+
agentClasses.value = []
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
isLoading.value = false
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function fetchInstances(agentClass: string) {
|
|
272
|
+
isLoadingInstances.value = true
|
|
273
|
+
try {
|
|
274
|
+
const response = await getAgentClassInstances({
|
|
275
|
+
composable: '$fetch',
|
|
276
|
+
path: { tenant_id: tenantId.value!, agent_class: agentClass },
|
|
277
|
+
})
|
|
278
|
+
agentInstances.value = response
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error(`Failed to fetch instances for class "${agentClass}":`, error)
|
|
282
|
+
agentInstances.value = []
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
isLoadingInstances.value = false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Fetch classes on mount
|
|
290
|
+
onMounted(() => {
|
|
291
|
+
fetchClasses()
|
|
292
|
+
|
|
293
|
+
// If there's already a selected class, fetch its instances
|
|
294
|
+
if (currentValue.value?.agent_class) {
|
|
295
|
+
fetchInstances(currentValue.value.agent_class)
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
// Refetch if startEvent changes
|
|
300
|
+
watch(startEvent, () => {
|
|
301
|
+
// Clear selection if current class no longer matches filter
|
|
302
|
+
if (selectedClass.value && !filteredClassOptions.value.some(opt => opt.name === selectedClass.value)) {
|
|
303
|
+
props.context.node.input(null)
|
|
304
|
+
agentInstances.value = []
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col gap-2">
|
|
3
|
+
<InputText
|
|
4
|
+
v-model="draft"
|
|
5
|
+
:input-id="inputId"
|
|
6
|
+
:placeholder="placeholder"
|
|
7
|
+
class="w-full"
|
|
8
|
+
@keydown.enter.stop.prevent="commitDraft"
|
|
9
|
+
@keyup.enter.stop.prevent
|
|
10
|
+
@keypress.enter.stop.prevent
|
|
11
|
+
@blur="commitDraft"
|
|
12
|
+
/>
|
|
13
|
+
<div
|
|
14
|
+
v-if="chips.length > 0"
|
|
15
|
+
class="flex flex-wrap gap-2"
|
|
16
|
+
>
|
|
17
|
+
<Chip
|
|
18
|
+
v-for="value in chips"
|
|
19
|
+
:key="value"
|
|
20
|
+
:label="value"
|
|
21
|
+
removable
|
|
22
|
+
@remove="removeChip(value)"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
interface ChipsInputProps {
|
|
30
|
+
context: {
|
|
31
|
+
node: { input: (value: string[]) => void }
|
|
32
|
+
value?: string[] | null
|
|
33
|
+
placeholder?: string
|
|
34
|
+
inputId?: string
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const props = defineProps<ChipsInputProps>()
|
|
39
|
+
|
|
40
|
+
const placeholder = computed(() => props.context.placeholder)
|
|
41
|
+
const inputId = computed(() => props.context.inputId)
|
|
42
|
+
const draft = ref('')
|
|
43
|
+
|
|
44
|
+
const chips = computed<string[]>({
|
|
45
|
+
get: () => props.context.value ?? [],
|
|
46
|
+
set: (value: string[]) => props.context.node.input(value),
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
function commitDraft() {
|
|
50
|
+
const trimmed = draft.value.trim()
|
|
51
|
+
draft.value = ''
|
|
52
|
+
if (!trimmed)
|
|
53
|
+
return
|
|
54
|
+
if (chips.value.includes(trimmed))
|
|
55
|
+
return
|
|
56
|
+
chips.value = [...chips.value, trimmed]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function removeChip(value: string) {
|
|
60
|
+
chips.value = chips.value.filter(v => v !== value)
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="content">
|
|
3
|
+
<FormKit
|
|
4
|
+
id="form"
|
|
5
|
+
v-model="data"
|
|
6
|
+
type="form"
|
|
7
|
+
:submit-label="t('common.actions.save')"
|
|
8
|
+
:submit-attrs="{
|
|
9
|
+
inputClass: 'p-button p-component w-full',
|
|
10
|
+
}"
|
|
11
|
+
:config="{
|
|
12
|
+
validationVisibility: 'blur',
|
|
13
|
+
}"
|
|
14
|
+
@submit="submitHandler"
|
|
15
|
+
>
|
|
16
|
+
<FormKitSchema
|
|
17
|
+
:schema="schema"
|
|
18
|
+
:data="{ ...data }"
|
|
19
|
+
/>
|
|
20
|
+
|
|
21
|
+
<!-- Render repeaters separately (not supported by FormKit standard) -->
|
|
22
|
+
<FormKitRepeater
|
|
23
|
+
v-for="rep in repeaterElements"
|
|
24
|
+
:key="rep.path"
|
|
25
|
+
:model-value="getRepeaterData(rep.path)"
|
|
26
|
+
:name="rep.name"
|
|
27
|
+
:label="rep.label"
|
|
28
|
+
:add-label="rep.addLabel"
|
|
29
|
+
:children-schema="rep.childrenSchema"
|
|
30
|
+
:min="rep.min"
|
|
31
|
+
:max="rep.max"
|
|
32
|
+
@update:model-value="setRepeaterData(rep.path, $event)"
|
|
33
|
+
/>
|
|
34
|
+
</FormKit>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import {
|
|
40
|
+
buildFormKitSchema,
|
|
41
|
+
coerceNullableToggles,
|
|
42
|
+
extractRepeaterConfigs,
|
|
43
|
+
getNestedValue,
|
|
44
|
+
normalizeFormLocaleStrings,
|
|
45
|
+
seedFormDefaults,
|
|
46
|
+
seedNullableToggles,
|
|
47
|
+
setNestedValue,
|
|
48
|
+
type FormElement,
|
|
49
|
+
type RepeaterConfig,
|
|
50
|
+
} from '@core/composables/form/useFormKitTransform'
|
|
51
|
+
import { merge } from 'lodash-es'
|
|
52
|
+
|
|
53
|
+
import type { FormkitElement } from '@core/sdk/client'
|
|
54
|
+
import type { FormKitSchemaDefinition } from '@formkit/core'
|
|
55
|
+
|
|
56
|
+
const { t } = useI18n()
|
|
57
|
+
|
|
58
|
+
const props = defineProps<{
|
|
59
|
+
form: FormkitElement[]
|
|
60
|
+
initialData?: Record<string, unknown>
|
|
61
|
+
}>()
|
|
62
|
+
|
|
63
|
+
function hydrate(raw: Record<string, unknown>): Record<string, unknown> {
|
|
64
|
+
const seeded = seedNullableToggles(raw, props.form as FormElement[])
|
|
65
|
+
return seedFormDefaults(seeded, props.form as FormElement[])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const data = ref<Record<string, unknown>>(hydrate(props.initialData || {}))
|
|
69
|
+
|
|
70
|
+
watch(() => props.initialData, (newData) => {
|
|
71
|
+
if (newData && Object.keys(newData).length > 0) {
|
|
72
|
+
data.value = merge({}, data.value, hydrate(newData))
|
|
73
|
+
}
|
|
74
|
+
}, { deep: true })
|
|
75
|
+
|
|
76
|
+
const emit = defineEmits<{
|
|
77
|
+
submit: [Record<string, unknown>]
|
|
78
|
+
}>()
|
|
79
|
+
|
|
80
|
+
function createLabelPattern(): RegExp {
|
|
81
|
+
const keys = Object.keys(data.value)
|
|
82
|
+
if (keys.length === 0) return /(?!)/ // Never matches
|
|
83
|
+
return new RegExp(keys.map(key => `\\$${key}`).join('|'), 'g')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function replaceLabelVariables(label: string): string {
|
|
87
|
+
const pattern = createLabelPattern()
|
|
88
|
+
return label.replace(pattern, (match: string) => {
|
|
89
|
+
const key = match.substring(1)
|
|
90
|
+
return (data.value[key] as string) || match
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const schema = computed<FormKitSchemaDefinition>(() => {
|
|
95
|
+
return buildFormKitSchema(props.form as FormElement[], {
|
|
96
|
+
labelTransform: replaceLabelVariables,
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const repeaterElements = computed<RepeaterConfig[]>(() => {
|
|
101
|
+
return extractRepeaterConfigs(props.form as FormElement[])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
function getRepeaterData(path: string): Record<string, unknown>[] {
|
|
105
|
+
return getNestedValue(data.value, path)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function setRepeaterData(path: string, value: Record<string, unknown>[]): void {
|
|
109
|
+
setNestedValue(data.value, path, value)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function submitHandler() {
|
|
113
|
+
const coerced = coerceNullableToggles(data.value, props.form as FormElement[])
|
|
114
|
+
const normalizedData = normalizeFormLocaleStrings(coerced)
|
|
115
|
+
emit('submit', normalizedData)
|
|
116
|
+
}
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<style scoped>
|
|
120
|
+
.content {
|
|
121
|
+
@apply font-light text-xs
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.content :deep(form) {
|
|
125
|
+
@apply gap-6;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.content :deep(.formkit-help) {
|
|
129
|
+
@apply text-xs font-light text-surface-500 dark:text-surface-400;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.content :deep(.formkit-outer) {
|
|
133
|
+
@apply pt-3 pb-1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.content :deep(.formkit-group-fieldset) {
|
|
137
|
+
@apply border border-surface-300 dark:border-surface-600 rounded-lg p-4 mb-4;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.content :deep(.formkit-group-fieldset legend) {
|
|
141
|
+
@apply text-sm font-semibold px-2 text-surface-700 dark:text-surface-300;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.content :deep(.formkit-group-fieldset .formkit-group-fieldset) {
|
|
145
|
+
@apply ml-2 mt-2;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.content :deep(.formkit-slider-value-input) {
|
|
149
|
+
@apply w-20;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.content :deep(.formkit-slider-value-input .p-inputnumber-input) {
|
|
153
|
+
@apply text-center text-sm;
|
|
154
|
+
}
|
|
155
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center gap-3">
|
|
3
|
+
<!-- Icon preview (fixed width to prevent layout shift) -->
|
|
4
|
+
<div class="flex w-8 shrink-0 items-center justify-center">
|
|
5
|
+
<Icon
|
|
6
|
+
v-if="selectedIcon"
|
|
7
|
+
:name="selectedIcon"
|
|
8
|
+
class="text-2xl text-surface-700 dark:text-surface-200"
|
|
9
|
+
/>
|
|
10
|
+
<Icon
|
|
11
|
+
v-else
|
|
12
|
+
name="mage:image"
|
|
13
|
+
class="text-2xl text-surface-300 dark:text-surface-600"
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Editable Select -->
|
|
18
|
+
<Select
|
|
19
|
+
v-model="selectedIcon"
|
|
20
|
+
:options="iconOptions"
|
|
21
|
+
:editable="true"
|
|
22
|
+
:placeholder="placeholder"
|
|
23
|
+
option-label="name"
|
|
24
|
+
option-value="name"
|
|
25
|
+
class="grow"
|
|
26
|
+
>
|
|
27
|
+
<!-- Option template - shows icon + name for each option -->
|
|
28
|
+
<template #option="{ option }">
|
|
29
|
+
<div class="flex items-center gap-2">
|
|
30
|
+
<Icon
|
|
31
|
+
:name="option.name"
|
|
32
|
+
class="shrink-0 text-lg"
|
|
33
|
+
/>
|
|
34
|
+
<span>{{ option.name }}</span>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
</Select>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
interface IconSelectorProps {
|
|
43
|
+
context: {
|
|
44
|
+
node: {
|
|
45
|
+
input: (value: string) => void
|
|
46
|
+
}
|
|
47
|
+
value?: string
|
|
48
|
+
attrs: Record<string, unknown>
|
|
49
|
+
options?: string[]
|
|
50
|
+
placeholder?: string
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const props = defineProps<IconSelectorProps>()
|
|
55
|
+
|
|
56
|
+
// Get options from context (passed from backend)
|
|
57
|
+
const options = computed(() => props.context.options ?? [])
|
|
58
|
+
const placeholder = computed(() => props.context.placeholder ?? 'Select or enter an icon...')
|
|
59
|
+
|
|
60
|
+
// Convert string options to objects for Select component
|
|
61
|
+
const iconOptions = computed(() =>
|
|
62
|
+
options.value.map(name => ({ name })),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// Current selected icon value
|
|
66
|
+
const selectedIcon = computed({
|
|
67
|
+
get: () => props.context.value ?? '',
|
|
68
|
+
set: (newVal: string) => {
|
|
69
|
+
props.context.node.input(newVal || '')
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
</script>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<MultiSelect
|
|
4
|
+
v-model="selectedDatabases"
|
|
5
|
+
:options="databaseOptions"
|
|
6
|
+
option-label="displayName"
|
|
7
|
+
option-value="name"
|
|
8
|
+
:placeholder="placeholder ?? t('common.selectDatabases')"
|
|
9
|
+
:filter="filter"
|
|
10
|
+
display="chip"
|
|
11
|
+
class="w-full"
|
|
12
|
+
:loading="isLoading"
|
|
13
|
+
>
|
|
14
|
+
<template #option="{ option }">
|
|
15
|
+
<div class="flex items-center gap-2">
|
|
16
|
+
<Icon
|
|
17
|
+
name="mage:database"
|
|
18
|
+
size="1.2em"
|
|
19
|
+
/>
|
|
20
|
+
<span>{{ option.displayName }}</span>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
</MultiSelect>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { getDatabases } from '@core/sdk/client'
|
|
29
|
+
import { useChangeCase } from '@vueuse/integrations/useChangeCase'
|
|
30
|
+
|
|
31
|
+
import type { DatabaseDto } from '@core/sdk/client'
|
|
32
|
+
|
|
33
|
+
interface DatabaseOption {
|
|
34
|
+
name: string
|
|
35
|
+
displayName: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface KnowledgeDatabaseSelectorProps {
|
|
39
|
+
context: {
|
|
40
|
+
node: { input: (value: string[] | null) => void }
|
|
41
|
+
value?: string[] | null
|
|
42
|
+
placeholder?: string
|
|
43
|
+
filter?: boolean
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const props = defineProps<KnowledgeDatabaseSelectorProps>()
|
|
48
|
+
const { t } = useI18n()
|
|
49
|
+
const { tenantId } = useTenant()
|
|
50
|
+
|
|
51
|
+
const placeholder = computed(() => props.context.placeholder)
|
|
52
|
+
const filter = computed(() => props.context.filter ?? true)
|
|
53
|
+
|
|
54
|
+
const databases = ref<DatabaseDto[]>([])
|
|
55
|
+
const isLoading = ref(false)
|
|
56
|
+
|
|
57
|
+
const selectedDatabases = computed({
|
|
58
|
+
get: () => props.context.value ?? [],
|
|
59
|
+
set: (value: string[]) => {
|
|
60
|
+
props.context.node.input(value.length > 0 ? value : null)
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const databaseOptions = computed<DatabaseOption[]>(() =>
|
|
65
|
+
databases.value.map(db => ({
|
|
66
|
+
name: db.name,
|
|
67
|
+
displayName: db.display_name || useChangeCase(db.name, 'capitalCase').value,
|
|
68
|
+
})),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
async function fetchDatabases() {
|
|
72
|
+
isLoading.value = true
|
|
73
|
+
try {
|
|
74
|
+
const response = await getDatabases({
|
|
75
|
+
composable: '$fetch',
|
|
76
|
+
path: { tenant_id: tenantId.value! },
|
|
77
|
+
})
|
|
78
|
+
databases.value = response
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('Failed to fetch databases:', error)
|
|
82
|
+
databases.value = []
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
isLoading.value = false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
onMounted(() => {
|
|
90
|
+
fetchDatabases()
|
|
91
|
+
})
|
|
92
|
+
</script>
|