@edgedev/create-edge-app 1.1.25 → 1.1.27
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/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 +1475 -0
- package/edge/components/cms/themeDefaultMenu.vue +548 -0
- package/edge/components/cms/themeEditor.vue +429 -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/package.json +1 -1
- package/edge-components-install.sh +0 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
class: String,
|
|
4
|
+
})
|
|
5
|
+
const state = reactive({
|
|
6
|
+
count: 20,
|
|
7
|
+
})
|
|
8
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
9
|
+
|
|
10
|
+
const incrementCount = async () => {
|
|
11
|
+
while (true) {
|
|
12
|
+
if (state.count < 100) {
|
|
13
|
+
state.count += 10
|
|
14
|
+
await sleep(100) // Wait for 100 milliseconds
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
state.count = 0
|
|
18
|
+
await sleep(500) // Wait for 100 milliseconds
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
incrementCount()
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<div class="flex h-full w-full align-bottom items-center">
|
|
28
|
+
<Card :class="props.class" class="w-full pt-10 pb-6">
|
|
29
|
+
<CardContent class="text-center">
|
|
30
|
+
<slot>
|
|
31
|
+
Loading...
|
|
32
|
+
</slot>
|
|
33
|
+
<Progress v-model="state.count" class="w-100" />
|
|
34
|
+
</CardContent>
|
|
35
|
+
</Card>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<style lang="scss" scoped>
|
|
40
|
+
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
type: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: false,
|
|
8
|
+
default: 'Card',
|
|
9
|
+
},
|
|
10
|
+
class: {
|
|
11
|
+
type: String,
|
|
12
|
+
required: false,
|
|
13
|
+
default: '',
|
|
14
|
+
},
|
|
15
|
+
menuItems: {
|
|
16
|
+
type: Array,
|
|
17
|
+
required: false,
|
|
18
|
+
default: () => [],
|
|
19
|
+
},
|
|
20
|
+
navClass: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: false,
|
|
23
|
+
default: '',
|
|
24
|
+
},
|
|
25
|
+
buttonClass: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: false,
|
|
28
|
+
default: '',
|
|
29
|
+
},
|
|
30
|
+
singleOrg: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
showIcon: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
required: false,
|
|
37
|
+
default: true,
|
|
38
|
+
},
|
|
39
|
+
selectedBgColor: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: false,
|
|
42
|
+
default: '',
|
|
43
|
+
},
|
|
44
|
+
selectedTextColor: {
|
|
45
|
+
type: String,
|
|
46
|
+
required: false,
|
|
47
|
+
default: '',
|
|
48
|
+
},
|
|
49
|
+
showStart: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
required: false,
|
|
52
|
+
default: true,
|
|
53
|
+
},
|
|
54
|
+
showEnd: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
required: false,
|
|
57
|
+
default: true,
|
|
58
|
+
},
|
|
59
|
+
showCenter: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
required: false,
|
|
62
|
+
default: true,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const typeClasses = computed(() => {
|
|
67
|
+
return {
|
|
68
|
+
header: 'top-0',
|
|
69
|
+
footer: 'bottom-0',
|
|
70
|
+
div: '',
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<Card
|
|
77
|
+
v-if="props.type === 'Card'"
|
|
78
|
+
:class="cn(typeClasses[props.type], 'z-10 flex items-center gap-1 px-4 flex-shrink-0 overflow-hidden', props.class)"
|
|
79
|
+
>
|
|
80
|
+
<edge-menu-content
|
|
81
|
+
v-bind="props"
|
|
82
|
+
>
|
|
83
|
+
<template v-if="props.showStart" #start>
|
|
84
|
+
<slot name="start" />
|
|
85
|
+
</template>
|
|
86
|
+
<template v-if="props.showCenter" #center>
|
|
87
|
+
<slot name="center" />
|
|
88
|
+
</template>
|
|
89
|
+
<template v-if="props.showEnd" #end>
|
|
90
|
+
<slot name="end" />
|
|
91
|
+
</template>
|
|
92
|
+
</edge-menu-content>
|
|
93
|
+
</Card>
|
|
94
|
+
|
|
95
|
+
<nav
|
|
96
|
+
v-else-if="props.type === 'nav'"
|
|
97
|
+
:class="cn(typeClasses[props.type], 'z-10 flex items-center gap-1 border-b px-4 flex-shrink-0', props.class)"
|
|
98
|
+
>
|
|
99
|
+
<edge-menu-content
|
|
100
|
+
v-bind="props"
|
|
101
|
+
>
|
|
102
|
+
<template v-if="props.showStart" #start>
|
|
103
|
+
<slot name="start" />
|
|
104
|
+
</template>
|
|
105
|
+
<template v-if="props.showCenter" #center>
|
|
106
|
+
<slot name="center" />
|
|
107
|
+
</template>
|
|
108
|
+
<template v-if="props.showEnd" #end>
|
|
109
|
+
<slot name="end" />
|
|
110
|
+
</template>
|
|
111
|
+
</edge-menu-content>
|
|
112
|
+
</nav>
|
|
113
|
+
<footer
|
|
114
|
+
v-else-if="props.type === 'footer'"
|
|
115
|
+
:class="cn(typeClasses[props.type], 'z-10 flex items-center gap-1 border-t px-4 flex-shrink-0', props.class)"
|
|
116
|
+
>
|
|
117
|
+
<edge-menu-content
|
|
118
|
+
v-bind="props"
|
|
119
|
+
>
|
|
120
|
+
<template v-if="props.showStart" #start>
|
|
121
|
+
<slot name="start" />
|
|
122
|
+
</template>
|
|
123
|
+
<template v-if="props.showCenter" #center>
|
|
124
|
+
<slot name="center" />
|
|
125
|
+
</template>
|
|
126
|
+
<template v-if="props.showEnd" #end>
|
|
127
|
+
<slot name="end" />
|
|
128
|
+
</template>
|
|
129
|
+
</edge-menu-content>
|
|
130
|
+
</footer>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<style lang="scss" scoped>
|
|
134
|
+
.inverted-logo {
|
|
135
|
+
filter: invert(1);
|
|
136
|
+
}
|
|
137
|
+
</style>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
type: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: false,
|
|
8
|
+
default: 'Card',
|
|
9
|
+
},
|
|
10
|
+
singleOrg: {
|
|
11
|
+
type: Boolean,
|
|
12
|
+
default: false,
|
|
13
|
+
},
|
|
14
|
+
class: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: false,
|
|
17
|
+
default: '',
|
|
18
|
+
},
|
|
19
|
+
menuItems: {
|
|
20
|
+
type: Array,
|
|
21
|
+
required: false,
|
|
22
|
+
default: () => [],
|
|
23
|
+
},
|
|
24
|
+
navClass: {
|
|
25
|
+
type: String,
|
|
26
|
+
required: false,
|
|
27
|
+
default: '',
|
|
28
|
+
},
|
|
29
|
+
buttonClass: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: false,
|
|
32
|
+
default: '',
|
|
33
|
+
},
|
|
34
|
+
showIcon: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
required: false,
|
|
37
|
+
default: true,
|
|
38
|
+
},
|
|
39
|
+
selectedBgColor: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: false,
|
|
42
|
+
default: '',
|
|
43
|
+
},
|
|
44
|
+
selectedTextColor: {
|
|
45
|
+
type: String,
|
|
46
|
+
required: false,
|
|
47
|
+
default: '',
|
|
48
|
+
},
|
|
49
|
+
showStart: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
required: false,
|
|
52
|
+
default: true,
|
|
53
|
+
},
|
|
54
|
+
showEnd: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
required: false,
|
|
57
|
+
default: true,
|
|
58
|
+
},
|
|
59
|
+
showCenter: {
|
|
60
|
+
type: Boolean,
|
|
61
|
+
required: false,
|
|
62
|
+
default: true,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const route = useRoute()
|
|
67
|
+
|
|
68
|
+
const orgName = computed(() => {
|
|
69
|
+
const org = edgeGlobal.edgeState.organizations.find(
|
|
70
|
+
org => org.docId === edgeGlobal.edgeState.currentOrganization,
|
|
71
|
+
)
|
|
72
|
+
return org?.name
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const startsWithCurrentRoute = (path) => {
|
|
76
|
+
const currentRoutePath = route.fullPath.endsWith('/')
|
|
77
|
+
? route.fullPath.substring(0, route.fullPath.length - 1)
|
|
78
|
+
: route.fullPath
|
|
79
|
+
return (
|
|
80
|
+
path === currentRoutePath || currentRoutePath.startsWith(path)
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<div v-if="props.showStart" class="flex items-center gap-1">
|
|
87
|
+
<slot name="start">
|
|
88
|
+
<Package class="h-6 w-6 mr-2" />
|
|
89
|
+
<h1 class="text-xl font-bold">
|
|
90
|
+
{{ orgName }}
|
|
91
|
+
</h1>
|
|
92
|
+
</slot>
|
|
93
|
+
</div>
|
|
94
|
+
<div v-if="props.showCenter" class="grow flex items-center gap-1">
|
|
95
|
+
<slot name="center" />
|
|
96
|
+
<div v-if="props.menuItems.length > 0" class="grow">
|
|
97
|
+
<nav
|
|
98
|
+
:class="cn('justify-center ml-4 hidden flex-col gap-3 text-lg font-medium md:flex md:flex-row md:items-center md:gap-2 md:text-sm lg:gap-3', navClass)"
|
|
99
|
+
>
|
|
100
|
+
<edge-shad-button
|
|
101
|
+
v-for="(item, key) in props.menuItems"
|
|
102
|
+
:key="key"
|
|
103
|
+
:to="item.to"
|
|
104
|
+
:class="cn(
|
|
105
|
+
'transition-colors px-0',
|
|
106
|
+
hoverClass,
|
|
107
|
+
buttonClass,
|
|
108
|
+
{
|
|
109
|
+
[selectedBgColor]: startsWithCurrentRoute(item.to),
|
|
110
|
+
[selectedTextColor]: startsWithCurrentRoute(item.to),
|
|
111
|
+
},
|
|
112
|
+
)"
|
|
113
|
+
variant="text"
|
|
114
|
+
>
|
|
115
|
+
<component
|
|
116
|
+
:is="item.icon"
|
|
117
|
+
v-if="item.icon && props.showIcon"
|
|
118
|
+
class="h-4 w-4 mr-1"
|
|
119
|
+
/>
|
|
120
|
+
{{ item.title }}
|
|
121
|
+
</edge-shad-button>
|
|
122
|
+
</nav>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div v-if="props.showEnd" class="flex items-center gap-1">
|
|
126
|
+
<slot name="end">
|
|
127
|
+
<div class="grow text-right">
|
|
128
|
+
<edge-user-menu :single-org="props.singleOrg" button-class="bg-primary" />
|
|
129
|
+
</div>
|
|
130
|
+
</slot>
|
|
131
|
+
</div>
|
|
132
|
+
</template>
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, inject, nextTick, onBeforeMount, reactive, watch } from 'vue'
|
|
3
|
+
import { toTypedSchema } from '@vee-validate/zod'
|
|
4
|
+
import * as z from 'zod'
|
|
5
|
+
const edgeFirebase = inject('edgeFirebase')
|
|
6
|
+
// const edgeGlobal = inject('edgeGlobal')
|
|
7
|
+
|
|
8
|
+
const router = useRouter()
|
|
9
|
+
|
|
10
|
+
const state = reactive({
|
|
11
|
+
loading: false,
|
|
12
|
+
username: '',
|
|
13
|
+
newPassword: '',
|
|
14
|
+
oldPassword: '',
|
|
15
|
+
passwordForm: false,
|
|
16
|
+
userForm: false,
|
|
17
|
+
loaded: true,
|
|
18
|
+
passwordVisible: false,
|
|
19
|
+
passwordShow: false,
|
|
20
|
+
passwordError: { success: true, message: '' },
|
|
21
|
+
userError: { success: true, message: '' },
|
|
22
|
+
showDeleteAccount: false,
|
|
23
|
+
deleteForm: false,
|
|
24
|
+
})
|
|
25
|
+
const updateUser = async () => {
|
|
26
|
+
state.loading = true
|
|
27
|
+
state.userError = await edgeFirebase.updateEmail(state.username)
|
|
28
|
+
if (state.userError.message === 'Firebase: Error (auth/email-already-in-use).') {
|
|
29
|
+
state.userError = { success: false, message: 'Email already in use.' }
|
|
30
|
+
}
|
|
31
|
+
if (state.userError.message === 'Firebase: Error (auth/requires-recent-login).') {
|
|
32
|
+
state.userError = { success: false, message: 'Please log out and log back in to change your email.' }
|
|
33
|
+
}
|
|
34
|
+
state.userError = { success: state.userError.success, message: state.userError.message.replace('Firebase: ', '').replace(' (auth/invalid-email)', '') }
|
|
35
|
+
if (state.userError.success) {
|
|
36
|
+
state.userError = { success: true, message: 'A verification link has been sent to your new email address. Please click the link to complete the email change process.' }
|
|
37
|
+
}
|
|
38
|
+
edgeGlobal.edgeState.changeTracker = {}
|
|
39
|
+
state.loaded = false
|
|
40
|
+
state.loading = false
|
|
41
|
+
await nextTick()
|
|
42
|
+
state.loaded = true
|
|
43
|
+
}
|
|
44
|
+
const updatePassword = async () => {
|
|
45
|
+
state.loading = true
|
|
46
|
+
state.passwordError = await edgeFirebase.setPassword(state.oldPassword, state.newPassword)
|
|
47
|
+
if (state.passwordError.message === 'Firebase: Error (auth/wrong-password).') {
|
|
48
|
+
state.passwordError = { success: false, message: 'Old Password is incorrect.' }
|
|
49
|
+
}
|
|
50
|
+
state.passwordError = { success: state.passwordError.success, message: state.passwordError.message.replace('Firebase: ', '').replace(' (auth/weak-password)', '') }
|
|
51
|
+
if (state.passwordError.success) {
|
|
52
|
+
state.oldPassword = ''
|
|
53
|
+
state.newPassword = ''
|
|
54
|
+
state.passwordError = { success: true, message: 'Password successfully changed' }
|
|
55
|
+
}
|
|
56
|
+
edgeGlobal.edgeState.changeTracker = {}
|
|
57
|
+
state.loading = false
|
|
58
|
+
state.loaded = false
|
|
59
|
+
await nextTick()
|
|
60
|
+
state.loaded = true
|
|
61
|
+
}
|
|
62
|
+
const deleteAccount = async () => {
|
|
63
|
+
state.loading = true
|
|
64
|
+
await edgeFirebase.runFunction('edgeFirebase-deleteSelf', { uid: edgeFirebase.user.uid })
|
|
65
|
+
await edgeFirebase.logOut()
|
|
66
|
+
state.loading = false
|
|
67
|
+
router.push('/app/login')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const currentOrgName = computed(() => {
|
|
71
|
+
if (edgeGlobal.objHas(edgeFirebase.data, edgeGlobal.edgeState.organizationDocPath) === false) {
|
|
72
|
+
return ''
|
|
73
|
+
}
|
|
74
|
+
return edgeFirebase.data[edgeGlobal.edgeState.organizationDocPath].name
|
|
75
|
+
})
|
|
76
|
+
onBeforeMount(() => {
|
|
77
|
+
if (edgeFirebase.user.firebaseUser.providerData.length === 0) {
|
|
78
|
+
state.username = edgeFirebase.user.uid
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
state.username = edgeFirebase.user.firebaseUser.providerData[0].email
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
watch(currentOrgName, async () => {
|
|
85
|
+
state.org = currentOrgName.value
|
|
86
|
+
edgeGlobal.edgeState.changeTracker = {}
|
|
87
|
+
state.loaded = false
|
|
88
|
+
await nextTick()
|
|
89
|
+
state.loaded = true
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
|
93
|
+
|
|
94
|
+
const passwordSchema = toTypedSchema(z.object({
|
|
95
|
+
oldPassword: z.string({
|
|
96
|
+
required_error: 'Password is required',
|
|
97
|
+
}).superRefine((value, ctx) => {
|
|
98
|
+
if (value.length < 8 || value.length > 50 || !passwordPattern.test(value)) {
|
|
99
|
+
ctx.addIssue({
|
|
100
|
+
code: z.ZodIssueCode.custom,
|
|
101
|
+
message: 'Password must have at least 8 characters, including uppercase and lowercase letters, numbers, and a special character',
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}),
|
|
105
|
+
newPassword: z.string({
|
|
106
|
+
required_error: 'Password is required',
|
|
107
|
+
}).superRefine((value, ctx) => {
|
|
108
|
+
if (value.length < 8 || value.length > 50 || !passwordPattern.test(value)) {
|
|
109
|
+
ctx.addIssue({
|
|
110
|
+
code: z.ZodIssueCode.custom,
|
|
111
|
+
message: 'Password must have at least 8 characters, including uppercase and lowercase letters, numbers, and a special character',
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}),
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
const usernameSchema = toTypedSchema(z.object({
|
|
118
|
+
username: z.string({
|
|
119
|
+
required_error: 'Username is required',
|
|
120
|
+
}).email({
|
|
121
|
+
message: 'Invalid email address',
|
|
122
|
+
}),
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
const deleteSchema = toTypedSchema(z.object({
|
|
126
|
+
delete_account: z.boolean({
|
|
127
|
+
required_error: 'You must confirm that you understand the consequences of deleting your account',
|
|
128
|
+
}),
|
|
129
|
+
}))
|
|
130
|
+
|
|
131
|
+
const route = useRoute()
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<template>
|
|
135
|
+
<Card class="w-full flex-1 bg-muted/50 mx-auto w-full border-none shadow-none pt-2">
|
|
136
|
+
<slot name="header">
|
|
137
|
+
<edge-menu class="bg-secondary text-foreground rounded-none sticky top-0 py-6">
|
|
138
|
+
<template #start>
|
|
139
|
+
<slot name="header-start">
|
|
140
|
+
<component :is="edgeGlobal.iconFromMenu(route)" class="mr-2" />
|
|
141
|
+
<span class="capitalize">My Account</span>
|
|
142
|
+
</slot>
|
|
143
|
+
</template>
|
|
144
|
+
<template #center>
|
|
145
|
+
<slot name="header-center">
|
|
146
|
+
<div class="w-full px-6" />
|
|
147
|
+
</slot>
|
|
148
|
+
</template>
|
|
149
|
+
<template #end>
|
|
150
|
+
<slot name="header-end">
|
|
151
|
+
<div />
|
|
152
|
+
</slot>
|
|
153
|
+
</template>
|
|
154
|
+
</edge-menu>
|
|
155
|
+
</slot>
|
|
156
|
+
<CardContent v-if="state.loaded" class="p-3 w-full overflow-y-auto scroll-area">
|
|
157
|
+
<Card v-if="state.loaded" class="bg-transparent border-0">
|
|
158
|
+
<CardContent>
|
|
159
|
+
<template v-if="edgeFirebase.user.firebaseUser.providerData.length === 0">
|
|
160
|
+
<edge-v-alert>
|
|
161
|
+
Logged in as:
|
|
162
|
+
<edge-v-alert-title>{{ state.username }}</edge-v-alert-title>
|
|
163
|
+
<strong>Custom Provider</strong>
|
|
164
|
+
<Separator class="my-4 dark:bg-slate-600" />
|
|
165
|
+
Notice: You're signed in with a custom provider. Nothing to update here.
|
|
166
|
+
</edge-v-alert>
|
|
167
|
+
</template>
|
|
168
|
+
<template v-else-if="edgeFirebase.user.firebaseUser.providerData[0].providerId === 'password'">
|
|
169
|
+
<div class="mb-2 font-bold">
|
|
170
|
+
Update Email
|
|
171
|
+
</div>
|
|
172
|
+
<edge-shad-form
|
|
173
|
+
v-model="state.userForm"
|
|
174
|
+
:schema="usernameSchema"
|
|
175
|
+
@submit="updateUser"
|
|
176
|
+
>
|
|
177
|
+
<edge-g-input
|
|
178
|
+
v-model="state.username"
|
|
179
|
+
name="username"
|
|
180
|
+
field-type="text"
|
|
181
|
+
label="Username"
|
|
182
|
+
parent-tracker-id="my-account"
|
|
183
|
+
hint="Update your email address, which also serves as your username."
|
|
184
|
+
persistent-hint
|
|
185
|
+
/>
|
|
186
|
+
<edge-v-alert
|
|
187
|
+
v-if="state.userError.message !== ''"
|
|
188
|
+
:type="state.userError.success ? 'success' : 'error'"
|
|
189
|
+
dismissible
|
|
190
|
+
class="mt-0 mb-3 text-caption" density="compact" variant="tonal"
|
|
191
|
+
>
|
|
192
|
+
{{ state.userError.message }}
|
|
193
|
+
</edge-v-alert>
|
|
194
|
+
|
|
195
|
+
<edge-shad-button
|
|
196
|
+
type="submit"
|
|
197
|
+
:disabled="state.loading"
|
|
198
|
+
class="text-white bg-slate-800 hover:bg-slate-400"
|
|
199
|
+
>
|
|
200
|
+
<Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
|
|
201
|
+
Update Email
|
|
202
|
+
</edge-shad-button>
|
|
203
|
+
</edge-shad-form>
|
|
204
|
+
<Separator class="my-4 dark:bg-slate-600" />
|
|
205
|
+
<edge-shad-form
|
|
206
|
+
v-model="state.passwordForm"
|
|
207
|
+
:schema="passwordSchema"
|
|
208
|
+
@submit="updatePassword"
|
|
209
|
+
>
|
|
210
|
+
<div class="mb-2 font-bold">
|
|
211
|
+
Change Password
|
|
212
|
+
</div>
|
|
213
|
+
<edge-shad-input
|
|
214
|
+
v-model="state.oldPassword"
|
|
215
|
+
type="password"
|
|
216
|
+
label="Old Password"
|
|
217
|
+
placeholder="Enter your old password"
|
|
218
|
+
name="oldPassword"
|
|
219
|
+
/>
|
|
220
|
+
<edge-shad-input
|
|
221
|
+
v-model="state.newPassword"
|
|
222
|
+
type="password"
|
|
223
|
+
label="New Password"
|
|
224
|
+
placeholder="Enter your new password"
|
|
225
|
+
name="newPassword"
|
|
226
|
+
/>
|
|
227
|
+
<edge-v-alert
|
|
228
|
+
v-if="state.passwordError.message !== ''"
|
|
229
|
+
:type="state.passwordError.success ? 'success' : 'error'"
|
|
230
|
+
dismissible
|
|
231
|
+
class="mt-0 mb-3 text-caption" density="compact" variant="tonal"
|
|
232
|
+
>
|
|
233
|
+
{{ state.passwordError.message }}
|
|
234
|
+
</edge-v-alert>
|
|
235
|
+
<edge-shad-button
|
|
236
|
+
type="submit"
|
|
237
|
+
:disabled="state.loading"
|
|
238
|
+
class="text-white bg-slate-800 hover:bg-slate-400"
|
|
239
|
+
>
|
|
240
|
+
<Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
|
|
241
|
+
Update Password
|
|
242
|
+
</edge-shad-button>
|
|
243
|
+
</edge-shad-form>
|
|
244
|
+
</template>
|
|
245
|
+
<template v-else>
|
|
246
|
+
<edge-v-alert>
|
|
247
|
+
Logged in as:
|
|
248
|
+
<edge-v-alert-title>{{ edgeFirebase.user.firebaseUser.providerData[0].email }}</edge-v-alert-title>
|
|
249
|
+
<strong>Provider: {{ edgeFirebase.user.firebaseUser.providerData[0].providerId }}</strong>
|
|
250
|
+
<Separator class="my-4 dark:bg-slate-600" />
|
|
251
|
+
Notice: You're signed in with a third-party provider. To update your login information, please visit your provider's account settings. Changes cannot be made directly within this app.
|
|
252
|
+
</edge-v-alert>
|
|
253
|
+
</template>
|
|
254
|
+
<van-divider class="my-2">
|
|
255
|
+
<h4 class="font-bold">
|
|
256
|
+
Delete Account
|
|
257
|
+
</h4>
|
|
258
|
+
</van-divider>
|
|
259
|
+
<Separator class="my-4 dark:bg-slate-600" />
|
|
260
|
+
<edge-shad-form
|
|
261
|
+
v-model="state.deleteForm"
|
|
262
|
+
:schema="deleteSchema"
|
|
263
|
+
@submit="deleteAccount"
|
|
264
|
+
>
|
|
265
|
+
<edge-shad-button
|
|
266
|
+
v-if="!state.showDeleteAccount"
|
|
267
|
+
:disabled="state.loading"
|
|
268
|
+
variant="destructive"
|
|
269
|
+
class="w-full"
|
|
270
|
+
@click.stop.prevent="state.showDeleteAccount = true"
|
|
271
|
+
>
|
|
272
|
+
<Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
|
|
273
|
+
Delete Account
|
|
274
|
+
</edge-shad-button>
|
|
275
|
+
<edge-v-alert v-else closable variant="tonal" border="start" type="error" prominent @click:close="state.showDeleteAccount = false">
|
|
276
|
+
<div class="text-xl font-bold">
|
|
277
|
+
Are you sure?
|
|
278
|
+
</div>
|
|
279
|
+
<h3 class="my-2">
|
|
280
|
+
<strong>Warning:</strong> Deleting your account will permanently remove all of your data from this app. This action cannot be undone.
|
|
281
|
+
</h3>
|
|
282
|
+
<edge-g-input
|
|
283
|
+
name="delete_account"
|
|
284
|
+
field-type="boolean"
|
|
285
|
+
label="I understand the consequences of deleting my account."
|
|
286
|
+
:disable-tracking="true"
|
|
287
|
+
/>
|
|
288
|
+
<div class="flex gap-2 items-center">
|
|
289
|
+
<edge-shad-button
|
|
290
|
+
:disabled="state.loading"
|
|
291
|
+
variant="destructive"
|
|
292
|
+
class="text-white bg-slate-800 hover:bg-slate-400 mt-3"
|
|
293
|
+
@click.stop.prevent="state.showDeleteAccount = false"
|
|
294
|
+
>
|
|
295
|
+
Cancel
|
|
296
|
+
</edge-shad-button>
|
|
297
|
+
<edge-shad-button
|
|
298
|
+
type="submit"
|
|
299
|
+
:disabled="state.loading"
|
|
300
|
+
variant="destructive"
|
|
301
|
+
class="text-white mt-3 uppercase text-lg"
|
|
302
|
+
>
|
|
303
|
+
<Loader2 v-if="state.loading" class="w-4 h-4 mr-2 animate-spin" />
|
|
304
|
+
Delete Account
|
|
305
|
+
</edge-shad-button>
|
|
306
|
+
</div>
|
|
307
|
+
</edge-v-alert>
|
|
308
|
+
</edge-shad-form>
|
|
309
|
+
</CardContent>
|
|
310
|
+
</Card>
|
|
311
|
+
</CardContent>
|
|
312
|
+
</Card>
|
|
313
|
+
</template>
|
|
314
|
+
|
|
315
|
+
<style lang="scss" scoped>
|
|
316
|
+
|
|
317
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, defineProps, inject, nextTick, reactive, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
registrationCode: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: '',
|
|
8
|
+
},
|
|
9
|
+
title: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: 'My Organizations',
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const edgeFirebase = inject('edgeFirebase')
|
|
16
|
+
// const edgeGlobal = inject('edgeGlobal')
|
|
17
|
+
|
|
18
|
+
const state = reactive({
|
|
19
|
+
loaded: true,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const roles = computed(() => {
|
|
23
|
+
return edgeFirebase.user.roles
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
watch(roles, async () => {
|
|
27
|
+
await edgeGlobal.getOrganizations(edgeFirebase)
|
|
28
|
+
state.loaded = false
|
|
29
|
+
await nextTick()
|
|
30
|
+
state.loaded = true
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const route = useRoute()
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<Card class="w-full flex-1 bg-muted/50 mx-auto w-full border-none shadow-none pt-2">
|
|
38
|
+
<slot name="header">
|
|
39
|
+
<edge-menu class="bg-secondary text-foreground rounded-none sticky top-0 py-6">
|
|
40
|
+
<template #start>
|
|
41
|
+
<slot name="header-start">
|
|
42
|
+
<component :is="edgeGlobal.iconFromMenu(route)" class="mr-2" />
|
|
43
|
+
<span class="capitalize">My Organizations</span>
|
|
44
|
+
</slot>
|
|
45
|
+
</template>
|
|
46
|
+
<template #center>
|
|
47
|
+
<slot name="header-center">
|
|
48
|
+
<div class="w-full px-6" />
|
|
49
|
+
</slot>
|
|
50
|
+
</template>
|
|
51
|
+
<template #end>
|
|
52
|
+
<slot name="header-end">
|
|
53
|
+
<div />
|
|
54
|
+
</slot>
|
|
55
|
+
</template>
|
|
56
|
+
</edge-menu>
|
|
57
|
+
</slot>
|
|
58
|
+
<CardContent v-if="state.loaded" class="p-3 w-full overflow-y-auto scroll-area">
|
|
59
|
+
<edge-g-input
|
|
60
|
+
v-if="state.loaded"
|
|
61
|
+
v-model="edgeGlobal.edgeState.organizations"
|
|
62
|
+
name="organizations"
|
|
63
|
+
:disable-tracking="true"
|
|
64
|
+
field-type="objectList"
|
|
65
|
+
sub-field-type="my-orgs"
|
|
66
|
+
parent-tracker-id="myOrgs"
|
|
67
|
+
:pass-through-props="props.registrationCode"
|
|
68
|
+
/>
|
|
69
|
+
</CardContent>
|
|
70
|
+
</Card>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<style lang="scss" scoped>
|
|
74
|
+
|
|
75
|
+
</style>
|