@edgedev/create-edge-app 1.1.29 → 1.2.30

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.
Files changed (32) hide show
  1. package/agents.md +2 -0
  2. package/bin/cli.js +13 -5
  3. package/deploy-services.sh +237 -0
  4. package/deploy.sh +88 -1
  5. package/edge/components/cms/blockEditor.vue +748 -7
  6. package/{pages/app/dashboard/blocks/index.vue → edge/components/cms/blocksManager.vue} +9 -24
  7. package/edge/components/cms/menu.vue +100 -15
  8. package/edge/components/cms/site.vue +83 -3
  9. package/edge/components/cms/sitesManager.vue +110 -0
  10. package/edge/components/cms/themeEditor.vue +9 -3
  11. package/edge/components/dashboard.vue +22 -3
  12. package/edge/components/editor.vue +100 -35
  13. package/edge/components/organizationMembers.vue +294 -221
  14. package/edge/components/shad/combobox.vue +2 -2
  15. package/edge/composables/global.ts +1 -1
  16. package/edge/routes/cms/dashboard/blocks/index.vue +21 -0
  17. package/edge/routes/cms/dashboard/sites/index.vue +13 -0
  18. package/edge/routes/cms/nuxtHooks.js +52 -0
  19. package/edge/routes/cms/routes.js +56 -0
  20. package/firebase_init.sh +63 -2
  21. package/nuxt.config.ts +19 -2
  22. package/package.json +1 -1
  23. package/services/.deploy.shared.env.example +12 -0
  24. package/pages/app/dashboard/sites/index.vue +0 -114
  25. /package/{pages/app → edge/routes/cms}/dashboard/blocks/[block].vue +0 -0
  26. /package/{pages/app → edge/routes/cms}/dashboard/media/index.vue +0 -0
  27. /package/{pages/app → edge/routes/cms}/dashboard/sites/[site]/[[page]].vue +0 -0
  28. /package/{pages/app → edge/routes/cms}/dashboard/sites/[site].vue +0 -0
  29. /package/{pages/app → edge/routes/cms}/dashboard/templates/[page].vue +0 -0
  30. /package/{pages/app/dashboard/templates.vue → edge/routes/cms/dashboard/templates/index.vue} +0 -0
  31. /package/{pages/app → edge/routes/cms}/dashboard/themes/[theme].vue +0 -0
  32. /package/{pages/app → edge/routes/cms}/dashboard/themes/index.vue +0 -0
@@ -81,6 +81,7 @@ const route = useRoute()
81
81
  const edgeFirebase = inject('edgeFirebase')
