@dotted-labs/ngx-supabase-stripe 0.1.13 → 0.2.0

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.
@@ -1,11 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, computed, Component, input, output, signal } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, computed, Component, input, output, effect, Directive, signal } from '@angular/core';
3
3
  import { signalStore, withState, withComputed, withMethods, patchState, withHooks } from '@ngrx/signals';
4
4
  import { loadStripe } from '@stripe/stripe-js';
5
5
  import { createClient } from '@supabase/supabase-js';
6
+ import { withEntities, upsertEntity, setAllEntities } from '@ngrx/signals/entities';
6
7
  import * as i1 from '@angular/common';
7
8
  import { CommonModule } from '@angular/common';
8
9
  import { ActivatedRoute, Router } from '@angular/router';
10
+ import { DomSanitizer } from '@angular/platform-browser';
9
11
 
10
12
  const STRIPE_CONFIG = new InjectionToken('STRIPE_CONFIG');
11
13
  /**
@@ -575,6 +577,36 @@ class StripeClientService {
575
577
  return { customer: null, error: error };
576
578
  }
577
579
  }
580
+ async getCustomerPaymentMethods(customerId) {
581
+ try {
582
+ const { data, error } = await this.supabase.getClient()
583
+ .functions.invoke('customer_payment_methods', {
584
+ body: { customerId }
585
+ });
586
+ if (error) {
587
+ throw error;
588
+ }
589
+ return { paymentMethods: data, error: null };
590
+ }
591
+ catch (error) {
592
+ return { paymentMethods: null, error: error };
593
+ }
594
+ }
595
+ async getCustomerPaymentMethod(customerId, paymentMethodId) {
596
+ try {
597
+ const { data, error } = await this.supabase.getClient()
598
+ .functions.invoke('customer_payment_method', {
599
+ body: { customerId, paymentMethodId }
600
+ });
601
+ if (error) {
602
+ throw error;
603
+ }
604
+ return { paymentMethod: data, error: null };
605
+ }
606
+ catch (error) {
607
+ return { paymentMethod: null, error: error };
608
+ }
609
+ }
578
610
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: StripeClientService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
579
611
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: StripeClientService, providedIn: 'root' });
580
612
  }
@@ -585,19 +617,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
585
617
  }]
586
618
  }], ctorParameters: () => [] });
587
619
 
620
+ var Currency;
621
+ (function (Currency) {
622
+ Currency["EUR"] = "eur";
623
+ Currency["USD"] = "usd";
624
+ Currency["GBP"] = "gbp";
625
+ Currency["JPY"] = "jpy";
626
+ Currency["KRW"] = "krw";
627
+ Currency["CNY"] = "cny";
628
+ Currency["INR"] = "inr";
629
+ })(Currency || (Currency = {}));
630
+
588
631
  const initialProductsState = {
589
- products: null,
590
- prices: null,
632
+ products: [],
633
+ prices: [],
591
634
  status: 'idle',
592
635
  error: null,
636
+ currency: Currency.EUR,
593
637
  };
594
638
  const ProductsStore = signalStore({ providedIn: 'root' }, withState(initialProductsState), withComputed((state) => ({
595
639
  isStatusLoading: computed(() => state.status() === 'loading'),
596
640
  isStatusSuccess: computed(() => state.status() === 'success'),
597
641
  isStatusError: computed(() => state.status() === 'error'),
598
- recurringProducts: computed(() => state.products()?.filter(product => product.prices?.some(price => price.details.type === 'recurring')) || []),
599
- oneTimeProducts: computed(() => state.products()?.filter(product => product.prices?.some(price => price.details.type === 'one_time')) || []),
600
- hasProducts: computed(() => state.products() !== null && state.products().length > 0),
642
+ oneTimeproductsByCurrency: computed(() => state.products().filter(product => product.prices.some(price => (price.details.type === 'one_time' && price.details.currency === state.currency()))) || []),
643
+ recurringProductsByCurrency: computed(() => state.products().filter(product => product.prices.some(price => (price.details.type === 'recurring' && price.details.currency === state.currency()))) || []),
644
+ productsByCurrency: computed(() => state.products().filter(product => product.prices.some(price => (price.details.currency === state.currency()))) || []),
645
+ hasProducts: computed(() => state.products().length > 0),
601
646
  isError: computed(() => state.error())
602
647
  })), withMethods((store, supabaseService = inject(SupabaseClientService)) => ({
603
648
  /**
@@ -607,6 +652,12 @@ const ProductsStore = signalStore({ providedIn: 'root' }, withState(initialProdu
607
652
  console.log('🎮 [ProductsStore] Loading products by ids: ', ids);
608
653
  return store.products()?.filter(product => product.id && ids.includes(product.id)) || [];
609
654
  },
655
+ /**
656
+ * Set the currency filter for products
657
+ */
658
+ setCurrency(currency) {
659
+ patchState(store, { currency });
660
+ },
610
661
  /**
611
662
  * Load product by id
612
663
  */
