mirror of
https://github.com/bartvdbraak/omnidash.git
synced 2025-10-25 21:59:09 +00:00
feat: add dashboard and settings pages and components
This commit is contained in:
parent
cb51a4507d
commit
39a36462bb
109 changed files with 3770 additions and 116 deletions
|
|
@ -0,0 +1,43 @@
|
|||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
import { page } from "$app/stores";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import { cubicInOut } from "svelte/easing";
|
||||
import { crossfade } from "svelte/transition";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export let items: { href: string; title: string }[];
|
||||
export { className as class };
|
||||
|
||||
const [send, receive] = crossfade({
|
||||
duration: 250,
|
||||
easing: cubicInOut
|
||||
});
|
||||
</script>
|
||||
|
||||
<nav class={cn("flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1", className)}>
|
||||
{#each items as item}
|
||||
{@const isActive = $page.url.pathname === item.href}
|
||||
|
||||
<Button
|
||||
href={item.href}
|
||||
variant="ghost"
|
||||
class={cn(
|
||||
!isActive && "hover:underline",
|
||||
"relative justify-start hover:bg-transparent"
|
||||
)}
|
||||
data-sveltekit-noscroll
|
||||
>
|
||||
{#if isActive}
|
||||
<div
|
||||
class="absolute inset-0 rounded-md bg-muted"
|
||||
in:send={{ key: "active-sidebar-tab" }}
|
||||
out:receive={{ key: "active-sidebar-tab" }}
|
||||
/>
|
||||
{/if}
|
||||
<div class="relative">
|
||||
{item.title}
|
||||
</div>
|
||||
</Button>
|
||||
{/each}
|
||||
</nav>
|
||||
41
apps/web/src/routes/(dashboard)/settings/+layout.svelte
Normal file
41
apps/web/src/routes/(dashboard)/settings/+layout.svelte
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<script lang="ts">
|
||||
import { Separator } from "$lib/components/ui/separator";
|
||||
import SidebarNav from "./(components)/sidebar-nav.svelte";
|
||||
|
||||
const sidebarNavItems = [
|
||||
{
|
||||
title: "Profile",
|
||||
href: "/settings"
|
||||
},
|
||||
{
|
||||
title: "Account",
|
||||
href: "/settings/account"
|
||||
},
|
||||
{
|
||||
title: "Appearance",
|
||||
href: "/settings/appearance"
|
||||
},
|
||||
{
|
||||
title: "Notifications",
|
||||
href: "/settings/notifications"
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="hidden space-y-6 p-10 pb-16 md:block">
|
||||
<div class="space-y-0.5">
|
||||
<h2 class="text-2xl font-bold tracking-tight">Settings</h2>
|
||||
<p class="text-muted-foreground">
|
||||
Manage your account settings and set e-mail preferences.
|
||||
</p>
|
||||
</div>
|
||||
<Separator class="my-6" />
|
||||
<div class="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
|
||||
<aside class="-mx-4 lg:w-1/5">
|
||||
<SidebarNav items={sidebarNavItems} />
|
||||
</aside>
|
||||
<div class="flex-1 lg:max-w-2xl">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
24
apps/web/src/routes/(dashboard)/settings/+page.server.ts
Normal file
24
apps/web/src/routes/(dashboard)/settings/+page.server.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import type { PageServerLoad } from "./$types";
|
||||
import { superValidate } from "sveltekit-superforms/server";
|
||||
import { profileFormSchema } from "./profile-form.svelte";
|
||||
import { fail, type Actions } from "@sveltejs/kit";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
form: await superValidate(profileFormSchema)
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const form = await superValidate(event, profileFormSchema);
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
}
|
||||
return {
|
||||
form
|
||||
};
|
||||
}
|
||||
};
|
||||
15
apps/web/src/routes/(dashboard)/settings/+page.svelte
Normal file
15
apps/web/src/routes/(dashboard)/settings/+page.svelte
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from "./$types";
|
||||
import ProfileForm from "./profile-form.svelte";
|
||||
import { Separator } from "$lib/components/ui/separator";
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Profile</h3>
|
||||
<p class="text-sm text-muted-foreground">This is how others will see you on the site.</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<ProfileForm data={data.form} />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { superValidate } from "sveltekit-superforms/server";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import { accountFormSchema } from "./account-form.svelte";
|
||||
import { fail, type Actions } from "@sveltejs/kit";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
form: await superValidate(accountFormSchema)
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const form = await superValidate(event, accountFormSchema);
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
}
|
||||
return {
|
||||
form
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { Separator } from "$lib/components/ui/separator";
|
||||
import AccountForm from "./account-form.svelte";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Account</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Update your account settings. Set your preferred language and timezone.
|
||||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<AccountForm data={data.form} />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<script lang="ts" context="module">
|
||||
import { z } from "zod";
|
||||
|
||||
const languages = {
|
||||
en: "English",
|
||||
fr: "French",
|
||||
de: "German",
|
||||
es: "Spanish",
|
||||
pt: "Portuguese",
|
||||
ru: "Russian",
|
||||
ja: "Japanese",
|
||||
ko: "Korean",
|
||||
zh: "Chinese"
|
||||
} as const;
|
||||
|
||||
type Language = keyof typeof languages;
|
||||
|
||||
export const accountFormSchema = z.object({
|
||||
name: z
|
||||
.string({
|
||||
required_error: "Required."
|
||||
})
|
||||
.min(2, "Name must be at least 2 characters.")
|
||||
.max(30, "Name must not be longer than 30 characters"),
|
||||
// Hack: https://github.com/colinhacks/zod/issues/2280
|
||||
language: z.enum(Object.keys(languages) as [Language, ...Language[]])
|
||||
});
|
||||
|
||||
export type AccountFormSchema = typeof accountFormSchema;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import type { SuperValidated } from "sveltekit-superforms";
|
||||
import { cn } from "$lib/utils";
|
||||
|
||||
export let data: SuperValidated<AccountFormSchema>;
|
||||
</script>
|
||||
|
||||
<Form.Root
|
||||
method="POST"
|
||||
class="space-y-8"
|
||||
let:config
|
||||
schema={accountFormSchema}
|
||||
form={data}
|
||||
debug={true}
|
||||
>
|
||||
<Form.Item>
|
||||
<Form.Field name="name" {config}>
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Input placeholder="Your name" />
|
||||
<Form.Description>
|
||||
This is the name that will be displayed on your profile and in emails.
|
||||
</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="language" let:attrs>
|
||||
{@const { value } = attrs.input}
|
||||
<Form.Label>Language</Form.Label>
|
||||
<Form.Select selected={{ value, label: languages[value] }}>
|
||||
<Form.SelectTrigger
|
||||
placeholder="Select language"
|
||||
class={cn(
|
||||
"w-[200px] justify-between",
|
||||
!attrs.input.value && "text-muted-foreground"
|
||||
)}
|
||||
/>
|
||||
<Form.SelectContent class="h-52 overflow-y-auto">
|
||||
{#each Object.entries(languages) as [value, lang]}
|
||||
<Form.SelectItem {value}>
|
||||
{lang}
|
||||
</Form.SelectItem>
|
||||
{/each}
|
||||
</Form.SelectContent>
|
||||
</Form.Select>
|
||||
<Form.Description>
|
||||
This is the language that will be used in the dashboard.
|
||||
</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Button>Update account</Form.Button>
|
||||
</Form.Root>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { superValidate } from "sveltekit-superforms/server";
|
||||
import type { PageServerLoad } from "../$types";
|
||||
import { appearanceFormSchema } from "./appearance-form.svelte";
|
||||
import { fail, type Actions } from "@sveltejs/kit";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
form: await superValidate(appearanceFormSchema)
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const form = await superValidate(event, appearanceFormSchema);
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
}
|
||||
return {
|
||||
form
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { Separator } from "$lib/components/ui/separator";
|
||||
import type { PageData } from "./$types";
|
||||
import AppearanceForm from "./appearance-form.svelte";
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Appearance</h3>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Customize the appearance of the app. Automatically switch between day and night themes.
|
||||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<AppearanceForm data={data.form} />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<script lang="ts" context="module">
|
||||
import type { SuperValidated } from "sveltekit-superforms";
|
||||
import { z } from "zod";
|
||||
|
||||
export const appearanceFormSchema = z.object({
|
||||
theme: z.enum(["light", "dark"], {
|
||||
required_error: "Please select a theme."
|
||||
}),
|
||||
font: z.enum(["inter", "manrope", "system"], {
|
||||
invalid_type_error: "Select a font",
|
||||
required_error: "Please select a font."
|
||||
})
|
||||
});
|
||||
|
||||
export type AppearanceFormSchema = typeof appearanceFormSchema;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import Label from "$lib/components/ui/label/label.svelte";
|
||||
export let data: SuperValidated<AppearanceFormSchema>;
|
||||
</script>
|
||||
|
||||
<Form.Root
|
||||
schema={appearanceFormSchema}
|
||||
form={data}
|
||||
class="space-y-8"
|
||||
method="POST"
|
||||
let:config
|
||||
debug={true}
|
||||
>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="font">
|
||||
<Form.Label>Font</Form.Label>
|
||||
<div class="w-max">
|
||||
<Form.NativeSelect class="w-[200px]">
|
||||
<option value="inter">Inter</option>
|
||||
<option value="manrope">Manrope</option>
|
||||
<option value="system">System</option>
|
||||
</Form.NativeSelect>
|
||||
</div>
|
||||
<Form.Description>Set the font you want to use in the dashboard.</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="theme">
|
||||
<Form.Label>Theme</Form.Label>
|
||||
<Form.Description>Select the theme for the dashboard.</Form.Description>
|
||||
<Form.Validation />
|
||||
<Form.RadioGroup class="grid max-w-md grid-cols-2 gap-8 pt-2" orientation="horizontal">
|
||||
<Label for="light" class="[&:has([data-state=checked])>div]:border-primary">
|
||||
<Form.RadioItem id="light" value="light" class="sr-only" />
|
||||
<div
|
||||
class="items-center rounded-md border-2 border-muted p-1 hover:border-accent"
|
||||
>
|
||||
<div class="space-y-2 rounded-sm bg-[#ecedef] p-2">
|
||||
<div class="space-y-2 rounded-md bg-white p-2 shadow-sm">
|
||||
<div class="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm"
|
||||
>
|
||||
<div class="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm"
|
||||
>
|
||||
<div class="h-4 w-4 rounded-full bg-[#ecedef]" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="block w-full p-2 text-center font-normal"> Light </span>
|
||||
</Label>
|
||||
<Label for="dark" class="[&:has([data-state=checked])>div]:border-primary">
|
||||
<Form.RadioItem id="dark" value="dark" class="sr-only" />
|
||||
<div
|
||||
class="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
<div class="space-y-2 rounded-sm bg-slate-950 p-2">
|
||||
<div class="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
|
||||
<div class="h-2 w-[80px] rounded-lg bg-slate-400" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm"
|
||||
>
|
||||
<div class="h-4 w-4 rounded-full bg-slate-400" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm"
|
||||
>
|
||||
<div class="h-4 w-4 rounded-full bg-slate-400" />
|
||||
<div class="h-2 w-[100px] rounded-lg bg-slate-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="block w-full p-2 text-center font-normal"> Dark </span>
|
||||
</Label>
|
||||
</Form.RadioGroup>
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Button>Update preferences</Form.Button>
|
||||
</Form.Root>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { superValidate } from "sveltekit-superforms/server";
|
||||
import type { PageServerLoad } from "../$types";
|
||||
import { notificationsFormSchema } from "./notifications-form.svelte";
|
||||
import { fail, type Actions } from "@sveltejs/kit";
|
||||
|
||||
export const load: PageServerLoad = async () => {
|
||||
return {
|
||||
form: await superValidate(notificationsFormSchema)
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const form = await superValidate(event, notificationsFormSchema);
|
||||
if (!form.valid) {
|
||||
return fail(400, {
|
||||
form
|
||||
});
|
||||
}
|
||||
return {
|
||||
form
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { Separator } from "$lib/components/ui/separator";
|
||||
import NotificationsForm from "./notifications-form.svelte";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium">Notifications</h3>
|
||||
<p class="text-sm text-muted-foreground">Configure how you receive notifications.</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<NotificationsForm data={data.form} />
|
||||
</div>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<script lang="ts" context="module">
|
||||
import { z } from "zod";
|
||||
export const notificationsFormSchema = z.object({
|
||||
type: z.enum(["all", "mentions", "none"], {
|
||||
required_error: "You need to select a notification type."
|
||||
}),
|
||||
mobile: z.boolean().default(false).optional(),
|
||||
communication_emails: z.boolean().default(false).optional(),
|
||||
social_emails: z.boolean().default(false).optional(),
|
||||
marketing_emails: z.boolean().default(false).optional(),
|
||||
security_emails: z.boolean()
|
||||
});
|
||||
type NotificationFormSchema = typeof notificationsFormSchema;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { SuperValidated } from "sveltekit-superforms";
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
export let data: SuperValidated<NotificationFormSchema>;
|
||||
</script>
|
||||
|
||||
<Form.Root
|
||||
form={data}
|
||||
schema={notificationsFormSchema}
|
||||
let:config
|
||||
method="POST"
|
||||
class="space-y-8"
|
||||
debug={true}
|
||||
>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="type">
|
||||
<Form.Label>Notify me about...</Form.Label>
|
||||
<Form.RadioGroup class="flex flex-col space-y-1">
|
||||
<div class="flex items-center space-x-3">
|
||||
<Form.RadioItem value="all" id="all" />
|
||||
<Label for="all" class="font-normal">All new messages</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<Form.RadioItem value="mentions" id="mentions" />
|
||||
<Label for="mentions" class="font-normal">Direct messages and mentions</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<Form.RadioItem value="none" id="none" />
|
||||
<Label for="none" class="font-normal">Nothing</Label>
|
||||
</div>
|
||||
</Form.RadioGroup>
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<div>
|
||||
<h3 class="mb-4 text-lg font-medium">Email Notifications</h3>
|
||||
<div class="space-y-4">
|
||||
<Form.Field {config} name="communication_emails">
|
||||
<Form.Item class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div class="space-y-0.5">
|
||||
<Form.Label class="text-base">Communication emails</Form.Label>
|
||||
<Form.Description>
|
||||
Receive emails about your account activity.
|
||||
</Form.Description>
|
||||
</div>
|
||||
<Form.Switch />
|
||||
</Form.Item>
|
||||
</Form.Field>
|
||||
<Form.Field {config} name="marketing_emails">
|
||||
<Form.Item class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div class="space-y-0.5">
|
||||
<Form.Label class="text-base">Marketing emails</Form.Label>
|
||||
<Form.Description>
|
||||
Receive emails about new products, features, and more.
|
||||
</Form.Description>
|
||||
</div>
|
||||
<Form.Switch />
|
||||
</Form.Item>
|
||||
</Form.Field>
|
||||
<Form.Field {config} name="social_emails">
|
||||
<Form.Item class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div class="space-y-0.5">
|
||||
<Form.Label class="text-base">Social emails</Form.Label>
|
||||
<Form.Description>
|
||||
Receive emails for friend requests, follows, and more.
|
||||
</Form.Description>
|
||||
</div>
|
||||
<Form.Switch />
|
||||
</Form.Item>
|
||||
</Form.Field>
|
||||
<Form.Field {config} name="security_emails">
|
||||
<Form.Item class="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div class="space-y-0.5">
|
||||
<Form.Label class="text-base">Security emails</Form.Label>
|
||||
<Form.Description>
|
||||
Receive emails about your account activity and security.
|
||||
</Form.Description>
|
||||
</div>
|
||||
<Form.Switch />
|
||||
</Form.Item>
|
||||
</Form.Field>
|
||||
</div>
|
||||
</div>
|
||||
<Form.Field {config} name="mobile">
|
||||
<Form.Item class="flex flex-row items-start space-x-3 space-y-0">
|
||||
<Form.Checkbox />
|
||||
<div class="space-y-1 leading-none">
|
||||
<Form.Label>Use different settings for my mobile devices</Form.Label>
|
||||
<Form.Description>
|
||||
You can manage your mobile notifications in the{" "}<a href="/settings"
|
||||
>mobile settings</a
|
||||
> page.
|
||||
</Form.Description>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form.Field>
|
||||
<Form.Button>Update notifications</Form.Button>
|
||||
</Form.Root>
|
||||
88
apps/web/src/routes/(dashboard)/settings/profile-form.svelte
Normal file
88
apps/web/src/routes/(dashboard)/settings/profile-form.svelte
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<script lang="ts" context="module">
|
||||
import { z } from "zod";
|
||||
export const profileFormSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.min(2, "Username must be at least 2 characters.")
|
||||
.max(30, "Username must not be longer than 30 characters"),
|
||||
email: z.string({ required_error: "Please select an email to display" }).email(),
|
||||
bio: z.string().min(4).max(160).default("I own a computer."),
|
||||
website: z
|
||||
.string()
|
||||
.url({ message: "Please enter a valid URL." })
|
||||
.default("https://shadcn-svelte.com")
|
||||
});
|
||||
export type ProfileFormSchema = typeof profileFormSchema;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as Form from "$lib/components/ui/form";
|
||||
import type { SuperValidated } from "sveltekit-superforms";
|
||||
|
||||
export let data: SuperValidated<ProfileFormSchema>;
|
||||
</script>
|
||||
|
||||
<Form.Root
|
||||
form={data}
|
||||
schema={profileFormSchema}
|
||||
let:config
|
||||
method="POST"
|
||||
class="space-y-8"
|
||||
debug={true}
|
||||
>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="username">
|
||||
<Form.Label>Username</Form.Label>
|
||||
<Form.Input placeholder="@shadcn" />
|
||||
<Form.Description>
|
||||
This is your public display name. It can be your real name or a pseudonym. You can
|
||||
only change this once every 30 days.
|
||||
</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="email">
|
||||
<Form.Label>Email</Form.Label>
|
||||
<Form.Select>
|
||||
<Form.SelectTrigger placeholder="Select a verified email to display" />
|
||||
<Form.SelectContent>
|
||||
<Form.SelectItem value="m@example.com" label="m@example.com"
|
||||
>m@example.com
|
||||
</Form.SelectItem>
|
||||
<Form.SelectItem value="m@google.com" label="m@google.com"
|
||||
>m@google.com
|
||||
</Form.SelectItem>
|
||||
<Form.SelectItem value="m@support.com" label="m@support.com"
|
||||
>m@support.com
|
||||
</Form.SelectItem>
|
||||
</Form.SelectContent>
|
||||
</Form.Select>
|
||||
<Form.Description>
|
||||
You can manage verified email addresses in your <a href="/examples/forms"
|
||||
>email settings</a
|
||||
>.
|
||||
</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="bio">
|
||||
<Form.Label>Bio</Form.Label>
|
||||
<Form.Textarea placeholder="Tell us a little bit about yourself" class="resize-none" />
|
||||
<Form.Description>
|
||||
You can <span>@mention</span> other users and organizations to link to them.
|
||||
</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Form.Field {config} name="website">
|
||||
<Form.Label>Website</Form.Label>
|
||||
<Form.Input />
|
||||
<Form.Description>Your personal website, blog, or portfolio.</Form.Description>
|
||||
<Form.Validation />
|
||||
</Form.Field>
|
||||
</Form.Item>
|
||||
<Form.Button>Update profile</Form.Button>
|
||||
</Form.Root>
|
||||
Loading…
Add table
Add a link
Reference in a new issue