@edgedev/create-edge-app 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.eslintrc +10 -0
  2. package/.vscode/settings.json +14 -0
  3. package/README.md +63 -0
  4. package/app.vue +106 -0
  5. package/bin/cli-edge-app.js +99 -0
  6. package/components/.gitkeep +0 -0
  7. package/components/account.vue +84 -0
  8. package/components/bottomMenu.vue +35 -0
  9. package/components/dashboard.vue +186 -0
  10. package/components/editor.vue +250 -0
  11. package/components/formSubtypes/.gitkeep +0 -0
  12. package/components/topMenu.vue +17 -0
  13. package/components/userMenu.vue +64 -0
  14. package/composables/global.ts +15 -0
  15. package/composables/vuetify.ts +10 -0
  16. package/deploy.sh +6 -0
  17. package/emulator.sh +17 -0
  18. package/firebase.json +56 -0
  19. package/firestore.indexes.json +4 -0
  20. package/firestore.rules +294 -0
  21. package/firestore.rules.backup +1 -0
  22. package/functions/.runtimeconfig.json +5 -0
  23. package/functions/index.js +226 -0
  24. package/functions/index.js.backup +4 -0
  25. package/functions/package-lock.json +4535 -0
  26. package/functions/package.json +25 -0
  27. package/middleware/auth.ts +17 -0
  28. package/nuxt.config.ts +27 -0
  29. package/package.json +34 -0
  30. package/pages/app/[[page]]/[[collection]]/[[docId]].vue +48 -0
  31. package/pages/app/login.vue +16 -0
  32. package/pages/app/signup.vue +17 -0
  33. package/plugins/draggable.ts +5 -0
  34. package/plugins/edgeFirebaseFramework.ts +5 -0
  35. package/plugins/firebase.client.ts +16 -0
  36. package/plugins/maska.ts +5 -0
  37. package/plugins/number.ts +4 -0
  38. package/plugins/vuetify.ts +14 -0
  39. package/public/favicon.ico +0 -0
  40. package/public/images/logo-square.png +0 -0
  41. package/public/images/logo.png +0 -0
  42. package/server/tsconfig.json +3 -0
  43. package/storage.rules +8 -0
  44. package/tsconfig.json +4 -0
