Release 0.5.9

This commit is contained in:
hnzio committed 2026-10-23 13:16:32 +02:00
1 parent 2851c7d936
commit c3033479b5
32 files changed
+1598 -50

No files matched your search

+38
View File
@@ -7,6 +7,7 @@ import {
type TaxonomyKind
} from '#lib/server/content.js';
import { getMailConfig } from '#lib/server/config.js';
import { createTool, listTools, updateTool, type ToolInput } from '#lib/server/tools.js';
import {
inviteUser,
isRole,
@@ -25,6 +26,7 @@ export const load: PageServerLoad = ({ locals }) => {
taxonomy: getTaxonomy(),
users: listUsers(),
invitations: listOpenInvitations(),
tools: listTools(true),
mailTransport: getMailConfig().transport
};
};
@@ -40,9 +42,25 @@ 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 === '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.';
if (code === 'INVALID_TOOL_FORMAT')
return 'Mindestens ein gewähltes Format existiert nicht mehr.';
if (code === 'INVALID_TOOL') return 'Bitte die Werkzeugdaten prüfen.';
return 'Die Aktion konnte nicht ausgeführt werden.';
}
function toolInput(data: FormData): ToolInput {
return {
name: String(data.get('name') || ''),
startUrl: String(data.get('startUrl') || ''),
description: String(data.get('description') || ''),
isActive: data.get('isActive') === '1',
formatIds: data.getAll('formatIds').map(String)
};
}
export const actions = {
invite: async ({ locals, request }) => {
const actor = requireBarkeeper(locals.user);
@@ -131,5 +149,25 @@ export const actions = {
} catch (cause) {
return fail(400, { intent: 'taxonomy', error: actionError(cause) });
}
},
createTool: async ({ locals, request }) => {
requireBarkeeper(locals.user);
const data = await request.formData();
try {
createTool(toolInput(data));
return { intent: 'tool', success: 'Werkzeug angelegt.' };
} catch (cause) {
return fail(400, { intent: 'tool', error: actionError(cause) });
}
},
updateTool: async ({ locals, request }) => {
requireBarkeeper(locals.user);
const data = await request.formData();
try {
updateTool(String(data.get('toolId') || ''), toolInput(data));
return { intent: 'tool', success: 'Werkzeug aktualisiert.' };
} catch (cause) {
return fail(400, { intent: 'tool', error: actionError(cause) });
}
}
} satisfies Actions;
+84 -1
View File
@@ -12,7 +12,7 @@
<div class="page-wrap narrow-page">
<header class="page-heading">
<h1>Verwaltung</h1>
<p class="page-intro">Konten, Rollen und bestehende Taxonomie verwalten.</p>
<p class="page-intro">Konten, Werkzeuge und bestehende Taxonomie verwalten.</p>
</header>
{#if form?.error}<div class="form-message error" role="alert">{form.error}</div>{/if}
{#if form?.success}<div class="form-message success" role="status">{form.success}</div>{/if}
@@ -113,6 +113,89 @@
</section>
</div>
<h2 class="section-heading">Werkzeuge</h2>
<section class="quiet-panel tool-management">
<div class="panel-heading">
<div>
<p class="eyebrow">Redaktionelle Hilfsmittel</p>
<h2>Werkzeugkatalog</h2>
</div>
<small>Deaktivierte Werkzeuge bleiben in bestehenden Pieces und Sendungen erhalten.</small>
</div>
<div class="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
>
<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>
{/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>
<h2 class="section-heading">Taxonomie</h2>
<div class="settings-grid">
<section class="quiet-panel taxonomy-panel">