codexly-ui 0.10.57 → 0.10.59

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.
@@ -13034,43 +13034,18 @@ class ClxPageEmptyComponent {
13034
13034
  ctaLabel = input('', ...(ngDevMode ? [{ debugName: "ctaLabel" }] : /* istanbul ignore next */ []));
13035
13035
  ctaIcon = input('add', ...(ngDevMode ? [{ debugName: "ctaIcon" }] : /* istanbul ignore next */ []));
13036
13036
  ctaColor = input(undefined, ...(ngDevMode ? [{ debugName: "ctaColor" }] : /* istanbul ignore next */ []));
13037
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
13038
13037
  _ctaColor = computed(() => this.ctaColor() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_ctaColor" }] : /* istanbul ignore next */ []));
13039
13038
  cta = output();
13040
- _iconWrap() {
13041
- const s = this.size();
13042
- const sz = s === 'sm' ? 'w-12 h-12' : s === 'lg' ? 'w-20 h-20' : 'w-16 h-16';
13043
- return `${sz} rounded-2xl bg-clx-surface-2 flex items-center justify-center mb-4`;
13044
- }
13045
- _iconSize() {
13046
- const s = this.size();
13047
- return s === 'sm' ? 'md' : s === 'lg' ? 'xl' : 'lg';
13048
- }
13049
- _titleCls() {
13050
- const s = this.size();
13051
- return s === 'sm'
13052
- ? 'text-sm font-semibold text-clx-text-label mb-1'
13053
- : s === 'lg'
13054
- ? 'text-xl font-semibold text-clx-text-label mb-2'
13055
- : 'text-base font-semibold text-clx-text-label mb-1';
13056
- }
13057
- _descCls() {
13058
- const s = this.size();
13059
- return `${s === 'lg' ? 'text-sm' : 'text-xs'} text-clx-text-subtle max-w-xs mb-5`;
13060
- }
13061
- _ctaCls() {
13062
- return this.size() === 'sm' ? 'mt-3' : 'mt-5';
13063
- }
13064
13039
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxPageEmptyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13065
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageEmptyComponent, isStandalone: true, selector: "clx-page-empty", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, ctaLabel: { classPropertyName: "ctaLabel", publicName: "ctaLabel", isSignal: true, isRequired: false, transformFunction: null }, ctaIcon: { classPropertyName: "ctaIcon", publicName: "ctaIcon", isSignal: true, isRequired: false, transformFunction: null }, ctaColor: { classPropertyName: "ctaColor", publicName: "ctaColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cta: "cta" }, host: { classAttribute: "flex flex-col items-center justify-center w-full px-6 text-center" }, ngImport: i0, template: `
13066
- <div [class]="_iconWrap()">
13067
- <span clx-icon [name]="icon()" [size]="_iconSize()" class="text-clx-text-faint"></span>
13040
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageEmptyComponent, isStandalone: true, selector: "clx-page-empty", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, ctaLabel: { classPropertyName: "ctaLabel", publicName: "ctaLabel", isSignal: true, isRequired: false, transformFunction: null }, ctaIcon: { classPropertyName: "ctaIcon", publicName: "ctaIcon", isSignal: true, isRequired: false, transformFunction: null }, ctaColor: { classPropertyName: "ctaColor", publicName: "ctaColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cta: "cta" }, host: { classAttribute: "flex flex-col items-center justify-center w-full px-6 text-center text-sm" }, ngImport: i0, template: `
13041
+ <div class="w-20 h-20 rounded-2xl bg-clx-surface-2 flex items-center justify-center mb-6">
13042
+ <span clx-icon [name]="icon()" size="xl" class="text-clx-text-faint"></span>
13068
13043
  </div>
13069
13044
 
13070
- <p [class]="_titleCls()">{{ title() }}</p>
13045
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13071
13046
 
13072
13047
  @if (description()) {
13073
- <p [class]="_descCls()">{{ description() }}</p>
13048
+ <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13074
13049
  }
13075
13050
 
13076
13051
  @if (ctaLabel()) {
@@ -13080,7 +13055,6 @@ class ClxPageEmptyComponent {
13080
13055
  [color]="_ctaColor()"
13081
13056
  variant="solid"
13082
13057
  [icon]="ctaIcon()"
13083
- [class]="_ctaCls()"
13084
13058
  (click)="cta.emit()">
13085
13059
  {{ ctaLabel() }}
13086
13060
  </button>
@@ -13095,16 +13069,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13095
13069
  imports: [ClxButtonComponent, ClxIconComponent],
13096
13070
  encapsulation: ViewEncapsulation.None,
13097
13071
  changeDetection: ChangeDetectionStrategy.OnPush,
13098
- host: { class: 'flex flex-col items-center justify-center w-full px-6 text-center' },
13072
+ host: { class: 'flex flex-col items-center justify-center w-full px-6 text-center text-sm' },
13099
13073
  template: `
13100
- <div [class]="_iconWrap()">
13101
- <span clx-icon [name]="icon()" [size]="_iconSize()" class="text-clx-text-faint"></span>
13074
+ <div class="w-20 h-20 rounded-2xl bg-clx-surface-2 flex items-center justify-center mb-6">
13075
+ <span clx-icon [name]="icon()" size="xl" class="text-clx-text-faint"></span>
13102
13076
  </div>
13103
13077
 
13104
- <p [class]="_titleCls()">{{ title() }}</p>
13078
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13105
13079
 
13106
13080
  @if (description()) {
13107
- <p [class]="_descCls()">{{ description() }}</p>
13081
+ <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13108
13082
  }
13109
13083
 
13110
13084
  @if (ctaLabel()) {
@@ -13114,14 +13088,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13114
13088
  [color]="_ctaColor()"
13115
13089
  variant="solid"
13116
13090
  [icon]="ctaIcon()"
13117
- [class]="_ctaCls()"
13118
13091
  (click)="cta.emit()">
13119
13092
  {{ ctaLabel() }}
13120
13093
  </button>
13121
13094
  }
13122
13095
  `,
13123
13096
  }]
13124
- }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], ctaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaLabel", required: false }] }], ctaIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaIcon", required: false }] }], ctaColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaColor", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], cta: [{ type: i0.Output, args: ["cta"] }] } });
13097
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], ctaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaLabel", required: false }] }], ctaIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaIcon", required: false }] }], ctaColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "ctaColor", required: false }] }], cta: [{ type: i0.Output, args: ["cta"] }] } });
13125
13098
 
13126
13099
  class ClxTableComponent {
13127
13100
  _themeSvc = inject(ClxThemeService);
@@ -13416,7 +13389,7 @@ class ClxTableComponent {
13416
13389
  </div>
13417
13390
  }
13418
13391
  }
