feat: Frontend for Account Manager
This commit is contained in:
parent
09dce72dfc
commit
79552dd57c
@ -24,7 +24,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.16",
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"bits-ui": "^0.22.0",
|
"bits-ui": "0.22.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"eslint": "^9.18.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-config-prettier": "^10.0.1",
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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()}
|
{@render children()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
34
src/lib/components/ui/select/index.ts
Normal file
34
src/lib/components/ui/select/index.ts
Normal 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,
|
||||||
|
};
|
36
src/lib/components/ui/select/select-content.svelte
Normal file
36
src/lib/components/ui/select/select-content.svelte
Normal 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>
|
37
src/lib/components/ui/select/select-item.svelte
Normal file
37
src/lib/components/ui/select/select-item.svelte
Normal 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>
|
13
src/lib/components/ui/select/select-label.svelte
Normal file
13
src/lib/components/ui/select/select-label.svelte
Normal 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>
|
11
src/lib/components/ui/select/select-separator.svelte
Normal file
11
src/lib/components/ui/select/select-separator.svelte
Normal 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} />
|
24
src/lib/components/ui/select/select-trigger.svelte
Normal file
24
src/lib/components/ui/select/select-trigger.svelte
Normal 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>
|
12
src/routes/(main)/account/+page.server.ts
Normal file
12
src/routes/(main)/account/+page.server.ts
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
76
src/routes/(main)/account/+page.svelte
Normal file
76
src/routes/(main)/account/+page.svelte
Normal 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>
|
Loading…
Reference in New Issue
Block a user