@edgedev/create-edge-app 1.1.29 → 1.2.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/agents.md +2 -0
- package/bin/cli.js +13 -5
- package/deploy-services.sh +237 -0
- package/deploy.sh +88 -1
- package/edge/components/cms/blockEditor.vue +748 -7
- package/edge/components/cms/menu.vue +100 -15
- package/edge/components/cms/site.vue +83 -3
- package/edge/components/cms/themeEditor.vue +9 -3
- package/edge/components/dashboard.vue +22 -3
- package/edge/components/organizationMembers.vue +294 -221
- package/edge/components/shad/combobox.vue +2 -2
- package/edge/composables/global.ts +1 -1
- package/firebase_init.sh +63 -2
- package/package.json +1 -1
- package/services/.deploy.shared.env.example +12 -0
|
@@ -382,12 +382,82 @@ const resolveBlockForPreview = (block) => {
|
|
|
382
382
|
return null
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
const
|
|
385
|
+
const normalizePreviewColumns = (row) => {
|
|
386
|
+
if (!Array.isArray(row?.columns) || !row.columns.length)
|
|
387
|
+
return []
|
|
388
|
+
return row.columns.map((column, idx) => ({
|
|
389
|
+
id: column?.id || `${row?.id || 'row'}-col-${idx}`,
|
|
390
|
+
span: Number(column?.span) || null,
|
|
391
|
+
blocks: Array.isArray(column?.blocks) ? column.blocks.filter(Boolean) : [],
|
|
392
|
+
}))
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const templatePreviewRows = (template) => {
|
|
396
|
+
const structureRows = Array.isArray(template?.structure) ? template.structure : []
|
|
397
|
+
if (structureRows.length) {
|
|
398
|
+
return structureRows
|
|
399
|
+
.map((row, rowIndex) => ({
|
|
400
|
+
id: row?.id || `${template?.docId || 'template'}-row-${rowIndex}`,
|
|
401
|
+
columns: normalizePreviewColumns(row),
|
|
402
|
+
}))
|
|
403
|
+
.filter(row => row.columns.length > 0)
|
|
404
|
+
}
|
|
386
405
|
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
406
|
+
const legacyBlocks = Array.isArray(template?.content) ? template.content.filter(Boolean) : []
|
|
407
|
+
if (!legacyBlocks.length)
|
|
389
408
|
return []
|
|
390
|
-
return
|
|
409
|
+
return [{
|
|
410
|
+
id: `${template?.docId || 'template'}-legacy-row`,
|
|
411
|
+
columns: [{
|
|
412
|
+
id: `${template?.docId || 'template'}-legacy-col`,
|
|
413
|
+
span: null,
|
|
414
|
+
blocks: legacyBlocks,
|
|
415
|
+
}],
|
|
416
|
+
}]
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const templateHasPreview = template => templatePreviewRows(template).length > 0
|
|
420
|
+
|
|
421
|
+
const resolveTemplateBlockSource = (template, blockRef) => {
|
|
422
|
+
if (!blockRef)
|
|
423
|
+
return null
|
|
424
|
+
if (typeof blockRef === 'object')
|
|
425
|
+
return blockRef
|
|
426
|
+
const lookupId = String(blockRef).trim()
|
|
427
|
+
if (!lookupId)
|
|
428
|
+
return null
|
|
429
|
+
const templateBlocks = Array.isArray(template?.content) ? template.content : []
|
|
430
|
+
return templateBlocks.find(block => block?.id === lookupId || block?.blockId === lookupId) || null
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const resolveTemplateBlockForPreview = (template, blockRef) => {
|
|
434
|
+
const source = resolveTemplateBlockSource(template, blockRef)
|
|
435
|
+
return resolveBlockForPreview(source)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const hasPreviewSpans = row => (row?.columns || []).some(column => Number.isFinite(Number(column?.span)))
|
|
439
|
+
|
|
440
|
+
const previewGridClass = (row) => {
|
|
441
|
+
if (hasPreviewSpans(row))
|
|
442
|
+
return 'grid grid-cols-1 sm:grid-cols-6 gap-4'
|
|
443
|
+
const count = row?.columns?.length || 1
|
|
444
|
+
const map = {
|
|
445
|
+
1: 'grid grid-cols-1 gap-4',
|
|
446
|
+
2: 'grid grid-cols-1 sm:grid-cols-2 gap-4',
|
|
447
|
+
3: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4',
|
|
448
|
+
4: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4',
|
|
449
|
+
5: 'grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-5 gap-4',
|
|
450
|
+
6: 'grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-6 gap-4',
|
|
451
|
+
}
|
|
452
|
+
return map[count] || map[1]
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const previewColumnStyle = (column) => {
|
|
456
|
+
const span = Number(column?.span)
|
|
457
|
+
if (!Number.isFinite(span))
|
|
458
|
+
return {}
|
|
459
|
+
const safeSpan = Math.min(Math.max(span, 1), 6)
|
|
460
|
+
return { gridColumn: `span ${safeSpan} / span ${safeSpan}` }
|
|
391
461
|
}
|
|
392
462
|
|
|
393
463
|
const renameFolderOrPageShow = (item) => {
|
|
@@ -1155,19 +1225,34 @@ const theme = computed(() => {
|
|
|
1155
1225
|
Blank page
|
|
1156
1226
|
</div>
|
|
1157
1227
|
</template>
|
|
1158
|
-
<template v-else-if="
|
|
1228
|
+
<template v-else-if="templateHasPreview(template)">
|
|
1159
1229
|
<div
|
|
1160
|
-
v-for="(
|
|
1161
|
-
:key="`${template.docId}-
|
|
1230
|
+
v-for="(row, rowIndex) in templatePreviewRows(template)"
|
|
1231
|
+
:key="`${template.docId}-row-${row.id || rowIndex}`"
|
|
1232
|
+
class="w-full"
|
|
1162
1233
|
>
|
|
1163
|
-
<
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1234
|
+
<div :class="previewGridClass(row)">
|
|
1235
|
+
<div
|
|
1236
|
+
v-for="(column, colIndex) in row.columns"
|
|
1237
|
+
:key="`${template.docId}-row-${row.id || rowIndex}-col-${column.id || colIndex}`"
|
|
1238
|
+
class="min-w-0"
|
|
1239
|
+
:style="previewColumnStyle(column)"
|
|
1240
|
+
>
|
|
1241
|
+
<div
|
|
1242
|
+
v-for="(blockRef, blockIdx) in column.blocks || []"
|
|
1243
|
+
:key="`${template.docId}-row-${row.id || rowIndex}-col-${column.id || colIndex}-block-${blockIdx}`"
|
|
1244
|
+
>
|
|
1245
|
+
<edge-cms-block-api
|
|
1246
|
+
v-if="resolveTemplateBlockForPreview(template, blockRef)"
|
|
1247
|
+
:content="resolveTemplateBlockForPreview(template, blockRef).content"
|
|
1248
|
+
:values="resolveTemplateBlockForPreview(template, blockRef).values"
|
|
1249
|
+
:meta="resolveTemplateBlockForPreview(template, blockRef).meta"
|
|
1250
|
+
:theme="theme"
|
|
1251
|
+
:isolated="true"
|
|
1252
|
+
/>
|
|
1253
|
+
</div>
|
|
1254
|
+
</div>
|
|
1255
|
+
</div>
|
|
1171
1256
|
</div>
|
|
1172
1257
|
</template>
|
|
1173
1258
|
<template v-else>
|
|
@@ -14,6 +14,11 @@ const props = defineProps({
|
|
|
14
14
|
required: false,
|
|
15
15
|
default: '',
|
|
16
16
|
},
|
|
17
|
+
disableAddSiteForNonAdmin: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
required: false,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
17
22
|
})
|
|
18
23
|
const edgeFirebase = inject('edgeFirebase')
|
|
19
24
|
const { createDefaults: createSiteSettingsDefaults, createNewDocSchema: createSiteSettingsNewDocSchema } = useSiteSettingsTemplate()
|
|
@@ -137,6 +142,17 @@ const schemas = {
|
|
|
137
142
|
const isAdmin = computed(() => {
|
|
138
143
|
return edgeGlobal.isAdminGlobal(edgeFirebase).value
|
|
139
144
|
})
|
|
145
|
+
const currentOrgRoleName = computed(() => {
|
|
146
|
+
return String(edgeGlobal.getRoleName(edgeFirebase?.user?.roles || [], edgeGlobal.edgeState.currentOrganization) || '').toLowerCase()
|
|
147
|
+
})
|
|
148
|
+
const isOrgAdmin = computed(() => {
|
|
149
|
+
return currentOrgRoleName.value === 'admin'
|
|
150
|
+
})
|
|
151
|
+
const canCreateSite = computed(() => {
|
|
152
|
+
if (!props.disableAddSiteForNonAdmin)
|
|
153
|
+
return true
|
|
154
|
+
return isOrgAdmin.value
|
|
155
|
+
})
|
|
140
156
|
|
|
141
157
|
const siteData = computed(() => {
|
|
142
158
|
return edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`]?.[props.site] || {}
|
|
@@ -362,6 +378,65 @@ const userOptions = computed(() => {
|
|
|
362
378
|
}))
|
|
363
379
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
364
380
|
})
|
|
381
|
+
const authUid = computed(() => String(edgeFirebase?.user?.uid || '').trim())
|
|
382
|
+
const currentOrgUser = computed(() => {
|
|
383
|
+
if (!authUid.value)
|
|
384
|
+
return null
|
|
385
|
+
const users = Object.values(orgUsers.value || {})
|
|
386
|
+
return users.find((user) => {
|
|
387
|
+
const userId = String(user?.userId || '').trim()
|
|
388
|
+
const docId = String(user?.docId || '').trim()
|
|
389
|
+
const uid = String(user?.uid || '').trim()
|
|
390
|
+
return userId === authUid.value || docId === authUid.value || uid === authUid.value
|
|
391
|
+
}) || null
|
|
392
|
+
})
|
|
393
|
+
const currentOrgUserId = computed(() => {
|
|
394
|
+
return String(
|
|
395
|
+
currentOrgUser.value?.userId
|
|
396
|
+
|| currentOrgUser.value?.docId
|
|
397
|
+
|| authUid.value
|
|
398
|
+
|| '',
|
|
399
|
+
).trim()
|
|
400
|
+
})
|
|
401
|
+
const currentUserOption = computed(() => {
|
|
402
|
+
if (!currentOrgUserId.value)
|
|
403
|
+
return null
|
|
404
|
+
return {
|
|
405
|
+
value: currentOrgUserId.value,
|
|
406
|
+
label: currentOrgUser.value?.meta?.name || currentOrgUser.value?.meta?.email || currentOrgUserId.value,
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
const shouldForceCurrentUserForNewSite = computed(() => !isAdmin.value && props.site === 'new')
|
|
410
|
+
const aiUserOptions = computed(() => {
|
|
411
|
+
if (!shouldForceCurrentUserForNewSite.value)
|
|
412
|
+
return userOptions.value
|
|
413
|
+
return currentUserOption.value ? [currentUserOption.value] : []
|
|
414
|
+
})
|
|
415
|
+
const normalizeUserIds = items => (Array.isArray(items) ? items : [])
|
|
416
|
+
.map(item => String(item || '').trim())
|
|
417
|
+
.filter(Boolean)
|
|
418
|
+
const getSiteUsersModel = (workingDoc) => {
|
|
419
|
+
if (!workingDoc || typeof workingDoc !== 'object')
|
|
420
|
+
return []
|
|
421
|
+
const users = normalizeUserIds(workingDoc?.users)
|
|
422
|
+
if (!shouldForceCurrentUserForNewSite.value)
|
|
423
|
+
return users
|
|
424
|
+
if (!currentOrgUserId.value)
|
|
425
|
+
return users
|
|
426
|
+
if (users.length === 1 && users[0] === currentOrgUserId.value)
|
|
427
|
+
return users
|
|
428
|
+
workingDoc.users = [currentOrgUserId.value]
|
|
429
|
+
return workingDoc.users
|
|
430
|
+
}
|
|
431
|
+
const updateSiteUsersModel = (workingDoc, value) => {
|
|
432
|
+
if (!workingDoc || typeof workingDoc !== 'object')
|
|
433
|
+
return
|
|
434
|
+
if (shouldForceCurrentUserForNewSite.value) {
|
|
435
|
+
workingDoc.users = currentOrgUserId.value ? [currentOrgUserId.value] : []
|
|
436
|
+
return
|
|
437
|
+
}
|
|
438
|
+
workingDoc.users = normalizeUserIds(value)
|
|
439
|
+
}
|
|
365
440
|
|
|
366
441
|
const themeItemsForAllowed = (allowed, current) => {
|
|
367
442
|
const base = themeOptions.value
|
|
@@ -1133,7 +1208,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1133
1208
|
v-if="edgeGlobal.edgeState.organizationDocPath"
|
|
1134
1209
|
>
|
|
1135
1210
|
<edge-editor
|
|
1136
|
-
v-if="!props.page && props.site === 'new'"
|
|
1211
|
+
v-if="!props.page && props.site === 'new' && canCreateSite"
|
|
1137
1212
|
collection="sites"
|
|
1138
1213
|
:doc-id="props.site"
|
|
1139
1214
|
:schema="schemas.sites"
|
|
@@ -1215,7 +1290,8 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1215
1290
|
/>
|
|
1216
1291
|
<edge-shad-select-tags
|
|
1217
1292
|
v-if="Object.keys(orgUsers).length > 0"
|
|
1218
|
-
|
|
1293
|
+
:model-value="getSiteUsersModel(slotProps.workingDoc)"
|
|
1294
|
+
:disabled="shouldForceCurrentUserForNewSite || !edgeGlobal.isAdminGlobal(edgeFirebase).value"
|
|
1219
1295
|
:items="userOptions"
|
|
1220
1296
|
name="users"
|
|
1221
1297
|
label="Users"
|
|
@@ -1224,6 +1300,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1224
1300
|
placeholder="Select users"
|
|
1225
1301
|
class="w-full"
|
|
1226
1302
|
:multiple="true"
|
|
1303
|
+
@update:model-value="value => updateSiteUsersModel(slotProps.workingDoc, value)"
|
|
1227
1304
|
/>
|
|
1228
1305
|
<div class="rounded-lg border border-dashed border-slate-200 p-4 ">
|
|
1229
1306
|
<div class="flex items-start justify-between gap-3">
|
|
@@ -1248,7 +1325,7 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1248
1325
|
label="User Data for AI to use to build initial site"
|
|
1249
1326
|
placeholder="- select one -"
|
|
1250
1327
|
class="w-full"
|
|
1251
|
-
:items="
|
|
1328
|
+
:items="aiUserOptions"
|
|
1252
1329
|
item-title="label"
|
|
1253
1330
|
item-value="value"
|
|
1254
1331
|
@update:model-value="value => (slotProps.workingDoc.aiAgentUserId = value || '')"
|
|
@@ -1265,6 +1342,9 @@ const pageSettingsUpdated = async (pageData) => {
|
|
|
1265
1342
|
</div>
|
|
1266
1343
|
</template>
|
|
1267
1344
|
</edge-editor>
|
|
1345
|
+
<div v-else-if="!props.page && props.site === 'new' && !canCreateSite" class="p-6 text-sm text-red-600">
|
|
1346
|
+
Only organization admins can create sites.
|
|
1347
|
+
</div>
|
|
1268
1348
|
<div v-else class="flex flex-col h-[calc(100vh-58px)] overflow-hidden">
|
|
1269
1349
|
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-3 px-4 py-2 border-b bg-secondary">
|
|
1270
1350
|
<div class="flex items-center gap-3">
|
|
@@ -132,7 +132,10 @@ watch(headObject, (newHeadElements) => {
|
|
|
132
132
|
}, { immediate: true, deep: true })
|
|
133
133
|
|
|
134
134
|
const sites = computed(() => {
|
|
135
|
-
|
|
135
|
+
const sitesMap = edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`] || {}
|
|
136
|
+
return Object.entries(sitesMap)
|
|
137
|
+
.map(([docId, data]) => ({ docId, ...(data || {}) }))
|
|
138
|
+
.filter(site => site.docId && site.docId !== 'templates')
|
|
136
139
|
})
|
|
137
140
|
|
|
138
141
|
const templatePages = computed(() => {
|
|
@@ -324,9 +327,12 @@ const templatePageOptions = computed(() => {
|
|
|
324
327
|
|
|
325
328
|
watch (sites, async (newSites) => {
|
|
326
329
|
state.loading = true
|
|
327
|
-
|
|
330
|
+
const selectedSite = String(edgeGlobal.edgeState.blockEditorSite || '').trim()
|
|
331
|
+
const hasSelectedSite = newSites.some(site => site.docId === selectedSite)
|
|
332
|
+
if ((!selectedSite || !hasSelectedSite) && newSites.length > 0)
|
|
328
333
|
edgeGlobal.edgeState.blockEditorSite = newSites[0].docId
|
|
329
|
-
|
|
334
|
+
else if (!newSites.length)
|
|
335
|
+
edgeGlobal.edgeState.blockEditorSite = ''
|
|
330
336
|
await nextTick()
|
|
331
337
|
state.loading = false
|
|
332
338
|
}, { immediate: true, deep: true })
|
|
@@ -170,6 +170,20 @@ const getByPath = (obj, path) => {
|
|
|
170
170
|
}, obj)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
const normalizeCollectionItems = (collectionMap = {}) => {
|
|
174
|
+
const items = Object.entries(collectionMap).map(([docId, data]) => {
|
|
175
|
+
const source = (data && typeof data === 'object') ? data : {}
|
|
176
|
+
return {
|
|
177
|
+
...source,
|
|
178
|
+
docId: String(source.docId || docId || ''),
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
if (props.collection === 'sites') {
|
|
182
|
+
return items.filter(item => item.docId !== 'templates')
|
|
183
|
+
}
|
|
184
|
+
return items
|
|
185
|
+
}
|
|
186
|
+
|
|
173
187
|
const snapShotQuery = computed(() => {
|
|
174
188
|
if (state.queryField && state.queryValue) {
|
|
175
189
|
// console.log('snapShotQuery', state.queryField, state.queryOperator, state.queryValue)
|
|
@@ -232,7 +246,10 @@ const allData = computed(() => {
|
|
|
232
246
|
if (!edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`]) {
|
|
233
247
|
return []
|
|
234
248
|
}
|
|
235
|
-
return
|
|
249
|
+
return normalizeCollectionItems(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`])
|
|
250
|
+
}
|
|
251
|
+
if (props.collection === 'sites') {
|
|
252
|
+
return state.paginatedResults.filter(item => String(item?.docId || '') !== 'templates')
|
|
236
253
|
}
|
|
237
254
|
return state.paginatedResults
|
|
238
255
|
})
|
|
@@ -243,10 +260,12 @@ const filtered = computed(() => {
|
|
|
243
260
|
if (!edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`]) {
|
|
244
261
|
return []
|
|
245
262
|
}
|
|
246
|
-
allData =
|
|
263
|
+
allData = normalizeCollectionItems(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`])
|
|
247
264
|
}
|
|
248
265
|
else {
|
|
249
|
-
allData =
|
|
266
|
+
allData = props.collection === 'sites'
|
|
267
|
+
? state.paginatedResults.filter(item => String(item?.docId || '') !== 'templates')
|
|
268
|
+
: state.paginatedResults
|
|
250
269
|
}
|
|
251
270
|
|
|
252
271
|
const qRaw = filterText.value
|