13419
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClxCheckboxComponent, selector: "clx-checkbox", inputs: ["color", "variant", "size", "label", "value", "disabled"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor", "size"], outputs: ["cta"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
13392
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClxCheckboxComponent, selector: "clx-checkbox", inputs: ["color", "variant", "size", "label", "value", "disabled"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor"], outputs: ["cta"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
13420
13393
  }
13421
13394
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableComponent, decorators: [{
13422
13395
  type: Component,
@@ -13576,13 +13549,13 @@ class ClxPageNotFoundComponent {
13576
13549
  backLabel = input('Volver al inicio', ...(ngDevMode ? [{ debugName: "backLabel" }] : /* istanbul ignore next */ []));
13577
13550
  back = output();
13578
13551
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxPageNotFoundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13579
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageNotFoundComponent, isStandalone: true, selector: "clx-page-not-found", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center" }, ngImport: i0, template: `
13552
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageNotFoundComponent, isStandalone: true, selector: "clx-page-not-found", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm" }, ngImport: i0, template: `
13580
13553
  <div class="w-20 h-20 rounded-2xl bg-indigo-50 flex items-center justify-center mb-6">
13581
13554
  <span clx-icon name="search_off" size="xl" class="text-indigo-400"></span>
13582
13555
  </div>
13583
13556
 
13584
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">404</p>
13585
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13557
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">404</p>
13558
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13586
13559
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13587
13560
 
13588
13561
  @if (backLabel()) {
@@ -13606,14 +13579,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13606
13579
  imports: [ClxButtonComponent, ClxIconComponent],
13607
13580
  encapsulation: ViewEncapsulation.None,
13608
13581
  changeDetection: ChangeDetectionStrategy.OnPush,
13609
- host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center' },
13582
+ host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm' },
13610
13583
  template: `
13611
13584
  <div class="w-20 h-20 rounded-2xl bg-indigo-50 flex items-center justify-center mb-6">
13612
13585
  <span clx-icon name="search_off" size="xl" class="text-indigo-400"></span>
13613
13586
  </div>
13614
13587
 
13615
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">404</p>
13616
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13588
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">404</p>
13589
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13617
13590
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13618
13591
 
13619
13592
  @if (backLabel()) {
@@ -13640,13 +13613,13 @@ class ClxPageUnauthorizedComponent {
13640
13613
  login = output();
13641
13614
  back = output();
13642
13615
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxPageUnauthorizedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13643
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageUnauthorizedComponent, isStandalone: true, selector: "clx-page-unauthorized", inputs: { code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, loginLabel: { classPropertyName: "loginLabel", publicName: "loginLabel", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center" }, ngImport: i0, template: `
13616
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageUnauthorizedComponent, isStandalone: true, selector: "clx-page-unauthorized", inputs: { code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, loginLabel: { classPropertyName: "loginLabel", publicName: "loginLabel", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { login: "login", back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm" }, ngImport: i0, template: `
13644
13617
  <div class="w-20 h-20 rounded-2xl bg-amber-50 flex items-center justify-center mb-6">
13645
13618
  <span clx-icon name="lock" size="xl" class="text-amber-400"></span>
13646
13619
  </div>
13647
13620
 
13648
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">{{ code() }}</p>
13649
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13621
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">{{ code() }}</p>
13622
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13650
13623
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13651
13624
 
13652
13625
  <div class="flex items-center gap-3">
@@ -13683,14 +13656,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13683
13656
  imports: [ClxButtonComponent, ClxIconComponent],
13684
13657
  encapsulation: ViewEncapsulation.None,
13685
13658
  changeDetection: ChangeDetectionStrategy.OnPush,
13686
- host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center' },
13659
+ host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm' },
13687
13660
  template: `
13688
13661
  <div class="w-20 h-20 rounded-2xl bg-amber-50 flex items-center justify-center mb-6">
13689
13662
  <span clx-icon name="lock" size="xl" class="text-amber-400"></span>
13690
13663
  </div>
13691
13664
 
13692
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">{{ code() }}</p>
13693
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13665
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">{{ code() }}</p>
13666
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13694
13667
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13695
13668
 
13696
13669
  <div class="flex items-center gap-3">
@@ -13730,13 +13703,13 @@ class ClxPageServerErrorComponent {
13730
13703
  retry = output();
13731
13704
  back = output();
13732
13705
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxPageServerErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13733
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageServerErrorComponent, isStandalone: true, selector: "clx-page-server-error", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null }, errorId: { classPropertyName: "errorId", publicName: "errorId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry", back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center" }, ngImport: i0, template: `
13706
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxPageServerErrorComponent, isStandalone: true, selector: "clx-page-server-error", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null }, errorId: { classPropertyName: "errorId", publicName: "errorId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry", back: "back" }, host: { classAttribute: "flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm" }, ngImport: i0, template: `
13734
13707
  <div class="w-20 h-20 rounded-2xl bg-red-50 flex items-center justify-center mb-6">
13735
13708
  <span clx-icon name="error_outline" size="xl" class="text-red-400"></span>
13736
13709
  </div>
13737
13710
 
13738
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">500</p>
13739
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13711
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">500</p>
13712
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13740
13713
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13741
13714
 
13742
13715
  @if (errorId()) {
@@ -13777,14 +13750,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13777
13750
  imports: [ClxButtonComponent, ClxIconComponent],
13778
13751
  encapsulation: ViewEncapsulation.None,
13779
13752
  changeDetection: ChangeDetectionStrategy.OnPush,
13780
- host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center' },
13753
+ host: { class: 'flex flex-col items-center justify-center min-h-[60vh] px-6 text-center text-sm' },
13781
13754
  template: `
13782
13755
  <div class="w-20 h-20 rounded-2xl bg-red-50 flex items-center justify-center mb-6">
13783
13756
  <span clx-icon name="error_outline" size="xl" class="text-red-400"></span>
13784
13757
  </div>
13785
13758
 
13786
- <p class="text-7xl font-black text-clx-text-faint leading-none select-none mb-2">500</p>
13787
- <h1 class="text-xl font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13759
+ <p class="text-[50px] font-black text-clx-text-faint leading-none select-none mb-2">500</p>
13760
+ <h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
13788
13761
  <p class="text-clx-text-subtle max-w-sm mb-8">{{ description() }}</p>
13789
13762
 
13790
13763
  @if (errorId()) {
@@ -14271,7 +14244,7 @@ class ClxProductComponent {
14271
14244
  clxTooltip="Añadir a favoritos"
14272
14245
  clxTooltipPosition="left"
14273
14246
  [clxTooltipColor]="clxColor()"
14274
- (click)="addToWishlist.emit(product()!)"
14247
+ (click)="$event.stopPropagation(); addToWishlist.emit(product()!)"
14275
14248
  ></button>
14276
14249
  </div>
14277
14250
 
@@ -14292,10 +14265,10 @@ class ClxProductComponent {
14292
14265
 
14293
14266
  <!-- Price -->
14294
14267
  <div class="flex items-baseline gap-2 mt-2">
14295
- <span [class]="_priceClass()">{{ _price() | currency:'COP':'symbol':'1.0-0' }}</span>
14268
+ <span [class]="_priceClass()">{{ _price() | currency:'COP':'$':'1.0-0' }}</span>
14296
14269
  @if (product()?.has_discount && product()?.discount_price) {
14297
14270
  <span class="text-sm text-clx-text-subtle line-through">
14298
- {{ product()?.discount_price | currency:'COP':'symbol':'1.0-0' }}
14271
+ {{ product()?.discount_price | currency:'COP':'$':'1.0-0' }}
14299
14272
  </span>
14300
14273
  }
14301
14274
  </div>
@@ -14313,7 +14286,7 @@ class ClxProductComponent {
14313
14286
  iconPosition="left"
14314
14287
  [disabled]="_disabled()"
14315
14288
  [block]="true"
14316
- (click)="_hasMultipleOptions() ? viewOptions.emit(product()!) : addToCart.emit(product()!)"
14289
+ (click)="$event.stopPropagation(); _hasMultipleOptions() ? viewOptions.emit(product()!) : addToCart.emit(product()!)"
14317
14290
  >
14318
14291
  {{ _hasMultipleOptions() ? 'Ver opciones' : 'Añadir al carrito' }}
14319
14292
  </button>
@@ -14406,7 +14379,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14406
14379
  clxTooltip="Añadir a favoritos"
14407
14380
  clxTooltipPosition="left"
14408
14381
  [clxTooltipColor]="clxColor()"
14409
- (click)="addToWishlist.emit(product()!)"
14382
+ (click)="$event.stopPropagation(); addToWishlist.emit(product()!)"
14410
14383
  ></button>
14411
14384
  </div>
14412
14385
 
@@ -14427,10 +14400,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14427
14400
 
14428
14401
  <!-- Price -->
14429
14402
  <div class="flex items-baseline gap-2 mt-2">
14430
- <span [class]="_priceClass()">{{ _price() | currency:'COP':'symbol':'1.0-0' }}</span>
14403
+ <span [class]="_priceClass()">{{ _price() | currency:'COP':'$':'1.0-0' }}</span>
14431
14404
  @if (product()?.has_discount && product()?.discount_price) {
14432
14405
  <span class="text-sm text-clx-text-subtle line-through">
14433
- {{ product()?.discount_price | currency:'COP':'symbol':'1.0-0' }}
14406
+ {{ product()?.discount_price | currency:'COP':'$':'1.0-0' }}
14434
14407
  </span>
14435
14408
  }
14436
14409
  </div>
@@ -14448,7 +14421,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14448
14421
  iconPosition="left"
14449
14422
  [disabled]="_disabled()"
14450
14423
  [block]="true"
14451
- (click)="_hasMultipleOptions() ? viewOptions.emit(product()!) : addToCart.emit(product()!)"
14424
+ (click)="$event.stopPropagation(); _hasMultipleOptions() ? viewOptions.emit(product()!) : addToCart.emit(product()!)"
14452
14425
  >
14453
14426
  {{ _hasMultipleOptions() ? 'Ver opciones' : 'Añadir al carrito' }}
14454
14427
  </button>
@@ -14468,6 +14441,9 @@ class ClxProductDetailComponent {
14468
14441
  clxColor = input('indigo', ...(ngDevMode ? [{ debugName: "clxColor" }] : /* istanbul ignore next */ []));
14469
14442
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
14470
14443
  currency = input('COP', ...(ngDevMode ? [{ debugName: "currency" }] : /* istanbul ignore next */ []));
14444
+ /** Hides the tabs (Características/Especificaciones/Descripción) and the Reviews section —
14445
+ * for contexts like a quick-view modal where only the gallery + spec selector + CTA matter. */
14446
+ compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []));
14471
14447
  addToCart = output();
14472
14448
  buyNow = output();
14473
14449
  wishlist = output();
@@ -14556,7 +14532,7 @@ class ClxProductDetailComponent {
14556
14532
  specsLeft(specs) { return specs.slice(0, Math.ceil(specs.length / 2)); }
14557
14533
  specsRight(specs) { return specs.slice(Math.ceil(specs.length / 2)); }
14558
14534
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14559
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProductDetailComponent, isStandalone: true, selector: "clx-product-detail", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, initialVariationId: { classPropertyName: "initialVariationId", publicName: "initialVariationId", isSignal: true, isRequired: false, transformFunction: null }, clxColor: { classPropertyName: "clxColor", publicName: "clxColor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { addToCart: "addToCart", buyNow: "buyNow", wishlist: "wishlist", variationChange: "variationChange" }, viewQueries: [{ propertyName: "_carousel", first: true, predicate: ClxCarouselComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 md:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2014 each column sizes to its own content (no forced equal\n height, no position:sticky). A `sticky` gallery reads great in isolation, but a host app\n that already has its own `position: sticky` header (a full-width top bar, as opposed to\n this library's own `clx-app-layout` sidebar, which is `fixed` and never competes) forces\n the browser to keep recomputing both stickies' viewport-relative offsets together on every\n resize \u2014 confirmed as the source of a persistent resize-lag bug in codexly-store's product\n page. Not worth reintroducing until sticky positioning is made robust to an arbitrary host\n header. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"relative aspect-4/3\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <ng-template clxSlide>\n <div class=\"w-full h-full p-5 sm:p-12.5 md:p-5 lg:p-12.5\">\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-contain\"\n [loading]=\"i === 0 ? 'eager' : 'lazy'\"\n [attr.fetchpriority]=\"i === 0 ? 'high' : null\" />\n </div>\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Agregar a favoritos\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\"\n (click)=\"wishlist.emit()\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-2.5\">\n\n <!-- Stock status -->\n @if (v) {\n <span clx-badge [variant]=\"v.in_stock ? 'light' : 'solid'\" [color]=\"v.in_stock ? clxColor() : 'green'\" size=\"sm\" shape=\"pill\">\n @if (v.in_stock) {\n @if (v.stock_quantity != null) {\n {{ v.stock_quantity }} en stock\n } @else {\n En stock\n }\n } @else {\n Sin stock\n }\n </span>\n }\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-1\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-sm text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ v?.name || p.product_name }}</h1>\n @if (p.product_detail) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ p.product_detail }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div>\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-2xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-base text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) \u2014 opciones no disponibles\n para la combinaci\u00F3n actual (ej. Talla S con Color Rojo) se ocultan por completo en vez\n de mostrarse deshabilitadas; reaparecen si el usuario cambia a una combinaci\u00F3n donde s\u00ED\n aplican, ya que isOptionAvailable() se reeval\u00FAa contra selectedSpecs() en cada cambio. -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button\n type=\"button\"\n class=\"w-7 h-7 rounded-full border-2 transition-[transform,box-shadow] shrink-0 cursor-pointer\"\n [class]=\"isSpecSelected(specType.id, opt.id) ? 'scale-110 shadow-md' : 'opacity-90 hover:opacity-100'\"\n [style.background-color]=\"opt.value\"\n [style.border-color]=\"isSpecSelected(specType.id, opt.id) ? 'var(--clx-text-label, #111)' : 'transparent'\"\n [clxTooltip]=\"opt.label ?? opt.value\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button clx-button\n type=\"button\"\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.label ?? opt.value }}\n </button>\n }\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitBuyNow()\">Comprar ahora</button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitAddToCart()\">Agrega al carrito</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxButtonGroupComponent, selector: "clx-button-group", inputs: ["label", "shape", "attached", "orientation"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxRatingComponent, selector: "clx-rating", inputs: ["value", "max", "color"] }, { kind: "component", type: ClxCarouselComponent, selector: "clx-carousel", inputs: ["clxColor", "autoPlay", "interval", "loop", "aspectRatio", "transparent"] }, { kind: "directive", type: ClxCarouselDirective, selector: "[clxSlide]" }, { kind: "component", type: ClxTabsComponent, selector: "clx-tabs", inputs: ["tabs", "clxColor", "activeTab"], outputs: ["activeTabChange"] }, { kind: "directive", type: ClxTabDirective, selector: "[clxTabPanel]", inputs: ["clxTabPanel"] }, { kind: "component", type: ClxNumberComponent, selector: "clx-number", inputs: ["variant", "size", "color", "label", "placeholder", "hint", "prefixIcon", "min", "max", "step", "value", "disabled"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "component", type: ClxListComponent, selector: "clx-list", inputs: ["color", "variant", "size", "numbered"] }, { kind: "component", type: ClxListItemComponent, selector: "clx-list-item", inputs: ["label", "sublabel", "icon", "meta", "metaColor", "disabled"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
14535
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProductDetailComponent, isStandalone: true, selector: "clx-product-detail", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, initialVariationId: { classPropertyName: "initialVariationId", publicName: "initialVariationId", isSignal: true, isRequired: false, transformFunction: null }, clxColor: { classPropertyName: "clxColor", publicName: "clxColor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { addToCart: "addToCart", buyNow: "buyNow", wishlist: "wishlist", variationChange: "variationChange" }, viewQueries: [{ propertyName: "_carousel", first: true, predicate: ClxCarouselComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 md:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2014 each column sizes to its own content (no forced equal\n height, no position:sticky). A `sticky` gallery reads great in isolation, but a host app\n that already has its own `position: sticky` header (a full-width top bar, as opposed to\n this library's own `clx-app-layout` sidebar, which is `fixed` and never competes) forces\n the browser to keep recomputing both stickies' viewport-relative offsets together on every\n resize \u2014 confirmed as the source of a persistent resize-lag bug in codexly-store's product\n page. Not worth reintroducing until sticky positioning is made robust to an arbitrary host\n header. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"relative aspect-4/3\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <ng-template clxSlide>\n <div class=\"w-full h-full p-5 sm:p-12.5 md:p-5 lg:p-12.5\">\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-contain\"\n [loading]=\"i === 0 ? 'eager' : 'lazy'\"\n [attr.fetchpriority]=\"i === 0 ? 'high' : null\" />\n </div>\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Agregar a favoritos\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\"\n (click)=\"wishlist.emit()\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-2.5\">\n\n <!-- Stock status -->\n @if (v) {\n <span clx-badge [variant]=\"v.in_stock ? 'light' : 'solid'\" [color]=\"v.in_stock ? clxColor() : 'green'\" size=\"sm\" shape=\"pill\">\n @if (v.in_stock) {\n @if (v.stock_quantity != null) {\n {{ v.stock_quantity }} en stock\n } @else {\n En stock\n }\n } @else {\n Sin stock\n }\n </span>\n }\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-1\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-sm text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ v?.name || p.product_name }}</h1>\n @if (p.product_detail) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ p.product_detail }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div>\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-2xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'$':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-base text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'$':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) \u2014 opciones no disponibles\n para la combinaci\u00F3n actual (ej. Talla S con Color Rojo) se ocultan por completo en vez\n de mostrarse deshabilitadas; reaparecen si el usuario cambia a una combinaci\u00F3n donde s\u00ED\n aplican, ya que isOptionAvailable() se reeval\u00FAa contra selectedSpecs() en cada cambio. -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button\n type=\"button\"\n class=\"w-7 h-7 rounded-full border-2 transition-[transform,box-shadow] shrink-0 cursor-pointer\"\n [class]=\"isSpecSelected(specType.id, opt.id) ? 'scale-110 shadow-md' : 'opacity-90 hover:opacity-100'\"\n [style.background-color]=\"opt.value\"\n [style.border-color]=\"isSpecSelected(specType.id, opt.id) ? 'var(--clx-text-label, #111)' : 'transparent'\"\n [clxTooltip]=\"opt.label ?? opt.value\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button clx-button\n type=\"button\"\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.label ?? opt.value }}\n </button>\n }\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitBuyNow()\">Comprar ahora</button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitAddToCart()\">Agrega al carrito</button>\n </div>\n\n </div>\n </div>\n\n @if (!compact()) {\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n }\n\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxButtonGroupComponent, selector: "clx-button-group", inputs: ["label", "shape", "attached", "orientation"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxRatingComponent, selector: "clx-rating", inputs: ["value", "max", "color"] }, { kind: "component", type: ClxCarouselComponent, selector: "clx-carousel", inputs: ["clxColor", "autoPlay", "interval", "loop", "aspectRatio", "transparent"] }, { kind: "directive", type: ClxCarouselDirective, selector: "[clxSlide]" }, { kind: "component", type: ClxTabsComponent, selector: "clx-tabs", inputs: ["tabs", "clxColor", "activeTab"], outputs: ["activeTabChange"] }, { kind: "directive", type: ClxTabDirective, selector: "[clxTabPanel]", inputs: ["clxTabPanel"] }, { kind: "component", type: ClxNumberComponent, selector: "clx-number", inputs: ["variant", "size", "color", "label", "placeholder", "hint", "prefixIcon", "min", "max", "step", "value", "disabled"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "component", type: ClxListComponent, selector: "clx-list", inputs: ["color", "variant", "size", "numbered"] }, { kind: "component", type: ClxListItemComponent, selector: "clx-list-item", inputs: ["label", "sublabel", "icon", "meta", "metaColor", "disabled"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
14560
14536
  }
14561
14537
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, decorators: [{
14562
14538
  type: Component,
@@ -14578,8 +14554,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14578
14554
  ClxTooltipDirective,
14579
14555
  ClxListComponent,
14580
14556
  ClxListItemComponent,
14581
- ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 md:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2014 each column sizes to its own content (no forced equal\n height, no position:sticky). A `sticky` gallery reads great in isolation, but a host app\n that already has its own `position: sticky` header (a full-width top bar, as opposed to\n this library's own `clx-app-layout` sidebar, which is `fixed` and never competes) forces\n the browser to keep recomputing both stickies' viewport-relative offsets together on every\n resize \u2014 confirmed as the source of a persistent resize-lag bug in codexly-store's product\n page. Not worth reintroducing until sticky positioning is made robust to an arbitrary host\n header. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"relative aspect-4/3\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <ng-template clxSlide>\n <div class=\"w-full h-full p-5 sm:p-12.5 md:p-5 lg:p-12.5\">\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-contain\"\n [loading]=\"i === 0 ? 'eager' : 'lazy'\"\n [attr.fetchpriority]=\"i === 0 ? 'high' : null\" />\n </div>\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Agregar a favoritos\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\"\n (click)=\"wishlist.emit()\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-2.5\">\n\n <!-- Stock status -->\n @if (v) {\n <span clx-badge [variant]=\"v.in_stock ? 'light' : 'solid'\" [color]=\"v.in_stock ? clxColor() : 'green'\" size=\"sm\" shape=\"pill\">\n @if (v.in_stock) {\n @if (v.stock_quantity != null) {\n {{ v.stock_quantity }} en stock\n } @else {\n En stock\n }\n } @else {\n Sin stock\n }\n </span>\n }\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-1\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-sm text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ v?.name || p.product_name }}</h1>\n @if (p.product_detail) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ p.product_detail }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div>\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-2xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-base text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) \u2014 opciones no disponibles\n para la combinaci\u00F3n actual (ej. Talla S con Color Rojo) se ocultan por completo en vez\n de mostrarse deshabilitadas; reaparecen si el usuario cambia a una combinaci\u00F3n donde s\u00ED\n aplican, ya que isOptionAvailable() se reeval\u00FAa contra selectedSpecs() en cada cambio. -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button\n type=\"button\"\n class=\"w-7 h-7 rounded-full border-2 transition-[transform,box-shadow] shrink-0 cursor-pointer\"\n [class]=\"isSpecSelected(specType.id, opt.id) ? 'scale-110 shadow-md' : 'opacity-90 hover:opacity-100'\"\n [style.background-color]=\"opt.value\"\n [style.border-color]=\"isSpecSelected(specType.id, opt.id) ? 'var(--clx-text-label, #111)' : 'transparent'\"\n [clxTooltip]=\"opt.label ?? opt.value\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button clx-button\n type=\"button\"\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.label ?? opt.value }}\n </button>\n }\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitBuyNow()\">Comprar ahora</button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitAddToCart()\">Agrega al carrito</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n" }]
14582
- }], ctorParameters: () => [], propDecorators: { product: [{ type: i0.Input, args: [{ isSignal: true, alias: "product", required: false }] }], initialVariationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialVariationId", required: false }] }], clxColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "clxColor", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], addToCart: [{ type: i0.Output, args: ["addToCart"] }], buyNow: [{ type: i0.Output, args: ["buyNow"] }], wishlist: [{ type: i0.Output, args: ["wishlist"] }], variationChange: [{ type: i0.Output, args: ["variationChange"] }], _carousel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClxCarouselComponent), { isSignal: true }] }] } });
14557
+ ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-8 md:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2014 each column sizes to its own content (no forced equal\n height, no position:sticky). A `sticky` gallery reads great in isolation, but a host app\n that already has its own `position: sticky` header (a full-width top bar, as opposed to\n this library's own `clx-app-layout` sidebar, which is `fixed` and never competes) forces\n the browser to keep recomputing both stickies' viewport-relative offsets together on every\n resize \u2014 confirmed as the source of a persistent resize-lag bug in codexly-store's product\n page. Not worth reintroducing until sticky positioning is made robust to an arbitrary host\n header. \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"flex flex-col gap-3\">\n <div class=\"relative aspect-4/3\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <ng-template clxSlide>\n <div class=\"w-full h-full p-5 sm:p-12.5 md:p-5 lg:p-12.5\">\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-contain\"\n [loading]=\"i === 0 ? 'eager' : 'lazy'\"\n [attr.fetchpriority]=\"i === 0 ? 'high' : null\" />\n </div>\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Agregar a favoritos\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\"\n (click)=\"wishlist.emit()\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track i; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-2.5\">\n\n <!-- Stock status -->\n @if (v) {\n <span clx-badge [variant]=\"v.in_stock ? 'light' : 'solid'\" [color]=\"v.in_stock ? clxColor() : 'green'\" size=\"sm\" shape=\"pill\">\n @if (v.in_stock) {\n @if (v.stock_quantity != null) {\n {{ v.stock_quantity }} en stock\n } @else {\n En stock\n }\n } @else {\n Sin stock\n }\n </span>\n }\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-1\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-sm text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ v?.name || p.product_name }}</h1>\n @if (p.product_detail) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ p.product_detail }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div>\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-2xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'$':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-base text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'$':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) \u2014 opciones no disponibles\n para la combinaci\u00F3n actual (ej. Talla S con Color Rojo) se ocultan por completo en vez\n de mostrarse deshabilitadas; reaparecen si el usuario cambia a una combinaci\u00F3n donde s\u00ED\n aplican, ya que isOptionAvailable() se reeval\u00FAa contra selectedSpecs() en cada cambio. -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button\n type=\"button\"\n class=\"w-7 h-7 rounded-full border-2 transition-[transform,box-shadow] shrink-0 cursor-pointer\"\n [class]=\"isSpecSelected(specType.id, opt.id) ? 'scale-110 shadow-md' : 'opacity-90 hover:opacity-100'\"\n [style.background-color]=\"opt.value\"\n [style.border-color]=\"isSpecSelected(specType.id, opt.id) ? 'var(--clx-text-label, #111)' : 'transparent'\"\n [clxTooltip]=\"opt.label ?? opt.value\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n @if (isOptionAvailable(specType.id, opt.id)) {\n <button clx-button\n type=\"button\"\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.label ?? opt.value }}\n </button>\n }\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitBuyNow()\">Comprar ahora</button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\" (click)=\"_emitAddToCart()\">Agrega al carrito</button>\n </div>\n\n </div>\n </div>\n\n @if (!compact()) {\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [icon]=\"spec.icon ?? ''\">\n <div clxContent>\n <p class=\"text-sm font-medium text-clx-text-label\">{{ spec.name }}:</p>\n <p class=\"text-sm text-clx-text-muted mt-0.5\">{{ spec.value }}</p>\n </div>\n </clx-list-item>\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button type=\"button\" variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button type=\"button\" variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button type=\"button\" variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n }\n\n </div>\n}\n" }]
14558
+ }], ctorParameters: () => [], propDecorators: { product: [{ type: i0.Input, args: [{ isSignal: true, alias: "product", required: false }] }], initialVariationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialVariationId", required: false }] }], clxColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "clxColor", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], addToCart: [{ type: i0.Output, args: ["addToCart"] }], buyNow: [{ type: i0.Output, args: ["buyNow"] }], wishlist: [{ type: i0.Output, args: ["wishlist"] }], variationChange: [{ type: i0.Output, args: ["variationChange"] }], _carousel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClxCarouselComponent), { isSignal: true }] }] } });
14559
+
14560
+ class ClxProductQuickViewComponent {
14561
+ ref = inject(CLX_MODAL_REF, { optional: true });
14562
+ data = inject(CLX_MODAL_DATA, { optional: true });
14563
+ _onAddToCart(event) {
14564
+ this.ref?.close(event);
14565
+ }
14566
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductQuickViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14567
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: ClxProductQuickViewComponent, isStandalone: true, selector: "clx-product-quick-view", ngImport: i0, template: `
14568
+ <clx-modal>
14569
+ <div clx-modal-title>{{ data?.product?.product_name }}</div>
14570
+
14571
+ <clx-product-detail
14572
+ [compact]="true"
14573
+ [product]="data?.product ?? null"
14574
+ [initialVariationId]="data?.initialVariationId ?? null"
14575
+ [clxColor]="data?.clxColor ?? 'indigo'"
14576
+ [currency]="data?.currency ?? 'COP'"
14577
+ (addToCart)="_onAddToCart($event)">
14578
+ </clx-product-detail>
14579
+ </clx-modal>
14580
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxModalComponent, selector: "clx-modal", inputs: ["size", "showClose", "confirmButton", "cancelButton", "showCancelButton"], outputs: ["confirmClick", "cancelClick"] }, { kind: "component", type: ClxProductDetailComponent, selector: "clx-product-detail", inputs: ["product", "initialVariationId", "clxColor", "loading", "currency", "compact"], outputs: ["addToCart", "buyNow", "wishlist", "variationChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14581
+ }
14582
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductQuickViewComponent, decorators: [{
14583
+ type: Component,
14584
+ args: [{
14585
+ selector: 'clx-product-quick-view',
14586
+ standalone: true,
14587
+ imports: [ClxModalComponent, ClxProductDetailComponent],
14588
+ template: `
14589
+ <clx-modal>
14590
+ <div clx-modal-title>{{ data?.product?.product_name }}</div>
14591
+
14592
+ <clx-product-detail
14593
+ [compact]="true"
14594
+ [product]="data?.product ?? null"
14595
+ [initialVariationId]="data?.initialVariationId ?? null"
14596
+ [clxColor]="data?.clxColor ?? 'indigo'"
14597
+ [currency]="data?.currency ?? 'COP'"
14598
+ (addToCart)="_onAddToCart($event)">
14599
+ </clx-product-detail>
14600
+ </clx-modal>
14601
+ `,
14602
+ changeDetection: ChangeDetectionStrategy.OnPush,
14603
+ }]
14604
+ }] });
14583
14605
 
14584
14606
  const WISHLIST_PAGE_SIZE_DEFAULT = 6;
14585
14607
  const WISHLIST_SORT_OPTIONS = [
@@ -14709,22 +14731,19 @@ class ClxWishlistComponent {
14709
14731
  <!-- ── Info Bar ─────────────────────────────────────────────────────── -->
14710
14732
  <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
14711
14733
  <div class="flex items-center gap-2">
14712
- <span class="text-clx-text-muted text-sm">Adding</span>
14713
- <span class="font-bold text-clx-text-label text-sm">{{ _totalItems() }}</span>
14714
- <span clx-badge variant="light" [color]="color()" size="sm">items</span>
14715
- <span class="text-clx-text-muted text-sm">to your wishlist</span>
14734
+ <h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
14735
+ <span clx-badge variant="light" [color]="color()" size="sm">{{ _totalItems() }}</span>
14716
14736
  </div>
14717
14737
  <button
14718
14738
  clx-button
14719
14739
  type="button"
14720
14740
  variant="light"
14721
14741
  [color]="color()"
14722
- size="sm"
14723
14742
  icon="arrow_forward"
14724
14743
  iconPosition="right"
14725
14744
  [disabled]="_totalItems() === 0"
14726
14745
  (click)="handleCheckoutAll()"
14727
- >Checkout All</button>
14746
+ >Agregar todo al carrito</button>
14728
14747
  </div>
14729
14748
 
14730
14749
  <!-- ── List ─────────────────────────────────────────────────────────── -->
@@ -14748,11 +14767,14 @@ class ClxWishlistComponent {
14748
14767
 
14749
14768
  } @else if (_pagedItems().length === 0) {
14750
14769
 
14751
- <div class="flex flex-col items-center justify-center py-20 gap-4 text-clx-text-subtle">
14752
- <span clx-icon name="heart_broken" size="xl"></span>
14753
- <p class="text-base font-medium">Your wishlist is empty</p>
14754
- <p class="text-sm">Start adding items you love!</p>
14755
- </div>
14770
+ <clx-page-empty
14771
+ icon="favorite"
14772
+ title="Sin favoritos"
14773
+ description="No tienes productos guardados."
14774
+ [ctaColor]="color()"
14775
+ ctaIcon="storefront"
14776
+ ctaLabel="Ir a la tienda">
14777
+ </clx-page-empty>
14756
14778
 
14757
14779
  } @else {
14758
14780
 
@@ -14796,10 +14818,10 @@ class ClxWishlistComponent {
14796
14818
 
14797
14819
  <!-- Price -->
14798
14820
  <div class="flex items-baseline gap-2 mt-2">
14799
- <span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'symbol':'1.0-0' }}</span>
14821
+ <span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'$':'1.0-0' }}</span>
14800
14822
  @if (item.has_discount && item.discount_price) {
14801
14823
  <span class="text-sm text-clx-text-subtle line-through">
14802
- {{ item.discount_price | currency:'COP':'symbol':'1.0-0' }}
14824
+ {{ item.discount_price | currency:'COP':'$':'1.0-0' }}
14803
14825
  </span>
14804
14826
  }
14805
14827
  </div>
@@ -14861,7 +14883,7 @@ class ClxWishlistComponent {
14861
14883
  }
14862
14884
 
14863
14885
  </div>
14864
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSelectComponent, selector: "clx-select", inputs: ["label", "placeholder", "options", "multiple", "searchable", "searchPlaceholder", "activeColor", "size", "hint", "errorMessage", "disabled", "async", "loadOptions", "debounceMs", "minChars"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
14886
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSelectComponent, selector: "clx-select", inputs: ["label", "placeholder", "options", "multiple", "searchable", "searchPlaceholder", "activeColor", "size", "hint", "errorMessage", "disabled", "async", "loadOptions", "debounceMs", "minChars"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor"], outputs: ["cta"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
14865
14887
  }
14866
14888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWishlistComponent, decorators: [{
14867
14889
  type: Component,
@@ -14873,12 +14895,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14873
14895
  FormsModule,
14874
14896
  ClxButtonComponent,
14875
14897
  ClxBadgeComponent,
14876
- ClxIconComponent,
14877
14898
  ClxSkeletonComponent,
14878
14899
  ClxPaginationComponent,
14879
14900
  ClxInputComponent,
14880
14901
  ClxSelectComponent,
14881
- ClxTooltipDirective
14902
+ ClxTooltipDirective,
14903
+ ClxPageEmptyComponent
14882
14904
  ],
14883
14905
  template: `
14884
14906
  <div class="flex flex-col gap-6">
@@ -14911,22 +14933,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14911
14933
  <!-- ── Info Bar ─────────────────────────────────────────────────────── -->
14912
14934
  <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
14913
14935
  <div class="flex items-center gap-2">
14914
- <span class="text-clx-text-muted text-sm">Adding</span>
14915
- <span class="font-bold text-clx-text-label text-sm">{{ _totalItems() }}</span>
14916
- <span clx-badge variant="light" [color]="color()" size="sm">items</span>
14917
- <span class="text-clx-text-muted text-sm">to your wishlist</span>
14936
+ <h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
14937
+ <span clx-badge variant="light" [color]="color()" size="sm">{{ _totalItems() }}</span>
14918
14938
  </div>
14919
14939
  <button
14920
14940
  clx-button
14921
14941
  type="button"
14922
14942
  variant="light"
14923
14943
  [color]="color()"
14924
- size="sm"
14925
14944
  icon="arrow_forward"
14926
14945
  iconPosition="right"
14927
14946
  [disabled]="_totalItems() === 0"
14928
14947
  (click)="handleCheckoutAll()"
14929
- >Checkout All</button>
14948
+ >Agregar todo al carrito</button>
14930
14949
  </div>
14931
14950
 
14932
14951
  <!-- ── List ─────────────────────────────────────────────────────────── -->
@@ -14950,11 +14969,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14950
14969
 
14951
14970
  } @else if (_pagedItems().length === 0) {
14952
14971
 
14953
- <div class="flex flex-col items-center justify-center py-20 gap-4 text-clx-text-subtle">
14954
- <span clx-icon name="heart_broken" size="xl"></span>
14955
- <p class="text-base font-medium">Your wishlist is empty</p>
14956
- <p class="text-sm">Start adding items you love!</p>
14957
- </div>
14972
+ <clx-page-empty
14973
+ icon="favorite"
14974
+ title="Sin favoritos"
14975
+ description="No tienes productos guardados."
14976
+ [ctaColor]="color()"
14977
+ ctaIcon="storefront"
14978
+ ctaLabel="Ir a la tienda">
14979
+ </clx-page-empty>
14958
14980
 
14959
14981
  } @else {
14960
14982
 
@@ -14998,10 +15020,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14998
15020
 
14999
15021
  <!-- Price -->
15000
15022
  <div class="flex items-baseline gap-2 mt-2">
15001
- <span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'symbol':'1.0-0' }}</span>
15023
+ <span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'$':'1.0-0' }}</span>
15002
15024
  @if (item.has_discount && item.discount_price) {
15003
15025
  <span class="text-sm text-clx-text-subtle line-through">
15004
- {{ item.discount_price | currency:'COP':'symbol':'1.0-0' }}
15026
+ {{ item.discount_price | currency:'COP':'$':'1.0-0' }}
15005
15027
  </span>
15006
15028
  }
15007
15029
  </div>
@@ -15141,11 +15163,14 @@ class ClxCartComponent {
15141
15163
  <!-- ── Empty ───────────────────────────────────────────────────────────────── -->
15142
15164
  } @else if (items().length === 0) {
15143
15165
 
15144
- <div class="flex flex-col items-center justify-center py-24 gap-4 text-clx-text-subtle">
15145
- <span clx-icon name="shopping_cart" size="xl"></span>
15146
- <p class="text-base font-semibold text-clx-text-muted">Tu carrito está vacío</p>
15147
- <p class="text-sm">Agrega productos para continuar.</p>
15148
- </div>
15166
+ <clx-page-empty
15167
+ icon="remove_shopping_cart"
15168
+ title="Carrito vacio"
15169
+ description="Tu carrito esta vacio. Agrega productos para continuar."
15170
+ [ctaColor]="color()"
15171
+ ctaIcon="storefront"
15172
+ ctaLabel="Ir a la tienda">
15173
+ </clx-page-empty>
15149
15174
 
15150
15175
  <!-- ── Main layout ─────────────────────────────────────────────────────────── -->
15151
15176
  } @else {
@@ -15158,19 +15183,19 @@ class ClxCartComponent {
15158
15183
  <!-- Top bar -->
15159
15184
  <div class="flex items-center justify-between">
15160
15185
  <div class="flex items-center gap-2">
15161
- <h2 class="text-base font-semibold text-clx-text-label">Cart Items</h2>
15186
+ <h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
15162
15187
  <span clx-badge variant="light" [color]="color()" size="sm">{{ _totalUnits() }}</span>
15163
15188
  </div>
15164
15189
  <button
15165
- clx-button type="button" variant="ghost" color="red" size="sm" icon="delete_sweep"
15190
+ clx-button type="button" variant="light" color="red" icon="delete"
15166
15191
  (click)="onClearCart.emit()">
15167
- Vaciar
15192
+ Vaciar carrito
15168
15193
  </button>
15169
15194
  </div>
15170
15195
 
15171
15196
  <!-- Item cards -->
15172
15197
  @for (item of items(); track item.id) {
15173
- <article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-xl hover:-translate-y-0.5">
15198
+ <article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-md hover:-translate-y-0.5">
15174
15199
 
15175
15200
  <!-- Image — fixed square -->
15176
15201
  <div class="relative shrink-0 w-28 h-28 lg:w-36 lg:h-36 overflow-hidden bg-clx-surface-2">
@@ -15215,54 +15240,29 @@ class ClxCartComponent {
15215
15240
  <p class="text-xs font-mono text-clx-text-subtle mt-0.5">{{ item.sku }}</p>
15216
15241
  </div>
15217
15242
 
15218
- @if (item.rating) {
15219
- <div class="flex items-center gap-1.5">
15220
- <clx-rating [value]="item.rating" [max]="5" color="amber" />
15221
- @if (item.review_count) {
15222
- <span class="text-xs text-clx-text-subtle">({{ item.review_count }})</span>
15223
- }
15224
- </div>
15225
- }
15226
-
15227
15243
  <!-- Price + Quantity row -->
15228
15244
  <div class="flex items-center justify-between gap-2 mt-auto flex-wrap">
15229
15245
 
15230
15246
  <!-- Price -->
15231
15247
  <div class="flex items-baseline gap-1.5">
15232
15248
  <span class="text-base font-bold text-clx-text-label">
15233
- {{ item.unit_price | currency:'COP':'symbol':'1.0-0' }}
15249
+ {{ item.unit_price | currency:'COP':'$':'1.0-0' }}
15234
15250
  </span>
15235
15251
  @if (item.has_discount && item.original_price) {
15236
15252
  <span class="text-xs text-clx-text-subtle line-through">
15237
- {{ item.original_price | currency:'COP':'symbol':'1.0-0' }}
15253
+ {{ item.original_price | currency:'COP':'$':'1.0-0' }}
15238
15254
  </span>
15239
15255
  }
15240
15256
  </div>
15241
15257
 
15242
15258
  <!-- Quantity control -->
15243
- <div class="flex items-center border border-clx-border rounded-xl overflow-hidden">
15244
- <button
15245
- clx-button type="button" variant="ghost" color="slate" size="sm"
15246
- icon="remove" [iconOnly]="true"
15247
- [disabled]="item.quantity <= 1"
15248
- (click)="onQuantityChange.emit({ item, quantity: item.quantity - 1 })">
15249
- </button>
15250
- <span class="w-8 text-center text-sm font-semibold text-clx-text-label select-none">
15251
- {{ item.quantity }}
15252
- </span>
15253
- <button
15254
- clx-button type="button" variant="ghost" [color]="color()" size="sm"
15255
- icon="add" [iconOnly]="true"
15256
- [disabled]="!item.in_stock || (!!item.max_quantity && item.quantity >= item.max_quantity)"
15257
- (click)="onQuantityChange.emit({ item, quantity: item.quantity + 1 })">
15258
- </button>
15259
- </div>
15260
-
15261
- <!-- Line total -->
15262
- <p class="text-sm font-bold text-clx-text-label">
15263
- {{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
15264
- </p>
15265
-
15259
+ <clx-number
15260
+ variant="stepper" size="sm" [color]="color()"
15261
+ [min]="1" [max]="item.max_quantity ?? null"
15262
+ [disabled]="!item.in_stock"
15263
+ [ngModel]="item.quantity"
15264
+ (ngModelChange)="onQuantityChange.emit({ item, quantity: $event })">
15265
+ </clx-number>
15266
15266
  </div>
15267
15267
  </div>
15268
15268
 
@@ -15327,12 +15327,12 @@ class ClxCartComponent {
15327
15327
  <dl class="flex flex-col gap-2.5 text-sm">
15328
15328
  <div class="flex justify-between text-clx-text-muted">
15329
15329
  <dt>Subtotal <span class="text-clx-text-subtle text-xs">({{ _totalUnits() }} items)</span></dt>
15330
- <dd class="font-medium">{{ _subtotal() | currency:'COP':'symbol':'1.0-0' }}</dd>
15330
+ <dd class="font-medium">{{ _subtotal() | currency:'COP':'$':'1.0-0' }}</dd>
15331
15331
  </div>
15332
15332
  @if (_savingsAmount() > 0) {
15333
15333
  <div class="flex justify-between text-emerald-600">
15334
15334
  <dt>Descuento productos</dt>
15335
- <dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'symbol':'1.0-0' }}</dd>
15335
+ <dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'$':'1.0-0' }}</dd>
15336
15336
  </div>
15337
15337
  }
15338
15338
  @if (coupon()?.valid) {
@@ -15341,18 +15341,18 @@ class ClxCartComponent {
15341
15341
  Cupón
15342
15342
  <code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ coupon()!.code }}</code>
15343
15343
  </dt>
15344
- <dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'symbol':'1.0-0' }}</dd>
15344
+ <dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'$':'1.0-0' }}</dd>
15345
15345
  </div>
15346
15346
  }
15347
15347
  <div class="flex justify-between border-t border-clx-border-soft pt-2.5">
15348
15348
  <dt class="font-bold text-clx-text-label">Total</dt>
15349
- <dd class="text-xl font-black text-clx-text-label">{{ _total() | currency:'COP':'symbol':'1.0-0' }}</dd>
15349
+ <dd class="text-lg font-black text-clx-text-label">{{ _total() | currency:'COP':'$':'1.0-0' }}</dd>
15350
15350
  </div>
15351
15351
  </dl>
15352
15352
 
15353
15353
  <!-- Checkout -->
15354
15354
  <button
15355
- clx-button type="button" [color]="color()" variant="solid" size="lg"
15355
+ clx-button type="button" [color]="color()" variant="solid" size="md"
15356
15356
  icon="arrow_forward" iconPosition="right" [block]="true"
15357
15357
  (click)="onCheckout.emit()">
15358
15358
  Proceder al pago
@@ -15371,7 +15371,7 @@ class ClxCartComponent {
15371
15371
  </div>
15372
15372
 
15373
15373
  }
15374
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxRatingComponent, selector: "clx-rating", inputs: ["value", "max", "color"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
15374
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "textColor", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxNumberComponent, selector: "clx-number", inputs: ["variant", "size", "color", "label", "placeholder", "hint", "prefixIcon", "min", "max", "step", "value", "disabled"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor"], outputs: ["cta"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
15375
15375
  }
15376
15376
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCartComponent, decorators: [{
15377
15377
  type: Component,
@@ -15386,7 +15386,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15386
15386
  ClxIconComponent,
15387
15387
  ClxSkeletonComponent,
15388
15388
  ClxInputComponent,
15389
- ClxRatingComponent,
15389
+ ClxNumberComponent,
15390
+ ClxPageEmptyComponent
15390
15391
  ],
15391
15392
  encapsulation: ViewEncapsulation.None,
15392
15393
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -15421,11 +15422,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15421
15422
  <!-- ── Empty ───────────────────────────────────────────────────────────────── -->
15422
15423
  } @else if (items().length === 0) {
15423
15424
 
15424
- <div class="flex flex-col items-center justify-center py-24 gap-4 text-clx-text-subtle">
15425
- <span clx-icon name="shopping_cart" size="xl"></span>
15426
- <p class="text-base font-semibold text-clx-text-muted">Tu carrito está vacío</p>
15427
- <p class="text-sm">Agrega productos para continuar.</p>
15428
- </div>
15425
+ <clx-page-empty
15426
+ icon="remove_shopping_cart"
15427
+ title="Carrito vacio"
15428
+ description="Tu carrito esta vacio. Agrega productos para continuar."
15429
+ [ctaColor]="color()"
15430
+ ctaIcon="storefront"
15431
+ ctaLabel="Ir a la tienda">
15432
+ </clx-page-empty>
15429
15433
 
15430
15434
  <!-- ── Main layout ─────────────────────────────────────────────────────────── -->
15431
15435
  } @else {
@@ -15438,19 +15442,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15438
15442
  <!-- Top bar -->
15439
15443
  <div class="flex items-center justify-between">
15440
15444
  <div class="flex items-center gap-2">
15441
- <h2 class="text-base font-semibold text-clx-text-label">Cart Items</h2>
15445
+ <h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
15442
15446
  <span clx-badge variant="light" [color]="color()" size="sm">{{ _totalUnits() }}</span>
15443
15447
  </div>
15444
15448
  <button
15445
- clx-button type="button" variant="ghost" color="red" size="sm" icon="delete_sweep"
15449
+ clx-button type="button" variant="light" color="red" icon="delete"
15446
15450
  (click)="onClearCart.emit()">
15447
- Vaciar
15451
+ Vaciar carrito
15448
15452
  </button>
15449
15453
  </div>
15450
15454
 
15451
15455
  <!-- Item cards -->
15452
15456
  @for (item of items(); track item.id) {
15453
- <article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-xl hover:-translate-y-0.5">
15457
+ <article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-md hover:-translate-y-0.5">
15454
15458
 
15455
15459
  <!-- Image — fixed square -->
15456
15460
  <div class="relative shrink-0 w-28 h-28 lg:w-36 lg:h-36 overflow-hidden bg-clx-surface-2">
@@ -15495,54 +15499,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15495
15499
  <p class="text-xs font-mono text-clx-text-subtle mt-0.5">{{ item.sku }}</p>
15496
15500
  </div>
15497
15501
 
15498
- @if (item.rating) {
15499
- <div class="flex items-center gap-1.5">
15500
- <clx-rating [value]="item.rating" [max]="5" color="amber" />
15501
- @if (item.review_count) {
15502
- <span class="text-xs text-clx-text-subtle">({{ item.review_count }})</span>
15503
- }
15504
- </div>
15505
- }
15506
-
15507
15502
  <!-- Price + Quantity row -->
15508
15503
  <div class="flex items-center justify-between gap-2 mt-auto flex-wrap">
15509
15504
 
15510
15505
  <!-- Price -->
15511
15506
  <div class="flex items-baseline gap-1.5">
15512
15507
  <span class="text-base font-bold text-clx-text-label">
15513
- {{ item.unit_price | currency:'COP':'symbol':'1.0-0' }}
15508
+ {{ item.unit_price | currency:'COP':'$':'1.0-0' }}
15514
15509
  </span>
15515
15510
  @if (item.has_discount && item.original_price) {
15516
15511
  <span class="text-xs text-clx-text-subtle line-through">
15517
- {{ item.original_price | currency:'COP':'symbol':'1.0-0' }}
15512
+ {{ item.original_price | currency:'COP':'$':'1.0-0' }}
15518
15513
  </span>
15519
15514
  }
15520
15515
  </div>
15521
15516
 
15522
15517
  <!-- Quantity control -->
15523
- <div class="flex items-center border border-clx-border rounded-xl overflow-hidden">
15524
- <button
15525
- clx-button type="button" variant="ghost" color="slate" size="sm"
15526
- icon="remove" [iconOnly]="true"
15527
- [disabled]="item.quantity <= 1"
15528
- (click)="onQuantityChange.emit({ item, quantity: item.quantity - 1 })">
15529
- </button>
15530
- <span class="w-8 text-center text-sm font-semibold text-clx-text-label select-none">
15531
- {{ item.quantity }}
15532
- </span>
15533
- <button
15534
- clx-button type="button" variant="ghost" [color]="color()" size="sm"
15535
- icon="add" [iconOnly]="true"
15536
- [disabled]="!item.in_stock || (!!item.max_quantity && item.quantity >= item.max_quantity)"
15537
- (click)="onQuantityChange.emit({ item, quantity: item.quantity + 1 })">
15538
- </button>
15539
- </div>
15540
-
15541
- <!-- Line total -->
15542
- <p class="text-sm font-bold text-clx-text-label">
15543
- {{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
15544
- </p>
15545
-
15518
+ <clx-number
15519
+ variant="stepper" size="sm" [color]="color()"
15520
+ [min]="1" [max]="item.max_quantity ?? null"
15521
+ [disabled]="!item.in_stock"
15522
+ [ngModel]="item.quantity"
15523
+ (ngModelChange)="onQuantityChange.emit({ item, quantity: $event })">
15524
+ </clx-number>
15546
15525
  </div>
15547
15526
  </div>
15548
15527
 
@@ -15607,12 +15586,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15607
15586
  <dl class="flex flex-col gap-2.5 text-sm">
15608
15587
  <div class="flex justify-between text-clx-text-muted">
15609
15588
  <dt>Subtotal <span class="text-clx-text-subtle text-xs">({{ _totalUnits() }} items)</span></dt>
15610
- <dd class="font-medium">{{ _subtotal() | currency:'COP':'symbol':'1.0-0' }}</dd>
15589
+ <dd class="font-medium">{{ _subtotal() | currency:'COP':'$':'1.0-0' }}</dd>
15611
15590
  </div>
15612
15591
  @if (_savingsAmount() > 0) {
15613
15592
  <div class="flex justify-between text-emerald-600">
15614
15593
  <dt>Descuento productos</dt>
15615
- <dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'symbol':'1.0-0' }}</dd>
15594
+ <dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'$':'1.0-0' }}</dd>
15616
15595
  </div>
15617
15596
  }
15618
15597
  @if (coupon()?.valid) {
@@ -15621,18 +15600,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15621
15600
  Cupón
15622
15601
  <code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ coupon()!.code }}</code>
15623
15602
  </dt>
15624
- <dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'symbol':'1.0-0' }}</dd>
15603
+ <dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'$':'1.0-0' }}</dd>
15625
15604
  </div>
15626
15605
  }
15627
15606
  <div class="flex justify-between border-t border-clx-border-soft pt-2.5">
15628
15607
  <dt class="font-bold text-clx-text-label">Total</dt>
15629
- <dd class="text-xl font-black text-clx-text-label">{{ _total() | currency:'COP':'symbol':'1.0-0' }}</dd>
15608
+ <dd class="text-lg font-black text-clx-text-label">{{ _total() | currency:'COP':'$':'1.0-0' }}</dd>
15630
15609
  </div>
15631
15610
  </dl>
15632
15611
 
15633
15612
  <!-- Checkout -->
15634
15613
  <button
15635
- clx-button type="button" [color]="color()" variant="solid" size="lg"
15614
+ clx-button type="button" [color]="color()" variant="solid" size="md"
15636
15615
  icon="arrow_forward" iconPosition="right" [block]="true"
15637
15616
  (click)="onCheckout.emit()">
15638
15617
  Proceder al pago
@@ -15695,7 +15674,7 @@ class ClxCartSummaryDrawer {
15695
15674
  <p class="text-xs text-clx-text-subtle">x{{ item.quantity }}</p>
15696
15675
  </div>
15697
15676
  <span class="text-sm font-semibold text-clx-text-label shrink-0">
15698
- {{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
15677
+ {{ (item.unit_price * item.quantity) | currency:'COP':'$':'1.0-0' }}
15699
15678
  </span>
15700
15679
  </li>
15701
15680
  }
@@ -15708,12 +15687,12 @@ class ClxCartSummaryDrawer {
15708
15687
  <dl class="space-y-2">
15709
15688
  <div class="flex justify-between text-sm text-clx-text-muted">
15710
15689
  <dt>Subtotal</dt>
15711
- <dd class="font-medium">{{ data.subtotal | currency:'COP':'symbol':'1.0-0' }}</dd>
15690
+ <dd class="font-medium">{{ data.subtotal | currency:'COP':'$':'1.0-0' }}</dd>
15712
15691
  </div>
15713
15692
  @if (data.savingsAmount > 0) {
15714
15693
  <div class="flex justify-between text-sm text-emerald-600">
15715
15694
  <dt>Descuento</dt>
15716
- <dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'symbol':'1.0-0' }}</dd>
15695
+ <dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'$':'1.0-0' }}</dd>
15717
15696
  </div>
15718
15697
  }
15719
15698
  @if (data.coupon?.valid) {
@@ -15722,12 +15701,12 @@ class ClxCartSummaryDrawer {
15722
15701
  Cupón
15723
15702
  <code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ data.coupon!.code }}</code>
15724
15703
  </dt>
15725
- <dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'symbol':'1.0-0' }}</dd>
15704
+ <dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'$':'1.0-0' }}</dd>
15726
15705
  </div>
15727
15706
  }
