Initial commit

This commit is contained in:
2026-06-24 23:47:55 -07:00
commit d134b480a0
297 changed files with 30726 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
<template>
<PageLoader message="ALPHANE" />
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue"
import { useRouter } from "vue-router"
import PageLoader from "@/components/common/PageLoader.vue"
const router = useRouter()
const timeout = ref<number>()
onMounted(() => {
timeout.value = setTimeout(() => {
router.push({ name: "DashboardPage" })
}, 5000)
})
onUnmounted(() => {
clearTimeout(timeout.value)
})
</script>
+16
View File
@@ -0,0 +1,16 @@
<template>
<div v-if="isSystemAdmin">
<AppCard :to="{ name: 'administration/DashboardPage' }">You are a system admin</AppCard>
</div>
</template>
<script lang="ts" setup>
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useCurrentUser from "@/use/use-current-user"
import AppCard from "@/components/common/AppCard.vue"
const { isSystemAdmin } = useCurrentUser<true>()
useBreadcrumbs()
</script>
+27
View File
@@ -0,0 +1,27 @@
<template>
<v-row>
<v-col>
<UserProfileCard :user-id="currentUser.id" />
</v-col>
</v-row>
</template>
<script lang="ts" setup>
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useCurrentUser from "@/use/use-current-user"
import UserProfileCard from "@/components/users/UserProfileCard.vue"
const { currentUser } = useCurrentUser<true>()
useBreadcrumbs("My Profile", [
{
title: "My Profile",
to: {
name: "ProfilePage",
},
},
])
</script>
<style scoped></style>
+115
View File
@@ -0,0 +1,115 @@
<template>
<div class="pa-3">
<v-row class="h-100vh auth">
<v-col
cols="12"
lg="5"
xl="4"
class="d-flex align-center justify-center bg-surface"
>
<div class="mt-xl-0 mt-5 mw-100 text-center">
<div
class="px-8"
style="max-width: 500px"
>
<img
class="d-inline-block d-lg-none"
src="@/assets/app_logo_small.png"
style="height: 66px; transform: rotate(-12deg)"
/>
<h2
class="text-h1 textPrimary font-weight-semibold mb-0"
style="font-size: 2.5rem !important"
>
ALPHANE
</h2>
<div
class="card-subtitle mb-6"
style="font-size: 1.2rem"
>
Helicopter Maintenance Tracking Platform
</div>
<p>
This application is will streamline maintenance control decision making and unlock
operational constraints.
</p>
<h6 class="text-h6 text-medium-emphasis d-flex align-center mt-6 font-weight-medium">
<v-btn
block
color="primary"
@click="doLogin"
>Sign in</v-btn
>
</h6>
<div class="my-6 text-medium-emphasis">
If you don't have an account, create one to get started using ROTYR. It's free to sign
up and only takes a few seconds.
</div>
<!-- <v-btn
block
variant="outlined"
color="primary"
class="mt-3"
:to="{ name: 'SignUpPage' }"
text="Sign Up"
/> -->
<div
v-if="isAuthenticated"
class="mt-5 text-center"
>
<v-btn
color="warning"
variant="text"
@click="logoutWrapper"
>Sign out</v-btn
>
</div>
</div>
</div>
</v-col>
<v-col
cols="12"
lg="7"
xl="8"
class="d-none d-lg-flex align-center justify-center authentication position-relative"
>
<div class="text-center">
<img
src="@/assets/app_logo_splash.png"
class="position-relative"
style="opacity: 0.15; width: 80%"
/>
</div>
</v-col>
</v-row>
</div>
</template>
<script setup lang="ts">
import { onMounted } from "vue"
import { useAuth0 } from "@auth0/auth0-vue"
import useCurrentUser from "@/use/use-current-user"
const { reset: resetCurrentUser } = useCurrentUser()
const { isAuthenticated, loginWithRedirect, logout } = useAuth0()
onMounted(() => {
resetCurrentUser()
})
function doLogin() {
loginWithRedirect({
appState: { target: "/dashboard" },
})
}
async function logoutWrapper() {
await logout({
logoutParams: {
returnTo: window.location.origin,
},
})
}
</script>
+93
View File
@@ -0,0 +1,93 @@
<template>
<v-app>
<v-container>
Return to <router-link :to="{ name: returnTo.name }">{{ returnTo.title }}</router-link>
<v-row class="mt-5">
<v-col cols="12">
<v-card
outlined
class="pa-3"
:loading="isLoading"
>
<v-card-title
>Environment Information
<v-btn
class="ma-0 ml-1"
icon
size="small"
color="success"
title="refresh"
@click="refresh"
>
<v-icon>mdi-cached</v-icon>
</v-btn>
</v-card-title>
<v-list dense>
<v-list-item> Release Tag: {{ environment.releaseTag }} </v-list-item>
<v-list-item> Git Commit Hash: {{ environment.gitCommitHash }} </v-list-item>
</v-list>
</v-card>
</v-col>
</v-row>
</v-container>
</v-app>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref } from "vue"
import { useAuth0 } from "@auth0/auth0-vue"
import http from "@/api/http-client"
const { isAuthenticated } = useAuth0()
const returnTo = computed<{ name: string; title: string }>(() => {
if (isAuthenticated.value) {
return {
name: "DashboardPage",
title: "Dashboard",
}
}
return {
name: "SignInPage",
title: "Sign In",
}
})
const environment = reactive({
releaseTag: "not-set",
gitCommitHash: "not-set",
})
const isLoading = ref(true)
onMounted(async () => {
await refresh()
})
async function fetchVersion() {
return http
.get("/_status")
.then(({ data }) => {
environment.releaseTag = data.RELEASE_TAG
environment.gitCommitHash = data.GIT_COMMIT_HASH
return data
})
.catch((error: unknown) => {
console.error(`Error fetching version: ${error}`)
})
}
async function refresh() {
isLoading.value = true
try {
await fetchVersion()
} catch (error) {
console.error(`Error fetching version: ${error}`)
} finally {
isLoading.value = false
}
}
</script>
@@ -0,0 +1,118 @@
<template>
<div>
<v-row>
<!-- Users Card -->
<v-col
v-if="isSystemAdmin"
cols="12"
md="6"
lg="4"
>
<v-card
elevation="10"
:to="{ name: 'administration/UsersPage' }"
hover
>
<v-card-item>
<div class="d-flex align-center">
<v-avatar
color="success"
size="56"
class="mr-4"
>
<v-icon
size="32"
color="white"
>
mdi-account-group
</v-icon>
</v-avatar>
<div>
<v-card-title class="text-h5">Users</v-card-title>
<v-card-subtitle>Manage users and permissions</v-card-subtitle>
</div>
</div>
</v-card-item>
<v-divider />
<v-card-text>
<div class="d-flex align-center justify-space-between">
<span class="text-h3 font-weight-bold">{{ usersCount }}</span>
<v-icon
size="large"
color="success"
>
mdi-chevron-right
</v-icon>
</div>
<div class="text-caption text-medium-emphasis mt-1">Total Users</div>
</v-card-text>
</v-card>
</v-col>
<!-- Settings Card -->
<v-col
v-if="isSystemAdmin"
cols="12"
md="6"
lg="4"
>
<v-card
elevation="10"
:to="{ name: 'administration/SettingsPage' }"
hover
>
<v-card-item>
<div class="d-flex align-center">
<v-avatar
color="info"
size="56"
class="mr-4"
>
<v-icon
size="32"
color="white"
>
mdi-cog
</v-icon>
</v-avatar>
<div>
<v-card-title class="text-h5">Settings</v-card-title>
<v-card-subtitle>System configuration</v-card-subtitle>
</div>
</div>
</v-card-item>
<v-divider />
<v-card-text>
<div class="d-flex align-center justify-space-between">
<span class="text-h3 font-weight-bold">&nbsp;</span>
<v-icon
size="large"
color="info"
>
mdi-chevron-right
</v-icon>
</div>
<div class="text-caption text-medium-emphasis mt-1">Configure System</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script setup lang="ts">
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useUsers from "@/use/use-users"
import useCurrentUser from "@/use/use-current-user"
const { isSystemAdmin } = useCurrentUser()
const { totalCount: usersCount } = useUsers()
useBreadcrumbs("Administration Dashboard", [])
</script>
@@ -0,0 +1,14 @@
<template><div></div></template>
<script setup lang="ts">
import useBreadcrumbs from "@/use/use-breadcrumbs"
useBreadcrumbs("Settings", [
{
title: "Settings",
to: {
name: "administration/SettingsPage",
},
},
])
</script>
@@ -0,0 +1,46 @@
<template>
<HeaderActionsCard
title="Users"
elevation="10"
>
<template
v-if="isSystemAdmin"
#header-actions
>
<v-btn
color="secondary"
:to="{
name: 'administration/users/UserNewPage',
}"
>
<v-icon class="mr-3">mdi-plus</v-icon>
Add User
</v-btn>
</template>
<UsersDataTableServer />
</HeaderActionsCard>
</template>
<script setup lang="ts">
import { computed } from "vue"
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useCurrentUser from "@/use/use-current-user"
import HeaderActionsCard from "@/components/common/HeaderActionsCard.vue"
import UsersDataTableServer from "@/components/users/UsersDataTableServer.vue"
const { isSystemAdmin } = useCurrentUser()
const breadcrumbs = computed(() => [
{
title: "Users",
to: {
name: "administration/UsersPage",
},
},
])
useBreadcrumbs("Users", breadcrumbs)
</script>
@@ -0,0 +1,70 @@
<template>
<v-row>
<v-col>
<UserEditCardForm
:user-id="userId"
:return-to="{
name: 'administration/users/UserPage',
params: {
userId,
},
}"
@saved="refreshUserOrCurrentUser"
/>
</v-col>
</v-row>
</template>
<script lang="ts" setup>
import { computed } from "vue"
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useCurrentUser from "@/use/use-current-user"
import useUser from "@/use/use-user"
import useSnack from "@/use/use-snack"
import UserEditCardForm from "@/components/users/UserEditCardForm.vue"
const props = defineProps<{
userId: string
}>()
const userId = computed(() => parseInt(props.userId))
const { user, refresh } = useUser(userId)
const { currentUser, refresh: refreshCurrentUser } = useCurrentUser<true>()
const snack = useSnack()
async function refreshUserOrCurrentUser() {
if (userId.value === currentUser.value.id) {
await refreshCurrentUser()
snack.info("Logged-in user updated. App refreshed.")
} else {
await refresh()
}
}
const breadcrumbs = computed(() => {
return [
{
title: "Users",
to: {
name: "administration/UsersPage",
},
},
{
title: user.value?.displayName || user.value?.email,
to: {
name: "administration/users/UserPage",
params: {
userId: props.userId,
},
},
},
]
})
useBreadcrumbs("User Details", breadcrumbs)
</script>
<style scoped></style>
@@ -0,0 +1,353 @@
<template>
<HeaderActionsFormCard
ref="headerActionsFormCard"
title="New User"
@submit.prevent="createUser"
>
<v-row>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="userAttributes.firstName"
label="First name *"
required
:rules="[required]"
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="userAttributes.lastName"
label="Last name *"
required
:rules="[required]"
@update:focused="autoFillDependentFields"
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="userAttributes.displayName"
label="Display name *"
required
:rules="[required]"
/>
</v-col>
</v-row>
<v-row>
<v-col
cols="12"
md="6"
>
<UserEmailUniqueTextField
v-model="userAttributes.email"
label="Email *"
:rules="[required, minimum(2), email]"
required
validate-on="blur"
/>
</v-col>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="userAttributes.title"
label="Title"
clearable
/>
</v-col>
</v-row>
<v-row>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="userAttributes.pilotLicense"
label="Pilot License"
placeholder="e.g., CPL-123456"
clearable
/>
</v-col>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="userAttributes.ameLicense"
label="AME License"
placeholder="e.g., AME-789012"
clearable
/>
</v-col>
</v-row>
<h4 class="mt-3">Images</h4>
<v-divider class="mt-1 mb-2" />
<v-row>
<v-col
cols="12"
md="6"
>
<v-file-input
v-model="profileImageFile"
accept="image/*"
prepend-icon="mdi-camera"
label="Upload Profile Image"
clearable
@change="handleProfileImageUpload"
/>
<div
v-if="profileImagePreview"
class="mt-4 d-flex justify-center"
>
<v-img
:src="profileImagePreview"
max-width="200"
max-height="200"
class="rounded elevation-2"
cover
/>
</div>
</v-col>
<v-col
cols="12"
md="6"
>
<v-file-input
v-model="signatureImageFile"
accept="image/*"
prepend-icon="mdi-draw"
label="Upload Signature"
clearable
@change="handleSignatureImageUpload"
/>
<div
v-if="signatureImagePreview"
class="mt-4 d-flex justify-center"
>
<v-img
:src="signatureImagePreview"
max-width="300"
max-height="150"
class="rounded elevation-2"
contain
/>
</div>
</v-col>
</v-row>
<h4 class="mt-3">Notification Details</h4>
<v-divider class="mt-1 mb-2" />
<v-col
cols="12"
md="6"
>
<v-switch
v-model="userAttributes.emailNotificationsEnabled"
label="Email notifications enabled?"
inset
center-affix
/>
</v-col>
<template #actions>
<div class="d-flex">
<v-btn
:loading="isLoading"
type="submit"
>
Create User
</v-btn>
<v-spacer />
<v-btn
color="warning"
class="ml-4"
variant="outlined"
:loading="isLoading"
:to="{
name: 'administration/UsersPage',
}"
>
Cancel
</v-btn>
</div>
</template>
</HeaderActionsFormCard>
</template>
<script setup lang="ts">
import { ref } from "vue"
import { useRouter } from "vue-router"
import { isEmpty, isNil } from "lodash"
import { required, minimum, email } from "@/utils/validators"
import { resizeToStandard } from "@/utils/image-resizer"
import usersApi, { User } from "@/api/users-api"
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useSnack from "@/use/use-snack"
import HeaderActionsFormCard from "@/components/shared/cards/HeaderActionsFormCard.vue"
import UserEmailUniqueTextField from "@/components/users/UserEmailUniqueTextField.vue"
const userAttributes = ref<Partial<User>>({
email: "",
displayName: "",
firstName: "",
lastName: "",
title: null,
pilotLicense: null,
ameLicense: null,
emailNotificationsEnabled: true,
})
// Image handling
const profileImageFile = ref<File[] | File | null>(null)
const signatureImageFile = ref<File[] | File | null>(null)
const profileImagePreview = ref<string | null>(null)
const signatureImagePreview = ref<string | null>(null)
async function handleProfileImageUpload() {
if (!profileImageFile.value) {
profileImagePreview.value = null
userAttributes.value.profileImage = null
return
}
// Handle both array and single file
const file = Array.isArray(profileImageFile.value)
? profileImageFile.value[0]
: profileImageFile.value
if (!file) {
profileImagePreview.value = null
userAttributes.value.profileImage = null
return
}
// Ensure we have a valid File object
if (!(file instanceof File)) {
console.error("Invalid file type:", file)
return
}
try {
// Resize image to 512x512 before upload
const base64DataUrl = await resizeToStandard(file)
profileImagePreview.value = base64DataUrl
userAttributes.value.profileImage = base64DataUrl
} catch (error) {
console.error("Error resizing profile image:", error)
snack.error("Failed to process profile image")
}
}
async function handleSignatureImageUpload() {
if (!signatureImageFile.value) {
signatureImagePreview.value = null
userAttributes.value.signatureImage = null
return
}
// Handle both array and single file
const file = Array.isArray(signatureImageFile.value)
? signatureImageFile.value[0]
: signatureImageFile.value
if (!file) {
signatureImagePreview.value = null
userAttributes.value.signatureImage = null
return
}
// Ensure we have a valid File object
if (!(file instanceof File)) {
console.error("Invalid file type:", file)
return
}
try {
// Resize image to 512x512 before upload
const base64DataUrl = await resizeToStandard(file)
signatureImagePreview.value = base64DataUrl
userAttributes.value.signatureImage = base64DataUrl
} catch (error) {
console.error("Error resizing signature image:", error)
snack.error("Failed to process signature image")
}
}
function autoFillDependentFields(focused: boolean) {
if (focused) return
const { email, displayName, firstName, lastName } = userAttributes.value
if ((isNil(firstName) || isEmpty(firstName)) && (isNil(lastName) || isEmpty(lastName))) return
if (isNil(displayName) || isEmpty(displayName)) {
userAttributes.value.displayName = [firstName, lastName].filter(Boolean).join(" ")
}
if (isNil(email) || isEmpty(email)) {
userAttributes.value.email = `${firstName}.${lastName}@unknown.ca`
}
}
const headerActionsFormCard = ref<InstanceType<typeof HeaderActionsFormCard> | null>(null)
const isLoading = ref(false)
const snack = useSnack()
const router = useRouter()
async function createUser() {
if (headerActionsFormCard.value === null) return
const { valid } = await headerActionsFormCard.value.validate()
if (!valid) return
const userAttributesWithAuthSubject = {
...userAttributes.value,
authSubject: userAttributes.value.email,
}
isLoading.value = true
try {
const { user } = await usersApi.create(userAttributesWithAuthSubject)
snack.success("User created!")
return router.push({
name: "administration/users/UserPage",
params: {
userId: user.id,
},
})
} catch (error) {
console.error(error)
snack.error(`Failed to create user: ${error}`)
} finally {
isLoading.value = false
}
}
useBreadcrumbs("User Creation", [
{
title: "Users",
to: {
name: "administration/UsersPage",
},
},
{
title: "New User",
to: {
name: "administration/users/UserNewPage",
},
},
])
</script>
+79
View File
@@ -0,0 +1,79 @@
<template>
<v-empty-state
headline="Whoops, 403"
title="Access Forbidden"
text="You do not have permission to access that page"
:image="SplashImage"
>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
color="primary"
width="200"
variant="outlined"
@click="goBack"
>Back</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<!-- href="/" performs a more aggressive refresh than using to="xxx" -->
<v-btn
href="/dashboard"
color="primary"
width="200"
variant="outlined"
>Home</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
width="200"
@click="signOut"
>Logout</v-btn
>
</v-col>
</v-row>
<v-divider
class="mt-10 mb-6"
thickness="1"
/>
<p>Site: {{ APPLICATION_NAME }}</p>
<p>Version: {{ releaseTag }}</p>
<p>Commit Hash: {{ gitCommitHash }}</p>
</v-empty-state>
</template>
<script lang="ts" setup>
import SplashImage from "@/assets/SplashImage.png"
import { useAuth0 } from "@auth0/auth0-vue"
import { APPLICATION_NAME } from "@/config"
import useCurrentUser from "@/use/use-current-user"
import useStatus from "@/use/use-status"
const { logout } = useAuth0()
const { reset: resetCurrentUser } = useCurrentUser()
const { releaseTag, gitCommitHash } = useStatus()
function goBack() {
window.history.back()
}
async function signOut() {
resetCurrentUser()
const returnTo = encodeURI(window.location.origin + "/sign-in")
return logout({
logoutParams: {
returnTo,
},
})
}
</script>
<style scoped></style>
@@ -0,0 +1,79 @@
<template>
<v-empty-state
headline="Whoops, 500"
title="Internal Server Error"
:text="'Oops! The server encountered an unexpected error. Please\u00a0contact\u00a0support.'"
:image="SplashImage"
>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
color="primary"
width="200"
variant="outlined"
@click="goBack"
>Back</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<!-- href="/" performs a more aggressive refresh than using to="xxx" -->
<v-btn
href="/dashboard"
color="primary"
width="200"
variant="outlined"
>Home</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
width="200"
@click="signOut"
>Logout</v-btn
>
</v-col>
</v-row>
<v-divider
class="mt-10 mb-6"
thickness="1"
/>
<p>Site: {{ APPLICATION_NAME }}</p>
<p>Version: {{ releaseTag }}</p>
<p>Commit Hash: {{ gitCommitHash }}</p>
</v-empty-state>
</template>
<script lang="ts" setup>
import SplashImage from "@/assets/SplashImage.png"
import { useAuth0 } from "@auth0/auth0-vue"
import { APPLICATION_NAME } from "@/config"
import useCurrentUser from "@/use/use-current-user"
import useStatus from "@/use/use-status"
const { logout } = useAuth0()
const { reset: resetCurrentUser } = useCurrentUser()
const { releaseTag, gitCommitHash } = useStatus()
function goBack() {
window.history.back()
}
async function signOut() {
resetCurrentUser()
const returnTo = encodeURI(window.location.origin + "/sign-in")
return logout({
logoutParams: {
returnTo,
},
})
}
</script>
<style scoped></style>
+79
View File
@@ -0,0 +1,79 @@
<template>
<v-empty-state
headline="Whoops, 404"
title="Page Not Found"
text="Oops! The page you're looking doesn't exist."
:image="SplashImage"
>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
color="primary"
width="200"
variant="outlined"
@click="goBack"
>Back</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<!-- href="/" performs a more aggressive refresh than using to="xxx" -->
<v-btn
href="/dashboard"
color="primary"
width="200"
variant="outlined"
>Home</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
width="200"
@click="signOut"
>Logout</v-btn
>
</v-col>
</v-row>
<v-divider
class="mt-10 mb-6"
thickness="1"
/>
<p>Site: {{ APPLICATION_NAME }}</p>
<p>Version: {{ releaseTag }}</p>
<p>Commit Hash: {{ gitCommitHash }}</p>
</v-empty-state>
</template>
<script lang="ts" setup>
import SplashImage from "@/assets/SplashImage.png"
import { useAuth0 } from "@auth0/auth0-vue"
import { APPLICATION_NAME } from "@/config"
import useCurrentUser from "@/use/use-current-user"
import useStatus from "@/use/use-status"
const { logout } = useAuth0()
const { reset: resetCurrentUser } = useCurrentUser()
const { releaseTag, gitCommitHash } = useStatus()
function goBack() {
window.history.back()
}
async function signOut() {
resetCurrentUser()
const returnTo = encodeURI(window.location.origin + "/sign-in")
return logout({
logoutParams: {
returnTo,
},
})
}
</script>
<style scoped></style>
+79
View File
@@ -0,0 +1,79 @@
<template>
<v-empty-state
headline="Whoops, 401"
title="Unauthorized"
text="If you think this is an error, please contact support. Alternatively, try logging out and signing back in."
:image="SplashImage"
>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
color="primary"
width="200"
variant="outlined"
@click="goBack"
>Back</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<!-- href="/" performs a more aggressive refresh than using to="xxx" -->
<v-btn
href="/dashboard"
color="primary"
width="200"
variant="outlined"
>Home</v-btn
>
</v-col>
</v-row>
<v-row>
<v-col class="d-flex justify-center">
<v-btn
width="200"
@click="signOut"
>Logout</v-btn
>
</v-col>
</v-row>
<v-divider
class="mt-10 mb-6"
thickness="1"
/>
<p>Site: {{ APPLICATION_NAME }}</p>
<p>Version: {{ releaseTag }}</p>
<p>Commit Hash: {{ gitCommitHash }}</p>
</v-empty-state>
</template>
<script lang="ts" setup>
import SplashImage from "@/assets/SplashImage.png"
import { useAuth0 } from "@auth0/auth0-vue"
import { APPLICATION_NAME } from "@/config"
import useCurrentUser from "@/use/use-current-user"
import useStatus from "@/use/use-status"
const { logout } = useAuth0()
const { reset: resetCurrentUser } = useCurrentUser()
const { releaseTag, gitCommitHash } = useStatus()
function goBack() {
window.history.back()
}
async function signOut() {
resetCurrentUser()
const returnTo = encodeURI(window.location.origin + "/sign-in")
return logout({
logoutParams: {
returnTo,
},
})
}
</script>
<style scoped></style>
+46
View File
@@ -0,0 +1,46 @@
<template>
<v-row>
<v-col>
<UserEditCardForm
:user-id="currentUser.id"
:return-to="{
name: 'ProfilePage',
}"
@saved="refreshWrapper"
/>
</v-col>
</v-row>
</template>
<script lang="ts" setup>
import useBreadcrumbs from "@/use/use-breadcrumbs"
import useCurrentUser from "@/use/use-current-user"
import useSnack from "@/use/use-snack"
import UserEditCardForm from "@/components/users/UserEditCardForm.vue"
const { currentUser, refresh } = useCurrentUser<true>()
const snack = useSnack()
async function refreshWrapper() {
await refresh()
snack.info("Logged-in user updated. App refreshed.")
}
useBreadcrumbs("Edit My Profile", [
{
title: "My Profile",
to: {
name: "ProfilePage",
},
},
{
title: "Edit",
to: {
name: "profile/ProfileEditPage",
},
},
])
</script>
<style scoped></style>