feat: Frontend for Account Manager

This commit is contained in:
April Hall 2025-02-15 16:44:17 -05:00
parent 09dce72dfc
commit 79552dd57c
Signed by: arithefirst
GPG Key ID: 4508A15C4DB91C5B
11 changed files with 245 additions and 2 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -24,7 +24,7 @@
"@tailwindcss/typography": "^0.5.16",
"@types/markdown-it": "^14.1.2",
"autoprefixer": "^10.4.20",
"bits-ui": "^0.22.0",
"bits-ui": "0.22.0",
"clsx": "^2.1.1",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",

View File

@ -41,7 +41,7 @@
</div>
</div>
</div>
<div class="max-h-screen p-2" style={`max-width: calc(100vw - 1px - ${sidebarWidth}px)`}>
<div class="relative max-h-screen p-2" style={`max-width: calc(100vw - 1px - ${sidebarWidth}px)`}>
{@render children()}
</div>
</div>

View File

@ -0,0 +1,34 @@
import { Select as SelectPrimitive } from "bits-ui";
import Label from "./select-label.svelte";
import Item from "./select-item.svelte";
import Content from "./select-content.svelte";
import Trigger from "./select-trigger.svelte";
import Separator from "./select-separator.svelte";
const Root = SelectPrimitive.Root;
const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export {
Root,
Item,
Group,
Input,
Label,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Item as SelectItem,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
};

View File

@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { scale } from "svelte/transition";
import { cn, flyAndScale } from "$lib/utils.js";
type $$Props = SelectPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let sideOffset: $$Props["sideOffset"] = 4;
export let inTransition: $$Props["inTransition"] = flyAndScale;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = scale;
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
start: 0.95,
opacity: 0,
duration: 50,
};
export { className as class };
</script>
<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
{sideOffset}
class={cn(
"bg-popover text-popover-foreground relative z-50 min-w-[8rem] overflow-hidden rounded-md border shadow-md focus:outline-none",
className
)}
{...$$restProps}
>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>

View File

@ -0,0 +1,37 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import Check from "svelte-radix/Check.svelte";
import { cn } from "$lib/utils.js";
type $$Props = SelectPrimitive.ItemProps;
type $$Events = Required<SelectPrimitive.ItemEvents>;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"];
export let label: $$Props["label"] = undefined;
export let disabled: $$Props["disabled"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...$$restProps}
on:click
on:pointermove
on:focusin
>
<span class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot>
{label || value}
</slot>
</SelectPrimitive.Item>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = SelectPrimitive.LabelProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Label class={cn("px-2 py-1.5 text-sm font-semibold", className)} {...$$restProps}>
<slot />
</SelectPrimitive.Label>

View File

@ -0,0 +1,11 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = SelectPrimitive.SeparatorProps;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Separator class={cn("bg-muted -mx-1 my-1 h-px", className)} {...$$restProps} />

View File

@ -0,0 +1,24 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import CaretSort from "svelte-radix/CaretSort.svelte";
import { cn } from "$lib/utils.js";
type $$Props = SelectPrimitive.TriggerProps;
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<SelectPrimitive.Trigger
class={cn(
"border-input ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring aria-[invalid]:border-destructive data-[placeholder]:[&>span]:text-muted-foreground flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...$$restProps}
>
<slot />
<div>
<CaretSort class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

View File

@ -0,0 +1,12 @@
import { auth } from '$lib/server/db/auth';
import { redirect } from '@sveltejs/kit';
export async function load({ request }): Promise<void> {
const session = await auth.api.getSession({
headers: request.headers,
});
if (!session) {
redirect(307, '/signup');
}
}

View File

@ -0,0 +1,76 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input/index';
import { Label } from '$lib/components/ui/label/index';
import { Button } from '$lib/components/ui/button/index';
import * as Dialog from '$lib/components/ui/dialog';
let open: boolean = $state(false);
</script>
<main class="abs-center w-2/3">
<div class="relative grid w-full grid-cols-1 gap-3 md:grid-cols-2">
<!-- Update Password -->
<form class="grid w-full items-start gap-3">
<fieldset class="grid w-full gap-3 rounded-lg border p-4">
<legend class="-ml-1 px-1 text-sm font-medium"> Update Password </legend>
<div class="grid gap-3">
<Label for="temperature">Current Password</Label>
<Input id="current" type="password" placeholder="Current Password" />
</div>
<div class="grid gap-3">
<Label for="temperature">New Password</Label>
<Input id="new" type="password" placeholder="New Password" />
</div>
<Button type="submit">Update Password</Button>
</fieldset>
</form>
<!-- Change Username -->
<form class="grid w-full items-start gap-3">
<fieldset class="grid w-full gap-3 rounded-lg border p-4">
<legend class="-ml-1 px-1 text-sm font-medium"> Update Username </legend>
<div class="grid gap-3">
<Label for="temperature">New Username</Label>
<Input id="newUsername" type="password" placeholder="New Username" />
</div>
<div class="grid gap-3">
<Label for="temperature">Password</Label>
<Input id="password" type="password" placeholder="Password" />
</div>
<Button type="submit">Update Username</Button>
</fieldset>
</form>
<!-- Upload Profile Photo -->
<form class="grid w-full items-start gap-3">
<fieldset class="grid w-full gap-3 rounded-lg border p-4">
<legend class="-ml-1 px-1 text-sm font-medium"> Upload Profile Image </legend>
<Input type="file" accept="image/jpeg, image/png" />
<Button type="submit">Update Profile Photo</Button>
</fieldset>
</form>
<!-- Account Actions -->
<div class="grid w-full items-start gap-3">
<fieldset class="grid w-full gap-3 rounded-lg border p-4">
<legend class="-ml-1 px-1 text-sm font-medium"> Account Actions </legend>
<form>
<Button type="submit" class="w-full">Sign Out</Button>
</form>
<Button variant="destructive" class="w-full" onclick={() => (open = !open)}>Delete Account</Button>
</fieldset>
</div>
</div>
</main>
<Dialog.Root bind:open>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Are you sure absolutely sure?</Dialog.Title>
<Dialog.Description>
This action cannot be undone. This will permanently delete your account and remove your data from our database.
<div class="mt-2 flex gap-2">
<Button class="w-1/2" onclick={() => (open = !open)}>I changed my mind!</Button>
<Button variant="destructive" class="w-1/2" type="submit">Delete Account</Button>
</div>
</Dialog.Description>
</Dialog.Header>
</Dialog.Content>
</Dialog.Root>