15728
15707
  <div class="border-t border-clx-border-soft pt-2 flex justify-between">
15729
15708
  <dt class="font-bold text-clx-text-label">Total</dt>
15730
- <dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'symbol':'1.0-0' }}</dd>
15709
+ <dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'$':'1.0-0' }}</dd>
15731
15710
  </div>
15732
15711
  </dl>
15733
15712
 
@@ -15784,7 +15763,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15784
15763
  <p class="text-xs text-clx-text-subtle">x{{ item.quantity }}</p>
15785
15764
  </div>
15786
15765
  <span class="text-sm font-semibold text-clx-text-label shrink-0">
15787
- {{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
15766
+ {{ (item.unit_price * item.quantity) | currency:'COP':'$':'1.0-0' }}
15788
15767
  </span>
15789
15768
  </li>
15790
15769
  }
@@ -15797,12 +15776,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15797
15776
  <dl class="space-y-2">
15798
15777
  <div class="flex justify-between text-sm text-clx-text-muted">
15799
15778
  <dt>Subtotal</dt>
15800
- <dd class="font-medium">{{ data.subtotal | currency:'COP':'symbol':'1.0-0' }}</dd>
15779
+ <dd class="font-medium">{{ data.subtotal | currency:'COP':'$':'1.0-0' }}</dd>
15801
15780
  </div>
15802
15781
  @if (data.savingsAmount > 0) {
15803
15782
  <div class="flex justify-between text-sm text-emerald-600">
15804
15783
  <dt>Descuento</dt>
15805
- <dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'symbol':'1.0-0' }}</dd>
15784
+ <dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'$':'1.0-0' }}</dd>
15806
15785
  </div>
