aboutsummaryrefslogtreecommitdiff
path: root/src/routes
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/categories/+page.server.ts17
-rw-r--r--src/routes/categories/+page.svelte67
2 files changed, 80 insertions, 4 deletions
diff --git a/src/routes/categories/+page.server.ts b/src/routes/categories/+page.server.ts
index ac53ca8..c97b619 100644
--- a/src/routes/categories/+page.server.ts
+++ b/src/routes/categories/+page.server.ts
@@ -1,5 +1,10 @@
import { fail } from '@sveltejs/kit';
-import { loadAll, add, deleteCategory } from '$lib/server/services/categories';
+import {
+ loadAll,
+ add,
+ deleteCategory,
+ reorder,
+} from '$lib/server/services/categories';
import { numericId, parseFormString } from '$lib/server/services/validation';
import type { Category } from '$lib/server/services/types';
import type { Actions, PageServerLoad } from './$types';
@@ -35,4 +40,14 @@ export const actions: Actions = {
}
return { success: true };
},
+
+ reorder: async ({ request }) => {
+ const data = await request.formData();
+ const ids = data.getAll('orderedIds').map(numericId);
+ if (ids.length === 0) {
+ return fail(400, { error: 'Aucune catégorie' });
+ }
+ await reorder(ids);
+ return { success: true };
+ },
};
diff --git a/src/routes/categories/+page.svelte b/src/routes/categories/+page.svelte
index 1060eaa..9c91578 100644
--- a/src/routes/categories/+page.svelte
+++ b/src/routes/categories/+page.svelte
@@ -3,6 +3,36 @@
let { data, form } = $props();
let name = $state('');
+ let draggedIndex = $state<number | null>(null);
+ // eslint-disable-next-line svelte/prefer-writable-derived
+ let localCategories = $state(data?.categories ?? []);
+
+ // Sync with server data on navigation
+ $effect(() => {
+ localCategories = data?.categories ?? [];
+ });
+
+ let reorderFormRef: HTMLFormElement;
+
+ function handleDragStart(index: number) {
+ draggedIndex = index;
+ }
+
+ function handleDragOver(e: DragEvent, index: number) {
+ e.preventDefault();
+ if (draggedIndex === null || draggedIndex === index) return;
+
+ const next = [...localCategories];
+ const [moved] = next.splice(draggedIndex, 1);
+ next.splice(index, 0, moved);
+ localCategories = next;
+ draggedIndex = index;
+ }
+
+ function handleDrop() {
+ draggedIndex = null;
+ reorderFormRef?.requestSubmit();
+ }
</script>
<main>
@@ -30,10 +60,33 @@
<p class="error">{form.error}</p>
{/if}
+ <form
+ bind:this={reorderFormRef}
+ method="POST"
+ action="?/reorder"
+ use:enhance={() => {
+ return async ({ update }) => {
+ await update({ reset: false });
+ };
+ }}
+ >
+ {#each localCategories as cat (cat.id)}
+ <input type="hidden" name="orderedIds" value={cat.id} />
+ {/each}
+ </form>
+
<form method="POST" action="?/delete" use:enhance>
<ul class="item-list">
- {#each data?.categories ?? [] as cat (cat.id)}
- <li class="item-row item-row--card">
+ {#each localCategories as cat, index (cat.id)}
+ <li
+ class="item-row item-row--card"
+ draggable="true"
+ ondragstart={() => handleDragStart(index)}
+ ondragover={(e) => handleDragOver(e, index)}
+ ondrop={handleDrop}
+ ondragend={() => (draggedIndex = null)}
+ >
+ <span class="drag-handle">⋮⋮</span>
<span class="cat-name">{cat.name}</span>
<button
class="btn-ghost"
@@ -49,7 +102,7 @@
</ul>
</form>
- {#if (data?.categories?.length ?? 0) === 0}
+ {#if (localCategories.length ?? 0) === 0}
<p class="empty">Aucune catégorie</p>
{/if}
</main>
@@ -61,6 +114,14 @@
color: var(--text);
}
+ .drag-handle {
+ color: var(--text-secondary);
+ font-size: 0.875rem;
+ cursor: grab;
+ padding-right: 0.5rem;
+ user-select: none;
+ }
+
.error {
margin-top: -0.25rem;
margin-bottom: 0.5rem;