@@ -0,0 +1,250 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ docId: {
4
+ type: String,
5
+ default: '',
6
+ },
7
+ collection: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ newDocSchema: {
12
+ type: Object,
13
+ required: true,
14
+ },
15
+ })
16
+
17
+ const newDoc = computed(() => {
18
+ return Object.entries(props.newDocSchema).reduce((newObj, [key, val]) => {
19
+ newObj[key] = val.value
20
+ return newObj
21
+ }, {})
22
+ })
23
+
24
+ const router = useRouter()
25
+
26
+ const state = reactive({
27
+ workingDoc: {},
28
+ form: false,
29
+ tab: 'forms',
30
+ bypassUnsavedChanges: false,
31
+ afterMount: false,
32
+ })
33
+ const edgeFirebase = inject('edgeFirebase')
34
+ const edgeGlobal = inject('edgeGlobal')
35
+
36
+ const unsavedChanges = computed(() => {
37
+ if (props.docId === 'new') {
38
+ return false
39
+ }
40
+ return JSON.stringify(state.workingDoc) !== JSON.stringify(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`][props.docId])
41
+ })
42
+
43
+ const subCollection = (collection) => {
44
+ if (edgeGlobal.objHas(edgeFirebase.data, `${edgeGlobal.edgeState.organizationDocPath}/${collection}`) === false) {
45
+ return []
46
+ }
47
+ // need to return an array of objects title is name and value is docId
48
+ return Object.entries(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${collection}`]).map(([key, val]) => {
49
+ return {
50
+ title: val.name,
51
+ value: key,
52
+ }
53
+ })
54
+ }
55
+
56
+ onBeforeRouteUpdate((to, from, next) => {
57
+ if (unsavedChanges.value && !state.bypassUnsavedChanges) {
58
+ state.dialog = true
59
+ next(false)
60
+ return
61
+ }
62
+ edgeGlobal.edgeState.changeTracker = {}
63
+ next()
64
+ })
65
+
66
+ const discardChanges = async () => {
67
+ if (props.docId === 'new') {
68
+ state.bypassUnsavedChanges = true
69
+ edgeGlobal.edgeState.changeTracker = {}
70
+ router.push('/app/dashboard')
71
+ return
72
+ }
73
+ state.workingDoc = await edgeGlobal.dupObject(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`][props.docId])
74
+ state.bypassUnsavedChanges = true
75
+ edgeGlobal.edgeState.changeTracker = {}
76
+ router.push('/app/dashboard')
77
+ }
78
+
79
+ const capitalizeFirstLetter = (str) => {
80
+ return str.charAt(0).toUpperCase() + str.slice(1)
81
+ }
82
+
83
+ const singularize = (word) => {
84
+ if (word.endsWith('ies')) {
85
+ return `${word.slice(0, -3)}y`
86
+ }
87
+ else if (word.endsWith('es')) {
88
+ return word.slice(0, -2)
89
+ }
90
+ else if (word.endsWith('s')) {
91
+ return word.slice(0, -1)
92
+ }
93
+ else {
94
+ return word
95
+ }
96
+ }
97
+
98
+ const title = computed(() => {
99
+ if (props.docId !== 'new') {
100
+ if (!edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`]) {
101
+ return ''
102
+ }
103
+ return capitalizeFirstLetter(`${edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`][props.docId].name}`)
104
+ }
105
+ else {
106
+ return `New ${capitalizeFirstLetter(singularize(props.collection))}`
107
+ }
108
+ })
109
+
110
+ const onSubmit = async (event) => {
111
+ const results = await event
112
+ if (results.valid) {
113
+ state.bypassUnsavedChanges = true
114
+ edgeFirebase.storeDoc(`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`, state.workingDoc)
115
+ edgeGlobal.edgeState.changeTracker = {}
116
+ router.push(`/app/dashboard/${props.collection}`)
117
+ }
118
+ }
119
+
120
+ onBeforeMount(async () => {
121
+ edgeGlobal.edgeState.changeTracker = {}
122
+ for (const field of Object.keys(props.newDocSchema)) {
123
+ if (props.newDocSchema[field].type === 'collection') {
124
+ await edgeFirebase.startSnapshot(`${edgeGlobal.edgeState.organizationDocPath}/${field}`)
125
+ }
126
+ }
127
+ await edgeFirebase.startSnapshot(`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`)
128
+ })
129
+
130
+ watch(() => edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`], (newVal) => {
131
+ if (props.docId !== 'new') {
132
+ if (edgeGlobal.objHas(newVal, props.docId) === false) {
133
+ return
134
+ }
135
+ state.workingDoc = edgeGlobal.dupObject(newVal[props.docId])
136
+ Object.keys(newDoc.value).forEach((field) => {
137
+ if (!edgeGlobal.objHas(state.workingDoc, field)) {
138
+ state.workingDoc[field] = newDoc.value[field]
139
+ }
140
+ })
141
+ state.afterMount = true
142
+ }
143
+ else {
144
+ state.workingDoc = edgeGlobal.dupObject(newDoc.value)
145
+ state.afterMount = true
146
+ }
147
+ })
148
+ </script>
149
+
150
+ <template>
151
+ <v-card v-if="state.afterMount">
152
+ <v-form
153
+ v-model="state.form"
154
+ validate-on="submit"
155
+ @submit.prevent="onSubmit"
156
+ >
157
+ <v-toolbar flat>
158
+ <v-icon class="mx-4">
159
+ mdi-atom-variant
160
+ </v-icon>
161
+
162
+ {{ title }}
163
+ <v-spacer />
164
+ <v-btn
165
+ type="submit"
166
+ color="primary"
167
+ variant="text"
168
+ >
169
+ Save
170
+ </v-btn>
171
+ </v-toolbar>
172
+ <v-card-text>
173
+ <v-row>
174
+ <v-col v-for="(field, name, index) in props.newDocSchema" :key="index" :cols="field.cols">
175
+ <g-input
176
+ v-if="field.type !== 'collection'"
177
+ v-model="state.workingDoc[name]"
178
+ :disable-tracking="props.docId === 'new'"
179
+ :field-type="field.type"
180
+ :rules="[edgeGlobal.edgeRules.required]"
181
+ :label="field.label"
182
+ :parent-tracker-id="`${props.collection}-${props.docId}`"
183
+ :helper="field.helper"
184
+ />
185
+ <g-input
186
+ v-else
187
+ v-model="state.workingDoc[name]"
188
+ :disable-tracking="props.docId === 'new'"
189
+ field-type="select"
190
+ :label="field.label"
191
+ :items="subCollection(name)"
192
+ :parent-tracker-id="`${props.collection}-${props.docId}`"
193
+ />
194
+ </v-col>
195
+ </v-row>
196
+ </v-card-text>
197
+ <v-card-actions>
198
+ <v-spacer />
199
+ <v-btn
200
+ v-if="!unsavedChanges"
201
+ color="secondary"
202
+ variant="text"
203
+ to="/app/dashboard"
204
+ >
205
+ Close
206
+ </v-btn>
207
+ <v-btn
208
+ v-else
209
+ color="secondary"
210
+ variant="text"
211
+ to="/app/dashboard"
212
+ >
213
+ Cancel
214
+ </v-btn>
215
+
216
+ <v-btn
217
+ type="submit"
218
+ color="primary"
219
+ variant="text"
220
+ >
221
+ Save
222
+ </v-btn>
223
+ </v-card-actions>
224
+ </v-form>
225
+ </v-card>
226
+ <v-dialog v-model="state.dialog" max-width="500px">
227
+ <v-card>
228
+ <v-card-title class="headline">
229
+ Unsaved Changes!
230
+ </v-card-title>
231
+ <v-card-text>
232
+ <h4>"{{ title }}" has unsaved changes.</h4>
233
+ <p>Are you sure you want to discard them?</p>
234
+ </v-card-text>
235
+ <v-card-actions>
236
+ <v-spacer />
237
+ <v-btn color="blue darken-1" text @click="state.dialog = false">
238
+ Cancel
239
+ </v-btn>
240
+ <v-btn color="error" text @click="discardChanges()">
241
+ Discard
242
+ </v-btn>
243
+ </v-card-actions>
244
+ </v-card>
245
+ </v-dialog>
246
+ </template>
247
+
248
+ <style lang="scss" scoped>
249
+
250
+ </style>
File without changes
@@ -0,0 +1,17 @@
1
+ <script setup>
2
+ const edgeFirebase = inject('edgeFirebase')
3
+ const edgeGlobal = inject('edgeGlobal')
4
+ </script>
5
+
6
+ <template>
7
+ <v-app-bar v-if="edgeFirebase.user.loggedIn">
8
+ <v-spacer />
9
+ <user-menu />
10
+ </v-app-bar>
11
+ </template>
12
+
13
+ <style lang="scss" scoped>
14
+ .inverted-logo {
15
+ filter: invert(1);
16
+ }
17
+ </style>
@@ -0,0 +1,64 @@
1
+ <script setup>
2
+ const edgeFirebase = inject('edgeFirebase')
3
+ const edgeGlobal = inject('edgeGlobal')
4
+ </script>
5
+
6
+ <template>
7
+ <v-menu>
8
+ <template #activator="{ props }">
9
+ <v-btn
10
+ prepend-icon="mdi-account-group-outline"
11
+ color="primary"
12
+ dark
13
+ v-bind="props"
14
+ variant="plain"
15
+ >
16
+ <template #prepend>
17
+ <v-icon color="secondary" />
18
+ </template>
19
+ {{ edgeGlobal.currentOrganizationObject.name }}
20
+ </v-btn>
21
+ </template>
22
+ <v-card>
23
+ <v-list>
24
+ <v-list-item
25
+ :title="edgeFirebase.user.meta.name"
26
+ :subtitle="edgeFirebase.user.firebaseUser.providerData[0].email"
27
+ >
28
+ <template #prepend>
29
+ <v-avatar>
30
+ <v-icon>
31
+ mdi-account
32
+ </v-icon>
33
+ </v-avatar>
34
+ </template>
35
+ <template #append>
36
+ <v-btn size="small" variant="text" icon="mdi-menu-down" />
37
+ </template>
38
+ </v-list-item>
39
+ </v-list>
40
+ <v-divider />
41
+ <edge-org-switcher />
42
+
43
+ <v-divider />
44
+
45
+ <v-list :lines="false" density="compact" nav>
46
+ <v-list-item link to="/app/account/organization-settings">
47
+ <v-list-item-title>Manage Account</v-list-item-title>
48
+ </v-list-item>
49
+
50
+ <v-list-item link to="/app/account/my-profile">
51
+ <v-list-item-title>My Profile</v-list-item-title>
52
+ </v-list-item>
53
+ </v-list>
54
+
55
+ <v-divider />
56
+
57
+ <v-list :lines="false" density="compact" nav>
58
+ <v-list-item @click="logOut(edgeFirebase, edgeGlobal)">
59
+ <v-list-item-title>Log out</v-list-item-title>
60
+ </v-list-item>
61
+ </v-list>
62
+ </v-card>
63
+ </v-menu>
64
+ </template>
@@ -0,0 +1,15 @@
1
+ export const globalState = reactive({
2
+ drawer: false,
3
+ dark: true,
4
+ })
5
+
6
+ export const projectSetOrg = async (organization: string, edgeFirebase: any, edgeGlobal: any) => {
7
+ // set Organization Paths and start snapshots here
8
+ }
9
+
10
+ export const logOut = async (edgeFirebase: any, edgeGlobal: any) => {
11
+ const auth = useState('auth')
12
+ auth.value = ''
13
+ globalState.drawer = false
14
+ await edgeGlobal.edgeLogOut(edgeFirebase)
15
+ }
@@ -0,0 +1,10 @@
1
+ // DO NOT CHANGE THIS FILE
2
+ import { getCurrentInstance } from 'vue'
3
+
4
+ export function useVuetify() {
5
+ const instance: any = getCurrentInstance()
6
+ if (!instance) {
7
+ throw new Error('useVuetify should be called in setup().')
8
+ }
9
+ return instance.proxy.$vuetify
10
+ }
package/deploy.sh ADDED
@@ -0,0 +1,6 @@
1
+ pnpm run generate
2
+ firebase functions:config:set openai.api_key="SOME_API_KEY"
3
+ firebase functions:config:get
4
+ firebase deploy --only functions
5
+ firebase deploy --only hosting
6
+ firebase deploy --only firestore
package/emulator.sh ADDED
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+
3
+ ports=(9099 4000 5001 8080 5025 9199)
4
+ for port in "${ports[@]}"; do
5
+ pid=$(lsof -ti :$port)
6
+ if [ ! -z "$pid" ]; then
7
+ process_name=$(ps -p $pid -o comm=)
8
+ echo $process_name
9
+ echo "Stopping Firebase emulator on port $port with PID $pid"
10
+ kill -9 $pid
11
+ fi
12
+ done
13
+ DIR="./firebase_data"
14
+ if [ ! -d "$DIR" ]; then
15
+ cp -r ./firebase_data_emulator_seed ./firebase_data
16
+ fi
17
+ firebase emulators:start --import ./firebase_data --export-on-exit
package/firebase.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "firestore": {
3
+ "rules": "firestore.rules",
4
+ "indexes": "firestore.indexes.json"
5
+ },
6
+ "functions": [
7
+ {
8
+ "source": "functions",
9
+ "codebase": "default",
10
+ "ignore": [
11
+ "node_modules",
12
+ ".git",
13
+ "firebase-debug.log",
14
+ "firebase-debug.*.log"
15
+ ]
16
+ }
17
+ ],
18
+ "hosting": {
19
+ "public": ".output/public",
20
+ "ignore": [
21
+ "firebase.json",
22
+ "**/.*",
23
+ "**/node_modules/**"
24
+ ],
25
+ "rewrites": [
26
+ {
27
+ "source": "**",
28
+ "destination": "/index.html"
29
+ }
30
+ ]
31
+ },
32
+ "storage": {
33
+ "rules": "storage.rules"
34
+ },
35
+ "emulators": {
36
+ "auth": {
37
+ "port": 9099
38
+ },
39
+ "functions": {
40
+ "port": 5001
41
+ },
42
+ "firestore": {
43
+ "port": 8080
44
+ },
45
+ "hosting": {
46
+ "port": 5025
47
+ },
48
+ "storage": {
49
+ "port": 9199
50
+ },
51
+ "ui": {
52
+ "enabled": true
53
+ },
54
+ "singleProjectMode": true
55
+ }
56
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "indexes": [],
3
+ "fieldOverrides": []
4
+ }