qdadm 0.50.0 → 0.51.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdadm",
3
- "version": "0.50.0",
3
+ "version": "0.51.4",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -87,6 +87,7 @@ import { useBreadcrumb } from './useBreadcrumb'
87
87
  import { useEntityItemPage } from './useEntityItemPage'
88
88
  import { registerGuardDialog, unregisterGuardDialog } from './useGuardStore'
89
89
  import { deepClone } from '../utils/transformers'
90
+ import { getSiblingRoutes } from '../module/moduleRegistry'
90
91
 
91
92
  export function useEntityItemFormPage(config = {}) {
92
93
  const {
@@ -98,7 +99,7 @@ export function useEntityItemFormPage(config = {}) {
98
99
  // Form options
99
100
  loadOnMount = true,
100
101
  enableGuard = true,
101
- redirectOnCreate = true, // Redirect to edit mode after create
102
+ // redirectOnCreate removed: "Create" now resets form, "Create & Close" navigates
102
103
  usePatch = false, // Use PATCH instead of PUT for updates
103
104
  // Hooks for custom behavior
104
105
  transformLoad = (data) => data,
@@ -109,7 +110,9 @@ export function useEntityItemFormPage(config = {}) {
109
110
  // Validation options
110
111
  validateOnBlur = true, // Validate field on blur
111
112
  validateOnSubmit = true, // Validate all fields before submit
112
- showErrorSummary = false // Show error summary at top of form
113
+ showErrorSummary = false, // Show error summary at top of form
114
+ // Field generation
115
+ generateFormFields = true // Auto-generate fields from manager schema
113
116
  } = config
114
117
 
115
118
  const router = useRouter()
@@ -316,12 +319,24 @@ export function useEntityItemFormPage(config = {}) {
316
319
  }
317
320
 
318
321
  if (andClose) {
319
- router.push({ name: routePrefix })
320
- } else if (!isEdit.value && redirectOnCreate) {
321
- // Redirect to edit mode after create
322
- const newId = responseData[manager.idField] || responseData.id || responseData.key
323
- router.replace({ name: `${routePrefix}-${editRouteSuffix}`, params: { id: newId } })
322
+ // Navigate to list route (or previous page)
323
+ const listRoute = findListRoute()
324
+ router.push(listRoute)
325
+ } else if (!isEdit.value) {
326
+ // "Create" without close: reset form for new entry, stay on route
327
+ data.value = deepClone(initialData)
328
+ originalData.value = null
329
+ takeSnapshot()
330
+ errors.value = {}
331
+ submitted.value = false
332
+ toast.add({
333
+ severity: 'info',
334
+ summary: 'Ready',
335
+ detail: 'Form reset for new entry',
336
+ life: 2000
337
+ })
324
338
  }
339
+ // Edit mode without close: just stay on page (data already updated)
325
340
 
326
341
  return responseData
327
342
  } catch (error) {
@@ -393,12 +408,59 @@ export function useEntityItemFormPage(config = {}) {
393
408
 
394
409
  // ============ NAVIGATION ============
395
410
 
411
+ /**
412
+ * Find the list route for redirects after save/cancel
413
+ * For child entities: finds sibling list route with parent params
414
+ * For top-level entities: uses routePrefix
415
+ *
416
+ * When parent has multiple child entity types (e.g., bots → commands AND bots → logs),
417
+ * we find the list route matching the current entity's route prefix.
418
+ */
419
+ function findListRoute() {
420
+ // If has parent config, find sibling list route
421
+ if (parentConfig.value) {
422
+ const { entity: parentEntityName, param } = parentConfig.value
423
+ const siblings = getSiblingRoutes(parentEntityName, param)
424
+
425
+ // Extract base route name from current route (e.g., 'bot-commands-create' → 'bot-commands')
426
+ const currentRouteName = route.name || ''
427
+ const baseRouteName = currentRouteName.replace(/-(create|edit|new)$/, '')
428
+
429
+ // Find list routes among siblings (exclude create/edit/new routes)
430
+ const listRoutes = siblings.filter(r => {
431
+ const name = r.name || ''
432
+ const path = r.path || ''
433
+ return !name.match(/-(create|edit|new)$/) && !path.match(/\/(create|edit|new)$/)
434
+ })
435
+
436
+ // Prefer route matching current entity's base name
437
+ let listRoute = listRoutes.find(r => r.name === baseRouteName)
438
+
439
+ // Fallback: try route containing routePrefix (e.g., 'bot-commands' contains 'command')
440
+ if (!listRoute && routePrefix) {
441
+ listRoute = listRoutes.find(r => r.name?.includes(routePrefix))
442
+ }
443
+
444
+ // Last fallback: first list route
445
+ if (!listRoute && listRoutes.length > 0) {
446
+ listRoute = listRoutes[0]
447
+ }
448
+
449
+ if (listRoute?.name) {
450
+ return { name: listRoute.name, params: route.params }
451
+ }
452
+ }
453
+
454
+ // Default: top-level entity list
455
+ return { name: routePrefix }
456
+ }
457
+
396
458
  function cancel() {
397
- router.push({ name: routePrefix })
459
+ router.push(findListRoute())
398
460
  }
399
461
 
400
462
  function goToList() {
401
- router.push({ name: routePrefix })
463
+ router.push(findListRoute())
402
464
  }
403
465
 
404
466
  // ============ FIELDS ============
@@ -598,11 +660,41 @@ export function useEntityItemFormPage(config = {}) {
598
660
  } else if (currentIndex === -1) {
599
661
  // New field, add at end
600
662
  fieldOrder.value.push(name)
663
+ } else {
664
+ // Existing field without repositioning, restore at original position
665
+ fieldOrder.value.splice(currentIndex, 0, name)
601
666
  }
602
667
 
603
668
  return builderApi
604
669
  }
605
670
 
671
+ /**
672
+ * Update an existing field configuration
673
+ *
674
+ * Use this to modify properties of auto-generated fields.
675
+ * Throws error if field doesn't exist (use addField for new fields).
676
+ *
677
+ * @param {string} name - Field name to update
678
+ * @param {object} fieldConfig - Properties to merge with existing config
679
+ * @returns {object} - The builder instance for chaining
680
+ *
681
+ * @example
682
+ * form.updateField('book_id', { disabled: true })
683
+ * form.updateField('email', { validate: v => v.includes('@') || 'Invalid' })
684
+ */
685
+ function updateField(name, fieldConfig) {
686
+ if (!fieldsMap.value.has(name)) {
687
+ throw new Error(`Field '${name}' does not exist. Use addField() to create new fields.`)
688
+ }
689
+
690
+ // Merge with existing config (keeps position)
691
+ const existingConfig = fieldsMap.value.get(name)
692
+ const mergedConfig = { ...existingConfig, ...fieldConfig }
693
+ fieldsMap.value.set(name, mergedConfig)
694
+
695
+ return builderApi
696
+ }
697
+
606
698
  /**
607
699
  * Exclude a field from generation
608
700
  * Call before generateFields() or use exclude option
@@ -1123,6 +1215,7 @@ export function useEntityItemFormPage(config = {}) {
1123
1215
  confirmDelete,
1124
1216
  reset,
1125
1217
  goToList,
1218
+ findListRoute,
1126
1219
 
1127
1220
  // Dirty tracking
1128
1221
  takeSnapshot,
@@ -1134,6 +1227,7 @@ export function useEntityItemFormPage(config = {}) {
1134
1227
  generateFields,
1135
1228
  resolveReferences,
1136
1229
  addField,
1230
+ updateField,
1137
1231
  removeField,
1138
1232
  excludeField,
1139
1233
  getFieldConfig,
@@ -1188,5 +1282,15 @@ export function useEntityItemFormPage(config = {}) {
1188
1282
  events: formEvents
1189
1283
  }
1190
1284
 
1285
+ // Auto-generate fields from manager schema if enabled
1286
+ if (generateFormFields) {
1287
+ generateFields()
1288
+ }
1289
+
1290
+ // Auto-disable parent foreignKey field (it's auto-filled from route)
1291
+ if (parentConfig.value?.foreignKey && fieldsMap.value.has(parentConfig.value.foreignKey)) {
1292
+ updateField(parentConfig.value.foreignKey, { disabled: true })
1293
+ }
1294
+
1191
1295
  return builderApi
1192
1296
  }