@edgedev/create-edge-app 1.1.27 → 1.1.28
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/cms/block.vue +334 -26
- package/edge/components/cms/blockEditor.vue +50 -3
- package/edge/components/cms/codeEditor.vue +15 -0
- 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 +16 -2
- package/edge/components/cms/menu.vue +253 -42
- package/edge/components/cms/page.vue +151 -18
- package/edge/components/cms/site.vue +517 -372
- package/edge/components/cms/siteSettingsForm.vue +616 -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/orgSwitcher.vue +1 -1
- package/edge/components/organizationMembers.vue +171 -21
- package/edge/components/shad/html.vue +6 -0
- package/edge/components/sideBar.vue +7 -4
- package/edge/components/sideBarContent.vue +1 -1
- package/edge/components/userMenu.vue +50 -14
- package/edge/composables/siteSettingsTemplate.js +79 -0
- package/edge/composables/structuredDataTemplates.js +36 -0
- package/package.json +1 -1
|
@@ -30,18 +30,38 @@ const state = reactive({
|
|
|
30
30
|
},
|
|
31
31
|
deleteDialog: false,
|
|
32
32
|
loading: false,
|
|
33
|
+
bringUserIds: [],
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const edgeUsers = toRef(edgeFirebase.state, 'users')
|
|
37
|
+
const users = computed(() => Object.values(edgeUsers.value ?? {}))
|
|
38
|
+
const roles = computed(() => edgeFirebase.user.roles)
|
|
39
|
+
|
|
40
|
+
const bringUserOptions = computed(() => {
|
|
41
|
+
return users.value.filter(user => user.userId !== edgeFirebase.user.uid)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const showBringUsers = computed(() => {
|
|
45
|
+
return state.saveButton === 'Add Organization' && bringUserOptions.value.length > 0
|
|
33
46
|
})
|
|
34
47
|
|
|
35
48
|
const newItem = {
|
|
36
49
|
name: '',
|
|
37
50
|
}
|
|
38
51
|
|
|
52
|
+
const startUsersSnapshot = async () => {
|
|
53
|
+
if (!edgeGlobal.edgeState.currentOrganization)
|
|
54
|
+
return
|
|
55
|
+
await edgeFirebase.startUsersSnapshot(`organizations/${edgeGlobal.edgeState.currentOrganization}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
const addItem = () => {
|
|
40
59
|
console.log(newItem)
|
|
41
60
|
state.saveButton = 'Add Organization'
|
|
42
61
|
state.workingItem = edgeGlobal.dupObject(newItem)
|
|
43
62
|
state.workingItem.id = edgeGlobal.generateShortId()
|
|
44
63
|
state.currentTitle = 'Add Organization'
|
|
64
|
+
state.bringUserIds = []
|
|
45
65
|
state.dialog = true
|
|
46
66
|
}
|
|
47
67
|
|
|
@@ -75,9 +95,77 @@ const register = reactive({
|
|
|
75
95
|
dynamicDocumentFieldValue: '',
|
|
76
96
|
})
|
|
77
97
|
|
|
98
|
+
const updateBringUserSelection = (docId, checked) => {
|
|
99
|
+
const selections = new Set(state.bringUserIds)
|
|
100
|
+
if (checked) {
|
|
101
|
+
selections.add(docId)
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
selections.delete(docId)
|
|
105
|
+
}
|
|
106
|
+
state.bringUserIds = Array.from(selections)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const addUsersToOrganization = async (orgId) => {
|
|
110
|
+
if (!orgId || state.bringUserIds.length === 0)
|
|
111
|
+
return
|
|
112
|
+
const targetUsers = users.value.filter(user => state.bringUserIds.includes(user.docId))
|
|
113
|
+
for (const user of targetUsers) {
|
|
114
|
+
const roleName = edgeGlobal.getRoleName(user.roles, edgeGlobal.edgeState.currentOrganization)
|
|
115
|
+
const resolvedRoleName = roleName === 'Unknown' ? 'User' : roleName
|
|
116
|
+
const orgRoles = edgeGlobal.orgUserRoles(orgId)
|
|
117
|
+
const roleMatch = orgRoles.find(role => role.name === resolvedRoleName)
|
|
118
|
+
if (!roleMatch)
|
|
119
|
+
continue
|
|
120
|
+
for (const role of roleMatch.roles) {
|
|
121
|
+
await edgeFirebase.storeUserRoles(user.docId, role.collectionPath, role.role)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const waitForOrgRole = async (orgId) => {
|
|
127
|
+
const orgPath = `organizations-${String(orgId).replaceAll('/', '-')}`
|
|
128
|
+
const maxAttempts = 30
|
|
129
|
+
const delayMs = 300
|
|
130
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
131
|
+
if (edgeFirebase.user.roles.some(role => role.collectionPath === orgPath)) {
|
|
132
|
+
return true
|
|
133
|
+
}
|
|
134
|
+
await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
135
|
+
}
|
|
136
|
+
return false
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const waitForNewOrgRole = async (previousOrgPaths) => {
|
|
140
|
+
const maxAttempts = 30
|
|
141
|
+
const delayMs = 300
|
|
142
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
143
|
+
const currentOrgRoles = edgeFirebase.user.roles.filter(role => role.collectionPath.startsWith('organizations-'))
|
|
144
|
+
const newOrgRole = currentOrgRoles.find(role => !previousOrgPaths.includes(role.collectionPath))
|
|
145
|
+
if (newOrgRole) {
|
|
146
|
+
return newOrgRole
|
|
147
|
+
}
|
|
148
|
+
await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
149
|
+
}
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
onBeforeMount(async () => {
|
|
154
|
+
await startUsersSnapshot()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
watch(() => edgeGlobal.edgeState.currentOrganization, async (nextOrg, prevOrg) => {
|
|
158
|
+
if (nextOrg && nextOrg !== prevOrg) {
|
|
159
|
+
await startUsersSnapshot()
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
|
|
78
163
|
const onSubmit = async () => {
|
|
79
164
|
const registerSend = edgeGlobal.dupObject(register)
|
|
80
165
|
state.loading = true
|
|
166
|
+
const existingOrgPaths = edgeFirebase.user.roles
|
|
167
|
+
.filter(role => role.collectionPath.startsWith('organizations-'))
|
|
168
|
+
.map(role => role.collectionPath)
|
|
81
169
|
if (state.saveButton === 'Add Organization') {
|
|
82
170
|
registerSend.dynamicDocumentFieldValue = state.workingItem.name
|
|
83
171
|
}
|
|
@@ -86,7 +174,14 @@ const onSubmit = async () => {
|
|
|
86
174
|
registerSend.registrationCode = state.workingItem.name
|
|
87
175
|
}
|
|
88
176
|
const results = await edgeFirebase.currentUserRegister(registerSend)
|
|
89
|
-
edgeGlobal.getOrganizations(edgeFirebase)
|
|
177
|
+
await edgeGlobal.getOrganizations(edgeFirebase)
|
|
178
|
+
if (state.saveButton === 'Add Organization') {
|
|
179
|
+
const newOrgRole = await waitForNewOrgRole(existingOrgPaths)
|
|
180
|
+
const newOrgId = newOrgRole?.collectionPath?.replace('organizations-', '')
|
|
181
|
+
if (newOrgId) {
|
|
182
|
+
await addUsersToOrganization(newOrgId)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
90
185
|
console.log(results)
|
|
91
186
|
edgeGlobal.edgeState.changeTracker = {}
|
|
92
187
|
state.dialog = false
|
|
@@ -189,6 +284,22 @@ const schema = toTypedSchema(z.object({
|
|
|
189
284
|
<template v-else>
|
|
190
285
|
To join an existing organization, please enter the registration code provided by the organization.
|
|
191
286
|
</template>
|
|
287
|
+
<div v-if="showBringUsers" class="mt-4 w-full">
|
|
288
|
+
<div class="text-sm font-medium text-foreground">
|
|
289
|
+
Users to bring over (you will be added automatically)
|
|
290
|
+
</div>
|
|
291
|
+
<div class="mt-2 w-full flex flex-wrap gap-2">
|
|
292
|
+
<div v-for="user in bringUserOptions" :key="user.docId" class="flex-1 min-w-[220px]">
|
|
293
|
+
<edge-shad-checkbox
|
|
294
|
+
:name="`bring-user-${user.docId}`"
|
|
295
|
+
:model-value="state.bringUserIds.includes(user.docId)"
|
|
296
|
+
@update:model-value="val => updateBringUserSelection(user.docId, val)"
|
|
297
|
+
>
|
|
298
|
+
{{ user.meta.name }}
|
|
299
|
+
</edge-shad-checkbox>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
192
303
|
<DialogFooter class="pt-6 flex justify-between">
|
|
193
304
|
<edge-shad-button variant="destructive" @click="closeDialog">
|
|
194
305
|
Close
|
|
@@ -80,6 +80,9 @@ const state = reactive({
|
|
|
80
80
|
role: '',
|
|
81
81
|
isTemplate: false,
|
|
82
82
|
},
|
|
83
|
+
inviteOrgIds: [],
|
|
84
|
+
editOrgIds: [],
|
|
85
|
+
removeOrgIds: [],
|
|
83
86
|
loaded: false,
|
|
84
87
|
})
|
|
85
88
|
|
|
@@ -92,6 +95,37 @@ const roleNamesOnly = computed(() => {
|
|
|
92
95
|
const edgeUsers = toRef(edgeFirebase.state, 'users')
|
|
93
96
|
const users = computed(() => Object.values(edgeUsers.value ?? {}))
|
|
94
97
|
|
|
98
|
+
const orgCollectionPath = orgId => `organizations-${String(orgId).replaceAll('/', '-')}`
|
|
99
|
+
|
|
100
|
+
const adminOrgOptions = computed(() => {
|
|
101
|
+
const orgs = edgeGlobal.edgeState.organizations || []
|
|
102
|
+
const roles = edgeFirebase?.user?.roles || []
|
|
103
|
+
return orgs.filter(org =>
|
|
104
|
+
roles.some(role => role.collectionPath === orgCollectionPath(org.docId) && role.role === 'admin'),
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const inviteOrgOptions = computed(() => adminOrgOptions.value)
|
|
109
|
+
|
|
110
|
+
const editOrgOptions = computed(() => adminOrgOptions.value)
|
|
111
|
+
|
|
112
|
+
const removeOrgOptions = computed(() => {
|
|
113
|
+
const userRoles = state.workingItem?.roles || []
|
|
114
|
+
return adminOrgOptions.value.filter(org =>
|
|
115
|
+
userRoles.some(role => role.collectionPath.startsWith(orgCollectionPath(org.docId))),
|
|
116
|
+
)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const showInviteOrgSelect = computed(() => inviteOrgOptions.value.length > 1)
|
|
120
|
+
const showEditOrgSelect = computed(() => editOrgOptions.value.length > 1)
|
|
121
|
+
const showRemoveOrgSelect = computed(() => removeOrgOptions.value.length > 1)
|
|
122
|
+
|
|
123
|
+
const selfRemoveBlocked = computed(() => {
|
|
124
|
+
return state.workingItem.userId === edgeFirebase.user.uid
|
|
125
|
+
&& adminCount.value === 1
|
|
126
|
+
&& state.removeOrgIds.includes(edgeGlobal.edgeState.currentOrganization)
|
|
127
|
+
})
|
|
128
|
+
|
|
95
129
|
const WIDTHS = {
|
|
96
130
|
1: 'md:col-span-1',
|
|
97
131
|
2: 'md:col-span-2',
|
|
@@ -175,6 +209,8 @@ const addItem = () => {
|
|
|
175
209
|
state.workingItem = newItem
|
|
176
210
|
state.workingItem.id = edgeGlobal.generateShortId()
|
|
177
211
|
state.currentTitle = 'Invite User'
|
|
212
|
+
state.inviteOrgIds = [edgeGlobal.edgeState.currentOrganization]
|
|
213
|
+
state.editOrgIds = []
|
|
178
214
|
state.dialog = true
|
|
179
215
|
}
|
|
180
216
|
|
|
@@ -184,6 +220,9 @@ const editItem = (item) => {
|
|
|
184
220
|
state.workingItem = edgeGlobal.dupObject(item)
|
|
185
221
|
state.workingItem.meta = edgeGlobal.dupObject(item.meta)
|
|
186
222
|
state.workingItem.role = edgeGlobal.getRoleName(item.roles, edgeGlobal.edgeState.currentOrganization)
|
|
223
|
+
state.editOrgIds = editOrgOptions.value
|
|
224
|
+
.filter(org => state.workingItem.roles.some(role => role.collectionPath.startsWith(orgCollectionPath(org.docId))))
|
|
225
|
+
.map(org => org.docId)
|
|
187
226
|
const newItemKeys = Object.keys(state.newItem)
|
|
188
227
|
newItemKeys.forEach((key) => {
|
|
189
228
|
if (!state.workingItem?.[key]) {
|
|
@@ -205,15 +244,20 @@ const editItem = (item) => {
|
|
|
205
244
|
const deleteConfirm = (item) => {
|
|
206
245
|
state.currentTitle = item.name
|
|
207
246
|
state.workingItem = edgeGlobal.dupObject(item)
|
|
247
|
+
state.removeOrgIds = [edgeGlobal.edgeState.currentOrganization]
|
|
208
248
|
state.deleteDialog = true
|
|
209
249
|
}
|
|
210
250
|
|
|
211
251
|
const deleteAction = async () => {
|
|
252
|
+
const targetUserId = state.workingItem.docId || state.workingItem.userId
|
|
253
|
+
const selectedOrgIds = state.removeOrgIds.length > 0
|
|
254
|
+
? state.removeOrgIds
|
|
255
|
+
: [edgeGlobal.edgeState.currentOrganization]
|
|
212
256
|
const userRoles = state.workingItem.roles.filter((role) => {
|
|
213
|
-
return role.collectionPath.startsWith(
|
|
257
|
+
return selectedOrgIds.some(orgId => role.collectionPath.startsWith(orgCollectionPath(orgId)))
|
|
214
258
|
})
|
|
215
259
|
for (const role of userRoles) {
|
|
216
|
-
await edgeFirebase.removeUserRoles(
|
|
260
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
217
261
|
// console.log(role.collectionPath)
|
|
218
262
|
}
|
|
219
263
|
state.deleteDialog = false
|
|
@@ -229,10 +273,49 @@ const disableTracking = computed(() => {
|
|
|
229
273
|
return state.saveButton === 'Invite User'
|
|
230
274
|
})
|
|
231
275
|
|
|
276
|
+
const updateInviteOrgSelection = (orgId, checked) => {
|
|
277
|
+
const selections = new Set(state.inviteOrgIds)
|
|
278
|
+
if (checked) {
|
|
279
|
+
selections.add(orgId)
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
selections.delete(orgId)
|
|
283
|
+
}
|
|
284
|
+
state.inviteOrgIds = Array.from(selections)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const updateEditOrgSelection = (orgId, checked) => {
|
|
288
|
+
const selections = new Set(state.editOrgIds)
|
|
289
|
+
if (checked) {
|
|
290
|
+
selections.add(orgId)
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
selections.delete(orgId)
|
|
294
|
+
}
|
|
295
|
+
state.editOrgIds = Array.from(selections)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const updateRemoveOrgSelection = (orgId, checked) => {
|
|
299
|
+
const selections = new Set(state.removeOrgIds)
|
|
300
|
+
if (checked) {
|
|
301
|
+
selections.add(orgId)
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
selections.delete(orgId)
|
|
305
|
+
}
|
|
306
|
+
state.removeOrgIds = Array.from(selections)
|
|
307
|
+
}
|
|
308
|
+
|
|
232
309
|
const onSubmit = async () => {
|
|
233
310
|
state.loading = true
|
|
234
|
-
const
|
|
235
|
-
|
|
311
|
+
const selectedOrgIds = state.inviteOrgIds.length > 0
|
|
312
|
+
? state.inviteOrgIds
|
|
313
|
+
: [edgeGlobal.edgeState.currentOrganization]
|
|
314
|
+
const roles = selectedOrgIds.flatMap((orgId) => {
|
|
315
|
+
const userRoles = edgeGlobal.orgUserRoles(orgId)
|
|
316
|
+
const roleMatch = userRoles.find(role => role.name === state.workingItem.role)
|
|
317
|
+
return roleMatch ? roleMatch.roles : []
|
|
318
|
+
})
|
|
236
319
|
if (state.saveButton === 'Invite User') {
|
|
237
320
|
if (!state.workingItem.isTemplate) {
|
|
238
321
|
await edgeFirebase.addUser({ roles, meta: state.workingItem.meta })
|
|
@@ -242,19 +325,41 @@ const onSubmit = async () => {
|
|
|
242
325
|
}
|
|
243
326
|
}
|
|
244
327
|
else {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
328
|
+
const targetUserId = state.workingItem.docId || state.workingItem.userId
|
|
329
|
+
const selectedOrgIds = state.editOrgIds
|
|
330
|
+
for (const org of editOrgOptions.value) {
|
|
331
|
+
const orgId = org.docId
|
|
332
|
+
const orgPath = orgCollectionPath(orgId)
|
|
333
|
+
const shouldHave = selectedOrgIds.includes(orgId)
|
|
334
|
+
const existingRoles = state.workingItem.roles.filter(role =>
|
|
335
|
+
role.collectionPath.startsWith(orgPath),
|
|
336
|
+
)
|
|
337
|
+
if (!shouldHave && existingRoles.length > 0) {
|
|
338
|
+
for (const role of existingRoles) {
|
|
339
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
340
|
+
}
|
|
341
|
+
continue
|
|
342
|
+
}
|
|
343
|
+
if (shouldHave) {
|
|
344
|
+
const orgRoles = edgeGlobal.orgUserRoles(orgId)
|
|
345
|
+
const roleMatch = orgRoles.find(role => role.name === state.workingItem.role)
|
|
346
|
+
if (!roleMatch)
|
|
347
|
+
continue
|
|
348
|
+
for (const role of existingRoles) {
|
|
349
|
+
if (!roleMatch.roles.some(r => r.collectionPath === role.collectionPath && r.role === role.role)) {
|
|
350
|
+
await edgeFirebase.removeUserRoles(targetUserId, role.collectionPath)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
for (const role of roleMatch.roles) {
|
|
354
|
+
if (!existingRoles.some(r => r.collectionPath === role.collectionPath && r.role === role.role)) {
|
|
355
|
+
await edgeFirebase.storeUserRoles(targetUserId, role.collectionPath, role.role)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
256
359
|
}
|
|
257
360
|
const stagedUserId = state.workingItem.docId
|
|
361
|
+
console.log('Staged User ID:', stagedUserId)
|
|
362
|
+
console.log('Updating meta:', state.workingItem.meta)
|
|
258
363
|
await edgeFirebase.setUserMeta(state.workingItem.meta, '', stagedUserId)
|
|
259
364
|
}
|
|
260
365
|
edgeGlobal.edgeState.changeTracker = {}
|
|
@@ -386,21 +491,34 @@ onBeforeMount(async () => {
|
|
|
386
491
|
<DialogDescription />
|
|
387
492
|
</DialogHeader>
|
|
388
493
|
|
|
389
|
-
<h3 v-if="
|
|
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">
|
|
494
|
+
<h3 v-if="selfRemoveBlocked">
|
|
393
495
|
You cannot remove yourself from this organization because you are the only admin. You can delete the organization or add another admin.
|
|
394
496
|
</h3>
|
|
497
|
+
<h3 v-else-if="state.workingItem.userId === edgeFirebase.user.uid">
|
|
498
|
+
<span v-if="showRemoveOrgSelect">Select the organizations you want to leave.</span>
|
|
499
|
+
<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>
|
|
500
|
+
</h3>
|
|
395
501
|
<h3 v-else>
|
|
396
|
-
|
|
502
|
+
<span v-if="showRemoveOrgSelect">Select the organizations you want to remove "{{ state.workingItem.meta.name }}" from.</span>
|
|
503
|
+
<span v-else>Are you sure you want to remove "{{ state.workingItem.meta.name }}" from the organization "{{ currentOrganization.name }}"?</span>
|
|
397
504
|
</h3>
|
|
505
|
+
<div v-if="showRemoveOrgSelect" class="mt-4 w-full flex flex-wrap gap-2">
|
|
506
|
+
<div v-for="org in removeOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
|
|
507
|
+
<edge-shad-checkbox
|
|
508
|
+
:name="`remove-org-${org.docId}`"
|
|
509
|
+
:model-value="state.removeOrgIds.includes(org.docId)"
|
|
510
|
+
@update:model-value="val => updateRemoveOrgSelection(org.docId, val)"
|
|
511
|
+
>
|
|
512
|
+
{{ org.name }}
|
|
513
|
+
</edge-shad-checkbox>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
398
516
|
<DialogFooter class="pt-6 flex justify-between">
|
|
399
517
|
<edge-shad-button class="text-white bg-slate-800 hover:bg-slate-400" @click="state.deleteDialog = false">
|
|
400
518
|
Cancel
|
|
401
519
|
</edge-shad-button>
|
|
402
520
|
<edge-shad-button
|
|
403
|
-
:disabled="
|
|
521
|
+
:disabled="selfRemoveBlocked"
|
|
404
522
|
class="w-full"
|
|
405
523
|
variant="destructive"
|
|
406
524
|
@click="deleteAction()"
|
|
@@ -437,6 +555,38 @@ onBeforeMount(async () => {
|
|
|
437
555
|
:parent-tracker-id="`inviteUser-${state.workingItem.id}`"
|
|
438
556
|
:disabled="state.workingItem.userId === edgeFirebase.user.uid"
|
|
439
557
|
/>
|
|
558
|
+
<div v-if="state.saveButton !== 'Invite User' && showEditOrgSelect" class="mt-4 w-full">
|
|
559
|
+
<div class="text-sm font-medium text-foreground">
|
|
560
|
+
Organizations
|
|
561
|
+
</div>
|
|
562
|
+
<div class="mt-2 w-full flex flex-wrap gap-2">
|
|
563
|
+
<div v-for="org in editOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
|
|
564
|
+
<edge-shad-checkbox
|
|
565
|
+
:name="`edit-add-org-${org.docId}`"
|
|
566
|
+
:model-value="state.editOrgIds.includes(org.docId)"
|
|
567
|
+
@update:model-value="val => updateEditOrgSelection(org.docId, val)"
|
|
568
|
+
>
|
|
569
|
+
{{ org.name }}
|
|
570
|
+
</edge-shad-checkbox>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
</div>
|
|
574
|
+
<div v-if="state.saveButton === 'Invite User' && showInviteOrgSelect" class="mt-4 w-full">
|
|
575
|
+
<div class="text-sm font-medium text-foreground">
|
|
576
|
+
Add to organizations
|
|
577
|
+
</div>
|
|
578
|
+
<div class="mt-2 w-full flex flex-wrap gap-2">
|
|
579
|
+
<div v-for="org in inviteOrgOptions" :key="org.docId" class="flex-1 min-w-[220px]">
|
|
580
|
+
<edge-shad-checkbox
|
|
581
|
+
:name="`invite-org-${org.docId}`"
|
|
582
|
+
:model-value="state.inviteOrgIds.includes(org.docId)"
|
|
583
|
+
@update:model-value="val => updateInviteOrgSelection(org.docId, val)"
|
|
584
|
+
>
|
|
585
|
+
{{ org.name }}
|
|
586
|
+
</edge-shad-checkbox>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
589
|
+
</div>
|
|
440
590
|
<edge-g-input
|
|
441
591
|
v-if="state.saveButton === 'Invite User'"
|
|
442
592
|
v-model="state.workingItem.meta.email"
|
|
@@ -163,6 +163,10 @@ const modelValue = useVModel(props, 'modelValue', emits, {
|
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
const editor = ref(null)
|
|
166
|
+
const enterKeyHandler = (event) => {
|
|
167
|
+
if (event.key === 'Enter')
|
|
168
|
+
event.stopPropagation()
|
|
169
|
+
}
|
|
166
170
|
const imageState = reactive({
|
|
167
171
|
active: false,
|
|
168
172
|
size: 'medium',
|
|
@@ -279,6 +283,7 @@ onMounted(() => {
|
|
|
279
283
|
})
|
|
280
284
|
editor.value.on('selectionUpdate', updateImageState)
|
|
281
285
|
editor.value.on('transaction', updateImageState)
|
|
286
|
+
editor.value?.view?.dom?.addEventListener('keydown', enterKeyHandler)
|
|
282
287
|
updateImageState()
|
|
283
288
|
applyHeightClasses()
|
|
284
289
|
})
|
|
@@ -287,6 +292,7 @@ onBeforeUnmount(() => {
|
|
|
287
292
|
if (editor.value) {
|
|
288
293
|
editor.value.off('selectionUpdate', updateImageState)
|
|
289
294
|
editor.value.off('transaction', updateImageState)
|
|
295
|
+
editor.value?.view?.dom?.removeEventListener('keydown', enterKeyHandler)
|
|
290
296
|
editor.value.destroy()
|
|
291
297
|
}
|
|
292
298
|
})
|
|
@@ -16,7 +16,7 @@ const props = defineProps({
|
|
|
16
16
|
},
|
|
17
17
|
organizationTitle: {
|
|
18
18
|
type: String,
|
|
19
|
-
default: 'Organization
|
|
19
|
+
default: 'Organization',
|
|
20
20
|
},
|
|
21
21
|
menuItems: {
|
|
22
22
|
type: Array,
|
|
@@ -86,7 +86,8 @@ onMounted(() => {
|
|
|
86
86
|
try {
|
|
87
87
|
devOverride.value = localStorage.getItem(DEV_OVERRIDE_KEY) === '1'
|
|
88
88
|
edgeGlobal.edgeState.devOverride = devOverride.value
|
|
89
|
-
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
90
91
|
console.warn('dev override read failed', error)
|
|
91
92
|
}
|
|
92
93
|
})
|
|
@@ -97,10 +98,12 @@ const toggleDevOverride = () => {
|
|
|
97
98
|
try {
|
|
98
99
|
if (devOverride.value) {
|
|
99
100
|
localStorage.setItem(DEV_OVERRIDE_KEY, '1')
|
|
100
|
-
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
101
103
|
localStorage.removeItem(DEV_OVERRIDE_KEY)
|
|
102
104
|
}
|
|
103
|
-
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
104
107
|
console.warn('dev override write failed', error)
|
|
105
108
|
}
|
|
106
109
|
}
|
|
@@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'
|
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
title: {
|
|
5
5
|
type: String,
|
|
6
|
-
default: 'Organization
|
|
6
|
+
default: 'Organization',
|
|
7
7
|
},
|
|
8
8
|
buttonClass: {
|
|
9
9
|
type: String,
|
|
@@ -26,6 +26,20 @@ const isAdmin = computed(() => {
|
|
|
26
26
|
|
|
27
27
|
return orgRole && orgRole.role === 'admin'
|
|
28
28
|
})
|
|
29
|
+
const organizations = computed(() => edgeGlobal.edgeState.organizations || [])
|
|
30
|
+
const currentOrg = computed(() => organizations.value.find(org => org.docId === edgeGlobal.edgeState.currentOrganization))
|
|
31
|
+
const currentOrgName = computed(() => currentOrg.value?.name || 'Organization')
|
|
32
|
+
const hasMultipleOrgs = computed(() => organizations.value.length > 1)
|
|
33
|
+
const orgDialogOpen = ref(false)
|
|
34
|
+
const openOrgDialog = () => {
|
|
35
|
+
if (hasMultipleOrgs.value) {
|
|
36
|
+
orgDialogOpen.value = true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const selectOrg = (orgId) => {
|
|
40
|
+
edgeGlobal.setOrganization(orgId, edgeFirebase)
|
|
41
|
+
orgDialogOpen.value = false
|
|
42
|
+
}
|
|
29
43
|
const route = useRoute()
|
|
30
44
|
const router = useRouter()
|
|
31
45
|
const goTo = (path) => {
|
|
@@ -69,19 +83,16 @@ const firstPart = computed(() => {
|
|
|
69
83
|
<DropdownMenuLabel v-if="!props.singleOrg" class="text-xs text-muted-foreground">
|
|
70
84
|
{{ props.title }}
|
|
71
85
|
</DropdownMenuLabel>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<div v-else class="h-3 w-3 mr-2" />
|
|
83
|
-
</DropdownMenuItem>
|
|
84
|
-
</template>
|
|
86
|
+
<DropdownMenuItem
|
|
87
|
+
v-if="!props.singleOrg"
|
|
88
|
+
class="cursor-pointer"
|
|
89
|
+
:disabled="!hasMultipleOrgs"
|
|
90
|
+
@click="openOrgDialog"
|
|
91
|
+
>
|
|
92
|
+
<span class="truncate max-w-[180px]">{{ currentOrgName }}</span>
|
|
93
|
+
<span v-if="hasMultipleOrgs" class="ml-auto text-xs text-muted-foreground">Switch</span>
|
|
94
|
+
<ChevronsUpDown v-if="hasMultipleOrgs" class="h-4 w-4 ml-2" />
|
|
95
|
+
</DropdownMenuItem>
|
|
85
96
|
<template v-if="isAdmin">
|
|
86
97
|
<DropdownMenuSeparator />
|
|
87
98
|
<DropdownMenuLabel class="text-xs text-muted-foreground">
|
|
@@ -145,4 +156,29 @@ const firstPart = computed(() => {
|
|
|
145
156
|
</DropdownMenuItem>
|
|
146
157
|
</DropdownMenuContent>
|
|
147
158
|
</DropdownMenu>
|
|
159
|
+
|
|
160
|
+
<edge-shad-dialog v-model="orgDialogOpen">
|
|
161
|
+
<DialogContent class="w-full max-w-3xl max-h-[80vh] flex flex-col">
|
|
162
|
+
<DialogHeader>
|
|
163
|
+
<DialogTitle>Select Organization</DialogTitle>
|
|
164
|
+
<DialogDescription class="text-left">
|
|
165
|
+
Choose an organization to switch context.
|
|
166
|
+
</DialogDescription>
|
|
167
|
+
</DialogHeader>
|
|
168
|
+
<div class="mt-4 flex-1 overflow-y-auto">
|
|
169
|
+
<div class="space-y-2">
|
|
170
|
+
<edge-shad-button
|
|
171
|
+
v-for="org in organizations"
|
|
172
|
+
:key="org.docId"
|
|
173
|
+
variant="ghost"
|
|
174
|
+
class="w-full justify-between"
|
|
175
|
+
@click="selectOrg(org.docId)"
|
|
176
|
+
>
|
|
177
|
+
<span class="truncate text-left">{{ org.name }}</span>
|
|
178
|
+
<Check v-if="org.docId === edgeGlobal.edgeState.currentOrganization" class="h-4 w-4" />
|
|
179
|
+
</edge-shad-button>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</DialogContent>
|
|
183
|
+
</edge-shad-dialog>
|
|
148
184
|
</template>
|