@quazardous/qdadm 2.7.0 → 2.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quazardous/qdadm",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -31,6 +31,8 @@ import { version as qdadmVersion } from '../../../package.json'
31
31
  interface FeaturesConfig {
32
32
  poweredBy?: boolean
33
33
  breadcrumb?: boolean
34
+ /** Opt-in (#1332/#1341): View↔Edit toggle next to the terminal crumb */
35
+ breadcrumbModeToggle?: boolean
34
36
  [key: string]: unknown
35
37
  }
36
38
 
@@ -239,7 +241,7 @@ watch(() => route.fullPath, () => {
239
241
 
240
242
  // Navigation context (breadcrumb + navlinks from route config)
241
243
  // Entity data comes from activeStack (populated by useEntityItemPage/useEntityItemFormPage)
242
- const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks } = useNavContext()
244
+ const { breadcrumb: defaultBreadcrumb, navlinks: defaultNavlinks, modeToggle } = useNavContext()
243
245
 
244
246
  // Allow child pages to override breadcrumb/navlinks via provide/inject
245
247
  const breadcrumbOverride = ref<BreadcrumbItem[] | null>(null)
@@ -258,6 +260,12 @@ const showBreadcrumb = computed<boolean>(() => {
258
260
  if (route.name === 'dashboard') return false
259
261
  return true
260
262
  })
263
+
264
+ // View↔Edit toggle on the terminal crumb (#1332/#1341) — same opt-in contract
265
+ // as DefaultBreadcrumb; this inline breadcrumb must honor the flag too
266
+ const shownModeToggle = computed(() =>
267
+ features.breadcrumbModeToggle === true ? modeToggle.value : null
268
+ )
261
269
  </script>
262
270
 
263
271
  <template>
@@ -382,6 +390,17 @@ const showBreadcrumb = computed<boolean>(() => {
382
390
  </template>
383
391
  </Breadcrumb>
384
392
 
393
+ <!-- View↔Edit mode toggle (#1332/#1341) — plain navigation; the form
394
+ page's own route-leave guard covers unsaved changes on edit→show -->
395
+ <RouterLink
396
+ v-if="shownModeToggle"
397
+ :to="shownModeToggle.to"
398
+ class="breadcrumb-mode-toggle"
399
+ >
400
+ <i :class="shownModeToggle.target === 'edit' ? 'pi pi-pencil' : 'pi pi-eye'"></i>
401
+ <span>{{ shownModeToggle.label }}</span>
402
+ </RouterLink>
403
+
385
404
  <!-- Navlinks (provided by PageNav for child routes) -->
386
405
  <div v-if="navlinks.length > 0" class="layout-navlinks">
387
406
  <template v-for="(link, index) in navlinks" :key="link.to?.name || index">
@@ -702,6 +721,24 @@ const showBreadcrumb = computed<boolean>(() => {
702
721
  padding-bottom: 0;
703
722
  }
704
723
 
724
+ /* Mode toggle sits right after the crumbs; auto margin keeps navlinks pinned right */
725
+ .breadcrumb-mode-toggle {
726
+ display: inline-flex;
727
+ align-items: center;
728
+ gap: 0.35rem;
729
+ margin-left: 0.75rem;
730
+ margin-right: auto;
731
+ font-size: 0.875rem;
732
+ color: var(--p-primary-500, #3b82f6);
733
+ text-decoration: none;
734
+ transition: color var(--fad-transition-fast);
735
+ }
736
+
737
+ .breadcrumb-mode-toggle:hover {
738
+ color: var(--p-primary-600, #2563eb);
739
+ text-decoration: underline;
740
+ }
741
+
705
742
  .layout-navlinks {
706
743
  display: flex;
707
744
  align-items: center;
@@ -39,7 +39,8 @@ export interface ShowFieldDefinition {
39
39
  type?: string
40
40
  schemaType?: string
41
41
  label?: string
42
- reference?: string
42
+ /** Canonical FieldConfig object form, or the entity name as a bare string */
43
+ reference?: string | { entity: string; labelField?: string; valueField?: string }
43
44
  referenceRoute?: string | ((value: unknown) => { name: string; params: Record<string, unknown> })
44
45
  referenceLabel?: string | ((value: unknown, option?: unknown) => string)
45
46
  options?: unknown[]
@@ -73,6 +74,8 @@ export interface ShowResolvedFieldConfig extends ShowFieldDefinition {
73
74
  */
74
75
  export interface ShowResolverOrchestrator {
75
76
  get: (entityName: string) => ShowResolverManager | undefined
77
+ /** Real Orchestrator.get THROWS on unknown entities — probe first when available */
78
+ has?: (entityName: string) => boolean
76
79
  }
77
80
 
78
81
  /**
@@ -133,13 +136,20 @@ export function createShowFieldResolver(
133
136
  let resolvedDisplayType = SHOW_TYPE_MAPPINGS[type] || SHOW_TYPE_MAPPINGS[schemaType] || 'text'
134
137
  const resolvedLabel = label || snakeCaseToTitle(name)
135
138
 
136
- // Auto-set reference route if reference entity is specified
139
+ // Auto-set reference route if reference entity is specified.
140
+ // FieldConfig.reference is canonically { entity, ... }; the bare-string
141
+ // form stays accepted. Passing the object to orchestrator.get produced
142
+ // '[object Object]' lookups that THREW during generateFields (#1341).
137
143
  let referenceRoute = rest.referenceRoute
138
144
  if (reference && !referenceRoute) {
139
- const refManager = orchestrator.get(reference)
145
+ const refEntity = typeof reference === 'string' ? reference : reference.entity
146
+ const refManager =
147
+ refEntity && (!orchestrator.has || orchestrator.has(refEntity))
148
+ ? orchestrator.get(refEntity)
149
+ : undefined
140
150
  if (refManager) {
141
151
  referenceRoute = (value: unknown) => ({
142
- name: `${refManager.routePrefix || reference}-show`,
152
+ name: `${refManager.routePrefix || refEntity}-show`,
143
153
  params: { [refManager.idField]: value },
144
154
  })
145
155
  }