82
82
  const state = reactive({
83
83
  filter: '',
84
+ roleFilter: 'all',
84
85
  workingItem: {},
85
86
  dialog: false,
86
87
  form: false,
@@ -190,6 +191,42 @@ const userKey = (user) => {
190
191
  return user?.docId || user?.userId || user?.id || user?.uid || ''
191
192
  }
192
193
 
194
+ const userRoleName = (user) => {
195
+ return String(edgeGlobal.getRoleName(user?.roles, edgeGlobal.edgeState.currentOrganization) || '').trim()
196
+ }
197
+
198
+ const selectedRole = computed(() => {
199
+ if (state.roleFilter === 'all' || state.roleFilter === 'no-role')
200
+ return ''
201
+ return String(state.roleFilter || '').trim()
202
+ })
203
+
204
+ const roleFilterOptions = computed(() => {
205
+ const allRoleNames = Array.from(new Set([
206
+ ...roleNamesOnly.value,
207
+ ...users.value.map(user => userRoleName(user)),
208
+ ]
209
+ .map(name => String(name || '').trim())
210
+ .filter(Boolean)))
211
+ .sort((a, b) => a.localeCompare(b))
212
+
213
+ return [
214
+ { name: 'All Roles', docId: 'all' },
215
+ { name: 'No Role', docId: 'no-role' },
216
+ ...allRoleNames.map(role => ({ name: role, docId: role })),
217
+ ]
218
+ })
219
+
220
+ const usersByRoleFilter = computed(() => {
221
+ if (state.roleFilter === 'all')
222
+ return users.value
223
+ if (state.roleFilter === 'no-role')
224
+ return users.value.filter(user => !userRoleName(user))
225
+ if (!selectedRole.value)
226
+ return users.value
227
+ return users.value.filter(user => userRoleName(user) === selectedRole.value)
228
+ })
229
+
193
230
  const PROFILE_IMAGE_SIZE = 96
194
231
  const PROFILE_IMAGE_VARIANT = `width=${PROFILE_IMAGE_SIZE},height=${PROFILE_IMAGE_SIZE},fit=cover,quality=85`
195
232
 
@@ -247,10 +284,11 @@ const sortedFilteredUsers = computed(() => {
247
284
  return parts[parts.length - 1] || ''
248
285
  }
249
286
 
250
- return users.value
287
+ return usersByRoleFilter.value
251
288
  .filter((user) => {
252
- const name = String(user?.meta?.name || '')
253
- return name.toLowerCase().includes(filter)
289
+ const name = String(user?.meta?.name || '').toLowerCase()
290
+ const email = String(user?.meta?.email || '').toLowerCase()
291
+ return name.includes(filter) || email.includes(filter)
254
292
  })
255
293
  .sort((a, b) => {
256
294
  const lastA = getLastName(a?.meta?.name).toLowerCase()
@@ -259,6 +297,12 @@ const sortedFilteredUsers = computed(() => {
259
297
  })
260
298
  })
261
299
 
300
+ const detailViewKey = computed(() => {
301
+ if (!state.dialog)
302
+ return 'member-empty'
303
+ return `member-${userKey(state.workingItem) || state.workingItem?.id || 'new'}-${state.saveButton}`
304
+ })
305
+
262
306
  const addItem = () => {
263
307
  state.saveButton = 'Invite User'
264
308
  const newItem = edgeGlobal.dupObject(state.newItem)
@@ -517,11 +561,28 @@ onBeforeMount(async () => {
517
561
  Invite
518
562
  </edge-shad-button>
519
563
  </div>
520
- <Input
521
- v-model="state.filter"
522
- class="mt-3 h-8 w-full"
523
- placeholder="Filter members..."
524
- />
564
+ <div class="mt-3 flex items-center gap-2">
565
+ <div class="w-1/2 min-w-0">
566
+ <edge-shad-combobox
567
+ v-model="state.roleFilter"
568
+ :items="roleFilterOptions"
569
+ name="roleFilter"
570
+ item-title="name"
571
+ item-value="docId"
572
+ placeholder="Select role"
573
+ class="w-full !h-8"
574
+ />
575
+ </div>
576
+ <div class="w-1/2 min-w-0">
577
+ <edge-shad-input
578
+ v-model="state.filter"
579
+ label=""
580
+ name="filter"
581
+ class="h-8 w-full"
582
+ placeholder="Filter members..."
583
+ />
584
+ </div>
585
+ </div>
525
586
  </div>
526
587
  <div class="flex-1 overflow-y-auto">
527
588
  <SidebarMenu class="px-2 py-2 space-y-0">
@@ -587,248 +648,250 @@ onBeforeMount(async () => {
587
648
  </ResizablePanel>
588
649
  <ResizablePanel class="bg-background">
589
650
  <div class="h-full flex flex-col">
590
- <div v-if="state.dialog" class="h-full flex flex-col">
591
- <edge-shad-form
592
- :key="userKey(state.workingItem) || state.workingItem?.id || 'member-form'"
593
- :initial-values="state.workingItem"
594
- :schema="computedUserSchema"
595
- class="flex flex-col h-full"
596
- @submit="onSubmit"
597
- >
598
- <div class="flex items-center justify-between border-b bg-secondary px-4 py-3">
599
- <div class="text-sm font-semibold">
600
- {{ state.currentTitle || 'Member' }}
601
- </div>
602
- <div class="flex items-center gap-2">
603
- <edge-shad-button variant="text" class="text-xs text-red-700" @click="closeDialog">
604
- Close
605
- </edge-shad-button>
606
- <edge-shad-button
607
- type="submit"
608
- class="text-xs bg-primary"
609
- :disabled="state.loading"
610
- >
611
- <Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
612
- {{ state.saveButton }}
613
- </edge-shad-button>
651
+ <Transition name="fade" mode="out-in">
652
+ <div v-if="state.dialog" :key="detailViewKey" class="h-full flex flex-col">
653
+ <edge-shad-form
654
+ :key="userKey(state.workingItem) || state.workingItem?.id || 'member-form'"
655
+ :initial-values="state.workingItem"
656
+ :schema="computedUserSchema"
657
+ class="flex flex-col h-full"
658
+ @submit="onSubmit"
659
+ >
660
+ <div class="flex items-center justify-between border-b bg-secondary px-4 py-3">
661
+ <div class="text-sm font-semibold">
662
+ {{ state.currentTitle || 'Member' }}
663
+ </div>
664
+ <div class="flex items-center gap-2">
665
+ <edge-shad-button variant="text" class="text-xs text-red-700" @click="closeDialog">
666
+ Close
667
+ </edge-shad-button>
668
+ <edge-shad-button
669
+ type="submit"
670
+ class="text-xs bg-primary"
671
+ :disabled="state.loading"
672
+ >
673
+ <Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
674
+ {{ state.saveButton }}
675
+ </edge-shad-button>
676
+ </div>
614
677
  </div>
615
- </div>
616
- <div class="flex-1 overflow-y-auto p-6 space-y-4">
617
- <slot name="edit-fields" :working-item="state.workingItem">
618
- <div class="rounded-xl border bg-card p-4 space-y-4 shadow-sm">
619
- <div class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
620
- Member Details
621
- </div>
622
- <div class="flex flex-col gap-4 md:flex-row md:items-stretch">
623
- <div class="w-full md:w-[260px] self-stretch">
624
- <edge-image-picker
625
- v-model="state.workingItem.meta.profilephoto"
626
- label="Profile Photo"
627
- dialog-title="Select Profile Photo"
628
- site="clearwater-hub-images"
629
- :default-tags="props.defaultImageTags"
630
- height-class="h-full min-h-[180px]"
631
- :include-cms-all="false"
632
- />
678
+ <div class="flex-1 overflow-y-auto p-6 space-y-4">
679
+ <slot name="edit-fields" :working-item="state.workingItem">
680
+ <div class="rounded-xl border bg-card p-4 space-y-4 shadow-sm">
681
+ <div class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
682
+ Member Details
633
683
  </div>
634
- <div class="flex-1 space-y-4">
635
- <edge-g-input
636
- v-model="state.workingItem.role"
637
- name="role"
638
- :disable-tracking="true"
639
- :items="roleNamesOnly"
640
- field-type="select"
641
- label="Role"
642
- :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
643
- :disabled="state.workingItem.userId === edgeFirebase.user.uid"
644
- />
645
- <edge-g-input
646
- v-model="state.workingItem.meta.name"
647
- name="meta.name"
648
- :disable-tracking="true"
649
- field-type="text"
650
- label="Name"
651
- :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
652
- />
653
- <edge-g-input
654
- v-model="state.workingItem.meta.email"
655
- name="meta.email"
656
- :disable-tracking="true"
657
- field-type="text"
658
- label="Email"
659
- :disabled="state.saveButton !== 'Invite User'"
660
- :hint="state.saveButton !== 'Invite User' ? emailDisabledHint : ''"
661
- :persistent-hint="state.saveButton !== 'Invite User'"
662
- :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
663
- />
664
- <edge-g-input
665
- v-model="state.workingItem.meta.phone"
666
- name="meta.phone"
667
- :disable-tracking="true"
668
- field-type="text"
669
- label="Phone"
670
- :mask-options="{ mask: '(###) ###-####' }"
671
- :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
672
- />
684
+ <div class="flex flex-col gap-4 md:flex-row md:items-stretch">
685
+ <div class="w-full md:w-[260px] self-stretch">
686
+ <edge-image-picker
687
+ v-model="state.workingItem.meta.profilephoto"
688
+ label="Profile Photo"
689
+ dialog-title="Select Profile Photo"
690
+ site="clearwater-hub-images"
691
+ :default-tags="props.defaultImageTags"
692
+ height-class="h-full min-h-[180px]"
693
+ :include-cms-all="false"
694
+ />
695
+ </div>
696
+ <div class="flex-1 space-y-4">
697
+ <edge-g-input
698
+ v-model="state.workingItem.role"
699
+ name="role"
700
+ :disable-tracking="true"
701
+ :items="roleNamesOnly"
702
+ field-type="select"
703
+ label="Role"
704
+ :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
705
+ :disabled="state.workingItem.userId === edgeFirebase.user.uid"
706
+ />
707
+ <edge-g-input
708
+ v-model="state.workingItem.meta.name"
709
+ name="meta.name"
710
+ :disable-tracking="true"
711
+ field-type="text"
712
+ label="Name"
713
+ :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
714
+ />
715
+ <edge-g-input
716
+ v-model="state.workingItem.meta.email"
717
+ name="meta.email"
718
+ :disable-tracking="true"
719
+ field-type="text"
720
+ label="Email"
721
+ :disabled="state.saveButton !== 'Invite User'"
722
+ :hint="state.saveButton !== 'Invite User' ? emailDisabledHint : ''"
723
+ :persistent-hint="state.saveButton !== 'Invite User'"
724
+ :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
725
+ />
726
+ <edge-g-input
727
+ v-model="state.workingItem.meta.phone"
728
+ name="meta.phone"
729
+ :disable-tracking="true"
730
+ field-type="text"
731
+ label="Phone"
732
+ :mask-options="{ mask: '(###) ###-####' }"
733
+ :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
734
+ />
735
+ </div>
673
736
  </div>
674
737
  </div>
675
- </div>
676
- <div v-if="state.saveButton !== 'Invite User' && showEditOrgSelect" class="mt-4 w-full">
677
- <div class="text-sm font-medium text-foreground">
678
- Organizations
679
- </div>
680
- <div class="mt-2 w-full flex flex-wrap gap-2">
681
- <div v-for="org in editOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
682
- <edge-shad-checkbox
683
- :name="`edit-add-org-${org.docId}`"
684
- :model-value="state.editOrgIds.includes(org.docId)"
685
- @update:model-value="val => updateEditOrgSelection(org.docId, val)"
686
- >
687
- {{ org.name }}
688
- </edge-shad-checkbox>
738
+ <div v-if="state.saveButton !== 'Invite User' && showEditOrgSelect" class="mt-4 w-full">
739
+ <div class="text-sm font-medium text-foreground">
740
+ Organizations
741
+ </div>
742
+ <div class="mt-2 w-full flex flex-wrap gap-2">
743
+ <div v-for="org in editOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
744
+ <edge-shad-checkbox
745
+ :name="`edit-add-org-${org.docId}`"
746
+ :model-value="state.editOrgIds.includes(org.docId)"
747
+ @update:model-value="val => updateEditOrgSelection(org.docId, val)"
748
+ >
749
+ {{ org.name }}
750
+ </edge-shad-checkbox>
751
+ </div>
689
752
  </div>
690
753
  </div>
691
- </div>
692
- <div v-if="state.saveButton === 'Invite User' && showInviteOrgSelect" class="mt-4 w-full">
693
- <div class="text-sm font-medium text-foreground">
694
- Add to organizations
695
- </div>
696
- <div class="mt-2 w-full flex flex-wrap gap-2">
697
- <div v-for="org in inviteOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
698
- <edge-shad-checkbox
699
- :name="`invite-org-${org.docId}`"
700
- :model-value="state.inviteOrgIds.includes(org.docId)"
701
- @update:model-value="val => updateInviteOrgSelection(org.docId, val)"
702
- >
703
- {{ org.name }}
704
- </edge-shad-checkbox>
754
+ <div v-if="state.saveButton === 'Invite User' && showInviteOrgSelect" class="mt-4 w-full">
755
+ <div class="text-sm font-medium text-foreground">
756
+ Add to organizations
757
+ </div>
758
+ <div class="mt-2 w-full flex flex-wrap gap-2">
759
+ <div v-for="org in inviteOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
760
+ <edge-shad-checkbox
761
+ :name="`invite-org-${org.docId}`"
762
+ :model-value="state.inviteOrgIds.includes(org.docId)"
763
+ @update:model-value="val => updateInviteOrgSelection(org.docId, val)"
764
+ >
765
+ {{ org.name }}
766
+ </edge-shad-checkbox>
767
+ </div>
705
768
  </div>
706
769
  </div>
707
- </div>
708
- <Separator class="my-6" />
709
- <div class="grid grid-cols-12 gap-2">
710
- <div v-for="field in props.metaFields" :key="field.field" class="mb-3 col-span-12" :class="numColsToTailwind(field.cols)">
711
- <!-- Use explicit model binding so dotted paths (e.g., "address.street") work -->
712
- <edge-image-picker
713
- v-if="field?.type === 'imagePicker'"
714
- :model-value="getByPath(state.workingItem.meta, field.field, '')"
715
- :label="field?.label || 'Photo'"
716
- :dialog-title="field?.dialogTitle || 'Select Image'"
717
- :site="field?.site || 'clearwater-hub-images'"
718
- :default-tags="field?.tags || []"
719
- :height-class="field?.heightClass || 'h-[160px]'"
720
- :disabled="field?.disabled || false"
721
- :include-cms-all="false"
722
- @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
723
- />
724
- <p v-if="field?.type === 'imagePicker' && field?.disabled" class="mt-1 text-xs text-muted-foreground">
725
- {{ getDisabledNote(field) }}
726
- </p>
727
- <div v-else-if="field?.type === 'richText'" class="member-richtext">
728
- <edge-shad-html
770
+ <Separator class="my-6" />
771
+ <div class="grid grid-cols-12 gap-2">
772
+ <div v-for="field in props.metaFields" :key="field.field" class="mb-3 col-span-12" :class="numColsToTailwind(field.cols)">
773
+ <!-- Use explicit model binding so dotted paths (e.g., "address.street") work -->
774
+ <edge-image-picker
775
+ v-if="field?.type === 'imagePicker'"
729
776
  :model-value="getByPath(state.workingItem.meta, field.field, '')"
777
+ :label="field?.label || 'Photo'"
778
+ :dialog-title="field?.dialogTitle || 'Select Image'"
779
+ :site="field?.site || 'clearwater-hub-images'"
780
+ :default-tags="field?.tags || []"
781
+ :height-class="field?.heightClass || 'h-[160px]'"
782
+ :disabled="field?.disabled || false"
783
+ :include-cms-all="false"
784
+ @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
785
+ />
786
+ <p v-if="field?.type === 'imagePicker' && field?.disabled" class="mt-1 text-xs text-muted-foreground">
787
+ {{ getDisabledNote(field) }}
788
+ </p>
789
+ <div v-else-if="field?.type === 'richText'" class="member-richtext">
790
+ <edge-shad-html
791
+ :model-value="getByPath(state.workingItem.meta, field.field, '')"
792
+ :name="`meta.${field.field}`"
793
+ :label="field?.label"
794
+ :disabled="field?.disabled || false"
795
+ :description="mergeDisabledNote(field?.description, field)"
796
+ :enabled-toggles="field?.enabledToggles || ['bold', 'italic', 'strike', 'bulletlist', 'orderedlist', 'underline']"
797
+ @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
798
+ />
799
+ </div>
800
+ <edge-shad-select-tags
801
+ v-else-if="field?.type === 'selectTags'"
802
+ :model-value="getByPath(state.workingItem.meta, field.field, [])"
730
803
  :name="`meta.${field.field}`"
731
804
  :label="field?.label"
732
- :disabled="field?.disabled || false"
733
805
  :description="mergeDisabledNote(field?.description, field)"
734
- :enabled-toggles="field?.enabledToggles || ['bold', 'italic', 'strike', 'bulletlist', 'orderedlist', 'underline']"
806
+ :items="field?.items || []"
807
+ :item-title="field?.itemTitle || 'title'"
808
+ :item-value="field?.itemValue || 'name'"
809
+ :allow-additions="field?.allowAdditions || false"
810
+ :placeholder="field?.placeholder"
811
+ :disabled="field?.disabled || false"
735
812
  @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
736
813
  />
737
- </div>
738
- <edge-shad-select-tags
739
- v-else-if="field?.type === 'selectTags'"
740
- :model-value="getByPath(state.workingItem.meta, field.field, [])"
741
- :name="`meta.${field.field}`"
742
- :label="field?.label"
743
- :description="mergeDisabledNote(field?.description, field)"
744
- :items="field?.items || []"
745
- :item-title="field?.itemTitle || 'title'"
746
- :item-value="field?.itemValue || 'name'"
747
- :allow-additions="field?.allowAdditions || false"
748
- :placeholder="field?.placeholder"
749
- :disabled="field?.disabled || false"
750
- @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
751
- />
752
- <div v-else-if="field?.type === 'boolean'" class="space-y-1 -mt-3">
753
- <div class="text-sm font-medium leading-none opacity-0 select-none h-4">
754
- {{ field?.label || '' }}
814
+ <div v-else-if="field?.type === 'boolean'" class="space-y-1 -mt-3">
815
+ <div class="text-sm font-medium leading-none opacity-0 select-none h-4">
816
+ {{ field?.label || '' }}
817
+ </div>
818
+ <edge-g-input
819
+ :model-value="getByPath(state.workingItem.meta, field.field, false)"
820
+ :name="`meta.${field.field}`"
821
+ :field-type="field?.type"
822
+ :label="field?.label"
823
+ parent-tracker-id="user-settings"
824
+ :disable-tracking="true"
825
+ :disabled="field?.disabled || false"
826
+ @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
827
+ />
828
+ <p v-if="mergeDisabledNote(field?.hint, field)" class="text-xs text-muted-foreground">
829
+ {{ mergeDisabledNote(field?.hint, field) }}
830
+ </p>
755
831
  </div>
756
832
  <edge-g-input
757
- :model-value="getByPath(state.workingItem.meta, field.field, false)"
833
+ v-else-if="field?.type === 'textarea'"
834
+ :model-value="getByPath(state.workingItem.meta, field.field, '')"
758
835
  :name="`meta.${field.field}`"
759
836
  :field-type="field?.type"
760
837
  :label="field?.label"
761
838
  parent-tracker-id="user-settings"
839
+ :hint="mergeDisabledNote(field?.hint, field)"
840
+ :persistent-hint="Boolean(mergeDisabledNote(field?.hint, field))"
762
841
  :disable-tracking="true"
842
+ :bindings="{ class: 'h-60' }"
843
+ :mask-options="field?.maskOptions"
844
+ :disabled="field?.disabled || false"
845
+ @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
846
+ />
847
+ <edge-shad-tags
848
+ v-else-if="field?.type === 'tags' || field?.type === 'commaTags'"
849
+ :model-value="getByPath(state.workingItem.meta, field.field, '')"
850
+ :name="`meta.${field.field}`"
851
+ :field-type="field?.type"
852
+ :label="field?.label"
853
+ parent-tracker-id="user-settings"
854
+ :description="mergeDisabledNote(field?.description || field?.hint, field)"
855
+ :disable-tracking="true"
856
+ :disabled="field?.disabled || false"
857
+ @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
858
+ />
859
+ <edge-g-input
860
+ v-else
861
+ :model-value="getByPath(state.workingItem.meta, field.field, '')"
862
+ :name="`meta.${field.field}`"
863
+ :field-type="field?.type"
864
+ :label="field?.label"
865
+ parent-tracker-id="user-settings"
866
+ :hint="mergeDisabledNote(field?.hint, field)"
867
+ :persistent-hint="Boolean(mergeDisabledNote(field?.hint, field))"
868
+ :disable-tracking="true"
869
+ :mask-options="field?.maskOptions"
763
870
  :disabled="field?.disabled || false"
764
871
  @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
765
872
  />
766
- <p v-if="mergeDisabledNote(field?.hint, field)" class="text-xs text-muted-foreground">
767
- {{ mergeDisabledNote(field?.hint, field) }}
768
- </p>
769
873
  </div>
770
- <edge-g-input
771
- v-else-if="field?.type === 'textarea'"
772
- :model-value="getByPath(state.workingItem.meta, field.field, '')"
773
- :name="`meta.${field.field}`"
774
- :field-type="field?.type"
775
- :label="field?.label"
776
- parent-tracker-id="user-settings"
777
- :hint="mergeDisabledNote(field?.hint, field)"
778
- :persistent-hint="Boolean(mergeDisabledNote(field?.hint, field))"
779
- :disable-tracking="true"
780
- :bindings="{ class: 'h-60' }"
781
- :mask-options="field?.maskOptions"
782
- :disabled="field?.disabled || false"
783
- @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
784
- />
785
- <edge-shad-tags
786
- v-else-if="field?.type === 'tags' || field?.type === 'commaTags'"
787
- :model-value="getByPath(state.workingItem.meta, field.field, '')"
788
- :name="`meta.${field.field}`"
789
- :field-type="field?.type"
790
- :label="field?.label"
791
- parent-tracker-id="user-settings"
792
- :description="mergeDisabledNote(field?.description || field?.hint, field)"
793
- :disable-tracking="true"
794
- :disabled="field?.disabled || false"
795
- @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
796
- />
797
- <edge-g-input
798
- v-else
799
- :model-value="getByPath(state.workingItem.meta, field.field, '')"
800
- :name="`meta.${field.field}`"
801
- :field-type="field?.type"
802
- :label="field?.label"
803
- parent-tracker-id="user-settings"
804
- :hint="mergeDisabledNote(field?.hint, field)"
805
- :persistent-hint="Boolean(mergeDisabledNote(field?.hint, field))"
806
- :disable-tracking="true"
807
- :mask-options="field?.maskOptions"
808
- :disabled="field?.disabled || false"
809
- @update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
810
- />
811
874
  </div>
812
- </div>
813
875
 
814
- <edge-g-input
815
- v-if="state.saveButton === 'Invite User'"
816
- v-model="state.workingItem.isTemplate"
817
- name="isTemplate"
818
- :disable-tracking="true"
819
- field-type="boolean"
820
- label="Template User"
821
- :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
822
- />
823
- </slot>
876
+ <edge-g-input
877
+ v-if="state.saveButton === 'Invite User'"
878
+ v-model="state.workingItem.isTemplate"
879
+ name="isTemplate"
880
+ :disable-tracking="true"
881
+ field-type="boolean"
882
+ label="Template User"
883
+ :parent-tracker-id="`inviteUser-${state.workingItem.id}`"
884
+ />
885
+ </slot>
886
+ </div>
887
+ </edge-shad-form>
888
+ </div>
889
+ <div v-else :key="detailViewKey" class="p-4 text-center flex text-slate-500 h-[calc(100vh-4rem)] justify-center items-center overflow-y-auto">
890
+ <div class="text-4xl">
891
+ <ArrowLeft class="inline-block w-12 h-12 mr-2" /> Select a member to view details.
824
892
  </div>
825
- </edge-shad-form>
826
- </div>
827
- <div v-else class="p-4 text-center flex text-slate-500 h-[calc(100vh-4rem)] justify-center items-center overflow-y-auto">
828
- <div class="text-4xl">
829
- <ArrowLeft class="inline-block w-12 h-12 mr-2" /> Select a member to view details.
830
893
  </div>
831
- </div>
894
+ </Transition>
832
895
  </div>
833
896
  </ResizablePanel>
834
897
  </ResizablePanelGroup>
@@ -904,4 +967,14 @@ onBeforeMount(async () => {
904
967
  margin-top: 0;
905
968
  margin-bottom: 1rem;
906
969
  }
970
+
971
+ .fade-enter-active,
972
+ .fade-leave-active {
973
+ transition: opacity 0.3s ease;
974
+ }
975
+
976
+ .fade-enter-from,
977
+ .fade-leave-to {
978
+ opacity: 0;
979
+ }
907
980
  </style>
@@ -188,11 +188,11 @@ const triggerTitle = computed(() => {
188
188
  variant="outline"
189
189
  role="combobox"
190
190
  :aria-expanded="open"
191
- class="w-[200px] justify-between text-foreground"
191
+ class="w-[200px] min-w-0 justify-between text-foreground"
192
192
  :class="props.class"
193
193
  :disabled="props.disabled"
194
194
  >
195
- {{ triggerTitle }}
195
+ <span class="min-w-0 flex-1 truncate text-left" :title="triggerTitle">{{ triggerTitle }}</span>
196
196
  <ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
197
197
  </Button>
198
198
  </FormControl>
@@ -428,7 +428,7 @@ const allowMenuItem = (item: any, isAdmin: boolean) => {
428
428
  const isDev = process.dev || devOverrideEnabled()
429
429
  const adminOnly = toBool(item.adminOnly)
430
430
  const devOnly = toBool(item.devOnly)
431
- console.log('allowMenuItem', { item, isAdmin, isDev, adminOnly, devOnly })
431
+ // console.log('allowMenuItem', { item, isAdmin, isDev, adminOnly, devOnly })
432
432
  const override = toBool(item.override)
433
433
  if (item.override !== undefined)
434
434
  return override