101 lines
3.6 KiB
Plaintext
101 lines
3.6 KiB
Plaintext
---
|
|
import DashboardLayout from '../../layouts/DashboardLayout.astro';
|
|
import Icon from '../../components/Icon.astro';
|
|
import { db } from '../../db';
|
|
import { apiTokens, passkeys } from '../../db/schema';
|
|
import { eq, desc } from 'drizzle-orm';
|
|
import ProfileForm from '../../components/settings/ProfileForm.vue';
|
|
import PasswordForm from '../../components/settings/PasswordForm.vue';
|
|
import ApiTokenManager from '../../components/settings/ApiTokenManager.vue';
|
|
import PasskeyManager from '../../components/settings/PasskeyManager.vue';
|
|
|
|
const user = Astro.locals.user;
|
|
if (!user) return Astro.redirect('/login');
|
|
|
|
const url = new URL(Astro.request.url);
|
|
const successType = url.searchParams.get('success');
|
|
|
|
const userTokens = await db.select()
|
|
.from(apiTokens)
|
|
.where(eq(apiTokens.userId, user.id))
|
|
.orderBy(desc(apiTokens.createdAt))
|
|
.all();
|
|
|
|
const userPasskeys = await db.select()
|
|
.from(passkeys)
|
|
.where(eq(passkeys.userId, user.id))
|
|
.orderBy(desc(passkeys.createdAt))
|
|
.all();
|
|
---
|
|
|
|
<DashboardLayout title="Account Settings - Chronus">
|
|
<div class="max-w-4xl mx-auto px-4 sm:px-6">
|
|
<h1 class="text-2xl font-extrabold tracking-tight mb-6 sm:mb-8">
|
|
Account Settings
|
|
</h1>
|
|
|
|
{/* Success Messages */}
|
|
{successType === 'profile' && (
|
|
<div class="alert alert-success mb-6">
|
|
<Icon name="check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
|
|
<span class="text-sm sm:text-base">Profile updated successfully!</span>
|
|
</div>
|
|
)}
|
|
|
|
{successType === 'password' && (
|
|
<div class="alert alert-success mb-6">
|
|
<Icon name="check-circle" class="w-5 h-5 sm:w-6 sm:h-6 shrink-0" />
|
|
<span class="text-sm sm:text-base">Password changed successfully!</span>
|
|
</div>
|
|
)}
|
|
|
|
<!-- Profile Information -->
|
|
<ProfileForm client:idle user={user} />
|
|
|
|
<!-- Change Password -->
|
|
<PasswordForm client:idle />
|
|
|
|
<!-- Passkeys -->
|
|
<PasskeyManager client:idle initialPasskeys={userPasskeys.map(pk => ({
|
|
...pk,
|
|
lastUsedAt: pk.lastUsedAt ? pk.lastUsedAt.toISOString() : null,
|
|
createdAt: pk.createdAt ? pk.createdAt.toISOString() : null
|
|
}))} />
|
|
|
|
<!-- API Tokens -->
|
|
<ApiTokenManager client:idle initialTokens={userTokens.map(t => ({
|
|
...t,
|
|
lastUsedAt: t.lastUsedAt ? t.lastUsedAt.toISOString() : null,
|
|
createdAt: t.createdAt ? t.createdAt.toISOString() : ''
|
|
}))} />
|
|
|
|
<div class="card card-border bg-base-100">
|
|
<div class="card-body p-4">
|
|
<h2 class="text-sm font-semibold flex items-center gap-2 mb-4">
|
|
<Icon name="information-circle" class="w-4 h-4" />
|
|
Account Information
|
|
</h2>
|
|
|
|
<div class="space-y-3">
|
|
<div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-200 gap-2 sm:gap-0">
|
|
<span class="text-base-content/60 text-sm">Account ID</span>
|
|
<span class="font-mono text-xs break-all">{user.id}</span>
|
|
</div>
|
|
<div class="flex flex-col sm:flex-row sm:justify-between py-3 border-b border-base-200 gap-2 sm:gap-0">
|
|
<span class="text-base-content/60 text-sm">Email</span>
|
|
<span class="text-sm break-all">{user.email}</span>
|
|
</div>
|
|
<div class="flex flex-col sm:flex-row sm:justify-between py-3 gap-2 sm:gap-0">
|
|
<span class="text-base-content/60 text-sm">Site Administrator</span>
|
|
<span class={user.isSiteAdmin ? "badge badge-xs badge-primary" : "badge badge-xs badge-ghost"}>
|
|
{user.isSiteAdmin ? "Yes" : "No"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</DashboardLayout>
|