@forgeroute/ui 0.4.41 → 0.4.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9381,6 +9381,9 @@ var ImportExportComponent = class ImportExportComponent {
9381
9381
  this.importSources = input(void 0, ...ngDevMode ? [{ debugName: "importSources" }] : /* istanbul ignore next */ []);
9382
9382
  this.exportParams = input({}, ...ngDevMode ? [{ debugName: "exportParams" }] : /* istanbul ignore next */ []);
9383
9383
  this.exportOnly = input(false, ...ngDevMode ? [{ debugName: "exportOnly" }] : /* istanbul ignore next */ []);
9384
+ this.closeOnly = input(false, ...ngDevMode ? [{ debugName: "closeOnly" }] : /* istanbul ignore next */ []);
9385
+ this.submit = input(null, ...ngDevMode ? [{ debugName: "submit" }] : /* istanbul ignore next */ []);
9386
+ this.finished = output();
9384
9387
  this.imported = output();
9385
9388
  this.http = inject(HttpClient);
9386
9389
  this.apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? "/api";
@@ -9400,6 +9403,7 @@ var ImportExportComponent = class ImportExportComponent {
9400
9403
  this.mappingError = null;
9401
9404
  this.importing = false;
9402
9405
  this.importResult = null;
9406
+ this.csvFile = null;
9403
9407
  this.destroyRef.onDestroy(() => this.stopImportTimer());
9404
9408
  effect(() => {
9405
9409
  const v = this._configInput();
@@ -9466,6 +9470,7 @@ var ImportExportComponent = class ImportExportComponent {
9466
9470
  this.selectedSource = "";
9467
9471
  this.csvHeaders = [];
9468
9472
  this.csvRows = [];
9473
+ this.csvFile = null;
9469
9474
  this.fileError = null;
9470
9475
  this.columnMapping = {};
9471
9476
  this.mappingError = null;
@@ -9492,6 +9497,7 @@ var ImportExportComponent = class ImportExportComponent {
9492
9497
  }
9493
9498
  finishImport() {
9494
9499
  this.importOpen = false;
9500
+ if (this.importResult) this.finished.emit(this.importResult);
9495
9501
  this.imported.emit();
9496
9502
  }
9497
9503
  onFileChange(event) {
@@ -9500,6 +9506,7 @@ var ImportExportComponent = class ImportExportComponent {
9500
9506
  this.readCsvFile(file);
9501
9507
  }
9502
9508
  readCsvFile(file, encoding) {
9509
+ this.csvFile = file;
9503
9510
  const reader = new FileReader();
9504
9511
  reader.onload = (e) => {
9505
9512
  const raw = e.target.result;
@@ -9641,7 +9648,12 @@ var ImportExportComponent = class ImportExportComponent {
9641
9648
  }
9642
9649
  return record;
9643
9650
  });
9644
- this.http.post(`${this.apiBase}${this.config.importEndpoint ?? ""}`, { records }).subscribe({
9651
+ const custom = this.submit();
9652
+ (custom ? custom({
9653
+ file: this.csvFile,
9654
+ records,
9655
+ mapping: { ...this.columnMapping }
9656
+ }) : this.http.post(`${this.apiBase}${this.config.importEndpoint ?? ""}`, { records })).subscribe({
9645
9657
  next: (result) => {
9646
9658
  this.stopImportTimer();
9647
9659
  this.importing = false;
@@ -9654,10 +9666,21 @@ var ImportExportComponent = class ImportExportComponent {
9654
9666
  if (result.imported > 0) this.toast.show(`${result.imported} ${this.config.entityNamePlural.toLowerCase()} imported successfully`);
9655
9667
  this.cdr.detectChanges();
9656
9668
  },
9657
- error: () => {
9669
+ error: (err) => {
9658
9670
  this.stopImportTimer();
9659
9671
  this.importing = false;
9660
- this.toast.error("Import failed. Please check your data and try again.");
9672
+ if (custom && Array.isArray(err?.error?.errors)) {
9673
+ this.importResult = {
9674
+ imported: err.error.successCount ?? 0,
9675
+ errors: err.error.errors,
9676
+ warnings: []
9677
+ };
9678
+ this.step = "result";
9679
+ this.cdr.detectChanges();
9680
+ return;
9681
+ }
9682
+ const serverMessage = custom && typeof err?.error?.message === "string" ? err.error.message : "";
9683
+ this.toast.error(serverMessage || "Import failed. Please check your data and try again.");
9661
9684
  this.cdr.detectChanges();
9662
9685
  }
9663
9686
  });
@@ -9707,9 +9730,26 @@ var ImportExportComponent = class ImportExportComponent {
9707
9730
  isSignal: true,
9708
9731
  isRequired: false,
9709
9732
  transformFunction: null
9733
+ },
9734
+ closeOnly: {
9735
+ classPropertyName: "closeOnly",
9736
+ publicName: "closeOnly",
9737
+ isSignal: true,
9738
+ isRequired: false,
9739
+ transformFunction: null
9740
+ },
9741
+ submit: {
9742
+ classPropertyName: "submit",
9743
+ publicName: "submit",
9744
+ isSignal: true,
9745
+ isRequired: false,
9746
+ transformFunction: null
9710
9747
  }
9711
9748
  },
9712
- outputs: { imported: "imported" },
9749
+ outputs: {
9750
+ finished: "finished",
9751
+ imported: "imported"
9752
+ },
9713
9753
  ngImport: i0,
9714
9754
  template: `
9715
9755
  <!-- Trigger buttons -->
@@ -9768,7 +9808,7 @@ var ImportExportComponent = class ImportExportComponent {
9768
9808
  <div class="ie-template-hint">
9769
9809
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
9770
9810
  Start with our CSV template to ensure correct column names.
9771
- <ui-button variant="ghost" size="sm" (click)="downloadTemplate()">Download template</ui-button>
9811
+ <ui-button variant="ghost" size="sm" data-testid="import-template-button" (click)="downloadTemplate()">{{ config.templateLabel || 'Download template' }}</ui-button>
9772
9812
  </div>
9773
9813
  <label class="ie-dropzone" [class.has-error]="!!fileError">
9774
9814
  <input type="file" class="ie-file-hidden" accept=".csv,text/csv" aria-label="Choose CSV file to import" (change)="onFileChange($event)" />
@@ -9900,38 +9940,51 @@ var ImportExportComponent = class ImportExportComponent {
9900
9940
  </div>
9901
9941
  }
9902
9942
 
9903
- <!-- Footer -->
9904
- @if (step === 'source') {
9943
+ <!-- Footer. Every item is its own top-level @if with ONE root element: that is what lets ui-modal project it into its footer slot (a wrapper block with several items would land in the body). -->
9944
+ <!-- closeOnly (the setup wizard): no Back, no Cancel - one Close that shuts the window at once, plus the step's primary button -->
9945
+ @if (closeOnly() && step !== 'result') {
9946
+ <ui-button modal-footer-left variant="secondary" size="sm" data-testid="import-close-button" [disabled]="importing" (click)="closeImport()">Close</ui-button>
9947
+ }
9948
+ @if (closeOnly() && step === 'source') {
9949
+ <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!selectedSource" (click)="confirmSource()">Next</ui-button>
9950
+ }
9951
+ @if (closeOnly() && step === 'mapping') {
9952
+ <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!canSubmit" (click)="goToPreview()">Preview Import</ui-button>
9953
+ }
9954
+ @if (closeOnly() && step === 'preview') {
9955
+ <ui-button modal-footer-right variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
9956
+ }
9957
+ @if (!closeOnly() && step === 'source') {
9905
9958
  <ui-button modal-footer-left variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
9906
9959
  }
9907
- @if (step === 'source') {
9960
+ @if (!closeOnly() && step === 'source') {
9908
9961
  <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!selectedSource" (click)="confirmSource()">Next</ui-button>
9909
9962
  }
9910
- @if (step === 'upload') {
9963
+ @if (!closeOnly() && step === 'upload') {
9911
9964
  <span modal-footer-left></span>
9912
9965
  }
9913
- @if (step === 'upload' && importSources()?.length) {
9966
+ @if (!closeOnly() && step === 'upload' && importSources()?.length) {
9914
9967
  <div modal-footer-right class="ie-footer-right">
9915
9968
  <ui-button variant="ghost" size="sm" (click)="step = 'source'">Back</ui-button>
9916
9969
  <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
9917
9970
  </div>
9918
9971
  }
9919
- @if (step === 'upload' && !importSources()?.length) {
9972
+ @if (!closeOnly() && step === 'upload' && !importSources()?.length) {
9920
9973
  <ui-button modal-footer-right variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
9921
9974
  }
9922
- @if (step === 'mapping') {
9975
+ @if (!closeOnly() && step === 'mapping') {
9923
9976
  <ui-button modal-footer-left variant="ghost" size="sm" (click)="step = 'upload'">Back</ui-button>
9924
9977
  }
9925
- @if (step === 'mapping') {
9978
+ @if (!closeOnly() && step === 'mapping') {
9926
9979
  <div modal-footer-right class="ie-footer-right">
9927
9980
  <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
9928
9981
  <ui-button variant="primary" size="sm" [disabled]="!canSubmit" (click)="goToPreview()">Preview Import</ui-button>
9929
9982
  </div>
9930
9983
  }
9931
- @if (step === 'preview') {
9984
+ @if (!closeOnly() && step === 'preview') {
9932
9985
  <ui-button modal-footer-left variant="ghost" size="sm" (click)="step = 'mapping'">Back</ui-button>
9933
9986
  }
9934
- @if (step === 'preview') {
9987
+ @if (!closeOnly() && step === 'preview') {
9935
9988
  <div modal-footer-right class="ie-footer-right">
9936
9989
  <ui-button variant="secondary" size="sm" [disabled]="importing" (click)="closeImport()">Cancel</ui-button>
9937
9990
  <ui-button variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
@@ -10131,7 +10184,7 @@ i0.ɵɵngDeclareClassMetadata({
10131
10184
  <div class="ie-template-hint">
10132
10185
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
10133
10186
  Start with our CSV template to ensure correct column names.
10134
- <ui-button variant="ghost" size="sm" (click)="downloadTemplate()">Download template</ui-button>
10187
+ <ui-button variant="ghost" size="sm" data-testid="import-template-button" (click)="downloadTemplate()">{{ config.templateLabel || 'Download template' }}</ui-button>
10135
10188
  </div>
10136
10189
  <label class="ie-dropzone" [class.has-error]="!!fileError">
10137
10190
  <input type="file" class="ie-file-hidden" accept=".csv,text/csv" aria-label="Choose CSV file to import" (change)="onFileChange($event)" />
@@ -10263,38 +10316,51 @@ i0.ɵɵngDeclareClassMetadata({
10263
10316
  </div>
10264
10317
  }
10265
10318
 
10266
- <!-- Footer -->
10267
- @if (step === 'source') {
10319
+ <!-- Footer. Every item is its own top-level @if with ONE root element: that is what lets ui-modal project it into its footer slot (a wrapper block with several items would land in the body). -->
10320
+ <!-- closeOnly (the setup wizard): no Back, no Cancel - one Close that shuts the window at once, plus the step's primary button -->
10321
+ @if (closeOnly() && step !== 'result') {
10322
+ <ui-button modal-footer-left variant="secondary" size="sm" data-testid="import-close-button" [disabled]="importing" (click)="closeImport()">Close</ui-button>
10323
+ }
10324
+ @if (closeOnly() && step === 'source') {
10325
+ <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!selectedSource" (click)="confirmSource()">Next</ui-button>
10326
+ }
10327
+ @if (closeOnly() && step === 'mapping') {
10328
+ <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!canSubmit" (click)="goToPreview()">Preview Import</ui-button>
10329
+ }
10330
+ @if (closeOnly() && step === 'preview') {
10331
+ <ui-button modal-footer-right variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
10332
+ }
10333
+ @if (!closeOnly() && step === 'source') {
10268
10334
  <ui-button modal-footer-left variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
10269
10335
  }
10270
- @if (step === 'source') {
10336
+ @if (!closeOnly() && step === 'source') {
10271
10337
  <ui-button modal-footer-right variant="primary" size="sm" [disabled]="!selectedSource" (click)="confirmSource()">Next</ui-button>
10272
10338
  }
10273
- @if (step === 'upload') {
10339
+ @if (!closeOnly() && step === 'upload') {
10274
10340
  <span modal-footer-left></span>
10275
10341
  }
10276
- @if (step === 'upload' && importSources()?.length) {
10342
+ @if (!closeOnly() && step === 'upload' && importSources()?.length) {
10277
10343
  <div modal-footer-right class="ie-footer-right">
10278
10344
  <ui-button variant="ghost" size="sm" (click)="step = 'source'">Back</ui-button>
10279
10345
  <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
10280
10346
  </div>
10281
10347
  }
10282
- @if (step === 'upload' && !importSources()?.length) {
10348
+ @if (!closeOnly() && step === 'upload' && !importSources()?.length) {
10283
10349
  <ui-button modal-footer-right variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
10284
10350
  }
10285
- @if (step === 'mapping') {
10351
+ @if (!closeOnly() && step === 'mapping') {
10286
10352
  <ui-button modal-footer-left variant="ghost" size="sm" (click)="step = 'upload'">Back</ui-button>
10287
10353
  }
10288
- @if (step === 'mapping') {
10354
+ @if (!closeOnly() && step === 'mapping') {
10289
10355
  <div modal-footer-right class="ie-footer-right">
10290
10356
  <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
10291
10357
  <ui-button variant="primary" size="sm" [disabled]="!canSubmit" (click)="goToPreview()">Preview Import</ui-button>
10292
10358
  </div>
10293
10359
  }
10294
- @if (step === 'preview') {
10360
+ @if (!closeOnly() && step === 'preview') {
10295
10361
  <ui-button modal-footer-left variant="ghost" size="sm" (click)="step = 'mapping'">Back</ui-button>
10296
10362
  }
10297
- @if (step === 'preview') {
10363
+ @if (!closeOnly() && step === 'preview') {
10298
10364
  <div modal-footer-right class="ie-footer-right">
10299
10365
  <ui-button variant="secondary" size="sm" [disabled]="importing" (click)="closeImport()">Cancel</ui-button>
10300
10366
  <ui-button variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
@@ -10346,6 +10412,26 @@ i0.ɵɵngDeclareClassMetadata({
10346
10412
  required: false
10347
10413
  }]
10348
10414
  }],
10415
+ closeOnly: [{
10416
+ type: i0.Input,
10417
+ args: [{
10418
+ isSignal: true,
10419
+ alias: "closeOnly",
10420
+ required: false
10421
+ }]
10422
+ }],
10423
+ submit: [{
10424
+ type: i0.Input,
10425
+ args: [{
10426
+ isSignal: true,
10427
+ alias: "submit",
10428
+ required: false
10429
+ }]
10430
+ }],
10431
+ finished: [{
10432
+ type: i0.Output,
10433
+ args: ["finished"]
10434
+ }],
10349
10435
  imported: [{
10350
10436
  type: i0.Output,
10351
10437
  args: ["imported"]
@@ -13650,9 +13736,11 @@ var DataTableComponent = class DataTableComponent {
13650
13736
  "config",
13651
13737
  "importSources",
13652
13738
  "exportParams",
13653
- "exportOnly"
13739
+ "exportOnly",
13740
+ "closeOnly",
13741
+ "submit"
13654
13742
  ],
13655
- outputs: ["imported"]
13743
+ outputs: ["finished", "imported"]
13656
13744
  },
13657
13745
  {
13658
13746
  kind: "component",
@@ -43299,8 +43387,8 @@ const DEFAULT_TEXTS = {
43299
43387
  selectSource: "Select",
43300
43388
  connected: "Connected",
43301
43389
  importMore: "Import more",
43302
- downloadSample: "Download CSV Template",
43303
43390
  importRequired: "Import your data to continue, or skip this step.",
43391
+ connectRequired: "Connect at least one to continue, or skip this step.",
43304
43392
  importErrorsTitle: (errors) => `${errors} row${errors === 1 ? "" : "s"} could not be imported`,
43305
43393
  importErrorsText: (_imported, errors) => `Nothing was imported yet. Fix the ${errors === 1 ? "row" : "rows"} listed in the import window in your file, then import the whole file again to continue.`,
43306
43394
  importAgain: "Import again",
@@ -43333,6 +43421,10 @@ var SetupWizardComponent = class SetupWizardComponent {
43333
43421
  this.apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? "/api";
43334
43422
  this.destroyRef = inject(DestroyRef);
43335
43423
  this.narrow = signal(false, ...ngDevMode ? [{ debugName: "narrow" }] : /* istanbul ignore next */ []);
43424
+ this.connectBlocked = computed(() => {
43425
+ const st = this.current();
43426
+ return st.kind === "integrations" && !!st.requireConnection && !(st.integrations ?? []).some((i) => this.isConnected(i));
43427
+ }, ...ngDevMode ? [{ debugName: "connectBlocked" }] : /* istanbul ignore next */ []);
43336
43428
  this.importBlocked = computed(() => {
43337
43429
  const st = this.current();
43338
43430
  return st.kind === "import" && !!st.requireImport && !this.completed().has(st.key);
@@ -43495,6 +43587,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43495
43587
  if (this.busy()) return;
43496
43588
  const step = this.current();
43497
43589
  if (step.canContinue && !step.canContinue()) return;
43590
+ if (this.connectBlocked()) return;
43498
43591
  this.busy.set(true);
43499
43592
  this.error.set("");
43500
43593
  try {
@@ -43523,9 +43616,6 @@ var SetupWizardComponent = class SetupWizardComponent {
43523
43616
  importer.onSourceSelected(source);
43524
43617
  importer.confirmSource();
43525
43618
  }
43526
- downloadSample() {
43527
- this.importers()[0]?.downloadTemplate();
43528
- }
43529
43619
  openImport(key) {
43530
43620
  if (this.steps().findIndex((s) => s.key === key) < 0) return;
43531
43621
  this.importers()[0]?.openImport();
@@ -43818,7 +43908,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43818
43908
  </ui-button>
43819
43909
  }
43820
43910
  } @else if (current().kind === 'integrations') {
43821
- <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="current().canContinue ? !current().canContinue!() : false" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43911
+ <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="(current().canContinue ? !current().canContinue!() : false) || connectBlocked()" [ariaDescribedBy]="connectBlocked() ? 'sw-continue-hint' : ''" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43822
43912
  } @else if (current().kind === 'import') {
43823
43913
  <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-continue" [disabled]="importBlocked()" [ariaDescribedBy]="importBlocked() ? 'sw-continue-hint' : ''" (click)="continueFromImport()">{{ t().continue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43824
43914
  }
@@ -43870,16 +43960,13 @@ var SetupWizardComponent = class SetupWizardComponent {
43870
43960
  </ui-alert>
43871
43961
  } @else {
43872
43962
  @if (imp.hint) { <p class="sw-hint">{{ imp.hint }}</p> }
43873
- @if (imp.config.importFields?.length) {
43874
- <ui-button variant="link" [attr.data-testid]="'download-sample-' + current().key" (click)="downloadSample()">{{ imp.templateLabel || t().downloadSample }}</ui-button>
43875
- }
43876
43963
  @if (imp.sources?.length) {
43877
43964
  <ui-import-source-grid appearance="card" [disabled]="busy()" [actionLabel]="t().selectSource" [sources]="imp.sources!" [ariaLabel]="'Select your ' + imp.config.entityName + ' source'" (sourceSelect)="startImport(current().key, $event)" />
43878
43965
  } @else {
43879
43966
  <ui-button variant="primary" size="sm" [attr.data-testid]="'open-import-' + current().key" (click)="openImport(current().key)"><svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('upload')" /></svg> {{ imp.buttonLabel }}</ui-button>
43880
43967
  }
43881
43968
  }
43882
- <ui-import-export #importer style="display:none" [config]="imp.config" [importSources]="imp.sources" (imported)="onImported(current().key)" />
43969
+ <ui-import-export #importer style="display:none" [config]="imp.config" [importSources]="imp.sources" [closeOnly]="true" [submit]="imp.submit ?? null" (imported)="onImported(current().key)" />
43883
43970
  }
43884
43971
  }
43885
43972
  @case ('integrations') {
@@ -43901,6 +43988,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43901
43988
  }
43902
43989
 
43903
43990
  @if (importBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().importRequired }}</p> }
43991
+ @if (connectBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().connectRequired }}</p> }
43904
43992
 
43905
43993
  @if (error()) { <ui-alert variant="danger" data-testid="wizard-save-error">{{ error() }}</ui-alert> }
43906
43994
 
@@ -44013,9 +44101,11 @@ var SetupWizardComponent = class SetupWizardComponent {
44013
44101
  "config",
44014
44102
  "importSources",
44015
44103
  "exportParams",
44016
- "exportOnly"
44104
+ "exportOnly",
44105
+ "closeOnly",
44106
+ "submit"
44017
44107
  ],
44018
- outputs: ["imported"]
44108
+ outputs: ["finished", "imported"]
44019
44109
  },
44020
44110
  {
44021
44111
  kind: "component",
@@ -44298,7 +44388,7 @@ i0.ɵɵngDeclareClassMetadata({
44298
44388
  </ui-button>
44299
44389
  }
44300
44390
  } @else if (current().kind === 'integrations') {
44301
- <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="current().canContinue ? !current().canContinue!() : false" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
44391
+ <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="(current().canContinue ? !current().canContinue!() : false) || connectBlocked()" [ariaDescribedBy]="connectBlocked() ? 'sw-continue-hint' : ''" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
44302
44392
  } @else if (current().kind === 'import') {
44303
44393
  <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-continue" [disabled]="importBlocked()" [ariaDescribedBy]="importBlocked() ? 'sw-continue-hint' : ''" (click)="continueFromImport()">{{ t().continue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
44304
44394
  }
@@ -44350,16 +44440,13 @@ i0.ɵɵngDeclareClassMetadata({
44350
44440
  </ui-alert>
44351
44441
  } @else {
44352
44442
  @if (imp.hint) { <p class="sw-hint">{{ imp.hint }}</p> }
44353
- @if (imp.config.importFields?.length) {
44354
- <ui-button variant="link" [attr.data-testid]="'download-sample-' + current().key" (click)="downloadSample()">{{ imp.templateLabel || t().downloadSample }}</ui-button>
44355
- }
44356
44443
  @if (imp.sources?.length) {
44357
44444
  <ui-import-source-grid appearance="card" [disabled]="busy()" [actionLabel]="t().selectSource" [sources]="imp.sources!" [ariaLabel]="'Select your ' + imp.config.entityName + ' source'" (sourceSelect)="startImport(current().key, $event)" />
44358
44445
  } @else {
44359
44446
  <ui-button variant="primary" size="sm" [attr.data-testid]="'open-import-' + current().key" (click)="openImport(current().key)"><svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('upload')" /></svg> {{ imp.buttonLabel }}</ui-button>
44360
44447
  }
44361
44448
  }
44362
- <ui-import-export #importer style="display:none" [config]="imp.config" [importSources]="imp.sources" (imported)="onImported(current().key)" />
44449
+ <ui-import-export #importer style="display:none" [config]="imp.config" [importSources]="imp.sources" [closeOnly]="true" [submit]="imp.submit ?? null" (imported)="onImported(current().key)" />
44363
44450
  }
44364
44451
  }
44365
44452
  @case ('integrations') {
@@ -44381,6 +44468,7 @@ i0.ɵɵngDeclareClassMetadata({
44381
44468
  }
44382
44469
 
44383
44470
  @if (importBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().importRequired }}</p> }
44471
+ @if (connectBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().connectRequired }}</p> }
44384
44472
 
44385
44473
  @if (error()) { <ui-alert variant="danger" data-testid="wizard-save-error">{{ error() }}</ui-alert> }
44386
44474