Release 0.5.10

This commit is contained in:
hnzio committed 2026-10-23 21:33:15 +02:00
1 parent c3033479b5
commit 4fe61971e4
52 files changed
+38565 -466

No files matched your search

+28 -1
View File
@@ -4,6 +4,7 @@ import {
createTaxonomy,
deleteTaxonomy,
getTaxonomy,
updateTaxonomy,
type TaxonomyKind
} from '#lib/server/content.js';
import { getMailConfig } from '#lib/server/config.js';
@@ -42,6 +43,7 @@ function actionError(cause: unknown): string {
return 'Verwendete oder für Vorlagen benötigte Einträge können nicht entfernt werden.';
if (code === 'INVALID_COLOR') return 'Bitte eine gültige Farbe wählen.';
if (code === 'INVALID_TAXONOMY') return 'Bitte einen gültigen Namen eingeben.';
if (code === 'INVALID_ICON') return 'Bitte ein Icon aus der angebotenen Auswahl verwenden.';
if (code === 'TOOL_EXISTS') return 'Ein Werkzeug mit diesem Namen existiert bereits.';
if (code === 'INVALID_TOOL_URL')
return 'Die Start-URL muss eine vollständige HTTP- oder HTTPS-Adresse sein.';
@@ -56,6 +58,7 @@ function toolInput(data: FormData): ToolInput {
name: String(data.get('name') || ''),
startUrl: String(data.get('startUrl') || ''),
description: String(data.get('description') || ''),
iconName: String(data.get('iconName') || ''),
isActive: data.get('isActive') === '1',
formatIds: data.getAll('formatIds').map(String)
};
@@ -130,12 +133,36 @@ export const actions = {
return fail(400, { intent: 'taxonomy', error: 'Ungültige Taxonomieart.' });
}
try {
createTaxonomy(kind, String(data.get('name') || ''), String(data.get('color') || ''));
createTaxonomy(
kind,
String(data.get('name') || ''),
String(data.get('color') || ''),
String(data.get('iconName') || '')
);
return { intent: 'taxonomy', success: 'Eintrag angelegt.' };
} catch (cause) {
return fail(400, { intent: 'taxonomy', error: actionError(cause) });
}
},
updateTaxonomy: async ({ locals, request }) => {
requireBarkeeper(locals.user);
const data = await request.formData();
const kind = String(data.get('kind') || '') as TaxonomyKind;
if (!['category', 'format', 'topic'].includes(kind)) {
return fail(400, { intent: 'taxonomy', error: 'Ungültige Taxonomieart.' });
}
try {
updateTaxonomy(
kind,
String(data.get('taxonomyId') || ''),
String(data.get('name') || ''),
String(data.get('iconName') || '')
);
return { intent: 'taxonomy', success: 'Eintrag aktualisiert.' };
} catch (cause) {
return fail(400, { intent: 'taxonomy', error: actionError(cause) });
}
},
deleteTaxonomy: async ({ locals, request }) => {
requireBarkeeper(locals.user);
const data = await request.formData();
+59 -83
View File
@@ -1,6 +1,10 @@
<script lang="ts">
import AppIcon from '#lib/components/AppIcon.svelte';
import IconPicker from '#lib/components/IconPicker.svelte';
import ToolEditorDialog from '#lib/components/ToolEditorDialog.svelte';
import type { PageProps } from './$types';
let { data, form }: PageProps = $props();
let toolDialog: { showModal: (tool?: (typeof data.tools)[number] | null) => void };
function confirmTaxonomyRemoval(event: SubmitEvent) {
if (!confirm('Eintrag wirklich entfernen?')) event.preventDefault();
@@ -120,81 +124,38 @@
<p class="eyebrow">Redaktionelle Hilfsmittel</p>
<h2>Werkzeugkatalog</h2>
</div>
<small>Deaktivierte Werkzeuge bleiben in bestehenden Pieces und Sendungen erhalten.</small>
<button class="compact-add-button" type="button" onclick={() => toolDialog.showModal()}>
<AppIcon name="plus" /> Werkzeug hinzufügen
</button>
</div>
<div class="tool-settings-list">
<p class="catalog-note">
Deaktivierte Werkzeuge bleiben in bestehenden Pieces und Sendungen erhalten.
</p>
<div class="catalog-settings-list tool-settings-list">
{#each data.tools as tool (tool.id)}
<details class:inactive={!tool.isActive}>
<summary
><strong>{tool.name}</strong><span>{tool.isActive ? 'Aktiv' : 'Deaktiviert'}</span
></summary
>
<form class="compact-form tool-form" method="POST" action="?/updateTool">
<input type="hidden" name="toolId" value={tool.id} />
<label>Name<input name="name" required maxlength="120" value={tool.name} /></label>
<label
>Start-URL<input
name="startUrl"
type="url"
maxlength="2000"
value={tool.startUrl}
placeholder="https://…"
/></label
<button
type="button"
class:inactive={!tool.isActive}
aria-label={`${tool.name} bearbeiten`}
onclick={() => toolDialog.showModal(tool)}
>
<span class="catalog-entry-icon"><AppIcon name={tool.iconName || 'wrench'} /></span>
<span class="catalog-entry-copy">
<strong>{tool.name}</strong>
<small
>{tool.isActive ? 'Aktiv' : 'Deaktiviert'}{#if tool.formatIds.length}
· {data.taxonomy.formats
.filter((format) => tool.formatIds.includes(format.id))
.map((format) => format.name)
.join(', ')}{/if}</small
>
<label class="wide-field"
>Beschreibung<textarea name="description" rows="2" maxlength="2000"
>{tool.description}</textarea
></label
>
<fieldset class="wide-field">
<legend>Passende Formate</legend>
<div class="choice-grid">
{#each data.taxonomy.formats as format (format.id)}<label
><input
type="checkbox"
name="formatIds"
value={format.id}
checked={tool.formatIds.includes(format.id)}
/>{format.name}</label
>{/each}
</div>
</fieldset>
<label class="tool-active"
><input type="checkbox" name="isActive" value="1" checked={tool.isActive} /> Aktiv</label
>
<button class="secondary-button" type="submit">Speichern</button>
</form>
</details>
</span>
<AppIcon name="pen" />
</button>
{/each}
</div>
<details class="new-tool">
<summary>Werkzeug hinzufügen</summary>
<form class="compact-form tool-form" method="POST" action="?/createTool">
<label>Name<input name="name" required maxlength="120" /></label>
<label
>Start-URL<input
name="startUrl"
type="url"
maxlength="2000"
placeholder="https://…"
/></label
>
<label class="wide-field"
>Beschreibung<textarea name="description" rows="2" maxlength="2000"></textarea></label
>
<fieldset class="wide-field">
<legend>Passende Formate</legend>
<div class="choice-grid">
{#each data.taxonomy.formats as format (format.id)}<label
><input type="checkbox" name="formatIds" value={format.id} />{format.name}</label
>{/each}
</div>
</fieldset>
<input type="hidden" name="isActive" value="1" />
<button class="primary-button" type="submit">Werkzeug anlegen</button>
</form>
</details>
</section>
<ToolEditorDialog bind:this={toolDialog} formats={data.taxonomy.formats} />
<h2 class="section-heading">Taxonomie</h2>
<div class="settings-grid">
@@ -209,7 +170,9 @@
type="hidden"
name="taxonomyId"
value={category.id}
/><button type="submit" aria-label={`${category.name} entfernen`}>×</button>
/><button type="submit" aria-label={`${category.name} entfernen`}
><AppIcon name="trash" /></button
>
</form></span
>{/each}
</div>
@@ -241,7 +204,9 @@
type="hidden"
name="taxonomyId"
value={format.id}
/><button type="submit" aria-label={`${format.name} entfernen`}>×</button>
/><button type="submit" aria-label={`${format.name} entfernen`}
><AppIcon name="trash" /></button
>
</form></span
>{/each}
</div>
@@ -256,20 +221,31 @@
><button class="text-button" type="submit">Hinzufügen</button>
</form>
</section>
<section class="quiet-panel taxonomy-panel">
<section class="quiet-panel taxonomy-panel topic-management-panel">
<p class="eyebrow">Themen</p>
<h2>Inhaltliche Zuordnung</h2>
<div class="taxonomy-list editable-taxonomy">
{#each data.taxonomy.topics as topic (topic.id)}<span
>{topic.name}
<div class="topic-settings-list">
{#each data.taxonomy.topics as topic (topic.id)}
<details>
<summary
><AppIcon name={topic.iconName || 'circle'} /><strong>{topic.name}</strong><AppIcon
name="pen"
/></summary
>
<form class="topic-edit-form" method="POST" action="?/updateTaxonomy">
<input type="hidden" name="kind" value="topic" />
<input type="hidden" name="taxonomyId" value={topic.id} />
<label>Name<input name="name" required maxlength="80" value={topic.name} /></label>
<IconPicker name={topic.iconName} />
<button class="secondary-button" type="submit">Änderungen speichern</button>
</form>
<form method="POST" action="?/deleteTaxonomy" onsubmit={confirmTaxonomyRemoval}>
<input type="hidden" name="kind" value="topic" /><input
type="hidden"
name="taxonomyId"
value={topic.id}
/><button type="submit" aria-label={`${topic.name} entfernen`}>×</button>
</form></span
>{/each}
<input type="hidden" name="kind" value="topic" />
<input type="hidden" name="taxonomyId" value={topic.id} />
<button class="text-button danger-text" type="submit">Thema löschen</button>
</form>
</details>
{/each}
</div>
<form class="taxonomy-form" method="POST" action="?/createTaxonomy">
<input type="hidden" name="kind" value="topic" /><label
@@ -279,7 +255,7 @@
maxlength="80"
placeholder="Neues Thema"
/></label
><button class="text-button" type="submit">Hinzufügen</button>
><IconPicker name="" /><button class="text-button" type="submit">Hinzufügen</button>
</form>
</section>
</div>