From 36b44c41f1ca92a9420ac4201b17f870ef99a696 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Wed, 1 Oct 2025 14:39:10 +0200 Subject: [PATCH] Fix issues --- .../components/domain/auth/RegisterForm.tsx | 119 ++++---- .../domain/auth/SubscriptionSelection.tsx | 264 +++++++++--------- .../components/training/training-service.yaml | 25 -- .../overlays/dev/kustomization.yaml | 79 ------ services/notification/app/main.py | 66 ++--- 5 files changed, 229 insertions(+), 324 deletions(-) diff --git a/frontend/src/components/domain/auth/RegisterForm.tsx b/frontend/src/components/domain/auth/RegisterForm.tsx index f34b8325..6572d7f9 100644 --- a/frontend/src/components/domain/auth/RegisterForm.tsx +++ b/frontend/src/components/domain/auth/RegisterForm.tsx @@ -8,6 +8,7 @@ import { SubscriptionSelection } from './SubscriptionSelection'; import PaymentForm from './PaymentForm'; import { loadStripe } from '@stripe/stripe-js'; import { Elements } from '@stripe/react-stripe-js'; +import { CheckCircle } from 'lucide-react'; // Initialize Stripe - In production, use environment variable const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY || 'pk_test_51234567890123456789012345678901234567890123456789012345678901234567890123456789012345'); @@ -168,48 +169,69 @@ export const RegisterForm: React.FC = ({ }; // Render step indicator - const renderStepIndicator = () => ( -
-
-
- 1 -
-
-
- 2 -
-
-
- 3 + const renderStepIndicator = () => { + const steps = [ + { key: 'basic_info', label: 'Información', number: 1 }, + { key: 'subscription', label: 'Plan', number: 2 }, + { key: 'payment', label: 'Pago', number: 3 } + ]; + + const getStepIndex = (step: RegistrationStep) => { + return steps.findIndex(s => s.key === step); + }; + + const currentIndex = getStepIndex(currentStep); + + return ( +
+
+ {steps.map((step, index) => ( + +
+
+ {index < currentIndex ? ( + + ) : ( + step.number + )} +
+ +
+ {index < steps.length - 1 && ( +
+ )} + + ))}
-
- ); + ); + }; // Render current step const renderCurrentStep = () => { switch (currentStep) { case 'basic_info': return ( -
-
-

+
+
+

{t('auth:register.title', 'Crear Cuenta')}

-

+

{t('auth:register.subtitle', 'Únete y comienza hoy mismo')}

-

- Paso 1 de 3: Información Básica -