15807
15786
  }
15808
15787
  @if (data.coupon?.valid) {
@@ -15811,12 +15790,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15811
15790
  Cupón
15812
15791
  <code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ data.coupon!.code }}</code>
15813
15792
  </dt>
15814
- <dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'symbol':'1.0-0' }}</dd>
15793
+ <dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'$':'1.0-0' }}</dd>
15815
15794
  </div>
15816
15795
  }
15817
15796
  <div class="border-t border-clx-border-soft pt-2 flex justify-between">
15818
15797
  <dt class="font-bold text-clx-text-label">Total</dt>
15819
- <dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'symbol':'1.0-0' }}</dd>
15798
+ <dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'$':'1.0-0' }}</dd>
15820
15799
  </div>
15821
15800
  </dl>
15822
15801
 
@@ -16269,5 +16248,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
16269
16248
  * Generated bundle index. Do not edit.
16270
16249
  */
16271
16250
 
16272
- export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
16251
+ export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderActionsDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFabComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxMenuItemTrailingDirective, ClxModalComponent, ClxModalService, ClxNativeOverlayService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageHeaderComponent, ClxPageHeaderTitleDirective, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProductQuickViewComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSearchComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
16273
16252
  //# sourceMappingURL=codexly-ui.mjs.map