diff --git a/frontend/src/components/dashboard/ActionQueueCard.tsx b/frontend/src/components/dashboard/ActionQueueCard.tsx index 7e9ddf24..dd68f4fb 100644 --- a/frontend/src/components/dashboard/ActionQueueCard.tsx +++ b/frontend/src/components/dashboard/ActionQueueCard.tsx @@ -177,12 +177,36 @@ function ActionItemCard({ {/* Action Buttons */}
{(action.actions || []).map((button, index) => { - const buttonStyles = { - primary: 'bg-blue-600 hover:bg-blue-700 text-white', - secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800', - tertiary: 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300', + const buttonStyles: Record = { + primary: { + backgroundColor: 'var(--color-info-600)', + color: 'var(--text-inverse, #ffffff)', + }, + secondary: { + backgroundColor: 'var(--bg-tertiary)', + color: 'var(--text-primary)', + }, + tertiary: { + backgroundColor: 'var(--bg-primary)', + color: 'var(--text-primary)', + border: '1px solid var(--border-primary)', + }, }; + const hoverStyles: Record = { + primary: { + backgroundColor: 'var(--color-info-700)', + }, + secondary: { + backgroundColor: 'var(--bg-quaternary)', + }, + tertiary: { + backgroundColor: 'var(--bg-secondary)', + }, + }; + + const [isHovered, setIsHovered] = useState(false); + const handleClick = () => { if (button.action === 'approve' && onApprove) { onApprove(action.id); @@ -193,11 +217,19 @@ function ActionItemCard({ } }; + const currentStyle = { + ...buttonStyles[button.type], + ...(isHovered ? hoverStyles[button.type] : {}), + }; + return (
@@ -676,26 +678,23 @@ const FeaturesPage: React.FC = () => { - {/* Grant Programs */} + {/* Sustainability Benefits */}

- {t('features:sustainability.grants.title', 'Elegible para 5 Programas de Ayudas:')} + {t('features:sustainability.benefits.title', 'Funcionalidades de Sostenibilidad:')}

- +

- {t('features:sustainability.grants.pima.title', 'PIMA Adapta (MITECO)')} + {t('features:sustainability.benefits.impact_tracking.title', 'Seguimiento de Impacto Ambiental')}

-

- {t('features:sustainability.grants.pima.amount', 'Hasta €50,000')} -

- {t('features:sustainability.grants.pima.description', 'Proyectos de economía circular. Tu sistema = reducción desperdicio certificada')} + {t('features:sustainability.benefits.impact_tracking.description', 'Medición automática de CO₂, agua y desperdicio alimentario con métricas detalladas')}

@@ -704,17 +703,14 @@ const FeaturesPage: React.FC = () => {
- +

- {t('features:sustainability.grants.tourism.title', 'Planes de Sostenibilidad Turística')} + {t('features:sustainability.benefits.sdg_compliance.title', 'Cumplimiento ODS 12.3')}

-

- {t('features:sustainability.grants.tourism.amount', 'Hasta €30,000')} -

- {t('features:sustainability.grants.tourism.description', 'Para panaderías en zonas turísticas')} + {t('features:sustainability.benefits.sdg_compliance.description', 'Seguimiento automático del cumplimiento de los Objetivos de Desarrollo Sostenible de la ONU')}

@@ -723,17 +719,14 @@ const FeaturesPage: React.FC = () => {
- +

- {t('features:sustainability.grants.moves.title', 'MOVES Circular (Empresas)')} + {t('features:sustainability.benefits.reporting.title', 'Informes de Sostenibilidad')}

-

- {t('features:sustainability.grants.moves.amount', 'Hasta €20,000')} -

- {t('features:sustainability.grants.moves.description', 'Transformación digital sostenible')} + {t('features:sustainability.benefits.reporting.description', 'Generación automática de informes listos para auditorías y certificaciones ambientales')}

@@ -742,17 +735,14 @@ const FeaturesPage: React.FC = () => {
- +

- {t('features:sustainability.grants.regional.title', 'Ayudas Economía Circular Autonómicas')} + {t('features:sustainability.benefits.certification.title', 'Preparación para Certificaciones')}

-

- {t('features:sustainability.grants.regional.amount', 'Variable')} -

- {t('features:sustainability.grants.regional.description', 'Depende de tu comunidad autónoma')} + {t('features:sustainability.benefits.certification.description', 'Datos y documentación listos para solicitudes de certificaciones de sostenibilidad')}

@@ -761,17 +751,14 @@ const FeaturesPage: React.FC = () => {
- +

- {t('features:sustainability.grants.tax.title', 'Bonificaciones Fiscales')} + {t('features:sustainability.benefits.data_export.title', 'Exportación de Datos Ambientales')}

-

- {t('features:sustainability.grants.tax.amount', 'Hasta 25% reducción')} -

- {t('features:sustainability.grants.tax.description', 'Deducción por inversión en sostenibilidad')} + {t('features:sustainability.benefits.data_export.description', 'Exporta tus métricas de sostenibilidad en formatos estándar para reportes externos y auditorías')}

diff --git a/frontend/src/pages/public/LandingPage.tsx b/frontend/src/pages/public/LandingPage.tsx index 2269a9f4..9bb2044c 100644 --- a/frontend/src/pages/public/LandingPage.tsx +++ b/frontend/src/pages/public/LandingPage.tsx @@ -342,7 +342,7 @@ const LandingPage: React.FC = () => {
- {/* Pilar 3: Tus Datos, Tus Subvenciones */} + {/* Pilar 3: Tus Datos, Tu Impacto Ambiental */}
@@ -350,39 +350,45 @@ const LandingPage: React.FC = () => {

- {t('landing:pillar3.title', '🌱 Tus Datos, Tus Subvenciones')} + {t('landing:pillar3.title', 'Tus Datos, Tu Impacto Ambiental')}

- {t('landing:pillar3.intro', '100% de tus datos te pertenecen. Cumples ODS 12.3 de la ONU automáticamente, lo que te hace elegible para subvenciones de sostenibilidad.')} + {t('landing:pillar3.intro', '100% de tus datos te pertenecen. Mide tu impacto ambiental automáticamente y genera informes de sostenibilidad que cumplen con los estándares internacionales.')}

-
€500-2,000
+
+ {t('landing:pillar3.data_ownership_value', '100%')} +

- {t('landing:pillar3.savings', 'Ahorro mensual')} + {t('landing:pillar3.data_ownership', 'Propiedad de datos')}

-
85kg CO₂
+
+ {t('landing:pillar3.co2_metric', 'CO₂')} +

- {t('landing:pillar3.co2', 'Reducidos al mes')} + {t('landing:pillar3.co2', 'Medición automática')}

-
5
+
+ {t('landing:pillar3.sdg_value', 'ODS 12.3')} +

- {t('landing:pillar3.grants', 'Programas de ayudas')} + {t('landing:pillar3.sdg', 'Seguimiento de cumplimiento')}

- {t('landing:pillar3.grants_title', '💶 Elegible para hasta €50,000 en subvenciones')} + {t('landing:pillar3.sustainability_title', 'Informes de Sostenibilidad Automatizados')}

- {t('landing:pillar3.grants_desc', 'PIMA Adapta, Planes Turismo, MOVES Circular, y más')} + {t('landing:pillar3.sustainability_desc', 'Genera informes que cumplen con los estándares internacionales de sostenibilidad y reducción de desperdicio alimentario')}

diff --git a/frontend/src/styles/themes/dark.css b/frontend/src/styles/themes/dark.css index 1fc155d0..3b48ca59 100644 --- a/frontend/src/styles/themes/dark.css +++ b/frontend/src/styles/themes/dark.css @@ -52,50 +52,50 @@ --color-success-light: #4ade80; --color-success-dark: #16a34a; - /* Warning Colors */ - --color-warning-50: #fffbeb; - --color-warning-100: #fef3c7; - --color-warning-200: #fde68a; - --color-warning-300: #fcd34d; - --color-warning-400: #fbbf24; + /* Warning Colors - Inverted scale for dark mode */ + --color-warning-50: #422006; + --color-warning-100: #78350f; + --color-warning-200: #9a3412; + --color-warning-300: #c2410c; + --color-warning-400: #ea580c; --color-warning-500: #f59e0b; - --color-warning-600: #ea580c; - --color-warning-700: #c2410c; - --color-warning-800: #9a3412; - --color-warning-900: #7c2d12; + --color-warning-600: #fbbf24; + --color-warning-700: #fcd34d; + --color-warning-800: #fde68a; + --color-warning-900: #fef3c7; --color-warning: #fb923c; /* Brighter for dark theme */ --color-warning-light: #fdba74; --color-warning-dark: #ea580c; - /* Error Colors */ - --color-error-50: #fef2f2; - --color-error-100: #fee2e2; - --color-error-200: #fecaca; - --color-error-300: #fca5a5; - --color-error-400: #f87171; + /* Error Colors - Inverted scale for dark mode */ + --color-error-50: #450a0a; + --color-error-100: #7f1d1d; + --color-error-200: #991b1b; + --color-error-300: #b91c1c; + --color-error-400: #dc2626; --color-error-500: #ef4444; - --color-error-600: #dc2626; - --color-error-700: #b91c1c; - --color-error-800: #991b1b; - --color-error-900: #7f1d1d; + --color-error-600: #f87171; + --color-error-700: #fca5a5; + --color-error-800: #fecaca; + --color-error-900: #fee2e2; --color-error: #ef4444; /* Brighter for dark theme */ --color-error-light: #f87171; --color-error-dark: #dc2626; - /* Info Colors */ - --color-info-50: #eff6ff; - --color-info-100: #dbeafe; - --color-info-200: #bfdbfe; - --color-info-300: #93c5fd; - --color-info-400: #60a5fa; - --color-info-500: #3b82f6; - --color-info-600: #0284c7; - --color-info-700: #0369a1; - --color-info-800: #075985; - --color-info-900: #0c4a6e; - --color-info: #0ea5e9; /* Brighter for dark theme */ - --color-info-light: #0ea5e9; - --color-info-dark: #0369a1; + /* Info Colors - Adjusted for dark mode */ + --color-info-50: #0c4a6e; + --color-info-100: #075985; + --color-info-200: #0369a1; + --color-info-300: #0284c7; + --color-info-400: #0ea5e9; + --color-info-500: #38bdf8; + --color-info-600: #38bdf8; + --color-info-700: #60a5fa; + --color-info-800: #93c5fd; + --color-info-900: #bfdbfe; + --color-info: #38bdf8; /* Brighter cyan for dark theme */ + --color-info-light: #60a5fa; + --color-info-dark: #0ea5e9; /* === THEME-SPECIFIC COLORS === */ @@ -116,10 +116,10 @@ --text-muted: #64748b; --text-disabled: #475569; - /* Border Colors */ - --border-primary: #334155; - --border-secondary: #475569; - --border-tertiary: #64748b; + /* Border Colors - Enhanced visibility for dark mode */ + --border-primary: #475569; + --border-secondary: #64748b; + --border-tertiary: #94a3b8; --border-focus: #f59e0b; --border-error: #ef4444; --border-success: #22c55e; diff --git a/services/inventory/app/services/sustainability_service.py b/services/inventory/app/services/sustainability_service.py index e355dc2c..eeeabe72 100644 --- a/services/inventory/app/services/sustainability_service.py +++ b/services/inventory/app/services/sustainability_service.py @@ -734,44 +734,65 @@ class SustainabilityService: def _assess_grant_readiness(self, sdg_compliance: Dict[str, Any]) -> Dict[str, Any]: """ Assess readiness for EU grant programs accessible to Spanish bakeries and retail. - Based on 2025 research and Spain's Law 1/2025 on food waste prevention. + Based on 2026 verified research. Updated Dec 2025. """ reduction = sdg_compliance['sdg_12_3']['reduction_achieved'] grants = { - 'life_circular_economy': { - 'eligible': reduction >= 15, - 'confidence': 'high' if reduction >= 25 else 'medium' if reduction >= 15 else 'low', - 'requirements_met': reduction >= 15, - 'funding_eur': 73_000_000, # €73M available for circular economy - 'deadline': '2025-09-23', - 'program_type': 'grant' - }, - 'horizon_europe_cluster_6': { + 'horizon_europe_food_systems': { 'eligible': reduction >= 20, 'confidence': 'high' if reduction >= 35 else 'medium' if reduction >= 20 else 'low', 'requirements_met': reduction >= 20, - 'funding_eur': 880_000_000, # €880M+ annually for food systems - 'deadline': 'rolling_2025', - 'program_type': 'grant' + 'funding_eur': 12_000_000, # €3-12M per project + 'deadline': '2026-02-18', + 'program_type': 'grant', + 'category': 'European Union' }, - 'fedima_sustainability_grant': { + 'horizon_europe_circular_sme': { 'eligible': reduction >= 15, - 'confidence': 'high' if reduction >= 20 else 'medium' if reduction >= 15 else 'low', + 'confidence': 'high' if reduction >= 25 else 'medium' if reduction >= 15 else 'low', 'requirements_met': reduction >= 15, - 'funding_eur': 20_000, # €20k bi-annual - 'deadline': '2025-06-30', + 'funding_eur': 10_000_000, # €10M total program + 'deadline': '2026-02-18', 'program_type': 'grant', - 'sector_specific': 'bakery' + 'category': 'European Union' }, - 'eit_food_retail': { - 'eligible': reduction >= 20, - 'confidence': 'high' if reduction >= 30 else 'medium' if reduction >= 20 else 'low', - 'requirements_met': reduction >= 20, - 'funding_eur': 45_000, # €15-45k range - 'deadline': 'rolling', + 'eit_food_impact_2026': { + 'eligible': reduction >= 15, + 'confidence': 'high' if reduction >= 25 else 'medium' if reduction >= 15 else 'low', + 'requirements_met': reduction >= 15, + 'funding_eur': 1_000_000, # €50K-1M range + 'deadline': 'rolling_2026', 'program_type': 'grant', - 'sector_specific': 'retail' + 'category': 'European Union' + }, + 'eib_circular_economy': { + 'eligible': reduction >= 10, + 'confidence': 'high' if reduction >= 20 else 'medium' if reduction >= 10 else 'low', + 'requirements_met': reduction >= 10, + 'funding_eur': 12_500_000, # Up to €12.5M loans + 'deadline': 'ongoing_2026', + 'program_type': 'loan', + 'category': 'European Union' + }, + 'circular_economy_perte': { + 'eligible': reduction >= 15, + 'confidence': 'high' if reduction >= 25 else 'medium' if reduction >= 15 else 'low', + 'requirements_met': reduction >= 15, + 'funding_eur': 10_000_000, # €150K-10M range + 'deadline': 'rolling_until_2026', + 'program_type': 'grant', + 'category': 'Spain' + }, + 'planes_turismo_2026': { + 'eligible': reduction >= 10, + 'confidence': 'medium', + 'requirements_met': reduction >= 10, + 'funding_eur': 500_000, # Variable by region + 'deadline': '2026-12-31', + 'program_type': 'grant', + 'category': 'Spain', + 'sector_specific': 'tourism' }, 'un_sdg_certified': { 'eligible': reduction >= 50, @@ -779,7 +800,8 @@ class SustainabilityService: 'requirements_met': reduction >= 50, 'funding_eur': 0, # Certification, not funding 'deadline': 'ongoing', - 'program_type': 'certification' + 'program_type': 'certification', + 'category': 'International' } } diff --git a/services/orchestrator/scripts/demo/seed_demo_orchestration_runs.py b/services/orchestrator/scripts/demo/seed_demo_orchestration_runs.py index 5c29b8cf..54787d3a 100644 --- a/services/orchestrator/scripts/demo/seed_demo_orchestration_runs.py +++ b/services/orchestrator/scripts/demo/seed_demo_orchestration_runs.py @@ -189,30 +189,46 @@ async def generate_orchestration_for_tenant( runs_created = 0 steps_created = 0 + # Special case: Create at least 1 recent completed run for "today" (for dashboard visibility) + # This ensures the dashboard "Listo Para Planificar Tu Día" shows data + today_run_created = False + for i in range(total_runs): - # Determine temporal distribution - rand_temporal = random.random() - cumulative = 0 - temporal_category = None - - for category, details in orch_config["temporal_distribution"].items(): - cumulative += details["percentage"] - if rand_temporal <= cumulative: - temporal_category = details - break - - if not temporal_category: + # For the first run, create it for today with completed status + if i == 0 and not today_run_created: temporal_category = orch_config["temporal_distribution"]["completed"] + # Use current time instead of BASE_REFERENCE_DATE + now = datetime.now(timezone.utc) + # Set offset to create run that started yesterday and completed today + offset_days = 0 # Today + run_date = now.date() + today_run_created = True + # Force status to completed for dashboard visibility + status = "completed" + else: + # Determine temporal distribution + rand_temporal = random.random() + cumulative = 0 + temporal_category = None - # Calculate run date - offset_days = random.randint( - temporal_category["offset_days_min"], - temporal_category["offset_days_max"] - ) - run_date = calculate_date_from_offset(offset_days) + for category, details in orch_config["temporal_distribution"].items(): + cumulative += details["percentage"] + if rand_temporal <= cumulative: + temporal_category = details + break - # Select status - status = random.choice(temporal_category["statuses"]) + if not temporal_category: + temporal_category = orch_config["temporal_distribution"]["completed"] + + # Calculate run date + offset_days = random.randint( + temporal_category["offset_days_min"], + temporal_category["offset_days_max"] + ) + run_date = calculate_date_from_offset(offset_days) + + # Select status + status = random.choice(temporal_category["statuses"]) # Select run type run_type_choice = weighted_choice(orch_config["run_types"]) @@ -232,19 +248,26 @@ async def generate_orchestration_for_tenant( run_number = generate_run_number(tenant_id, i + 1, run_type) # Calculate timing based on status - started_at = calculate_datetime_from_offset(offset_days - 1) - completed_at = None - duration_seconds = None + # For today's run (i==0), use current datetime instead of BASE_REFERENCE_DATE + if i == 0 and today_run_created: + now = datetime.now(timezone.utc) + started_at = now - timedelta(hours=2) # Started 2 hours ago + completed_at = now - timedelta(minutes=30) # Completed 30 minutes ago + duration_seconds = int((completed_at - started_at).total_seconds()) + else: + started_at = calculate_datetime_from_offset(offset_days - 1) + completed_at = None + duration_seconds = None - if status in ["completed", "partial_success"]: - completed_at = calculate_datetime_from_offset(offset_days) - duration_seconds = int((completed_at - started_at).total_seconds()) - elif status == "failed": - completed_at = calculate_datetime_from_offset(offset_days - 0.5) - duration_seconds = int((completed_at - started_at).total_seconds()) - elif status == "cancelled": - completed_at = calculate_datetime_from_offset(offset_days - 0.2) - duration_seconds = int((completed_at - started_at).total_seconds()) + if status in ["completed", "partial_success"]: + completed_at = calculate_datetime_from_offset(offset_days) + duration_seconds = int((completed_at - started_at).total_seconds()) + elif status == "failed": + completed_at = calculate_datetime_from_offset(offset_days - 0.5) + duration_seconds = int((completed_at - started_at).total_seconds()) + elif status == "cancelled": + completed_at = calculate_datetime_from_offset(offset_days - 0.2) + duration_seconds = int((completed_at - started_at).total_seconds()) # Generate step timing forecasting_started_at = started_at