{$t('app.ingestions.last_sync_message')}:
@@ -374,8 +517,6 @@
class="cursor-pointer"
checked={source.status !== 'paused'}
onCheckedChange={() => handleToggle(source)}
- disabled={source.status === 'importing' ||
- source.status === 'syncing'}
/>
+
+
+ {#if hasChildren && isExpanded}
+ {#each children as child (child.id)}
+
+
+
+
+
+
+
+ {child.provider.split('_').join(' ')}
+
+
+
+
+ {child.status.split('_').join(' ')}
+
+
+
+
+
+ {$t(
+ 'app.ingestions.last_sync_message'
+ )}:
+ {child.lastSyncStatusMessage ||
+ $t('app.ingestions.empty')}
+
+
+
+
+
+
+ handleToggle(child)}
+ />
+
+ {new Date(
+ child.createdAt
+ ).toLocaleDateString()}
+
+
+
+ {#snippet child({ props })}
+
+ {/snippet}
+
+
+ {$t(
+ 'app.ingestions.actions'
+ )}
+ openEditDialog(child)}
+ >{$t('app.ingestions.edit')}
+ handleSync(child.id)}
+ >{$t(
+ 'app.ingestions.force_sync'
+ )}
+ openUnmergeDialog(child)}
+ >
+ {$t('app.ingestions.unmerge')}
+
+
+ openDeleteDialog(child)}
+ >{$t(
+ 'app.ingestions.delete'
+ )}
+
+
+
+
+ {/each}
+ {/if}
{/each}
{:else}
@@ -451,7 +706,11 @@
>
-
+
@@ -461,6 +720,13 @@
{$t('app.ingestions.delete_confirmation_title')}
{$t('app.ingestions.delete_confirmation_description')}
+ {#if deleteChildCount > 0}
+
+ {$t('app.ingestions.delete_root_warning', {
+ count: deleteChildCount,
+ } as any)}
+
+ {/if}
@@ -512,3 +778,31 @@
+
+
+
+
+
+ {$t('app.ingestions.unmerge_confirmation_title')}
+
+ {$t('app.ingestions.unmerge_confirmation_description')}
+
+
+
+ - {$t('app.ingestions.unmerge_warning_emails')}
+ - {$t('app.ingestions.unmerge_warning_future')}
+
+
+
+
+
+
+
+
+
diff --git a/packages/types/src/ingestion.types.ts b/packages/types/src/ingestion.types.ts
index e398286..27a195a 100644
--- a/packages/types/src/ingestion.types.ts
+++ b/packages/types/src/ingestion.types.ts
@@ -35,7 +35,8 @@ export type IngestionStatus =
| 'syncing'
| 'importing'
| 'auth_success'
- | 'imported';
+ | 'imported'
+ | 'partially_active'; // For sources with merged children where some are active and others are not
export interface BaseIngestionCredentials {
type: IngestionProvider;
@@ -123,6 +124,9 @@ export interface IngestionSource {
/** When true, the raw EML file is stored without any modification (no attachment
* stripping). Required for GoBD / SEC 17a-4 compliance. Defaults to false. */
preserveOriginalFile: boolean;
+ /** The ID of the root ingestion source this child is merged into.
+ * Null or undefined when this source is a standalone root. */
+ mergedIntoId?: string | null;
}
/**
@@ -138,6 +142,8 @@ export interface CreateIngestionSourceDto {
providerConfig: Record;
/** Store the unmodified raw EML for GoBD compliance. Defaults to false. */
preserveOriginalFile?: boolean;
+ /** Merge this new source into an existing root source's group. */
+ mergedIntoId?: string;
}
export interface UpdateIngestionSourceDto {
@@ -149,6 +155,8 @@ export interface UpdateIngestionSourceDto {
lastSyncFinishedAt?: Date;
lastSyncStatusMessage?: string;
syncState?: SyncState;
+ /** Set or clear the merge parent. Use null to unmerge. */
+ mergedIntoId?: string | null;
}
export interface IContinuousSyncJob {