From 6d4090f8255676d91c537758d8d967a921a20649 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Wed, 24 Sep 2025 22:22:01 +0200 Subject: [PATCH] Reorganize teh menus --- .../src/components/layout/Sidebar/Sidebar.tsx | 1 + frontend/src/locales/es/common.json | 2 +- .../database/information/InformationPage.tsx | 577 +++++++++++++++++ .../src/pages/app/operations/pos/POSPage.tsx | 595 +++++++++++++++++- .../CommunicationPreferencesPage.tsx | 50 ++ .../personal-info/PersonalInfoPage.tsx | 393 ++++++++++++ .../subscription/SubscriptionPage.tsx | 476 ++++++++++++++ frontend/src/router/AppRouter.tsx | 34 +- frontend/src/router/routes.config.ts | 251 ++++---- 9 files changed, 2251 insertions(+), 128 deletions(-) create mode 100644 frontend/src/pages/app/database/information/InformationPage.tsx create mode 100644 frontend/src/pages/app/settings/communication-preferences/CommunicationPreferencesPage.tsx create mode 100644 frontend/src/pages/app/settings/personal-info/PersonalInfoPage.tsx create mode 100644 frontend/src/pages/app/settings/subscription/SubscriptionPage.tsx diff --git a/frontend/src/components/layout/Sidebar/Sidebar.tsx b/frontend/src/components/layout/Sidebar/Sidebar.tsx index c0bfc4f5..37d2b4eb 100644 --- a/frontend/src/components/layout/Sidebar/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar/Sidebar.tsx @@ -104,6 +104,7 @@ const iconMap: Record> = { database: Store, training: GraduationCap, notifications: Bell, + bell: Bell, settings: Settings, user: User, 'credit-card': CreditCard, diff --git a/frontend/src/locales/es/common.json b/frontend/src/locales/es/common.json index 9541bdd3..ada36b5c 100644 --- a/frontend/src/locales/es/common.json +++ b/frontend/src/locales/es/common.json @@ -14,7 +14,7 @@ "sales": "Ventas", "performance": "Rendimiento", "insights": "Insights IA", - "data": "Datos", + "data": "Mi Panadería", "weather": "Clima", "traffic": "Tráfico", "events": "Eventos", diff --git a/frontend/src/pages/app/database/information/InformationPage.tsx b/frontend/src/pages/app/database/information/InformationPage.tsx new file mode 100644 index 00000000..9068ecf3 --- /dev/null +++ b/frontend/src/pages/app/database/information/InformationPage.tsx @@ -0,0 +1,577 @@ +import React, { useState } from 'react'; +import { Store, MapPin, Clock, Phone, Mail, Globe, Save, X, AlertCircle, Loader } from 'lucide-react'; +import { Button, Card, Input, Select } from '../../../../components/ui'; +import { PageHeader } from '../../../../components/layout'; +import { useToast } from '../../../../hooks/ui/useToast'; +import { useUpdateTenant } from '../../../../api/hooks/tenant'; +import { useCurrentTenant, useTenantActions } from '../../../../stores/tenant.store'; + +interface BakeryConfig { + // General Info + name: string; + description: string; + email: string; + phone: string; + website: string; + // Location + address: string; + city: string; + postalCode: string; + country: string; + // Business + taxId: string; + currency: string; + timezone: string; + language: string; +} + +interface BusinessHours { + [key: string]: { + open: string; + close: string; + closed: boolean; + }; +} + +const InformationPage: React.FC = () => { + const { addToast } = useToast(); + const currentTenant = useCurrentTenant(); + const { loadUserTenants, setCurrentTenant } = useTenantActions(); + const tenantId = currentTenant?.id || ''; + + // Use the current tenant from the store instead of making additional API calls + // to avoid the 422 validation error on the tenant GET endpoint + const tenant = currentTenant; + const tenantLoading = !currentTenant; + const tenantError = null; + + const updateTenantMutation = useUpdateTenant(); + + const [isLoading, setIsLoading] = useState(false); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + + const [config, setConfig] = useState({ + name: '', + description: '', + email: '', + phone: '', + website: '', + address: '', + city: '', + postalCode: '', + country: '', + taxId: '', + currency: 'EUR', + timezone: 'Europe/Madrid', + language: 'es' + }); + + // Load user tenants on component mount to ensure fresh data + React.useEffect(() => { + loadUserTenants(); + }, [loadUserTenants]); + + // Update config when tenant data is loaded + React.useEffect(() => { + if (tenant) { + setConfig({ + name: tenant.name || '', + description: tenant.description || '', + email: tenant.email || '', + phone: tenant.phone || '', + website: tenant.website || '', + address: tenant.address || '', + city: tenant.city || '', + postalCode: tenant.postal_code || '', + country: tenant.country || '', + taxId: '', // Not supported by backend yet + currency: 'EUR', // Default value + timezone: 'Europe/Madrid', // Default value + language: 'es' // Default value + }); + setHasUnsavedChanges(false); // Reset unsaved changes when loading fresh data + } + }, [tenant]); + + const [businessHours, setBusinessHours] = useState({ + monday: { open: '07:00', close: '20:00', closed: false }, + tuesday: { open: '07:00', close: '20:00', closed: false }, + wednesday: { open: '07:00', close: '20:00', closed: false }, + thursday: { open: '07:00', close: '20:00', closed: false }, + friday: { open: '07:00', close: '20:00', closed: false }, + saturday: { open: '08:00', close: '14:00', closed: false }, + sunday: { open: '09:00', close: '13:00', closed: false } + }); + + const [errors, setErrors] = useState>({}); + + const daysOfWeek = [ + { key: 'monday', label: 'Lunes' }, + { key: 'tuesday', label: 'Martes' }, + { key: 'wednesday', label: 'Miércoles' }, + { key: 'thursday', label: 'Jueves' }, + { key: 'friday', label: 'Viernes' }, + { key: 'saturday', label: 'Sábado' }, + { key: 'sunday', label: 'Domingo' } + ]; + + const currencyOptions = [ + { value: 'EUR', label: 'EUR (€)' }, + { value: 'USD', label: 'USD ($)' }, + { value: 'GBP', label: 'GBP (£)' } + ]; + + const timezoneOptions = [ + { value: 'Europe/Madrid', label: 'Madrid (CET/CEST)' }, + { value: 'Atlantic/Canary', label: 'Canarias (WET/WEST)' }, + { value: 'Europe/London', label: 'Londres (GMT/BST)' } + ]; + + const languageOptions = [ + { value: 'es', label: 'Español' }, + { value: 'ca', label: 'Català' }, + { value: 'en', label: 'English' } + ]; + + const validateConfig = (): boolean => { + const newErrors: Record = {}; + + if (!config.name.trim()) { + newErrors.name = 'El nombre es requerido'; + } + + if (!config.email.trim()) { + newErrors.email = 'El email es requerido'; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(config.email)) { + newErrors.email = 'Email inválido'; + } + + if (!config.address.trim()) { + newErrors.address = 'La dirección es requerida'; + } + + if (!config.city.trim()) { + newErrors.city = 'La ciudad es requerida'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSaveConfig = async () => { + if (!validateConfig() || !tenantId) return; + + setIsLoading(true); + + try { + const updateData = { + name: config.name, + description: config.description, + email: config.email, + phone: config.phone, + website: config.website, + address: config.address, + city: config.city, + postal_code: config.postalCode, + country: config.country + }; + + const updatedTenant = await updateTenantMutation.mutateAsync({ + tenantId, + updateData + }); + + // Update the tenant store with the new data + if (updatedTenant) { + setCurrentTenant(updatedTenant); + // Force reload tenant list to ensure cache consistency + await loadUserTenants(); + + // Update localStorage to persist the changes + const tenantStorage = localStorage.getItem('tenant-storage'); + if (tenantStorage) { + const parsedStorage = JSON.parse(tenantStorage); + if (parsedStorage.state && parsedStorage.state.currentTenant) { + parsedStorage.state.currentTenant = updatedTenant; + localStorage.setItem('tenant-storage', JSON.stringify(parsedStorage)); + } + } + } + + setHasUnsavedChanges(false); + addToast('Información actualizada correctamente', { type: 'success' }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Error desconocido'; + addToast(`Error al actualizar: ${errorMessage}`, { type: 'error' }); + } finally { + setIsLoading(false); + } + }; + + const handleInputChange = (field: keyof BakeryConfig) => (e: React.ChangeEvent) => { + setConfig(prev => ({ ...prev, [field]: e.target.value })); + setHasUnsavedChanges(true); + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }; + + const handleSelectChange = (field: keyof BakeryConfig) => (value: string) => { + setConfig(prev => ({ ...prev, [field]: value })); + setHasUnsavedChanges(true); + }; + + const handleHoursChange = (day: string, field: 'open' | 'close' | 'closed', value: string | boolean) => { + setBusinessHours(prev => ({ + ...prev, + [day]: { + ...prev[day], + [field]: value + } + })); + setHasUnsavedChanges(true); + }; + + if (tenantLoading || !currentTenant) { + return ( +
+ +
+ + Cargando información... +
+
+ ); + } + + if (tenantError) { + return ( +
+ + +
+ Error al cargar la información: Error desconocido +
+
+
+ ); + } + + return ( +
+ + + {/* Bakery Header */} + +
+
+ {config.name.charAt(0)} +
+
+

+ {config.name} +

+

{config.email}

+

{config.address}, {config.city}

+
+
+ {hasUnsavedChanges && ( +
+ + Cambios sin guardar +
+ )} +
+
+
+ + {/* Information Sections */} +
+ {/* General Information */} + +

+ + Información General +

+ +
+ } + /> + + } + /> + + } + /> + + } + className="md:col-span-2 xl:col-span-3" + /> +
+ +
+ +