Fix checklist alignment and input completion tracking

- All items use a consistent grid layout (checkbox/indicator, content, delete)
- Input items show a green check indicator when a value is filled
- Saving a value auto-sets checked=true (clearing it sets checked=false)
- Progress bar and count now include input items with values as completed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 11:03:24 +07:00
parent 97f97f5571
commit d40dc9b796
2 changed files with 35 additions and 32 deletions
@@ -325,11 +325,11 @@ export const actions: Actions = {
saveChecklistValue: async ({ request }) => { saveChecklistValue: async ({ request }) => {
const formData = await request.formData(); const formData = await request.formData();
const itemId = formData.get('itemId') as string; const itemId = formData.get('itemId') as string;
const value = formData.get('value') as string; const value = (formData.get('value') as string)?.trim();
await db await db
.update(checklistItems) .update(checklistItems)
.set({ value: value || null }) .set({ value: value || null, checked: !!value })
.where(eq(checklistItems.id, itemId)); .where(eq(checklistItems.id, itemId));
return { valueSaved: true }; return { valueSaved: true };
+33 -30
View File
@@ -385,7 +385,7 @@
<h3 class="text-sm font-medium text-gray-900 dark:text-white">{checklist.title}</h3> <h3 class="text-sm font-medium text-gray-900 dark:text-white">{checklist.title}</h3>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-xs text-gray-400 dark:text-gray-500"> <span class="text-xs text-gray-400 dark:text-gray-500">
{checklist.items.filter((i: any) => i.checked).length}/{checklist.items.length} {checklist.items.filter((i: any) => i.itemType === 'input' ? !!i.value : i.checked).length}/{checklist.items.length}
</span> </span>
<form method="POST" action="?/deleteChecklist" use:enhance> <form method="POST" action="?/deleteChecklist" use:enhance>
<input type="hidden" name="checklistId" value={checklist.id} /> <input type="hidden" name="checklistId" value={checklist.id} />
@@ -400,7 +400,8 @@
<!-- Progress bar --> <!-- Progress bar -->
{#if checklist.items.length > 0} {#if checklist.items.length > 0}
{@const pct = Math.round((checklist.items.filter((i: any) => i.checked).length / checklist.items.length) * 100)} {@const completed = checklist.items.filter((i: any) => i.itemType === 'input' ? !!i.value : i.checked).length}
{@const pct = Math.round((completed / checklist.items.length) * 100)}
<div class="mb-2 h-1.5 w-full rounded-full bg-gray-100 dark:bg-gray-700"> <div class="mb-2 h-1.5 w-full rounded-full bg-gray-100 dark:bg-gray-700">
<div class="h-1.5 rounded-full transition-all {pct === 100 ? 'bg-green-500' : 'bg-blue-500'}" style="width: {pct}%"></div> <div class="h-1.5 rounded-full transition-all {pct === 100 ? 'bg-green-500' : 'bg-blue-500'}" style="width: {pct}%"></div>
</div> </div>
@@ -409,12 +410,24 @@
<!-- Items --> <!-- Items -->
<div class="space-y-1.5"> <div class="space-y-1.5">
{#each checklist.items as item} {#each checklist.items as item}
{#if item.itemType === 'input'} <div class="group grid grid-cols-[auto_1fr_auto] items-center gap-2">
<!-- Input type item --> {#if item.itemType === 'input'}
<div class="group flex items-center gap-2"> <!-- Input: filled = completed -->
<span class="flex-shrink-0 text-sm text-gray-700 dark:text-gray-300">{item.text}</span> {@const filled = !!item.value}
<form method="POST" action="?/saveChecklistValue" use:enhance class="flex flex-1 items-center gap-1"> <span class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded border
{filled
? 'border-green-500 bg-green-500 text-white dark:border-green-400 dark:bg-green-500'
: 'border-gray-200 dark:border-gray-600'}
">
{#if filled}
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7" />
</svg>
{/if}
</span>
<form method="POST" action="?/saveChecklistValue" use:enhance class="flex items-center gap-2">
<input type="hidden" name="itemId" value={item.id} /> <input type="hidden" name="itemId" value={item.id} />
<span class="text-sm text-gray-700 dark:text-gray-300">{item.text}</span>
<input type="text" name="value" value={item.value ?? ''} <input type="text" name="value" value={item.value ?? ''}
placeholder="—" placeholder="—"
class="w-24 rounded border border-gray-200 px-2 py-0.5 text-sm text-right font-mono focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none dark:border-gray-600 dark:bg-gray-700 dark:text-white" /> class="w-24 rounded border border-gray-200 px-2 py-0.5 text-sm text-right font-mono focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none dark:border-gray-600 dark:bg-gray-700 dark:text-white" />
@@ -423,18 +436,8 @@
{/if} {/if}
<button type="submit" class="rounded px-1.5 py-0.5 text-xs text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-900/20">Save</button> <button type="submit" class="rounded px-1.5 py-0.5 text-xs text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-blue-900/20">Save</button>
</form> </form>
<form method="POST" action="?/deleteChecklistItem" use:enhance> {:else}
<input type="hidden" name="itemId" value={item.id} /> <!-- Checkbox -->
<button type="submit" class="hidden text-gray-400 hover:text-red-500 group-hover:block dark:text-gray-500 dark:hover:text-red-400" title="Remove">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</form>
</div>
{:else}
<!-- Checkbox type item -->
<div class="group flex items-center gap-2">
<form method="POST" action="?/toggleChecklistItem" use:enhance class="flex items-center"> <form method="POST" action="?/toggleChecklistItem" use:enhance class="flex items-center">
<input type="hidden" name="itemId" value={item.id} /> <input type="hidden" name="itemId" value={item.id} />
<input type="hidden" name="checked" value={String(item.checked)} /> <input type="hidden" name="checked" value={String(item.checked)} />
@@ -450,19 +453,19 @@
{/if} {/if}
</button> </button>
</form> </form>
<span class="flex-1 text-sm {item.checked ? 'text-gray-400 line-through dark:text-gray-500' : 'text-gray-700 dark:text-gray-300'}"> <span class="text-sm {item.checked ? 'text-gray-400 line-through dark:text-gray-500' : 'text-gray-700 dark:text-gray-300'}">
{item.text} {item.text}
</span> </span>
<form method="POST" action="?/deleteChecklistItem" use:enhance> {/if}
<input type="hidden" name="itemId" value={item.id} /> <form method="POST" action="?/deleteChecklistItem" use:enhance>
<button type="submit" class="hidden text-gray-400 hover:text-red-500 group-hover:block dark:text-gray-500 dark:hover:text-red-400" title="Remove"> <input type="hidden" name="itemId" value={item.id} />
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <button type="submit" class="hidden text-gray-400 hover:text-red-500 group-hover:block dark:text-gray-500 dark:hover:text-red-400" title="Remove">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> <svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</button> </svg>
</form> </button>
</div> </form>
{/if} </div>
{/each} {/each}
</div> </div>