aboutsummaryrefslogtreecommitdiff
path: root/src/routes/categories
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/categories')
-rw-r--r--src/routes/categories/+page.server.ts38
-rw-r--r--src/routes/categories/+page.svelte134
2 files changed, 172 insertions, 0 deletions
diff --git a/src/routes/categories/+page.server.ts b/src/routes/categories/+page.server.ts
new file mode 100644
index 0000000..241370e
--- /dev/null
+++ b/src/routes/categories/+page.server.ts
@@ -0,0 +1,38 @@
+import { fail } from '@sveltejs/kit';
+import { db } from '$lib/server/db';
+import { categories } from '$lib/server/db/schema';
+import { eq, asc } from 'drizzle-orm';
+import type { Actions, PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async () => {
+ const items = await db
+ .select()
+ .from(categories)
+ .orderBy(asc(categories.name));
+
+ return { categories: items };
+};
+
+export const actions: Actions = {
+ add: async ({ request }) => {
+ const data = await request.formData();
+ const name = data.get('name')?.toString().trim();
+
+ if (!name) {
+ return fail(400, { error: 'Name is required' });
+ }
+
+ await db.insert(categories).values({ name });
+
+ return { success: true };
+ },
+
+ delete: async ({ request }) => {
+ const data = await request.formData();
+ const id = Number(data.get('id'));
+
+ await db.delete(categories).where(eq(categories.id, id));
+
+ return { success: true };
+ },
+};
diff --git a/src/routes/categories/+page.svelte b/src/routes/categories/+page.svelte
new file mode 100644
index 0000000..0dfc96c
--- /dev/null
+++ b/src/routes/categories/+page.svelte
@@ -0,0 +1,134 @@
+<script lang="ts">
+ import '../../app.css';
+ import { enhance } from '$app/forms';
+
+ let { data, form } = $props();
+ let name = $state('');
+</script>
+
+<h1>Categories</h1>
+
+<form class="add-form" method="POST" action="?/add" use:enhance>
+ <label>
+ <span class="label">New category</span>
+ <input
+ type="text"
+ name="name"
+ bind:value={name}
+ placeholder="e.g. Boissons"
+ autocomplete="off"
+ />
+ </label>
+ <button type="submit">Add</button>
+</form>
+
+{#if form?.error}
+ <p class="error">{form.error}</p>
+{/if}
+
+<form method="POST" action="?/delete" use:enhance>
+ <ul class="item-list">
+ {#each data?.categories ?? [] as cat (cat.id)}
+ <li class="catalog-item">
+ <span class="cat-name">{cat.name}</span>
+ <button class="delete-btn" name="id" value={cat.id} type="submit">
+ &times;
+ </button>
+ </li>
+ {/each}
+ </ul>
+</form>
+
+{#if (data?.categories?.length ?? 0) === 0}
+ <p class="empty">No categories yet</p>
+{/if}
+
+<style>
+ .add-form {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+ }
+
+ .add-form label {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .label {
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ color: #999;
+ }
+
+ input {
+ padding: 0.75rem;
+ border: 1px solid #ccc;
+ border-radius: 0.5rem;
+ font-size: 1rem;
+ }
+
+ .add-form button[type='submit'] {
+ padding: 0.75rem 1.25rem;
+ border: none;
+ border-radius: 0.5rem;
+ background: #333;
+ color: #fff;
+ font-size: 1rem;
+ align-self: flex-start;
+ }
+
+ .error {
+ color: #d00;
+ margin-top: -0.5rem;
+ margin-bottom: 1rem;
+ }
+
+ .item-list {
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .catalog-item {
+ display: flex;
+ gap: 0.25rem;
+ height: 2rem;
+ align-items: center;
+ }
+
+ .cat-name {
+ flex-grow: 1;
+ padding: 0 0.75rem;
+ }
+
+ .delete-btn {
+ border: 1px solid #e0e0e0;
+ border-radius: 0.5rem;
+ background: #fff;
+ color: #d00;
+ transition: background 0.15s;
+ width: 2rem;
+ aspect-ratio: 1;
+ padding: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.25rem;
+ cursor: pointer;
+ }
+
+ .delete-btn:hover {
+ background: #fee;
+ }
+
+ .empty {
+ color: #999;
+ font-size: 0.875rem;
+ margin-top: 0.5rem;
+ }
+</style>