From 684d3c954e3577a6488101bf5ee85e36439fd53c Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Wed, 29 Jul 2026 10:31:36 +0200 Subject: feat: multi-select and bulk-update category from catalog --- src/lib/server/services/groceries.ts | 21 +++++++- src/routes/catalog/+page.server.ts | 25 +++++++++- src/routes/catalog/+page.svelte | 97 ++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib/server/services/groceries.ts b/src/lib/server/services/groceries.ts index db9e736..3c6de70 100644 --- a/src/lib/server/services/groceries.ts +++ b/src/lib/server/services/groceries.ts @@ -1,6 +1,6 @@ import { db as defaultDb } from '$lib/server/db'; import { groceries, categories, references } from '$lib/server/db/schema'; -import { eq, desc } from 'drizzle-orm'; +import { eq, desc, inArray } from 'drizzle-orm'; import type { LibSQLDatabase } from 'drizzle-orm/libsql'; import type * as schema from '$lib/server/db/schema'; import type { Category, Grocery, Reference } from './types'; @@ -109,6 +109,25 @@ export async function updateGrocery( .where(eq(groceries.id, id)); } +export async function updateGroceryCategory( + id: number, + categoryId: number | null, + db = defaultDb, +): Promise { + await db.update(groceries).set({ categoryId }).where(eq(groceries.id, id)); +} + +export async function bulkUpdateGroceryCategory( + ids: number[], + categoryId: number | null, + db = defaultDb, +): Promise { + await db + .update(groceries) + .set({ categoryId }) + .where(inArray(groceries.id, ids)); +} + export async function deleteGrocery(id: number, db = defaultDb): Promise { await db.delete(groceries).where(eq(groceries.id, id)); } diff --git a/src/routes/catalog/+page.server.ts b/src/routes/catalog/+page.server.ts index 69a775a..22464dc 100644 --- a/src/routes/catalog/+page.server.ts +++ b/src/routes/catalog/+page.server.ts @@ -1,6 +1,13 @@ import { fail } from '@sveltejs/kit'; -import { loadAll, toggle } from '$lib/server/services/groceries'; -import { numericId } from '$lib/server/services/validation'; +import { + loadAll, + toggle, + bulkUpdateGroceryCategory, +} from '$lib/server/services/groceries'; +import { + numericId, + parseOptionalNumber, +} from '$lib/server/services/validation'; import type { EnrichedGrocery } from '$lib/server/services/groceries'; import type { Category } from '$lib/server/services/types'; import type { Actions, PageServerLoad } from './$types'; @@ -25,4 +32,18 @@ export const actions: Actions = { } return { success: true }; }, + + bulkUpdateCategory: async ({ request }) => { + const data = await request.formData(); + const ids = data.getAll('ids').map(numericId); + const rawCategoryId = parseOptionalNumber(data.get('category_id')); + const categoryId = rawCategoryId === -1 ? null : rawCategoryId; + + if (ids.length === 0) { + return fail(400, { error: 'Aucun article sélectionné' }); + } + + await bulkUpdateGroceryCategory(ids, categoryId); + return { success: true, type: 'bulkUpdate' as const }; + }, }; diff --git a/src/routes/catalog/+page.svelte b/src/routes/catalog/+page.svelte index 7d3ebbe..2079d12 100644 --- a/src/routes/catalog/+page.svelte +++ b/src/routes/catalog/+page.svelte @@ -1,9 +1,16 @@ +{#if catalogItems.length > 0} +
+
{ + return async ({ update }) => { + await update({ reset: false }); + selectedIds.clear(); + }; + }} + > + {#each Array.from(selectedIds) as id (id)} + + {/each} + + +
+
+{/if} +
+ {#if form?.error} +

{form.error}

+ {/if} + {#if catalogItems.length === 0}

Aucun article

{:else} @@ -25,6 +68,12 @@
    {#each items as item (item.id)}
  • + toggleId(item.id)} + />