From abe1d003c6f399b983efbff86b833c02d03e328a Mon Sep 17 00:00:00 2001 From: Bart van der Braak Date: Fri, 9 Feb 2024 03:08:40 +0100 Subject: [PATCH] feat: added crud --- apps/web/package.json | 1 + apps/web/pnpm-lock.yaml | 11 ++ apps/web/src/app.html | 6 +- apps/web/src/hooks.server.ts | 4 +- .../src/lib/components/site/icons/logo.svelte | 2 +- .../lib/components/site/nav/mobile-nav.svelte | 4 +- .../src/lib/components/site/particles.svelte | 3 - .../lib/components/site/site-navbar.svelte | 4 +- .../components/ui/card/card-content.svelte | 13 +++ .../ui/card/card-description.svelte | 13 +++ .../lib/components/ui/card/card-footer.svelte | 13 +++ .../lib/components/ui/card/card-header.svelte | 13 +++ .../lib/components/ui/card/card-title.svelte | 21 ++++ .../src/lib/components/ui/card/card.svelte | 22 ++++ apps/web/src/lib/components/ui/card/index.ts | 24 ++++ .../web/src/lib/components/ui/sonner/index.ts | 1 + .../lib/components/ui/sonner/sonner.svelte | 21 ++++ apps/web/src/lib/utils.ts | 6 + .../settings/(components)/avatar-form.svelte | 85 ++++++++++++++ .../settings/(components)/email-form.svelte | 104 ++++++++++++++++++ .../settings/(components)/name-form.svelte | 79 +++++++++++++ .../(components)/password-form.svelte | 78 +++++++++++++ .../(dashboard)/settings/+page.server.ts | 69 ++++++++++-- .../routes/(dashboard)/settings/+page.svelte | 23 +++- .../settings/appearance/+page.server.ts | 2 +- .../settings/appearance/+page.svelte | 2 +- .../appearance/appearance-form.svelte | 24 +++- .../(dashboard)/settings/profile-form.svelte | 90 --------------- apps/web/src/routes/+layout.svelte | 3 +- apps/web/static/android-chrome-192x192.png | Bin 0 -> 9944 bytes apps/web/static/android-chrome-512x512.png | Bin 0 -> 23384 bytes apps/web/static/apple-touch-icon.png | Bin 3245 -> 3492 bytes apps/web/static/favicon-16x16.png | Bin 801 -> 1100 bytes apps/web/static/favicon-32x32.png | Bin 1053 -> 1652 bytes apps/web/static/favicon.ico | Bin 15086 -> 15086 bytes apps/web/static/omnidash.svg | 8 +- apps/web/static/site.webmanifest | 4 +- 37 files changed, 628 insertions(+), 125 deletions(-) create mode 100644 apps/web/src/lib/components/ui/card/card-content.svelte create mode 100644 apps/web/src/lib/components/ui/card/card-description.svelte create mode 100644 apps/web/src/lib/components/ui/card/card-footer.svelte create mode 100644 apps/web/src/lib/components/ui/card/card-header.svelte create mode 100644 apps/web/src/lib/components/ui/card/card-title.svelte create mode 100644 apps/web/src/lib/components/ui/card/card.svelte create mode 100644 apps/web/src/lib/components/ui/card/index.ts create mode 100644 apps/web/src/lib/components/ui/sonner/index.ts create mode 100644 apps/web/src/lib/components/ui/sonner/sonner.svelte create mode 100644 apps/web/src/routes/(dashboard)/settings/(components)/avatar-form.svelte create mode 100644 apps/web/src/routes/(dashboard)/settings/(components)/email-form.svelte create mode 100644 apps/web/src/routes/(dashboard)/settings/(components)/name-form.svelte create mode 100644 apps/web/src/routes/(dashboard)/settings/(components)/password-form.svelte delete mode 100644 apps/web/src/routes/(dashboard)/settings/profile-form.svelte create mode 100644 apps/web/static/android-chrome-192x192.png create mode 100644 apps/web/static/android-chrome-512x512.png diff --git a/apps/web/package.json b/apps/web/package.json index 6f45161..b8c8223 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -47,6 +47,7 @@ "pocketbase": "^0.21.0", "radix-icons-svelte": "^1.2.1", "svelte-headless-table": "^0.18.1", + "svelte-sonner": "^0.3.17", "tailwind-merge": "^2.2.1", "tailwind-variants": "^0.1.20" } diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 6ea71e9..03132f0 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -32,6 +32,9 @@ dependencies: svelte-headless-table: specifier: ^0.18.1 version: 0.18.1(svelte@4.2.9) + svelte-sonner: + specifier: ^0.3.17 + version: 0.3.17(svelte@4.2.9) tailwind-merge: specifier: ^2.2.1 version: 2.2.1 @@ -2427,6 +2430,14 @@ packages: svelte-subscribe: 2.0.1(svelte@4.2.9) dev: false + /svelte-sonner@0.3.17(svelte@4.2.9): + resolution: {integrity: sha512-jociRGESILpHi6fIIcqGYE1bWAfK4ZeTLHuXxNgGMhG1FwhNRIK1bsCbXEl8/AjRV8aiNXtkS/+IlQtvb2jR9g==} + peerDependencies: + svelte: '>=3 <5' + dependencies: + svelte: 4.2.9 + dev: false + /svelte-subscribe@2.0.1(svelte@4.2.9): resolution: {integrity: sha512-eKXIjLxB4C7eQWPqKEdxcGfNXm2g/qJ67zmEZK/GigCZMfrTR3m7DPY93R6MX+5uoqM1FRYxl8LZ1oy4URWi2A==} peerDependencies: diff --git a/apps/web/src/app.html b/apps/web/src/app.html index 8e74737..b8c3939 100644 --- a/apps/web/src/app.html +++ b/apps/web/src/app.html @@ -8,9 +8,9 @@ - - - + + + %sveltekit.head% { .authRefresh<{ id: string; email: string }>(); event.locals.id = auth.record.id; event.locals.email = auth.record.email; - } catch (err) { - console.log('Error: ', err); + } catch (_) { + event.locals.pocketBase.authStore.clear(); } const response = await resolve(event); diff --git a/apps/web/src/lib/components/site/icons/logo.svelte b/apps/web/src/lib/components/site/icons/logo.svelte index 5167ff6..199d086 100644 --- a/apps/web/src/lib/components/site/icons/logo.svelte +++ b/apps/web/src/lib/components/site/icons/logo.svelte @@ -3,7 +3,7 @@ topLeft: '#FF6C22', topRight: '#FF9209', bottomLeft: '#FFD099', - bottomRight: '#2B3499' + bottomRight: '#3B48D3' }; diff --git a/apps/web/src/lib/components/site/nav/mobile-nav.svelte b/apps/web/src/lib/components/site/nav/mobile-nav.svelte index e213c81..5712101 100644 --- a/apps/web/src/lib/components/site/nav/mobile-nav.svelte +++ b/apps/web/src/lib/components/site/nav/mobile-nav.svelte @@ -25,7 +25,9 @@ Logo icon (return home) - +
+ +
{siteConfig.name}
diff --git a/apps/web/src/lib/components/site/particles.svelte b/apps/web/src/lib/components/site/particles.svelte index 6fc5880..675f3ab 100644 --- a/apps/web/src/lib/components/site/particles.svelte +++ b/apps/web/src/lib/components/site/particles.svelte @@ -58,10 +58,7 @@ } mode.subscribe((value) => { - console.log('value', value); color = value === 'dark' ? '#ffffff' : '#000000'; - - // Move the `rgb` calculation inside the `mode.subscribe` callback rgb = hexToRgb(color); }); diff --git a/apps/web/src/lib/components/site/site-navbar.svelte b/apps/web/src/lib/components/site/site-navbar.svelte index 0f5ec7b..bdfc3cb 100644 --- a/apps/web/src/lib/components/site/site-navbar.svelte +++ b/apps/web/src/lib/components/site/site-navbar.svelte @@ -13,7 +13,9 @@
Logo (return home) - +
+ +
{siteConfig.name}
diff --git a/apps/web/src/lib/components/ui/card/card-content.svelte b/apps/web/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..c87d58a --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/apps/web/src/lib/components/ui/card/card-description.svelte b/apps/web/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..ffc81f9 --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,13 @@ + + +

+ +

diff --git a/apps/web/src/lib/components/ui/card/card-footer.svelte b/apps/web/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..414ded9 --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/apps/web/src/lib/components/ui/card/card-header.svelte b/apps/web/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..8079df3 --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,13 @@ + + +
+ +
diff --git a/apps/web/src/lib/components/ui/card/card-title.svelte b/apps/web/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..d0d98c0 --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/apps/web/src/lib/components/ui/card/card.svelte b/apps/web/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..8bc551c --- /dev/null +++ b/apps/web/src/lib/components/ui/card/card.svelte @@ -0,0 +1,22 @@ + + + +
+ +
diff --git a/apps/web/src/lib/components/ui/card/index.ts b/apps/web/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..86c5408 --- /dev/null +++ b/apps/web/src/lib/components/ui/card/index.ts @@ -0,0 +1,24 @@ +import Root from './card.svelte'; +import Content from './card-content.svelte'; +import Description from './card-description.svelte'; +import Footer from './card-footer.svelte'; +import Header from './card-header.svelte'; +import Title from './card-title.svelte'; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle +}; + +export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; diff --git a/apps/web/src/lib/components/ui/sonner/index.ts b/apps/web/src/lib/components/ui/sonner/index.ts new file mode 100644 index 0000000..fcaf06b --- /dev/null +++ b/apps/web/src/lib/components/ui/sonner/index.ts @@ -0,0 +1 @@ +export { default as Toaster } from './sonner.svelte'; diff --git a/apps/web/src/lib/components/ui/sonner/sonner.svelte b/apps/web/src/lib/components/ui/sonner/sonner.svelte new file mode 100644 index 0000000..f1d41c5 --- /dev/null +++ b/apps/web/src/lib/components/ui/sonner/sonner.svelte @@ -0,0 +1,21 @@ + + + diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index eba19d8..a02cbcf 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -54,3 +54,9 @@ export const flyAndScale = ( easing: cubicOut }; }; + +import { z } from 'zod'; +const emptyStringToUndefined = z.literal('').transform(() => undefined); +export function asOptionalStringWithoutEmpty(schema: T) { + return schema.optional().or(emptyStringToUndefined); +} diff --git a/apps/web/src/routes/(dashboard)/settings/(components)/avatar-form.svelte b/apps/web/src/routes/(dashboard)/settings/(components)/avatar-form.svelte new file mode 100644 index 0000000..e3df797 --- /dev/null +++ b/apps/web/src/routes/(dashboard)/settings/(components)/avatar-form.svelte @@ -0,0 +1,85 @@ + + + + + + + Avatar image + + This is the image that will be displayed on your profile, in dashboards and in emails. + + + + + + Avatar + + + {user?.initials} + + + + + + Update avatar + + + diff --git a/apps/web/src/routes/(dashboard)/settings/(components)/email-form.svelte b/apps/web/src/routes/(dashboard)/settings/(components)/email-form.svelte new file mode 100644 index 0000000..acd356b --- /dev/null +++ b/apps/web/src/routes/(dashboard)/settings/(components)/email-form.svelte @@ -0,0 +1,104 @@ + + + + + + + Modify your email + Use a different email by requesting a token and entering it for verification below. + + + + + + New email +
+ + Request token +
+ +
+
+
+ + + + Email verification token + + + + + + + Password + + + + + Confirm email change + +
+
diff --git a/apps/web/src/routes/(dashboard)/settings/(components)/name-form.svelte b/apps/web/src/routes/(dashboard)/settings/(components)/name-form.svelte new file mode 100644 index 0000000..c084ca6 --- /dev/null +++ b/apps/web/src/routes/(dashboard)/settings/(components)/name-form.svelte @@ -0,0 +1,79 @@ + + + + + + + Change your name + + You can change the name that will be displayed on your profile and in emails as well as your + username. + + + + + + + Display name + + + + + + + Username + + + + + Update name + + + diff --git a/apps/web/src/routes/(dashboard)/settings/(components)/password-form.svelte b/apps/web/src/routes/(dashboard)/settings/(components)/password-form.svelte new file mode 100644 index 0000000..8b07ec1 --- /dev/null +++ b/apps/web/src/routes/(dashboard)/settings/(components)/password-form.svelte @@ -0,0 +1,78 @@ + + + + + + + Password change + You can change your account password here. + + + + + + Current Password + + + + + New Password + + + + + Confirm new password + + + + + Update password + + + diff --git a/apps/web/src/routes/(dashboard)/settings/+page.server.ts b/apps/web/src/routes/(dashboard)/settings/+page.server.ts index 7307867..8f1e189 100644 --- a/apps/web/src/routes/(dashboard)/settings/+page.server.ts +++ b/apps/web/src/routes/(dashboard)/settings/+page.server.ts @@ -1,24 +1,79 @@ import type { PageServerLoad } from './$types'; import { superValidate } from 'sveltekit-superforms/server'; -import { profileFormSchema } from './profile-form.svelte'; import { fail, type Actions } from '@sveltejs/kit'; +import { nameFormSchema } from './(components)/name-form.svelte'; +import { emailConfirmFormSchema, emailRequestFormSchema } from './(components)/email-form.svelte'; +import { passwordFormSchema } from './(components)/password-form.svelte'; +import { avatarFormSchema } from './(components)/avatar-form.svelte'; export const load: PageServerLoad = async () => { return { - form: await superValidate(profileFormSchema) + forms: { + name: await superValidate(nameFormSchema), + emailRequest: await superValidate(emailRequestFormSchema), + emailConfirm: await superValidate(emailConfirmFormSchema), + password: await superValidate(passwordFormSchema), + avatar: await superValidate(avatarFormSchema), + debug: false + } }; }; export const actions: Actions = { - default: async (event) => { - const form = await superValidate(event, profileFormSchema); + name: async ({ request, locals }: { request: Request; locals: App.Locals }) => { + const form = await superValidate(request, nameFormSchema); if (!form.valid) { return fail(400, { form }); } - return { - form - }; + await locals.pocketBase.collection('users').update(locals.id, form.data); + return { form }; + }, + emailRequest: async ({ request, locals }: { request: Request; locals: App.Locals }) => { + console.log(request); + const form = await superValidate(request, emailRequestFormSchema); + if (!form.valid) { + return fail(400, { + form + }); + } + await locals.pocketBase.collection('users').requestEmailChange(form.data.newEmail); + return { form }; + }, + emailConfirm: async ({ request, locals }: { request: Request; locals: App.Locals }) => { + const form = await superValidate(request, emailConfirmFormSchema); + if (!form.valid) { + return fail(400, { + form + }); + } + await locals.pocketBase + .collection('users') + .confirmEmailChange(form.data.token, form.data.password); + return { form }; + }, + password: async ({ request, locals }: { request: Request; locals: App.Locals }) => { + const form = await superValidate(request, passwordFormSchema); + console.log(form); + if (!form.valid) { + return fail(400, { + form + }); + } + await locals.pocketBase.collection('users').update(locals.id, form.data); + return { form }; + }, + avatar: async ({ request, locals }: { request: Request; locals: App.Locals }) => { + const formData = await request.formData(); + const form = await superValidate(request, avatarFormSchema); + + if (!form.valid) return fail(400, { form }); + + const file = formData.get('file'); + if (file instanceof File) { + await locals.pocketBase.collection('users').update(locals.id, { avatar: file }); + } + return { form }; } }; diff --git a/apps/web/src/routes/(dashboard)/settings/+page.svelte b/apps/web/src/routes/(dashboard)/settings/+page.svelte index 121af54..21cadb4 100644 --- a/apps/web/src/routes/(dashboard)/settings/+page.svelte +++ b/apps/web/src/routes/(dashboard)/settings/+page.svelte @@ -1,10 +1,13 @@
@@ -13,5 +16,19 @@

Update your account and profile settings.

- +
+
+ + + +
+
+ +
+
diff --git a/apps/web/src/routes/(dashboard)/settings/appearance/+page.server.ts b/apps/web/src/routes/(dashboard)/settings/appearance/+page.server.ts index 3401cd6..e435a79 100644 --- a/apps/web/src/routes/(dashboard)/settings/appearance/+page.server.ts +++ b/apps/web/src/routes/(dashboard)/settings/appearance/+page.server.ts @@ -12,7 +12,6 @@ export const load: PageServerLoad = async () => { export const actions: Actions = { default: async ({ request, locals }: { request: Request; locals: App.Locals }) => { const form = await superValidate(request, appearanceFormSchema); - console.log('form: ', form); if (!form.valid) { return fail(400, { form @@ -21,5 +20,6 @@ export const actions: Actions = { await locals.pocketBase .collection('users') .update(locals.id, { appearanceMode: form.data.theme }); + return { form }; } }; diff --git a/apps/web/src/routes/(dashboard)/settings/appearance/+page.svelte b/apps/web/src/routes/(dashboard)/settings/appearance/+page.svelte index bf51e55..6dea5a0 100644 --- a/apps/web/src/routes/(dashboard)/settings/appearance/+page.svelte +++ b/apps/web/src/routes/(dashboard)/settings/appearance/+page.svelte @@ -15,5 +15,5 @@

- + diff --git a/apps/web/src/routes/(dashboard)/settings/appearance/appearance-form.svelte b/apps/web/src/routes/(dashboard)/settings/appearance/appearance-form.svelte index e7d8c5b..09d8e33 100644 --- a/apps/web/src/routes/(dashboard)/settings/appearance/appearance-form.svelte +++ b/apps/web/src/routes/(dashboard)/settings/appearance/appearance-form.svelte @@ -16,12 +16,28 @@ import Label from '$lib/components/ui/label/label.svelte'; import { dev } from '$app/environment'; import type { PageData } from '../$types'; + import type { FormOptions } from 'formsnap'; + import { toast } from 'svelte-sonner'; + export let isLoading = false; export let data: SuperValidated; + const options: FormOptions = { + onSubmit() { + isLoading = true; + toast.info('Updating appearance...'); + }, + onResult({ result }) { + isLoading = false; + if (result.status === 200) toast.success('Your appearance has been updated!'); + if (result.status === 400) toast.error('There was an error updating your appearance.'); + }, + dataType: 'form' + }; export let user: PageData['user']; Theme Select the theme for the dashboard. - +