valtech-components 4.0.151 → 4.0.153

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.
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
56
56
  * Current version of valtech-components.
57
57
  * This is automatically updated during the publish process.
58
58
  */
59
- const VERSION = '4.0.151';
59
+ const VERSION = '4.0.153';
60
60
 
61
61
  // Control de estado de refresco (singleton a nivel de módulo)
62
62
  let isRefreshing = false;
@@ -1789,6 +1789,7 @@ class ToastService {
1789
1789
  duration: request.duration,
1790
1790
  position: request.position ?? 'top',
1791
1791
  color: request.color ?? 'dark',
1792
+ buttons: request.buttons,
1792
1793
  });
1793
1794
  await toast.present();
1794
1795
  }
@@ -1883,7 +1884,7 @@ class ValtechErrorService {
1883
1884
  */
1884
1885
  handle(err, opts = {}) {
1885
1886
  const interpreted = interpretError(err);
1886
- const { context, i18nMap, fallbackKey, i18nNamespace, toast } = opts;
1887
+ const { context, i18nMap, fallbackKey, i18nNamespace, toast, showOperationId } = opts;
1887
1888
  // 1. Observabilidad — log estructurado a consola.
1888
1889
  console.error('[ValtechError] handled', {
1889
1890
  context: context ?? 'n/a',
@@ -1917,11 +1918,23 @@ class ValtechErrorService {
1917
1918
  });
1918
1919
  // 4. Toast (estándar Valtech: color 'dark', position 'top').
1919
1920
  if (toast !== false) {
1921
+ const opId = showOperationId ? interpreted.operationId : undefined;
1920
1922
  this.toast.show({
1921
1923
  message,
1922
1924
  color: 'dark',
1923
1925
  position: 'top',
1924
- duration: 3000,
1926
+ // Más tiempo cuando hay botón de copia — el usuario necesita leerlo y tapear.
1927
+ duration: opId ? 6000 : 3000,
1928
+ buttons: opId
1929
+ ? [
1930
+ {
1931
+ text: 'Copiar ID',
1932
+ handler: () => {
1933
+ void navigator.clipboard.writeText(opId);
1934
+ },
1935
+ },
1936
+ ]
1937
+ : undefined,
1925
1938
  });
1926
1939
  }
1927
1940
  // 5. Devolver el error normalizado para lógica extra del caller.
@@ -30390,6 +30403,8 @@ addIcons({
30390
30403
  alertCircleOutline,
30391
30404
  cloudOfflineOutline,
30392
30405
  documentOutline,
30406
+ copyOutline,
30407
+ checkmarkOutline,
30393
30408
  });
30394
30409
  const DEFAULT_ICON_BY_VARIANT = {
30395
30410
  empty: 'document-outline',
@@ -30422,6 +30437,7 @@ const DEFAULT_ICON_BY_VARIANT = {
30422
30437
  class EmptyStateComponent {
30423
30438
  constructor() {
30424
30439
  this.props = input.required();
30440
+ this.copied = signal(false);
30425
30441
  /** Variante resuelta (default `'empty'`). */
30426
30442
  this.variantClass = computed(() => this.props().variant ?? 'empty');
30427
30443
  /** Icono final — custom si se pasó, default de la variante si no. */
@@ -30448,6 +30464,12 @@ class EmptyStateComponent {
30448
30464
  };
30449
30465
  });
30450
30466
  }
30467
+ copyOpId(opId) {
30468
+ void navigator.clipboard.writeText(opId).then(() => {
30469
+ this.copied.set(true);
30470
+ setTimeout(() => this.copied.set(false), 1500);
30471
+ });
30472
+ }
30451
30473
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30452
30474
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: EmptyStateComponent, isStandalone: true, selector: "val-empty-state", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
30453
30475
  <section
@@ -30488,8 +30510,15 @@ class EmptyStateComponent {
30488
30510
  @if (props().action; as a) {
30489
30511
  <val-button class="empty-state__cta" [props]="ctaProps()" (click)="a.handler()" />
30490
30512
  }
30513
+
30514
+ @if (props().operationId; as opId) {
30515
+ <button class="empty-state__op-id" (click)="copyOpId(opId)" [attr.aria-label]="'Copiar ID de operación'">
30516
+ <ion-icon [name]="copied() ? 'checkmark-outline' : 'copy-outline'" />
30517
+ <span>{{ copied() ? 'ID copiado' : 'ID: ' + opId }}</span>
30518
+ </button>
30519
+ }
30491
30520
  </section>
30492
- `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;padding:32px 16px;min-height:240px}.empty-state__icon{color:var(--ion-color-medium)}.empty-state__emoji{line-height:1}.empty-state--error .empty-state__icon{color:var(--ion-color-warning, #ffb800)}.empty-state--offline .empty-state__icon{color:var(--ion-color-medium);opacity:.7}.empty-state__cta{margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
30521
+ `, isInline: true, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;padding:32px 16px;min-height:240px}.empty-state__icon{color:var(--ion-color-medium)}.empty-state__emoji{line-height:1}.empty-state--error .empty-state__icon{color:var(--ion-color-warning, #ffb800)}.empty-state--offline .empty-state__icon{color:var(--ion-color-medium);opacity:.7}.empty-state__cta{margin-top:8px}.empty-state__op-id{display:inline-flex;align-items:center;gap:6px;margin-top:4px;padding:5px 10px;background:none;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:999px;cursor:pointer;color:var(--ion-color-medium);font-size:.75rem;font-family:monospace;line-height:1;transition:color .15s,border-color .15s}.empty-state__op-id:hover{color:var(--ion-color-dark);border-color:var(--ion-color-medium)}.empty-state__op-id ion-icon{font-size:.875rem;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
30493
30522
  }
30494
30523
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EmptyStateComponent, decorators: [{
30495
30524
  type: Component,
@@ -30532,8 +30561,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
30532
30561
  @if (props().action; as a) {
30533
30562
  <val-button class="empty-state__cta" [props]="ctaProps()" (click)="a.handler()" />
30534
30563
  }
30564
+
30565
+ @if (props().operationId; as opId) {
30566
+ <button class="empty-state__op-id" (click)="copyOpId(opId)" [attr.aria-label]="'Copiar ID de operación'">
30567
+ <ion-icon [name]="copied() ? 'checkmark-outline' : 'copy-outline'" />
30568
+ <span>{{ copied() ? 'ID copiado' : 'ID: ' + opId }}</span>
30569
+ </button>
30570
+ }
30535
30571
  </section>
30536
- `, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;padding:32px 16px;min-height:240px}.empty-state__icon{color:var(--ion-color-medium)}.empty-state__emoji{line-height:1}.empty-state--error .empty-state__icon{color:var(--ion-color-warning, #ffb800)}.empty-state--offline .empty-state__icon{color:var(--ion-color-medium);opacity:.7}.empty-state__cta{margin-top:8px}\n"] }]
30572
+ `, styles: [".empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;padding:32px 16px;min-height:240px}.empty-state__icon{color:var(--ion-color-medium)}.empty-state__emoji{line-height:1}.empty-state--error .empty-state__icon{color:var(--ion-color-warning, #ffb800)}.empty-state--offline .empty-state__icon{color:var(--ion-color-medium);opacity:.7}.empty-state__cta{margin-top:8px}.empty-state__op-id{display:inline-flex;align-items:center;gap:6px;margin-top:4px;padding:5px 10px;background:none;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));border-radius:999px;cursor:pointer;color:var(--ion-color-medium);font-size:.75rem;font-family:monospace;line-height:1;transition:color .15s,border-color .15s}.empty-state__op-id:hover{color:var(--ion-color-dark);border-color:var(--ion-color-medium)}.empty-state__op-id ion-icon{font-size:.875rem;flex-shrink:0}\n"] }]
30537
30573
  }] });
30538
30574
 
30539
30575
  /**
@@ -30587,6 +30623,7 @@ function createErrorStateProps(err, opts) {
30587
30623
  description: opts.description ? pick(opts.description) : undefined,
30588
30624
  action,
30589
30625
  emoji: opts.emoji ? pick(opts.emoji) : undefined,
30626
+ operationId: interpreted.operationId,
30590
30627
  };
30591
30628
  }
30592
30629
 
@@ -69934,20 +69971,24 @@ class TicketCardComponent {
69934
69971
  }
69935
69972
  </div>
69936
69973
 
69937
- @if (props().showDownload && qr()) {
69938
- <ion-button fill="clear" size="small" [disabled]="downloading()" (click)="downloadCard()">
69939
- <ion-icon name="download-outline" slot="start" />
69940
- {{ props().downloadLabel || t('download') }}
69941
- </ion-button>
69942
- }
69943
- @if (props().showShare && qr()) {
69944
- <ion-button fill="clear" size="small" [disabled]="sharing()" (click)="shareCard()">
69945
- <ion-icon name="share-outline" slot="start" />
69946
- {{ props().shareLabel || t('share') }}
69947
- </ion-button>
69974
+ @if ((props().showDownload || props().showShare) && qr()) {
69975
+ <div class="ticket-card__actions">
69976
+ @if (props().showDownload) {
69977
+ <ion-button fill="outline" expand="block" [disabled]="downloading()" (click)="downloadCard()">
69978
+ <ion-icon name="download-outline" slot="start" />
69979
+ {{ props().downloadLabel || t('download') }}
69980
+ </ion-button>
69981
+ }
69982
+ @if (props().showShare) {
69983
+ <ion-button fill="outline" expand="block" [disabled]="sharing()" (click)="shareCard()">
69984
+ <ion-icon name="share-outline" slot="start" />
69985
+ {{ props().shareLabel || t('share') }}
69986
+ </ion-button>
69987
+ }
69988
+ </div>
69948
69989
  }
69949
69990
  </div>
69950
- `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:block}.ticket-card{display:flex;flex-direction:column;align-items:center;gap:12px;padding:20px;border:1px solid var(--ion-color-light-shade, #e0e0e0);border-radius:16px;background:var(--ion-card-background, #ffffff);max-width:320px;margin:0 auto}.ticket-card__title{font-size:1rem;font-weight:700;color:var(--ion-color-dark);text-align:center}.ticket-card__qr{display:flex;align-items:center;justify-content:center;min-height:200px}.ticket-card__qr-loading{display:flex;align-items:center;justify-content:center;width:200px;height:200px}.ticket-card__qr-fallback{font-family:monospace;font-size:.75rem;color:var(--ion-color-medium);word-break:break-all;text-align:center;padding:16px}.ticket-card__meta{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}.ticket-card__folio{font-size:1rem;font-weight:700;color:var(--ion-color-dark)}.ticket-card__buyer{font-size:.875rem;color:var(--ion-color-medium)}.ticket-card__badge{margin-top:4px;font-size:.75rem;font-weight:600;padding:3px 12px;border-radius:999px;background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff);white-space:nowrap}.ticket-card__badge[data-status=sold]{background:var(--ion-color-success, #2dd36f);color:var(--ion-color-success-contrast, #fff)}.ticket-card__badge[data-status=checkedIn]{background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: QrCodeComponent, selector: "val-qr-code", inputs: ["props"], outputs: ["actionComplete", "imageLoad", "imageError"] }] }); }
69991
+ `, isInline: true, styles: ["@charset \"UTF-8\";:host{display:block}.ticket-card{display:flex;flex-direction:column;align-items:center;gap:12px;padding:20px;border:1px solid var(--ion-color-light-shade, #e0e0e0);border-radius:16px;background:var(--ion-card-background, #ffffff);max-width:320px;margin:0 auto}.ticket-card__title{font-size:1rem;font-weight:700;color:var(--ion-color-dark);text-align:center}.ticket-card__qr{display:flex;align-items:center;justify-content:center;min-height:200px}.ticket-card__qr-loading{display:flex;align-items:center;justify-content:center;width:200px;height:200px}.ticket-card__qr-fallback{font-family:monospace;font-size:.75rem;color:var(--ion-color-medium);word-break:break-all;text-align:center;padding:16px}.ticket-card__meta{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}.ticket-card__folio{font-size:1rem;font-weight:700;color:var(--ion-color-dark)}.ticket-card__buyer{font-size:.875rem;color:var(--ion-color-medium)}.ticket-card__badge{margin-top:4px;font-size:.75rem;font-weight:600;padding:3px 12px;border-radius:999px;background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff);white-space:nowrap}.ticket-card__badge[data-status=sold]{background:var(--ion-color-success, #2dd36f);color:var(--ion-color-success-contrast, #fff)}.ticket-card__badge[data-status=checkedIn]{background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff)}.ticket-card__actions{width:100%;display:flex;flex-direction:column;gap:8px}.ticket-card__actions ion-button{--border-radius: 10px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: QrCodeComponent, selector: "val-qr-code", inputs: ["props"], outputs: ["actionComplete", "imageLoad", "imageError"] }] }); }
69951
69992
  }
69952
69993
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TicketCardComponent, decorators: [{
69953
69994
  type: Component,
@@ -69981,20 +70022,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
69981
70022
  }
69982
70023
  </div>
69983
70024
 
69984
- @if (props().showDownload && qr()) {
69985
- <ion-button fill="clear" size="small" [disabled]="downloading()" (click)="downloadCard()">
69986
- <ion-icon name="download-outline" slot="start" />
69987
- {{ props().downloadLabel || t('download') }}
69988
- </ion-button>
69989
- }
69990
- @if (props().showShare && qr()) {
69991
- <ion-button fill="clear" size="small" [disabled]="sharing()" (click)="shareCard()">
69992
- <ion-icon name="share-outline" slot="start" />
69993
- {{ props().shareLabel || t('share') }}
69994
- </ion-button>
70025
+ @if ((props().showDownload || props().showShare) && qr()) {
70026
+ <div class="ticket-card__actions">
70027
+ @if (props().showDownload) {
70028
+ <ion-button fill="outline" expand="block" [disabled]="downloading()" (click)="downloadCard()">
70029
+ <ion-icon name="download-outline" slot="start" />
70030
+ {{ props().downloadLabel || t('download') }}
70031
+ </ion-button>
70032
+ }
70033
+ @if (props().showShare) {
70034
+ <ion-button fill="outline" expand="block" [disabled]="sharing()" (click)="shareCard()">
70035
+ <ion-icon name="share-outline" slot="start" />
70036
+ {{ props().shareLabel || t('share') }}
70037
+ </ion-button>
70038
+ }
70039
+ </div>
69995
70040
  }
69996
70041
  </div>
69997
- `, styles: ["@charset \"UTF-8\";:host{display:block}.ticket-card{display:flex;flex-direction:column;align-items:center;gap:12px;padding:20px;border:1px solid var(--ion-color-light-shade, #e0e0e0);border-radius:16px;background:var(--ion-card-background, #ffffff);max-width:320px;margin:0 auto}.ticket-card__title{font-size:1rem;font-weight:700;color:var(--ion-color-dark);text-align:center}.ticket-card__qr{display:flex;align-items:center;justify-content:center;min-height:200px}.ticket-card__qr-loading{display:flex;align-items:center;justify-content:center;width:200px;height:200px}.ticket-card__qr-fallback{font-family:monospace;font-size:.75rem;color:var(--ion-color-medium);word-break:break-all;text-align:center;padding:16px}.ticket-card__meta{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}.ticket-card__folio{font-size:1rem;font-weight:700;color:var(--ion-color-dark)}.ticket-card__buyer{font-size:.875rem;color:var(--ion-color-medium)}.ticket-card__badge{margin-top:4px;font-size:.75rem;font-weight:600;padding:3px 12px;border-radius:999px;background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff);white-space:nowrap}.ticket-card__badge[data-status=sold]{background:var(--ion-color-success, #2dd36f);color:var(--ion-color-success-contrast, #fff)}.ticket-card__badge[data-status=checkedIn]{background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff)}\n"] }]
70042
+ `, styles: ["@charset \"UTF-8\";:host{display:block}.ticket-card{display:flex;flex-direction:column;align-items:center;gap:12px;padding:20px;border:1px solid var(--ion-color-light-shade, #e0e0e0);border-radius:16px;background:var(--ion-card-background, #ffffff);max-width:320px;margin:0 auto}.ticket-card__title{font-size:1rem;font-weight:700;color:var(--ion-color-dark);text-align:center}.ticket-card__qr{display:flex;align-items:center;justify-content:center;min-height:200px}.ticket-card__qr-loading{display:flex;align-items:center;justify-content:center;width:200px;height:200px}.ticket-card__qr-fallback{font-family:monospace;font-size:.75rem;color:var(--ion-color-medium);word-break:break-all;text-align:center;padding:16px}.ticket-card__meta{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center}.ticket-card__folio{font-size:1rem;font-weight:700;color:var(--ion-color-dark)}.ticket-card__buyer{font-size:.875rem;color:var(--ion-color-medium)}.ticket-card__badge{margin-top:4px;font-size:.75rem;font-weight:600;padding:3px 12px;border-radius:999px;background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff);white-space:nowrap}.ticket-card__badge[data-status=sold]{background:var(--ion-color-success, #2dd36f);color:var(--ion-color-success-contrast, #fff)}.ticket-card__badge[data-status=checkedIn]{background:var(--ion-color-medium);color:var(--ion-color-medium-contrast, #fff)}.ticket-card__actions{width:100%;display:flex;flex-direction:column;gap:8px}.ticket-card__actions ion-button{--border-radius: 10px;margin:0}\n"] }]
69998
70043
  }], ctorParameters: () => [] });
69999
70044
 
70000
70045
  /**