diff --git a/apps/mana/apps/web/src/lib/modules/articles/components/AddUrlForm.svelte b/apps/mana/apps/web/src/lib/modules/articles/components/AddUrlForm.svelte
index 959454e0f..d46cce29b 100644
--- a/apps/mana/apps/web/src/lib/modules/articles/components/AddUrlForm.svelte
+++ b/apps/mana/apps/web/src/lib/modules/articles/components/AddUrlForm.svelte
@@ -257,6 +257,10 @@
+
@@ -354,7 +358,19 @@
.input-row {
display: flex;
gap: 0.55rem;
- margin-bottom: 0.9rem;
+ margin-bottom: 0.45rem;
+ }
+ .bulk-link {
+ margin: 0 0 0.9rem 0;
+ font-size: 0.85rem;
+ color: var(--color-text-muted, #64748b);
+ }
+ .bulk-link a {
+ color: #ea580c;
+ text-decoration: none;
+ }
+ .bulk-link a:hover {
+ text-decoration: underline;
}
.url-input {
flex: 1;
diff --git a/apps/mana/apps/web/src/lib/modules/articles/components/BulkImportForm.svelte b/apps/mana/apps/web/src/lib/modules/articles/components/BulkImportForm.svelte
new file mode 100644
index 000000000..ea7c72065
--- /dev/null
+++ b/apps/mana/apps/web/src/lib/modules/articles/components/BulkImportForm.svelte
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+ {parsed.valid.length} gültig
+ {#if parsed.duplicates.length > 0}
+ {parsed.duplicates.length} doppelt (übersprungen)
+ {/if}
+ {#if parsed.invalid.length > 0}
+ {parsed.invalid.length} ungültig
+ {/if}
+
+
+ {#if parsed.invalid.length > 0}
+
+ Ungültige Zeilen anzeigen ({parsed.invalid.length})
+
+ {#each parsed.invalid as bad (bad)}
+ {bad}
+ {/each}
+
+
+ {/if}
+
+ {#if error}
+
{error}
+ {/if}
+
+
+
+ {#if busy}Erstelle Job…{:else}{parsed.valid.length} URLs importieren{/if}
+
+
+
+
+ Im Hintergrund — du kannst den Tab schließen und später zurückkommen. Bei 50 URLs dauert es grob
+ 5–10 Minuten. Den Fortschritt siehst du auf der Detailseite.
+
+
+
+
diff --git a/apps/mana/apps/web/src/lib/modules/articles/components/JobDetailView.svelte b/apps/mana/apps/web/src/lib/modules/articles/components/JobDetailView.svelte
new file mode 100644
index 000000000..2123e091d
--- /dev/null
+++ b/apps/mana/apps/web/src/lib/modules/articles/components/JobDetailView.svelte
@@ -0,0 +1,376 @@
+
+
+
+
+ {#if !job}
+
Job nicht gefunden.
+ {:else}
+ {@const j = job}
+
+
+
+ {#each items as item (item.id)}
+ {@const pill = statePill(item.state)}
+
+ {pill.label}
+ {shortUrl(item)}
+ {#if item.articleId && (item.state === 'saved' || item.state === 'consent-wall' || item.state === 'duplicate')}
+ Öffnen
+ {:else if item.state === 'error' && item.error}
+ {item.error}
+ {/if}
+
+ {/each}
+
+ {/if}
+
+
+
diff --git a/apps/mana/apps/web/src/lib/modules/articles/components/JobsList.svelte b/apps/mana/apps/web/src/lib/modules/articles/components/JobsList.svelte
new file mode 100644
index 000000000..8d456bcc6
--- /dev/null
+++ b/apps/mana/apps/web/src/lib/modules/articles/components/JobsList.svelte
@@ -0,0 +1,143 @@
+
+
+
+{#if jobs.length > 0}
+
+ Bisherige Imports
+
+ {#each jobs as job (job.id)}
+ goto(`/articles/import/${job.id}`)}>
+ {statusLabel(job.status)}
+ {progress(job)}
+
+ {#if job.errorCount > 0}{job.errorCount} Fehler {/if}
+ {#if job.duplicateCount > 0}
+ {job.duplicateCount} Duplikate
+ {/if}
+ {#if job.warningCount > 0}
+ {job.warningCount} Warnungen
+ {/if}
+
+ {new Date(job.createdAt).toLocaleString('de-DE')}
+
+ {/each}
+
+
+{/if}
+
+
diff --git a/apps/mana/apps/web/src/routes/(app)/articles/import/+page.svelte b/apps/mana/apps/web/src/routes/(app)/articles/import/+page.svelte
new file mode 100644
index 000000000..1130390b5
--- /dev/null
+++ b/apps/mana/apps/web/src/routes/(app)/articles/import/+page.svelte
@@ -0,0 +1,14 @@
+
+
+
+ Artikel-Import — Mana
+
+
+
+
+
+
diff --git a/apps/mana/apps/web/src/routes/(app)/articles/import/[jobId]/+page.svelte b/apps/mana/apps/web/src/routes/(app)/articles/import/[jobId]/+page.svelte
new file mode 100644
index 000000000..9dbbad9cf
--- /dev/null
+++ b/apps/mana/apps/web/src/routes/(app)/articles/import/[jobId]/+page.svelte
@@ -0,0 +1,17 @@
+
+
+
+ Artikel-Import — Mana
+
+
+
+ {#if jobId}
+
+ {/if}
+