adds features
Build and Push Docker Image / build (push) Successful in 1m28s

This commit is contained in:
2026-08-27 07:38:35 +02:00
parent 891026ccbb
commit adaae41e44
33 changed files with 2771 additions and 396 deletions
@@ -4,6 +4,7 @@
import SubDocumentEditor from '$lib/components/SubDocumentEditor.svelte';
import {
buildArchiveExport,
buildSubDocumentExport,
createSubDocument,
dissolveSubDocument,
moveAttachmentIntoSubDocument,
@@ -41,8 +42,11 @@
let downloadMode = $state<ExportMode>('single');
let isMerging = $state(false);
let downloadingSubDocumentId = $state<string | null>(null);
let mergeError = $state<string | null>(null);
const isExportActive = $derived(isMerging || downloadingSubDocumentId !== null);
$effect(() => {
archiveName = archive.name;
looseAttachments = archive.attachments.map((attachment) => ({ ...attachment }));
@@ -293,7 +297,7 @@
return;
}
if (isMerging) {
if (isExportActive) {
return;
}
@@ -313,6 +317,29 @@
isMerging = false;
}
};
const downloadSubDocument = async (subDocument: SubDocument) => {
if (isExportActive || subDocument.attachments.length === 0) {
return;
}
downloadingSubDocumentId = subDocument.id;
mergeError = null;
try {
const exportUnit = await buildSubDocumentExport(archiveName, subDocument);
downloadPdfBytes(exportUnit.bytes, exportUnit.name);
} catch (error) {
console.error(
`Failed to export sub-document "${subDocument.name}" from archive ${archive.name}`,
error
);
mergeError = `Teildokument „${subDocument.name}“ konnte nicht als PDF erstellt werden.`;
} finally {
downloadingSubDocumentId = null;
}
};
</script>
<svelte:window onkeydown={handleWindowKeydown} />
@@ -354,17 +381,21 @@
{#if hasSubDocuments}
<div
class="inline-flex items-center rounded-[16px] border border-primary-200 bg-white p-0.5"
class={cn(
'inline-flex items-center rounded-[16px] border border-primary-200 bg-white p-0.5',
downloadingSubDocumentId !== null ? 'opacity-60' : ''
)}
role="radiogroup"
aria-label="Export-Modus"
>
{#each [{ value: 'single', label: 'Eine PDF' }, { value: 'separate', label: 'Getrennt' }] as option (option.value)}
<label
class={cn(
'cursor-pointer rounded-[14px] px-3 py-1 text-[13px] font-semibold transition',
'rounded-[14px] px-3 py-1 text-[13px] font-semibold transition',
downloadMode === option.value
? 'bg-primary text-primary-foreground'
: 'text-primary-700 hover:bg-primary-50'
: 'text-primary-700 hover:bg-primary-50',
downloadingSubDocumentId !== null ? 'cursor-not-allowed' : 'cursor-pointer'
)}
>
<input
@@ -373,6 +404,7 @@
name={`download-mode-${archive.name}`}
value={option.value}
checked={downloadMode === option.value}
disabled={downloadingSubDocumentId !== null}
onchange={() => {
downloadMode = option.value as ExportMode;
}}
@@ -386,7 +418,7 @@
<Button
class="bg-primary"
type="button"
disabled={(looseAttachments.length === 0 && subDocuments.length === 0) || isMerging}
disabled={(looseAttachments.length === 0 && subDocuments.length === 0) || isExportActive}
onclick={downloadArchive}
>
{isMerging ? 'PDF wird erstellt' : 'PDF herunterladen'}
@@ -495,6 +527,9 @@
onBlockDragOver={handleBlockDragOver}
onBlockDrop={handleBlockDrop}
onBlockDragEnd={handleBlockDragEnd}
onDownload={() => downloadSubDocument(subDocument)}
isDownloading={downloadingSubDocumentId === subDocument.id}
downloadDisabled={isExportActive}
/>
{/each}
</div>