diff --git a/frontend/bun.lockb b/frontend/bun.lockb index f2996d8..a777d8d 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/package.json b/frontend/package.json index 1060e02..2d0e8ee 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,8 +14,8 @@ }, "devDependencies": { "@sveltejs/adapter-node": "^2.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/kit": "^2.5.5", + "@sveltejs/vite-plugin-svelte": "^3.0.2", "@types/qrcode": "^1.5.5", "autoprefixer": "^10.4.14", "bun-types": "^1.0.11", @@ -26,13 +26,13 @@ "prettier": "^3.1.0", "prettier-plugin-svelte": "^3.1.0", "prettier-plugin-tailwindcss": "^0.5.7", - "svelte": "^4.0.5", + "svelte": "^4.2.12", "svelte-adapter-bun": "^0.5.1", "svelte-check": "^3.4.3", "tailwindcss": "^3.3.2", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.0.0" + "vite": "^5.2.8" }, "type": "module", "dependencies": { @@ -40,10 +40,11 @@ "@prgm/sveltekit-progress-bar": "^2.0.0", "@types/he": "^1.2.3", "apexcharts": "^3.44.0", - "bits-ui": "^0.19.7", + "bits-ui": "^0.21.2", "clsx": "^2.0.0", "cmdk-sv": "^0.0.13", "drizzle-orm": "latest", + "formsnap": "^1.0.0", "he": "^1.2.0", "lucide-svelte": "^0.356.0", "mode-watcher": "^0.1.2", @@ -54,6 +55,7 @@ "postgres": "^3.4.3", "qrcode": "^1.5.3", "svelte-sonner": "^0.3.10", + "sveltekit-superforms": "^2.12.2", "tailwind-merge": "^2.0.0", "tailwind-variants": "^0.1.18", "zod": "^3.22.4" diff --git a/frontend/src/lib/components/ui/form/form-button.svelte b/frontend/src/lib/components/ui/form/form-button.svelte index 6e20da3..087c839 100644 --- a/frontend/src/lib/components/ui/form/form-button.svelte +++ b/frontend/src/lib/components/ui/form/form-button.svelte @@ -1,9 +1,10 @@ - + diff --git a/frontend/src/lib/components/ui/form/form-checkbox.svelte b/frontend/src/lib/components/ui/form/form-checkbox.svelte deleted file mode 100644 index a1cddf1..0000000 --- a/frontend/src/lib/components/ui/form/form-checkbox.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - { - onCheckedChange?.(v) - setValue(v) - }} - {...$$restProps} - on:click - on:keydown /> - diff --git a/frontend/src/lib/components/ui/form/form-description.svelte b/frontend/src/lib/components/ui/form/form-description.svelte index 14683c9..7d36254 100644 --- a/frontend/src/lib/components/ui/form/form-description.svelte +++ b/frontend/src/lib/components/ui/form/form-description.svelte @@ -1,15 +1,17 @@ - + class={cn("text-sm text-muted-foreground", className)} + {...$$restProps} + let:descriptionAttrs +> + diff --git a/frontend/src/lib/components/ui/form/form-element-field.svelte b/frontend/src/lib/components/ui/form/form-element-field.svelte new file mode 100644 index 0000000..2de747e --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-element-field.svelte @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/frontend/src/lib/components/ui/form/form-field-errors.svelte b/frontend/src/lib/components/ui/form/form-field-errors.svelte new file mode 100644 index 0000000..9395326 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-field-errors.svelte @@ -0,0 +1,26 @@ + + + + + {#each errors as error} + {error} + {/each} + + diff --git a/frontend/src/lib/components/ui/form/form-field.svelte b/frontend/src/lib/components/ui/form/form-field.svelte new file mode 100644 index 0000000..6e958a3 --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-field.svelte @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/frontend/src/lib/components/ui/form/form-fieldset.svelte b/frontend/src/lib/components/ui/form/form-fieldset.svelte new file mode 100644 index 0000000..81e8f1b --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-fieldset.svelte @@ -0,0 +1,30 @@ + + + + + + + diff --git a/frontend/src/lib/components/ui/form/form-input.svelte b/frontend/src/lib/components/ui/form/form-input.svelte deleted file mode 100644 index 33ca078..0000000 --- a/frontend/src/lib/components/ui/form/form-input.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/frontend/src/lib/components/ui/form/form-item.svelte b/frontend/src/lib/components/ui/form/form-item.svelte deleted file mode 100644 index 586e40c..0000000 --- a/frontend/src/lib/components/ui/form/form-item.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/frontend/src/lib/components/ui/form/form-label.svelte b/frontend/src/lib/components/ui/form/form-label.svelte index e9757c2..fcd1028 100644 --- a/frontend/src/lib/components/ui/form/form-label.svelte +++ b/frontend/src/lib/components/ui/form/form-label.svelte @@ -1,20 +1,17 @@ - - + + diff --git a/frontend/src/lib/components/ui/form/form-legend.svelte b/frontend/src/lib/components/ui/form/form-legend.svelte new file mode 100644 index 0000000..3b1387c --- /dev/null +++ b/frontend/src/lib/components/ui/form/form-legend.svelte @@ -0,0 +1,17 @@ + + + + + diff --git a/frontend/src/lib/components/ui/form/form-native-select.svelte b/frontend/src/lib/components/ui/form/form-native-select.svelte deleted file mode 100644 index 267bef7..0000000 --- a/frontend/src/lib/components/ui/form/form-native-select.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - diff --git a/frontend/src/lib/components/ui/form/form-radio-group.svelte b/frontend/src/lib/components/ui/form/form-radio-group.svelte deleted file mode 100644 index 566f4c8..0000000 --- a/frontend/src/lib/components/ui/form/form-radio-group.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - { - onValueChange?.(v) - setValue(v) - }} - {...$$restProps}> - - - diff --git a/frontend/src/lib/components/ui/form/form-select-trigger.svelte b/frontend/src/lib/components/ui/form/form-select-trigger.svelte deleted file mode 100644 index bb9bf42..0000000 --- a/frontend/src/lib/components/ui/form/form-select-trigger.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/frontend/src/lib/components/ui/form/form-select.svelte b/frontend/src/lib/components/ui/form/form-select.svelte deleted file mode 100644 index e0aeb97..0000000 --- a/frontend/src/lib/components/ui/form/form-select.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - { - onSelectedChange?.(v) - setValue(v ? v.value : undefined) - }} - {...$$restProps}> - - - diff --git a/frontend/src/lib/components/ui/form/form-switch.svelte b/frontend/src/lib/components/ui/form/form-switch.svelte deleted file mode 100644 index 5ed5c3a..0000000 --- a/frontend/src/lib/components/ui/form/form-switch.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - { - onCheckedChange?.(v) - setValue(v) - }} - {...$$restProps} - on:click - on:keydown /> - diff --git a/frontend/src/lib/components/ui/form/form-textarea.svelte b/frontend/src/lib/components/ui/form/form-textarea.svelte deleted file mode 100644 index b6aa594..0000000 --- a/frontend/src/lib/components/ui/form/form-textarea.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/frontend/src/lib/components/ui/form/form-validation.svelte b/frontend/src/lib/components/ui/form/form-validation.svelte deleted file mode 100644 index f8ff1f9..0000000 --- a/frontend/src/lib/components/ui/form/form-validation.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/frontend/src/lib/components/ui/form/index.ts b/frontend/src/lib/components/ui/form/index.ts index afa02e7..0713927 100644 --- a/frontend/src/lib/components/ui/form/index.ts +++ b/frontend/src/lib/components/ui/form/index.ts @@ -1,85 +1,33 @@ -import { Form as FormPrimitive, getFormField } from 'formsnap' -import * as RadioGroupComp from '$lib/components/ui/radio-group' -import * as SelectComp from '$lib/components/ui/select' -import type { Writable } from 'svelte/store' -import Item from './form-item.svelte' -import Input from './form-input.svelte' -import Textarea from './form-textarea.svelte' -import Description from './form-description.svelte' -import Label from './form-label.svelte' -import Validation from './form-validation.svelte' -import Checkbox from './form-checkbox.svelte' -import Switch from './form-switch.svelte' -import NativeSelect from './form-native-select.svelte' -import RadioGroup from './form-radio-group.svelte' -import Select from './form-select.svelte' -import SelectTrigger from './form-select-trigger.svelte' -import Button from './form-button.svelte' +import * as FormPrimitive from "formsnap"; +import Description from "./form-description.svelte"; +import Label from "./form-label.svelte"; +import FieldErrors from "./form-field-errors.svelte"; +import Field from "./form-field.svelte"; +import Fieldset from "./form-fieldset.svelte"; +import Legend from "./form-legend.svelte"; +import ElementField from "./form-element-field.svelte"; +import Button from "./form-button.svelte"; -const Root = FormPrimitive.Root -const Field = FormPrimitive.Field -const Control = FormPrimitive.Control -const RadioItem = RadioGroupComp.Item -const NativeRadio = FormPrimitive.Radio -const SelectContent = SelectComp.Content -const SelectLabel = SelectComp.Label -const SelectGroup = SelectComp.Group -const SelectItem = SelectComp.Item -const SelectSeparator = SelectComp.Separator - -export type TextareaGetFormField = Omit< - ReturnType, - 'value' -> & { - value: Writable -} +const Control = FormPrimitive.Control; export { - Root, Field, Control, - Item, - Input, Label, Button, - Switch, - Select, - Checkbox, - Textarea, - Validation, - RadioGroup, - RadioItem, + FieldErrors, Description, - SelectContent, - SelectLabel, - SelectGroup, - SelectItem, - SelectSeparator, - SelectTrigger, - NativeSelect, - NativeRadio, + Fieldset, + Legend, + ElementField, // - Root as Form, Field as FormField, Control as FormControl, - Item as FormItem, - Input as FormInput, - Textarea as FormTextarea, Description as FormDescription, Label as FormLabel, - Validation as FormValidation, - NativeSelect as FormNativeSelect, - NativeRadio as FormNativeRadio, - Checkbox as FormCheckbox, - Switch as FormSwitch, - RadioGroup as FormRadioGroup, - RadioItem as FormRadioItem, - Select as FormSelect, - SelectContent as FormSelectContent, - SelectLabel as FormSelectLabel, - SelectGroup as FormSelectGroup, - SelectItem as FormSelectItem, - SelectSeparator as FormSelectSeparator, - SelectTrigger as FormSelectTrigger, + FieldErrors as FormFieldErrors, + Fieldset as FormFieldset, + Legend as FormLegend, + ElementField as FormElementField, Button as FormButton, -} +}; diff --git a/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte b/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte index 90f7c5d..4c64199 100644 --- a/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte +++ b/frontend/src/routes/(app)/settings/qr/(components)/DemoQR.svelte @@ -1,6 +1,7 @@ + + + + + + + Background Color + + + QR Code background color + + + + + Foreground Color + + + QR Code foreground color + + + Save + + diff --git a/frontend/src/routes/(app)/settings/qr/+page.server.ts b/frontend/src/routes/(app)/settings/qr/+page.server.ts index 9ce04ef..1af2aa8 100644 --- a/frontend/src/routes/(app)/settings/qr/+page.server.ts +++ b/frontend/src/routes/(app)/settings/qr/+page.server.ts @@ -1,10 +1,53 @@ import { db } from '$lib/db' -import type { PageServerLoad } from './$types' +import type { PageServerLoad, Actions } from './$types' +import { fail } from '@sveltejs/kit' +import { superValidate } from 'sveltekit-superforms' +import { formSchema } from './schema' +import { zod } from 'sveltekit-superforms/adapters' +import { setting } from '$lib/db/schema' +import { eq } from 'drizzle-orm' export const load = (async (event) => { const settings = await db.query.setting.findFirst({ where: (setting, { eq }) => eq(setting.userId, event.locals.user.id), }) - return { settings } + + const qr_background = settings?.qr_background || '#000' + const qr_foreground = settings?.qr_foreground || '#fff' + + return { + settings, + form: await superValidate( + { qr_background, qr_foreground }, + zod(formSchema), + ), + } }) satisfies PageServerLoad + +export const actions: Actions = { + default: async (event) => { + const form = await superValidate(event, zod(formSchema)) + if (!form.valid) { + return fail(400, { + form, + }) + } + + const userId = event.locals.user.id + const settings = await db.query.setting.findFirst({ + where: (settingData, { eq }) => eq(settingData.userId, userId), + }) + + if (!settings) { + await db.insert(setting).values({ userId }) + } + await db + .update(setting) + .set(form.data) + .where(eq(setting.userId, userId)) + return { + form, + } + }, +} diff --git a/frontend/src/routes/(app)/settings/qr/+page.svelte b/frontend/src/routes/(app)/settings/qr/+page.svelte index d7dca02..a99898c 100644 --- a/frontend/src/routes/(app)/settings/qr/+page.svelte +++ b/frontend/src/routes/(app)/settings/qr/+page.svelte @@ -1,85 +1,19 @@ - Account - - Update your account settings. + QR + + Update your QR settings. - {#key qr_data} - - {/key} - - - Background Color - - - - - Foreground Color - - - - - {#if isLoading} - - {/if} - Save + diff --git a/frontend/src/routes/(app)/settings/qr/schema.ts b/frontend/src/routes/(app)/settings/qr/schema.ts new file mode 100644 index 0000000..0ba69bc --- /dev/null +++ b/frontend/src/routes/(app)/settings/qr/schema.ts @@ -0,0 +1,14 @@ +import { z } from 'zod' + +export const formSchema = z.object({ + qr_background: z + .string() + .min(1, { message: 'Background color is required' }) + .max(7), + qr_foreground: z + .string() + .min(1, { message: 'Foreground color is required' }) + .max(7), +}) + +export type FormSchema = typeof formSchema diff --git a/frontend/src/routes/api/account/qr/+server.ts b/frontend/src/routes/api/account/qr/+server.ts deleted file mode 100644 index 03a4802..0000000 --- a/frontend/src/routes/api/account/qr/+server.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { db } from '$lib/db' -import { setting } from '$lib/db/schema' -import { qrUpdateSchema } from '$lib/server/types' -import { eq } from 'drizzle-orm' -import type { RequestHandler } from './$types' - -export const PUT: RequestHandler = async (event) => { - const body = await event.request.json() - const qrUpdate = qrUpdateSchema.safeParse(body) - const userId = event.locals.user.id - - if (!qrUpdate.success) { - return new Response(JSON.stringify({ success: false })) - } - - try { - const settings = await db.query.setting.findFirst({ - where: (settingData, { eq }) => eq(settingData.userId, userId), - }) - - if (!settings) { - await db.insert(setting).values({ userId }) - } - await db - .update(setting) - .set(qrUpdate.data) - .where(eq(setting.userId, userId)) - return new Response(JSON.stringify({ success: true })) - } catch (error) { - return new Response(JSON.stringify({ success: false })) - } -}
- Update your account settings. +
+ Update your QR settings.