181 lines
5.4 KiB
Svelte
181 lines
5.4 KiB
Svelte
<!-- src/routes/account/++page.svelte -->
|
|
<script lang="ts">
|
|
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
|
import { Badge } from '$lib/components/ui/badge';
|
|
import { Separator } from '$lib/components/ui/separator';
|
|
import { Input } from '$lib/components/ui/input';
|
|
import { Label } from '$lib/components/ui/label';
|
|
import { Switch } from '$lib/components/ui/switch';
|
|
import { Button } from '$lib/components/ui/button';
|
|
import { DefaultUserSettings } from '$lib/types/user';
|
|
import { enhance } from '$app/forms';
|
|
import { toast } from 'svelte-sonner';
|
|
import { Toaster } from '$lib/components/ui/sonner';
|
|
import CheckIcon from '@lucide/svelte/icons/check';
|
|
|
|
let { data, form } = $props();
|
|
|
|
function signOut() {
|
|
document.cookie = 'jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
|
window.location.href = '/';
|
|
}
|
|
|
|
// Use top-level variables for two-way binding. Binding to object properties
|
|
// like `form.name` doesn't create proper reactive two-way bindings in Svelte.
|
|
let name: string = $state(data.userData.name);
|
|
let email: string = $state(data.userData.email);
|
|
let staleItemDays: number =
|
|
$state(data.userData.settings?.staleItemDays ?? DefaultUserSettings.staleItemDays);
|
|
let notifyAllApprovedInquiries: boolean =
|
|
$state(data.userData.settings?.notifyAllApprovedInquiries ?? false);
|
|
let notifyAllTurnedInInquiries: boolean =
|
|
$state(data.userData.settings?.notifyAllTurnedInInquiries ?? false);
|
|
|
|
const formatDate = (date: Date | string) => {
|
|
const d = new Date(date);
|
|
return d.toLocaleString();
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Account</title>
|
|
</svelte:head>
|
|
|
|
<Toaster></Toaster>
|
|
|
|
<div class="container mx-auto max-w-3xl py-10">
|
|
|
|
<Card class="rounded-2xl shadow-sm">
|
|
<CardHeader>
|
|
<div class="flex items-center justify-between">
|
|
<CardTitle class="text-2xl font-semibold">Account Overview</CardTitle>
|
|
<Badge variant="secondary">ID #{data.userData.id}</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
<CardContent class="space-y-6">
|
|
<!-- Editable Profile Form -->
|
|
<form method="POST" class="space-y-6" use:enhance={({ formElement, formData, action, cancel, submitter }) => {
|
|
return async ({ result, update }) => {
|
|
if (result.status === 200) {
|
|
toast('Saved', {
|
|
icon: CheckIcon,
|
|
description: 'Your account has been saved!',
|
|
});
|
|
}
|
|
};
|
|
}
|
|
}>
|
|
|
|
<div class="grid gap-4 sm:grid-cols-2">
|
|
<div class="space-y-2">
|
|
<Label for="username">Username</Label>
|
|
<Input id="username" value={data.userData.username} disabled />
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<Label for="name">Full Name<span class="text-error">*</span></Label>
|
|
<Input id="name" name="name" bind:value={name} />
|
|
</div>
|
|
|
|
<div class="space-y-2 sm:col-span-2">
|
|
<Label for="email">Email<span class="text-error">*</span></Label>
|
|
<Input id="email" name="email" type="email" bind:value={email} />
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<Label for="newPassword">New Password</Label>
|
|
<Input id="newPassword" name="newPassword" />
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<Label for="retypeNewPassword">Retype New Password</Label>
|
|
<Input id="retypeNewPassword" name="retypeNewPassword" />
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div class="space-y-4">
|
|
<h2 class="text-lg font-semibold">Settings</h2>
|
|
<div class="flex items-center justify-between">
|
|
<Label for="staleItemDays" class="text-base">
|
|
<div>
|
|
<p class="font-medium">Stale Item Days</p>
|
|
<p class="text-sm text-muted-foreground">
|
|
Number of days without activity before items show up as stale.
|
|
</p>
|
|
</div>
|
|
</Label>
|
|
<Input
|
|
class="w-min inline-block"
|
|
id="staleItemDays"
|
|
name="staleItemDays"
|
|
type="number"
|
|
bind:value={staleItemDays}
|
|
min="0"
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<Label for="notifyAllApprovedInquiries" class="text-base">
|
|
<div>
|
|
|
|
<p class="font-medium">Notify for All Approved Items</p>
|
|
<p class="text-sm text-muted-foreground">
|
|
Receive notifications for all approved items, not just yours.
|
|
</p>
|
|
</div>
|
|
</Label>
|
|
<Switch
|
|
name="notifyAllApprovedInquiries"
|
|
id="notifyAllApprovedInquiries"
|
|
bind:checked={notifyAllApprovedInquiries}
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<Label for="notifyAllTurnedInInquiries" class="text-base">
|
|
<div>
|
|
|
|
<p class="font-medium">Notify for All Turned In Items</p>
|
|
<p class="text-sm text-muted-foreground">
|
|
Receive notifications for all items turned in to the school lost-and-found.
|
|
</p>
|
|
</div>
|
|
</Label>
|
|
<Switch
|
|
name="notifyAllTurnedInInquiries"
|
|
id="notifyAllTurnedInInquiries"
|
|
bind:checked={notifyAllTurnedInInquiries}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between pt-4">
|
|
<div class="text-sm text-muted-foreground">
|
|
<p>
|
|
|
|
Member since {formatDate(data.userData.createdAt)}
|
|
</p>
|
|
<p>
|
|
|
|
Last sign in {formatDate(data.userData.lastSignIn)}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex gap-2">
|
|
<Button type="submit">Save Changes</Button>
|
|
<Button type="button" onclick={signOut} variant="destructive">
|
|
Sign Out
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|