Imporve the UI 5

This commit is contained in:
Urtzi Alfaro
2026-01-03 15:55:24 +01:00
parent db12c57b0b
commit 47ccea4900
8 changed files with 65 additions and 24 deletions

View File

@@ -94,7 +94,7 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
// Format limit display with emoji and user-friendly text
const formatLimit = (value: number | string | null | undefined, unlimitedKey: string): string => {
if (!value || value === -1 || value === 'unlimited') {
if (!value || value === -1 || (typeof value === 'string' && value.toLowerCase() === 'unlimited')) {
return t(unlimitedKey);
}
return value.toString();
@@ -174,16 +174,6 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
</div>
</div>
{/* Selection Mode Helper Text */}
{mode === 'selection' && (
<div className="text-center mb-6">
<p className="text-sm text-[var(--text-secondary)] flex items-center justify-center gap-2">
<span className="inline-block w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
{t('ui.click_to_select', 'Haz clic en cualquier plan para seleccionarlo')}
</p>
</div>
)}
{/* Simplified Plans Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 items-start lg:items-stretch">
{Object.entries(plans).map(([tier, plan]) => {
@@ -240,7 +230,7 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
{/* Plan Header */}
<div className="mb-6">
<h3 className={`text-2xl font-bold mb-2 ${(isPopular || isSelected) ? 'text-white' : 'text-[var(--text-primary)]'}`}>
{plan.name}
{t(`plans.${tier}.name`, plan.name)}
</h3>
<p className={`text-sm ${(isPopular || isSelected) ? 'text-white/90' : 'text-[var(--text-secondary)]'}`}>
{plan.tagline_key ? t(plan.tagline_key) : plan.tagline || ''}

View File

@@ -54,6 +54,12 @@
"standard_cost": "Target cost for budgeting and variance analysis",
"average_cost": "Automatically calculated from weighted average of purchases"
},
"type": {
"ingredient": "Ingredient",
"ingredient_desc": "Raw materials for production",
"finished_product": "Finished Product",
"finished_product_desc": "Products ready for sale"
},
"enums": {
"product_type": {
"ingredient": "Ingredient",
@@ -75,7 +81,16 @@
"pcs": "Pieces",
"pkg": "Packages",
"bags": "Bags",
"boxes": "Boxes"
"boxes": "Boxes",
"KILOGRAMS": "Kilograms",
"GRAMS": "Grams",
"LITERS": "Liters",
"MILLILITERS": "Milliliters",
"UNITS": "Units",
"PIECES": "Pieces",
"PACKAGES": "Packages",
"BAGS": "Bags",
"BOXES": "Boxes"
},
"ingredient_category": {
"flour": "Flour",

View File

@@ -69,6 +69,7 @@
},
"plans": {
"starter": {
"name": "Starter",
"description": "Perfect for small bakeries getting started",
"tagline": "Start reducing waste today",
"roi_badge": "Bakeries save €300-500/month on waste",
@@ -76,6 +77,7 @@
"recommended_for": "Your first bakery"
},
"professional": {
"name": "Professional",
"description": "For growing bakeries with multiple locations",
"tagline": "Grow with artificial intelligence",
"roi_badge": "Bakeries save €800-1,200/month on waste & ordering",
@@ -83,6 +85,7 @@
"recommended_for": "Expanding bakeries"
},
"enterprise": {
"name": "Enterprise",
"description": "For large bakery chains and franchises",
"tagline": "Complete control for your chain",
"roi_badge": "Contact us for custom ROI analysis",
@@ -128,7 +131,6 @@
"choose_plan": "Choose Plan",
"selected": "Selected",
"plan_selected": "Plan Selected",
"click_to_select": "Click on any plan to select it",
"best_value": "Best Value",
"free_trial_footer": "{months} months free • Card required",
"professional_value_badge": "10x capacity • Advanced AI • Multi-location",

View File

@@ -75,6 +75,12 @@
"standard_cost": "Costo objetivo para presupuesto y análisis de variación",
"average_cost": "Calculado automáticamente según el promedio ponderado de compras"
},
"type": {
"ingredient": "Ingrediente",
"ingredient_desc": "Materias primas para producir",
"finished_product": "Producto Terminado",
"finished_product_desc": "Productos listos para venta"
},
"enums": {
"product_type": {
"ingredient": "Ingrediente",
@@ -96,7 +102,16 @@
"pcs": "Piezas",
"pkg": "Paquetes",
"bags": "Bolsas",
"boxes": "Cajas"
"boxes": "Cajas",
"KILOGRAMS": "Kilogramos",
"GRAMS": "Gramos",
"LITERS": "Litros",
"MILLILITERS": "Mililitros",
"UNITS": "Unidades",
"PIECES": "Piezas",
"PACKAGES": "Paquetes",
"BAGS": "Bolsas",
"BOXES": "Cajas"
},
"ingredient_category": {
"flour": "Harinas",

View File

@@ -69,6 +69,7 @@
},
"plans": {
"starter": {
"name": "Inicial",
"description": "Perfecto para panaderías pequeñas comenzando",
"tagline": "Empieza a reducir desperdicios hoy",
"roi_badge": "Panaderías ahorran €300-500/mes en desperdicios",
@@ -76,6 +77,7 @@
"recommended_for": "Tu primera panadería"
},
"professional": {
"name": "Profesional",
"description": "Para panaderías en crecimiento con múltiples ubicaciones",
"tagline": "Crece con inteligencia artificial",
"roi_badge": "Panaderías ahorran €800-1,200/mes en desperdicios y pedidos",
@@ -83,6 +85,7 @@
"recommended_for": "Panaderías en expansión"
},
"enterprise": {
"name": "Empresa",
"description": "Para cadenas de panaderías y franquicias",
"tagline": "Control total para tu cadena",
"roi_badge": "Contacta para análisis ROI personalizado",
@@ -128,7 +131,6 @@
"choose_plan": "Elegir Plan",
"selected": "Seleccionado",
"plan_selected": "Plan Seleccionado",
"click_to_select": "Haz clic en cualquier plan para seleccionarlo",
"best_value": "Mejor Valor",
"free_trial_footer": "{months} meses gratis • Tarjeta requerida",
"professional_value_badge": "10x capacidad • IA Avanzada • Multi-ubicación",

View File

@@ -69,6 +69,12 @@
"cleaning": "Garbiketa",
"other": "Besteak"
},
"type": {
"ingredient": "Osagaia",
"ingredient_desc": "Produkzioko lehengaiak",
"finished_product": "Produktu Amaitua",
"finished_product_desc": "Saltzeko prest dauden produktuak"
},
"product_type": {
"ingredient": "Osagaia",
"finished_product": "Produktu Amaitua"
@@ -89,7 +95,16 @@
"pcs": "Piezak",
"pkg": "Paketeak",
"bags": "Poltsak",
"boxes": "Kutxak"
"boxes": "Kutxak",
"KILOGRAMS": "Kilogramoak",
"GRAMS": "Gramoak",
"LITERS": "Litroak",
"MILLILITERS": "Mililitroak",
"UNITS": "Unitateak",
"PIECES": "Piezak",
"PACKAGES": "Paketeak",
"BAGS": "Poltsak",
"BOXES": "Kutxak"
},
"product_category": {
"bread": "Ogiak",

View File

@@ -29,7 +29,7 @@ interface PasswordData {
const ProfilePage: React.FC = () => {
const user = useAuthUser();
const { t } = useTranslation(['settings', 'auth']);
const { t } = useTranslation(['settings', 'auth', 'subscription']);
const { data: profile, isLoading: profileLoading, error: profileError } = useAuthProfile();
@@ -626,7 +626,7 @@ const ProfilePage: React.FC = () => {
<div className="flex items-center justify-between mb-6">
<h3 className="text-lg font-semibold text-[var(--text-primary)] flex items-center">
<Crown className="w-5 h-5 mr-2 text-yellow-500" />
Plan Actual: {subscriptionService.getPlanDisplayInfo(usageSummary.plan).name}
Plan Actual: {t(`subscription:plans.${usageSummary.plan}.name`, subscriptionService.getPlanDisplayInfo(usageSummary.plan).name)}
</h3>
<Badge
variant={usageSummary.status === 'active' ? 'success' : 'default'}
@@ -792,7 +792,7 @@ const ProfilePage: React.FC = () => {
)}
<div className="text-center mb-6">
<h4 className="text-xl font-bold text-[var(--text-primary)] mb-2">{plan.name}</h4>
<h4 className="text-xl font-bold text-[var(--text-primary)] mb-2">{t(`subscription:plans.${planKey}.name`, plan.name)}</h4>
<div className="text-3xl font-bold text-[var(--color-primary)] mb-1">
{subscriptionService.formatPrice(plan.monthly_price)}
<span className="text-lg text-[var(--text-secondary)]">/mes</span>
@@ -916,11 +916,11 @@ const ProfilePage: React.FC = () => {
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg space-y-2">
<div className="flex justify-between">
<span>Plan actual:</span>
<span>{subscriptionService.getPlanDisplayInfo(usageSummary.plan).name}</span>
<span>{t(`subscription:plans.${usageSummary.plan}.name`, subscriptionService.getPlanDisplayInfo(usageSummary.plan).name)}</span>
</div>
<div className="flex justify-between">
<span>Nuevo plan:</span>
<span>{availablePlans.plans[selectedPlan].name}</span>
<span>{t(`subscription:plans.${selectedPlan}.name`, availablePlans.plans[selectedPlan].name)}</span>
</div>
<div className="flex justify-between font-medium">
<span>Nuevo precio:</span>

View File

@@ -16,11 +16,13 @@ import {
trackUpgradeCTAClicked,
trackUsageMetricViewed
} from '../../../../utils/subscriptionAnalytics';
import { useTranslation } from 'react-i18next';
const SubscriptionPage: React.FC = () => {
const user = useAuthUser();
const currentTenant = useCurrentTenant();
const { notifySubscriptionChanged } = useSubscriptionEvents();
const { t } = useTranslation('subscription');
const [usageSummary, setUsageSummary] = useState<UsageSummary | null>(null);
const [availablePlans, setAvailablePlans] = useState<AvailablePlans | null>(null);
@@ -942,11 +944,11 @@ const SubscriptionPage: React.FC = () => {
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg space-y-2">
<div className="flex justify-between">
<span>Plan actual:</span>
<span>{usageSummary.plan}</span>
<span>{t(`plans.${usageSummary.plan}.name`, usageSummary.plan)}</span>
</div>
<div className="flex justify-between">
<span>Nuevo plan:</span>
<span>{availablePlans.plans[selectedPlan as keyof typeof availablePlans.plans].name}</span>
<span>{t(`plans.${selectedPlan}.name`, availablePlans.plans[selectedPlan as keyof typeof availablePlans.plans].name)}</span>
</div>
<div className="flex justify-between font-medium">
<span>Nuevo precio:</span>