@edgedev/create-edge-app 1.1.23 → 1.1.26
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/.env +1 -0
- package/.env.dev +1 -0
- package/README.md +55 -20
- package/{agent.md → agents.md} +2 -0
- package/bin/cli.js +6 -6
- package/edge/components/auth/login.vue +384 -0
- package/edge/components/auth/register.vue +396 -0
- package/edge/components/auth.vue +108 -0
- package/edge/components/autoFileUpload.vue +215 -0
- package/edge/components/billing.vue +8 -0
- package/edge/components/buttonDivider.vue +14 -0
- package/edge/components/chip.vue +34 -0
- package/edge/components/clipboardButton.vue +42 -0
- package/edge/components/cms/block.vue +529 -0
- package/edge/components/cms/blockApi.vue +212 -0
- package/edge/components/cms/blockEditor.vue +725 -0
- package/edge/components/cms/blockInput.vue +66 -0
- package/edge/components/cms/blockPicker.vue +486 -0
- package/edge/components/cms/blockRender.vue +78 -0
- package/edge/components/cms/blockSheetContent.vue +28 -0
- package/edge/components/cms/codeEditor.vue +466 -0
- package/edge/components/cms/fontUpload.vue +327 -0
- package/edge/components/cms/htmlContent.vue +807 -0
- package/edge/components/cms/init_blocks/api_with_subarrays.html +17 -0
- package/edge/components/cms/init_blocks/array_with_collection.html +7 -0
- package/edge/components/cms/init_blocks/array_with_objects.html +7 -0
- package/edge/components/cms/init_blocks/carousel.html +103 -0
- package/edge/components/cms/init_blocks/contact_us.html +69 -0
- package/edge/components/cms/init_blocks/content_with_left_image.html +27 -0
- package/edge/components/cms/init_blocks/footer.html +24 -0
- package/edge/components/cms/init_blocks/header_divider.html +7 -0
- package/edge/components/cms/init_blocks/hero.html +35 -0
- package/edge/components/cms/init_blocks/hero_carousel.html +52 -0
- package/edge/components/cms/init_blocks/newsletter.html +117 -0
- package/edge/components/cms/init_blocks/post_content.html +7 -0
- package/edge/components/cms/init_blocks/post_title_header.html +21 -0
- package/edge/components/cms/init_blocks/posts_list.html +20 -0
- package/edge/components/cms/init_blocks/properties_showcase.html +100 -0
- package/edge/components/cms/init_blocks/property_carousel.html +59 -0
- package/edge/components/cms/init_blocks/property_detail.html +112 -0
- package/edge/components/cms/init_blocks/property_detail_header.html +34 -0
- package/edge/components/cms/init_blocks/property_results.html +137 -0
- package/edge/components/cms/init_blocks/property_search.html +75 -0
- package/edge/components/cms/init_blocks/simple_array.html +7 -0
- package/edge/components/cms/mediaCard.vue +116 -0
- package/edge/components/cms/mediaManager.vue +386 -0
- package/edge/components/cms/menu.vue +1103 -0
- package/edge/components/cms/optionsSelect.vue +107 -0
- package/edge/components/cms/page.vue +1785 -0
- package/edge/components/cms/posts.vue +1083 -0
- package/edge/components/cms/site.vue +1298 -0
- package/edge/components/cms/themeDefaultMenu.vue +548 -0
- package/edge/components/cms/themeEditor.vue +426 -0
- package/edge/components/dashboard.vue +776 -0
- package/edge/components/editor.vue +671 -0
- package/edge/components/fileTree.vue +72 -0
- package/edge/components/files.vue +89 -0
- package/edge/components/formSubtypes/myOrgs.vue +214 -0
- package/edge/components/formSubtypes/users.vue +336 -0
- package/edge/components/functionChips.vue +57 -0
- package/edge/components/gError.vue +98 -0
- package/edge/components/gHelper.vue +67 -0
- package/edge/components/gInput.vue +1331 -0
- package/edge/components/loggingIn.vue +41 -0
- package/edge/components/menu.vue +137 -0
- package/edge/components/menuContent.vue +132 -0
- package/edge/components/myAccount.vue +317 -0
- package/edge/components/myOrganizations.vue +75 -0
- package/edge/components/myProfile.vue +122 -0
- package/edge/components/orgSwitcher.vue +25 -0
- package/edge/components/organizationMembers.vue +522 -0
- package/edge/components/organizationSettings.vue +271 -0
- package/edge/components/shad/breadcrumbs.vue +35 -0
- package/edge/components/shad/button.vue +43 -0
- package/edge/components/shad/checkbox.vue +73 -0
- package/edge/components/shad/combobox.vue +238 -0
- package/edge/components/shad/datepicker.vue +184 -0
- package/edge/components/shad/dialog.vue +32 -0
- package/edge/components/shad/dropdownMenu.vue +54 -0
- package/edge/components/shad/dropdownMenuItem.vue +21 -0
- package/edge/components/shad/form.vue +59 -0
- package/edge/components/shad/html.vue +877 -0
- package/edge/components/shad/input.vue +139 -0
- package/edge/components/shad/number.vue +109 -0
- package/edge/components/shad/select.vue +151 -0
- package/edge/components/shad/selectTags.vue +278 -0
- package/edge/components/shad/switch.vue +67 -0
- package/edge/components/shad/tags.vue +137 -0
- package/edge/components/shad/textarea.vue +102 -0
- package/edge/components/shad/typeMoney.vue +167 -0
- package/edge/components/sideBar.vue +288 -0
- package/edge/components/sideBarContent.vue +268 -0
- package/edge/components/sidebarProvider.vue +33 -0
- package/edge/components/tooltip.vue +16 -0
- package/edge/components/userMenu.vue +148 -0
- package/edge/components/v/alert.vue +59 -0
- package/edge/components/v/alertTitle.vue +18 -0
- package/edge/components/v/card.vue +53 -0
- package/edge/components/v/cardActions.vue +18 -0
- package/edge/components/v/cardText.vue +18 -0
- package/edge/components/v/cardTitle.vue +20 -0
- package/edge/components/v/col.vue +56 -0
- package/edge/components/v/list.vue +46 -0
- package/edge/components/v/listItem.vue +26 -0
- package/edge/components/v/listItemTitle.vue +18 -0
- package/edge/components/v/row.vue +42 -0
- package/edge/components/v/toolbar.vue +24 -0
- package/edge/composables/global.ts +519 -0
- package/edge-pull.sh +2 -0
- package/edge-push.sh +1 -0
- package/edge-status.sh +14 -0
- package/firebase.json +5 -2
- package/firebase_init.sh +21 -6
- package/package.json +1 -1
- package/plugins/firebase.client.ts +1 -0
- package/edge-components-install.sh +0 -1
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
// DO NOT EDIT
|
|
2
|
+
|
|
3
|
+
const route = useRoute()
|
|
4
|
+
|
|
5
|
+
const edgeState = reactive({
|
|
6
|
+
currentOrganization: '',
|
|
7
|
+
organizationDocPath: '',
|
|
8
|
+
organizations: [],
|
|
9
|
+
changeTracker: {},
|
|
10
|
+
user: null,
|
|
11
|
+
userRoles: [],
|
|
12
|
+
lastPaginatedDoc: null,
|
|
13
|
+
subscribedStatus: null,
|
|
14
|
+
showLeftPanel: {} as Record<string, boolean>,
|
|
15
|
+
menuItems: [],
|
|
16
|
+
isAdminCollections: [] as string[],
|
|
17
|
+
redirectRoute: '',
|
|
18
|
+
isEmulator: false,
|
|
19
|
+
blockEditorTheme: '',
|
|
20
|
+
blockEditorSite: '',
|
|
21
|
+
cmsPageWithUnsavedChanges: null,
|
|
22
|
+
devOverride: false,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const setOrganization = async (organization: string, edgeFirebase: any) => {
|
|
26
|
+
if (organization) {
|
|
27
|
+
edgeState.changeTracker = {}
|
|
28
|
+
localStorage.setItem('organizationID', organization)
|
|
29
|
+
edgeState.currentOrganization = organization
|
|
30
|
+
await edgeFirebase.startUsersSnapshot(`organizations/${organization}`)
|
|
31
|
+
edgeState.organizationDocPath = `organizations/${organization}`
|
|
32
|
+
if (import.meta.env.VITE_FIREBASE_EMULATOR_FIRESTORE) {
|
|
33
|
+
edgeState.isEmulator = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const showLeftPanel = (show: boolean) => {
|
|
39
|
+
edgeState.showLeftPanel[route.path] = show
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const getSubscribedStatus = (org: any) => {
|
|
43
|
+
let isSubscribed = true
|
|
44
|
+
let status = ''
|
|
45
|
+
let description = ''
|
|
46
|
+
let color = ''
|
|
47
|
+
let icon = ''
|
|
48
|
+
|
|
49
|
+
if (!org || !org.stripeSubscription) {
|
|
50
|
+
isSubscribed = false
|
|
51
|
+
status = 'Not Subscribed'
|
|
52
|
+
description = 'No subscription found.'
|
|
53
|
+
color = 'bg-red-900'
|
|
54
|
+
icon = 'AlertCircle'
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const subscription = org.stripeSubscription
|
|
58
|
+
|
|
59
|
+
if (!subscription || subscription === 'canceled') {
|
|
60
|
+
isSubscribed = false
|
|
61
|
+
status = 'Canceled'
|
|
62
|
+
description = 'The subscription has been canceled.'
|
|
63
|
+
color = 'bg-red-600'
|
|
64
|
+
icon = 'X'
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
switch (subscription) {
|
|
68
|
+
case 'trialing':
|
|
69
|
+
status = 'Trial'
|
|
70
|
+
description = 'The subscription is currently in a trial period.'
|
|
71
|
+
color = 'bg-green-600'
|
|
72
|
+
icon = 'Check'
|
|
73
|
+
break
|
|
74
|
+
case 'active':
|
|
75
|
+
status = 'Active'
|
|
76
|
+
description = 'The subscription is in good standing.'
|
|
77
|
+
color = 'bg-green-600'
|
|
78
|
+
icon = 'Check'
|
|
79
|
+
break
|
|
80
|
+
case 'incomplete':
|
|
81
|
+
status = 'Incomplete'
|
|
82
|
+
description = 'A successful payment needs to be made within 23 hours to activate the subscription.'
|
|
83
|
+
color = 'bg-amber-500'
|
|
84
|
+
icon = 'Hourglass'
|
|
85
|
+
break
|
|
86
|
+
case 'incomplete_expired':
|
|
87
|
+
status = 'Incomplete Expired'
|
|
88
|
+
description = 'The initial payment on the subscription failed and no successful payment was made within 23 hours of creating the subscription.'
|
|
89
|
+
color = 'bg-red-600'
|
|
90
|
+
icon = 'AlertCircle'
|
|
91
|
+
break
|
|
92
|
+
case 'past_due':
|
|
93
|
+
status = 'Past Due'
|
|
94
|
+
description = 'Payment on the latest finalized invoice either failed or wasn’t attempted.'
|
|
95
|
+
color = 'bg-red-600'
|
|
96
|
+
icon = 'AlertCircle'
|
|
97
|
+
break
|
|
98
|
+
case 'unpaid':
|
|
99
|
+
status = 'Unpaid'
|
|
100
|
+
description = 'The latest invoice hasn’t been paid but the subscription remains in place.'
|
|
101
|
+
color = 'bg-red-600'
|
|
102
|
+
icon = 'AlertCircle'
|
|
103
|
+
break
|
|
104
|
+
case 'paused':
|
|
105
|
+
status = 'Paused'
|
|
106
|
+
description = 'The subscription has ended its trial period without a default payment method.'
|
|
107
|
+
color = 'bg-amber-500'
|
|
108
|
+
icon = 'PauseCircle'
|
|
109
|
+
break
|
|
110
|
+
default:
|
|
111
|
+
status = 'Unknown'
|
|
112
|
+
description = 'The subscription status is unknown.'
|
|
113
|
+
color = 'bg-red-600'
|
|
114
|
+
icon = 'AlertCircle'
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
isSubscribed,
|
|
122
|
+
status,
|
|
123
|
+
description,
|
|
124
|
+
color,
|
|
125
|
+
icon,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const isDarkMode = () => {
|
|
130
|
+
if (window.matchMedia) {
|
|
131
|
+
const darkMode = window.matchMedia('(prefers-color-scheme: dark)')
|
|
132
|
+
return darkMode.matches
|
|
133
|
+
}
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const generateShortId = () => {
|
|
138
|
+
return Math.random().toString(36).substr(2, 6)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const objHas = (obj: any, key: string): boolean => {
|
|
142
|
+
if (obj === null || obj === undefined) {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
return Object.prototype.hasOwnProperty.call(obj, key)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const getOrganizations = async (edgeFirebase: any) => {
|
|
149
|
+
console.log('getOrganizations')
|
|
150
|
+
const orgs: any = []
|
|
151
|
+
if (edgeFirebase.user.loggedIn) {
|
|
152
|
+
for (const role of edgeFirebase.user.roles) {
|
|
153
|
+
const segments = role.collectionPath.split('-')
|
|
154
|
+
if (segments[0] === 'organizations') {
|
|
155
|
+
await edgeFirebase.startDocumentSnapshot('organizations', segments[1])
|
|
156
|
+
let org = await edgeFirebase.getDocData('organizations', segments[1])
|
|
157
|
+
if (!org?.name) {
|
|
158
|
+
org = { name: 'Organization', docId: segments[1] }
|
|
159
|
+
}
|
|
160
|
+
if (!orgs.some((o: { docId: string }) => o.docId === org.docId)) {
|
|
161
|
+
orgs.push(org)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
edgeState.organizations = orgs
|
|
167
|
+
console.log('Organizations:', edgeState.organizations)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const dupObject = (obj: any): any => {
|
|
171
|
+
// console.log('Duplicating object:', obj)
|
|
172
|
+
return JSON.parse(JSON.stringify(obj))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const currentOrganizationObject = computed(() => {
|
|
176
|
+
const edgeFirebase: any = inject('edgeFirebase')
|
|
177
|
+
if (edgeState.organizations.length > 0) {
|
|
178
|
+
if (edgeState.currentOrganization && edgeFirebase?.data[`organizations/${edgeState.currentOrganization}`]) {
|
|
179
|
+
return edgeFirebase?.data[`organizations/${edgeState.currentOrganization}`]
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return ''
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const edgeRules = {
|
|
186
|
+
forms: (value: any) => {
|
|
187
|
+
if (!value.length) {
|
|
188
|
+
return 'You must setup at least one form.'
|
|
189
|
+
}
|
|
190
|
+
return true
|
|
191
|
+
},
|
|
192
|
+
submits: (value: any) => {
|
|
193
|
+
if (!value.length) {
|
|
194
|
+
return 'You must setup at least one submit.'
|
|
195
|
+
}
|
|
196
|
+
return true
|
|
197
|
+
},
|
|
198
|
+
gptFunctionName: (value: string) => {
|
|
199
|
+
const pattern = /^[a-zA-Z0-9_-]{1,64}$/
|
|
200
|
+
return pattern.test(value) || 'The function name must be 1-64 characters and can only contain letters, numbers, underscores, and dashes.'
|
|
201
|
+
},
|
|
202
|
+
endpoint: (value: string) => {
|
|
203
|
+
const urlPattern = /^https?:\/\/(?!:\/\/)([a-zA-Z0-9]+\.)?[a-zA-Z0-9][a-zA-Z0-9-]+(\.[a-zA-Z]{2,6})?(:\d{1,5})?(\/[^\s]*)?$/i
|
|
204
|
+
if (!urlPattern.test(value)) {
|
|
205
|
+
return `"${value}" is not a valid URL. The URL must include the protocol (http or https) and the path.`
|
|
206
|
+
}
|
|
207
|
+
return true
|
|
208
|
+
},
|
|
209
|
+
domains: (value: string) => {
|
|
210
|
+
const domainPattern = /^https?:\/\/(?!:\/\/)([a-zA-Z0-9]+\.)?[a-zA-Z0-9][a-zA-Z0-9-]+(\.[a-zA-Z]{2,6})?(:\d{1,5})?$/i
|
|
211
|
+
const localhostPattern = /^https?:\/\/localhost(:\d{1,5})?$/i
|
|
212
|
+
const ipAddressPattern = /^https?:\/\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d{1,5})?$/
|
|
213
|
+
const domains = value.split(',')
|
|
214
|
+
for (const domain of domains) {
|
|
215
|
+
if (
|
|
216
|
+
!domainPattern.test(domain)
|
|
217
|
+
&& !localhostPattern.test(domain)
|
|
218
|
+
&& !ipAddressPattern.test(domain)
|
|
219
|
+
) {
|
|
220
|
+
return `"${domain}" is not a valid domain or IP address. The domain or IP address must include the protocol (http or https).`
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return true
|
|
224
|
+
},
|
|
225
|
+
required: (value: any) => {
|
|
226
|
+
if (typeof value === 'string' && !value) {
|
|
227
|
+
return 'This field is required.'
|
|
228
|
+
}
|
|
229
|
+
else if (Array.isArray(value) && value.length === 0) {
|
|
230
|
+
return 'This field is required.'
|
|
231
|
+
}
|
|
232
|
+
else if (typeof value === 'object' && value !== null && Object.keys(value).length === 0) {
|
|
233
|
+
return 'This field is required.'
|
|
234
|
+
}
|
|
235
|
+
else if (typeof value === 'boolean' && !value) {
|
|
236
|
+
return 'This field is required.'
|
|
237
|
+
}
|
|
238
|
+
return true
|
|
239
|
+
},
|
|
240
|
+
email: (value: string) => {
|
|
241
|
+
const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
242
|
+
return pattern.test(value) || 'Invalid e-mail.'
|
|
243
|
+
},
|
|
244
|
+
emailOrField: (value: string) => {
|
|
245
|
+
const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
246
|
+
return pattern.test(value) || (value.startsWith('{{') && value.endsWith('}}')) || `Invalid e-mail or field. If you want to use a field, it must be wrapped in double curly braces, e.g. {{${value}}}`
|
|
247
|
+
},
|
|
248
|
+
toEmails: (value: string) => {
|
|
249
|
+
const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
250
|
+
const emails = value.split(',')
|
|
251
|
+
for (const email of emails) {
|
|
252
|
+
if (!pattern.test(email)) {
|
|
253
|
+
return `"${email}" is not a valid email address`
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return true
|
|
257
|
+
},
|
|
258
|
+
emailsOrFields: (value: string) => {
|
|
259
|
+
const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
260
|
+
const emails = value.split(',')
|
|
261
|
+
for (const email of emails) {
|
|
262
|
+
if (!pattern.test(email) && !(email.startsWith('{{') && email.endsWith('}}'))) {
|
|
263
|
+
return `"${email}" is not a valid email address or field. If you want to use a field, it must be wrapped in double curly braces, e.g. {{${email}}}`
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return true
|
|
267
|
+
},
|
|
268
|
+
password: (value: string) => {
|
|
269
|
+
const pattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
|
270
|
+
return pattern.test(value) || 'Password must have at least 8 characters, including uppercase and lowercase letters, numbers, and a special character'
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const edgeLogOut = async (edgeFirebase: any) => {
|
|
275
|
+
edgeState.currentOrganization = ''
|
|
276
|
+
edgeState.organizationDocPath = ''
|
|
277
|
+
edgeState.organizations = []
|
|
278
|
+
edgeState.changeTracker = {}
|
|
279
|
+
edgeState.user = null
|
|
280
|
+
nextTick(async () => {
|
|
281
|
+
await edgeFirebase.logOut()
|
|
282
|
+
window.location.reload()
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const orgUserRoles = (orgId: string) => {
|
|
287
|
+
orgId = orgId.replaceAll('/', '-')
|
|
288
|
+
const orgPath = `organizations-${orgId}`
|
|
289
|
+
const newData = JSON.parse(JSON.stringify(edgeState.userRoles))
|
|
290
|
+
|
|
291
|
+
for (let i = 0; i < newData.length; i++) {
|
|
292
|
+
const roles = newData[i].roles
|
|
293
|
+
for (let j = 0; j < roles.length; j++) {
|
|
294
|
+
const role = roles[j]
|
|
295
|
+
role.collectionPath = role.collectionPath.replace(/organizationDocPath/g, orgPath)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return newData
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
interface UserRoleType {
|
|
303
|
+
name: string
|
|
304
|
+
roles: { collectionPath: string; role: string }[]
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface RoleType {
|
|
308
|
+
collectionPath: string
|
|
309
|
+
role: string
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const getRoleName = (roles: RoleType[], orgId: string) => {
|
|
313
|
+
const userRoles: UserRoleType[] = orgUserRoles(orgId)
|
|
314
|
+
for (const user of userRoles) {
|
|
315
|
+
let match = true
|
|
316
|
+
for (const userRole of user.roles) {
|
|
317
|
+
if (!roles.some(role => role.collectionPath === userRole.collectionPath && role.role === userRole.role)) {
|
|
318
|
+
match = false
|
|
319
|
+
break
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (match) {
|
|
323
|
+
return user.name
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return 'Unknown'
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const isAdminGlobal = (edgeFirebase: any) => computed(() => {
|
|
330
|
+
const roleCompares = dupObject(edgeState.isAdminCollections)
|
|
331
|
+
roleCompares.push(`organizations-${edgeState.currentOrganization}`)
|
|
332
|
+
console.log('roles compare')
|
|
333
|
+
console.log(roleCompares)
|
|
334
|
+
for (const compare of roleCompares) {
|
|
335
|
+
const orgRole = edgeFirebase?.user?.roles.find((role: any) =>
|
|
336
|
+
role.collectionPath === compare.replaceAll('/', '-'),
|
|
337
|
+
)
|
|
338
|
+
if (orgRole && orgRole.role === 'admin') {
|
|
339
|
+
return true
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return false
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
interface MenuItem {
|
|
346
|
+
to: string
|
|
347
|
+
icon?: string
|
|
348
|
+
submenu?: SubMenuItem[]
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
interface SubMenuItem {
|
|
352
|
+
to: string
|
|
353
|
+
icon?: string
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
interface BestMatch {
|
|
357
|
+
icon: string
|
|
358
|
+
len: number
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const iconFromMenu = (route: { path: string }): string => {
|
|
362
|
+
const normalize = (p: string): string => {
|
|
363
|
+
if (!p)
|
|
364
|
+
return '/'
|
|
365
|
+
const cleaned = p.replace(/\/+$/, '')
|
|
366
|
+
return cleaned.length ? cleaned : '/'
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const current = normalize(route.path)
|
|
370
|
+
let best: BestMatch = { icon: 'LayoutDashboard', len: -1 }
|
|
371
|
+
|
|
372
|
+
for (const item of (edgeState.menuItems || []) as MenuItem[]) {
|
|
373
|
+
const parentTo = normalize(item.to)
|
|
374
|
+
|
|
375
|
+
// 1) Exact submenu match first (wins even if sub.to === item.to)
|
|
376
|
+
if (Array.isArray(item.submenu)) {
|
|
377
|
+
for (const sub of item.submenu) {
|
|
378
|
+
const subTo = normalize(sub.to)
|
|
379
|
+
if (subTo === current) {
|
|
380
|
+
return sub.icon || item.icon || 'LayoutDashboard'
|
|
381
|
+
}
|
|
382
|
+
// Track most specific submenu prefix match
|
|
383
|
+
if (current.startsWith(subTo) && subTo.length > best.len) {
|
|
384
|
+
best = { icon: sub.icon || item.icon || 'LayoutDashboard', len: subTo.length }
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 2) Exact parent match (only if no exact submenu already returned)
|
|
390
|
+
if (parentTo === current) {
|
|
391
|
+
return item.icon || 'LayoutDashboard'
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 3) Track most specific parent prefix match
|
|
395
|
+
if (current.startsWith(parentTo) && parentTo.length > best.len) {
|
|
396
|
+
best = { icon: item.icon || 'LayoutDashboard', len: parentTo.length }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 4) Fallback
|
|
401
|
+
return best.icon
|
|
402
|
+
}
|
|
403
|
+
const toBool = (v: any): boolean => v === true || v === 'true' || v === 1 || v === '1'
|
|
404
|
+
const DEV_OVERRIDE_KEY = 'edgeDevOverride'
|
|
405
|
+
|
|
406
|
+
const devOverrideEnabled = (): boolean => {
|
|
407
|
+
if (edgeState.devOverride)
|
|
408
|
+
return true
|
|
409
|
+
if (typeof window === 'undefined')
|
|
410
|
+
return edgeState.devOverride
|
|
411
|
+
try {
|
|
412
|
+
return localStorage.getItem(DEV_OVERRIDE_KEY) === '1'
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
console.warn('dev override read failed', error)
|
|
416
|
+
return false
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const syncDevOverride = () => {
|
|
421
|
+
if (typeof window === 'undefined')
|
|
422
|
+
return
|
|
423
|
+
edgeState.devOverride = devOverrideEnabled()
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const allowMenuItem = (item: any, isAdmin: boolean) => {
|
|
427
|
+
// const config = useRuntimeConfig()
|
|
428
|
+
const isDev = process.dev || devOverrideEnabled()
|
|
429
|
+
const adminOnly = toBool(item.adminOnly)
|
|
430
|
+
const devOnly = toBool(item.devOnly)
|
|
431
|
+
console.log('allowMenuItem', { item, isAdmin, isDev, adminOnly, devOnly })
|
|
432
|
+
const override = toBool(item.override)
|
|
433
|
+
if (item.override !== undefined)
|
|
434
|
+
return override
|
|
435
|
+
if (adminOnly && !isAdmin)
|
|
436
|
+
return false
|
|
437
|
+
if (devOnly && !isDev)
|
|
438
|
+
return false
|
|
439
|
+
return true
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const cmsCollectionData = async (edgeFirebase: any, value: any, meta: any, currentSite: any = '') => {
|
|
443
|
+
for (const key in meta) {
|
|
444
|
+
if (meta[key]?.collection) {
|
|
445
|
+
const staticSearch = new edgeFirebase.SearchStaticData()
|
|
446
|
+
|
|
447
|
+
const currentQuery = meta[key].collection.query || []
|
|
448
|
+
for (const queryKey in meta[key].queryItems || {}) {
|
|
449
|
+
console.log('key', queryKey)
|
|
450
|
+
if (meta[key].queryItems[queryKey]) {
|
|
451
|
+
const findIndex = currentQuery.findIndex((q: any) => q.field === queryKey)
|
|
452
|
+
const queryOption = meta[key]?.queryOptions?.find((o: any) => o.field === queryKey)
|
|
453
|
+
const operator = queryOption?.operator || '=='
|
|
454
|
+
const newQuery = { field: queryKey, operator, value: meta[key].queryItems[queryKey] }
|
|
455
|
+
if (findIndex > -1) {
|
|
456
|
+
currentQuery[findIndex] = newQuery
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
currentQuery.push(newQuery)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
const findIndex = currentQuery.findIndex((q: any) => q.field === queryKey)
|
|
464
|
+
if (findIndex > -1) {
|
|
465
|
+
currentQuery.splice(findIndex, 1)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
let collectionPath = `${edgeState.organizationDocPath}/${meta[key].collection.path}`
|
|
470
|
+
if (meta[key].collection.path === 'posts' || meta[key].collection.path === 'post') {
|
|
471
|
+
collectionPath = `${edgeState.organizationDocPath}/sites/${currentSite}/published_posts`
|
|
472
|
+
}
|
|
473
|
+
await staticSearch.getData(collectionPath, currentQuery, meta[key].collection.order, meta[key].limit)
|
|
474
|
+
|
|
475
|
+
value[key] = Object.values(staticSearch.results.data)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return value
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const getImage = (file: any, type: string) => {
|
|
482
|
+
const variants = Array.isArray(file?.cloudflareImageVariants)
|
|
483
|
+
? file.cloudflareImageVariants.filter((variant: any) => typeof variant === 'string' && variant.length)
|
|
484
|
+
: []
|
|
485
|
+
if (variants.length) {
|
|
486
|
+
const normalizedType = (type || '').trim().toLowerCase()
|
|
487
|
+
if (normalizedType) {
|
|
488
|
+
const match = variants.find((variant: string) => variant.toLowerCase().endsWith(`/${normalizedType}`))
|
|
489
|
+
if (match) {
|
|
490
|
+
return match
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return variants[0]
|
|
494
|
+
}
|
|
495
|
+
return file?.r2Url || ''
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export const edgeGlobal = {
|
|
499
|
+
edgeState,
|
|
500
|
+
setOrganization,
|
|
501
|
+
showLeftPanel,
|
|
502
|
+
getSubscribedStatus,
|
|
503
|
+
isDarkMode,
|
|
504
|
+
generateShortId,
|
|
505
|
+
objHas,
|
|
506
|
+
getOrganizations,
|
|
507
|
+
dupObject,
|
|
508
|
+
currentOrganizationObject,
|
|
509
|
+
edgeRules,
|
|
510
|
+
edgeLogOut,
|
|
511
|
+
orgUserRoles,
|
|
512
|
+
getRoleName,
|
|
513
|
+
isAdminGlobal,
|
|
514
|
+
iconFromMenu,
|
|
515
|
+
cmsCollectionData,
|
|
516
|
+
allowMenuItem,
|
|
517
|
+
syncDevOverride,
|
|
518
|
+
getImage,
|
|
519
|
+
}
|
package/edge-pull.sh
ADDED
package/edge-push.sh
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
git subtree push --prefix=edge edge-vue-components main
|
package/edge-status.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
git fetch edge-vue-components >/dev/null 2>&1
|
|
5
|
+
|
|
6
|
+
UP_TREE="$(git rev-parse edge-vue-components/main^{tree})"
|
|
7
|
+
LOCAL_TREE="$(git rev-parse HEAD:edge)"
|
|
8
|
+
|
|
9
|
+
if [ "$UP_TREE" = "$LOCAL_TREE" ]
|
|
10
|
+
then
|
|
11
|
+
echo "edge is in sync with edge-vue-components/main"
|
|
12
|
+
else
|
|
13
|
+
echo "edge differs from edge-vue-components/main"
|
|
14
|
+
fi
|
package/firebase.json
CHANGED
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"rewrites": [
|
|
26
26
|
{
|
|
27
27
|
"source": "/api/stripe",
|
|
28
|
-
"function":
|
|
28
|
+
"function": {
|
|
29
|
+
"functionId": "stripe-webhook",
|
|
30
|
+
"region": "us-west1"
|
|
31
|
+
}
|
|
29
32
|
},
|
|
30
33
|
{
|
|
31
34
|
"source": "**",
|
|
@@ -60,4 +63,4 @@
|
|
|
60
63
|
},
|
|
61
64
|
"singleProjectMode": true
|
|
62
65
|
}
|
|
63
|
-
}
|
|
66
|
+
}
|
package/firebase_init.sh
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
# Prompt for the Firebase configuration values
|
|
2
2
|
echo "Please enter your Firebase project ID:"
|
|
3
3
|
read project_id
|
|
4
|
+
|
|
4
5
|
echo "Please enter your Firebase API key:"
|
|
5
6
|
read api_key
|
|
7
|
+
|
|
6
8
|
echo "Please enter your Firebase Auth domain:"
|
|
7
9
|
read auth_domain
|
|
10
|
+
|
|
8
11
|
echo "Please enter your Firebase storage bucket:"
|
|
9
12
|
read storage_bucket
|
|
13
|
+
|
|
10
14
|
echo "Please enter your Firebase messaging sender ID:"
|
|
11
15
|
read messaging_sender_id
|
|
16
|
+
|
|
12
17
|
echo "Please enter your Firebase app ID:"
|
|
13
18
|
read app_id
|
|
19
|
+
|
|
14
20
|
echo "Please enter your Firebase measurement ID:"
|
|
15
21
|
read measurement_id
|
|
16
22
|
|
|
23
|
+
echo "Please enter your Firebase Functions region (default: us-west1):"
|
|
24
|
+
read functions_region
|
|
25
|
+
|
|
26
|
+
if [ -z "$functions_region" ]; then
|
|
27
|
+
functions_region="us-west1"
|
|
28
|
+
fi
|
|
29
|
+
|
|
17
30
|
# Check if project_id is empty
|
|
18
31
|
if [ -z "$project_id" ]; then
|
|
19
32
|
echo "Error: Firebase project ID cannot be empty."
|
|
@@ -26,26 +39,26 @@ if ! command -v firebase &> /dev/null; then
|
|
|
26
39
|
exit 1
|
|
27
40
|
fi
|
|
28
41
|
|
|
29
|
-
#
|
|
42
|
+
# Backup firebase.json if it exists
|
|
30
43
|
if [ -f ./firebase.json ]; then
|
|
31
44
|
cp ./firebase.json ./firebase.json.temp
|
|
32
45
|
fi
|
|
33
46
|
|
|
34
|
-
#
|
|
47
|
+
# Backup firestore.rules if it exists
|
|
35
48
|
if [ -f ./firestore.rules ]; then
|
|
36
49
|
cp ./firestore.rules ./firestore.rules.temp
|
|
37
50
|
fi
|
|
38
51
|
|
|
39
52
|
# Initialize Firebase
|
|
40
|
-
firebase use --add $project_id --alias default
|
|
53
|
+
firebase use --add "$project_id" --alias default
|
|
41
54
|
firebase init firestore functions hosting storage emulators --project default
|
|
42
55
|
|
|
43
|
-
# Restore firebase.json
|
|
56
|
+
# Restore firebase.json
|
|
44
57
|
if [ -f ./firebase.json.temp ]; then
|
|
45
58
|
mv ./firebase.json.temp ./firebase.json
|
|
46
59
|
fi
|
|
47
60
|
|
|
48
|
-
# Restore firestore.rules
|
|
61
|
+
# Restore firestore.rules
|
|
49
62
|
if [ -f ./firestore.rules.temp ]; then
|
|
50
63
|
mv ./firestore.rules.temp ./firestore.rules
|
|
51
64
|
fi
|
|
@@ -58,6 +71,7 @@ echo "VITE_FIREBASE_STORAGE_BUCKET=$storage_bucket" >> .env
|
|
|
58
71
|
echo "VITE_FIREBASE_MESSAGING_SENDER_ID=$messaging_sender_id" >> .env
|
|
59
72
|
echo "VITE_FIREBASE_APP_ID=$app_id" >> .env
|
|
60
73
|
echo "VITE_FIREBASE_MEASUREMENT_ID=$measurement_id" >> .env
|
|
74
|
+
echo "VITE_FIREBASE_FUNCTIONS_REGION=$functions_region" >> .env
|
|
61
75
|
echo "VITE_FIREBASE_EMULATOR_AUTH=" >> .env
|
|
62
76
|
echo "VITE_FIREBASE_EMULATOR_FIRESTORE=" >> .env
|
|
63
77
|
echo "VITE_FIREBASE_EMULATOR_FUNCTIONS=" >> .env
|
|
@@ -73,9 +87,10 @@ echo "VITE_FIREBASE_STORAGE_BUCKET=$storage_bucket" >> .env.dev
|
|
|
73
87
|
echo "VITE_FIREBASE_MESSAGING_SENDER_ID=$messaging_sender_id" >> .env.dev
|
|
74
88
|
echo "VITE_FIREBASE_APP_ID=$app_id" >> .env.dev
|
|
75
89
|
echo "VITE_FIREBASE_MEASUREMENT_ID=$measurement_id" >> .env.dev
|
|
90
|
+
echo "VITE_FIREBASE_FUNCTIONS_REGION=$functions_region" >> .env.dev
|
|
76
91
|
echo "VITE_FIREBASE_EMULATOR_AUTH=9099" >> .env.dev
|
|
77
92
|
echo "VITE_FIREBASE_EMULATOR_FIRESTORE=8080" >> .env.dev
|
|
78
93
|
echo "VITE_FIREBASE_EMULATOR_FUNCTIONS=5001" >> .env.dev
|
|
79
94
|
echo "VITE_FIREBASE_EMULATOR_STORAGE=9199" >> .env.dev
|
|
80
95
|
echo "REGISTRATION_CODE=organization-registration-template" >> .env.dev
|
|
81
|
-
echo "DEVELOPMENT_MODE=true" >> .env.dev
|
|
96
|
+
echo "DEVELOPMENT_MODE=true" >> .env.dev
|
package/package.json
CHANGED
|
@@ -13,5 +13,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
13
13
|
emulatorFirestore: import.meta.env.VITE_FIREBASE_EMULATOR_FIRESTORE,
|
|
14
14
|
emulatorFunctions: import.meta.env.VITE_FIREBASE_EMULATOR_FUNCTIONS,
|
|
15
15
|
emulatorStorage: import.meta.env.VITE_FIREBASE_EMULATOR_STORAGE,
|
|
16
|
+
functionsRegion: import.meta.env.VITE_FIREBASE_FUNCTIONS_REGION,
|
|
16
17
|
}, true, true)
|
|
17
18
|
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
git clone https://github.com/Edge-Marketing-and-Design/edge-vue-components.git edge
|