diff --git a/bun.lockb b/bun.lockb index 7a2c9e3..1f97931 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index c262846..b7ef6a4 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,14 @@ "prettier-plugin-tailwindcss": "^0.6.10", "svelte": "^5.0.0", "svelte-check": "^4.0.0", + "sveltekit-superforms": "^2.23.1", "tailwind-merge": "^3.0.1", "tailwind-variants": "^0.3.1", "tailwindcss": "^3.4.17", "typescript": "^5.0.0", "typescript-eslint": "^8.20.0", - "vite": "^6.0.0" + "vite": "^6.0.0", + "zod": "^3.24.1" }, "dependencies": { "@sveltejs/adapter-node": "^5.2.12", diff --git a/src/lib/components/channelDialog.svelte b/src/lib/components/channelDialog.svelte index 38ed02f..2dfd4a2 100644 --- a/src/lib/components/channelDialog.svelte +++ b/src/lib/components/channelDialog.svelte @@ -2,6 +2,13 @@ import { Button, buttonVariants } from '$lib/components/ui/button/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js'; import { Input } from '$lib/components/ui/input/index.js'; + import type { SuperValidated, Infer } from 'sveltekit-superforms'; + import { superForm } from 'sveltekit-superforms'; + import type { NewChannelSchema } from '$lib/types/schema'; + import { Label } from '$lib/components/ui/label/index'; + + let { data }: { data: SuperValidated> } = $props(); + const { form, errors, constraints, enhance } = superForm(data); @@ -10,11 +17,19 @@ Create Channel -
- + + + {#if $errors.channelName}{/if} + + +
- - -
diff --git a/src/lib/components/mainLayout.svelte b/src/lib/components/mainLayout.svelte index 3eae795..877c51a 100644 --- a/src/lib/components/mainLayout.svelte +++ b/src/lib/components/mainLayout.svelte @@ -1,17 +1,26 @@
@@ -32,7 +41,7 @@
- +
diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte index 0bc61b3..defccc8 100644 --- a/src/lib/components/ui/dialog/dialog-content.svelte +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -1,36 +1,34 @@ - - - - - - Close - - + + + + + + Close + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte index 8bc70cc..64e79f7 100644 --- a/src/lib/components/ui/dialog/dialog-description.svelte +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -1,16 +1,13 @@ - - + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte index a235d1f..c625327 100644 --- a/src/lib/components/ui/dialog/dialog-footer.svelte +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -1,16 +1,13 @@ -
- +
+
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte index 6b4448c..c0a1367 100644 --- a/src/lib/components/ui/dialog/dialog-header.svelte +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -1,13 +1,13 @@ -
- +
+
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte index 9da95c5..2ef74db 100644 --- a/src/lib/components/ui/dialog/dialog-overlay.svelte +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -1,21 +1,20 @@ + {transition} + {transitionConfig} + class={cn('bg-background/80 fixed inset-0 z-50 backdrop-blur-sm ', className)} + {...$$restProps} /> diff --git a/src/lib/components/ui/dialog/dialog-portal.svelte b/src/lib/components/ui/dialog/dialog-portal.svelte index 400e62b..e0720dc 100644 --- a/src/lib/components/ui/dialog/dialog-portal.svelte +++ b/src/lib/components/ui/dialog/dialog-portal.svelte @@ -1,9 +1,9 @@ - + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte index 06574f3..c514c31 100644 --- a/src/lib/components/ui/dialog/dialog-title.svelte +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -1,16 +1,13 @@ - - + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts index b17ba5e..707a04e 100644 --- a/src/lib/components/ui/dialog/index.ts +++ b/src/lib/components/ui/dialog/index.ts @@ -1,37 +1,37 @@ -import { Dialog as DialogPrimitive } from "bits-ui"; +import { Dialog as DialogPrimitive } from 'bits-ui'; -import Title from "./dialog-title.svelte"; -import Portal from "./dialog-portal.svelte"; -import Footer from "./dialog-footer.svelte"; -import Header from "./dialog-header.svelte"; -import Overlay from "./dialog-overlay.svelte"; -import Content from "./dialog-content.svelte"; -import Description from "./dialog-description.svelte"; +import Title from './dialog-title.svelte'; +import Portal from './dialog-portal.svelte'; +import Footer from './dialog-footer.svelte'; +import Header from './dialog-header.svelte'; +import Overlay from './dialog-overlay.svelte'; +import Content from './dialog-content.svelte'; +import Description from './dialog-description.svelte'; const Root = DialogPrimitive.Root; const Trigger = DialogPrimitive.Trigger; const Close = DialogPrimitive.Close; export { - Root, - Title, - Portal, - Footer, - Header, - Trigger, - Overlay, - Content, - Description, - Close, - // - Root as Dialog, - Title as DialogTitle, - Portal as DialogPortal, - Footer as DialogFooter, - Header as DialogHeader, - Trigger as DialogTrigger, - Overlay as DialogOverlay, - Content as DialogContent, - Description as DialogDescription, - Close as DialogClose, + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, }; diff --git a/src/lib/components/ui/label/index.ts b/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..36fb393 --- /dev/null +++ b/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from './label.svelte'; + +export { + Root, + // + Root as Label, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..a751e7f --- /dev/null +++ b/src/lib/components/ui/label/label.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index 3d21638..5a1a8ed 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -62,6 +62,19 @@ class Db { } } + async checkChannel(channel: string): Promise { + try { + const res = await this.client.execute(`SELECT table_name FROM system_schema.tables WHERE keyspace_name = 'channels' AND table_name = ?`, [ + channel.toLowerCase(), + ]); + + return res.rowLength !== 0; + } catch (e) { + console.log(`Error checking channel existance: ${e as Error}`); + return false; + } + } + // Get Channels method async getChannels(): Promise { try { diff --git a/src/lib/types/schema.ts b/src/lib/types/schema.ts new file mode 100644 index 0000000..cb6e77b --- /dev/null +++ b/src/lib/types/schema.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +export const newChannelSchema = z.object({ + channelName: z.string().min(1, 'Channel name is required'), +}); + +export type NewChannelSchema = typeof newChannelSchema; diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 8640f49..e4706b2 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,6 +1,10 @@ import { db } from '$lib/server/db'; +import { zod } from 'sveltekit-superforms/adapters'; +import { superValidate } from 'sveltekit-superforms'; +import { newChannelSchema } from '$lib/types/schema'; export async function load() { + const form = await superValidate(zod(newChannelSchema)); const rows = await db.getChannels(); const channels: string[] = rows ? rows.map((value) => { @@ -10,5 +14,6 @@ export async function load() { return { channels, + form, }; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9cb4ec7..ee13ea1 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -4,10 +4,9 @@ import MainLayout from '$lib/components/mainLayout.svelte'; import { ModeWatcher } from 'mode-watcher'; let { data, children }: LayoutProps = $props(); - const channels = data.channels; - + {@render children()} diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 0b7dad2..9c24f06 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,5 +1,29 @@ -import { redirect } from '@sveltejs/kit'; +import { redirect, fail } from '@sveltejs/kit'; +import { zod } from 'sveltekit-superforms/adapters'; +import { setError, superValidate, message } from 'sveltekit-superforms'; +import { newChannelSchema } from '$lib/types/schema'; +import type { Actions } from './$types'; +import { db } from '$lib/server/db'; export function load(): void { redirect(308, '/channel/general'); } + +export const actions = { + default: async ({ request }) => { + const form = await superValidate(request, zod(newChannelSchema)); + const channel = form.data.channelName; + + if (!form.valid) { + return fail(400, { form }); + } + + if (await db.checkChannel(channel)) { + return setError(form, 'channelName', 'Channel already exists.'); + } + + db.createChannel(channel); + + return message(form, 'Channel created!'); + }, +} satisfies Actions;