codexly-ui 0.10.58 → 0.10.60
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.
- package/fesm2022/codexly-ui.mjs +142 -204
- package/fesm2022/codexly-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/codexly-ui.d.ts +7 -9
package/fesm2022/codexly-ui.mjs
CHANGED
|
@@ -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 }
|
|
13066
|
-
<div
|
|
13067
|
-
<span clx-icon [name]="icon()"
|
|
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
|
-
<
|
|
13045
|
+
<h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
|
|
13071
13046
|
|
|
13072
13047
|
@if (description()) {
|
|
13073
|
-
<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
|
|
13101
|
-
<span clx-icon [name]="icon()"
|
|
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
|
-
<
|
|
13078
|
+
<h1 class="text-base font-semibold text-clx-text-label mb-2">{{ title() }}</h1>
|
|
13105
13079
|
|
|
13106
13080
|
@if (description()) {
|
|
13107
|
-
<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 }] }],
|
|
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"
|
|
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-
|
|
13585
|
-
<h1 class="text-
|
|
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-
|
|
13616
|
-
<h1 class="text-
|
|
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-
|
|
13649
|
-
<h1 class="text-
|
|
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-
|
|
13693
|
-
<h1 class="text-
|
|
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-
|
|
13739
|
-
<h1 class="text-
|
|
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-
|
|
13787
|
-
<h1 class="text-
|
|
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()) {
|
|
@@ -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':'
|
|
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':'
|
|
14271
|
+
{{ product()?.discount_price | currency:'COP':'$':'1.0-0' }}
|
|
14299
14272
|
</span>
|
|
14300
14273
|
}
|
|
14301
14274
|
</div>
|
|
@@ -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':'
|
|
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':'
|
|
14406
|
+
{{ product()?.discount_price | currency:'COP':'$':'1.0-0' }}
|
|
14434
14407
|
</span>
|
|
14435
14408
|
}
|
|
14436
14409
|
</div>
|
|
@@ -14559,7 +14532,7 @@ class ClxProductDetailComponent {
|
|
|
14559
14532
|
specsLeft(specs) { return specs.slice(0, Math.ceil(specs.length / 2)); }
|
|
14560
14533
|
specsRight(specs) { return specs.slice(Math.ceil(specs.length / 2)); }
|
|
14561
14534
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14562
|
-
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():'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 @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 });
|
|
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 });
|
|
14563
14536
|
}
|
|
14564
14537
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, decorators: [{
|
|
14565
14538
|
type: Component,
|
|
@@ -14581,7 +14554,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
14581
14554
|
ClxTooltipDirective,
|
|
14582
14555
|
ClxListComponent,
|
|
14583
14556
|
ClxListItemComponent,
|
|
14584
|
-
], 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 @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" }]
|
|
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" }]
|
|
14585
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 }] }] } });
|
|
14586
14559
|
|
|
14587
14560
|
class ClxProductQuickViewComponent {
|
|
@@ -14656,6 +14629,8 @@ class ClxWishlistComponent {
|
|
|
14656
14629
|
onSortChange = output();
|
|
14657
14630
|
onPageChange = output();
|
|
14658
14631
|
onCheckoutAll = output();
|
|
14632
|
+
/** Emitted from the empty-state's "Ir a la tienda" CTA — the host navigates to the catalog. */
|
|
14633
|
+
onBrowseCatalog = output();
|
|
14659
14634
|
_skeletonItems = Array.from({ length: 6 });
|
|
14660
14635
|
_cardClass = 'bg-clx-surface rounded-2xl shadow-sm transition-[box-shadow,transform] duration-300 overflow-hidden group hover:shadow-xl hover:-translate-y-0.5 flex flex-col sm:flex-row';
|
|
14661
14636
|
_imageContainerClass = 'relative shrink-0 w-full aspect-square sm:aspect-auto sm:w-40 overflow-hidden rounded-t-2xl sm:rounded-t-none sm:rounded-l-2xl';
|
|
@@ -14727,7 +14702,7 @@ class ClxWishlistComponent {
|
|
|
14727
14702
|
this.onCheckoutAll.emit(this._filtered());
|
|
14728
14703
|
}
|
|
14729
14704
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWishlistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14730
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxWishlistComponent, isStandalone: true, selector: "clx-wishlist", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, sortOptions: { classPropertyName: "sortOptions", publicName: "sortOptions", isSignal: true, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, sortValue: { classPropertyName: "sortValue", publicName: "sortValue", isSignal: true, isRequired: false, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange", sortValue: "sortValueChange", searchValue: "searchValueChange", onRemove: "onRemove", onAddToCart: "onAddToCart", onSearchChange: "onSearchChange", onSortChange: "onSortChange", onPageChange: "onPageChange", onCheckoutAll: "onCheckoutAll" }, ngImport: i0, template: `
|
|
14705
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxWishlistComponent, isStandalone: true, selector: "clx-wishlist", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, sortOptions: { classPropertyName: "sortOptions", publicName: "sortOptions", isSignal: true, isRequired: false, transformFunction: null }, currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, sortValue: { classPropertyName: "sortValue", publicName: "sortValue", isSignal: true, isRequired: false, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange", sortValue: "sortValueChange", searchValue: "searchValueChange", onRemove: "onRemove", onAddToCart: "onAddToCart", onSearchChange: "onSearchChange", onSortChange: "onSortChange", onPageChange: "onPageChange", onCheckoutAll: "onCheckoutAll", onBrowseCatalog: "onBrowseCatalog" }, ngImport: i0, template: `
|
|
14731
14706
|
<div class="flex flex-col gap-6">
|
|
14732
14707
|
|
|
14733
14708
|
<!-- ── Top Bar ──────────────────────────────────────────────────────── -->
|
|
@@ -14758,22 +14733,19 @@ class ClxWishlistComponent {
|
|
|
14758
14733
|
<!-- ── Info Bar ─────────────────────────────────────────────────────── -->
|
|
14759
14734
|
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
|
14760
14735
|
<div class="flex items-center gap-2">
|
|
14761
|
-
<
|
|
14762
|
-
<span
|
|
14763
|
-
<span clx-badge variant="light" [color]="color()" size="sm">items</span>
|
|
14764
|
-
<span class="text-clx-text-muted text-sm">to your wishlist</span>
|
|
14736
|
+
<h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
|
|
14737
|
+
<span clx-badge variant="light" [color]="color()" size="sm">{{ _totalItems() }}</span>
|
|
14765
14738
|
</div>
|
|
14766
14739
|
<button
|
|
14767
14740
|
clx-button
|
|
14768
14741
|
type="button"
|
|
14769
14742
|
variant="light"
|
|
14770
14743
|
[color]="color()"
|
|
14771
|
-
size="sm"
|
|
14772
14744
|
icon="arrow_forward"
|
|
14773
14745
|
iconPosition="right"
|
|
14774
14746
|
[disabled]="_totalItems() === 0"
|
|
14775
14747
|
(click)="handleCheckoutAll()"
|
|
14776
|
-
>
|
|
14748
|
+
>Agregar todo al carrito</button>
|
|
14777
14749
|
</div>
|
|
14778
14750
|
|
|
14779
14751
|
<!-- ── List ─────────────────────────────────────────────────────────── -->
|
|
@@ -14797,11 +14769,15 @@ class ClxWishlistComponent {
|
|
|
14797
14769
|
|
|
14798
14770
|
} @else if (_pagedItems().length === 0) {
|
|
14799
14771
|
|
|
14800
|
-
<
|
|
14801
|
-
|
|
14802
|
-
|
|
14803
|
-
|
|
14804
|
-
|
|
14772
|
+
<clx-page-empty
|
|
14773
|
+
icon="favorite"
|
|
14774
|
+
title="Sin favoritos"
|
|
14775
|
+
description="No tienes productos guardados."
|
|
14776
|
+
[ctaColor]="color()"
|
|
14777
|
+
ctaIcon="storefront"
|
|
14778
|
+
ctaLabel="Ir a la tienda"
|
|
14779
|
+
(cta)="onBrowseCatalog.emit()">
|
|
14780
|
+
</clx-page-empty>
|
|
14805
14781
|
|
|
14806
14782
|
} @else {
|
|
14807
14783
|
|
|
@@ -14845,10 +14821,10 @@ class ClxWishlistComponent {
|
|
|
14845
14821
|
|
|
14846
14822
|
<!-- Price -->
|
|
14847
14823
|
<div class="flex items-baseline gap-2 mt-2">
|
|
14848
|
-
<span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'
|
|
14824
|
+
<span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'$':'1.0-0' }}</span>
|
|
14849
14825
|
@if (item.has_discount && item.discount_price) {
|
|
14850
14826
|
<span class="text-sm text-clx-text-subtle line-through">
|
|
14851
|
-
{{ item.discount_price | currency:'COP':'
|
|
14827
|
+
{{ item.discount_price | currency:'COP':'$':'1.0-0' }}
|
|
14852
14828
|
</span>
|
|
14853
14829
|
}
|
|
14854
14830
|
</div>
|
|
@@ -14910,7 +14886,7 @@ class ClxWishlistComponent {
|
|
|
14910
14886
|
}
|
|
14911
14887
|
|
|
14912
14888
|
</div>
|
|
14913
|
-
`, 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:
|
|
14889
|
+
`, 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 });
|
|
14914
14890
|
}
|
|
14915
14891
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxWishlistComponent, decorators: [{
|
|
14916
14892
|
type: Component,
|
|
@@ -14922,12 +14898,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
14922
14898
|
FormsModule,
|
|
14923
14899
|
ClxButtonComponent,
|
|
14924
14900
|
ClxBadgeComponent,
|
|
14925
|
-
ClxIconComponent,
|
|
14926
14901
|
ClxSkeletonComponent,
|
|
14927
14902
|
ClxPaginationComponent,
|
|
14928
14903
|
ClxInputComponent,
|
|
14929
14904
|
ClxSelectComponent,
|
|
14930
|
-
ClxTooltipDirective
|
|
14905
|
+
ClxTooltipDirective,
|
|
14906
|
+
ClxPageEmptyComponent
|
|
14931
14907
|
],
|
|
14932
14908
|
template: `
|
|
14933
14909
|
<div class="flex flex-col gap-6">
|
|
@@ -14960,22 +14936,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
14960
14936
|
<!-- ── Info Bar ─────────────────────────────────────────────────────── -->
|
|
14961
14937
|
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
|
|
14962
14938
|
<div class="flex items-center gap-2">
|
|
14963
|
-
<
|
|
14964
|
-
<span
|
|
14965
|
-
<span clx-badge variant="light" [color]="color()" size="sm">items</span>
|
|
14966
|
-
<span class="text-clx-text-muted text-sm">to your wishlist</span>
|
|
14939
|
+
<h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
|
|
14940
|
+
<span clx-badge variant="light" [color]="color()" size="sm">{{ _totalItems() }}</span>
|
|
14967
14941
|
</div>
|
|
14968
14942
|
<button
|
|
14969
14943
|
clx-button
|
|
14970
14944
|
type="button"
|
|
14971
14945
|
variant="light"
|
|
14972
14946
|
[color]="color()"
|
|
14973
|
-
size="sm"
|
|
14974
14947
|
icon="arrow_forward"
|
|
14975
14948
|
iconPosition="right"
|
|
14976
14949
|
[disabled]="_totalItems() === 0"
|
|
14977
14950
|
(click)="handleCheckoutAll()"
|
|
14978
|
-
>
|
|
14951
|
+
>Agregar todo al carrito</button>
|
|
14979
14952
|
</div>
|
|
14980
14953
|
|
|
14981
14954
|
<!-- ── List ─────────────────────────────────────────────────────────── -->
|
|
@@ -14999,11 +14972,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
14999
14972
|
|
|
15000
14973
|
} @else if (_pagedItems().length === 0) {
|
|
15001
14974
|
|
|
15002
|
-
<
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
14975
|
+
<clx-page-empty
|
|
14976
|
+
icon="favorite"
|
|
14977
|
+
title="Sin favoritos"
|
|
14978
|
+
description="No tienes productos guardados."
|
|
14979
|
+
[ctaColor]="color()"
|
|
14980
|
+
ctaIcon="storefront"
|
|
14981
|
+
ctaLabel="Ir a la tienda"
|
|
14982
|
+
(cta)="onBrowseCatalog.emit()">
|
|
14983
|
+
</clx-page-empty>
|
|
15007
14984
|
|
|
15008
14985
|
} @else {
|
|
15009
14986
|
|
|
@@ -15047,10 +15024,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15047
15024
|
|
|
15048
15025
|
<!-- Price -->
|
|
15049
15026
|
<div class="flex items-baseline gap-2 mt-2">
|
|
15050
|
-
<span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'
|
|
15027
|
+
<span [class]="_priceClass">{{ (item.sale_price ?? item.product_sale_price) | currency:'COP':'$':'1.0-0' }}</span>
|
|
15051
15028
|
@if (item.has_discount && item.discount_price) {
|
|
15052
15029
|
<span class="text-sm text-clx-text-subtle line-through">
|
|
15053
|
-
{{ item.discount_price | currency:'COP':'
|
|
15030
|
+
{{ item.discount_price | currency:'COP':'$':'1.0-0' }}
|
|
15054
15031
|
</span>
|
|
15055
15032
|
}
|
|
15056
15033
|
</div>
|
|
@@ -15116,7 +15093,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15116
15093
|
encapsulation: ViewEncapsulation.None,
|
|
15117
15094
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
15118
15095
|
}]
|
|
15119
|
-
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], sortOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortOptions", required: false }] }], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], sortValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortValue", required: false }] }, { type: i0.Output, args: ["sortValueChange"] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: false }] }, { type: i0.Output, args: ["searchValueChange"] }], onRemove: [{ type: i0.Output, args: ["onRemove"] }], onAddToCart: [{ type: i0.Output, args: ["onAddToCart"] }], onSearchChange: [{ type: i0.Output, args: ["onSearchChange"] }], onSortChange: [{ type: i0.Output, args: ["onSortChange"] }], onPageChange: [{ type: i0.Output, args: ["onPageChange"] }], onCheckoutAll: [{ type: i0.Output, args: ["onCheckoutAll"] }] } });
|
|
15096
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], sortOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortOptions", required: false }] }], currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], sortValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortValue", required: false }] }, { type: i0.Output, args: ["sortValueChange"] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: false }] }, { type: i0.Output, args: ["searchValueChange"] }], onRemove: [{ type: i0.Output, args: ["onRemove"] }], onAddToCart: [{ type: i0.Output, args: ["onAddToCart"] }], onSearchChange: [{ type: i0.Output, args: ["onSearchChange"] }], onSortChange: [{ type: i0.Output, args: ["onSortChange"] }], onPageChange: [{ type: i0.Output, args: ["onPageChange"] }], onCheckoutAll: [{ type: i0.Output, args: ["onCheckoutAll"] }], onBrowseCatalog: [{ type: i0.Output, args: ["onBrowseCatalog"] }] } });
|
|
15120
15097
|
|
|
15121
15098
|
class ClxCartComponent {
|
|
15122
15099
|
items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
@@ -15131,6 +15108,8 @@ class ClxCartComponent {
|
|
|
15131
15108
|
onClearCart = output();
|
|
15132
15109
|
onApplyCoupon = output();
|
|
15133
15110
|
onCheckout = output();
|
|
15111
|
+
/** Emitted from the empty-state's "Ir a la tienda" CTA — the host navigates to the catalog. */
|
|
15112
|
+
onBrowseCatalog = output();
|
|
15134
15113
|
_couponCode = '';
|
|
15135
15114
|
_skeletons = Array.from({ length: 3 });
|
|
15136
15115
|
_subtotal = computed(() => this.items().reduce((s, i) => s + i.unit_price * i.quantity, 0), ...(ngDevMode ? [{ debugName: "_subtotal" }] : /* istanbul ignore next */ []));
|
|
@@ -15159,7 +15138,7 @@ class ClxCartComponent {
|
|
|
15159
15138
|
this.coupon.set(null);
|
|
15160
15139
|
}
|
|
15161
15140
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
15162
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxCartComponent, isStandalone: true, selector: "clx-cart", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showCoupon: { classPropertyName: "showCoupon", publicName: "showCoupon", isSignal: true, isRequired: false, transformFunction: null }, coupon: { classPropertyName: "coupon", publicName: "coupon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { coupon: "couponChange", onRemove: "onRemove", onQuantityChange: "onQuantityChange", onClearCart: "onClearCart", onApplyCoupon: "onApplyCoupon", onCheckout: "onCheckout" }, ngImport: i0, template: `
|
|
15141
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxCartComponent, isStandalone: true, selector: "clx-cart", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, showCoupon: { classPropertyName: "showCoupon", publicName: "showCoupon", isSignal: true, isRequired: false, transformFunction: null }, coupon: { classPropertyName: "coupon", publicName: "coupon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { coupon: "couponChange", onRemove: "onRemove", onQuantityChange: "onQuantityChange", onClearCart: "onClearCart", onApplyCoupon: "onApplyCoupon", onCheckout: "onCheckout", onBrowseCatalog: "onBrowseCatalog" }, ngImport: i0, template: `
|
|
15163
15142
|
<!-- ── Loading ─────────────────────────────────────────────────────────────── -->
|
|
15164
15143
|
@if (loading()) {
|
|
15165
15144
|
|
|
@@ -15190,11 +15169,15 @@ class ClxCartComponent {
|
|
|
15190
15169
|
<!-- ── Empty ───────────────────────────────────────────────────────────────── -->
|
|
15191
15170
|
} @else if (items().length === 0) {
|
|
15192
15171
|
|
|
15193
|
-
<
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15172
|
+
<clx-page-empty
|
|
15173
|
+
icon="remove_shopping_cart"
|
|
15174
|
+
title="Carrito vacio"
|
|
15175
|
+
description="Tu carrito esta vacio. Agrega productos para continuar."
|
|
15176
|
+
[ctaColor]="color()"
|
|
15177
|
+
ctaIcon="storefront"
|
|
15178
|
+
ctaLabel="Ir a la tienda"
|
|
15179
|
+
(cta)="onBrowseCatalog.emit()">
|
|
15180
|
+
</clx-page-empty>
|
|
15198
15181
|
|
|
15199
15182
|
<!-- ── Main layout ─────────────────────────────────────────────────────────── -->
|
|
15200
15183
|
} @else {
|
|
@@ -15207,19 +15190,19 @@ class ClxCartComponent {
|
|
|
15207
15190
|
<!-- Top bar -->
|
|
15208
15191
|
<div class="flex items-center justify-between">
|
|
15209
15192
|
<div class="flex items-center gap-2">
|
|
15210
|
-
<h2 class="text-base font-semibold text-clx-text-label">
|
|
15193
|
+
<h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
|
|
15211
15194
|
<span clx-badge variant="light" [color]="color()" size="sm">{{ _totalUnits() }}</span>
|
|
15212
15195
|
</div>
|
|
15213
15196
|
<button
|
|
15214
|
-
clx-button type="button" variant="
|
|
15197
|
+
clx-button type="button" variant="light" color="red" icon="delete"
|
|
15215
15198
|
(click)="onClearCart.emit()">
|
|
15216
|
-
Vaciar
|
|
15199
|
+
Vaciar carrito
|
|
15217
15200
|
</button>
|
|
15218
15201
|
</div>
|
|
15219
15202
|
|
|
15220
15203
|
<!-- Item cards -->
|
|
15221
15204
|
@for (item of items(); track item.id) {
|
|
15222
|
-
<article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-
|
|
15205
|
+
<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">
|
|
15223
15206
|
|
|
15224
15207
|
<!-- Image — fixed square -->
|
|
15225
15208
|
<div class="relative shrink-0 w-28 h-28 lg:w-36 lg:h-36 overflow-hidden bg-clx-surface-2">
|
|
@@ -15264,54 +15247,29 @@ class ClxCartComponent {
|
|
|
15264
15247
|
<p class="text-xs font-mono text-clx-text-subtle mt-0.5">{{ item.sku }}</p>
|
|
15265
15248
|
</div>
|
|
15266
15249
|
|
|
15267
|
-
@if (item.rating) {
|
|
15268
|
-
<div class="flex items-center gap-1.5">
|
|
15269
|
-
<clx-rating [value]="item.rating" [max]="5" color="amber" />
|
|
15270
|
-
@if (item.review_count) {
|
|
15271
|
-
<span class="text-xs text-clx-text-subtle">({{ item.review_count }})</span>
|
|
15272
|
-
}
|
|
15273
|
-
</div>
|
|
15274
|
-
}
|
|
15275
|
-
|
|
15276
15250
|
<!-- Price + Quantity row -->
|
|
15277
15251
|
<div class="flex items-center justify-between gap-2 mt-auto flex-wrap">
|
|
15278
15252
|
|
|
15279
15253
|
<!-- Price -->
|
|
15280
15254
|
<div class="flex items-baseline gap-1.5">
|
|
15281
15255
|
<span class="text-base font-bold text-clx-text-label">
|
|
15282
|
-
{{ item.unit_price | currency:'COP':'
|
|
15256
|
+
{{ item.unit_price | currency:'COP':'$':'1.0-0' }}
|
|
15283
15257
|
</span>
|
|
15284
15258
|
@if (item.has_discount && item.original_price) {
|
|
15285
15259
|
<span class="text-xs text-clx-text-subtle line-through">
|
|
15286
|
-
{{ item.original_price | currency:'COP':'
|
|
15260
|
+
{{ item.original_price | currency:'COP':'$':'1.0-0' }}
|
|
15287
15261
|
</span>
|
|
15288
15262
|
}
|
|
15289
15263
|
</div>
|
|
15290
15264
|
|
|
15291
15265
|
<!-- Quantity control -->
|
|
15292
|
-
<
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
<span class="w-8 text-center text-sm font-semibold text-clx-text-label select-none">
|
|
15300
|
-
{{ item.quantity }}
|
|
15301
|
-
</span>
|
|
15302
|
-
<button
|
|
15303
|
-
clx-button type="button" variant="ghost" [color]="color()" size="sm"
|
|
15304
|
-
icon="add" [iconOnly]="true"
|
|
15305
|
-
[disabled]="!item.in_stock || (!!item.max_quantity && item.quantity >= item.max_quantity)"
|
|
15306
|
-
(click)="onQuantityChange.emit({ item, quantity: item.quantity + 1 })">
|
|
15307
|
-
</button>
|
|
15308
|
-
</div>
|
|
15309
|
-
|
|
15310
|
-
<!-- Line total -->
|
|
15311
|
-
<p class="text-sm font-bold text-clx-text-label">
|
|
15312
|
-
{{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
|
|
15313
|
-
</p>
|
|
15314
|
-
|
|
15266
|
+
<clx-number
|
|
15267
|
+
variant="stepper" size="sm" [color]="color()"
|
|
15268
|
+
[min]="1" [max]="item.max_quantity ?? null"
|
|
15269
|
+
[disabled]="!item.in_stock"
|
|
15270
|
+
[ngModel]="item.quantity"
|
|
15271
|
+
(ngModelChange)="onQuantityChange.emit({ item, quantity: $event })">
|
|
15272
|
+
</clx-number>
|
|
15315
15273
|
</div>
|
|
15316
15274
|
</div>
|
|
15317
15275
|
|
|
@@ -15376,12 +15334,12 @@ class ClxCartComponent {
|
|
|
15376
15334
|
<dl class="flex flex-col gap-2.5 text-sm">
|
|
15377
15335
|
<div class="flex justify-between text-clx-text-muted">
|
|
15378
15336
|
<dt>Subtotal <span class="text-clx-text-subtle text-xs">({{ _totalUnits() }} items)</span></dt>
|
|
15379
|
-
<dd class="font-medium">{{ _subtotal() | currency:'COP':'
|
|
15337
|
+
<dd class="font-medium">{{ _subtotal() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15380
15338
|
</div>
|
|
15381
15339
|
@if (_savingsAmount() > 0) {
|
|
15382
15340
|
<div class="flex justify-between text-emerald-600">
|
|
15383
15341
|
<dt>Descuento productos</dt>
|
|
15384
|
-
<dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'
|
|
15342
|
+
<dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15385
15343
|
</div>
|
|
15386
15344
|
}
|
|
15387
15345
|
@if (coupon()?.valid) {
|
|
@@ -15390,18 +15348,18 @@ class ClxCartComponent {
|
|
|
15390
15348
|
Cupón
|
|
15391
15349
|
<code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ coupon()!.code }}</code>
|
|
15392
15350
|
</dt>
|
|
15393
|
-
<dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'
|
|
15351
|
+
<dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15394
15352
|
</div>
|
|
15395
15353
|
}
|
|
15396
15354
|
<div class="flex justify-between border-t border-clx-border-soft pt-2.5">
|
|
15397
15355
|
<dt class="font-bold text-clx-text-label">Total</dt>
|
|
15398
|
-
<dd class="text-
|
|
15356
|
+
<dd class="text-lg font-black text-clx-text-label">{{ _total() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15399
15357
|
</div>
|
|
15400
15358
|
</dl>
|
|
15401
15359
|
|
|
15402
15360
|
<!-- Checkout -->
|
|
15403
15361
|
<button
|
|
15404
|
-
clx-button type="button" [color]="color()" variant="solid" size="
|
|
15362
|
+
clx-button type="button" [color]="color()" variant="solid" size="md"
|
|
15405
15363
|
icon="arrow_forward" iconPosition="right" [block]="true"
|
|
15406
15364
|
(click)="onCheckout.emit()">
|
|
15407
15365
|
Proceder al pago
|
|
@@ -15420,7 +15378,7 @@ class ClxCartComponent {
|
|
|
15420
15378
|
</div>
|
|
15421
15379
|
|
|
15422
15380
|
}
|
|
15423
|
-
`, 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:
|
|
15381
|
+
`, 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 });
|
|
15424
15382
|
}
|
|
15425
15383
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxCartComponent, decorators: [{
|
|
15426
15384
|
type: Component,
|
|
@@ -15435,7 +15393,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15435
15393
|
ClxIconComponent,
|
|
15436
15394
|
ClxSkeletonComponent,
|
|
15437
15395
|
ClxInputComponent,
|
|
15438
|
-
|
|
15396
|
+
ClxNumberComponent,
|
|
15397
|
+
ClxPageEmptyComponent
|
|
15439
15398
|
],
|
|
15440
15399
|
encapsulation: ViewEncapsulation.None,
|
|
15441
15400
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -15470,11 +15429,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15470
15429
|
<!-- ── Empty ───────────────────────────────────────────────────────────────── -->
|
|
15471
15430
|
} @else if (items().length === 0) {
|
|
15472
15431
|
|
|
15473
|
-
<
|
|
15474
|
-
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
|
|
15432
|
+
<clx-page-empty
|
|
15433
|
+
icon="remove_shopping_cart"
|
|
15434
|
+
title="Carrito vacio"
|
|
15435
|
+
description="Tu carrito esta vacio. Agrega productos para continuar."
|
|
15436
|
+
[ctaColor]="color()"
|
|
15437
|
+
ctaIcon="storefront"
|
|
15438
|
+
ctaLabel="Ir a la tienda"
|
|
15439
|
+
(cta)="onBrowseCatalog.emit()">
|
|
15440
|
+
</clx-page-empty>
|
|
15478
15441
|
|
|
15479
15442
|
<!-- ── Main layout ─────────────────────────────────────────────────────────── -->
|
|
15480
15443
|
} @else {
|
|
@@ -15487,19 +15450,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15487
15450
|
<!-- Top bar -->
|
|
15488
15451
|
<div class="flex items-center justify-between">
|
|
15489
15452
|
<div class="flex items-center gap-2">
|
|
15490
|
-
<h2 class="text-base font-semibold text-clx-text-label">
|
|
15453
|
+
<h2 class="text-base font-semibold text-clx-text-label">Productos</h2>
|
|
15491
15454
|
<span clx-badge variant="light" [color]="color()" size="sm">{{ _totalUnits() }}</span>
|
|
15492
15455
|
</div>
|
|
15493
15456
|
<button
|
|
15494
|
-
clx-button type="button" variant="
|
|
15457
|
+
clx-button type="button" variant="light" color="red" icon="delete"
|
|
15495
15458
|
(click)="onClearCart.emit()">
|
|
15496
|
-
Vaciar
|
|
15459
|
+
Vaciar carrito
|
|
15497
15460
|
</button>
|
|
15498
15461
|
</div>
|
|
15499
15462
|
|
|
15500
15463
|
<!-- Item cards -->
|
|
15501
15464
|
@for (item of items(); track item.id) {
|
|
15502
|
-
<article class="bg-clx-surface rounded-2xl shadow-sm flex gap-0 overflow-hidden group transition-[box-shadow,transform] duration-300 hover:shadow-
|
|
15465
|
+
<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">
|
|
15503
15466
|
|
|
15504
15467
|
<!-- Image — fixed square -->
|
|
15505
15468
|
<div class="relative shrink-0 w-28 h-28 lg:w-36 lg:h-36 overflow-hidden bg-clx-surface-2">
|
|
@@ -15544,54 +15507,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15544
15507
|
<p class="text-xs font-mono text-clx-text-subtle mt-0.5">{{ item.sku }}</p>
|
|
15545
15508
|
</div>
|
|
15546
15509
|
|
|
15547
|
-
@if (item.rating) {
|
|
15548
|
-
<div class="flex items-center gap-1.5">
|
|
15549
|
-
<clx-rating [value]="item.rating" [max]="5" color="amber" />
|
|
15550
|
-
@if (item.review_count) {
|
|
15551
|
-
<span class="text-xs text-clx-text-subtle">({{ item.review_count }})</span>
|
|
15552
|
-
}
|
|
15553
|
-
</div>
|
|
15554
|
-
}
|
|
15555
|
-
|
|
15556
15510
|
<!-- Price + Quantity row -->
|
|
15557
15511
|
<div class="flex items-center justify-between gap-2 mt-auto flex-wrap">
|
|
15558
15512
|
|
|
15559
15513
|
<!-- Price -->
|
|
15560
15514
|
<div class="flex items-baseline gap-1.5">
|
|
15561
15515
|
<span class="text-base font-bold text-clx-text-label">
|
|
15562
|
-
{{ item.unit_price | currency:'COP':'
|
|
15516
|
+
{{ item.unit_price | currency:'COP':'$':'1.0-0' }}
|
|
15563
15517
|
</span>
|
|
15564
15518
|
@if (item.has_discount && item.original_price) {
|
|
15565
15519
|
<span class="text-xs text-clx-text-subtle line-through">
|
|
15566
|
-
{{ item.original_price | currency:'COP':'
|
|
15520
|
+
{{ item.original_price | currency:'COP':'$':'1.0-0' }}
|
|
15567
15521
|
</span>
|
|
15568
15522
|
}
|
|
15569
15523
|
</div>
|
|
15570
15524
|
|
|
15571
15525
|
<!-- Quantity control -->
|
|
15572
|
-
<
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
<span class="w-8 text-center text-sm font-semibold text-clx-text-label select-none">
|
|
15580
|
-
{{ item.quantity }}
|
|
15581
|
-
</span>
|
|
15582
|
-
<button
|
|
15583
|
-
clx-button type="button" variant="ghost" [color]="color()" size="sm"
|
|
15584
|
-
icon="add" [iconOnly]="true"
|
|
15585
|
-
[disabled]="!item.in_stock || (!!item.max_quantity && item.quantity >= item.max_quantity)"
|
|
15586
|
-
(click)="onQuantityChange.emit({ item, quantity: item.quantity + 1 })">
|
|
15587
|
-
</button>
|
|
15588
|
-
</div>
|
|
15589
|
-
|
|
15590
|
-
<!-- Line total -->
|
|
15591
|
-
<p class="text-sm font-bold text-clx-text-label">
|
|
15592
|
-
{{ (item.unit_price * item.quantity) | currency:'COP':'symbol':'1.0-0' }}
|
|
15593
|
-
</p>
|
|
15594
|
-
|
|
15526
|
+
<clx-number
|
|
15527
|
+
variant="stepper" size="sm" [color]="color()"
|
|
15528
|
+
[min]="1" [max]="item.max_quantity ?? null"
|
|
15529
|
+
[disabled]="!item.in_stock"
|
|
15530
|
+
[ngModel]="item.quantity"
|
|
15531
|
+
(ngModelChange)="onQuantityChange.emit({ item, quantity: $event })">
|
|
15532
|
+
</clx-number>
|
|
15595
15533
|
</div>
|
|
15596
15534
|
</div>
|
|
15597
15535
|
|
|
@@ -15656,12 +15594,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15656
15594
|
<dl class="flex flex-col gap-2.5 text-sm">
|
|
15657
15595
|
<div class="flex justify-between text-clx-text-muted">
|
|
15658
15596
|
<dt>Subtotal <span class="text-clx-text-subtle text-xs">({{ _totalUnits() }} items)</span></dt>
|
|
15659
|
-
<dd class="font-medium">{{ _subtotal() | currency:'COP':'
|
|
15597
|
+
<dd class="font-medium">{{ _subtotal() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15660
15598
|
</div>
|
|
15661
15599
|
@if (_savingsAmount() > 0) {
|
|
15662
15600
|
<div class="flex justify-between text-emerald-600">
|
|
15663
15601
|
<dt>Descuento productos</dt>
|
|
15664
|
-
<dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'
|
|
15602
|
+
<dd class="font-medium">-{{ _savingsAmount() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15665
15603
|
</div>
|
|
15666
15604
|
}
|
|
15667
15605
|
@if (coupon()?.valid) {
|
|
@@ -15670,18 +15608,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15670
15608
|
Cupón
|
|
15671
15609
|
<code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ coupon()!.code }}</code>
|
|
15672
15610
|
</dt>
|
|
15673
|
-
<dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'
|
|
15611
|
+
<dd class="font-medium">-{{ _couponDiscount() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15674
15612
|
</div>
|
|
15675
15613
|
}
|
|
15676
15614
|
<div class="flex justify-between border-t border-clx-border-soft pt-2.5">
|
|
15677
15615
|
<dt class="font-bold text-clx-text-label">Total</dt>
|
|
15678
|
-
<dd class="text-
|
|
15616
|
+
<dd class="text-lg font-black text-clx-text-label">{{ _total() | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15679
15617
|
</div>
|
|
15680
15618
|
</dl>
|
|
15681
15619
|
|
|
15682
15620
|
<!-- Checkout -->
|
|
15683
15621
|
<button
|
|
15684
|
-
clx-button type="button" [color]="color()" variant="solid" size="
|
|
15622
|
+
clx-button type="button" [color]="color()" variant="solid" size="md"
|
|
15685
15623
|
icon="arrow_forward" iconPosition="right" [block]="true"
|
|
15686
15624
|
(click)="onCheckout.emit()">
|
|
15687
15625
|
Proceder al pago
|
|
@@ -15702,7 +15640,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15702
15640
|
}
|
|
15703
15641
|
`,
|
|
15704
15642
|
}]
|
|
15705
|
-
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], showCoupon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCoupon", required: false }] }], coupon: [{ type: i0.Input, args: [{ isSignal: true, alias: "coupon", required: false }] }, { type: i0.Output, args: ["couponChange"] }], onRemove: [{ type: i0.Output, args: ["onRemove"] }], onQuantityChange: [{ type: i0.Output, args: ["onQuantityChange"] }], onClearCart: [{ type: i0.Output, args: ["onClearCart"] }], onApplyCoupon: [{ type: i0.Output, args: ["onApplyCoupon"] }], onCheckout: [{ type: i0.Output, args: ["onCheckout"] }] } });
|
|
15643
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], showCoupon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCoupon", required: false }] }], coupon: [{ type: i0.Input, args: [{ isSignal: true, alias: "coupon", required: false }] }, { type: i0.Output, args: ["couponChange"] }], onRemove: [{ type: i0.Output, args: ["onRemove"] }], onQuantityChange: [{ type: i0.Output, args: ["onQuantityChange"] }], onClearCart: [{ type: i0.Output, args: ["onClearCart"] }], onApplyCoupon: [{ type: i0.Output, args: ["onApplyCoupon"] }], onCheckout: [{ type: i0.Output, args: ["onCheckout"] }], onBrowseCatalog: [{ type: i0.Output, args: ["onBrowseCatalog"] }] } });
|
|
15706
15644
|
|
|
15707
15645
|
class ClxCartSummaryDrawer {
|
|
15708
15646
|
data = inject(CLX_MODAL_DATA);
|
|
@@ -15744,7 +15682,7 @@ class ClxCartSummaryDrawer {
|
|
|
15744
15682
|
<p class="text-xs text-clx-text-subtle">x{{ item.quantity }}</p>
|
|
15745
15683
|
</div>
|
|
15746
15684
|
<span class="text-sm font-semibold text-clx-text-label shrink-0">
|
|
15747
|
-
{{ (item.unit_price * item.quantity) | currency:'COP':'
|
|
15685
|
+
{{ (item.unit_price * item.quantity) | currency:'COP':'$':'1.0-0' }}
|
|
15748
15686
|
</span>
|
|
15749
15687
|
</li>
|
|
15750
15688
|
}
|
|
@@ -15757,12 +15695,12 @@ class ClxCartSummaryDrawer {
|
|
|
15757
15695
|
<dl class="space-y-2">
|
|
15758
15696
|
<div class="flex justify-between text-sm text-clx-text-muted">
|
|
15759
15697
|
<dt>Subtotal</dt>
|
|
15760
|
-
<dd class="font-medium">{{ data.subtotal | currency:'COP':'
|
|
15698
|
+
<dd class="font-medium">{{ data.subtotal | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15761
15699
|
</div>
|
|
15762
15700
|
@if (data.savingsAmount > 0) {
|
|
15763
15701
|
<div class="flex justify-between text-sm text-emerald-600">
|
|
15764
15702
|
<dt>Descuento</dt>
|
|
15765
|
-
<dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'
|
|
15703
|
+
<dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15766
15704
|
</div>
|
|
15767
15705
|
}
|
|
15768
15706
|
@if (data.coupon?.valid) {
|
|
@@ -15771,12 +15709,12 @@ class ClxCartSummaryDrawer {
|
|
|
15771
15709
|
Cupón
|
|
15772
15710
|
<code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ data.coupon!.code }}</code>
|
|
15773
15711
|
</dt>
|
|
15774
|
-
<dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'
|
|
15712
|
+
<dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15775
15713
|
</div>
|
|
15776
15714
|
}
|
|
15777
15715
|
<div class="border-t border-clx-border-soft pt-2 flex justify-between">
|
|
15778
15716
|
<dt class="font-bold text-clx-text-label">Total</dt>
|
|
15779
|
-
<dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'
|
|
15717
|
+
<dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15780
15718
|
</div>
|
|
15781
15719
|
</dl>
|
|
15782
15720
|
|
|
@@ -15833,7 +15771,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15833
15771
|
<p class="text-xs text-clx-text-subtle">x{{ item.quantity }}</p>
|
|
15834
15772
|
</div>
|
|
15835
15773
|
<span class="text-sm font-semibold text-clx-text-label shrink-0">
|
|
15836
|
-
{{ (item.unit_price * item.quantity) | currency:'COP':'
|
|
15774
|
+
{{ (item.unit_price * item.quantity) | currency:'COP':'$':'1.0-0' }}
|
|
15837
15775
|
</span>
|
|
15838
15776
|
</li>
|
|
15839
15777
|
}
|
|
@@ -15846,12 +15784,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15846
15784
|
<dl class="space-y-2">
|
|
15847
15785
|
<div class="flex justify-between text-sm text-clx-text-muted">
|
|
15848
15786
|
<dt>Subtotal</dt>
|
|
15849
|
-
<dd class="font-medium">{{ data.subtotal | currency:'COP':'
|
|
15787
|
+
<dd class="font-medium">{{ data.subtotal | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15850
15788
|
</div>
|
|
15851
15789
|
@if (data.savingsAmount > 0) {
|
|
15852
15790
|
<div class="flex justify-between text-sm text-emerald-600">
|
|
15853
15791
|
<dt>Descuento</dt>
|
|
15854
|
-
<dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'
|
|
15792
|
+
<dd class="font-medium">-{{ data.savingsAmount | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15855
15793
|
</div>
|
|
15856
15794
|
}
|
|
15857
15795
|
@if (data.coupon?.valid) {
|
|
@@ -15860,12 +15798,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
15860
15798
|
Cupón
|
|
15861
15799
|
<code class="bg-emerald-50 px-1 rounded text-xs ml-1">{{ data.coupon!.code }}</code>
|
|
15862
15800
|
</dt>
|
|
15863
|
-
<dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'
|
|
15801
|
+
<dd class="font-medium">-{{ data.couponDiscount | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15864
15802
|
</div>
|
|
15865
15803
|
}
|
|
15866
15804
|
<div class="border-t border-clx-border-soft pt-2 flex justify-between">
|
|
15867
15805
|
<dt class="font-bold text-clx-text-label">Total</dt>
|
|
15868
|
-
<dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'
|
|
15806
|
+
<dd class="text-xl font-black text-clx-text-label">{{ data.total | currency:'COP':'$':'1.0-0' }}</dd>
|
|
15869
15807
|
</div>
|
|
15870
15808
|
</dl>
|
|
15871
15809
|
|