@@ -635,6 +686,12 @@ const ProductsStore = signalStore({ providedIn: 'root' }, withState(initialProdu
635
686
  async loadProducts() {
636
687
  patchState(store, { status: 'loading', error: null });
637
688
  try {
689
+ const { data: prices, error: pricesError } = await supabaseService.selectStripePrices();
690
+ if (pricesError) {
691
+ console.error('🎮 [ProductsStore]: Error loading prices', pricesError);
692
+ patchState(store, { status: 'error', error: pricesError.message });
693
+ }
694
+ patchState(store, { prices: prices || [] });
638
695
  const { data: stripeProducts, error: productsError } = await supabaseService.selectStripeProducts();
639
696
  if (productsError) {
640
697
  console.error('🎮 [ProductsStore]: Error loading products', productsError);
@@ -670,15 +727,9 @@ const ProductsStore = signalStore({ providedIn: 'root' }, withState(initialProdu
670
727
  reset() {
671
728
  patchState(store, initialProductsState);
672
729
  }
673
- })), withHooks((store, supabaseService = inject(SupabaseClientService)) => ({
730
+ })), withHooks((store) => ({
674
731
  async onInit() {
675
732
  console.log('🎮 [ProductsStore] onInit');
676
- const { data: prices, error: pricesError } = await supabaseService.selectStripePrices();
677
- if (pricesError) {
678
- console.error('🎮 [ProductsStore]: Error loading prices', pricesError);
679
- patchState(store, { status: 'error', error: pricesError.message });
680
- }
681
- patchState(store, { prices });
682
733
  const products = store.products();
683
734
  console.log('🎮 [ProductsStore] products: ', products);
684
735
  if (!products) {
@@ -918,8 +969,10 @@ const initialCustomerState = {
918
969
  status: 'idle',
919
970
  error: null,
920
971
  },
972
+ paymentMethodsStatus: 'idle',
973
+ paymentMethodsError: null,
921
974
  };
922
- const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCustomerState), withComputed((state) => ({
975
+ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCustomerState), withEntities(), withComputed((state) => ({
923
976
  isPaymentIntentsStatusLoading: computed(() => state.paymentIntents.status() === 'loading'),
924
977
  isPaymentIntentsStatusSuccess: computed(() => state.paymentIntents.status() === 'success'),
925
978
  isPaymentIntentsStatusError: computed(() => state.paymentIntents.status() === 'error'),
@@ -931,6 +984,11 @@ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCusto
931
984
  firstSubscription: computed(() => state.subscriptions.data()?.[0]),
932
985
  restSubscriptions: computed(() => state.subscriptions.data()?.slice(1)),
933
986
  isError: computed(() => state.paymentIntents.error()),
987
+ isPaymentMethodsStatusLoading: computed(() => state.paymentMethodsStatus() === 'loading'),
988
+ isPaymentMethodsStatusSuccess: computed(() => state.paymentMethodsStatus() === 'success'),
989
+ isPaymentMethodsStatusError: computed(() => state.paymentMethodsStatus() === 'error'),
990
+ hasPaymentMethods: computed(() => state.entities().length > 0),
991
+ paymentMethods: computed(() => state.entities()),
934
992
  })), withMethods((state, supabaseService = inject(SupabaseClientService), stripeService = inject(StripeClientService), productsStore = inject(ProductsStore)) => ({
935
993
  /**
936
994
  * Load customer
@@ -947,6 +1005,7 @@ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCusto
947
1005
  if (customer) {
948
1006
  this.loadPaymentIntents(customer.id);
949
1007
  this.loadSubscriptions(customer.id);
1008
+ this.loadPaymentMethods(customer.id);
950
1009
  patchState(state, { customer: { data: customer, status: 'success', error: null } });
951
1010
  }
952
1011
  else {
@@ -974,7 +1033,7 @@ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCusto
974
1033
  async loadPaymentIntents(customerId) {
975
1034
  patchState(state, { paymentIntents: { status: 'loading', data: [], error: null } });
976
1035
  const { data, error } = await supabaseService.getCustomerPaymentIntents(customerId);
977
- const paymentIntents = data?.map((paymentIntent) => parsePaymentIntent(paymentIntent));
1036
+ const paymentIntents = data?.map((paymentIntent) => parsePaymentIntent(paymentIntent, state.entityMap()));
978
1037
  if (error) {
979
1038
  patchState(state, { paymentIntents: { error: error.message, status: 'error', data: [] } });
980
1039
  }
@@ -1009,6 +1068,61 @@ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCusto
1009
1068
  patchState(state, { subscriptions: { data: subscriptions ?? [], status: 'success', error: null } });
1010
1069
  }
1011
1070
  },
1071
+ /**
1072
+ * Load payment methods for a customer
1073
+ * @param customerId The customer ID
1074
+ */
1075
+ async loadPaymentMethods(customerId) {
1076
+ patchState(state, {
1077
+ paymentMethodsStatus: 'loading',
1078
+ paymentMethodsError: null
1079
+ });
1080
+ const { paymentMethods, error } = await stripeService.getCustomerPaymentMethods(customerId);
1081
+ if (error) {
1082
+ patchState(state, {
1083
+ paymentMethodsStatus: 'error',
1084
+ paymentMethodsError: error.message
1085
+ });
1086
+ }
1087
+ else {
1088
+ patchState(state, setAllEntities(paymentMethods ?? []), {
1089
+ paymentMethodsStatus: 'success',
1090
+ paymentMethodsError: null
1091
+ });
1092
+ }
1093
+ },
1094
+ /**
1095
+ * Load a single payment method and add/update it in the entity collection
1096
+ * @param customerId The customer ID
1097
+ * @param paymentMethodId The payment method ID
1098
+ */
1099
+ async loadPaymentMethod(customerId, paymentMethodId) {
1100
+ patchState(state, {
1101
+ paymentMethodsStatus: 'loading',
1102
+ paymentMethodsError: null
1103
+ });
1104
+ const { paymentMethod, error } = await stripeService.getCustomerPaymentMethod(customerId, paymentMethodId);
1105
+ if (error) {
1106
+ patchState(state, {
1107
+ paymentMethodsStatus: 'error',
1108
+ paymentMethodsError: error.message
1109
+ });
1110
+ }
1111
+ else if (paymentMethod) {
1112
+ }
1113
+ if (paymentMethod) {
1114
+ patchState(state, upsertEntity(paymentMethod), {
1115
+ paymentMethodsStatus: 'success',
1116
+ paymentMethodsError: null
1117
+ });
1118
+ }
1119
+ else {
1120
+ patchState(state, {
1121
+ paymentMethodsStatus: 'error',
1122
+ paymentMethodsError: 'Payment method not found'
1123
+ });
1124
+ }
1125
+ },
1012
1126
  })), withHooks(() => {
1013
1127
  return {
1014
1128
  onInit() {
@@ -1024,15 +1138,18 @@ const CustomerStore = signalStore({ providedIn: 'root' }, withState(initialCusto
1024
1138
  }
1025
1139
  };
1026
1140
  }));
1027
- function parsePaymentIntent(paymentIntent) {
1141
+ function parsePaymentIntent(paymentIntent, paymentMethodsMap = {}) {
1142
+ console.log('🔍 [CustomerStore] paymentIntent: ', paymentIntent);
1028
1143
  const paymentIntentAttrs = paymentIntent.attrs;
1144
+ const paymentMethodId = paymentIntentAttrs.payment_method;
1029
1145
  return {
1030
1146
  ...paymentIntent,
1031
1147
  status: paymentIntentAttrs.status,
1032
1148
  invoiceId: paymentIntentAttrs.invoice,
1033
1149
  liveMode: paymentIntentAttrs.livemode,
1034
1150
  confirmationMethod: paymentIntentAttrs.confirmation_method,
1035
- paymentMethodId: paymentIntentAttrs.payment_method,
1151
+ paymentMethodId,
1152
+ paymentMethod: paymentMethodId ? paymentMethodsMap[paymentMethodId] : undefined,
1036
1153
  };
1037
1154
  }
1038
1155
 
@@ -1135,6 +1252,55 @@ const CheckoutStore = signalStore({ providedIn: 'root' }, withState(initialCheck
1135
1252
  }
1136
1253
  }));
1137
1254
 
1255
+ const initialPortalState = {
1256
+ status: 'idle',
1257
+ error: null,
1258
+ portalUrl: null
1259
+ };
1260
+ const PortalAccountStore = signalStore({ providedIn: 'root' }, withState(initialPortalState), withComputed((state) => ({
1261
+ isStatusIdle: computed(() => state.status() === 'idle'),
1262
+ isStatusLoading: computed(() => state.status() === 'loading'),
1263
+ isStatusSuccess: computed(() => state.status() === 'success'),
1264
+ isStatusError: computed(() => state.status() === 'error'),
1265
+ })), withMethods((store, stripeService = inject(StripeClientService)) => ({
1266
+ /**
1267
+ * Creates a customer portal session and redirects to the URL
1268
+ * @param customerId Stripe customer ID
1269
+ * @param returnUrl URL to redirect to after the portal session
1270
+ */
1271
+ async createPortalSession(customerId, returnUrl) {
1272
+ patchState(store, { status: 'loading', error: null, portalUrl: null });
1273
+ try {
1274
+ const { url, error } = await stripeService.createPortalSession(customerId, returnUrl);
1275
+ if (error) {
1276
+ throw new Error(error.message);
1277
+ }
1278
+ if (!url) {
1279
+ throw new Error('No se pudo crear la sesión del portal');
1280
+ }
1281
+ patchState(store, {
1282
+ status: 'success',
1283
+ portalUrl: url
1284
+ });
1285
+ window.location.href = url;
1286
+ }
1287
+ catch (error) {
1288
+ console.error('🚨 [PortalAccountStore]', error);
1289
+ const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
1290
+ patchState(store, {
1291
+ status: 'error',
1292
+ error: errorMessage
1293
+ });
1294
+ }
1295
+ },
1296
+ /**
1297
+ * Resets the store to initial state
1298
+ */
1299
+ reset() {
1300
+ patchState(store, initialPortalState);
1301
+ }
1302
+ })));
1303
+
1138
1304
  class EmbeddedSkeletonComponent {
1139
1305
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: EmbeddedSkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1140
1306
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.5", type: EmbeddedSkeletonComponent, isStandalone: true, selector: "lib-embedded-skeleton", ngImport: i0, template: "<div class=\"w-full max-w-md mx-auto p-4\">\n <!-- Header skeleton -->\n <div class=\"flex items-center gap-4 mb-6\">\n <div class=\"skeleton w-16 h-16 rounded-lg\"></div>\n <div class=\"skeleton h-6 w-48\"></div>\n </div>\n\n <!-- Price skeleton -->\n <div class=\"mb-8\">\n <div class=\"skeleton h-10 w-32 mb-2\"></div>\n <div class=\"skeleton h-4 w-24\"></div>\n </div>\n\n <!-- Payment form skeleton -->\n <div class=\"space-y-4\">\n <!-- Email field -->\n <div class=\"skeleton h-12 w-full\"></div>\n \n <!-- Card info field -->\n <div class=\"skeleton h-12 w-full\"></div>\n \n <!-- Name field -->\n <div class=\"skeleton h-12 w-full\"></div>\n \n <!-- Country selector -->\n <div class=\"skeleton h-12 w-full\"></div>\n \n <!-- Submit button -->\n <div class=\"skeleton h-12 w-full mt-6\"></div>\n </div>\n</div> " });
@@ -1217,7 +1383,6 @@ class UtilsService {
1217
1383
  formatAmount(amount, currency = 'EUR') {
1218
1384
  if (!amount)
1219
1385
  return 'N/A';
1220
- console.log('[🛠️ UtilsService - formatAmount] currency: ', currency.toLocaleUpperCase());
1221
1386
  const formatter = new Intl.NumberFormat(currency.toLocaleUpperCase() === 'EUR' ? 'es-ES' : 'en-US', {
1222
1387
  style: 'currency',
1223
1388
  currency: currency || 'USD',
@@ -1266,24 +1431,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
1266
1431
 
1267
1432
  class ProductItemComponent {
1268
1433
  product = input.required();
1434
+ currency = input(Currency.EUR);
1269
1435
  productSelected = output();
1270
1436
  priceSelected = output();
1271
1437
  utils = inject(UtilsService);
1272
- onSelect(price) {
1438
+ sanitizer = inject(DomSanitizer);
1439
+ price = computed(() => this.product().prices.find(price => price.details.currency === this.currency()));
1440
+ sanitizedImage = computed(() => {
1441
+ const imageUrl = this.product().images?.[0];
1442
+ return imageUrl ? this.sanitizer.bypassSecurityTrustUrl(imageUrl) : null;
1443
+ });
1444
+ onSelect() {
1273
1445
  this.productSelected.emit(this.product());
1274
- this.priceSelected.emit(price);
1446
+ this.priceSelected.emit(this.price()?.details);
1275
1447
  }
1276
1448
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ProductItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1277
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: ProductItemComponent, isStandalone: true, selector: "lib-product-item", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { productSelected: "productSelected", priceSelected: "priceSelected" }, ngImport: i0, template: "<div class=\"card bg-base-100 shadow-xl\">\n @if (product().images && product().images.length) {\n <figure>\n <img src=\"{{ product().images[0] }}\" alt=\"{{ product().name }}\" class=\"h-48 w-full object-cover\" />\n </figure>\n }\n <div class=\"card-body\">\n @if (product().active) {\n <div class=\"badge badge-soft badge-success\">Active</div>\n } @else {\n <div class=\"badge badge-soft badge-warning\">Inactive</div>\n }\n\n <h2 class=\"card-title\">{{ product().name }}</h2>\n @if (product().description) {\n <p>{{ product().description }}</p>\n }\n \n @if (product().prices) {\n <div class=\"grid grid-cols-2 gap-4\">\n @for (price of product().prices; track price.details.id) {\n <div class=\"card bg-primary hover:brightness-90 cursor-pointer transition-colors p-6 rounded-2xl\" \n [class.bg-secondary]=\"price.recurringInterval === 'year'\"\n (click)=\"onSelect(price.details)\">\n <div class=\"flex flex-col gap-1\">\n @if (price.recurringInterval !== 'no-recurring') {\n <div class=\"text-xs font-medium text-primary-content\">Pay {{ price.recurringInterval === 'month' ? 'Monthly' : 'Yearly' }}</div>\n } @else {\n <div class=\"text-[11px] font-medium text-primary-content\">One time payment</div>\n }\n <div class=\"flex flex-col items-baseline\">\n <span class=\"text-2xl font-bold text-primary-content\">{{ utils.formatAmount(price.details.unit_amount ?? 0, price.details.currency ?? 'USD') }}</span>\n </div>\n @if (price.recurringInterval === 'year') {\n <div class=\"badge badge-sm badge-neutral text-primary-content\">Save 15%</div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1449
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: ProductItemComponent, isStandalone: true, selector: "lib-product-item", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: true, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { productSelected: "productSelected", priceSelected: "priceSelected" }, ngImport: i0, template: "<div class=\"card bg-base-100 shadow-xl max-w-sm mx-auto rounded-3xl overflow-hidden\">\n <!-- Header with image and favorite icon -->\n <div class=\"relative\">\n @if (sanitizedImage()) {\n <figure class=\"bg-gradient-to-br from-base-200 to-base-300\">\n <img \n [src]=\"sanitizedImage()\" \n alt=\"{{ product().name }}\" \n class=\"h-64 w-full object-cover p-8 rounded-t-3xl\" />\n </figure>\n } @else {\n <div class=\"h-64 bg-gradient-to-br from-base-200 to-base-300 flex items-center justify-center\">\n <div class=\"text-base-content/50 text-6xl\">\uD83D\uDCE6</div>\n </div>\n }\n </div>\n\n <div class=\"card-body p-6 flex flex-col h-full\">\n <!-- Product title -->\n <h2 class=\"card-title text-xl font-bold text-base-content\">\n {{ product().name }}\n </h2>\n\n <!-- Description (moved under title) -->\n @if (product().description) {\n <p class=\"text-base-content/70 text-sm mb-4 line-clamp-3\">\n {{ product().description }}\n </p>\n }\n\n <!-- Price options as text -->\n <div class=\"flex justify-between gap-2\">\n <span class=\"text-xl font-bold text-base-content\">\n @if (price()?.details?.type === 'recurring') {\n {{ utils.formatAmount(price()?.details?.unit_amount ?? 0, price()?.details?.currency ?? 'EUR') }}/{{ price()?.recurringInterval === 'month' ? 'mo' : 'yr' }}\n } @else {\n {{ utils.formatAmount(price()?.details?.unit_amount ?? 0, price()?.details?.currency ?? 'EUR') }}\n }\n </span>\n\n <!-- Status badge (only show if inactive) -->\n @if (!product().active) {\n <div class=\"mt-4\">\n <div class=\"badge badge-warning badge-sm\">Inactive</div>\n </div>\n } @else {\n <button \n class=\"btn btn-primary\"\n (click)=\"onSelect()\">\n Buy now\n </button>\n }\n </div>\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1278
1450
  }
1279
1451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ProductItemComponent, decorators: [{
1280
1452
  type: Component,
1281
- args: [{ selector: 'lib-product-item', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-base-100 shadow-xl\">\n @if (product().images && product().images.length) {\n <figure>\n <img src=\"{{ product().images[0] }}\" alt=\"{{ product().name }}\" class=\"h-48 w-full object-cover\" />\n </figure>\n }\n <div class=\"card-body\">\n @if (product().active) {\n <div class=\"badge badge-soft badge-success\">Active</div>\n } @else {\n <div class=\"badge badge-soft badge-warning\">Inactive</div>\n }\n\n <h2 class=\"card-title\">{{ product().name }}</h2>\n @if (product().description) {\n <p>{{ product().description }}</p>\n }\n \n @if (product().prices) {\n <div class=\"grid grid-cols-2 gap-4\">\n @for (price of product().prices; track price.details.id) {\n <div class=\"card bg-primary hover:brightness-90 cursor-pointer transition-colors p-6 rounded-2xl\" \n [class.bg-secondary]=\"price.recurringInterval === 'year'\"\n (click)=\"onSelect(price.details)\">\n <div class=\"flex flex-col gap-1\">\n @if (price.recurringInterval !== 'no-recurring') {\n <div class=\"text-xs font-medium text-primary-content\">Pay {{ price.recurringInterval === 'month' ? 'Monthly' : 'Yearly' }}</div>\n } @else {\n <div class=\"text-[11px] font-medium text-primary-content\">One time payment</div>\n }\n <div class=\"flex flex-col items-baseline\">\n <span class=\"text-2xl font-bold text-primary-content\">{{ utils.formatAmount(price.details.unit_amount ?? 0, price.details.currency ?? 'USD') }}</span>\n </div>\n @if (price.recurringInterval === 'year') {\n <div class=\"badge badge-sm badge-neutral text-primary-content\">Save 15%</div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n</div> " }]
1453
+ args: [{ selector: 'lib-product-item', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-base-100 shadow-xl max-w-sm mx-auto rounded-3xl overflow-hidden\">\n <!-- Header with image and favorite icon -->\n <div class=\"relative\">\n @if (sanitizedImage()) {\n <figure class=\"bg-gradient-to-br from-base-200 to-base-300\">\n <img \n [src]=\"sanitizedImage()\" \n alt=\"{{ product().name }}\" \n class=\"h-64 w-full object-cover p-8 rounded-t-3xl\" />\n </figure>\n } @else {\n <div class=\"h-64 bg-gradient-to-br from-base-200 to-base-300 flex items-center justify-center\">\n <div class=\"text-base-content/50 text-6xl\">\uD83D\uDCE6</div>\n </div>\n }\n </div>\n\n <div class=\"card-body p-6 flex flex-col h-full\">\n <!-- Product title -->\n <h2 class=\"card-title text-xl font-bold text-base-content\">\n {{ product().name }}\n </h2>\n\n <!-- Description (moved under title) -->\n @if (product().description) {\n <p class=\"text-base-content/70 text-sm mb-4 line-clamp-3\">\n {{ product().description }}\n </p>\n }\n\n <!-- Price options as text -->\n <div class=\"flex justify-between gap-2\">\n <span class=\"text-xl font-bold text-base-content\">\n @if (price()?.details?.type === 'recurring') {\n {{ utils.formatAmount(price()?.details?.unit_amount ?? 0, price()?.details?.currency ?? 'EUR') }}/{{ price()?.recurringInterval === 'month' ? 'mo' : 'yr' }}\n } @else {\n {{ utils.formatAmount(price()?.details?.unit_amount ?? 0, price()?.details?.currency ?? 'EUR') }}\n }\n </span>\n\n <!-- Status badge (only show if inactive) -->\n @if (!product().active) {\n <div class=\"mt-4\">\n <div class=\"badge badge-warning badge-sm\">Inactive</div>\n </div>\n } @else {\n <button \n class=\"btn btn-primary\"\n (click)=\"onSelect()\">\n Buy now\n </button>\n }\n </div>\n </div>\n</div> " }]
1282
1454
  }] });
1283
1455
 
1284
1456
  class ProductListComponent {
1285
1457
  productsStore = inject(ProductsStore);
1286
1458
  products = input([]);
1459
+ currency = input(Currency.EUR);
1287
1460
  productSelected = output();
1288
1461
  priceSelected = output();
1289
1462
  onProductSelect(product) {
@@ -1293,11 +1466,11 @@ class ProductListComponent {
1293
1466
  this.priceSelected.emit(price);
1294
1467
  }
1295
1468
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ProductListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1296
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: ProductListComponent, isStandalone: true, selector: "stripe-product-list", inputs: { products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { productSelected: "productSelected", priceSelected: "priceSelected" }, ngImport: i0, template: "@if (productsStore.isStatusLoading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (item of [1,2,3]; track item) {\n <lib-product-item-skeleton></lib-product-item-skeleton>\n }\n </div>\n} @else if (productsStore.isStatusError()) {\n <div class=\"alert alert-error\">\n <span>{{ productsStore.error() }}</span>\n </div>\n} @else if (!productsStore.hasProducts()) {\n <div class=\"alert alert-info\">\n <span>No products available</span>\n </div>\n} @else {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (product of products(); track product.id) {\n <lib-product-item\n [product]=\"product\"\n (productSelected)=\"onProductSelect($event)\"\n (priceSelected)=\"onPriceSelect($event)\">\n </lib-product-item>\n }\n </div>\n} \n\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ProductItemComponent, selector: "lib-product-item", inputs: ["product"], outputs: ["productSelected", "priceSelected"] }, { kind: "component", type: ProductItemSkeletonComponent, selector: "lib-product-item-skeleton" }] });
1469
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: ProductListComponent, isStandalone: true, selector: "stripe-product-list", inputs: { products: { classPropertyName: "products", publicName: "products", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { productSelected: "productSelected", priceSelected: "priceSelected" }, ngImport: i0, template: "@if (productsStore.isStatusLoading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (item of [1,2,3]; track item) {\n <lib-product-item-skeleton></lib-product-item-skeleton>\n }\n </div>\n}\n\n@if (productsStore.isStatusError() && !productsStore.isStatusLoading()) {\n <div class=\"alert alert-error\">\n <span>{{ productsStore.error() }}</span>\n </div>\n}\n\n@if (!productsStore.hasProducts()) {\n <div class=\"alert alert-info\">\n <span>No products available</span>\n </div>\n}\n\n@if (productsStore.hasProducts()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (product of products(); track product.id) {\n <lib-product-item\n [product]=\"product\"\n [currency]=\"currency()\"\n (productSelected)=\"onProductSelect($event)\"\n (priceSelected)=\"onPriceSelect($event)\">\n </lib-product-item>\n }\n </div>\n} \n\n\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ProductItemComponent, selector: "lib-product-item", inputs: ["product", "currency"], outputs: ["productSelected", "priceSelected"] }, { kind: "component", type: ProductItemSkeletonComponent, selector: "lib-product-item-skeleton" }] });
1297
1470
  }
1298
1471
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: ProductListComponent, decorators: [{
1299
1472
  type: Component,
1300
- args: [{ selector: 'stripe-product-list', standalone: true, imports: [CommonModule, ProductItemComponent, ProductItemSkeletonComponent], template: "@if (productsStore.isStatusLoading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (item of [1,2,3]; track item) {\n <lib-product-item-skeleton></lib-product-item-skeleton>\n }\n </div>\n} @else if (productsStore.isStatusError()) {\n <div class=\"alert alert-error\">\n <span>{{ productsStore.error() }}</span>\n </div>\n} @else if (!productsStore.hasProducts()) {\n <div class=\"alert alert-info\">\n <span>No products available</span>\n </div>\n} @else {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (product of products(); track product.id) {\n <lib-product-item\n [product]=\"product\"\n (productSelected)=\"onProductSelect($event)\"\n (priceSelected)=\"onPriceSelect($event)\">\n </lib-product-item>\n }\n </div>\n} \n\n\n" }]
1473
+ args: [{ selector: 'stripe-product-list', standalone: true, imports: [CommonModule, ProductItemComponent, ProductItemSkeletonComponent], template: "@if (productsStore.isStatusLoading()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (item of [1,2,3]; track item) {\n <lib-product-item-skeleton></lib-product-item-skeleton>\n }\n </div>\n}\n\n@if (productsStore.isStatusError() && !productsStore.isStatusLoading()) {\n <div class=\"alert alert-error\">\n <span>{{ productsStore.error() }}</span>\n </div>\n}\n\n@if (!productsStore.hasProducts()) {\n <div class=\"alert alert-info\">\n <span>No products available</span>\n </div>\n}\n\n@if (productsStore.hasProducts()) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n @for (product of products(); track product.id) {\n <lib-product-item\n [product]=\"product\"\n [currency]=\"currency()\"\n (productSelected)=\"onProductSelect($event)\"\n (priceSelected)=\"onPriceSelect($event)\">\n </lib-product-item>\n }\n </div>\n} \n\n\n" }]
1301
1474
  }] });
1302
1475
 
1303
1476
  class EmbeddedSubscriptionComponent {
@@ -1357,6 +1530,173 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
1357
1530
  args: [{ selector: 'lib-subscription-return-page', standalone: true, imports: [CommonModule], template: "<div class=\"flex justify-center min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-4\">\n <div class=\"hero-content text-center max-w-md\">\n <div class=\"card w-full bg-base-100 shadow-2xl\">\n <div class=\"card-body items-center text-center\">\n <!-- Success Icon -->\n <div class=\"mb-6\">\n <div class=\"avatar\">\n <div class=\"w-24 h-24 rounded-full bg-success/10 flex items-center justify-center\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-full w-full text-success\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n </div>\n </div>\n\n <!-- Status Content -->\n @if (checkoutStore.isStatusLoading()) {\n <div class=\"flex flex-col items-center gap-4 mb-6\">\n <span class=\"loading loading-spinner loading-lg text-primary\"></span>\n <div class=\"alert alert-info\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" class=\"stroke-current shrink-0 w-6 h-6\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n </svg>\n <span>Cargando detalles de la suscripci\u00F3n...</span>\n </div>\n </div>\n } @else if (checkoutStore.isStatusError()) {\n <div class=\"alert alert-error mb-6\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <span>{{ subscriptionsStore.error() }}</span>\n </div>\n } @else if (checkoutStore.isPaymentComplete()) {\n\n <!-- Title and Description -->\n <h2 class=\"card-title text-3xl font-bold text-base-content mb-2\">{{ sessionStatus()?.amount_total }}</h2>\n\n <div class=\"alert alert-success mb-6\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <span class=\"font-medium\">Tu suscripci\u00F3n est\u00E1 activa y lista para usar.</span>\n </div>\n\n <div class=\"bg-base-200 rounded-lg p-4 mb-6 w-full\">\n <h3 class=\"font-semibold text-sm text-base-content/80 mb-3\">Beneficios incluidos:</h3>\n <ul class=\"space-y-2\">\n <li class=\"flex items-center text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-success mr-2\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n Acceso completo a todas las funciones\n </li>\n <li class=\"flex items-center text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-success mr-2\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n Soporte prioritario 24/7\n </li>\n <li class=\"flex items-center text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-success mr-2\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n Actualizaciones autom\u00E1ticas\n </li>\n </ul>\n </div>\n }\n\n <!-- Action Buttons -->\n <div class=\"card-actions justify-center w-full\">\n <button class=\"btn btn-primary btn-wide\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5 mr-2\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6\"\n />\n </svg>\n Ir al Panel Principal\n </button>\n </div>\n\n <!-- Additional Help -->\n <div class=\"text-center mt-4\">\n <p class=\"text-xs text-base-content/50\">\n \u00BFNecesitas ayuda?\n <a href=\"#\" class=\"link link-primary\">Contacta soporte</a>\n </p>\n </div>\n </div>\n </div>\n </div>\n</div>" }]
1358
1531
  }] });
1359
1532
 
1533
+ class BrandIconDirective {
1534
+ el;
1535
+ paymentMethod = input.required({ alias: 'ngxBrandIcon' });
1536
+ constructor(el) {
1537
+ this.el = el;
1538
+ effect(() => {
1539
+ const icon = this.getBrandIcon();
1540
+ this.el.nativeElement.textContent = icon;
1541
+ });
1542
+ }
1543
+ getBrandIcon() {
1544
+ const brand = this.paymentMethod().card?.brand;
1545
+ const brandIcons = {
1546
+ 'visa': '💳',
1547
+ 'mastercard': '💳',
1548
+ 'amex': '💳',
1549
+ 'discover': '💳',
1550
+ 'diners': '💳',
1551
+ 'jcb': '💳',
1552
+ 'unionpay': '💳',
1553
+ 'unknown': '💳'
1554
+ };
1555
+ return brandIcons[brand || 'unknown'] || '💳';
1556
+ }
1557
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrandIconDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1558
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.5", type: BrandIconDirective, isStandalone: true, selector: "[ngxBrandIcon]", inputs: { paymentMethod: { classPropertyName: "paymentMethod", publicName: "ngxBrandIcon", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
1559
+ }
1560
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrandIconDirective, decorators: [{
1561
+ type: Directive,
1562
+ args: [{
1563
+ selector: '[ngxBrandIcon]',
1564
+ standalone: true
1565
+ }]
1566
+ }], ctorParameters: () => [{ type: i0.ElementRef }] });
1567
+
1568
+ class BrandNameDirective {
1569
+ el;
1570
+ paymentMethod = input.required({ alias: 'ngxBrandName' });
1571
+ constructor(el) {
1572
+ this.el = el;
1573
+ effect(() => {
1574
+ const name = this.getBrandName();
1575
+ this.el.nativeElement.textContent = name;
1576
+ });
1577
+ }
1578
+ getBrandName() {
1579
+ const brand = this.paymentMethod().card?.brand;
1580
+ const brandNames = {
1581
+ 'visa': 'Visa',
1582
+ 'mastercard': 'Mastercard',
1583
+ 'amex': 'American Express',
1584
+ 'discover': 'Discover',
1585
+ 'diners': 'Diners Club',
1586
+ 'jcb': 'JCB',
1587
+ 'unionpay': 'UnionPay',
1588
+ 'unknown': 'Tarjeta'
1589
+ };
1590
+ return brandNames[brand || 'unknown'] || 'Tarjeta';
1591
+ }
1592
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrandNameDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1593
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.5", type: BrandNameDirective, isStandalone: true, selector: "[ngxBrandName]", inputs: { paymentMethod: { classPropertyName: "paymentMethod", publicName: "ngxBrandName", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
1594
+ }
1595
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: BrandNameDirective, decorators: [{
1596
+ type: Directive,
1597
+ args: [{
1598
+ selector: '[ngxBrandName]',
1599
+ standalone: true
1600
+ }]
1601
+ }], ctorParameters: () => [{ type: i0.ElementRef }] });
1602
+
1603
+ class PaymentMethodTypeDirective {
1604
+ el;
1605
+ paymentMethod = input.required({ alias: 'ngxPaymentMethodType' });
1606
+ constructor(el) {
1607
+ this.el = el;
1608
+ effect(() => {
1609
+ const type = this.getPaymentMethodType();
1610
+ this.el.nativeElement.textContent = type;
1611
+ });
1612
+ }
1613
+ getPaymentMethodType() {
1614
+ const typeNames = {
1615
+ 'card': 'Tarjeta',
1616
+ 'paypal': 'PayPal',
1617
+ 'klarna': 'Klarna',
1618
+ 'afterpay_clearpay': 'Afterpay',
1619
+ 'alipay': 'Alipay',
1620
+ 'bancontact': 'Bancontact',
1621
+ 'eps': 'EPS',
1622
+ 'giropay': 'Giropay',
1623
+ 'ideal': 'iDEAL',
1624
+ 'p24': 'Przelewy24',
1625
+ 'sepa_debit': 'SEPA Direct Debit',
1626
+ 'sofort': 'Sofort',
1627
+ 'wechat_pay': 'WeChat Pay'
1628
+ };
1629
+ return typeNames[this.paymentMethod().type] || 'Método de pago';
1630
+ }
1631
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentMethodTypeDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1632
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.5", type: PaymentMethodTypeDirective, isStandalone: true, selector: "[ngxPaymentMethodType]", inputs: { paymentMethod: { classPropertyName: "paymentMethod", publicName: "ngxPaymentMethodType", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
1633
+ }
1634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentMethodTypeDirective, decorators: [{
1635
+ type: Directive,
1636
+ args: [{
1637
+ selector: '[ngxPaymentMethodType]',
1638
+ standalone: true
1639
+ }]
1640
+ }], ctorParameters: () => [{ type: i0.ElementRef }] });
1641
+
1642
+ class PaymentMethodComponent {
1643
+ paymentMethod = input.required();
1644
+ mode = input('detailed');
1645
+ get last4() {
1646
+ return this.paymentMethod().card?.last4 || '****';
1647
+ }
1648
+ get expirationDate() {
1649
+ const card = this.paymentMethod().card;
1650
+ if (!card?.exp_month || !card?.exp_year)
1651
+ return '';
1652
+ return `${card.exp_month.toString().padStart(2, '0')}/${card.exp_year.toString().slice(-2)}`;
1653
+ }
1654
+ get holderName() {
1655
+ return this.paymentMethod().billing_details?.name || 'N/A';
1656
+ }
1657
+ get isExpired() {
1658
+ const card = this.paymentMethod().card;
1659
+ if (!card?.exp_month || !card?.exp_year)
1660
+ return false;
1661
+ const now = new Date();
1662
+ const expDate = new Date(card.exp_year, card.exp_month - 1);
1663
+ return expDate < now;
1664
+ }
1665
+ get cardFunding() {
1666
+ return this.paymentMethod().card?.funding;
1667
+ }
1668
+ get cardCountry() {
1669
+ return this.paymentMethod().card?.country || undefined;
1670
+ }
1671
+ get billingEmail() {
1672
+ return this.paymentMethod().billing_details?.email || undefined;
1673
+ }
1674
+ get billingPhone() {
1675
+ return this.paymentMethod().billing_details?.phone || undefined;
1676
+ }
1677
+ get billingAddress() {
1678
+ return this.paymentMethod().billing_details?.address;
1679
+ }
1680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentMethodComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentMethodComponent, isStandalone: true, selector: "ngx-payment-method", inputs: { paymentMethod: { classPropertyName: "paymentMethod", publicName: "paymentMethod", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!-- Detailed Version -->\n@if (mode() === 'detailed') {\n <div class=\"w-full max-w-md mx-auto\">\n <!-- Stack of Credit Cards -->\n <div class=\"stack w-full h-56\">\n <div class=\"bg-gradient-to-br from-primary to-accent text-neutral-content relative place-content-center rounded-box\">\n <div class=\"card-body p-6 relative z-10\">\n <!-- Card Header -->\n <div class=\"flex justify-between items-start mb-6\">\n <div class=\"flex items-center gap-2\">\n <!--<span class=\"text-3xl filter drop-shadow-lg\" [ngxBrandIcon]=\"paymentMethod()\"></span>-->\n <div class=\"font-bold text-lg text-primary-content\" [ngxBrandName]=\"paymentMethod()\"></div>\n @if (cardCountry) {\n <div class=\"text-xs text-primary-content/60 uppercase\">{{ cardCountry }}</div>\n }\n @if (isExpired) {\n <div class=\"badge badge-error badge-sm animate-pulse\">Expirada</div>\n }\n </div>\n </div>\n\n <!-- Card Number -->\n <div class=\"mb-6\">\n <div class=\"font-mono text-xl tracking-wider text-primary-content\">\n **** **** **** {{ last4 }}\n </div>\n </div>\n\n <!-- Card Bottom Info -->\n <div class=\"flex justify-start items-end\">\n <div class=\"flex-1\">\n <div class=\"text-xs text-primary-content/60 mb-1\">TITULAR</div>\n <div class=\"font-medium text-sm text-primary-content truncate max-w-32\">{{ holderName }}</div>\n </div>\n \n @if (expirationDate) {\n <div class=\"flex-1\">\n <div class=\"text-xs text-primary-content/60 mb-1\">V\u00C1LIDA HASTA</div>\n <div class=\"font-mono text-sm text-primary-content\">{{ expirationDate }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Card Background Pattern -->\n <div class=\"absolute inset-0 opacity-20\">\n <div class=\"absolute top-4 right-4 w-20 h-20 rounded-full bg-primary-content/30\"></div>\n <div class=\"absolute bottom-4 left-4 w-16 h-16 rounded-full bg-primary-content/20\"></div>\n <div class=\"absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-32 h-32 rounded-full bg-primary-content/10\"></div>\n </div>\n </div>\n <div class=\"bg-secondary grid place-content-center rounded-box\"></div>\n <div class=\"bg-accent grid place-content-center rounded-box\"></div>\n </div>\n\n <!-- Additional Information Below Cards -->\n <div class=\"mt-6 space-y-4\">\n <!-- Billing Information -->\n @if (billingEmail || billingPhone) {\n <div class=\"card bg-base-100 shadow-sm border border-base-200\">\n <div class=\"card-body p-4\">\n <h4 class=\"card-title text-sm mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\" />\n </svg>\n Informaci\u00F3n de Contacto\n </h4>\n \n <div class=\"space-y-2\">\n @if (billingEmail) {\n <div class=\"flex items-center gap-2 text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-base-content/60\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207\" />\n </svg>\n <span class=\"text-base-content/70\">{{ billingEmail }}</span>\n </div>\n }\n \n @if (billingPhone) {\n <div class=\"flex items-center gap-2 text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-base-content/60\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z\" />\n </svg>\n <span class=\"text-base-content/70\">{{ billingPhone }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Billing Address -->\n @if (billingAddress && (billingAddress.line1 || billingAddress.city)) {\n <div class=\"card bg-base-100 shadow-sm border border-base-200\">\n <div class=\"card-body p-4\">\n <h4 class=\"card-title text-sm mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\" />\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Direcci\u00F3n de Facturaci\u00F3n\n </h4>\n \n <div class=\"text-sm text-base-content/70 space-y-1\">\n @if (billingAddress.line1) {\n <div>{{ billingAddress.line1 }}</div>\n @if (billingAddress.line2) {\n <div>{{ billingAddress.line2 }}</div>\n }\n }\n \n <div class=\"flex gap-2 flex-wrap\">\n @if (billingAddress.city) {\n <span>{{ billingAddress.city }}</span>\n }\n @if (billingAddress.postal_code) {\n <span>{{ billingAddress.postal_code }}</span>\n }\n @if (billingAddress.country) {\n <span class=\"uppercase\">{{ billingAddress.country }}</span>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Card Creation Date -->\n <div class=\"flex items-center justify-between text-sm text-base-content/60 px-2\">\n <span>M\u00E9todo agregado:</span>\n <span>{{ paymentMethod().created * 1000 | date:'short' }}</span>\n </div>\n </div>\n </div>\n}\n\n<!-- Compact Version -->\n@if (mode() === 'compact') {\n <div class=\"flex items-center gap-2\">\n <!--<span class=\"text-xl filter drop-shadow-sm\" [ngxBrandIcon]=\"paymentMethod()\"></span>-->\n \n <div class=\"flex-1 min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span class=\"font-medium\" [ngxBrandName]=\"paymentMethod()\"></span>\n <span class=\"text-base-content/70\">\u2022\u2022\u2022\u2022 {{ last4 }}</span>\n @if (paymentMethod().type === 'card' && expirationDate) {\n <span class=\"text-base-content/70 text-sm\">{{ expirationDate }}</span>\n }\n </div>\n </div>\n\n @if (isExpired) {\n <div class=\"badge badge-error badge-sm animate-pulse\">Expirada</div>\n } @else {\n <div class=\"badge badge-success badge-sm\">Activa</div>\n }\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "directive", type: BrandNameDirective, selector: "[ngxBrandName]", inputs: ["ngxBrandName"] }] });
1682
+ }
1683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentMethodComponent, decorators: [{
1684
+ type: Component,
1685
+ args: [{ selector: 'ngx-payment-method', standalone: true, imports: [CommonModule, BrandIconDirective, BrandNameDirective, PaymentMethodTypeDirective], template: "<!-- Detailed Version -->\n@if (mode() === 'detailed') {\n <div class=\"w-full max-w-md mx-auto\">\n <!-- Stack of Credit Cards -->\n <div class=\"stack w-full h-56\">\n <div class=\"bg-gradient-to-br from-primary to-accent text-neutral-content relative place-content-center rounded-box\">\n <div class=\"card-body p-6 relative z-10\">\n <!-- Card Header -->\n <div class=\"flex justify-between items-start mb-6\">\n <div class=\"flex items-center gap-2\">\n <!--<span class=\"text-3xl filter drop-shadow-lg\" [ngxBrandIcon]=\"paymentMethod()\"></span>-->\n <div class=\"font-bold text-lg text-primary-content\" [ngxBrandName]=\"paymentMethod()\"></div>\n @if (cardCountry) {\n <div class=\"text-xs text-primary-content/60 uppercase\">{{ cardCountry }}</div>\n }\n @if (isExpired) {\n <div class=\"badge badge-error badge-sm animate-pulse\">Expirada</div>\n }\n </div>\n </div>\n\n <!-- Card Number -->\n <div class=\"mb-6\">\n <div class=\"font-mono text-xl tracking-wider text-primary-content\">\n **** **** **** {{ last4 }}\n </div>\n </div>\n\n <!-- Card Bottom Info -->\n <div class=\"flex justify-start items-end\">\n <div class=\"flex-1\">\n <div class=\"text-xs text-primary-content/60 mb-1\">TITULAR</div>\n <div class=\"font-medium text-sm text-primary-content truncate max-w-32\">{{ holderName }}</div>\n </div>\n \n @if (expirationDate) {\n <div class=\"flex-1\">\n <div class=\"text-xs text-primary-content/60 mb-1\">V\u00C1LIDA HASTA</div>\n <div class=\"font-mono text-sm text-primary-content\">{{ expirationDate }}</div>\n </div>\n }\n </div>\n </div>\n\n <!-- Card Background Pattern -->\n <div class=\"absolute inset-0 opacity-20\">\n <div class=\"absolute top-4 right-4 w-20 h-20 rounded-full bg-primary-content/30\"></div>\n <div class=\"absolute bottom-4 left-4 w-16 h-16 rounded-full bg-primary-content/20\"></div>\n <div class=\"absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-32 h-32 rounded-full bg-primary-content/10\"></div>\n </div>\n </div>\n <div class=\"bg-secondary grid place-content-center rounded-box\"></div>\n <div class=\"bg-accent grid place-content-center rounded-box\"></div>\n </div>\n\n <!-- Additional Information Below Cards -->\n <div class=\"mt-6 space-y-4\">\n <!-- Billing Information -->\n @if (billingEmail || billingPhone) {\n <div class=\"card bg-base-100 shadow-sm border border-base-200\">\n <div class=\"card-body p-4\">\n <h4 class=\"card-title text-sm mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\" />\n </svg>\n Informaci\u00F3n de Contacto\n </h4>\n \n <div class=\"space-y-2\">\n @if (billingEmail) {\n <div class=\"flex items-center gap-2 text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-base-content/60\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207\" />\n </svg>\n <span class=\"text-base-content/70\">{{ billingEmail }}</span>\n </div>\n }\n \n @if (billingPhone) {\n <div class=\"flex items-center gap-2 text-sm\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 text-base-content/60\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z\" />\n </svg>\n <span class=\"text-base-content/70\">{{ billingPhone }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n <!-- Billing Address -->\n @if (billingAddress && (billingAddress.line1 || billingAddress.city)) {\n <div class=\"card bg-base-100 shadow-sm border border-base-200\">\n <div class=\"card-body p-4\">\n <h4 class=\"card-title text-sm mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\" />\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Direcci\u00F3n de Facturaci\u00F3n\n </h4>\n \n <div class=\"text-sm text-base-content/70 space-y-1\">\n @if (billingAddress.line1) {\n <div>{{ billingAddress.line1 }}</div>\n @if (billingAddress.line2) {\n <div>{{ billingAddress.line2 }}</div>\n }\n }\n \n <div class=\"flex gap-2 flex-wrap\">\n @if (billingAddress.city) {\n <span>{{ billingAddress.city }}</span>\n }\n @if (billingAddress.postal_code) {\n <span>{{ billingAddress.postal_code }}</span>\n }\n @if (billingAddress.country) {\n <span class=\"uppercase\">{{ billingAddress.country }}</span>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- Card Creation Date -->\n <div class=\"flex items-center justify-between text-sm text-base-content/60 px-2\">\n <span>M\u00E9todo agregado:</span>\n <span>{{ paymentMethod().created * 1000 | date:'short' }}</span>\n </div>\n </div>\n </div>\n}\n\n<!-- Compact Version -->\n@if (mode() === 'compact') {\n <div class=\"flex items-center gap-2\">\n <!--<span class=\"text-xl filter drop-shadow-sm\" [ngxBrandIcon]=\"paymentMethod()\"></span>-->\n \n <div class=\"flex-1 min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span class=\"font-medium\" [ngxBrandName]=\"paymentMethod()\"></span>\n <span class=\"text-base-content/70\">\u2022\u2022\u2022\u2022 {{ last4 }}</span>\n @if (paymentMethod().type === 'card' && expirationDate) {\n <span class=\"text-base-content/70 text-sm\">{{ expirationDate }}</span>\n }\n </div>\n </div>\n\n @if (isExpired) {\n <div class=\"badge badge-error badge-sm animate-pulse\">Expirada</div>\n } @else {\n <div class=\"badge badge-success badge-sm\">Activa</div>\n }\n </div>\n}\n" }]
1686
+ }] });
1687
+
1688
+ class PaymentIntentsDialogComponent {
1689
+ paymentIntent = input.required();
1690
+ dialogId = input.required();
1691
+ utils = new UtilsService();
1692
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentsDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1693
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentIntentsDialogComponent, isStandalone: true, selector: "lib-payment-intents-dialog", inputs: { paymentIntent: { classPropertyName: "paymentIntent", publicName: "paymentIntent", isSignal: true, isRequired: true, transformFunction: null }, dialogId: { classPropertyName: "dialogId", publicName: "dialogId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<dialog [id]=\"dialogId()\" class=\"modal\">\n <div class=\"modal-box max-w-2xl\">\n <h3 class=\"font-bold text-lg mb-4\">Detalles del Payment Intent</h3>\n \n <!-- Header with Amount and Status -->\n <div class=\"flex items-center gap-3 mb-6 p-4 bg-base-200 rounded-lg\">\n <div class=\"flex-1\">\n <div class=\"text-3xl font-bold text-primary\">\n {{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}\n </div>\n <div class=\"text-sm text-base-content/70\">\n {{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}\n </div>\n </div>\n <div class=\"flex gap-2\">\n <div class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }}\">\n {{ paymentIntent().status }}\n </div>\n @if (!paymentIntent().liveMode) {\n <div class=\"badge badge-warning\">TEST</div>\n }\n </div>\n </div>\n\n <div class=\"space-y-6\">\n <!-- Transaction Details -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Detalles de la Transacci\u00F3n\n </h4>\n \n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Estado</div>\n <div class=\"stat-value text-sm capitalize\">{{ paymentIntent().status }}</div>\n <div class=\"stat-desc\">{{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</div>\n </div>\n \n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Monto</div>\n <div class=\"stat-value text-sm\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</div>\n <div class=\"stat-desc\">{{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}</div>\n </div>\n </div>\n\n <!-- Confirmation Method -->\n <div class=\"mt-3 flex items-center gap-2 text-sm p-2 bg-base-200 rounded\">\n <div class=\"w-2 h-2 bg-success rounded-full\"></div>\n <span class=\"text-base-content/70\">Confirmaci\u00F3n:</span>\n <span class=\"capitalize font-medium\">{{ paymentIntent().confirmationMethod.replace('_', ' ') }}</span>\n </div>\n </div>\n\n <!-- Payment Information -->\n @if (paymentIntent().paymentMethod) {\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n Informaci\u00F3n del Pago\n </h4>\n \n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"detailed\">\n </ngx-payment-method>\n </div>\n }\n\n <!-- Additional Information -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n Informaci\u00F3n Adicional\n </h4>\n \n <div class=\"space-y-2\">\n @if (paymentIntent().invoiceId) {\n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Factura</span>\n <span class=\"text-sm font-mono\">{{ paymentIntent().invoiceId.slice(-8) }}</span>\n </div>\n }\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Entorno</span>\n <span class=\"text-sm\">\n @if (paymentIntent().liveMode) {\n <span class=\"text-success\">Producci\u00F3n</span>\n } @else {\n <span class=\"text-warning\">Pruebas</span>\n }\n </span>\n </div>\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Referencia</span>\n <span class=\"text-sm font-mono\">\u2022\u2022\u2022{{ paymentIntent().id?.slice(-6) || 'N/A' }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"modal-action\">\n <form method=\"dialog\">\n <button class=\"btn btn-primary\">Cerrar</button>\n </form>\n </div>\n </div>\n</dialog>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PaymentMethodComponent, selector: "ngx-payment-method", inputs: ["paymentMethod", "mode"] }] });
1694
+ }
1695
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentsDialogComponent, decorators: [{
1696
+ type: Component,
1697
+ args: [{ selector: 'lib-payment-intents-dialog', standalone: true, imports: [CommonModule, PaymentMethodComponent], template: "<dialog [id]=\"dialogId()\" class=\"modal\">\n <div class=\"modal-box max-w-2xl\">\n <h3 class=\"font-bold text-lg mb-4\">Detalles del Payment Intent</h3>\n \n <!-- Header with Amount and Status -->\n <div class=\"flex items-center gap-3 mb-6 p-4 bg-base-200 rounded-lg\">\n <div class=\"flex-1\">\n <div class=\"text-3xl font-bold text-primary\">\n {{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}\n </div>\n <div class=\"text-sm text-base-content/70\">\n {{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}\n </div>\n </div>\n <div class=\"flex gap-2\">\n <div class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }}\">\n {{ paymentIntent().status }}\n </div>\n @if (!paymentIntent().liveMode) {\n <div class=\"badge badge-warning\">TEST</div>\n }\n </div>\n </div>\n\n <div class=\"space-y-6\">\n <!-- Transaction Details -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Detalles de la Transacci\u00F3n\n </h4>\n \n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Estado</div>\n <div class=\"stat-value text-sm capitalize\">{{ paymentIntent().status }}</div>\n <div class=\"stat-desc\">{{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</div>\n </div>\n \n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Monto</div>\n <div class=\"stat-value text-sm\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</div>\n <div class=\"stat-desc\">{{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}</div>\n </div>\n </div>\n\n <!-- Confirmation Method -->\n <div class=\"mt-3 flex items-center gap-2 text-sm p-2 bg-base-200 rounded\">\n <div class=\"w-2 h-2 bg-success rounded-full\"></div>\n <span class=\"text-base-content/70\">Confirmaci\u00F3n:</span>\n <span class=\"capitalize font-medium\">{{ paymentIntent().confirmationMethod.replace('_', ' ') }}</span>\n </div>\n </div>\n\n <!-- Payment Information -->\n @if (paymentIntent().paymentMethod) {\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n Informaci\u00F3n del Pago\n </h4>\n \n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"detailed\">\n </ngx-payment-method>\n </div>\n }\n\n <!-- Additional Information -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n Informaci\u00F3n Adicional\n </h4>\n \n <div class=\"space-y-2\">\n @if (paymentIntent().invoiceId) {\n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Factura</span>\n <span class=\"text-sm font-mono\">{{ paymentIntent().invoiceId.slice(-8) }}</span>\n </div>\n }\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Entorno</span>\n <span class=\"text-sm\">\n @if (paymentIntent().liveMode) {\n <span class=\"text-success\">Producci\u00F3n</span>\n } @else {\n <span class=\"text-warning\">Pruebas</span>\n }\n </span>\n </div>\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Referencia</span>\n <span class=\"text-sm font-mono\">\u2022\u2022\u2022{{ paymentIntent().id?.slice(-6) || 'N/A' }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"modal-action\">\n <form method=\"dialog\">\n <button class=\"btn btn-primary\">Cerrar</button>\n </form>\n </div>\n </div>\n</dialog>\n" }]
1698
+ }] });
1699
+
1360
1700
  class PaymentIntentsTableComponent {
1361
1701
  paymentIntents = input.required();
1362
1702
  paymentIntentsTable = computed(() => {
@@ -1407,13 +1747,15 @@ class PaymentIntentsTableComponent {
1407
1747
  this.exportSelected.emit(this.paymentIntentsTable().filter(paymentIntent => paymentIntent.selected));
1408
1748
  }
1409
1749
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentsTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1410
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentIntentsTableComponent, isStandalone: true, selector: "lib-payment-intents-table", inputs: { paymentIntents: { classPropertyName: "paymentIntents", publicName: "paymentIntents", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, withControls: { classPropertyName: "withControls", publicName: "withControls", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { exportSelected: "exportSelected", onRefresh: "onRefresh" }, ngImport: i0, template: "<div class=\"payment-intents-container\">\n @if (withControls()) {\n <div class=\"controls mb-4 flex justify-between items-center\">\n <div class=\"stats\">\n @if (hasPaymentIntents()) {\n <span class=\"badge badge-soft badge-info\">{{ paymentIntentsTable().length }} payment intents</span>\n }\n </div>\n <div class=\"flex justify-end items-center gap-2\">\n <button class=\"btn btn-sm btn-outline\" (click)=\"refreshPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 mr-1\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n Actualizar\n </button>\n <button class=\"btn btn-sm btn-primary\" [disabled]=\"!somePaymentIntentsSelected()\" (click)=\"exportSelectedPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M8.71 7.71L11 5.41V15a1 1 0 0 0 2 0V5.41l2.29 2.3a1 1 0 0 0 1.42 0a1 1 0 0 0 0-1.42l-4-4a1 1 0 0 0-.33-.21a1 1 0 0 0-.76 0a1 1 0 0 0-.33.21l-4 4a1 1 0 1 0 1.42 1.42M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1\"/></svg>\n Export Selected\n </button>\n </div>\n </div>\n }\n\n @if (loading()) {\n <div class=\"overflow-x-auto\">\n <div class=\"skeleton h-32 w-full\"></div>\n </div>\n } @else if (error()) {\n <div class=\"error-container\">\n <div class=\"alert alert-error\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" /></svg>\n <span>{{ error() }}</span>\n </div>\n </div>\n } @else if (hasPaymentIntents()) {\n <div class=\"overflow-x-auto rounded-box bg-base-100\">\n <table class=\"table table-zebra\">\n <!-- head -->\n <thead>\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"toggleAllPaymentIntents($event)\" [checked]=\"allPaymentIntentsSelected()\" />\n </label>\n </th>\n <th>Amount</th>\n <th>Payment Method</th>\n <th>Status</th>\n <th>Mode</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (paymentIntent of paymentIntentsTable(); track trackByPaymentIntentId(0, paymentIntent)) {\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"togglePaymentIntent($event, paymentIntent.id ?? '')\" [checked]=\"paymentIntent.selected\" />\n </label>\n </th>\n <td>{{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</td>\n <td>\n <span class=\"badge badge-outline\">\n {{ paymentIntent.paymentMethodId }}\n </span>\n </td>\n <td>\n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status}}\n </span>\n </td>\n <td>{{ paymentIntent.liveMode ? 'Live' : 'Test' }}</td>\n <td>\n <button class=\"btn btn-xs btn-ghost\" (click)=\"showDetails(paymentIntent.id ?? '')\">\n Details\n </button>\n <dialog id=\"modal-{{ paymentIntent.id }}\" class=\"modal\">\n <div class=\"modal-box\">\n <h3 class=\"font-bold text-lg\">Payment Intent Details</h3>\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Payment Details</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Full ID:</span> {{ paymentIntent.id }}</li>\n <li><span class=\"font-medium\">Payment Method:</span> {{ paymentIntent.paymentMethodId }}</li>\n <li><span class=\"font-medium\">Confirmation Method:</span> {{ paymentIntent.confirmationMethod }}</li>\n @if (paymentIntent.invoiceId) {\n <li><span class=\"font-medium\">Invoice ID:</span> {{ paymentIntent.invoiceId }}</li>\n }\n </ul>\n </div>\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Status Information</h4>\n <ul class=\"space-y-1 text-sm\">\n <li>\n <span class=\"font-medium\">Status: </span> \n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status }}\n </span>\n </li>\n <li><span class=\"font-medium\">Mode: </span> {{ paymentIntent.liveMode ? 'Production' : 'Test' }}</li>\n <li><span class=\"font-medium\">Amount: </span> {{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</li>\n </ul>\n </div>\n </div>\n <div class=\"modal-action\">\n <form method=\"dialog\">\n <button class=\"btn\">Close</button>\n </form>\n </div>\n </div>\n </dialog>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <div class=\"card bg-base-100 shadow-sm\">\n <div class=\"card-body items-center text-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-12 w-12 text-gray-400 mb-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n <h2 class=\"card-title text-xl\">No recent payment intents</h2>\n <p class=\"text-gray-600\">No payment intents found in your history.</p>\n </div>\n </div>\n </div>\n }\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1750
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentIntentsTableComponent, isStandalone: true, selector: "lib-payment-intents-table", inputs: { paymentIntents: { classPropertyName: "paymentIntents", publicName: "paymentIntents", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, withControls: { classPropertyName: "withControls", publicName: "withControls", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { exportSelected: "exportSelected", onRefresh: "onRefresh" }, ngImport: i0, template: "<div class=\"payment-intents-container\">\n @if (withControls()) {\n <div class=\"controls mb-4 flex justify-between items-center\">\n <div class=\"stats\">\n @if (hasPaymentIntents()) {\n <span class=\"badge badge-soft badge-info\">{{ paymentIntentsTable().length }} payment intents</span>\n }\n </div>\n <div class=\"flex justify-end items-center gap-2\">\n <button class=\"btn btn-sm btn-outline\" (click)=\"refreshPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 mr-1\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n Actualizar\n </button>\n <button class=\"btn btn-sm btn-primary\" [disabled]=\"!somePaymentIntentsSelected()\" (click)=\"exportSelectedPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M8.71 7.71L11 5.41V15a1 1 0 0 0 2 0V5.41l2.29 2.3a1 1 0 0 0 1.42 0a1 1 0 0 0 0-1.42l-4-4a1 1 0 0 0-.33-.21a1 1 0 0 0-.76 0a1 1 0 0 0-.33.21l-4 4a1 1 0 1 0 1.42 1.42M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1\"/></svg>\n Export Selected\n </button>\n </div>\n </div>\n }\n\n @if (loading()) {\n <div class=\"overflow-x-auto\">\n <div class=\"skeleton h-32 w-full\"></div>\n </div>\n } @else if (error()) {\n <div class=\"error-container\">\n <div class=\"alert alert-error\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" /></svg>\n <span>{{ error() }}</span>\n </div>\n </div>\n } @else if (hasPaymentIntents()) {\n <div class=\"overflow-x-auto rounded-box bg-base-100\">\n <table class=\"table table-zebra\">\n <!-- head -->\n <thead>\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"toggleAllPaymentIntents($event)\" [checked]=\"allPaymentIntentsSelected()\" />\n </label>\n </th>\n <th>Amount</th>\n <th>Payment Method</th>\n <th>Status</th>\n <th>Mode</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (paymentIntent of paymentIntentsTable(); track trackByPaymentIntentId(0, paymentIntent)) {\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"togglePaymentIntent($event, paymentIntent.id ?? '')\" [checked]=\"paymentIntent.selected\" />\n </label>\n </th>\n <td>{{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</td>\n <td>\n @if (paymentIntent.paymentMethod) {\n <ngx-payment-method \n [paymentMethod]=\"paymentIntent.paymentMethod!\" \n mode=\"compact\">\n </ngx-payment-method>\n } @else {\n <span class=\"badge badge-outline\">\n {{ paymentIntent.paymentMethodId }}\n </span>\n }\n </td>\n <td>\n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status}}\n </span>\n </td>\n <td>{{ paymentIntent.liveMode ? 'Live' : 'Test' }}</td>\n <td>\n <button class=\"btn btn-xs btn-ghost\" (click)=\"showDetails(paymentIntent.id ?? '')\">\n Details\n </button>\n <lib-payment-intents-dialog \n [paymentIntent]=\"paymentIntent\" \n [dialogId]=\"'modal-' + paymentIntent.id\">\n </lib-payment-intents-dialog>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <div class=\"card bg-base-100 shadow-sm\">\n <div class=\"card-body items-center text-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-12 w-12 text-gray-400 mb-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n <h2 class=\"card-title text-xl\">No recent payment intents</h2>\n <p class=\"text-gray-600\">No payment intents found in your history.</p>\n </div>\n </div>\n </div>\n }\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PaymentMethodComponent, selector: "ngx-payment-method", inputs: ["paymentMethod", "mode"] }, { kind: "component", type: PaymentIntentsDialogComponent, selector: "lib-payment-intents-dialog", inputs: ["paymentIntent", "dialogId"] }] });
1411
1751
  }
1412
1752
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentsTableComponent, decorators: [{
1413
1753
  type: Component,
1414
1754
  args: [{ selector: 'lib-payment-intents-table', standalone: true, imports: [
1415
- CommonModule
1416
- ], template: "<div class=\"payment-intents-container\">\n @if (withControls()) {\n <div class=\"controls mb-4 flex justify-between items-center\">\n <div class=\"stats\">\n @if (hasPaymentIntents()) {\n <span class=\"badge badge-soft badge-info\">{{ paymentIntentsTable().length }} payment intents</span>\n }\n </div>\n <div class=\"flex justify-end items-center gap-2\">\n <button class=\"btn btn-sm btn-outline\" (click)=\"refreshPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 mr-1\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n Actualizar\n </button>\n <button class=\"btn btn-sm btn-primary\" [disabled]=\"!somePaymentIntentsSelected()\" (click)=\"exportSelectedPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M8.71 7.71L11 5.41V15a1 1 0 0 0 2 0V5.41l2.29 2.3a1 1 0 0 0 1.42 0a1 1 0 0 0 0-1.42l-4-4a1 1 0 0 0-.33-.21a1 1 0 0 0-.76 0a1 1 0 0 0-.33.21l-4 4a1 1 0 1 0 1.42 1.42M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1\"/></svg>\n Export Selected\n </button>\n </div>\n </div>\n }\n\n @if (loading()) {\n <div class=\"overflow-x-auto\">\n <div class=\"skeleton h-32 w-full\"></div>\n </div>\n } @else if (error()) {\n <div class=\"error-container\">\n <div class=\"alert alert-error\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" /></svg>\n <span>{{ error() }}</span>\n </div>\n </div>\n } @else if (hasPaymentIntents()) {\n <div class=\"overflow-x-auto rounded-box bg-base-100\">\n <table class=\"table table-zebra\">\n <!-- head -->\n <thead>\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"toggleAllPaymentIntents($event)\" [checked]=\"allPaymentIntentsSelected()\" />\n </label>\n </th>\n <th>Amount</th>\n <th>Payment Method</th>\n <th>Status</th>\n <th>Mode</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (paymentIntent of paymentIntentsTable(); track trackByPaymentIntentId(0, paymentIntent)) {\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"togglePaymentIntent($event, paymentIntent.id ?? '')\" [checked]=\"paymentIntent.selected\" />\n </label>\n </th>\n <td>{{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</td>\n <td>\n <span class=\"badge badge-outline\">\n {{ paymentIntent.paymentMethodId }}\n </span>\n </td>\n <td>\n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status}}\n </span>\n </td>\n <td>{{ paymentIntent.liveMode ? 'Live' : 'Test' }}</td>\n <td>\n <button class=\"btn btn-xs btn-ghost\" (click)=\"showDetails(paymentIntent.id ?? '')\">\n Details\n </button>\n <dialog id=\"modal-{{ paymentIntent.id }}\" class=\"modal\">\n <div class=\"modal-box\">\n <h3 class=\"font-bold text-lg\">Payment Intent Details</h3>\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Payment Details</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Full ID:</span> {{ paymentIntent.id }}</li>\n <li><span class=\"font-medium\">Payment Method:</span> {{ paymentIntent.paymentMethodId }}</li>\n <li><span class=\"font-medium\">Confirmation Method:</span> {{ paymentIntent.confirmationMethod }}</li>\n @if (paymentIntent.invoiceId) {\n <li><span class=\"font-medium\">Invoice ID:</span> {{ paymentIntent.invoiceId }}</li>\n }\n </ul>\n </div>\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Status Information</h4>\n <ul class=\"space-y-1 text-sm\">\n <li>\n <span class=\"font-medium\">Status: </span> \n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status }}\n </span>\n </li>\n <li><span class=\"font-medium\">Mode: </span> {{ paymentIntent.liveMode ? 'Production' : 'Test' }}</li>\n <li><span class=\"font-medium\">Amount: </span> {{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</li>\n </ul>\n </div>\n </div>\n <div class=\"modal-action\">\n <form method=\"dialog\">\n <button class=\"btn\">Close</button>\n </form>\n </div>\n </div>\n </dialog>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <div class=\"card bg-base-100 shadow-sm\">\n <div class=\"card-body items-center text-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-12 w-12 text-gray-400 mb-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n <h2 class=\"card-title text-xl\">No recent payment intents</h2>\n <p class=\"text-gray-600\">No payment intents found in your history.</p>\n </div>\n </div>\n </div>\n }\n</div> " }]
1755
+ CommonModule,
1756
+ PaymentMethodComponent,
1757
+ PaymentIntentsDialogComponent
1758
+ ], template: "<div class=\"payment-intents-container\">\n @if (withControls()) {\n <div class=\"controls mb-4 flex justify-between items-center\">\n <div class=\"stats\">\n @if (hasPaymentIntents()) {\n <span class=\"badge badge-soft badge-info\">{{ paymentIntentsTable().length }} payment intents</span>\n }\n </div>\n <div class=\"flex justify-end items-center gap-2\">\n <button class=\"btn btn-sm btn-outline\" (click)=\"refreshPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 mr-1\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n Actualizar\n </button>\n <button class=\"btn btn-sm btn-primary\" [disabled]=\"!somePaymentIntentsSelected()\" (click)=\"exportSelectedPaymentIntents()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M8.71 7.71L11 5.41V15a1 1 0 0 0 2 0V5.41l2.29 2.3a1 1 0 0 0 1.42 0a1 1 0 0 0 0-1.42l-4-4a1 1 0 0 0-.33-.21a1 1 0 0 0-.76 0a1 1 0 0 0-.33.21l-4 4a1 1 0 1 0 1.42 1.42M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1\"/></svg>\n Export Selected\n </button>\n </div>\n </div>\n }\n\n @if (loading()) {\n <div class=\"overflow-x-auto\">\n <div class=\"skeleton h-32 w-full\"></div>\n </div>\n } @else if (error()) {\n <div class=\"error-container\">\n <div class=\"alert alert-error\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"stroke-current shrink-0 h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z\" /></svg>\n <span>{{ error() }}</span>\n </div>\n </div>\n } @else if (hasPaymentIntents()) {\n <div class=\"overflow-x-auto rounded-box bg-base-100\">\n <table class=\"table table-zebra\">\n <!-- head -->\n <thead>\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"toggleAllPaymentIntents($event)\" [checked]=\"allPaymentIntentsSelected()\" />\n </label>\n </th>\n <th>Amount</th>\n <th>Payment Method</th>\n <th>Status</th>\n <th>Mode</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n @for (paymentIntent of paymentIntentsTable(); track trackByPaymentIntentId(0, paymentIntent)) {\n <tr>\n <th>\n <label>\n <input type=\"checkbox\" class=\"checkbox\" (change)=\"togglePaymentIntent($event, paymentIntent.id ?? '')\" [checked]=\"paymentIntent.selected\" />\n </label>\n </th>\n <td>{{ utils.formatAmount(paymentIntent.amount ?? 0, paymentIntent.currency ?? 'EUR') }}</td>\n <td>\n @if (paymentIntent.paymentMethod) {\n <ngx-payment-method \n [paymentMethod]=\"paymentIntent.paymentMethod!\" \n mode=\"compact\">\n </ngx-payment-method>\n } @else {\n <span class=\"badge badge-outline\">\n {{ paymentIntent.paymentMethodId }}\n </span>\n }\n </td>\n <td>\n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(paymentIntent.status) }}\">\n {{ paymentIntent.status}}\n </span>\n </td>\n <td>{{ paymentIntent.liveMode ? 'Live' : 'Test' }}</td>\n <td>\n <button class=\"btn btn-xs btn-ghost\" (click)=\"showDetails(paymentIntent.id ?? '')\">\n Details\n </button>\n <lib-payment-intents-dialog \n [paymentIntent]=\"paymentIntent\" \n [dialogId]=\"'modal-' + paymentIntent.id\">\n </lib-payment-intents-dialog>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"empty-state\">\n <div class=\"card bg-base-100 shadow-sm\">\n <div class=\"card-body items-center text-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-12 w-12 text-gray-400 mb-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n <h2 class=\"card-title text-xl\">No recent payment intents</h2>\n <p class=\"text-gray-600\">No payment intents found in your history.</p>\n </div>\n </div>\n </div>\n }\n</div> " }]
1417
1759
  }] });
1418
1760
 
1419
1761
  class SubscriptionItemSkeletonComponent {
@@ -1425,75 +1767,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
1425
1767
  args: [{ selector: 'lib-subscription-item-skeleton', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-base-100 shadow-sm\">\n <div class=\"card-body p-4\">\n <div class=\"flex justify-between\">\n <div>\n <div class=\"flex items-center gap-2\">\n <div class=\"skeleton h-6 w-32\"></div>\n <div class=\"skeleton h-5 w-20\"></div>\n </div>\n <div class=\"skeleton h-4 w-48 mt-2\"></div>\n </div>\n \n <div class=\"flex gap-2\">\n <div class=\"skeleton h-8 w-20\"></div>\n </div>\n </div>\n\n <div class=\"mt-2\">\n <div class=\"flex justify-between text-sm\">\n <div class=\"skeleton h-4 w-32\"></div>\n <div class=\"skeleton h-4 w-32\"></div>\n </div>\n </div>\n\n <!--<div class=\"mt-4 border-t pt-4\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <div class=\"skeleton h-5 w-24 mb-2\"></div>\n <div class=\"space-y-1\">\n <div class=\"skeleton h-4 w-full\"></div>\n <div class=\"skeleton h-4 w-full\"></div>\n <div class=\"skeleton h-4 w-3/4\"></div>\n </div>\n </div>\n \n <div>\n <div class=\"skeleton h-5 w-24 mb-2\"></div>\n <div class=\"space-y-1\">\n <div class=\"skeleton h-4 w-full\"></div>\n <div class=\"skeleton h-4 w-3/4\"></div>\n </div>\n </div>\n </div>\n \n <div class=\"card-actions mt-4 justify-end\">\n <div class=\"skeleton h-8 w-32\"></div>\n </div>\n </div>-->\n </div>\n</div>" }]
1426
1768
  }] });
1427
1769
 
1428
- const initialPortalState = {
1429
- status: 'idle',
1430
- error: null,
1431
- portalUrl: null
1432
- };
1433
- const PortalAccountStore = signalStore({ providedIn: 'root' }, withState(initialPortalState), withComputed((state) => ({
1434
- isStatusIdle: computed(() => state.status() === 'idle'),
1435
- isStatusLoading: computed(() => state.status() === 'loading'),
1436
- isStatusSuccess: computed(() => state.status() === 'success'),
1437
- isStatusError: computed(() => state.status() === 'error'),
1438
- })), withMethods((store, stripeService = inject(StripeClientService)) => ({
1439
- /**
1440
- * Creates a customer portal session and redirects to the URL
1441
- * @param customerId Stripe customer ID
1442
- * @param returnUrl URL to redirect to after the portal session
1443
- */
1444
- async createPortalSession(customerId, returnUrl) {
1445
- patchState(store, { status: 'loading', error: null, portalUrl: null });
1446
- try {
1447
- const { url, error } = await stripeService.createPortalSession(customerId, returnUrl);
1448
- if (error) {
1449
- throw new Error(error.message);
1450
- }
1451
- if (!url) {
1452
- throw new Error('No se pudo crear la sesión del portal');
1453
- }
1454
- patchState(store, {
1455
- status: 'success',
1456
- portalUrl: url
1457
- });
1458
- window.location.href = url;
1459
- }
1460
- catch (error) {
1461
- console.error('🚨 [PortalAccountStore]', error);
1462
- const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
1463
- patchState(store, {
1464
- status: 'error',
1465
- error: errorMessage
1466
- });
1467
- }
1468
- },
1469
- /**
1470
- * Resets the store to initial state
1471
- */
1472
- reset() {
1473
- patchState(store, initialPortalState);
1474
- }
1475
- })));
1476
-
1477
1770
  class SubscriptionItemComponent {
1478
1771
  subscriptionsStore = inject(SubscriptionsStore);
1479
1772
  portalAccountStore = inject(PortalAccountStore);
1480
1773
  utils = inject(UtilsService);
1774
+ sanitizer = inject(DomSanitizer);
1481
1775
  subscription = input.required();
1482
1776
  onManageSubscription = output();
1483
1777
  isStatusLoading = computed(() => this.portalAccountStore.isStatusLoading());
1778
+ productImage = computed(() => {
1779
+ return this.subscription().product?.images?.[0] || 'https://img.daisyui.com/images/profile/demo/yellingcat@192.webp';
1780
+ });
1484
1781
  isExpanded = false;
1485
1782
  toggleExpand() {
1486
1783
  this.isExpanded = !this.isExpanded;
1487
1784
  }
1488
- async manageSubscription() {
1785
+ manageSubscription() {
1489
1786
  this.onManageSubscription.emit(this.subscription().customer ?? '');
1490
1787
  }
1788
+ sanitizeImageUrl(imageUrl) {
1789
+ return this.sanitizer.bypassSecurityTrustUrl(imageUrl);
1790
+ }
1491
1791
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SubscriptionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1492
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: SubscriptionItemComponent, isStandalone: true, selector: "lib-subscription-item", inputs: { subscription: { classPropertyName: "subscription", publicName: "subscription", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { onManageSubscription: "onManageSubscription" }, ngImport: i0, template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-shadow duration-200\">\n <div class=\"card-body p-4\">\n <div class=\"flex justify-between\">\n <div>\n <h3 class=\"card-title text-lg\">\n customer: {{ subscription().customer }}\n \n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(subscription().status) }} status-badge\">\n {{ subscription().status }}\n </span>\n\n @if (subscription().cancel.cancel_at_period_end) {\n <span class=\"badge badge-soft badge-warning ml-2\">Cancelada al final del periodo</span>\n }\n </h3>\n <p class=\"text-gray-600 text-sm\">\n ID: {{ subscription().id }}\n </p>\n </div>\n \n <div class=\"flex gap-2\">\n <button class=\"btn btn-sm btn-ghost\" (click)=\"toggleExpand()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n {{ isExpanded ? 'Ocultar' : 'Detalles' }}\n </button>\n </div>\n </div>\n\n <div class=\"subscription-details mt-2\">\n <div class=\"flex justify-between text-sm\">\n <div>\n <span class=\"font-medium\">Precio: </span><span class=\"text-xl font-bold text-secondary-content\">{{ utils.formatAmount(subscription().plan.amount, subscription().currency ?? 'EUR') }}</span>\n </div>\n <div>\n <span class=\"font-medium\">Per\u00EDodo: </span><span class=\"text-md font-bold\">{{ subscription().plan.interval }}</span>\n </div>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded) {\n <div class=\"subscription-expanded mt-4 border-t pt-4\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles del Plan</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Inicio:</span> {{ utils.formatDate(subscription().current_period_start ?? '') }}</li>\n <li><span class=\"font-medium\">Renovaci\u00F3n:</span> {{ utils.formatDate(subscription().current_period_end ?? '') }}</li>\n </ul>\n </div>\n \n @if (subscription().cancel.cancel_at_period_end) {\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles de la Cancelaci\u00F3n</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Cancelado el: </span> {{ subscription().cancel.canceled_at * 1000 | date:'dd/MM/yyyy' }}</li>\n <li><span class=\"font-medium\">No se renovar\u00E1 el: </span> {{ subscription().cancel.cancel_at * 1000 | date:'dd/MM/yyyy' }}</li>\n <li><span class=\"font-medium\">Feedback: </span> {{ subscription().cancel.cancellation_details.feedback }}</li>\n <li><span class=\"font-medium\">Comentario: </span> {{ subscription().cancel.cancellation_details.comment }}</li>\n </ul>\n </div>\n }\n </div>\n \n <!-- Action Buttons -->\n <div class=\"card-actions mt-4 justify-end\">\n <button class=\"btn btn-sm btn-secondary\" [disabled]=\"isStatusLoading()\" (click)=\"manageSubscription()\">\n @if (isStatusLoading()) {\n <span class=\"loading loading-spinner\"></span>\n loading account\n } @else {\n Manage subscription\n }\n </button>\n </div>\n </div>\n }\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
1792
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: SubscriptionItemComponent, isStandalone: true, selector: "lib-subscription-item", inputs: { subscription: { classPropertyName: "subscription", publicName: "subscription", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { onManageSubscription: "onManageSubscription" }, ngImport: i0, template: "<div class=\"card bg-gradient-to-br from-primary/10 to-secondary/10 shadow-lg hover:shadow-xl transition-all duration-300 border border-base-300\">\n <div class=\"card-body p-6\">\n <!-- Header con t\u00EDtulo y precio -->\n <div class=\"flex justify-between items-start mb-4\">\n <div class=\"flex items-center gap-3\">\n <!-- Imagen de la suscripci\u00F3n -->\n <div class=\"avatar\">\n <div class=\"w-24 rounded\">\n <img [src]=\"sanitizeImageUrl(productImage())\" \n [alt]=\"subscription().product?.name || 'Subscription product'\"\n class=\"w-full h-full object-cover rounded-lg\" />\n </div>\n </div> \n \n <div>\n <h3 class=\"text-2xl font-bold text-base-content\">\n {{ subscription().product?.name || 'Subscription Plan' }}\n </h3>\n <div class=\"flex items-center gap-2 mt-1\">\n <span class=\"badge {{ utils.getStatusBadgeClass(subscription().status) }} badge-lg\">\n {{ subscription().status }}\n </span>\n @if (subscription().cancel.cancel_at_period_end) {\n <span class=\"badge badge-warning badge-lg\">Cancelada</span>\n }\n </div>\n </div>\n </div>\n \n <!-- Precio principal -->\n <div class=\"text-right\">\n <div class=\"text-4xl font-bold text-primary\">\n {{ utils.formatAmount(subscription().plan.amount, subscription().currency ?? 'EUR') }}\n </div>\n <div class=\"text-base-content/70 text-lg capitalize\">\n {{ subscription().plan.interval }}ly\n </div>\n </div>\n </div>\n\n <!-- Fechas de per\u00EDodo -->\n <div class=\"flex justify-between items-center py-4 border-t border-base-300\">\n <div class=\"text-left\">\n <div class=\"text-base-content/70 text-sm font-medium\">Start:</div>\n <div class=\"text-base-content font-semibold\">\n {{ utils.formatDate(subscription().current_period_start ?? '') }}\n </div>\n </div>\n \n <div class=\"text-right\">\n <div class=\"text-base-content/70 text-sm font-medium\">End:</div>\n <div class=\"text-base-content font-semibold\">\n {{ utils.formatDate(subscription().current_period_end ?? '') }}\n </div>\n </div>\n </div>\n <div class=\"flex justify-end w-full\">\n <button class=\"btn btn-primary\" [disabled]=\"isStatusLoading()\" (click)=\"manageSubscription()\">\n @if (isStatusLoading()) {\n <span class=\"loading loading-spinner loading-sm\"></span>\n Cargando...\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z\" />\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Gestionar Suscripci\u00F3n\n }\n </button>\n </div>\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1493
1793
  }
1494
1794
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: SubscriptionItemComponent, decorators: [{
1495
1795
  type: Component,
1496
- args: [{ selector: 'lib-subscription-item', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-shadow duration-200\">\n <div class=\"card-body p-4\">\n <div class=\"flex justify-between\">\n <div>\n <h3 class=\"card-title text-lg\">\n customer: {{ subscription().customer }}\n \n <span class=\"badge badge-soft {{ utils.getStatusBadgeClass(subscription().status) }} status-badge\">\n {{ subscription().status }}\n </span>\n\n @if (subscription().cancel.cancel_at_period_end) {\n <span class=\"badge badge-soft badge-warning ml-2\">Cancelada al final del periodo</span>\n }\n </h3>\n <p class=\"text-gray-600 text-sm\">\n ID: {{ subscription().id }}\n </p>\n </div>\n \n <div class=\"flex gap-2\">\n <button class=\"btn btn-sm btn-ghost\" (click)=\"toggleExpand()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n {{ isExpanded ? 'Ocultar' : 'Detalles' }}\n </button>\n </div>\n </div>\n\n <div class=\"subscription-details mt-2\">\n <div class=\"flex justify-between text-sm\">\n <div>\n <span class=\"font-medium\">Precio: </span><span class=\"text-xl font-bold text-secondary-content\">{{ utils.formatAmount(subscription().plan.amount, subscription().currency ?? 'EUR') }}</span>\n </div>\n <div>\n <span class=\"font-medium\">Per\u00EDodo: </span><span class=\"text-md font-bold\">{{ subscription().plan.interval }}</span>\n </div>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded) {\n <div class=\"subscription-expanded mt-4 border-t pt-4\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles del Plan</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Inicio:</span> {{ utils.formatDate(subscription().current_period_start ?? '') }}</li>\n <li><span class=\"font-medium\">Renovaci\u00F3n:</span> {{ utils.formatDate(subscription().current_period_end ?? '') }}</li>\n </ul>\n </div>\n \n @if (subscription().cancel.cancel_at_period_end) {\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles de la Cancelaci\u00F3n</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">Cancelado el: </span> {{ subscription().cancel.canceled_at * 1000 | date:'dd/MM/yyyy' }}</li>\n <li><span class=\"font-medium\">No se renovar\u00E1 el: </span> {{ subscription().cancel.cancel_at * 1000 | date:'dd/MM/yyyy' }}</li>\n <li><span class=\"font-medium\">Feedback: </span> {{ subscription().cancel.cancellation_details.feedback }}</li>\n <li><span class=\"font-medium\">Comentario: </span> {{ subscription().cancel.cancellation_details.comment }}</li>\n </ul>\n </div>\n }\n </div>\n \n <!-- Action Buttons -->\n <div class=\"card-actions mt-4 justify-end\">\n <button class=\"btn btn-sm btn-secondary\" [disabled]=\"isStatusLoading()\" (click)=\"manageSubscription()\">\n @if (isStatusLoading()) {\n <span class=\"loading loading-spinner\"></span>\n loading account\n } @else {\n Manage subscription\n }\n </button>\n </div>\n </div>\n }\n </div>\n</div> " }]
1796
+ args: [{ selector: 'lib-subscription-item', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-gradient-to-br from-primary/10 to-secondary/10 shadow-lg hover:shadow-xl transition-all duration-300 border border-base-300\">\n <div class=\"card-body p-6\">\n <!-- Header con t\u00EDtulo y precio -->\n <div class=\"flex justify-between items-start mb-4\">\n <div class=\"flex items-center gap-3\">\n <!-- Imagen de la suscripci\u00F3n -->\n <div class=\"avatar\">\n <div class=\"w-24 rounded\">\n <img [src]=\"sanitizeImageUrl(productImage())\" \n [alt]=\"subscription().product?.name || 'Subscription product'\"\n class=\"w-full h-full object-cover rounded-lg\" />\n </div>\n </div> \n \n <div>\n <h3 class=\"text-2xl font-bold text-base-content\">\n {{ subscription().product?.name || 'Subscription Plan' }}\n </h3>\n <div class=\"flex items-center gap-2 mt-1\">\n <span class=\"badge {{ utils.getStatusBadgeClass(subscription().status) }} badge-lg\">\n {{ subscription().status }}\n </span>\n @if (subscription().cancel.cancel_at_period_end) {\n <span class=\"badge badge-warning badge-lg\">Cancelada</span>\n }\n </div>\n </div>\n </div>\n \n <!-- Precio principal -->\n <div class=\"text-right\">\n <div class=\"text-4xl font-bold text-primary\">\n {{ utils.formatAmount(subscription().plan.amount, subscription().currency ?? 'EUR') }}\n </div>\n <div class=\"text-base-content/70 text-lg capitalize\">\n {{ subscription().plan.interval }}ly\n </div>\n </div>\n </div>\n\n <!-- Fechas de per\u00EDodo -->\n <div class=\"flex justify-between items-center py-4 border-t border-base-300\">\n <div class=\"text-left\">\n <div class=\"text-base-content/70 text-sm font-medium\">Start:</div>\n <div class=\"text-base-content font-semibold\">\n {{ utils.formatDate(subscription().current_period_start ?? '') }}\n </div>\n </div>\n \n <div class=\"text-right\">\n <div class=\"text-base-content/70 text-sm font-medium\">End:</div>\n <div class=\"text-base-content font-semibold\">\n {{ utils.formatDate(subscription().current_period_end ?? '') }}\n </div>\n </div>\n </div>\n <div class=\"flex justify-end w-full\">\n <button class=\"btn btn-primary\" [disabled]=\"isStatusLoading()\" (click)=\"manageSubscription()\">\n @if (isStatusLoading()) {\n <span class=\"loading loading-spinner loading-sm\"></span>\n Cargando...\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z\" />\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Gestionar Suscripci\u00F3n\n }\n </button>\n </div>\n </div>\n</div> " }]
1497
1797
  }] });
1498
1798
 
1499
1799
  class SubscriptionsListComponent {
@@ -1569,11 +1869,11 @@ class PaymentIntentItemComponent {
1569
1869
  this.isExpanded.update(value => !value);
1570
1870
  }
1571
1871
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1572
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentIntentItemComponent, isStandalone: true, selector: "lib-payment-intent-item", inputs: { paymentIntent: { classPropertyName: "paymentIntent", publicName: "paymentIntent", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-shadow duration-200 card-hover\">\n <div class=\"card-body p-4\">\n <div class=\"flex justify-between\">\n <div>\n <h3 class=\"card-title text-lg\">\n Customer: {{ paymentIntent().customer }}\n </h3>\n </div>\n \n <div class=\"flex gap-2\">\n <button class=\"btn btn-sm btn-ghost\" (click)=\"toggleExpand()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n {{ isExpanded() ? 'Ocultar' : 'Detalles' }}\n </button>\n </div>\n </div>\n\n <div class=\"payment-intent-details mt-2\">\n <div class=\"flex justify-start items-center gap-4 text-sm\">\n <div>\n <span class=\"font-medium\">Precio: </span><span class=\"text-xl font-bold text-base-content\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</span>\n </div>\n <div>\n <span class=\"badge badge-outline\">\n {{ paymentIntent().paymentMethodId }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded()) {\n <div class=\"payment-expanded mt-4 border-t pt-4\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles del Pago</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">ID Completo:</span> {{ paymentIntent().id }}</li>\n <li><span class=\"font-medium\">M\u00E9todo de pago:</span> {{ paymentIntent().paymentMethodId }}</li>\n <li><span class=\"font-medium\">M\u00E9todo de confirmaci\u00F3n:</span> {{ paymentIntent().confirmationMethod }}</li>\n @if (paymentIntent().invoiceId) {\n <li><span class=\"font-medium\">ID de factura:</span> {{ paymentIntent().invoiceId }}</li>\n }\n </ul>\n </div>\n \n <div>\n <ul class=\"space-y-1 text-sm\">\n <li>\n <span class=\"font-medium\">Estado: </span> \n <span class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }} status-badge\">\n {{ paymentIntent().status }}\n </span>\n </li>\n <li><span class=\"font-medium\">Modo: </span> {{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</li>\n </ul>\n </div>\n </div>\n </div>\n }\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1872
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.5", type: PaymentIntentItemComponent, isStandalone: true, selector: "lib-payment-intent-item", inputs: { paymentIntent: { classPropertyName: "paymentIntent", publicName: "paymentIntent", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-all duration-200 border border-base-200\">\n <div class=\"card-body p-4\">\n \n <!-- Header Section -->\n <div class=\"flex justify-between items-start mb-3\">\n <div class=\"flex-1\">\n <div class=\"flex items-center gap-2 mb-1\">\n <div class=\"text-2xl font-bold text-primary\">\n {{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}\n </div>\n <div class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }} badge-sm\">\n {{ paymentIntent().status }}\n </div>\n @if (!paymentIntent().liveMode) {\n <div class=\"badge badge-warning badge-sm\">TEST</div>\n }\n </div>\n </div>\n \n <button \n class=\"btn btn-sm btn-ghost btn-circle\" \n (click)=\"toggleExpand()\"\n [class.rotate-180]=\"isExpanded()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 transition-transform duration-200\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n </div>\n\n <!-- Main Info Section -->\n <div class=\"space-y-2\">\n <!-- Payment Method -->\n @if (paymentIntent().paymentMethod) {\n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"compact\">\n </ngx-payment-method>\n } @else {\n <div class=\"flex items-center gap-2 p-2 bg-base-200 rounded-lg\">\n <div class=\"w-8 h-8 bg-base-300 rounded-full flex items-center justify-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n </div>\n <div>\n <div class=\"font-medium text-sm\">M\u00E9todo de pago</div>\n <div class=\"text-xs text-base-content/70\">\u2022\u2022\u2022\u2022 {{ paymentIntent().paymentMethodId.slice(-4) }}</div>\n </div>\n </div>\n }\n\n <!-- Confirmation Method -->\n <div class=\"flex items-center gap-2 text-sm\">\n <div class=\"w-2 h-2 bg-success rounded-full\"></div>\n <span class=\"text-base-content/70\">Confirmaci\u00F3n:</span>\n <span class=\"capitalize font-medium\">{{ paymentIntent().confirmationMethod.replace('_', ' ') }}</span>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded()) {\n <div class=\"divider my-4\"></div>\n \n <div class=\"space-y-4\">\n <!-- Transaction Details -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Detalles de la Transacci\u00F3n\n </h4>\n \n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Estado</div>\n <div class=\"stat-value text-sm capitalize\">{{ paymentIntent().status }}</div>\n <div class=\"stat-desc\">{{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</div>\n </div>\n \n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Monto</div>\n <div class=\"stat-value text-sm\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</div>\n <div class=\"stat-desc\">{{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}</div>\n </div>\n </div>\n </div>\n\n <!-- Payment Information -->\n @if (paymentIntent().paymentMethod) {\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n Informaci\u00F3n del Pago\n </h4>\n \n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"detailed\">\n </ngx-payment-method>\n </div>\n }\n\n <!-- Additional Information -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n Informaci\u00F3n Adicional\n </h4>\n \n <div class=\"space-y-2\">\n @if (paymentIntent().invoiceId) {\n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Factura</span>\n <span class=\"text-sm font-mono\">{{ paymentIntent().invoiceId.slice(-8) }}</span>\n </div>\n }\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Entorno</span>\n <span class=\"text-sm\">\n @if (paymentIntent().liveMode) {\n <span class=\"text-success\">Producci\u00F3n</span>\n } @else {\n <span class=\"text-warning\">Pruebas</span>\n }\n </span>\n </div>\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Referencia</span>\n <span class=\"text-sm font-mono\">\u2022\u2022\u2022{{ paymentIntent().id?.slice(-6) || 'N/A' }}</span>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div> ", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PaymentMethodComponent, selector: "ngx-payment-method", inputs: ["paymentMethod", "mode"] }] });
1573
1873
  }
1574
1874
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: PaymentIntentItemComponent, decorators: [{
1575
1875
  type: Component,
1576
- args: [{ selector: 'lib-payment-intent-item', standalone: true, imports: [CommonModule], template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-shadow duration-200 card-hover\">\n <div class=\"card-body p-4\">\n <div class=\"flex justify-between\">\n <div>\n <h3 class=\"card-title text-lg\">\n Customer: {{ paymentIntent().customer }}\n </h3>\n </div>\n \n <div class=\"flex gap-2\">\n <button class=\"btn btn-sm btn-ghost\" (click)=\"toggleExpand()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n {{ isExpanded() ? 'Ocultar' : 'Detalles' }}\n </button>\n </div>\n </div>\n\n <div class=\"payment-intent-details mt-2\">\n <div class=\"flex justify-start items-center gap-4 text-sm\">\n <div>\n <span class=\"font-medium\">Precio: </span><span class=\"text-xl font-bold text-base-content\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</span>\n </div>\n <div>\n <span class=\"badge badge-outline\">\n {{ paymentIntent().paymentMethodId }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded()) {\n <div class=\"payment-expanded mt-4 border-t pt-4\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div>\n <h4 class=\"text-md font-semibold mb-2\">Detalles del Pago</h4>\n <ul class=\"space-y-1 text-sm\">\n <li><span class=\"font-medium\">ID Completo:</span> {{ paymentIntent().id }}</li>\n <li><span class=\"font-medium\">M\u00E9todo de pago:</span> {{ paymentIntent().paymentMethodId }}</li>\n <li><span class=\"font-medium\">M\u00E9todo de confirmaci\u00F3n:</span> {{ paymentIntent().confirmationMethod }}</li>\n @if (paymentIntent().invoiceId) {\n <li><span class=\"font-medium\">ID de factura:</span> {{ paymentIntent().invoiceId }}</li>\n }\n </ul>\n </div>\n \n <div>\n <ul class=\"space-y-1 text-sm\">\n <li>\n <span class=\"font-medium\">Estado: </span> \n <span class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }} status-badge\">\n {{ paymentIntent().status }}\n </span>\n </li>\n <li><span class=\"font-medium\">Modo: </span> {{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</li>\n </ul>\n </div>\n </div>\n </div>\n }\n </div>\n</div> " }]
1876
+ args: [{ selector: 'lib-payment-intent-item', standalone: true, imports: [CommonModule, PaymentMethodComponent], template: "<div class=\"card bg-base-100 shadow-sm hover:shadow-md transition-all duration-200 border border-base-200\">\n <div class=\"card-body p-4\">\n \n <!-- Header Section -->\n <div class=\"flex justify-between items-start mb-3\">\n <div class=\"flex-1\">\n <div class=\"flex items-center gap-2 mb-1\">\n <div class=\"text-2xl font-bold text-primary\">\n {{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}\n </div>\n <div class=\"badge {{ utils.getStatusBadgeClass(paymentIntent().status) }} badge-sm\">\n {{ paymentIntent().status }}\n </div>\n @if (!paymentIntent().liveMode) {\n <div class=\"badge badge-warning badge-sm\">TEST</div>\n }\n </div>\n </div>\n \n <button \n class=\"btn btn-sm btn-ghost btn-circle\" \n (click)=\"toggleExpand()\"\n [class.rotate-180]=\"isExpanded()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4 transition-transform duration-200\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n </div>\n\n <!-- Main Info Section -->\n <div class=\"space-y-2\">\n <!-- Payment Method -->\n @if (paymentIntent().paymentMethod) {\n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"compact\">\n </ngx-payment-method>\n } @else {\n <div class=\"flex items-center gap-2 p-2 bg-base-200 rounded-lg\">\n <div class=\"w-8 h-8 bg-base-300 rounded-full flex items-center justify-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n </div>\n <div>\n <div class=\"font-medium text-sm\">M\u00E9todo de pago</div>\n <div class=\"text-xs text-base-content/70\">\u2022\u2022\u2022\u2022 {{ paymentIntent().paymentMethodId.slice(-4) }}</div>\n </div>\n </div>\n }\n\n <!-- Confirmation Method -->\n <div class=\"flex items-center gap-2 text-sm\">\n <div class=\"w-2 h-2 bg-success rounded-full\"></div>\n <span class=\"text-base-content/70\">Confirmaci\u00F3n:</span>\n <span class=\"capitalize font-medium\">{{ paymentIntent().confirmationMethod.replace('_', ' ') }}</span>\n </div>\n </div>\n\n <!-- Expanded Details -->\n @if (isExpanded()) {\n <div class=\"divider my-4\"></div>\n \n <div class=\"space-y-4\">\n <!-- Transaction Details -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Detalles de la Transacci\u00F3n\n </h4>\n \n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Estado</div>\n <div class=\"stat-value text-sm capitalize\">{{ paymentIntent().status }}</div>\n <div class=\"stat-desc\">{{ paymentIntent().liveMode ? 'Producci\u00F3n' : 'Pruebas' }}</div>\n </div>\n \n <div class=\"stat bg-base-200 rounded-lg p-3\">\n <div class=\"stat-title text-xs\">Monto</div>\n <div class=\"stat-value text-sm\">{{ utils.formatAmount(paymentIntent().amount ?? 0, paymentIntent().currency ?? 'EUR') }}</div>\n <div class=\"stat-desc\">{{ (paymentIntent().currency ?? 'EUR').toUpperCase() }}</div>\n </div>\n </div>\n </div>\n\n <!-- Payment Information -->\n @if (paymentIntent().paymentMethod) {\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z\" />\n </svg>\n Informaci\u00F3n del Pago\n </h4>\n \n <ngx-payment-method \n [paymentMethod]=\"paymentIntent().paymentMethod!\" \n mode=\"detailed\">\n </ngx-payment-method>\n </div>\n }\n\n <!-- Additional Information -->\n <div>\n <h4 class=\"text-sm font-semibold text-base-content/80 mb-3 flex items-center gap-2\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n Informaci\u00F3n Adicional\n </h4>\n \n <div class=\"space-y-2\">\n @if (paymentIntent().invoiceId) {\n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Factura</span>\n <span class=\"text-sm font-mono\">{{ paymentIntent().invoiceId.slice(-8) }}</span>\n </div>\n }\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Entorno</span>\n <span class=\"text-sm\">\n @if (paymentIntent().liveMode) {\n <span class=\"text-success\">Producci\u00F3n</span>\n } @else {\n <span class=\"text-warning\">Pruebas</span>\n }\n </span>\n </div>\n \n <div class=\"flex items-center justify-between p-2 bg-base-200 rounded\">\n <span class=\"text-sm text-base-content/70\">Referencia</span>\n <span class=\"text-sm font-mono\">\u2022\u2022\u2022{{ paymentIntent().id?.slice(-6) || 'N/A' }}</span>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div> " }]
1577
1877
  }] });
1578
1878
 
1579
1879
  class PaymentIntentsListComponent {
@@ -1654,5 +1954,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImpor
1654
1954
  * Generated bundle index. Do not edit.
1655
1955
  */
1656
1956
 
1657
- export { CheckoutStore, CustomerDashboardComponent, CustomerStore, EmbeddedCheckoutComponent, EmbeddedSubscriptionComponent, PaymentIntentsListComponent, PaymentIntentsTableComponent, ProductListComponent, ProductsStore, ReturnPageComponent, STRIPE_CONFIG, SUPABASE_CONFIG, StripeClientService, SubscriptionCardComponent, SubscriptionReturnPageComponent, SubscriptionsListComponent, SubscriptionsStore, SupabaseClientService, parsePaymentIntent, parseProduct, parseSubscription, provideNgxSupabaseStripeConfig, provideStripeConfig, provideSupabaseConfig };
1957
+ export { CheckoutStore, Currency, CustomerDashboardComponent, CustomerStore, EmbeddedCheckoutComponent, EmbeddedSubscriptionComponent, PaymentIntentsListComponent, PaymentIntentsTableComponent, PortalAccountStore, ProductListComponent, ProductsStore, ReturnPageComponent, STRIPE_CONFIG, SUPABASE_CONFIG, StripeClientService, SubscriptionCardComponent, SubscriptionReturnPageComponent, SubscriptionsListComponent, SubscriptionsStore, SupabaseClientService, parsePaymentIntent, parseProduct, parseSubscription, provideNgxSupabaseStripeConfig, provideStripeConfig, provideSupabaseConfig };
1658
1958
  //# sourceMappingURL=dotted-labs-ngx-supabase-stripe.mjs.map