157 lines
5.4 KiB
Vue
157 lines
5.4 KiB
Vue
<script setup lang="ts">
|
|
const router = useRouter();
|
|
const loginModal = useLoginModal();
|
|
const { user, backendUser, initAuth, isLoading, signOut } = useAuth();
|
|
|
|
onMounted(() => {
|
|
initAuth();
|
|
});
|
|
|
|
const isAuthenticated = computed(() => Boolean(user.value));
|
|
|
|
const displayName = computed(() => {
|
|
return (
|
|
backendUser.value?.name ||
|
|
user.value?.displayName ||
|
|
backendUser.value?.email ||
|
|
user.value?.email ||
|
|
"Slipmat Creator"
|
|
);
|
|
});
|
|
|
|
const displayEmail = computed(() => backendUser.value?.email || user.value?.email || "Unknown");
|
|
|
|
const displayId = computed(() => backendUser.value?.id || user.value?.uid || null);
|
|
|
|
const lastLogin = computed(() => {
|
|
const raw =
|
|
backendUser.value?.lastLogin ||
|
|
backendUser.value?.updatedAt ||
|
|
user.value?.metadata?.lastSignInTime ||
|
|
user.value?.metadata?.creationTime ||
|
|
null;
|
|
|
|
return raw ? new Date(raw) : null;
|
|
});
|
|
|
|
const profileFields = computed(() => {
|
|
const entries: Array<{ label: string; value: string | null }> = [
|
|
{ label: "Display name", value: displayName.value },
|
|
{ label: "Email", value: displayEmail.value },
|
|
];
|
|
|
|
if (displayId.value) {
|
|
entries.push({ label: "User ID", value: displayId.value });
|
|
}
|
|
|
|
if (lastLogin.value) {
|
|
entries.push({ label: "Last login", value: lastLogin.value.toLocaleString() });
|
|
}
|
|
|
|
if (backendUser.value?.role) {
|
|
entries.push({ label: "Role", value: String(backendUser.value.role) });
|
|
}
|
|
|
|
if (backendUser.value?.createdAt) {
|
|
entries.push({ label: "Created", value: new Date(backendUser.value.createdAt).toLocaleString() });
|
|
}
|
|
|
|
return entries;
|
|
});
|
|
|
|
const openLogin = () => {
|
|
loginModal.value = true;
|
|
router.push("/");
|
|
};
|
|
|
|
const handleSignOut = async () => {
|
|
try {
|
|
await signOut();
|
|
router.push("/");
|
|
} catch (error) {
|
|
console.error("Sign out failed", error);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<main class="min-h-screen bg-white pb-16 text-slate-900">
|
|
<AppNavbar />
|
|
|
|
<section class="mx-auto flex max-w-3xl flex-col gap-8 px-4 pt-16">
|
|
<header class="space-y-3">
|
|
<p class="text-sm uppercase tracking-[0.35em] text-slate-600">Account</p>
|
|
<h1 class="text-3xl font-semibold text-slate-900">Profile</h1>
|
|
<p class="text-sm text-slate-600">
|
|
View your TableJerseys account details and manage sessions. Changes to your profile are controlled through your authentication provider.
|
|
</p>
|
|
</header>
|
|
|
|
<div v-if="isLoading" class="grid gap-4">
|
|
<div class="h-28 animate-pulse rounded-2xl border border-slate-200 bg-slate-50" />
|
|
<div class="h-28 animate-pulse rounded-2xl border border-slate-200 bg-slate-50" />
|
|
</div>
|
|
|
|
<div v-else-if="!isAuthenticated" class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
<h2 class="text-xl font-semibold text-slate-900">You're signed out</h2>
|
|
<p class="mt-2 text-sm text-slate-600">
|
|
Sign in to view your profile information and order history.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
class="mt-4 rounded-md border-2 border-slate-900 bg-slate-900 px-4 py-2 text-sm font-semibold text-white transition hover:bg-white hover:text-slate-900"
|
|
@click="openLogin"
|
|
>
|
|
Sign in
|
|
</button>
|
|
</div>
|
|
|
|
<div v-else class="space-y-6">
|
|
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
|
<div>
|
|
<h2 class="text-xl font-semibold text-slate-900">{{ displayName }}</h2>
|
|
<p class="text-sm text-slate-600">{{ displayEmail }}</p>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<NuxtLink
|
|
to="/orders"
|
|
class="rounded-md border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 transition hover:border-slate-900 hover:text-slate-900"
|
|
>
|
|
View order history
|
|
</NuxtLink>
|
|
<button
|
|
type="button"
|
|
class="rounded-md border border-rose-300 px-4 py-2 text-sm font-semibold text-rose-600 transition hover:bg-rose-50"
|
|
@click="handleSignOut"
|
|
>
|
|
Sign out
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
<h3 class="text-lg font-semibold text-slate-900">Account details</h3>
|
|
<dl class="mt-4 grid gap-4 text-sm text-slate-700 sm:grid-cols-2">
|
|
<div v-for="field in profileFields" :key="field.label" class="space-y-1">
|
|
<dt class="text-xs uppercase tracking-[0.25em] text-slate-500">{{ field.label }}</dt>
|
|
<dd class="text-sm text-slate-900 break-all">{{ field.value }}</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
<div v-if="backendUser" class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
<h3 class="text-lg font-semibold text-slate-900">Backend session</h3>
|
|
<p class="text-sm text-slate-600">
|
|
The following data is provided by the TableJerseys backend and may include additional metadata used for order fulfillment.
|
|
</p>
|
|
<pre class="mt-4 overflow-x-auto rounded-xl bg-slate-50 p-4 text-xs text-slate-700">
|
|
{{ JSON.stringify(backendUser, null, 2) }}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</template>
|