diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-08 09:25:03 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-09-08 09:25:03 +0200 |
| commit | 3148742b323ab71f1ef768a2f4dc353c00944034 (patch) | |
| tree | 8f88990514fb58d11a8669b58a7265dbd3420fe0 /src/lib/server | |
| parent | 7d85a18867ca0fe2310218c63cfc696c0f916171 (diff) | |
Share grocery row component and list-page form actions
- GroceryRow.svelte renders the toggle row (checkbox, name, best ref,
arrow link) for both the list and catalog pages
- grocery-actions.ts grows full form actions: addCategoryAction,
addGroceryAction, addGroceryAndEditAction, deleteByIdAction; the
categories/groceries route servers become thin wrappers
Diffstat (limited to 'src/lib/server')
| -rw-r--r-- | src/lib/server/services/grocery-actions.ts | 76 |
1 files changed, 73 insertions, 3 deletions
diff --git a/src/lib/server/services/grocery-actions.ts b/src/lib/server/services/grocery-actions.ts index 0966c92..c8974e9 100644 --- a/src/lib/server/services/grocery-actions.ts +++ b/src/lib/server/services/grocery-actions.ts @@ -1,6 +1,7 @@ -import { fail } from '@sveltejs/kit'; -import { toggle } from './groceries'; -import { numericId } from './validation'; +import { fail, redirect, type ActionFailure } from '@sveltejs/kit'; +import { addOrUncheck, loadUnchecked, toggle } from './groceries'; +import { add as addCategory } from './categories'; +import { numericId, parseFormString, parseOptionalNumber } from './validation'; export async function toggleGroceryAction(request: Request) { const data = await request.formData(); @@ -12,3 +13,72 @@ export async function toggleGroceryAction(request: Request) { } return { success: true }; } + +export type DeleteRowResult = + ActionFailure<{ error: string }> | { success: boolean; type?: 'delete' }; + +/** + * Shared 'delete a row by its id form field' action, used by the category + * list and the reference list (both submit one form whose buttons carry the + * row id). + */ +export async function deleteByIdAction( + request: Request, + deleteFn: (id: number) => Promise<unknown>, + extra?: { type: 'delete' }, +): Promise<DeleteRowResult> { + const data = await request.formData(); + try { + await deleteFn(numericId(data.get('id'))); + } catch { + return fail(400, { error: 'ID invalide' }); + } + return { success: true, type: extra?.type }; +} + +/** Parses the required 'name' field of an add form, or returns the failure. */ +function requireFormName( + data: FormData, +): string | ActionFailure<{ error: string }> { + const name = parseFormString(data.get('name')); + if (!name) { + return fail(400, { error: 'Le nom est requis' }); + } + return name; +} + +export async function addCategoryAction(request: Request) { + const data = await request.formData(); + const name = requireFormName(data); + if (typeof name !== 'string') { + return name; + } + + await addCategory(name); + return { success: true }; +} + +export async function addGroceryAction(request: Request) { + const data = await request.formData(); + const name = requireFormName(data); + if (typeof name !== 'string') { + return name; + } + const categoryId = parseOptionalNumber(data.get('category_id')); + + await addOrUncheck(name, categoryId); + const { items } = await loadUnchecked(); + return { success: true, items }; +} + +export async function addGroceryAndEditAction(request: Request) { + const data = await request.formData(); + const name = requireFormName(data); + if (typeof name !== 'string') { + return name; + } + const categoryId = parseOptionalNumber(data.get('category_id')); + + const { item } = await addOrUncheck(name, categoryId); + throw redirect(303, `/grocery/${item.id}`); +} |
