@edgedev/create-edge-app 1.1.27 → 1.1.29
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/edge/components/auth/register.vue +51 -0
- package/edge/components/cms/block.vue +363 -42
- package/edge/components/cms/blockEditor.vue +50 -3
- package/edge/components/cms/codeEditor.vue +39 -2
- package/edge/components/cms/htmlContent.vue +10 -2
- package/edge/components/cms/init_blocks/footer.html +111 -19
- package/edge/components/cms/init_blocks/image.html +8 -0
- package/edge/components/cms/init_blocks/post_content.html +3 -2
- package/edge/components/cms/init_blocks/post_title_header.html +8 -6
- package/edge/components/cms/init_blocks/posts_list.html +6 -5
- package/edge/components/cms/mediaCard.vue +13 -2
- package/edge/components/cms/mediaManager.vue +35 -5
- package/edge/components/cms/menu.vue +384 -61
- package/edge/components/cms/optionsSelect.vue +20 -3
- package/edge/components/cms/page.vue +160 -18
- package/edge/components/cms/site.vue +548 -374
- package/edge/components/cms/siteSettingsForm.vue +623 -0
- package/edge/components/cms/themeDefaultMenu.vue +258 -22
- package/edge/components/cms/themeEditor.vue +95 -11
- package/edge/components/editor.vue +1 -0
- package/edge/components/formSubtypes/myOrgs.vue +112 -1
- package/edge/components/imagePicker.vue +126 -0
- package/edge/components/myAccount.vue +1 -0
- package/edge/components/myProfile.vue +345 -61
- package/edge/components/orgSwitcher.vue +1 -1
- package/edge/components/organizationMembers.vue +620 -235
- package/edge/components/shad/html.vue +6 -0
- package/edge/components/shad/number.vue +2 -2
- package/edge/components/sideBar.vue +7 -4
- package/edge/components/sideBarContent.vue +1 -1
- package/edge/components/userMenu.vue +50 -14
- package/edge/composables/global.ts +4 -1
- package/edge/composables/siteSettingsTemplate.js +79 -0
- package/edge/composables/structuredDataTemplates.js +36 -0
- package/package.json +1 -1
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
// TODO: pass possible roles in prop
|
|
3
3
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
4
|
+
import { ArrowLeft, Loader2, Trash2, User } from 'lucide-vue-next'
|
|
4
5
|
import * as z from 'zod'
|
|
5
6
|
const props = defineProps({
|
|
6
7
|
usersCollectionPath: {
|
|
7
8
|
type: String,
|
|
8
9
|
default: () => `organizations/${edgeGlobal.edgeState.currentOrganization}`,
|
|
9
10
|
},
|
|
11
|
+
defaultImageTags: {
|
|
12
|
+
type: Array,
|
|
13
|
+
default: () => [
|
|
14
|
+
'Headshots',
|
|
15
|
+
],
|
|
16
|
+
},
|
|
10
17
|
metaFields: {
|
|
11
18
|
type: Array,
|
|
12
19
|
default: () => [
|
|
@@ -49,6 +56,11 @@ const props = defineProps({
|
|
|
49
56
|
name: z
|
|
50
57
|
.string({ required_error: 'Name is required' })
|
|
51
58
|
.min(1, { message: 'Name is required' }),
|
|
59
|
+
email: z
|
|
60
|
+
.string({ required_error: 'Email is required' })
|
|
61
|
+
.email({ message: 'Invalid email address' })
|
|
62
|
+
.min(6, { message: 'Email must be at least 6 characters long' })
|
|
63
|
+
.max(50, { message: 'Email must be less than 50 characters long' }),
|
|
52
64
|
}),
|
|
53
65
|
role: z
|
|
54
66
|
.string({ required_error: 'Role is required' })
|
|
@@ -56,6 +68,10 @@ const props = defineProps({
|
|
|
56
68
|
}),
|
|
57
69
|
),
|
|
58
70
|
},
|
|
71
|
+
metaFieldsSchema: {
|
|
72
|
+
type: Object,
|
|
73
|
+
default: null,
|
|
74
|
+
},
|
|
59
75
|
})
|
|
60
76
|
// TODO: If a removed user no longer has roles to any organiztions, need to a create new organization for them with
|
|
61
77
|
// default name of "Personal". This will allow them to continue to use the app.
|
|
@@ -80,6 +96,9 @@ const state = reactive({
|
|
|
80
96
|
role: '',
|
|
81
97
|
isTemplate: false,
|
|
82
98
|
},
|
|
99
|
+
inviteOrgIds: [],
|
|
100
|
+
editOrgIds: [],
|
|
101
|
+
removeOrgIds: [],
|
|
83
102
|
loaded: false,
|
|
84
103
|
})
|
|
85
104
|
|
|
@@ -92,6 +111,47 @@ const roleNamesOnly = computed(() => {
|
|
|
92
111
|
const edgeUsers = toRef(edgeFirebase.state, 'users')
|
|
93
112
|
const users = computed(() => Object.values(edgeUsers.value ?? {}))
|
|
94
113
|
|
|
114
|
+
const orgCollectionPath = orgId => `organizations-${String(orgId).replaceAll('/', '-')}`
|
|
115
|
+
|
|
116
|
+
const adminOrgOptions = computed(() => {
|
|
117
|
+
const orgs = edgeGlobal.edgeState.organizations || []
|
|
118
|
+
const roles = edgeFirebase?.user?.roles || []
|
|
119
|
+
return orgs.filter(org =>
|
|
120
|
+
roles.some(role => role.collectionPath === orgCollectionPath(org.docId) && role.role === 'admin'),
|
|
121
|
+
)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const inviteOrgOptions = computed(() => adminOrgOptions.value)
|
|
125
|
+
|
|
126
|
+
const editOrgOptions = computed(() => adminOrgOptions.value)
|
|
127
|
+
|
|
128
|
+
const removeOrgOptions = computed(() => {
|
|
129
|
+
const userRoles = state.workingItem?.roles || []
|
|
130
|
+
return adminOrgOptions.value.filter(org =>
|
|
131
|
+
userRoles.some(role => role.collectionPath.startsWith(orgCollectionPath(org.docId))),
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const showInviteOrgSelect = computed(() => inviteOrgOptions.value.length > 1)
|
|
136
|
+
const showEditOrgSelect = computed(() => editOrgOptions.value.length > 1)
|
|
137
|
+
const showRemoveOrgSelect = computed(() => removeOrgOptions.value.length > 1)
|
|
138
|
+
|
|
139
|
+
const adminCount = computed(() => {
|
|
140
|
+
return users.value.filter((item) => {
|
|
141
|
+
return item.roles.find((role) => {
|
|
142
|
+
return role.collectionPath === edgeGlobal.edgeState.organizationDocPath.replaceAll('/', '-') && role.role === 'admin'
|
|
143
|
+
})
|
|
144
|
+
}).length
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const selfRemoveBlocked = computed(() => {
|
|
148
|
+
return state.workingItem.userId === edgeFirebase.user.uid
|
|
149
|
+
&& adminCount.value === 1
|
|
150
|
+
&& state.removeOrgIds.includes(edgeGlobal.edgeState.currentOrganization)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const emailDisabledHint = 'This field is tied to the user\'s username and can only be changed by them.'
|
|
154
|
+
|
|
95
155
|
const WIDTHS = {
|
|
96
156
|
1: 'md:col-span-1',
|
|
97
157
|
2: 'md:col-span-2',
|
|
@@ -109,6 +169,42 @@ const WIDTHS = {
|
|
|
109
169
|
|
|
110
170
|
const numColsToTailwind = cols => WIDTHS[cols] || 'md:col-span-12'
|
|
111
171
|
|
|
172
|
+
const disabledNoteText = 'Contact admin to change.'
|
|
173
|
+
|
|
174
|
+
const getDisabledNote = (field) => {
|
|
175
|
+
if (!field?.disabled)
|
|
176
|
+
return ''
|
|
177
|
+
return field?.disabledNote || disabledNoteText
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const mergeDisabledNote = (text, field) => {
|
|
181
|
+
const note = getDisabledNote(field)
|
|
182
|
+
if (!note)
|
|
183
|
+
return text || ''
|
|
184
|
+
if (text)
|
|
185
|
+
return `${text} ${note}`
|
|
186
|
+
return note
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const userKey = (user) => {
|
|
190
|
+
return user?.docId || user?.userId || user?.id || user?.uid || ''
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const PROFILE_IMAGE_SIZE = 96
|
|
194
|
+
const PROFILE_IMAGE_VARIANT = `width=${PROFILE_IMAGE_SIZE},height=${PROFILE_IMAGE_SIZE},fit=cover,quality=85`
|
|
195
|
+
|
|
196
|
+
const profileImageUrl = (url) => {
|
|
197
|
+
if (!url || typeof url !== 'string')
|
|
198
|
+
return ''
|
|
199
|
+
if (url.includes('/cdn-cgi/image/'))
|
|
200
|
+
return url
|
|
201
|
+
if (url.includes('width=') && url.includes('height='))
|
|
202
|
+
return url
|
|
203
|
+
if (url.endsWith('/thumbnail'))
|
|
204
|
+
return url.replace(/\/thumbnail$/, `/${PROFILE_IMAGE_VARIANT}`)
|
|
205
|
+
return url
|
|
206
|
+
}
|
|
207
|
+
|
|
112
208
|
// Helpers to read/write nested keys like "profile.firstName" on plain objects
|
|
113
209
|
function getByPath(obj, path, fallback = undefined) {
|
|
114
210
|
if (!obj || !path)
|
|
@@ -142,39 +238,38 @@ function setByPath(obj, path, value) {
|
|
|
142
238
|
}
|
|
143
239
|
|
|
144
240
|
const sortedFilteredUsers = computed(() => {
|
|
145
|
-
const filter = state.filter.toLowerCase()
|
|
241
|
+
const filter = String(state.filter || '').toLowerCase()
|
|
146
242
|
|
|
147
243
|
const getLastName = (fullName) => {
|
|
148
244
|
if (!fullName)
|
|
149
245
|
return ''
|
|
150
|
-
const parts = fullName.trim().split(/\s+/)
|
|
246
|
+
const parts = String(fullName).trim().split(/\s+/)
|
|
151
247
|
return parts[parts.length - 1] || ''
|
|
152
248
|
}
|
|
153
249
|
|
|
154
250
|
return users.value
|
|
155
|
-
.filter(user =>
|
|
251
|
+
.filter((user) => {
|
|
252
|
+
const name = String(user?.meta?.name || '')
|
|
253
|
+
return name.toLowerCase().includes(filter)
|
|
254
|
+
})
|
|
156
255
|
.sort((a, b) => {
|
|
157
|
-
const lastA = getLastName(a
|
|
158
|
-
const lastB = getLastName(b
|
|
256
|
+
const lastA = getLastName(a?.meta?.name).toLowerCase()
|
|
257
|
+
const lastB = getLastName(b?.meta?.name).toLowerCase()
|
|
159
258
|
return lastA.localeCompare(lastB)
|
|
160
259
|
})
|
|
161
260
|
})
|
|
162
261
|
|
|
163
|
-
const adminCount = computed(() => {
|
|
164
|
-
return users.value.filter((item) => {
|
|
165
|
-
return item.roles.find((role) => {
|
|
166
|
-
return role.collectionPath === edgeGlobal.edgeState.organizationDocPath.replaceAll('/', '-') && role.role === 'admin'
|
|
167
|
-
})
|
|
168
|
-
}).length
|
|
169
|
-
})
|
|
170
|
-
|
|
171
262
|
const addItem = () => {
|
|
172
263
|
state.saveButton = 'Invite User'
|
|
173
264
|
const newItem = edgeGlobal.dupObject(state.newItem)
|
|
174
265
|
newItem.meta.email = ''
|
|
266
|
+
newItem.meta.name = ''
|
|
267
|
+
newItem.meta.profilephoto = ''
|
|
175
268
|
state.workingItem = newItem
|
|
176
269
|
state.workingItem.id = edgeGlobal.generateShortId()
|
|
177
270
|
state.currentTitle = 'Invite User'
|
|
271
|
+
state.inviteOrgIds = [edgeGlobal.edgeState.currentOrganization]
|
|
272
|
+
state.editOrgIds = []
|
|
178
273
|
state.dialog = true
|
|
179
274
|
}
|
|
180
275
|
|
|
@@ -184,6 +279,9 @@ const editItem = (item) => {
|
|
|
184
279
|
state.workingItem = edgeGlobal.dupObject(item)
|
|
185
280
|
state.workingItem.meta = edgeGlobal.dupObject(item.meta)
|
|
186
281
|
state.workingItem.role = edgeGlobal.getRoleName(item.roles, edgeGlobal.edgeState.currentOrganization)
|
|
282
|
+
state.editOrgIds = editOrgOptions.value
|
|
283
|
+
.filter(org => state.workingItem.roles.some(role => role.collectionPath.startsWith(orgCollectionPath(org.docId))))
|
|
284
|
+
.map(org => org.docId)
|
|
187
285
|
const newItemKeys = Object.keys(state.newItem)
|
|
188
286
|
newItemKeys.forEach((key) => {
|
|
189
287
|
if (!state.workingItem?.[key]) {
|
|
@@ -205,15 +303,20 @@ const editItem = (item) => {
|
|
|
205
303
|
const deleteConfirm = (item) => {
|
|
206
304
|
state.currentTitle = item.name
|
|
207
305
|
state.workingItem = edgeGlobal.dupObject(item)
|
|
306
|
+
state.removeOrgIds = [edgeGlobal.edgeState.currentOrganization]
|
|
208
307
|
state.deleteDialog = true
|
|
209
308
|
}
|
|
210
309
|
|
|
211
310
|
const deleteAction = async () => {
|
|
311
|
+
const targetUserId = state.workingItem.docId || state.workingItem.userId
|
|
312
|
+
const selectedOrgIds = state.removeOrgIds.length > 0
|
|
313
|
+
? state.removeOrgIds
|
|
314
|
+
: [edgeGlobal.edgeState.currentOrganization]
|
|
212
315
|
const userRoles = state.workingItem.roles.filter((role) => {
|
|
213
|
-
return role.collectionPath.startsWith(
|
|
316
|
+
return selectedOrgIds.some(orgId => role.collectionPath.startsWith(orgCollectionPath(orgId)))
|
|
214
317
|
})
|
|
215
318
|
for (const role of userRoles) {
|
|
216
|
-
await edgeFirebase.removeUserRoles(
|
|
319
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
217
320
|
// console.log(role.collectionPath)
|
|
218
321
|
}
|
|
219
322
|
state.deleteDialog = false
|
|
@@ -229,10 +332,49 @@ const disableTracking = computed(() => {
|
|
|
229
332
|
return state.saveButton === 'Invite User'
|
|
230
333
|
})
|
|
231
334
|
|
|
335
|
+
const updateInviteOrgSelection = (orgId, checked) => {
|
|
336
|
+
const selections = new Set(state.inviteOrgIds)
|
|
337
|
+
if (checked) {
|
|
338
|
+
selections.add(orgId)
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
selections.delete(orgId)
|
|
342
|
+
}
|
|
343
|
+
state.inviteOrgIds = Array.from(selections)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const updateEditOrgSelection = (orgId, checked) => {
|
|
347
|
+
const selections = new Set(state.editOrgIds)
|
|
348
|
+
if (checked) {
|
|
349
|
+
selections.add(orgId)
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
selections.delete(orgId)
|
|
353
|
+
}
|
|
354
|
+
state.editOrgIds = Array.from(selections)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const updateRemoveOrgSelection = (orgId, checked) => {
|
|
358
|
+
const selections = new Set(state.removeOrgIds)
|
|
359
|
+
if (checked) {
|
|
360
|
+
selections.add(orgId)
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
selections.delete(orgId)
|
|
364
|
+
}
|
|
365
|
+
state.removeOrgIds = Array.from(selections)
|
|
366
|
+
}
|
|
367
|
+
|
|
232
368
|
const onSubmit = async () => {
|
|
233
369
|
state.loading = true
|
|
234
|
-
const
|
|
235
|
-
|
|
370
|
+
const selectedOrgIds = state.inviteOrgIds.length > 0
|
|
371
|
+
? state.inviteOrgIds
|
|
372
|
+
: [edgeGlobal.edgeState.currentOrganization]
|
|
373
|
+
const roles = selectedOrgIds.flatMap((orgId) => {
|
|
374
|
+
const userRoles = edgeGlobal.orgUserRoles(orgId)
|
|
375
|
+
const roleMatch = userRoles.find(role => role.name === state.workingItem.role)
|
|
376
|
+
return roleMatch ? roleMatch.roles : []
|
|
377
|
+
})
|
|
236
378
|
if (state.saveButton === 'Invite User') {
|
|
237
379
|
if (!state.workingItem.isTemplate) {
|
|
238
380
|
await edgeFirebase.addUser({ roles, meta: state.workingItem.meta })
|
|
@@ -242,19 +384,41 @@ const onSubmit = async () => {
|
|
|
242
384
|
}
|
|
243
385
|
}
|
|
244
386
|
else {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
387
|
+
const targetUserId = state.workingItem.docId || state.workingItem.userId
|
|
388
|
+
const selectedOrgIds = state.editOrgIds
|
|
389
|
+
for (const org of editOrgOptions.value) {
|
|
390
|
+
const orgId = org.docId
|
|
391
|
+
const orgPath = orgCollectionPath(orgId)
|
|
392
|
+
const shouldHave = selectedOrgIds.includes(orgId)
|
|
393
|
+
const existingRoles = state.workingItem.roles.filter(role =>
|
|
394
|
+
role.collectionPath.startsWith(orgPath),
|
|
395
|
+
)
|
|
396
|
+
if (!shouldHave && existingRoles.length > 0) {
|
|
397
|
+
for (const role of existingRoles) {
|
|
398
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
399
|
+
}
|
|
400
|
+
continue
|
|
401
|
+
}
|
|
402
|
+
if (shouldHave) {
|
|
403
|
+
const orgRoles = edgeGlobal.orgUserRoles(orgId)
|
|
404
|
+
const roleMatch = orgRoles.find(role => role.name === state.workingItem.role)
|
|
405
|
+
if (!roleMatch)
|
|
406
|
+
continue
|
|
407
|
+
for (const role of existingRoles) {
|
|
408
|
+
if (!roleMatch.roles.some(r => r.collectionPath === role.collectionPath && r.role === role.role)) {
|
|
409
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
for (const role of roleMatch.roles) {
|
|
413
|
+
if (!existingRoles.some(r => r.collectionPath === role.collectionPath && r.role === role.role)) {
|
|
414
|
+
await edgeFirebase.storeUserRoles(targetUserId, role.collectionPath, role.role)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
256
418
|
}
|
|
257
419
|
const stagedUserId = state.workingItem.docId
|
|
420
|
+
console.log('Staged User ID:', stagedUserId)
|
|
421
|
+
console.log('Updating meta:', state.workingItem.meta)
|
|
258
422
|
await edgeFirebase.setUserMeta(state.workingItem.meta, '', stagedUserId)
|
|
259
423
|
}
|
|
260
424
|
edgeGlobal.edgeState.changeTracker = {}
|
|
@@ -262,10 +426,48 @@ const onSubmit = async () => {
|
|
|
262
426
|
state.dialog = false
|
|
263
427
|
}
|
|
264
428
|
|
|
429
|
+
const roleSchema = z
|
|
430
|
+
.string({ required_error: 'Role is required' })
|
|
431
|
+
.min(1, { message: 'Role is required' })
|
|
432
|
+
|
|
433
|
+
const baseMetaSchema = z.object({
|
|
434
|
+
name: z
|
|
435
|
+
.string({ required_error: 'Name is required' })
|
|
436
|
+
.min(1, { message: 'Name is required' }),
|
|
437
|
+
email: z
|
|
438
|
+
.string({ required_error: 'Email is required' })
|
|
439
|
+
.email({ message: 'Invalid email address' })
|
|
440
|
+
.min(6, { message: 'Email must be at least 6 characters long' })
|
|
441
|
+
.max(50, { message: 'Email must be less than 50 characters long' }),
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
const buildMetaSchema = () => {
|
|
445
|
+
const extra = props.metaFieldsSchema
|
|
446
|
+
if (!extra)
|
|
447
|
+
return baseMetaSchema
|
|
448
|
+
if (extra?.shape && typeof extra.shape === 'object')
|
|
449
|
+
return baseMetaSchema.merge(extra)
|
|
450
|
+
if (typeof extra === 'object')
|
|
451
|
+
return baseMetaSchema.extend(extra)
|
|
452
|
+
return baseMetaSchema
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const computedNewUserSchema = computed(() => {
|
|
456
|
+
if (!props.metaFieldsSchema)
|
|
457
|
+
return props.newUserSchema
|
|
458
|
+
return toTypedSchema(z.object({ meta: buildMetaSchema(), role: roleSchema }))
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
const computedUpdateUserSchema = computed(() => {
|
|
462
|
+
if (!props.metaFieldsSchema)
|
|
463
|
+
return props.updateUserSchema
|
|
464
|
+
return toTypedSchema(z.object({ meta: buildMetaSchema(), role: roleSchema }))
|
|
465
|
+
})
|
|
466
|
+
|
|
265
467
|
const computedUserSchema = computed(() =>
|
|
266
468
|
state.saveButton === 'Invite User'
|
|
267
|
-
?
|
|
268
|
-
:
|
|
469
|
+
? computedNewUserSchema.value
|
|
470
|
+
: computedUpdateUserSchema.value,
|
|
269
471
|
)
|
|
270
472
|
|
|
271
473
|
const currentOrganization = computed(() => {
|
|
@@ -301,222 +503,405 @@ onBeforeMount(async () => {
|
|
|
301
503
|
</script>
|
|
302
504
|
|
|
303
505
|
<template>
|
|
304
|
-
<
|
|
305
|
-
<
|
|
306
|
-
<
|
|
307
|
-
<
|
|
308
|
-
<
|
|
309
|
-
<
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
<template #center>
|
|
314
|
-
<slot name="header-center">
|
|
315
|
-
<div class="w-full px-6" />
|
|
316
|
-
</slot>
|
|
317
|
-
</template>
|
|
318
|
-
<template #end>
|
|
319
|
-
<slot name="header-end" :add-item="addItem">
|
|
320
|
-
<edge-shad-button class="bg-primary mx-2 h-6 text-xs" @click="addItem()">
|
|
321
|
-
Invite Member
|
|
322
|
-
</edge-shad-button>
|
|
323
|
-
</slot>
|
|
324
|
-
</template>
|
|
325
|
-
</edge-menu>
|
|
326
|
-
</slot>
|
|
327
|
-
<CardContent class="p-3 w-full overflow-y-auto scroll-area">
|
|
328
|
-
<Input
|
|
329
|
-
v-model="state.filter"
|
|
330
|
-
class="mb-2"
|
|
331
|
-
placeholder="Filter members..."
|
|
332
|
-
/>
|
|
333
|
-
<div v-if="sortedFilteredUsers.length > 0">
|
|
334
|
-
<div v-for="user in sortedFilteredUsers" :key="user.id" class="flex w-full py-2 justify-between items-center cursor-pointer" @click="editItem(user)">
|
|
335
|
-
<slot name="user" :user="user">
|
|
336
|
-
<Avatar class="handle pointer p-0 h-6 w-6 mr-2">
|
|
337
|
-
<User width="18" height="18" />
|
|
338
|
-
</Avatar>
|
|
339
|
-
<div class="flex gap-2 mr-2 items-center">
|
|
340
|
-
<div class="text-md text-bold mr-2">
|
|
341
|
-
{{ user.meta.name }}
|
|
506
|
+
<div v-if="state.loaded" class="w-full flex-1 min-h-0 h-[calc(100vh-58px)] overflow-hidden">
|
|
507
|
+
<ResizablePanelGroup direction="horizontal" class="w-full h-full flex-1">
|
|
508
|
+
<ResizablePanel class="bg-sidebar text-sidebar-foreground min-w-[400px]" :default-size="22" :min-size="30">
|
|
509
|
+
<div class="flex flex-col h-full">
|
|
510
|
+
<div class="px-3 py-3 border-b border-sidebar-border bg-sidebar/90">
|
|
511
|
+
<div class="flex items-center justify-between gap-2">
|
|
512
|
+
<div class="flex items-center gap-2 text-sm font-semibold">
|
|
513
|
+
<component :is="edgeGlobal.iconFromMenu(route)" class="h-4 w-4" />
|
|
514
|
+
<span>Members</span>
|
|
342
515
|
</div>
|
|
343
|
-
<edge-
|
|
344
|
-
|
|
345
|
-
</edge-
|
|
346
|
-
<!-- <edge-chip v-if="!user.userId" class="bg-primary">
|
|
347
|
-
Invited, Not Registered
|
|
348
|
-
</edge-chip> -->
|
|
349
|
-
</div>
|
|
350
|
-
<div class="grow flex gap-2 justify-end">
|
|
351
|
-
<template v-if="!user.userId">
|
|
352
|
-
<edge-chip class="bg-slate-600 w-[200px]">
|
|
353
|
-
{{ user.docId }}
|
|
354
|
-
<edge-clipboard-button class="relative ml-1 top-[2px] mt-0" :text="user.docId" />
|
|
355
|
-
</edge-chip>
|
|
356
|
-
</template>
|
|
357
|
-
<edge-chip>
|
|
358
|
-
{{ edgeGlobal.getRoleName(user.roles, edgeGlobal.edgeState.currentOrganization) }}
|
|
359
|
-
</edge-chip>
|
|
516
|
+
<edge-shad-button size="sm" class="h-7 text-xs bg-primary" @click="addItem()">
|
|
517
|
+
Invite
|
|
518
|
+
</edge-shad-button>
|
|
360
519
|
</div>
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
class="
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
520
|
+
<Input
|
|
521
|
+
v-model="state.filter"
|
|
522
|
+
class="mt-3 h-8 w-full"
|
|
523
|
+
placeholder="Filter members..."
|
|
524
|
+
/>
|
|
525
|
+
</div>
|
|
526
|
+
<div class="flex-1 overflow-y-auto">
|
|
527
|
+
<SidebarMenu class="px-2 py-2 space-y-0">
|
|
528
|
+
<SidebarMenuItem
|
|
529
|
+
v-for="user in sortedFilteredUsers"
|
|
530
|
+
:key="userKey(user)"
|
|
531
|
+
>
|
|
532
|
+
<SidebarMenuButton
|
|
533
|
+
class="w-full !h-auto items-start px-3 py-2"
|
|
534
|
+
:class="state.dialog && userKey(state.workingItem) && userKey(state.workingItem) === userKey(user) ? 'bg-sidebar-accent text-sidebar-accent-foreground' : ''"
|
|
535
|
+
@click="editItem(user)"
|
|
536
|
+
>
|
|
537
|
+
<div class="flex w-full items-start gap-3" :class="!user.userId ? 'opacity-70' : ''">
|
|
538
|
+
<Avatar class="h-12 w-12 rounded-md border bg-muted/40 flex items-center justify-center overflow-hidden">
|
|
539
|
+
<img
|
|
540
|
+
v-if="user?.meta?.profilephoto"
|
|
541
|
+
:src="profileImageUrl(user.meta.profilephoto)"
|
|
542
|
+
alt=""
|
|
543
|
+
class="h-full w-full object-cover"
|
|
544
|
+
>
|
|
545
|
+
<User v-else width="24" height="24" />
|
|
546
|
+
</Avatar>
|
|
547
|
+
<div class="min-w-0 flex-1">
|
|
548
|
+
<div class="flex items-center gap-2">
|
|
549
|
+
<span class="text-sm font-medium leading-snug whitespace-normal uppercase">
|
|
550
|
+
{{ user?.meta?.name || user?.meta?.email || 'Unnamed Member' }}
|
|
551
|
+
</span>
|
|
552
|
+
<!-- <span v-if="!user.userId" class="text-[10px] uppercase tracking-wide text-muted-foreground">
|
|
553
|
+
-
|
|
554
|
+
</span> -->
|
|
555
|
+
<edge-chip v-if="user.userId === edgeFirebase.user.uid">
|
|
556
|
+
You
|
|
557
|
+
</edge-chip>
|
|
558
|
+
</div>
|
|
559
|
+
<div class="mt-1 flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground leading-snug">
|
|
560
|
+
<span class="rounded-full bg-secondary px-2 py-0.5 text-[10px] text-secondary-foreground">
|
|
561
|
+
{{ edgeGlobal.getRoleName(user.roles, edgeGlobal.edgeState.currentOrganization) }}
|
|
562
|
+
</span>
|
|
563
|
+
<span v-if="!user.userId && user.docId" class="inline-flex items-center gap-1 whitespace-normal">
|
|
564
|
+
{{ user.docId }}
|
|
565
|
+
<edge-clipboard-button class="relative top-[1px]" :text="user.docId" />
|
|
566
|
+
</span>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
<edge-shad-button
|
|
570
|
+
size="icon"
|
|
571
|
+
variant="ghost"
|
|
572
|
+
class="h-7 w-7 text-destructive/80 hover:text-destructive"
|
|
573
|
+
@click.stop="deleteConfirm(user)"
|
|
574
|
+
>
|
|
575
|
+
<Trash2 class="h-4 w-4" />
|
|
576
|
+
</edge-shad-button>
|
|
577
|
+
</div>
|
|
578
|
+
</SidebarMenuButton>
|
|
579
|
+
<Separator class="my-1" />
|
|
580
|
+
</SidebarMenuItem>
|
|
581
|
+
<div v-if="sortedFilteredUsers.length === 0" class="px-4 py-6 text-xs text-muted-foreground">
|
|
582
|
+
No members found.
|
|
583
|
+
</div>
|
|
584
|
+
</SidebarMenu>
|
|
585
|
+
</div>
|
|
371
586
|
</div>
|
|
372
|
-
</
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
<span v-else>
|
|
383
|
-
Remove "{{ state.workingItem.meta.name }}"
|
|
384
|
-
</span>
|
|
385
|
-
</DialogTitle>
|
|
386
|
-
<DialogDescription />
|
|
387
|
-
</DialogHeader>
|
|
388
|
-
|
|
389
|
-
<h3 v-if="state.workingItem.userId === edgeFirebase.user.uid && adminCount > 1">
|
|
390
|
-
Are you sure you want to remove yourself from the organization "{{ currentOrganization.name }}"? You will no longer have access to any of the organization's data.
|
|
391
|
-
</h3>
|
|
392
|
-
<h3 v-else-if="state.workingItem.userId === edgeFirebase.user.uid && adminCount === 1">
|
|
393
|
-
You cannot remove yourself from this organization because you are the only admin. You can delete the organization or add another admin.
|
|
394
|
-
</h3>
|
|
395
|
-
<h3 v-else>
|
|
396
|
-
Are you sure you want to remove "{{ state.workingItem.meta.name }}" from the organization "{{ currentOrganization.name }}"?
|
|
397
|
-
</h3>
|
|
398
|
-
<DialogFooter class="pt-6 flex justify-between">
|
|
399
|
-
<edge-shad-button class="text-white bg-slate-800 hover:bg-slate-400" @click="state.deleteDialog = false">
|
|
400
|
-
Cancel
|
|
401
|
-
</edge-shad-button>
|
|
402
|
-
<edge-shad-button
|
|
403
|
-
:disabled="adminCount === 1 && state.workingItem.userId === edgeFirebase.user.uid"
|
|
404
|
-
class="w-full"
|
|
405
|
-
variant="destructive"
|
|
406
|
-
@click="deleteAction()"
|
|
587
|
+
</ResizablePanel>
|
|
588
|
+
<ResizablePanel class="bg-background">
|
|
589
|
+
<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"
|
|
407
597
|
>
|
|
408
|
-
<
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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>
|
|
614
|
+
</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
|
+
/>
|
|
633
|
+
</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
|
+
/>
|
|
673
|
+
</div>
|
|
674
|
+
</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>
|
|
689
|
+
</div>
|
|
690
|
+
</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>
|
|
705
|
+
</div>
|
|
706
|
+
</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
|
|
729
|
+
:model-value="getByPath(state.workingItem.meta, field.field, '')"
|
|
730
|
+
:name="`meta.${field.field}`"
|
|
731
|
+
:label="field?.label"
|
|
732
|
+
:disabled="field?.disabled || false"
|
|
733
|
+
:description="mergeDisabledNote(field?.description, field)"
|
|
734
|
+
:enabled-toggles="field?.enabledToggles || ['bold', 'italic', 'strike', 'bulletlist', 'orderedlist', 'underline']"
|
|
735
|
+
@update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
|
|
736
|
+
/>
|
|
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 || '' }}
|
|
755
|
+
</div>
|
|
756
|
+
<edge-g-input
|
|
757
|
+
:model-value="getByPath(state.workingItem.meta, field.field, false)"
|
|
758
|
+
:name="`meta.${field.field}`"
|
|
759
|
+
:field-type="field?.type"
|
|
760
|
+
:label="field?.label"
|
|
761
|
+
parent-tracker-id="user-settings"
|
|
762
|
+
:disable-tracking="true"
|
|
763
|
+
:disabled="field?.disabled || false"
|
|
764
|
+
@update:model-value="val => setByPath(state.workingItem.meta, field.field, val)"
|
|
765
|
+
/>
|
|
766
|
+
<p v-if="mergeDisabledNote(field?.hint, field)" class="text-xs text-muted-foreground">
|
|
767
|
+
{{ mergeDisabledNote(field?.hint, field) }}
|
|
768
|
+
</p>
|
|
769
|
+
</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
|
+
</div>
|
|
812
|
+
</div>
|
|
813
|
+
|
|
476
814
|
<edge-g-input
|
|
477
|
-
v-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
:field-type="field?.type"
|
|
481
|
-
:label="field?.label"
|
|
482
|
-
parent-tracker-id="user-settings"
|
|
483
|
-
:hint="field?.hint"
|
|
815
|
+
v-if="state.saveButton === 'Invite User'"
|
|
816
|
+
v-model="state.workingItem.isTemplate"
|
|
817
|
+
name="isTemplate"
|
|
484
818
|
:disable-tracking="true"
|
|
485
|
-
|
|
819
|
+
field-type="boolean"
|
|
820
|
+
label="Template User"
|
|
821
|
+
:parent-tracker-id="`inviteUser-${state.workingItem.id}`"
|
|
486
822
|
/>
|
|
487
|
-
</
|
|
823
|
+
</slot>
|
|
488
824
|
</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
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
</div>
|
|
833
|
+
</ResizablePanel>
|
|
834
|
+
</ResizablePanelGroup>
|
|
489
835
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
836
|
+
<edge-shad-dialog
|
|
837
|
+
v-model="state.deleteDialog"
|
|
838
|
+
>
|
|
839
|
+
<DialogContent>
|
|
840
|
+
<DialogHeader>
|
|
841
|
+
<DialogTitle>
|
|
842
|
+
<span v-if="state.workingItem.userId === edgeFirebase.user.uid">
|
|
843
|
+
Remove Yourself?
|
|
844
|
+
</span>
|
|
845
|
+
<span v-else>
|
|
846
|
+
Remove "{{ state.workingItem.meta.name }}"
|
|
847
|
+
</span>
|
|
848
|
+
</DialogTitle>
|
|
849
|
+
<DialogDescription />
|
|
850
|
+
</DialogHeader>
|
|
851
|
+
|
|
852
|
+
<h3 v-if="selfRemoveBlocked">
|
|
853
|
+
You cannot remove yourself from this organization because you are the only admin. You can delete the organization or add another admin.
|
|
854
|
+
</h3>
|
|
855
|
+
<h3 v-else-if="state.workingItem.userId === edgeFirebase.user.uid">
|
|
856
|
+
<span v-if="showRemoveOrgSelect">Select the organizations you want to leave.</span>
|
|
857
|
+
<span v-else>Are you sure you want to remove yourself from the organization "{{ currentOrganization.name }}"? You will no longer have access to any of the organization's data.</span>
|
|
858
|
+
</h3>
|
|
859
|
+
<h3 v-else>
|
|
860
|
+
<span v-if="showRemoveOrgSelect">Select the organizations you want to remove "{{ state.workingItem.meta.name }}" from.</span>
|
|
861
|
+
<span v-else>Are you sure you want to remove "{{ state.workingItem.meta.name }}" from the organization "{{ currentOrganization.name }}"?</span>
|
|
862
|
+
</h3>
|
|
863
|
+
<div v-if="showRemoveOrgSelect" class="mt-4 w-full flex flex-wrap gap-2">
|
|
864
|
+
<div v-for="org in removeOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
|
|
865
|
+
<edge-shad-checkbox
|
|
866
|
+
:name="`remove-org-${org.docId}`"
|
|
867
|
+
:model-value="state.removeOrgIds.includes(org.docId)"
|
|
868
|
+
@update:model-value="val => updateRemoveOrgSelection(org.docId, val)"
|
|
869
|
+
>
|
|
870
|
+
{{ org.name }}
|
|
871
|
+
</edge-shad-checkbox>
|
|
872
|
+
</div>
|
|
873
|
+
</div>
|
|
874
|
+
<DialogFooter class="pt-6 flex justify-between">
|
|
875
|
+
<edge-shad-button class="text-white bg-slate-800 hover:bg-slate-400" @click="state.deleteDialog = false">
|
|
876
|
+
Cancel
|
|
877
|
+
</edge-shad-button>
|
|
878
|
+
<edge-shad-button
|
|
879
|
+
:disabled="selfRemoveBlocked"
|
|
880
|
+
class="w-full"
|
|
881
|
+
variant="destructive"
|
|
882
|
+
@click="deleteAction()"
|
|
883
|
+
>
|
|
884
|
+
<span v-if="state.workingItem.userId === edgeFirebase.user.uid">
|
|
885
|
+
Leave
|
|
886
|
+
</span>
|
|
887
|
+
<span v-else>
|
|
888
|
+
Remove
|
|
889
|
+
</span>
|
|
890
|
+
</edge-shad-button>
|
|
891
|
+
</DialogFooter>
|
|
892
|
+
</DialogContent>
|
|
893
|
+
</edge-shad-dialog>
|
|
894
|
+
</div>
|
|
518
895
|
</template>
|
|
519
896
|
|
|
520
897
|
<style lang="scss" scoped>
|
|
898
|
+
:deep(.member-richtext .tiptap) {
|
|
899
|
+
min-height: 220px;
|
|
900
|
+
padding: 0.75rem 1rem;
|
|
901
|
+
}
|
|
521
902
|
|
|
903
|
+
:deep(.member-richtext .tiptap p) {
|
|
904
|
+
margin-top: 0;
|
|
905
|
+
margin-bottom: 1rem;
|
|
906
|
+
}
|
|
522
907
|
</style>
|