{ e.preventDefault(); handleNextStep(); }} className="space-y-6"> @@ -396,14 +418,13 @@ export const RegisterForm: React.FC = ({ )}
-
-
{/* Spacer for alignment */} +
@@ -415,16 +436,13 @@ export const RegisterForm: React.FC = ({ case 'subscription': return (
-
-

+
+

{t('auth:subscription.select_plan', 'Selecciona tu plan')}

-

+

{t('auth:subscription.choose_plan', 'Elige el plan que mejor se adapte a tu negocio')}

-

- Paso 2 de 3: Plan de Suscripción -

= ({ trialSelected={useTrial} /> -
+
@@ -450,7 +468,7 @@ export const RegisterForm: React.FC = ({ size="lg" onClick={handleNextStep} disabled={isLoading} - className="w-48" + className="w-full sm:w-48 order-1 sm:order-2" > Siguiente @@ -460,17 +478,14 @@ export const RegisterForm: React.FC = ({ case 'payment': return ( -
-
-

+
+
+

{t('auth:payment.payment_info', 'Información de Pago')}

-

+

{t('auth:payment.secure_payment', 'Tu información de pago está protegida con encriptación de extremo a extremo')}

-

- Paso 3 de 3: Procesamiento de Pago -

@@ -482,13 +497,13 @@ export const RegisterForm: React.FC = ({ /> -
+
@@ -499,7 +514,7 @@ export const RegisterForm: React.FC = ({ }; return ( - + {renderStepIndicator()} {renderCurrentStep()} diff --git a/frontend/src/components/domain/auth/SubscriptionSelection.tsx b/frontend/src/components/domain/auth/SubscriptionSelection.tsx index e7a8c76e..df865cf2 100644 --- a/frontend/src/components/domain/auth/SubscriptionSelection.tsx +++ b/frontend/src/components/domain/auth/SubscriptionSelection.tsx @@ -55,25 +55,16 @@ export const SubscriptionSelection: React.FC = ({ }; return ( -
-
-

- {t('auth:subscription.select_plan', 'Selecciona tu plan')} -

-

- {t('auth:subscription.choose_plan', 'Elige el plan que mejor se adapte a tu negocio')} -

-
- +
{showTrialOption && ( - -
-
-
- + +
+
+
+
-
-

+
+

{t('auth:subscription.trial_title', 'Prueba gratuita')}

@@ -83,151 +74,154 @@ export const SubscriptionSelection: React.FC = ({

)} -
+
{Object.entries(availablePlans.plans).map(([planKey, plan]) => { const isSelected = selectedPlan === planKey; - const getPlanColor = () => { - switch (planKey) { - case 'starter': return 'border-blue-500/30 bg-blue-500/5'; - case 'professional': return 'border-purple-500/30 bg-purple-500/5'; - case 'enterprise': return 'border-amber-500/30 bg-amber-500/5'; - default: return 'border-border-primary bg-bg-secondary'; - } - }; return ( onPlanSelect(planKey)} > + {/* Popular Badge */} {plan.popular && ( -
- - +
+ + {t('auth:subscription.popular', 'Más Popular')}
)} -
-

{plan.name}

-
- {subscriptionService.formatPrice(plan.monthly_price)} - /mes -
-

{plan.description}

-
+ {/* Horizontal Layout */} +
+ {/* Left Section: Plan Info & Pricing */} +
+

{plan.name}

+
+ + {subscriptionService.formatPrice(plan.monthly_price)} + + /mes +
+

{plan.description}

-
-
- - {plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`} + {/* Plan Limits */} +
+
+ + {plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`} +
+
+ + {plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`} +
+
+ + {plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`} +
+
-
- - {plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`} + + {/* Divider */} +
+ + {/* Right Section: Features */} +
+
+ +
+ {t('auth:subscription.features', 'Funcionalidades Incluidas')} +
+
+ +
+ {(() => { + const getPlanFeatures = (planKey: string) => { + switch (planKey) { + case 'starter': + return [ + 'Panel de Control Básico', + 'Gestión de Inventario', + 'Gestión de Pedidos', + 'Gestión de Proveedores', + 'Punto de Venta Básico' + ]; + case 'professional': + return [ + 'Todo lo de Starter', + 'Panel Avanzado', + 'Analytics de Ventas', + 'Pronósticos con IA', + 'Optimización de Producción' + ]; + case 'enterprise': + return [ + 'Todo lo de Professional', + 'Insights Predictivos IA', + 'Analytics Multi-ubicación', + 'Integración ERP', + 'Soporte 24/7 Prioritario', + 'API Personalizada' + ]; + default: + return []; + } + }; + + return getPlanFeatures(planKey).map((feature, index) => ( +
+ + {feature} +
+ )); + })()} +
-
- - {plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`} + + {/* Action Button */} +
+
- - {/* Features Section */} -
-
- - {t('auth:subscription.features', 'Funcionalidades Incluidas')} -
-
- {(() => { - const getPlanFeatures = (planKey: string) => { - switch (planKey) { - case 'starter': - return [ - '✓ Panel de Control Básico', - '✓ Gestión de Inventario', - '✓ Gestión de Pedidos', - '✓ Gestión de Proveedores', - '✓ Punto de Venta Básico', - '✗ Analytics Avanzados', - '✗ Pronósticos IA', - '✗ Insights Predictivos' - ]; - case 'professional': - return [ - '✓ Panel de Control Avanzado', - '✓ Gestión de Inventario Completa', - '✓ Analytics de Ventas', - '✓ Pronósticos con IA (92% precisión)', - '✓ Análisis de Rendimiento', - '✓ Optimización de Producción', - '✓ Integración POS', - '✗ Insights Predictivos Avanzados' - ]; - case 'enterprise': - return [ - '✓ Todas las funcionalidades Professional', - '✓ Insights Predictivos con IA', - '✓ Analytics Multi-ubicación', - '✓ Integración ERP', - '✓ API Personalizada', - '✓ Gestor de Cuenta Dedicado', - '✓ Soporte 24/7 Prioritario', - '✓ Demo Personalizada' - ]; - default: - return []; - } - }; - - return getPlanFeatures(planKey).map((feature, index) => ( -
- {feature} -
- )); - })()} -
-
- - ); })} diff --git a/infrastructure/kubernetes/base/components/training/training-service.yaml b/infrastructure/kubernetes/base/components/training/training-service.yaml index 612e48e0..3533100d 100644 --- a/infrastructure/kubernetes/base/components/training/training-service.yaml +++ b/infrastructure/kubernetes/base/components/training/training-service.yaml @@ -82,12 +82,7 @@ spec: name: pos-integration-secrets - secretRef: name: whatsapp-secrets - env: - - name: TRAINING_PERSISTENCE_PATH - value: "/app/training_state" volumeMounts: - - name: training-state - mountPath: /app/training_state - name: tmp-storage mountPath: /tmp resources: @@ -114,9 +109,6 @@ spec: periodSeconds: 15 failureThreshold: 5 volumes: - - name: training-state - persistentVolumeClaim: - claimName: training-state-pvc - name: tmp-storage emptyDir: sizeLimit: 2Gi @@ -140,20 +132,3 @@ spec: selector: app.kubernetes.io/name: training-service app.kubernetes.io/component: microservice - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: training-state-pvc - namespace: bakery-ia - labels: - app.kubernetes.io/name: training-service - app.kubernetes.io/component: storage -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - storageClassName: standard diff --git a/infrastructure/kubernetes/overlays/dev/kustomization.yaml b/infrastructure/kubernetes/overlays/dev/kustomization.yaml index 0adcf439..2acb96f7 100644 --- a/infrastructure/kubernetes/overlays/dev/kustomization.yaml +++ b/infrastructure/kubernetes/overlays/dev/kustomization.yaml @@ -311,85 +311,6 @@ patches: kind: Deployment name: external-service patch: |- - - op: add - path: /spec/template/spec/initContainers - value: - - name: wait-for-external-db - image: postgres:13-alpine - command: - - sh - - -c - - | - until pg_isready -h $EXTERNAL_DB_HOST -p $EXTERNAL_DB_PORT -U $EXTERNAL_DB_USER; do - echo "Waiting for external database..." - sleep 2 - done - echo "External database is ready!" - env: - - name: EXTERNAL_DB_HOST - valueFrom: - configMapKeyRef: - name: bakery-config - key: EXTERNAL_DB_HOST - - name: EXTERNAL_DB_PORT - valueFrom: - configMapKeyRef: - name: bakery-config - key: DB_PORT - - name: EXTERNAL_DB_USER - valueFrom: - secretKeyRef: - name: database-secrets - key: EXTERNAL_DB_USER - - name: PGPASSWORD - valueFrom: - secretKeyRef: - name: database-secrets - key: EXTERNAL_DB_PASSWORD - - name: wait-for-rabbitmq - image: busybox:1.35 - command: - - sh - - -c - - | - until nc -z $RABBITMQ_HOST $RABBITMQ_PORT; do - echo "Waiting for RabbitMQ..." - sleep 2 - done - echo "RabbitMQ is ready!" - env: - - name: RABBITMQ_HOST - valueFrom: - configMapKeyRef: - name: bakery-config - key: RABBITMQ_HOST - - name: RABBITMQ_PORT - valueFrom: - configMapKeyRef: - name: bakery-config - key: RABBITMQ_PORT - - name: wait-for-redis - image: redis:7-alpine - command: - - sh - - -c - - | - until redis-cli -h $REDIS_HOST -p $REDIS_PORT ping; do - echo "Waiting for Redis..." - sleep 2 - done - echo "Redis is ready!" - env: - - name: REDIS_HOST - valueFrom: - configMapKeyRef: - name: bakery-config - key: REDIS_HOST - - name: REDIS_PORT - valueFrom: - configMapKeyRef: - name: bakery-config - key: REDIS_PORT - op: replace path: /spec/template/spec/containers/0/resources value: diff --git a/services/notification/app/main.py b/services/notification/app/main.py index 4f967a8e..3e090d43 100644 --- a/services/notification/app/main.py +++ b/services/notification/app/main.py @@ -85,34 +85,34 @@ class NotificationService(StandardFastAPIService): } # Define custom health checks for notification service components - async def check_email_service(): - """Check email service health - service is ready even if credentials are invalid""" - try: - if not self.email_service: - return False - # Service is considered healthy if it's initialized, even if credentials fail - # This allows the pod to be ready while external services may have config issues - await self.email_service.health_check() - return True - except Exception as e: + #async def check_email_service(): + # """Check email service health - service is ready even if credentials are invalid""" + # try: + # if not self.email_service: + # return False + # # Service is considered healthy if it's initialized, even if credentials fail + # # This allows the pod to be ready while external services may have config issues + # await self.email_service.health_check() + # return True + # except Exception as e: # Log but don't fail readiness - email service config issues shouldn't block the pod - self.logger.error("Email service health check failed", error=str(e)) + # self.logger.error("Email service health check failed", error=str(e)) # Return True to indicate service is ready (initialized) even if credentials are wrong - return True + # return True - async def check_whatsapp_service(): - """Check WhatsApp service health - service is ready even if credentials are invalid""" - try: - if not self.whatsapp_service: - return False + #async def check_whatsapp_service(): + # """Check WhatsApp service health - service is ready even if credentials are invalid""" + # try: + # if not self.whatsapp_service: + # return False # Service is considered healthy if it's initialized, even if credentials fail - await self.whatsapp_service.health_check() - return True - except Exception as e: + # await self.whatsapp_service.health_check() + # return True + # except Exception as e: # Log but don't fail readiness - WhatsApp config issues shouldn't block the pod - self.logger.error("WhatsApp service health check failed", error=str(e)) + # self.logger.error("WhatsApp service health check failed", error=str(e)) # Return True to indicate service is ready (initialized) even if credentials are wrong - return True + # return True async def check_sse_service(): """Check SSE service health""" @@ -125,14 +125,14 @@ class NotificationService(StandardFastAPIService): self.logger.error("SSE service health check failed", error=str(e)) return False - async def check_messaging(): - """Check messaging service health""" - try: - from app.services.messaging import notification_publisher - return bool(notification_publisher and notification_publisher.connected) - except Exception as e: - self.logger.error("Messaging health check failed", error=str(e)) - return False + #async def check_messaging(): + # """Check messaging service health""" + # try: + # from app.services.messaging import notification_publisher + # return bool(notification_publisher and notification_publisher.connected) + # except Exception as e: + # self.logger.error("Messaging health check failed", error=str(e)) + # return False super().__init__( service_name="notification-service", @@ -145,10 +145,10 @@ class NotificationService(StandardFastAPIService): database_manager=database_manager, expected_tables=notification_expected_tables, custom_health_checks={ - "email_service": check_email_service, - "whatsapp_service": check_whatsapp_service, + # "email_service": check_email_service, + # "whatsapp_service": check_whatsapp_service, "sse_service": check_sse_service, - "messaging": check_messaging + # "messaging": check_messaging }, enable_messaging=True, custom_metrics=notification_custom_metrics