mirror of
https://github.com/bartvdbraak/omnidash.git
synced 2025-04-27 15:31:21 +00:00
feat: add profile form data and avatar
This commit is contained in:
parent
2fed4ffdf6
commit
b989c855ac
5 changed files with 64 additions and 67 deletions
|
@ -4,13 +4,6 @@
|
||||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||||
import type { BaseAuthStore } from 'pocketbase';
|
import type { BaseAuthStore } from 'pocketbase';
|
||||||
|
|
||||||
const fullNameToInitials = (fullName: string) =>
|
|
||||||
fullName
|
|
||||||
.split(' ')
|
|
||||||
.map((word) => word[0].toUpperCase())
|
|
||||||
.slice(0, 2)
|
|
||||||
.join('');
|
|
||||||
|
|
||||||
export let authenticated = false;
|
export let authenticated = false;
|
||||||
export let user: BaseAuthStore['model'];
|
export let user: BaseAuthStore['model'];
|
||||||
</script>
|
</script>
|
||||||
|
@ -21,7 +14,7 @@
|
||||||
<Button variant="ghost" builders={[builder]} class="relative h-8 w-8 rounded-full">
|
<Button variant="ghost" builders={[builder]} class="relative h-8 w-8 rounded-full">
|
||||||
<Avatar.Root class="h-9 w-9">
|
<Avatar.Root class="h-9 w-9">
|
||||||
<Avatar.Image src={user?.avatarUrl} alt={user?.name} />
|
<Avatar.Image src={user?.avatarUrl} alt={user?.name} />
|
||||||
<Avatar.Fallback>{fullNameToInitials(user?.name)}</Avatar.Fallback>
|
<Avatar.Fallback>{user?.initials}</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import ProfileForm from './profile-form.svelte';
|
import ProfileForm from './profile-form.svelte';
|
||||||
import { Separator } from '$lib/components/ui/separator';
|
import { Separator } from '$lib/components/ui/separator';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
export let { form, user } = data;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
|
@ -11,5 +13,5 @@
|
||||||
<p class="text-sm text-muted-foreground">This is how others will see you on the site.</p>
|
<p class="text-sm text-muted-foreground">This is how others will see you on the site.</p>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
<ProfileForm data={data.form} />
|
<ProfileForm data={form} {user} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,12 +5,8 @@
|
||||||
.string()
|
.string()
|
||||||
.min(2, 'Username must be at least 2 characters.')
|
.min(2, 'Username must be at least 2 characters.')
|
||||||
.max(30, 'Username must not be longer than 30 characters'),
|
.max(30, 'Username must not be longer than 30 characters'),
|
||||||
email: z.string({ required_error: 'Please select an email to display' }).email(),
|
email: z.string({ required_error: 'Please enter a valid email' }).email(),
|
||||||
bio: z.string().min(4).max(160).default('I own a computer.'),
|
avatar: z.any().refine((val) => val.length > 0, 'File is required')
|
||||||
website: z
|
|
||||||
.string()
|
|
||||||
.url({ message: 'Please enter a valid URL.' })
|
|
||||||
.default('https://shadcn-svelte.com')
|
|
||||||
});
|
});
|
||||||
export type ProfileFormSchema = typeof profileFormSchema;
|
export type ProfileFormSchema = typeof profileFormSchema;
|
||||||
</script>
|
</script>
|
||||||
|
@ -19,7 +15,11 @@
|
||||||
import * as Form from '$lib/components/ui/form';
|
import * as Form from '$lib/components/ui/form';
|
||||||
import type { SuperValidated } from 'sveltekit-superforms';
|
import type { SuperValidated } from 'sveltekit-superforms';
|
||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
|
import type { LayoutData } from '../$types';
|
||||||
|
import * as Avatar from '$lib/components/ui/avatar';
|
||||||
|
|
||||||
export let data: SuperValidated<ProfileFormSchema>;
|
export let data: SuperValidated<ProfileFormSchema>;
|
||||||
|
export let user: LayoutData['user'];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Form.Root
|
<Form.Root
|
||||||
|
@ -30,55 +30,45 @@
|
||||||
class="space-y-8"
|
class="space-y-8"
|
||||||
debug={dev ? true : false}
|
debug={dev ? true : false}
|
||||||
>
|
>
|
||||||
<Form.Item>
|
<div class="grid grid-cols-[1fr,16rem] gap-4">
|
||||||
<Form.Field {config} name="username">
|
<div>
|
||||||
<Form.Label>Username</Form.Label>
|
<Form.Item>
|
||||||
<Form.Input placeholder="@shadcn" />
|
<Form.Field {config} name="username">
|
||||||
<Form.Description>
|
<Form.Label>Username</Form.Label>
|
||||||
This is your public display name. It can be your real name or a pseudonym.
|
<Form.Input placeholder={user?.username} />
|
||||||
</Form.Description>
|
<Form.Description>
|
||||||
<Form.Validation />
|
This is your public display name. It can be your real name or a pseudonym.
|
||||||
</Form.Field>
|
</Form.Description>
|
||||||
</Form.Item>
|
<Form.Validation />
|
||||||
<Form.Item>
|
</Form.Field>
|
||||||
<Form.Field {config} name="email">
|
</Form.Item>
|
||||||
<Form.Label>Email</Form.Label>
|
<Form.Item>
|
||||||
<Form.Select>
|
<Form.Field {config} name="email">
|
||||||
<Form.SelectTrigger placeholder="Select a verified email to display" />
|
<Form.Label>Email</Form.Label>
|
||||||
<Form.SelectContent>
|
<Form.Input placeholder={user?.email} />
|
||||||
<Form.SelectItem value="m@example.com" label="m@example.com"
|
<Form.Description>
|
||||||
>m@example.com
|
<Form.Description>
|
||||||
</Form.SelectItem>
|
This is the email address associated with your account.
|
||||||
<Form.SelectItem value="m@google.com" label="m@google.com">m@google.com</Form.SelectItem>
|
</Form.Description>
|
||||||
<Form.SelectItem value="m@support.com" label="m@support.com"
|
</Form.Description>
|
||||||
>m@support.com
|
<Form.Validation />
|
||||||
</Form.SelectItem>
|
</Form.Field>
|
||||||
</Form.SelectContent>
|
</Form.Item>
|
||||||
</Form.Select>
|
</div>
|
||||||
<Form.Description>
|
<Form.Item>
|
||||||
You can manage verified email addresses in your <a href="/examples/forms">email settings</a
|
<Form.Field {config} name="avatar">
|
||||||
>.
|
<Form.Label>Profile Picture</Form.Label>
|
||||||
</Form.Description>
|
<Avatar.Root class="aspect-square h-auto w-full">
|
||||||
<Form.Validation />
|
<Avatar.Image src={user?.avatarUrl} alt={user?.name} />
|
||||||
</Form.Field>
|
<Avatar.Fallback>{user?.initials}</Avatar.Fallback>
|
||||||
</Form.Item>
|
</Avatar.Root>
|
||||||
<Form.Item>
|
<Form.Input type="file" placeholder={user?.email} />
|
||||||
<Form.Field {config} name="bio">
|
<Form.Description>
|
||||||
<Form.Label>Bio</Form.Label>
|
<Form.Description>Your avatar image displayed.</Form.Description>
|
||||||
<Form.Textarea placeholder="Tell us a little bit about yourself" class="resize-none" />
|
</Form.Description>
|
||||||
<Form.Description>
|
<Form.Validation />
|
||||||
You can <span>@mention</span> other users and organizations to link to them.
|
</Form.Field>
|
||||||
</Form.Description>
|
</Form.Item>
|
||||||
<Form.Validation />
|
</div>
|
||||||
</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.Button>Update profile</Form.Button>
|
||||||
</Form.Root>
|
</Form.Root>
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
|
|
||||||
|
const fullNameToInitials = (fullName: string) =>
|
||||||
|
fullName
|
||||||
|
.split(' ')
|
||||||
|
.map((word) => word[0].toUpperCase())
|
||||||
|
.slice(0, 2)
|
||||||
|
.join('');
|
||||||
|
|
||||||
export const load: LayoutServerLoad = async ({ locals }: { locals: App.Locals }) => {
|
export const load: LayoutServerLoad = async ({ locals }: { locals: App.Locals }) => {
|
||||||
const user = locals.pocketBase.authStore.model;
|
const user = locals.pocketBase.authStore.model;
|
||||||
if (user) user.avatarUrl = locals.pocketBase.getFileUrl(user, user.avatar);
|
if (user) {
|
||||||
|
user.avatarUrl = locals.pocketBase.getFileUrl(user, user.avatar);
|
||||||
|
user.initials = fullNameToInitials(user.name);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authenticated: locals.pocketBase.authStore.isValid,
|
authenticated: locals.pocketBase.authStore.isValid,
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
class="text-primary-500 ml-1 h-3 w-3 tracking-normal transition-transform duration-150 ease-in-out group-hover:translate-x-0.5"
|
class="text-primary-500 ml-1 h-3 w-3 tracking-normal transition-transform duration-150 ease-in-out group-hover:translate-x-0.5"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button href={siteConfig.links.gitHubProject} variant="outline">Star on GitHub</Button>
|
<Button href={siteConfig.links.gitHubProject} target="_blank" variant="outline"
|
||||||
|
>Star on GitHub</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue