@nettyapps/ntybase 21.1.29 → 21.1.30

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.
@@ -326,11 +326,11 @@ class ErrorAlert {
326
326
  dialogRef = inject((MatDialogRef));
327
327
  data = inject(MAT_DIALOG_DATA);
328
328
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ErrorAlert, deps: [], target: i0.ɵɵFactoryTarget.Component });
329
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ErrorAlert, isStandalone: true, selector: "ntybase-error-alert", ngImport: i0, template: "<div class=\"error-dialog-container\">\n <div class=\"header\">\n <mat-icon color=\"error\">error_outline</mat-icon>\n <h2 class=\"error-title\">{{ data.title | translate }}</h2>\n </div>\n\n <div class=\"error-content\">\n <p>{{ data.message | translate }}</p>\n </div>\n\n <mat-dialog-actions class=\"dialog-actions\">\n <button mat-flat-button [mat-dialog-close]=\"true\" class=\"btn-ok\">\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".error-dialog-container{padding:40px 32px;text-align:center;min-width:380px;max-width:450px;background:var(--mat-sys-primary);border-radius:20px;box-shadow:0 15px 35px #00000012}.error-dialog-container .header{display:flex;flex-direction:column;align-items:center;gap:12px}.error-dialog-container .header mat-icon{font-size:52px;width:52px;height:52px;color:var(--mat-sys-inverse-primary);margin-bottom:8px}.error-dialog-container .header .error-title{margin:0;font-size:1.4rem;font-weight:700;color:var(--mat-sys-surface);letter-spacing:-.02em}.error-dialog-container .error-content{margin:24px 0 32px;font-size:1rem;line-height:1.6;color:var(--mat-sys-surface)}.error-dialog-container .dialog-actions{display:flex;justify-content:center;padding:0;margin:0}.error-dialog-container .btn-ok{min-width:120px;padding:12px 24px;font-weight:600;font-size:1rem;border-radius:10px;background:var(--mat-sys-inverse-primary);color:var(--mat-sys-primary);border:none;cursor:pointer;transition:all .2s ease}.error-dialog-container .btn-ok:hover{background:var(--mat-sys-inverse-primary);opacity:.9;box-shadow:0 5px 15px #0000001a}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
329
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.2", type: ErrorAlert, isStandalone: true, selector: "ntybase-error-alert", ngImport: i0, template: "<div class=\"error-dialog-container\">\n <div class=\"header\">\n <mat-icon color=\"error\">error_outline</mat-icon>\n <h2 class=\"error-title\">{{ data.title }}</h2>\n </div>\n\n <div class=\"error-content\">\n <p>{{ data.message }}</p>\n </div>\n\n <mat-dialog-actions class=\"dialog-actions\">\n <button mat-flat-button [mat-dialog-close]=\"true\" class=\"btn-ok\">\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".error-dialog-container{padding:40px 32px;text-align:center;min-width:380px;max-width:450px;background:var(--mat-sys-primary);border-radius:20px;box-shadow:0 15px 35px #00000012}.error-dialog-container .header{display:flex;flex-direction:column;align-items:center;gap:12px}.error-dialog-container .header mat-icon{font-size:52px;width:52px;height:52px;color:var(--mat-sys-inverse-primary);margin-bottom:8px}.error-dialog-container .header .error-title{margin:0;font-size:1.4rem;font-weight:700;color:var(--mat-sys-surface);letter-spacing:-.02em}.error-dialog-container .error-content{margin:24px 0 32px;font-size:1rem;line-height:1.6;color:var(--mat-sys-surface)}.error-dialog-container .dialog-actions{display:flex;justify-content:center;padding:0;margin:0}.error-dialog-container .btn-ok{min-width:120px;padding:12px 24px;font-weight:600;font-size:1rem;border-radius:10px;background:var(--mat-sys-inverse-primary);color:var(--mat-sys-primary);border:none;cursor:pointer;transition:all .2s ease}.error-dialog-container .btn-ok:hover{background:var(--mat-sys-inverse-primary);opacity:.9;box-shadow:0 5px 15px #0000001a}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
330
330
  }
331
331
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: ErrorAlert, decorators: [{
332
332
  type: Component,
333
- args: [{ selector: 'ntybase-error-alert', imports: [MatDialogModule, MatIconModule, CommonModule, TranslateModule], template: "<div class=\"error-dialog-container\">\n <div class=\"header\">\n <mat-icon color=\"error\">error_outline</mat-icon>\n <h2 class=\"error-title\">{{ data.title | translate }}</h2>\n </div>\n\n <div class=\"error-content\">\n <p>{{ data.message | translate }}</p>\n </div>\n\n <mat-dialog-actions class=\"dialog-actions\">\n <button mat-flat-button [mat-dialog-close]=\"true\" class=\"btn-ok\">\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".error-dialog-container{padding:40px 32px;text-align:center;min-width:380px;max-width:450px;background:var(--mat-sys-primary);border-radius:20px;box-shadow:0 15px 35px #00000012}.error-dialog-container .header{display:flex;flex-direction:column;align-items:center;gap:12px}.error-dialog-container .header mat-icon{font-size:52px;width:52px;height:52px;color:var(--mat-sys-inverse-primary);margin-bottom:8px}.error-dialog-container .header .error-title{margin:0;font-size:1.4rem;font-weight:700;color:var(--mat-sys-surface);letter-spacing:-.02em}.error-dialog-container .error-content{margin:24px 0 32px;font-size:1rem;line-height:1.6;color:var(--mat-sys-surface)}.error-dialog-container .dialog-actions{display:flex;justify-content:center;padding:0;margin:0}.error-dialog-container .btn-ok{min-width:120px;padding:12px 24px;font-weight:600;font-size:1rem;border-radius:10px;background:var(--mat-sys-inverse-primary);color:var(--mat-sys-primary);border:none;cursor:pointer;transition:all .2s ease}.error-dialog-container .btn-ok:hover{background:var(--mat-sys-inverse-primary);opacity:.9;box-shadow:0 5px 15px #0000001a}\n"] }]
333
+ args: [{ selector: 'ntybase-error-alert', imports: [MatDialogModule, MatIconModule, CommonModule, TranslateModule], template: "<div class=\"error-dialog-container\">\n <div class=\"header\">\n <mat-icon color=\"error\">error_outline</mat-icon>\n <h2 class=\"error-title\">{{ data.title }}</h2>\n </div>\n\n <div class=\"error-content\">\n <p>{{ data.message }}</p>\n </div>\n\n <mat-dialog-actions class=\"dialog-actions\">\n <button mat-flat-button [mat-dialog-close]=\"true\" class=\"btn-ok\">\n {{'@btnOK' | translate}}\n </button>\n </mat-dialog-actions>\n</div>\n", styles: [".error-dialog-container{padding:40px 32px;text-align:center;min-width:380px;max-width:450px;background:var(--mat-sys-primary);border-radius:20px;box-shadow:0 15px 35px #00000012}.error-dialog-container .header{display:flex;flex-direction:column;align-items:center;gap:12px}.error-dialog-container .header mat-icon{font-size:52px;width:52px;height:52px;color:var(--mat-sys-inverse-primary);margin-bottom:8px}.error-dialog-container .header .error-title{margin:0;font-size:1.4rem;font-weight:700;color:var(--mat-sys-surface);letter-spacing:-.02em}.error-dialog-container .error-content{margin:24px 0 32px;font-size:1rem;line-height:1.6;color:var(--mat-sys-surface)}.error-dialog-container .dialog-actions{display:flex;justify-content:center;padding:0;margin:0}.error-dialog-container .btn-ok{min-width:120px;padding:12px 24px;font-weight:600;font-size:1rem;border-radius:10px;background:var(--mat-sys-inverse-primary);color:var(--mat-sys-primary);border:none;cursor:pointer;transition:all .2s ease}.error-dialog-container .btn-ok:hover{background:var(--mat-sys-inverse-primary);opacity:.9;box-shadow:0 5px 15px #0000001a}\n"] }]
334
334
  }] });
335
335
 
336
336
  // alert.service.ts
@@ -378,29 +378,38 @@ class AlertService {
378
378
  });
379
379
  }
380
380
  // For error notifications
381
- showError(error, customComponent = ErrorAlert, duration = 5000, width = 'auto', height = 'auto') {
382
- let componentToOpen = customComponent;
383
- let actualError = error;
384
- // Handle (message, error) pattern where second arg is mistakenly an error object instead of a component
385
- if (typeof error === 'string' && customComponent && typeof customComponent === 'object') {
386
- actualError = { message: error, details: customComponent };
387
- componentToOpen = ErrorAlert;
388
- }
389
- else if (typeof customComponent !== 'function') {
390
- // Ensure we always have a component to open
391
- componentToOpen = ErrorAlert;
392
- }
393
- const dialogData = (typeof actualError === 'object' && actualError.errors)
394
- ? actualError
395
- : { message: this.getErrorMessage(actualError) };
381
+ /**
382
+ * Flexible Error Display
383
+ * @param error - Error object from API or string
384
+ * @param fallbackOrComponent - Optional: Fallback message key (@...) OR Custom Component
385
+ * @param customComponent - Optional: Custom component to use if the 2nd parameter is a message
386
+ */
387
+ showError(error, fallbackOrComponent = ErrorAlert, customComponent = ErrorAlert, width = 'auto', height = 'auto') {
388
+ let componentToOpen = ErrorAlert;
389
+ let fallbackMsg = '';
390
+ if (typeof fallbackOrComponent === 'string' && fallbackOrComponent.startsWith('@')) {
391
+ // showError(err, '@updateFailed', ...)
392
+ fallbackMsg = fallbackOrComponent;
393
+ componentToOpen = (customComponent && typeof customComponent === 'function') ? customComponent : ErrorAlert;
394
+ }
395
+ else if (typeof fallbackOrComponent === 'function') {
396
+ // showError(err, MySpecialComponent)
397
+ componentToOpen = fallbackOrComponent;
398
+ }
399
+ // Extract Message (C# / Backend message has priority)
400
+ const backendError = this.getErrorMessage(error);
401
+ // Final Message Decision: Use backend message if exists, otherwise translate fallback key
402
+ const finalMessage = (backendError && backendError !== JSON.stringify(error))
403
+ ? backendError
404
+ : (fallbackMsg ? this.translate.instant(fallbackMsg) : backendError);
396
405
  return new Promise((resolve) => {
397
406
  const dialogRef = this.dialog.open(componentToOpen, {
398
407
  width: width,
399
408
  height: height,
400
409
  maxWidth: '95vw',
401
410
  data: {
402
- ...dialogData,
403
- title: '@errorOccurred',
411
+ message: finalMessage,
412
+ title: this.translate.instant('@errorOccurred'),
404
413
  },
405
414
  });
406
415
  dialogRef.afterClosed().subscribe(() => {
@@ -408,22 +417,27 @@ class AlertService {
408
417
  });
409
418
  });
410
419
  }
420
+ /**
421
+ * Extracts the most meaningful error message from the error object
422
+ */
411
423
  getErrorMessage(error) {
412
424
  if (!error)
413
425
  return '';
414
- if (typeof error === 'string')
415
- return error;
416
- if (error.message && error.details) {
417
- const backendError = this.getErrorMessage(error.details);
418
- return `${error.message}\n${backendError}`;
419
- }
420
- if (error.error && typeof error.error === 'string')
421
- return error.error;
422
- if (error.error && error.error.message)
426
+ // 1. C# API / Backend Error Messages
427
+ if (error?.error?.message)
423
428
  return error.error.message;
424
- if (error.message)
429
+ if (error?.error && typeof error.error === 'string')
430
+ return error.error;
431
+ if (error?.details && typeof error.details === 'string')
432
+ return error.details;
433
+ if (error?.message && typeof error.message === 'string' && !error.message.startsWith('@'))
425
434
  return error.message;
426
- return JSON.stringify(error);
435
+ /// 2. Localization Keys (starting with @)
436
+ if (typeof error === 'string' && error.startsWith('@'))
437
+ return this.translate.instant(error);
438
+ if (error?.message && typeof error.message === 'string' && error.message.startsWith('@'))
439
+ return this.translate.instant(error.message);
440
+ return typeof error === 'string' ? error : JSON.stringify(error);
427
441
  }
428
442
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AlertService, deps: [{ token: i1$2.MatSnackBar }, { token: i1.MatDialog }, { token: i1$1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
429
443
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: AlertService, providedIn: 'root' });