mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
feat(guides): collection selector in guide modal, quick inline step add
GuideEditModal: - Live collection list loaded from IndexedDB - Select dropdown to assign guide to a collection (or none) - Collection persisted on save Guide detail — quick step add (no modal needed): - Click "+ Schritt hinzufügen" → inline input appears - Enter to save, Esc to cancel, ⋯ to open full StepEditorModal - Works in both sections and unsectioned step list - Input stays open after adding (for rapid multi-step entry) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1a999f8cca
commit
7f1c83f8b6
2 changed files with 96 additions and 16 deletions
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { LocalGuide, Difficulty } from '$lib/data/local-store.js';
|
||||
import { liveQuery } from 'dexie';
|
||||
import type { LocalGuide, Difficulty, LocalCollection } from '$lib/data/local-store.js';
|
||||
import { collectionCollection } from '$lib/data/local-store.js';
|
||||
import type { BaseRecord } from '@manacore/local-store';
|
||||
|
||||
type GuideInput = Omit<LocalGuide, keyof BaseRecord>;
|
||||
|
|
@ -22,8 +24,18 @@
|
|||
let difficulty = $state<Difficulty>(guide?.difficulty ?? 'easy');
|
||||
let estimatedMinutes = $state(guide?.estimatedMinutes ?? 0);
|
||||
let tagsInput = $state((guide?.tags ?? []).join(', '));
|
||||
let collectionId = $state<string | undefined>(guide?.collectionId);
|
||||
let saving = $state(false);
|
||||
|
||||
// Live collections for selector
|
||||
let allCollections = $state<LocalCollection[]>([]);
|
||||
$effect(() => {
|
||||
const sub = liveQuery(() => collectionCollection.getAll()).subscribe((cols) => {
|
||||
allCollections = cols;
|
||||
});
|
||||
return () => sub.unsubscribe();
|
||||
});
|
||||
|
||||
// Re-init when guide prop changes
|
||||
$effect(() => {
|
||||
if (guide) {
|
||||
|
|
@ -34,6 +46,7 @@
|
|||
difficulty = guide.difficulty;
|
||||
estimatedMinutes = guide.estimatedMinutes ?? 0;
|
||||
tagsInput = guide.tags.join(', ');
|
||||
collectionId = guide.collectionId;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -68,7 +81,7 @@
|
|||
difficulty,
|
||||
estimatedMinutes: estimatedMinutes > 0 ? estimatedMinutes : undefined,
|
||||
tags,
|
||||
collectionId: guide?.collectionId,
|
||||
collectionId: collectionId || undefined,
|
||||
orderInCollection: guide?.orderInCollection,
|
||||
xpReward: guide?.xpReward,
|
||||
skillId: guide?.skillId,
|
||||
|
|
@ -217,6 +230,22 @@
|
|||
class="w-full rounded-xl border border-border bg-surface px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Collection -->
|
||||
{#if allCollections.length > 0}
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-medium text-muted-foreground">Sammlung</label>
|
||||
<select
|
||||
bind:value={collectionId}
|
||||
class="w-full rounded-xl border border-border bg-surface px-3 py-2.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
>
|
||||
<option value={undefined}>Keine Sammlung</option>
|
||||
{#each allCollections as col}
|
||||
<option value={col.id}>{col.coverEmoji ?? ''} {col.title}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
|
|
|
|||
|
|
@ -77,6 +77,25 @@
|
|||
let editingStep = $state<LocalStep | undefined>(undefined);
|
||||
let stepModalSectionId = $state<string | undefined>(undefined);
|
||||
|
||||
// Quick-add state (inline, no modal)
|
||||
let quickAddSectionId = $state<string | 'root' | null>(null);
|
||||
let quickAddTitle = $state('');
|
||||
|
||||
async function quickAddStep(sectionId?: string) {
|
||||
if (!quickAddTitle.trim()) { quickAddSectionId = null; return; }
|
||||
const targetSteps = sectionId ? getStepsForSection(sectionId) : getUnsectionedSteps();
|
||||
await guidesStore.createStep({
|
||||
guideId,
|
||||
sectionId,
|
||||
order: targetSteps.length,
|
||||
title: quickAddTitle.trim(),
|
||||
type: 'instruction',
|
||||
checkable: true,
|
||||
});
|
||||
quickAddTitle = '';
|
||||
// keep open for next step
|
||||
}
|
||||
|
||||
function openAddStep(sectionId?: string) {
|
||||
editingStep = undefined;
|
||||
stepModalSectionId = sectionId;
|
||||
|
|
@ -305,13 +324,26 @@
|
|||
{/each}
|
||||
|
||||
{#if editMode}
|
||||
<button
|
||||
onclick={() => openAddStep(section.id)}
|
||||
class="flex w-full items-center gap-2 rounded-xl border border-dashed border-border px-4 py-3 text-sm text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary"
|
||||
>
|
||||
<span class="text-lg leading-none">+</span>
|
||||
Schritt hinzufügen
|
||||
</button>
|
||||
{#if quickAddSectionId === section.id}
|
||||
<div class="flex gap-2">
|
||||
<input type="text" bind:value={quickAddTitle}
|
||||
placeholder="Schritt-Titel (Enter = hinzufügen)" autofocus
|
||||
onkeydown={async (e) => {
|
||||
if (e.key === 'Enter') await quickAddStep(section.id);
|
||||
if (e.key === 'Escape') { quickAddSectionId = null; quickAddTitle = ''; }
|
||||
}}
|
||||
class="flex-1 rounded-xl border border-primary/50 bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
/>
|
||||
<button onclick={() => quickAddStep(section.id)} class="rounded-xl bg-primary px-3 py-2 text-sm text-white">+</button>
|
||||
<button onclick={() => { quickAddSectionId = null; quickAddTitle = ''; }} class="rounded-xl border border-border px-3 py-2 text-sm text-muted-foreground">✕</button>
|
||||
<button onclick={() => openAddStep(section.id)} class="rounded-xl border border-border px-2 py-2 text-xs text-muted-foreground hover:bg-accent" title="Erweitert">⋯</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button onclick={() => { quickAddSectionId = section.id; quickAddTitle = ''; }}
|
||||
class="flex w-full items-center gap-2 rounded-xl border border-dashed border-border px-4 py-3 text-sm text-muted-foreground hover:border-primary/40 hover:text-primary">
|
||||
<span class="text-lg leading-none">+</span> Schritt hinzufügen
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -344,13 +376,32 @@
|
|||
{/each}
|
||||
|
||||
{#if editMode}
|
||||
<button
|
||||
onclick={() => openAddStep()}
|
||||
class="flex w-full items-center gap-2 rounded-xl border border-dashed border-border px-4 py-3 text-sm text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary"
|
||||
>
|
||||
<span class="text-lg leading-none">+</span>
|
||||
Schritt hinzufügen
|
||||
</button>
|
||||
{#if quickAddSectionId === 'root'}
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={quickAddTitle}
|
||||
placeholder="Schritt-Titel (Enter = hinzufügen)"
|
||||
autofocus
|
||||
onkeydown={async (e) => {
|
||||
if (e.key === 'Enter') await quickAddStep(undefined);
|
||||
if (e.key === 'Escape') { quickAddSectionId = null; quickAddTitle = ''; }
|
||||
}}
|
||||
class="flex-1 rounded-xl border border-primary/50 bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary/30"
|
||||
/>
|
||||
<button onclick={() => quickAddStep(undefined)} class="rounded-xl bg-primary px-3 py-2 text-sm text-white hover:bg-primary-hover">+</button>
|
||||
<button onclick={() => { quickAddSectionId = null; quickAddTitle = ''; }} class="rounded-xl border border-border px-3 py-2 text-sm text-muted-foreground">✕</button>
|
||||
<button onclick={() => openAddStep(undefined)} class="rounded-xl border border-border px-3 py-2 text-xs text-muted-foreground hover:bg-accent" title="Erweitert bearbeiten">⋯</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
onclick={() => { quickAddSectionId = 'root'; quickAddTitle = ''; }}
|
||||
class="flex w-full items-center gap-2 rounded-xl border border-dashed border-border px-4 py-3 text-sm text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary"
|
||||
>
|
||||
<span class="text-lg leading-none">+</span>
|
||||
Schritt hinzufügen
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue