feat(04-03): install Svelecte and add tag support to load/actions

- Install svelecte package for tag input with autocomplete
- Import tagRepository in +page.server.ts
- Attach tags to entries in load function alongside images
- Return allTags for autocomplete suggestions
- Add updateTags action to update entry tags via JSON payload
This commit is contained in:
Thomas Richter
2026-01-31 13:07:08 +01:00
parent 66fc04efe7
commit cfdb804118
3 changed files with 44 additions and 5 deletions

10
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"drizzle-orm": "^0.45.1", "drizzle-orm": "^0.45.1",
"nanoid": "^5.1.6", "nanoid": "^5.1.6",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"svelecte": "^5.3.0",
"svelte-gestures": "^5.2.2", "svelte-gestures": "^5.2.2",
"svelte-lightbox": "^1.1.7", "svelte-lightbox": "^1.1.7",
"svelte-persisted-store": "^0.12.0", "svelte-persisted-store": "^0.12.0",
@@ -5164,6 +5165,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/svelecte": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/svelecte/-/svelecte-5.3.0.tgz",
"integrity": "sha512-9WKD6KxPf31CweyZtObTetGtkq1TncBA2E+aSHcT+bfjALgP+LUGMMQUU+2qhlfSoGpw9JYmLfxphGjb0q350g==",
"license": "MIT",
"peerDependencies": {
"svelte": "^5.2.7"
}
},
"node_modules/svelte": { "node_modules/svelte": {
"version": "5.49.0", "version": "5.49.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.49.0.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.49.0.tgz",

View File

@@ -36,6 +36,7 @@
"drizzle-orm": "^0.45.1", "drizzle-orm": "^0.45.1",
"nanoid": "^5.1.6", "nanoid": "^5.1.6",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"svelecte": "^5.3.0",
"svelte-gestures": "^5.2.2", "svelte-gestures": "^5.2.2",
"svelte-lightbox": "^1.1.7", "svelte-lightbox": "^1.1.7",
"svelte-persisted-store": "^0.12.0", "svelte-persisted-store": "^0.12.0",

View File

@@ -1,6 +1,6 @@
import type { PageServerLoad, Actions } from './$types'; import type { PageServerLoad, Actions } from './$types';
import { fail } from '@sveltejs/kit'; import { fail } from '@sveltejs/kit';
import { entryRepository, imageRepository } from '$lib/server/db/repository'; import { entryRepository, imageRepository, tagRepository } from '$lib/server/db/repository';
import { import {
saveOriginal, saveOriginal,
saveThumbnail, saveThumbnail,
@@ -13,14 +13,19 @@ export const load: PageServerLoad = async ({ url }) => {
const showCompleted = url.searchParams.get('showCompleted') === 'true'; const showCompleted = url.searchParams.get('showCompleted') === 'true';
const entries = entryRepository.getOrdered({ showCompleted }); const entries = entryRepository.getOrdered({ showCompleted });
// Attach images to each entry // Attach images AND tags to each entry
const entriesWithImages = entries.map((entry) => ({ const entriesWithData = entries.map((entry) => ({
...entry, ...entry,
images: imageRepository.getByEntryId(entry.id) images: imageRepository.getByEntryId(entry.id),
tags: tagRepository.getByEntryId(entry.id)
})); }));
// Get all tags for autocomplete
const allTags = tagRepository.getAll();
return { return {
entries: entriesWithImages, entries: entriesWithData,
allTags,
showCompleted showCompleted
}; };
}; };
@@ -260,5 +265,28 @@ export const actions: Actions = {
entryRepository.update(id, { dueDate: dueDate || null }); entryRepository.update(id, { dueDate: dueDate || null });
return { success: true }; return { success: true };
},
updateTags: async ({ request }) => {
const formData = await request.formData();
const id = formData.get('id')?.toString();
const tagsJson = formData.get('tags')?.toString() || '[]';
if (!id) {
return fail(400, { error: 'Entry ID is required' });
}
const existing = entryRepository.getById(id);
if (!existing) {
return fail(404, { error: 'Entry not found' });
}
try {
const tagNames = JSON.parse(tagsJson) as string[];
tagRepository.updateEntryTags(id, tagNames);
return { success: true };
} catch {
return fail(400, { error: 'Invalid tags format' });
}
} }
}; };