Files
tablejerseys-web/app/pages/profile.vue
Frank John Begornia 3ba0b250ed
Some checks failed
Deploy Production / deploy (push) Has been cancelled
first commit
2026-01-12 22:16:36 +08:00

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>