@swiss-ai-hub/web 0.305.8 → 0.306.1

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.
@@ -9,6 +9,9 @@ export const useAgentClasses = defineQuery((options?: { online?: boolean }) => {
9
9
  const { data: agentClasses, isPending: agentClassesAreLoading } = useQuery<AgentClassDto[]>({
10
10
  key: () => ['tenant', tenantId.value, 'agent-classes', options?.online],
11
11
  staleTime: minutesToMilliseconds(5),
12
+ // A focus refetch replaces the classes array with new object references, which would
13
+ // reset an in-progress create form (issue #38). Keep the cached value until invalidated.
14
+ refetchOnWindowFocus: false,
12
15
  enabled: useTenantReady(),
13
16
  query: async () => {
14
17
  return await getAgentClasses({
@@ -9,6 +9,9 @@ export const useAgentInstance = defineQuery(() => {
9
9
  const { data: agentInstance, isPending: agentInstanceIsLoading } = useQuery<FullAgentInstanceDto>({
10
10
  key: () => ['tenant', tenantId.value, 'agent-instances', route.params.agent_class as string, route.params.agent_id as string],
11
11
  staleTime: minutesToMilliseconds(5),
12
+ // A focus refetch after leaving the tab would flip the loading/enabled state and remount
13
+ // the configuration form, discarding unsaved edits (issue #38). Keep the cached value.
14
+ refetchOnWindowFocus: false,
12
15
  enabled: useTenantReady('agent_id', 'agent_class'),
13
16
  query: async () => {
14
17
  return await getAgentInstance({
@@ -94,22 +94,30 @@ export function useCreateInstanceForm<T extends ClassDataLike>(options: CreateIn
94
94
  setNestedValue(formData.value, path, value)
95
95
  }
96
96
 
97
- watch(selectedClassData, (newClass) => {
98
- formData.value = newClass?.form && newClass.form.length > 0
97
+ // Reseed only when the chosen class changes; a refetch hands back a new reference for the
98
+ // same class, and reseeding on that churn would wipe the user's input (issue #38).
99
+ let seededForClass: string | null = null
100
+ watch([selectedClass, selectedClassData], () => {
101
+ if (seededForClass === selectedClass.value) return
102
+ if (!selectedClassData.value) return
103
+ formData.value = selectedClassData.value.form && selectedClassData.value.form.length > 0
99
104
  ? hydrateFormData({}, configForm.value as FormElement[])
100
105
  : {}
106
+ seededForClass = selectedClass.value
101
107
  }, { immediate: true })
102
108
 
103
109
  function applyInitialData(data: Record<string, unknown>) {
104
110
  // Hydrate from the template/clone data: nullable toggles follow the data's null-ness,
105
111
  // missing leaves fall back to their backend defaults — identical to the edit form.
106
112
  formData.value = hydrateFormData(data, configForm.value as FormElement[])
113
+ seededForClass = selectedClass.value
107
114
  }
108
115
 
109
116
  function resetForm() {
110
117
  selectedClass.value = initialClass()
111
118
  formData.value = {}
112
119
  activeStep.value = 0
120
+ seededForClass = null
113
121
  }
114
122
 
115
123
  return {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "bbv Software Services AG (https://www.bbv.ch)",
5
5
  "type": "module",
6
- "version": "0.305.8",
6
+ "version": "0.306.1",
7
7
  "description": "Swiss AI Hub - Admin & Management UI (Nuxt 3 layer)",
8
8
  "main": "./nuxt.config.ts",
9
9
  "repository": {
@@ -2,7 +2,7 @@
2
2
  <StructuralColumn
3
3
  :title="t('agent.configuration.title')"
4
4
  close-route="/service/agents"
5
- :loading="agentInstanceIsLoading"
5
+ :loading="agentInstanceIsLoading && !hasLoaded"
6
6
  size="normal"
7
7
  >
8
8
  <div class="flex flex-col gap-3">
@@ -10,7 +10,7 @@
10
10
  {{ t('agent.configuration.description') }}
11
11
  </p>
12
12
  <AgentConfiguration
13
- v-if="configForm && configForm.length > 0 && !agentInstanceIsLoading"
13
+ v-if="hasLoaded && configForm && configForm.length > 0"
14
14
  :title="t('agent.configuration.runtimeSettings')"
15
15
  :description="agentInstance?.agent_config.description || ''"
16
16
  :form="configForm"
@@ -18,7 +18,7 @@
18
18
  @submit="submitConfiguration"
19
19
  />
20
20
  <div
21
- v-else-if="agentInstanceIsLoading"
21
+ v-else-if="!hasLoaded"
22
22
  class="text-center text-sm text-surface-500 dark:text-surface-400"
23
23
  >
24
24
  {{ t('common.loading') }}
@@ -43,6 +43,20 @@ const { updateAgentInstance } = useUpdateAgentInstance()
43
43
  const { t } = useI18n()
44
44
  const toast = useToast()
45
45
 
46
+ // Latch so a background refetch or transient `enabled` flip can't remount the form and
47
+ // re-seed it from server data, dropping unsaved edits (issue #38).
48
+ const hasLoaded = ref(false)
49
+ watch(agentInstance, (value) => {
50
+ if (value) hasLoaded.value = true
51
+ }, { immediate: true })
52
+
53
+ // Nuxt reuses this instance across param changes; drop the latch on route identity change so
54
+ // the loading state shows until the new agent resolves, instead of the previous form lingering.
55
+ watch(
56
+ () => `${route.params.agent_class}/${route.params.agent_id}`,
57
+ () => { hasLoaded.value = false },
58
+ )
59
+
46
60
  // Lock agent_id on edit: it is the immutable instance key, and a divergent value silently breaks
47
61
  // the SSE completion check so the chat never finishes. The backend pins it on save too; this is UX.
48
62
  const configForm = computed<FormkitElement[]>(() =>