@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.
- package/.eslintrc +10 -0
- package/.vscode/settings.json +14 -0
- package/README.md +63 -0
- package/app.vue +106 -0
- package/bin/cli-edge-app.js +99 -0
- package/components/.gitkeep +0 -0
- package/components/account.vue +84 -0
- package/components/bottomMenu.vue +35 -0
- package/components/dashboard.vue +186 -0
- package/components/editor.vue +250 -0
- package/components/formSubtypes/.gitkeep +0 -0
- package/components/topMenu.vue +17 -0
- package/components/userMenu.vue +64 -0
- package/composables/global.ts +15 -0
- package/composables/vuetify.ts +10 -0
- package/deploy.sh +6 -0
- package/emulator.sh +17 -0
- package/firebase.json +56 -0
- package/firestore.indexes.json +4 -0
- package/firestore.rules +294 -0
- package/firestore.rules.backup +1 -0
- package/functions/.runtimeconfig.json +5 -0
- package/functions/index.js +226 -0
- package/functions/index.js.backup +4 -0
- package/functions/package-lock.json +4535 -0
- package/functions/package.json +25 -0
- package/middleware/auth.ts +17 -0
- package/nuxt.config.ts +27 -0
- package/package.json +34 -0
- package/pages/app/[[page]]/[[collection]]/[[docId]].vue +48 -0
- package/pages/app/login.vue +16 -0
- package/pages/app/signup.vue +17 -0
- package/plugins/draggable.ts +5 -0
- package/plugins/edgeFirebaseFramework.ts +5 -0
- package/plugins/firebase.client.ts +16 -0
- package/plugins/maska.ts +5 -0
- package/plugins/number.ts +4 -0
- package/plugins/vuetify.ts +14 -0
- package/public/favicon.ico +0 -0
- package/public/images/logo-square.png +0 -0
- package/public/images/logo.png +0 -0
- package/server/tsconfig.json +3 -0
- package/storage.rules +8 -0
- package/tsconfig.json +4 -0
package/.eslintrc
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files.associations": {
|
|
3
|
+
"*.json": "json"
|
|
4
|
+
},
|
|
5
|
+
"[json]": {
|
|
6
|
+
"editor.formatOnSave": true,
|
|
7
|
+
},
|
|
8
|
+
"editor.formatOnSave": false,
|
|
9
|
+
"vetur.validation.template": false,
|
|
10
|
+
"editor.codeActionsOnSave": {
|
|
11
|
+
"source.fixAll.eslint": true,
|
|
12
|
+
},
|
|
13
|
+
"vue3snippets.enable-compile-vue-file-on-did-save-code": false
|
|
14
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Nuxt 3 Minimal Starter
|
|
2
|
+
|
|
3
|
+
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Make sure to install the dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# npm
|
|
11
|
+
npm install
|
|
12
|
+
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm install
|
|
15
|
+
|
|
16
|
+
# yarn
|
|
17
|
+
yarn install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Development Server
|
|
21
|
+
|
|
22
|
+
Start the development server on `http://localhost:3000`:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# npm
|
|
26
|
+
npm run dev
|
|
27
|
+
|
|
28
|
+
# pnpm
|
|
29
|
+
pnpm run dev
|
|
30
|
+
|
|
31
|
+
# yarn
|
|
32
|
+
yarn dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Production
|
|
36
|
+
|
|
37
|
+
Build the application for production:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# npm
|
|
41
|
+
npm run build
|
|
42
|
+
|
|
43
|
+
# pnpm
|
|
44
|
+
pnpm run build
|
|
45
|
+
|
|
46
|
+
# yarn
|
|
47
|
+
yarn build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Locally preview production build:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# npm
|
|
54
|
+
npm run preview
|
|
55
|
+
|
|
56
|
+
# pnpm
|
|
57
|
+
pnpm run preview
|
|
58
|
+
|
|
59
|
+
# yarn
|
|
60
|
+
yarn preview
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
package/app.vue
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useTheme } from 'vuetify'
|
|
3
|
+
|
|
4
|
+
const vueTheme = useTheme()
|
|
5
|
+
const changeTheme = (theme) => {
|
|
6
|
+
vueTheme.global.name.value = theme
|
|
7
|
+
}
|
|
8
|
+
const edgeFirebase = inject('edgeFirebase')
|
|
9
|
+
const edgeGlobal = inject('edgeGlobal')
|
|
10
|
+
|
|
11
|
+
const currentOrganization = computed(() => {
|
|
12
|
+
return edgeGlobal.edgeState.currentOrganization
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
watch(currentOrganization, async () => {
|
|
16
|
+
if (currentOrganization.value) {
|
|
17
|
+
// RUN STUFF HERE WHEN ORGANIZATION CHANGES LIKE SNAPSHOTS
|
|
18
|
+
await projectSetOrg(currentOrganization.value, edgeFirebase, edgeGlobal)
|
|
19
|
+
|
|
20
|
+
// KEEP THIS CODE:
|
|
21
|
+
const auth = useState('auth')
|
|
22
|
+
auth.value = edgeFirebase.user
|
|
23
|
+
|
|
24
|
+
const preLoginRoute = useState('preLoginRoute')
|
|
25
|
+
const router = useRouter()
|
|
26
|
+
|
|
27
|
+
let cleanedRoute = ''
|
|
28
|
+
if (preLoginRoute.value) {
|
|
29
|
+
cleanedRoute = preLoginRoute.value.endsWith('/') ? preLoginRoute.value.slice(0, -1) : preLoginRoute.value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (cleanedRoute === ''
|
|
33
|
+
|| cleanedRoute === '/app'
|
|
34
|
+
|| cleanedRoute === '/app/login'
|
|
35
|
+
|| cleanedRoute === '/app/signup') {
|
|
36
|
+
router.push('/app/dashboard')
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
router.push(preLoginRoute.value)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(auth.value)
|
|
43
|
+
}
|
|
44
|
+
if (!currentOrganization.value) {
|
|
45
|
+
const auth = useState('auth')
|
|
46
|
+
auth.value = ''
|
|
47
|
+
const router = useRouter()
|
|
48
|
+
router.push('/app/login')
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const user = computed(() => {
|
|
53
|
+
return edgeFirebase.user
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
watch (user, async () => {
|
|
57
|
+
if (user.value) {
|
|
58
|
+
const auth = useState('auth')
|
|
59
|
+
auth.value = user.value
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
onMounted(() => {
|
|
64
|
+
if (edgeGlobal.isDarkMode()) {
|
|
65
|
+
changeTheme('dark')
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
changeTheme('light')
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
edgeFirebase.runFunction('initFirestore', {})
|
|
72
|
+
edgeGlobal.edgeState.userRoles = [
|
|
73
|
+
{
|
|
74
|
+
name: 'Admin',
|
|
75
|
+
roles: [
|
|
76
|
+
{
|
|
77
|
+
collectionPath: 'organizationDocPath',
|
|
78
|
+
role: 'admin',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'User',
|
|
84
|
+
roles: [
|
|
85
|
+
{
|
|
86
|
+
collectionPath: 'organizationDocPath',
|
|
87
|
+
role: 'user',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
]
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<template>
|
|
95
|
+
<v-app>
|
|
96
|
+
<top-menu v-if="edgeFirebase.user.loggedIn" />
|
|
97
|
+
<v-main>
|
|
98
|
+
<NuxtPage />
|
|
99
|
+
</v-main>
|
|
100
|
+
<bottom-menu v-if="edgeFirebase.user.loggedIn" />
|
|
101
|
+
</v-app>
|
|
102
|
+
</template>
|
|
103
|
+
|
|
104
|
+
<style lang="scss">
|
|
105
|
+
.firebase-emulator-warning { display: none; }
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { execSync } = require('child_process')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const readline = require('readline')
|
|
5
|
+
|
|
6
|
+
const runCommand = (command) => {
|
|
7
|
+
try {
|
|
8
|
+
execSync(`${command}`, { stdio: 'inherit' })
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
console.error(`Failed to run command: ${command}`, err)
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const promptForProjectId = () => {
|
|
18
|
+
const rl = readline.createInterface({
|
|
19
|
+
input: process.stdin,
|
|
20
|
+
output: process.stdout,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
rl.question('Please enter your Firebase project ID: ', (projectId) => {
|
|
25
|
+
rl.close()
|
|
26
|
+
resolve(projectId)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const initializeFirebase = async (projectId, repoName) => {
|
|
32
|
+
if (!projectId) {
|
|
33
|
+
console.error('Error: Firebase project ID cannot be empty.')
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
execSync('firebase --version', { stdio: 'inherit' })
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
console.error('Firebase CLI could not be found. Please install it and try again.')
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const pathToFirebaseJson = `${repoName}/firebase.json`
|
|
46
|
+
const pathToFirestoreRules = `${repoName}/firestore.rules`
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(pathToFirebaseJson)) {
|
|
49
|
+
fs.renameSync(pathToFirebaseJson, `${pathToFirebaseJson}.temp`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (fs.existsSync(pathToFirestoreRules)) {
|
|
53
|
+
fs.renameSync(pathToFirestoreRules, `${pathToFirestoreRules}.temp`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(`Initializing Firebase for ${repoName}...`)
|
|
57
|
+
runCommand(`cd ${repoName} && firebase use --add ${projectId} --alias default`)
|
|
58
|
+
runCommand(`cd ${repoName} && firebase init firestore functions hosting storage emulators --project default`)
|
|
59
|
+
|
|
60
|
+
if (fs.existsSync(`${pathToFirebaseJson}.temp`)) {
|
|
61
|
+
fs.renameSync(`${pathToFirebaseJson}.temp`, pathToFirebaseJson)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (fs.existsSync(`${pathToFirestoreRules}.temp`)) {
|
|
65
|
+
fs.renameSync(`${pathToFirestoreRules}.temp`, pathToFirestoreRules)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const repoName = process.argv[2]
|
|
70
|
+
const gitCheckoutCommand = `git clone --depth 1 https://github.com/Edge-Marketing-and-Design/edgeApp.git ${repoName}`
|
|
71
|
+
const installDependenciesCommand = `cd ${repoName} && pnpm install`
|
|
72
|
+
const installFunctionDependenciesCommand = `cd ${repoName}/functions && npm install`
|
|
73
|
+
|
|
74
|
+
console.log(`Cloning with name ${repoName}...`)
|
|
75
|
+
const checkedOut = runCommand(gitCheckoutCommand)
|
|
76
|
+
if (!checkedOut) {
|
|
77
|
+
process.exit(1)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(`Installing dependencies for ${repoName}...`)
|
|
81
|
+
const installedDeps = runCommand(installDependenciesCommand)
|
|
82
|
+
if (!installedDeps) {
|
|
83
|
+
process.exit(1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`Installing function dependencies for ${repoName}...`)
|
|
87
|
+
const installedFunctionDeps = runCommand(installFunctionDependenciesCommand)
|
|
88
|
+
if (!installedFunctionDeps) {
|
|
89
|
+
process.exit(1)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`Successfully created ${repoName}!`)
|
|
93
|
+
|
|
94
|
+
const main = async () => {
|
|
95
|
+
const projectId = await promptForProjectId()
|
|
96
|
+
await initializeFirebase(projectId, repoName)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const route = useRoute()
|
|
3
|
+
const edgeGlobal = inject('edgeGlobal')
|
|
4
|
+
const site = computed(() => {
|
|
5
|
+
return route.params.collection
|
|
6
|
+
})
|
|
7
|
+
const metaFields = [
|
|
8
|
+
{
|
|
9
|
+
field: 'name',
|
|
10
|
+
type: 'text',
|
|
11
|
+
label: 'Name',
|
|
12
|
+
hint: 'Your name, shown in the user interface.',
|
|
13
|
+
rules: [edgeGlobal.edgeRules.required],
|
|
14
|
+
},
|
|
15
|
+
]
|
|
16
|
+
const orgFields = [
|
|
17
|
+
{
|
|
18
|
+
field: 'name',
|
|
19
|
+
type: 'text',
|
|
20
|
+
label: 'Name',
|
|
21
|
+
hint: 'Your name, shown in the user interface.',
|
|
22
|
+
rules: [edgeGlobal.edgeRules.required],
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const config = useRuntimeConfig()
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<v-card>
|
|
31
|
+
<v-toolbar flat>
|
|
32
|
+
<v-icon class="mx-4">
|
|
33
|
+
mdi-account-group-outline
|
|
34
|
+
</v-icon>
|
|
35
|
+
{{ edgeGlobal.currentOrganizationObject.name }}
|
|
36
|
+
</v-toolbar>
|
|
37
|
+
<v-card-text>
|
|
38
|
+
<v-row>
|
|
39
|
+
<v-col cols="3">
|
|
40
|
+
<v-card>
|
|
41
|
+
<v-list :lines="false" density="compact" nav>
|
|
42
|
+
<v-list-subheader class="">
|
|
43
|
+
Organization
|
|
44
|
+
</v-list-subheader>
|
|
45
|
+
<v-list-item link to="/app/account/organization-settings">
|
|
46
|
+
<v-list-item-title>Settings</v-list-item-title>
|
|
47
|
+
</v-list-item>
|
|
48
|
+
|
|
49
|
+
<v-list-item link to="/app/account/organization-members">
|
|
50
|
+
<v-list-item-title>Members</v-list-item-title>
|
|
51
|
+
</v-list-item>
|
|
52
|
+
</v-list>
|
|
53
|
+
<v-divider />
|
|
54
|
+
|
|
55
|
+
<v-list :lines="false" density="compact" nav>
|
|
56
|
+
<v-list-subheader class="">
|
|
57
|
+
My Settings
|
|
58
|
+
</v-list-subheader>
|
|
59
|
+
<v-list-item link to="/app/account/my-profile">
|
|
60
|
+
<v-list-item-title>Profile</v-list-item-title>
|
|
61
|
+
</v-list-item>
|
|
62
|
+
|
|
63
|
+
<v-list-item link to="/app/account/my-account">
|
|
64
|
+
<v-list-item-title>Account</v-list-item-title>
|
|
65
|
+
</v-list-item>
|
|
66
|
+
<v-list-item link to="/app/account/my-organizations">
|
|
67
|
+
<v-list-item-title>Organizations</v-list-item-title>
|
|
68
|
+
</v-list-item>
|
|
69
|
+
</v-list>
|
|
70
|
+
|
|
71
|
+
<v-divider />
|
|
72
|
+
</v-card>
|
|
73
|
+
</v-col>
|
|
74
|
+
<v-col cols="9">
|
|
75
|
+
<edge-organization-settings v-if="site === 'organization-settings'" :org-fields="orgFields" />
|
|
76
|
+
<edge-my-account v-if="site === 'my-account'" />
|
|
77
|
+
<edge-my-profile v-if="site === 'my-profile'" :meta-fields="metaFields" />
|
|
78
|
+
<edge-organization-members v-if="site === 'organization-members'" />
|
|
79
|
+
<edge-my-organizations v-if="site === 'my-organizations'" :registration-code="config.public.registrationCode" />
|
|
80
|
+
</v-col>
|
|
81
|
+
</v-row>
|
|
82
|
+
</v-card-text>
|
|
83
|
+
</v-card>
|
|
84
|
+
</template>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const edgeFirebase = inject('edgeFirebase')
|
|
3
|
+
const edgeGlobal = inject('edgeGlobal')
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<v-bottom-navigation>
|
|
8
|
+
<v-btn to="/app/dashboard">
|
|
9
|
+
<v-icon>mdi-view-dashboard</v-icon>
|
|
10
|
+
|
|
11
|
+
Dashboard
|
|
12
|
+
</v-btn>
|
|
13
|
+
<v-btn to="/app/dashboard/subthings">
|
|
14
|
+
<v-icon>mdi-cube</v-icon>
|
|
15
|
+
|
|
16
|
+
Sub Things
|
|
17
|
+
</v-btn>
|
|
18
|
+
|
|
19
|
+
<v-btn to="/app/account/organization-settings">
|
|
20
|
+
<v-icon>mdi-cog</v-icon>
|
|
21
|
+
|
|
22
|
+
Settings
|
|
23
|
+
</v-btn>
|
|
24
|
+
|
|
25
|
+
<v-btn @click="logOut(edgeFirebase, edgeGlobal)">
|
|
26
|
+
<v-icon>mdi-logout</v-icon>
|
|
27
|
+
|
|
28
|
+
Logout
|
|
29
|
+
</v-btn>
|
|
30
|
+
</v-bottom-navigation>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<style lang="scss" scoped>
|
|
34
|
+
|
|
35
|
+
</style>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
collection: {
|
|
4
|
+
type: String,
|
|
5
|
+
required: true,
|
|
6
|
+
},
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const edgeFirebase = inject('edgeFirebase')
|
|
10
|
+
const edgeGlobal = inject('edgeGlobal')
|
|
11
|
+
const router = useRouter()
|
|
12
|
+
|
|
13
|
+
const state = reactive({
|
|
14
|
+
form: false,
|
|
15
|
+
menu: false,
|
|
16
|
+
dialog: false,
|
|
17
|
+
apiKeys: [],
|
|
18
|
+
filter: '',
|
|
19
|
+
empty: false,
|
|
20
|
+
afterMount: false,
|
|
21
|
+
deleteDialog: false,
|
|
22
|
+
deleteItemName: '',
|
|
23
|
+
deleteItemDocId: '',
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const gotoSite = (docId) => {
|
|
27
|
+
router.push(`/app/dashboard/${props.collection}/${docId}`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const capitalizeFirstLetter = (str) => {
|
|
31
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const singularize = (word) => {
|
|
35
|
+
if (word.endsWith('ies')) {
|
|
36
|
+
return `${word.slice(0, -3)}y`
|
|
37
|
+
}
|
|
38
|
+
else if (word.endsWith('es')) {
|
|
39
|
+
return word.slice(0, -2)
|
|
40
|
+
}
|
|
41
|
+
else if (word.endsWith('s')) {
|
|
42
|
+
return word.slice(0, -1)
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return word
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const filtered = computed(() => {
|
|
50
|
+
if (edgeGlobal.objHas(edgeFirebase.data, `${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`) === false) {
|
|
51
|
+
return []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const allData = Object.values(edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`])
|
|
55
|
+
|
|
56
|
+
const filtered = allData.filter((entry) => {
|
|
57
|
+
if (state.filter.trim() === '') {
|
|
58
|
+
return true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Modify the condition as needed, e.g., using "startsWith" or "includes"
|
|
62
|
+
return entry.name.toLowerCase().includes(state.filter.toLowerCase())
|
|
63
|
+
})
|
|
64
|
+
return filtered.sort((a, b) => {
|
|
65
|
+
if (a.name < b.name) {
|
|
66
|
+
return -1
|
|
67
|
+
}
|
|
68
|
+
if (a.name > b.name) {
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
return 0
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
onBeforeMount (async () => {
|
|
76
|
+
await edgeFirebase.startSnapshot(`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`)
|
|
77
|
+
state.afterMount = true
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const deleteItem = (docId) => {
|
|
81
|
+
state.deleteDialog = true
|
|
82
|
+
state.deleteItemName = edgeFirebase.data[`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`][docId].name
|
|
83
|
+
state.deleteItemDocId = docId
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const deleteAction = () => {
|
|
87
|
+
edgeFirebase.removeDoc(`${edgeGlobal.edgeState.organizationDocPath}/${props.collection}`, state.deleteItemDocId)
|
|
88
|
+
state.deleteDialog = false
|
|
89
|
+
}
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<template>
|
|
93
|
+
<v-card v-if="state.afterMount">
|
|
94
|
+
<v-toolbar flat>
|
|
95
|
+
<v-toolbar-title>{{ capitalizeFirstLetter(props.collection) }}</v-toolbar-title>
|
|
96
|
+
<v-text-field
|
|
97
|
+
v-model="state.filter"
|
|
98
|
+
label="Filter"
|
|
99
|
+
prepend-icon="mdi-filter"
|
|
100
|
+
variant="underlined"
|
|
101
|
+
hide-details
|
|
102
|
+
clearable
|
|
103
|
+
@click:clear="state.filter = ''"
|
|
104
|
+
/>
|
|
105
|
+
<v-spacer />
|
|
106
|
+
<v-btn variant="outlined" :to="`/app/dashboard/${props.collection}/new`">
|
|
107
|
+
Add {{ singularize(props.collection) }}
|
|
108
|
+
</v-btn>
|
|
109
|
+
</v-toolbar>
|
|
110
|
+
<v-card-text>
|
|
111
|
+
<v-list lines="two">
|
|
112
|
+
<template v-for="item in filtered" :key="item.docId">
|
|
113
|
+
<v-list-item @click="gotoSite(item.docId)">
|
|
114
|
+
<template #prepend>
|
|
115
|
+
<v-avatar color="grey-darken-1">
|
|
116
|
+
<v-icon>mdi-file-edit</v-icon>
|
|
117
|
+
</v-avatar>
|
|
118
|
+
</template>
|
|
119
|
+
|
|
120
|
+
<v-list-item-title>{{ item.name }}</v-list-item-title>
|
|
121
|
+
<template #append>
|
|
122
|
+
<v-btn
|
|
123
|
+
color="grey-lighten-1"
|
|
124
|
+
icon="mdi-delete"
|
|
125
|
+
variant="text"
|
|
126
|
+
@click.stop="deleteItem(item.docId)"
|
|
127
|
+
/>
|
|
128
|
+
</template>
|
|
129
|
+
</v-list-item>
|
|
130
|
+
<v-divider
|
|
131
|
+
inset
|
|
132
|
+
/>
|
|
133
|
+
</template>
|
|
134
|
+
</v-list>
|
|
135
|
+
</v-card-text>
|
|
136
|
+
<v-dialog
|
|
137
|
+
v-model="state.deleteDialog"
|
|
138
|
+
persistent
|
|
139
|
+
max-width="600"
|
|
140
|
+
transition="fade-transition"
|
|
141
|
+
>
|
|
142
|
+
<v-card>
|
|
143
|
+
<v-toolbar flat>
|
|
144
|
+
<v-icon class="mx-4">
|
|
145
|
+
mdi-list-box
|
|
146
|
+
</v-icon>
|
|
147
|
+
Delete
|
|
148
|
+
<v-spacer />
|
|
149
|
+
|
|
150
|
+
<v-btn
|
|
151
|
+
type="submit"
|
|
152
|
+
color="primary"
|
|
153
|
+
icon
|
|
154
|
+
@click="state.deleteDialog = false"
|
|
155
|
+
>
|
|
156
|
+
<v-icon> mdi-close</v-icon>
|
|
157
|
+
</v-btn>
|
|
158
|
+
</v-toolbar>
|
|
159
|
+
<v-card-title>Are you sure you want to delete "{{ state.deleteItemName }}"?</v-card-title>
|
|
160
|
+
<v-card-text>Hey there, you're about to banish "{{ state.deleteItemName }}" into the endless abyss of deletion, never to be seen again. Remember, this is not just another trashy reality TV show - there are no sudden comebacks or surprise twists. Once it's gone, it's gone, just like the dignity of anyone who wears socks with sandals. Are you 100% sure you're ready to make ""{{ state.deleteItemName }}"" disappear like a magician's assistant? Tick-tock, the choice is yours!</v-card-text>
|
|
161
|
+
<v-card-actions>
|
|
162
|
+
<v-spacer />
|
|
163
|
+
<v-btn
|
|
164
|
+
color="blue-darken-1"
|
|
165
|
+
variant="text"
|
|
166
|
+
@click="state.deleteDialog = false"
|
|
167
|
+
>
|
|
168
|
+
Cancel
|
|
169
|
+
</v-btn>
|
|
170
|
+
<v-btn
|
|
171
|
+
type="submit"
|
|
172
|
+
color="error"
|
|
173
|
+
variant="text"
|
|
174
|
+
@click="deleteAction()"
|
|
175
|
+
>
|
|
176
|
+
Delete
|
|
177
|
+
</v-btn>
|
|
178
|
+
</v-card-actions>
|
|
179
|
+
</v-card>
|
|
180
|
+
</v-dialog>
|
|
181
|
+
</v-card>
|
|
182
|
+
</template>
|
|
183
|
+
|
|
184
|
+
<style lang="scss" scoped>
|
|
185
|
+
|
|
186
|
+
</style>
|