Imporve the UI 5
This commit is contained in:
@@ -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 || ''}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user