diff --git a/src/lib/components/SchemaForm.svelte b/src/lib/components/SchemaForm.svelte index 5e959c9..a976b3b 100644 --- a/src/lib/components/SchemaForm.svelte +++ b/src/lib/components/SchemaForm.svelte @@ -1,25 +1,35 @@
+ +
+ + + {#if showTemplateSwitcher} + + {/if} + + {#if pendingMigration} + + {/if} +
+ + {#if suggestion}

@@ -94,3 +148,50 @@ /> {/each}

+ +{#if showMigrationModal && pendingMigration} + { showMigrationModal = false; }}> +

Template Update

+

The {pendingMigration.preset.name} template has been updated:

+ +

Your existing data will be preserved.

+
+ + +
+
+{/if} + + { + if (showTemplateSwitcher) { + showTemplateSwitcher = false; + } +}} /> diff --git a/src/lib/components/UpgradeModal.svelte b/src/lib/components/UpgradeModal.svelte deleted file mode 100644 index 8a9fdc7..0000000 --- a/src/lib/components/UpgradeModal.svelte +++ /dev/null @@ -1,58 +0,0 @@ - - -{#if upgrades.length > 0} - -

Templates Updated

-
- {#each upgrades as upgrade} -
-

{upgrade.characterName}

-

{upgrade.templateName} template

-
    - {#each upgrade.diff.renamedFields as r} -
  • {r.from} → {r.to}
  • - {/each} - {#each upgrade.diff.addedRecords as r} -
  • + New record: {r}
  • - {/each} - {#each upgrade.diff.removedRecords as r} -
  • - Removed record: {r}
  • - {/each} - {#each upgrade.diff.addedFields as f} -
  • + New field: {f}
  • - {/each} - {#each upgrade.diff.removedFields as f} -
  • - Removed field: {f}
  • - {/each} -
-
- - -
-
- {/each} -
-

Your existing data will be preserved. Skipped updates will be offered again next time.

-
-{/if} diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index 5cd6485..2c8c4d5 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -1,38 +1,14 @@ import { getAllCharacters, saveCharacter, deleteCharacter } from './storage'; import { isBlankCharacter } from './utils/blank'; -import { presets } from './presets'; -import { diffTemplates, hasChanges, type TemplateDiff } from './utils/template-diff'; import { slugify } from './utils/slugify'; import type { Character, Template } from './types'; -export interface PendingUpgrade { - characterId: string; - characterName: string; - templateName: string; - preset: Template; - diff: TemplateDiff; -} - let characters = $state([]); let activeId = $state(null); let saveStatus = $state<'idle' | 'saving' | 'saved'>('idle'); -let pendingUpgrades = $state([]); let saveTimer: ReturnType | null = null; let statusTimer: ReturnType | null = null; -function getSkippedUpgrades(): Record { - try { - return JSON.parse(localStorage.getItem('skippedUpgrades') || '{}'); - } catch { - return {}; - } -} - -function upgradeFingerprint(preset: Template): string { - const fields = preset.records.flatMap((r) => r.fields.map((f) => f.label)).sort(); - return `${preset.id}:${fields.join(',')}`; -} - function migrateData(char: Character, preset: Template) { for (const record of preset.records) { for (const field of record.fields) { @@ -52,84 +28,30 @@ function migrateData(char: Character, preset: Template) { } } -function charDisplayName(char: Character): string { - return (char.data['name'] as string) - || (char.data['designation'] as string) - || 'Unnamed Character'; -} - export const roster = { get characters() { return characters; }, get active() { return characters.find((c) => c.id === activeId) ?? null; }, get saveStatus() { return saveStatus; }, - get pendingUpgrades() { return pendingUpgrades; }, - clearUpgrades() { - pendingUpgrades = []; - }, - - async applyUpgrade(characterId: string) { - const upgrade = pendingUpgrades.find((u) => u.characterId === characterId); - const char = characters.find((c) => c.id === characterId); - if (!upgrade || !char) return; - - migrateData(char, upgrade.preset); - char.template = upgrade.preset; + async migrateToPreset(char: Character, preset: Template) { + migrateData(char, preset); + char.template = $state.snapshot(preset); await saveCharacter($state.snapshot(char)); - const skipped = getSkippedUpgrades(); - delete skipped[characterId]; - localStorage.setItem('skippedUpgrades', JSON.stringify(skipped)); - pendingUpgrades = pendingUpgrades.filter((u) => u.characterId !== characterId); - }, - - skipUpgrade(characterId: string) { - const upgrade = pendingUpgrades.find((u) => u.characterId === characterId); - if (upgrade) { - const skipped = getSkippedUpgrades(); - skipped[characterId] = upgradeFingerprint(upgrade.preset); - localStorage.setItem('skippedUpgrades', JSON.stringify(skipped)); - } - pendingUpgrades = pendingUpgrades.filter((u) => u.characterId !== characterId); }, async load() { const all = await getAllCharacters(); const kept: Character[] = []; - const upgrades: PendingUpgrade[] = []; - const skipped = getSkippedUpgrades(); for (const char of all) { if (isBlankCharacter(char)) { await deleteCharacter(char.id); - continue; + } else { + kept.push(char); } - - if (char.template.id.startsWith('preset:')) { - const preset = presets.find((p) => p.id === char.template.id); - if (preset) { - const diff = diffTemplates(char.template, preset); - if (hasChanges(diff)) { - const fp = upgradeFingerprint(preset); - if (skipped[char.id] === fp) { - kept.push(char); - continue; - } - upgrades.push({ - characterId: char.id, - characterName: charDisplayName(char), - templateName: preset.name, - preset, - diff - }); - } - } - } - - kept.push(char); } characters = kept; - pendingUpgrades = upgrades; const stored = localStorage.getItem('activeCharacterId'); if (stored && characters.some((c) => c.id === stored)) { @@ -155,7 +77,6 @@ export const roster = { async remove(id: string) { characters = characters.filter((c) => c.id !== id); await deleteCharacter(id); - pendingUpgrades = pendingUpgrades.filter((u) => u.characterId !== id); if (activeId === id) { activeId = characters[0]?.id ?? null; if (activeId) localStorage.setItem('activeCharacterId', activeId); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 2388afe..8ae07f9 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -7,7 +7,6 @@ import { roster } from '$lib/state.svelte'; import { presets } from '$lib/presets'; import TemplatePicker from '$lib/components/TemplatePicker.svelte'; - import UpgradeModal from '$lib/components/UpgradeModal.svelte'; let importData = $state(null); let mobileView = $state<'edit' | 'preview' | 'split'>('split'); @@ -104,5 +103,3 @@ {/if} {/if}
- -