Traducciones

This commit is contained in:
David Arranz 2026-06-15 11:39:05 +02:00
parent 8ecebc64bb
commit 879927db45
23 changed files with 851 additions and 614 deletions

View File

@ -2,6 +2,7 @@
"common": { "common": {
"cancel": "Cancelar", "cancel": "Cancelar",
"save": "Guardar", "save": "Guardar",
"saving": "Guardando...",
"required": "•" "required": "•"
}, },
"components": { "components": {

View File

@ -1,208 +1,305 @@
{ {
"common": { "common": {
"more_details": "More details", "actions": {
"back_to_list": "Back to the list", "cancel": "Cancel",
"cancel": "Cancel", "create": "Create customer",
"save": "Save", "edit": "Edit",
"saving": "Saving...", "more": "More",
"validating_form": { "save": "Save",
"title": "Check the fields", "saving": "Saving...",
"message": "There are validation errors in the form" "view": "View"
} },
}, "feedback": {
"catalog": { "validation": {
"status": { "message": "There are validation errors in the form.",
"active": "active", "title": "Check the fields"
"inactive": "inactive"
}
},
"pages": {
"title": "Customers",
"description": "Manage your customers",
"list": {
"title": "Customer list",
"description": "Manage your customers and their contact information",
"grid_columns": {
"customer": "Customer",
"status": "Status",
"contact": "Contact",
"address": "Address",
"actions": "Actions"
},
"actions": {
"more": "More",
"view": "View",
"edit": "Edit customer",
"delete": "Delete customer",
"visit_website": "Visit website",
"copy_email": "Copy email"
} }
}, },
"create": { "states": {
"title": "New customer", "active": "Active",
"description": "Create a new customer", "inactive": "Inactive"
"error": { }
"title": "Error creating", },
"message": "The new customer could not be created" "list": {
} "actions": {
"copy_email": "Copy email",
"delete": "Delete customer",
"edit": "Edit customer",
"more": "More",
"view": "View"
}, },
"update": { "columns": {
"title": "Update customer", "actions": "Actions",
"description": "Update a customer", "address": "Address",
"contact": "Contact",
"customer": "Customer"
},
"description": "Manage your customers and their contact information.",
"errors": {
"load_message": "Please try again later.",
"load_title": "The customer list could not be loaded"
},
"title": "Customers"
},
"view": {
"actions": {
"edit": "Edit",
"open_menu": "More actions"
},
"errors": {
"load_message": "Please try again later.",
"load_title": "The customer could not be loaded"
},
"fields": {
"city": "City",
"country": "Country",
"currency_code": "Preferred currency",
"default_taxes": "Default taxes",
"email": "Email",
"fax": "Fax",
"language_code": "Preferred language",
"legal_record": "Legal record",
"mobile": "Mobile",
"name": "Name",
"phone": "Phone",
"postal_code": "Postal code",
"province": "Province",
"reference": "Reference",
"street": "Street",
"website": "Website"
},
"sections": {
"address": "Address",
"basic_info": "Basic information",
"contact_info": "Contact information",
"other_contacts": "Other",
"preferences": "Preferences",
"primary_contact": "Primary contact",
"secondary_contact": "Secondary contact"
}
},
"create": {
"description": "Create a new customer.",
"errors": {
"message": "The new customer could not be created.",
"title": "Error creating customer",
"unknown": "An unexpected error occurred."
},
"feedback": {
"success": { "success": {
"title": "Customer updated", "message": "The customer has been created successfully.",
"message": "The customer has been successfully updated" "title": "Customer created"
} }
} },
"title": "New customer"
}, },
"form_fields": { "update": {
"customer_type": { "actions": {
"label": "This contact is...", "submit": "Save changes"
"description": "Select the type of customer",
"company": "a company",
"individual": "a person"
}, },
"name": { "description": "Update a customer's data.",
"label": "Name", "errors": {
"placeholder": "Enter customer name", "load_message": "Please try again later.",
"description": "The full name of the customer" "load_title": "The customer could not be loaded",
"message": "Review the data and try again.",
"missing_id": "The customer ID is missing.",
"not_found_message": "Check the identifier or return to the list.",
"not_found_title": "Customer not found",
"title": "The changes could not be saved",
"unexpected": "An unexpected error occurred."
}, },
"trade_name": { "feedback": {
"label": "Trade name", "no_changes": {
"placeholder": "Enter trade name", "message": "You have not made any changes.",
"description": "The trade name of the customer" "title": "No changes"
},
"success": {
"message": "The customer has been updated successfully.",
"title": "Customer updated"
},
"validation": {
"message": "There are validation errors. Fix the highlighted fields.",
"title": "Check the fields"
}
}, },
"tin": { "loading": "Loading customer...",
"label": "Tax Identification Number", "title": "Edit customer"
"placeholder": "Enter TIN",
"description": "The tax identification number of the customer"
},
"reference": {
"label": "Reference",
"placeholder": "Enter reference",
"description": "A reference for the customer"
},
"street": {
"label": "Street",
"placeholder": "Enter street",
"description": "The street address of the customer"
},
"street2": {
"label": "Street 2",
"placeholder": "Enter street 2",
"description": "The street address of the customer"
},
"city": {
"label": "City",
"placeholder": "Enter city",
"description": "The city of the customer"
},
"postal_code": {
"label": "Postal code",
"placeholder": "Enter postal code",
"description": "The postal code of the customer"
},
"province": {
"label": "Province",
"placeholder": "Enter province",
"description": "The province of the customer"
},
"country": {
"label": "Country",
"placeholder": "Select country",
"description": "The country of the customer"
},
"email_primary": {
"label": "Primary email",
"placeholder": "Enter primary email",
"description": "The primary email address of the customer"
},
"email_secondary": {
"label": "Secondary email",
"placeholder": "Enter secondary email",
"description": "The secondary email address of the customer"
},
"phone_primary": {
"label": "Primary phone",
"placeholder": "Enter primary phone number",
"description": "The primary phone number of the customer"
},
"phone_secondary": {
"label": "Secondary phone",
"placeholder": "Enter secondary phone number ",
"description": "The secondary phone number of the customer"
},
"mobile_primary": {
"label": "Primary mobile",
"placeholder": "Enter primary mobile number",
"description": "The primary mobile number of the customer"
},
"mobile_secondary": {
"label": "Secondary mobile",
"placeholder": "Enter secondary mobile number",
"description": "The secondary mobile number of the customer"
},
"fax": {
"label": "Fax",
"placeholder": "Enter fax number",
"description": "The fax number of the customer"
},
"website": {
"label": "Website",
"placeholder": "Enter website URL",
"description": "The website of the customer"
},
"default_taxes": {
"label": "Default taxes",
"placeholder": "Select default taxes",
"description": "The default tax rates for the customer"
},
"language_code": {
"label": "Language",
"placeholder": "Select language",
"description": "The preferred language of the customer"
},
"currency_code": {
"label": "Currency",
"placeholder": "Select currency",
"description": "The preferred currency of the customer"
},
"legal_record": {
"label": "Legal record",
"placeholder": "Enter legal record",
"description": "The legal record of the customer"
}
}, },
"form_groups": { "form_groups": {
"basic_info": {
"title": "Basic information",
"description": "General customer details"
},
"address": { "address": {
"title": "Address", "description": "Customer location.",
"description": "Customer location" "title": "Address"
},
"basic_info": {
"description": "General customer details.",
"title": "Basic information"
}, },
"contact_info": { "contact_info": {
"title": "Contact information", "description": "Customer contact details.",
"description": "Customer contact details" "title": "Contact information"
}, },
"preferences": { "preferences": {
"title": "Preferences", "description": "Additional customer settings.",
"description": "Additional customer configurations" "title": "Preferences"
} }
}, },
"components": { "fields": {
"entity_selector": { "city": {
"close": "Close", "description": "The customer's city.",
"select_entity": "Select entity", "label": "City",
"create_new_entity": "Create new entity", "placeholder": "Enter the city"
"search_entity": "Search entity",
"no_entities_found": "No results found for \"{{search}}\"",
"select_or_create": "Select an item from the list or create a new one.",
"create_label": "Create new item"
}, },
"country": {
"description": "The customer's country.",
"label": "Country",
"placeholder": "Select the country"
},
"currency_code": {
"description": "The customer's preferred currency.",
"label": "Currency",
"placeholder": "Select the currency"
},
"customer_type": {
"description": "Select the customer type.",
"label": "This customer is...",
"options": {
"company": "Company",
"individual": "Individual"
}
},
"default_taxes": {
"description": "The customer's default taxes.",
"label": "Default taxes",
"placeholder": "Select the default taxes"
},
"email_primary": {
"description": "The customer's primary email address.",
"label": "Primary email",
"placeholder": "Enter the email address"
},
"email_secondary": {
"description": "The customer's secondary email address.",
"label": "Secondary email",
"placeholder": "Enter the email address"
},
"fax": {
"description": "The customer's fax number.",
"label": "Fax",
"placeholder": "Enter the fax number"
},
"language_code": {
"description": "The customer's preferred language.",
"label": "Language",
"placeholder": "Select the language"
},
"legal_record": {
"description": "The customer's legal record.",
"label": "Legal record",
"placeholder": "Enter the legal record"
},
"mobile_primary": {
"description": "The customer's primary mobile number.",
"label": "Primary mobile",
"placeholder": "Enter the mobile number"
},
"mobile_secondary": {
"description": "The customer's secondary mobile number.",
"label": "Secondary mobile",
"placeholder": "Enter the secondary mobile number"
},
"name": {
"description": "The customer's full name.",
"label": "Name",
"placeholder": "Enter the customer name"
},
"phone_primary": {
"description": "The customer's primary phone number.",
"label": "Primary phone",
"placeholder": "Enter the phone number"
},
"phone_secondary": {
"description": "The customer's secondary phone number.",
"label": "Secondary phone",
"placeholder": "Enter the secondary phone number"
},
"postal_code": {
"description": "The customer's postal code.",
"label": "Postal code",
"placeholder": "Enter the postal code"
},
"province": {
"description": "The customer's province.",
"label": "Province",
"placeholder": "Enter the province"
},
"reference": {
"description": "An internal reference for the customer.",
"label": "Reference",
"placeholder": "Enter the reference"
},
"street": {
"description": "The customer's main street address.",
"label": "Street",
"placeholder": "Enter the street"
},
"street2": {
"description": "Additional address information for the customer.",
"label": "Street 2",
"placeholder": "Enter additional address information"
},
"tin": {
"description": "The customer's tax identification number.",
"label": "Tax identification number",
"placeholder": "Enter the TIN"
},
"trade_name": {
"description": "The customer's trade name.",
"label": "Trade name",
"placeholder": "Enter the trade name"
},
"website": {
"description": "The customer's website.",
"label": "Website",
"placeholder": "Enter the website URL"
}
},
"dialogs": {
"quick_create_customer": {
"actions": {
"submit": "Create customer"
},
"title": "New customer"
}
},
"toasts": {
"quick_create_customer": {
"error": {
"title": "Error creating customer"
},
"success": {
"message": "The customer has been created successfully.",
"title": "Customer created"
}
}
},
"errors": {
"unexpected": "An unexpected error occurred."
},
"components": {
"address_cell": { "address_cell": {
"open_in_google_maps": "Open address in Google Maps" "open_in_google_maps": "Open address in Google Maps"
},
"entity_selector": {
"close": "Close",
"create_label": "Create new item",
"create_new_entity": "Create new entity",
"no_entities_found": "No results found for \"{{search}}\"",
"search_entity": "Search entity",
"select_entity": "Select entity",
"select_or_create": "Select an item from the list or create a new one."
},
"taxes_multi_select": {
"placeholder": "Select taxes"
} }
} }
} }

View File

@ -1,210 +1,305 @@
{ {
"common": { "common": {
"more_details": "Más detalles", "actions": {
"back_to_list": "Back to the list", "cancel": "Cancelar",
"cancel": "Cancelar", "create": "Crear cliente",
"save": "Guardar", "edit": "Editar",
"saving": "Guardando...", "more": "Más",
"validating_form": { "save": "Guardar",
"title": "Revisa los campos", "saving": "Guardando...",
"message": "Hay errores de validación en el formulario" "view": "Ver"
} },
}, "feedback": {
"catalog": { "validation": {
"status": { "message": "Hay errores de validación en el formulario.",
"active": "activo", "title": "Revisa los campos"
"inactive": "inactivo"
}
},
"pages": {
"title": "Clientes",
"description": "Gestiona tus clientes",
"list": {
"title": "Lista de clientes",
"description": "Gestiona la información de tus clientes y sus datos de contacto",
"grid_columns": {
"customer": "Cliente",
"status": "Estado",
"contact": "Contacto",
"address": "Dirección",
"actions": "Acciones"
},
"actions": {
"more": "Más",
"view": "Ver",
"edit": "Editar cliente",
"delete": "Eliminar cliente",
"visit_website": "Visitar sitio web",
"copy_email": "Copiar email"
} }
}, },
"create": { "states": {
"title": "Nuevo cliente", "active": "Activo",
"description": "Crear un nuevo cliente", "inactive": "Inactivo"
"back_to_list": "Volver a la lista", }
"error": { },
"title": "Error al crear", "list": {
"message": "No se pudo crear el nuevo cliente" "actions": {
} "copy_email": "Copiar email",
"delete": "Eliminar cliente",
"edit": "Editar cliente",
"more": "Más",
"view": "Ver"
}, },
"update": { "columns": {
"title": "Modificación de cliente", "actions": "Acciones",
"description": "Modificar los datos de un cliente", "address": "Dirección",
"back_to_list": "Back to the list", "contact": "Contacto",
"customer": "Cliente"
},
"description": "Gestiona la información de tus clientes y sus datos de contacto.",
"errors": {
"load_message": "Inténtalo de nuevo más tarde.",
"load_title": "No se pudo cargar la lista de clientes"
},
"title": "Clientes"
},
"view": {
"actions": {
"edit": "Editar",
"open_menu": "Más acciones"
},
"errors": {
"load_message": "Inténtalo de nuevo más tarde.",
"load_title": "No se pudo cargar el cliente"
},
"fields": {
"city": "Ciudad",
"country": "País",
"currency_code": "Moneda preferida",
"default_taxes": "Impuestos por defecto",
"email": "Email",
"fax": "Fax",
"language_code": "Idioma preferido",
"legal_record": "Registro legal",
"mobile": "Móvil",
"name": "Nombre",
"phone": "Teléfono",
"postal_code": "Código postal",
"province": "Provincia",
"reference": "Referencia",
"street": "Calle",
"website": "Sitio web"
},
"sections": {
"address": "Dirección",
"basic_info": "Información básica",
"contact_info": "Información de contacto",
"other_contacts": "Otros",
"preferences": "Preferencias",
"primary_contact": "Contacto principal",
"secondary_contact": "Contacto secundario"
}
},
"create": {
"description": "Crear un nuevo cliente.",
"errors": {
"message": "No se pudo crear el nuevo cliente.",
"title": "Error al crear",
"unknown": "Ha ocurrido un error inesperado."
},
"feedback": {
"success": { "success": {
"title": "Cliente actualizado", "message": "Se ha creado el cliente correctamente.",
"message": "El cliente ha sido actualizado correctamente" "title": "Cliente creado"
} }
} },
"title": "Nuevo cliente"
}, },
"form_fields": { "update": {
"customer_type": { "actions": {
"label": "Este cliente es...", "submit": "Guardar cambios"
"description": "Seleccione el tipo de cliente",
"company": "Empresa",
"individual": "Particular"
}, },
"name": { "description": "Modificar los datos de un cliente.",
"label": "Nombre", "errors": {
"placeholder": "Ingrese el nombre del cliente", "load_message": "Inténtalo de nuevo más tarde.",
"description": "El nombre completo del cliente" "load_title": "No se pudo cargar el cliente",
"message": "Revisa los datos e inténtalo de nuevo.",
"missing_id": "Falta el ID del cliente.",
"not_found_message": "Revisa el identificador o vuelve al listado.",
"not_found_title": "Cliente no encontrado",
"title": "No se pudo guardar los cambios",
"unexpected": "Ha ocurrido un error inesperado."
}, },
"trade_name": { "feedback": {
"label": "Nombre comercial", "no_changes": {
"placeholder": "Ingrese el nombre comercial", "message": "No has realizado ningún cambio.",
"description": "El nombre comercial del cliente" "title": "Sin cambios"
},
"success": {
"message": "El cliente ha sido actualizado correctamente.",
"title": "Cliente actualizado"
},
"validation": {
"message": "Hay errores de validación. Corrige los campos indicados.",
"title": "Revisa los campos"
}
}, },
"tin": { "loading": "Cargando cliente...",
"label": "Número de Identificación Fiscal", "title": "Modificación de cliente"
"placeholder": "Ingrese el NIF",
"description": "El número de identificación fiscal del cliente"
},
"reference": {
"label": "Referencia",
"placeholder": "Ingrese la referencia",
"description": "Una referencia interna para el cliente"
},
"street": {
"label": "Calle",
"placeholder": "Ingrese la calle",
"description": "La dirección de la calle del cliente"
},
"street2": {
"label": "Calle (ampliación)",
"placeholder": "Ingrese la calle",
"description": "La dirección de la calle del cliente"
},
"city": {
"label": "Ciudad",
"placeholder": "Ingrese la ciudad",
"description": "La ciudad del cliente"
},
"postal_code": {
"label": "Código postal",
"placeholder": "Ingrese el código postal",
"description": "El código postal del cliente"
},
"province": {
"label": "Provincia",
"placeholder": "Ingrese la provincia",
"description": "La provincia del cliente"
},
"country": {
"label": "País",
"placeholder": "Seleccione el país",
"description": "El país del cliente"
},
"email_primary": {
"label": "Email principal",
"placeholder": "Ingrese el correo electrónico",
"description": "La dirección de correo electrónico principal del cliente"
},
"email_secondary": {
"label": "Email secundario",
"placeholder": "Ingrese el correo electrónico",
"description": "La dirección de correo electrónico secundario del cliente"
},
"phone_primary": {
"label": "Teléfono",
"placeholder": "Ingrese el número de teléfono",
"description": "El número de teléfono del cliente"
},
"phone_secondary": {
"label": "Teléfono secundario",
"placeholder": "Ingrese el número de teléfono secundario",
"description": "El número de teléfono secundario del cliente"
},
"mobile_primary": {
"label": "Teléfono",
"placeholder": "Ingrese el número de teléfono",
"description": "El número de teléfono del cliente"
},
"mobile_secondary": {
"label": "Teléfono secundario",
"placeholder": "Ingrese el número de teléfono secundario",
"description": "El número de teléfono secundario del cliente"
},
"fax": {
"label": "Fax",
"placeholder": "Ingrese el número de fax",
"description": "El número de fax del cliente"
},
"website": {
"label": "Sitio web",
"placeholder": "Ingrese la URL del sitio web",
"description": "El sitio web del cliente"
},
"default_taxes": {
"label": "Impuesto por defecto",
"placeholder": "Seleccione el impuesto por defecto",
"description": "La tasa de impuesto por defecto para el cliente"
},
"language_code": {
"label": "Idioma",
"placeholder": "Seleccione el idioma",
"description": "El idioma preferido del cliente"
},
"currency_code": {
"label": "Moneda",
"placeholder": "Seleccione la moneda",
"description": "La moneda preferida del cliente"
},
"legal_record": {
"label": "Registro legal",
"placeholder": "Ingrese el registro legal",
"description": "El registro legal del cliente"
}
}, },
"form_groups": { "form_groups": {
"basic_info": {
"title": "Información básica",
"description": "Detalles generales del cliente"
},
"address": { "address": {
"title": "Dirección", "description": "Ubicación del cliente.",
"description": "Ubicación del cliente" "title": "Dirección"
},
"basic_info": {
"description": "Detalles generales del cliente.",
"title": "Información básica"
}, },
"contact_info": { "contact_info": {
"title": "Información de contacto", "description": "Detalles de contacto del cliente.",
"description": "Detalles de contacto del cliente" "title": "Información de contacto"
}, },
"preferences": { "preferences": {
"title": "Preferencias", "description": "Configuraciones adicionales del cliente.",
"description": "Configuraciones adicionales del cliente" "title": "Preferencias"
} }
}, },
"components": { "fields": {
"entity_selector": { "city": {
"close": "Cerrar", "description": "La ciudad del cliente.",
"select_entity": "Seleccionar entidad", "label": "Ciudad",
"create_new_entity": "Crear nueva entidad", "placeholder": "Introduce la ciudad"
"search_entity": "Buscar entidad",
"no_entities_found": "No se encontraron resultados para \"{{search}}\"",
"select_or_create": "Seleccione un elemento de la lista o cree uno nuevo.",
"create_label": "Crear nuevo elemento"
}, },
"country": {
"description": "El país del cliente.",
"label": "País",
"placeholder": "Selecciona el país"
},
"currency_code": {
"description": "La moneda preferida del cliente.",
"label": "Moneda",
"placeholder": "Selecciona la moneda"
},
"customer_type": {
"description": "Selecciona el tipo de cliente.",
"label": "Este cliente es...",
"options": {
"company": "Empresa",
"individual": "Particular"
}
},
"default_taxes": {
"description": "Los impuestos predeterminados del cliente.",
"label": "Impuestos por defecto",
"placeholder": "Selecciona los impuestos por defecto"
},
"email_primary": {
"description": "La dirección de correo electrónico principal del cliente.",
"label": "Email principal",
"placeholder": "Introduce el correo electrónico"
},
"email_secondary": {
"description": "La dirección de correo electrónico secundaria del cliente.",
"label": "Email secundario",
"placeholder": "Introduce el correo electrónico"
},
"fax": {
"description": "El número de fax del cliente.",
"label": "Fax",
"placeholder": "Introduce el número de fax"
},
"language_code": {
"description": "El idioma preferido del cliente.",
"label": "Idioma",
"placeholder": "Selecciona el idioma"
},
"legal_record": {
"description": "El registro legal del cliente.",
"label": "Registro legal",
"placeholder": "Introduce el registro legal"
},
"mobile_primary": {
"description": "El número de móvil principal del cliente.",
"label": "Móvil principal",
"placeholder": "Introduce el número de móvil"
},
"mobile_secondary": {
"description": "El número de móvil secundario del cliente.",
"label": "Móvil secundario",
"placeholder": "Introduce el número de móvil secundario"
},
"name": {
"description": "El nombre completo del cliente.",
"label": "Nombre",
"placeholder": "Introduce el nombre del cliente"
},
"phone_primary": {
"description": "El número de teléfono principal del cliente.",
"label": "Teléfono principal",
"placeholder": "Introduce el número de teléfono"
},
"phone_secondary": {
"description": "El número de teléfono secundario del cliente.",
"label": "Teléfono secundario",
"placeholder": "Introduce el número de teléfono secundario"
},
"postal_code": {
"description": "El código postal del cliente.",
"label": "Código postal",
"placeholder": "Introduce el código postal"
},
"province": {
"description": "La provincia del cliente.",
"label": "Provincia",
"placeholder": "Introduce la provincia"
},
"reference": {
"description": "Una referencia interna para el cliente.",
"label": "Referencia",
"placeholder": "Introduce la referencia"
},
"street": {
"description": "La dirección principal del cliente.",
"label": "Calle",
"placeholder": "Introduce la calle"
},
"street2": {
"description": "Información adicional de la dirección del cliente.",
"label": "Calle (ampliación)",
"placeholder": "Introduce información adicional de la dirección"
},
"tin": {
"description": "El número de identificación fiscal del cliente.",
"label": "Número de identificación fiscal",
"placeholder": "Introduce el NIF"
},
"trade_name": {
"description": "El nombre comercial del cliente.",
"label": "Nombre comercial",
"placeholder": "Introduce el nombre comercial"
},
"website": {
"description": "El sitio web del cliente.",
"label": "Sitio web",
"placeholder": "Introduce la URL del sitio web"
}
},
"dialogs": {
"quick_create_customer": {
"actions": {
"submit": "Crear cliente"
},
"title": "Nuevo cliente"
}
},
"toasts": {
"quick_create_customer": {
"error": {
"title": "Error al crear cliente"
},
"success": {
"message": "Se ha creado correctamente.",
"title": "Cliente creado"
}
}
},
"errors": {
"unexpected": "Ha ocurrido un error inesperado."
},
"components": {
"address_cell": { "address_cell": {
"open_in_google_maps": "Abrir dirección en Google Maps" "open_in_google_maps": "Abrir dirección en Google Maps"
},
"entity_selector": {
"close": "Cerrar",
"create_label": "Crear nuevo elemento",
"create_new_entity": "Crear nueva entidad",
"no_entities_found": "No se encontraron resultados para \"{{search}}\"",
"search_entity": "Buscar entidad",
"select_entity": "Seleccionar entidad",
"select_or_create": "Selecciona un elemento de la lista o crea uno nuevo."
},
"taxes_multi_select": {
"placeholder": "Selecciona los impuestos"
} }
} }
} }

View File

@ -56,17 +56,17 @@ export const useCustomerCreateController = (options?: UseCustomerCreateControlle
if (options?.successToasts !== false) { if (options?.successToasts !== false) {
showSuccessToast( showSuccessToast(
t("pages.create.success.title", "Cliente creado"), t("create.feedback.success.title"),
t("pages.create.success.message", "Se ha creado el cliente correctamente.") t("create.feedback.success.message")
); );
} }
options?.onCreated?.(created); options?.onCreated?.(created);
} catch (error: unknown) { } catch (error: unknown) {
const normalizedError = const normalizedError =
error instanceof Error ? error : new Error(t("pages.create.error.unknown")); error instanceof Error ? error : new Error(t("create.errors.unknown"));
if (options?.errorToasts !== false) { if (options?.errorToasts !== false) {
showErrorToast(t("pages.create.error.title"), normalizedError.message); showErrorToast(t("create.errors.title"), normalizedError.message);
} }
options?.onError?.(normalizedError, params); options?.onError?.(normalizedError, params);
@ -80,8 +80,8 @@ export const useCustomerCreateController = (options?: UseCustomerCreateControlle
} }
showWarningToast( showWarningToast(
t("forms.validation.title", "Revisa los campos"), t("common.feedback.validation.title"),
t("forms.validation.message", "Hay errores de validación en el formulario.") t("common.feedback.validation.message")
); );
} }
); );

View File

@ -28,22 +28,22 @@ export const CustomerAdditionalConfigFields = ({
<FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4"> <FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4">
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
<SelectField <SelectField
description={t("form_fields.language_code.description")} description={t("fields.language_code.description")}
items={[...LANGUAGE_OPTIONS]} items={[...LANGUAGE_OPTIONS]}
label={t("form_fields.language_code.label")} label={t("fields.language_code.label")}
name="languageCode" name="languageCode"
placeholder={t("form_fields.language_code.placeholder")} placeholder={t("fields.language_code.placeholder")}
required required
/> />
</Field> </Field>
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
<SelectField <SelectField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.currency_code.description")} description={t("fields.currency_code.description")}
items={[...CURRENCY_OPTIONS]} items={[...CURRENCY_OPTIONS]}
label={t("form_fields.currency_code.label")} label={t("fields.currency_code.label")}
name="currencyCode" name="currencyCode"
placeholder={t("form_fields.currency_code.placeholder")} placeholder={t("fields.currency_code.placeholder")}
required required
/> />
</Field> </Field>

View File

@ -25,48 +25,48 @@ export const CustomerAddressFields = ({ className, ...props }: CustomerAddressFi
<FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4"> <FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4">
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.street.description")} description={t("fields.street.description")}
label={t("form_fields.street.label")} label={t("fields.street.label")}
name="street" name="street"
placeholder={t("form_fields.street.placeholder")} placeholder={t("fields.street.placeholder")}
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.street2.description")} description={t("fields.street2.description")}
label={t("form_fields.street2.label")} label={t("fields.street2.label")}
name="street2" name="street2"
placeholder={t("form_fields.street2.placeholder")} placeholder={t("fields.street2.placeholder")}
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.city.description")} description={t("fields.city.description")}
label={t("form_fields.city.label")} label={t("fields.city.label")}
name="city" name="city"
placeholder={t("form_fields.city.placeholder")} placeholder={t("fields.city.placeholder")}
/> />
<TextField <TextField
description={t("form_fields.postal_code.description")} description={t("fields.postal_code.description")}
label={t("form_fields.postal_code.label")} label={t("fields.postal_code.label")}
name="postalCode" name="postalCode"
placeholder={t("form_fields.postal_code.placeholder")} placeholder={t("fields.postal_code.placeholder")}
/> />
<Field className="lg:col-span-2 lg:col-start-1"> <Field className="lg:col-span-2 lg:col-start-1">
<TextField <TextField
description={t("form_fields.province.description")} description={t("fields.province.description")}
label={t("form_fields.province.label")} label={t("fields.province.label")}
name="province" name="province"
placeholder={t("form_fields.province.placeholder")} placeholder={t("fields.province.placeholder")}
/> />
</Field> </Field>
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
<SelectField <SelectField
description={t("form_fields.country.description")} description={t("fields.country.description")}
items={[...COUNTRY_OPTIONS]} items={[...COUNTRY_OPTIONS]}
label={t("form_fields.country.label")} label={t("fields.country.label")}
name="country" name="country"
placeholder={t("form_fields.country.placeholder")} placeholder={t("fields.country.placeholder")}
required required
/> />
</Field> </Field>

View File

@ -37,10 +37,10 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
<FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4"> <FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4">
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.name.description")} description={t("fields.name.description")}
label={t("form_fields.name.label")} label={t("fields.name.label")}
name="name" name="name"
placeholder={t("form_fields.name.placeholder")} placeholder={t("fields.name.placeholder")}
required required
/> />
@ -53,7 +53,7 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
className="gap-1 lg:col-span-1 lg:col-start-1" className="gap-1 lg:col-span-1 lg:col-start-1"
data-invalid={fieldState.invalid} data-invalid={fieldState.invalid}
> >
<FormFieldLabel required>{t("form_fields.customer_type.label")}</FormFieldLabel> <FormFieldLabel required>{t("fields.customer_type.label")}</FormFieldLabel>
<RadioGroup <RadioGroup
className="gap-3" className="gap-3"
@ -76,7 +76,7 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
className="cursor-pointer text-sm font-medium leading-none" className="cursor-pointer text-sm font-medium leading-none"
htmlFor="customer-type-company" htmlFor="customer-type-company"
> >
{t("form_fields.customer_type.company")} {t("fields.customer_type.options.company")}
</FieldLabel> </FieldLabel>
</FieldContent> </FieldContent>
</Field> </Field>
@ -92,13 +92,13 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
className="cursor-pointer text-sm font-medium leading-none" className="cursor-pointer text-sm font-medium leading-none"
htmlFor="customer-type-individual" htmlFor="customer-type-individual"
> >
{t("form_fields.customer_type.individual")} {t("fields.customer_type.options.individual")}
</FieldLabel> </FieldLabel>
</FieldContent> </FieldContent>
</Field> </Field>
</RadioGroup> </RadioGroup>
<FieldDescription>{t("form_fields.customer_type.description")}</FieldDescription> <FieldDescription>{t("fields.customer_type.description")}</FieldDescription>
<FieldError errors={[fieldState.error]} /> <FieldError errors={[fieldState.error]} />
</Field> </Field>
); );
@ -107,27 +107,27 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
<TextField <TextField
className="lg:col-span-1" className="lg:col-span-1"
description={t("form_fields.tin.description")} description={t("fields.tin.description")}
label={t("form_fields.tin.label")} label={t("fields.tin.label")}
name="tin" name="tin"
placeholder={t("form_fields.tin.placeholder")} placeholder={t("fields.tin.placeholder")}
required required
/> />
<TextField <TextField
className="lg:col-span-full" className="lg:col-span-full"
description={t("form_fields.trade_name.description")} description={t("fields.trade_name.description")}
label={t("form_fields.trade_name.label")} label={t("fields.trade_name.label")}
name="tradeName" name="tradeName"
placeholder={t("form_fields.trade_name.placeholder")} placeholder={t("fields.trade_name.placeholder")}
/> />
<TextField <TextField
className="lg:col-span-2 lg:col-start-1" className="lg:col-span-2 lg:col-start-1"
description={t("form_fields.reference.description")} description={t("fields.reference.description")}
label={t("form_fields.reference.label")} label={t("fields.reference.label")}
name="reference" name="reference"
placeholder={t("form_fields.reference.placeholder")} placeholder={t("fields.reference.placeholder")}
/> />
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
@ -137,19 +137,19 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<Field className="gap-1" data-invalid={fieldState.invalid}> <Field className="gap-1" data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="defaultTaxes"> <FieldLabel htmlFor="defaultTaxes">
{t("form_fields.default_taxes.label")} {t("fields.default_taxes.label")}
</FieldLabel> </FieldLabel>
<CustomerTaxesMultiSelect <CustomerTaxesMultiSelect
description={t("form_fields.default_taxes.description")} description={t("fields.default_taxes.description")}
label={t("form_fields.default_taxes.label")} label={t("fields.default_taxes.label")}
onChange={field.onChange} onChange={field.onChange}
placeholder={t("form_fields.default_taxes.placeholder")} placeholder={t("fields.default_taxes.placeholder")}
required required
value={field.value} value={field.value}
/> />
<FieldDescription>{t("form_fields.default_taxes.description")}</FieldDescription> <FieldDescription>{t("fields.default_taxes.description")}</FieldDescription>
<FieldError errors={[fieldState.error]} /> <FieldError errors={[fieldState.error]} />
</Field> </Field>
)} )}
@ -158,10 +158,10 @@ export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicIn
<TextAreaField <TextAreaField
className="lg:col-span-full" className="lg:col-span-full"
description={t("form_fields.legal_record.description")} description={t("fields.legal_record.description")}
label={t("form_fields.legal_record.label")} label={t("fields.legal_record.label")}
name="legalRecord" name="legalRecord"
placeholder={t("form_fields.legal_record.placeholder")} placeholder={t("fields.legal_record.placeholder")}
/> />
</FieldGroup> </FieldGroup>
</FieldSet> </FieldSet>

View File

@ -30,21 +30,21 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
<FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4"> <FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4">
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.email_primary.description")} description={t("fields.email_primary.description")}
label={t("form_fields.email_primary.label")} label={t("fields.email_primary.label")}
leftIcon={ leftIcon={
<AtSignIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} /> <AtSignIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} />
} }
name="primaryEmail" name="primaryEmail"
placeholder={t("form_fields.email_primary.placeholder")} placeholder={t("fields.email_primary.placeholder")}
required required
typePreset="email" typePreset="email"
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.mobile_primary.description")} description={t("fields.mobile_primary.description")}
label={t("form_fields.mobile_primary.label")} label={t("fields.mobile_primary.label")}
leftIcon={ leftIcon={
<SmartphoneIcon <SmartphoneIcon
className="h-[18px] w-[18px] text-muted-foreground" className="h-[18px] w-[18px] text-muted-foreground"
@ -52,19 +52,19 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
/> />
} }
name="primaryMobile" name="primaryMobile"
placeholder={t("form_fields.mobile_primary.placeholder")} placeholder={t("fields.mobile_primary.placeholder")}
typePreset="phone" typePreset="phone"
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.phone_primary.description")} description={t("fields.phone_primary.description")}
label={t("form_fields.phone_primary.label")} label={t("fields.phone_primary.label")}
leftIcon={ leftIcon={
<PhoneIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} /> <PhoneIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} />
} }
name="primaryPhone" name="primaryPhone"
placeholder={t("form_fields.phone_primary.placeholder")} placeholder={t("fields.phone_primary.placeholder")}
typePreset="phone" typePreset="phone"
/> />
</FieldGroup> </FieldGroup>
@ -72,20 +72,20 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
<FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4"> <FieldGroup className="grid grid-cols-1 gap-x-6 lg:grid-cols-4">
<TextField <TextField
className="lg:col-span-2 lg:col-start-1" className="lg:col-span-2 lg:col-start-1"
description={t("form_fields.email_secondary.description")} description={t("fields.email_secondary.description")}
label={t("form_fields.email_secondary.label")} label={t("fields.email_secondary.label")}
leftIcon={ leftIcon={
<AtSignIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} /> <AtSignIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} />
} }
name="secondaryEmail" name="secondaryEmail"
placeholder={t("form_fields.email_secondary.placeholder")} placeholder={t("fields.email_secondary.placeholder")}
typePreset="email" typePreset="email"
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.mobile_secondary.description")} description={t("fields.mobile_secondary.description")}
label={t("form_fields.mobile_secondary.label")} label={t("fields.mobile_secondary.label")}
leftIcon={ leftIcon={
<SmartphoneIcon <SmartphoneIcon
className="h-[18px] w-[18px] text-muted-foreground" className="h-[18px] w-[18px] text-muted-foreground"
@ -93,18 +93,18 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
/> />
} }
name="secondaryMobile" name="secondaryMobile"
placeholder={t("form_fields.mobile_secondary.placeholder")} placeholder={t("fields.mobile_secondary.placeholder")}
typePreset="phone" typePreset="phone"
/> />
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.phone_secondary.description")} description={t("fields.phone_secondary.description")}
label={t("form_fields.phone_secondary.label")} label={t("fields.phone_secondary.label")}
leftIcon={ leftIcon={
<PhoneIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} /> <PhoneIcon className="h-[18px] w-[18px] text-muted-foreground" strokeWidth={1.5} />
} }
name="secondaryPhone" name="secondaryPhone"
placeholder={t("form_fields.phone_secondary.placeholder")} placeholder={t("fields.phone_secondary.placeholder")}
typePreset="phone" typePreset="phone"
/> />
</FieldGroup> </FieldGroup>
@ -116,7 +116,7 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
open={open} open={open}
> >
<CollapsibleTrigger className="inline-flex items-center gap-1 text-sm text-primary hover:underline"> <CollapsibleTrigger className="inline-flex items-center gap-1 text-sm text-primary hover:underline">
{t("common.more_details")}{" "} {t("common.actions.more")}{" "}
<ChevronDown className={`h-4 w-4 transition-transform ${open ? "rotate-180" : ""}`} /> <ChevronDown className={`h-4 w-4 transition-transform ${open ? "rotate-180" : ""}`} />
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent> <CollapsibleContent>
@ -124,8 +124,8 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.website.description")} description={t("fields.website.description")}
label={t("form_fields.website.label")} label={t("fields.website.label")}
leftIcon={ leftIcon={
<GlobeIcon <GlobeIcon
className="h-[18px] w-[18px] text-muted-foreground" className="h-[18px] w-[18px] text-muted-foreground"
@ -133,17 +133,17 @@ export const CustomerContactFields = ({ className, ...props }: CustomerContactFi
/> />
} }
name="website" name="website"
placeholder={t("form_fields.website.placeholder")} placeholder={t("fields.website.placeholder")}
typePreset="text" typePreset="text"
/> />
</Field> </Field>
<Field className="lg:col-span-2"> <Field className="lg:col-span-2">
<TextField <TextField
className="lg:col-span-2" className="lg:col-span-2"
description={t("form_fields.fax.description")} description={t("fields.fax.description")}
label={t("form_fields.fax.label")} label={t("fields.fax.label")}
name="fax" name="fax"
placeholder={t("form_fields.fax.placeholder")} placeholder={t("fields.fax.placeholder")}
typePreset="phone" typePreset="phone"
/> />
</Field> </Field>

View File

@ -55,7 +55,7 @@ export const CustomerTaxesMultiSelect = ({
maxCount={3} maxCount={3}
onValueChange={onChange} onValueChange={onChange}
options={catalogLookup} options={catalogLookup}
placeholder={t("components.customer_invoice_taxes_multi_select.placeholder")} placeholder={t("components.taxes_multi_select.placeholder")}
value={value ?? []} value={value ?? []}
variant="secondary" variant="secondary"
{...otherProps} {...otherProps}

View File

@ -21,7 +21,7 @@ export const CustomerCreatePage = () => {
<UnsavedChangesProvider isDirty={form.formState.isDirty}> <UnsavedChangesProvider isDirty={form.formState.isDirty}>
<AppHeader className="mx-auto max-w-7xl space-y-4"> <AppHeader className="mx-auto max-w-7xl space-y-4">
<PageHeader <PageHeader
description={t("pages.create.description")} description={t("create.description")}
onBackClick={() => navigate("/customers/list")} onBackClick={() => navigate("/customers/list")}
rightSlot={ rightSlot={
/* /*
@ -38,15 +38,15 @@ export const CustomerCreatePage = () => {
/> />
*/ <></> */ <></>
} }
title={t("pages.create.title")} title={t("create.title")}
/> />
</AppHeader> </AppHeader>
<AppContent className="mx-auto max-w-7xl space-y-4"> <AppContent className="mx-auto max-w-7xl space-y-4">
{isCreateError && ( {isCreateError && (
<ErrorAlert <ErrorAlert
message={(createError as Error)?.message ?? t("pages.create.errorMsg")} message={(createError as Error)?.message ?? t("create.errors.message")}
title={t("pages.create.errorTitle")} title={t("create.errors.title")}
/> />
)} )}

View File

@ -51,8 +51,8 @@ export const useQuickCreateCustomerDialogController = (
options.onCreated(option); options.onCreated(option);
showSuccessToast( showSuccessToast(
t("customers.quick_create.success.title", "Cliente creado"), t("toasts.quick_create_customer.success.title"),
t("customers.quick_create.success.message", "Se ha creado correctamente") t("toasts.quick_create_customer.success.message")
); );
form.reset( form.reset(
@ -67,7 +67,7 @@ export const useQuickCreateCustomerDialogController = (
const err = error instanceof Error ? error : new Error("Unknown error"); const err = error instanceof Error ? error : new Error("Unknown error");
showErrorToast( showErrorToast(
t("customers.quick_create.error.title", "Error al crear cliente"), t("toasts.quick_create_customer.error.title"),
err.message err.message
); );
} }

View File

@ -29,7 +29,7 @@ export const QuickCreateCustomerDialog = ({ ctrl }: QuickCreateCustomerDialogPro
<Dialog onOpenChange={(open) => !open && ctrl.closeDialog()} open={ctrl.isOpen}> <Dialog onOpenChange={(open) => !open && ctrl.closeDialog()} open={ctrl.isOpen}>
<DialogContent className="sm:max-w-2xl"> <DialogContent className="sm:max-w-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>{t("customers.quick_create.title", "Nuevo cliente")}</DialogTitle> <DialogTitle>{t("dialogs.quick_create_customer.title")}</DialogTitle>
</DialogHeader> </DialogHeader>
<FormProvider {...ctrl.form}> <FormProvider {...ctrl.form}>
@ -43,13 +43,13 @@ export const QuickCreateCustomerDialog = ({ ctrl }: QuickCreateCustomerDialogPro
type="button" type="button"
variant="outline" variant="outline"
> >
{t("common.cancel", "Cancelar")} {t("common.actions.cancel")}
</Button> </Button>
<Button disabled={ctrl.isSubmitting} type="submit"> <Button disabled={ctrl.isSubmitting} type="submit">
{ctrl.isSubmitting {ctrl.isSubmitting
? t("common.saving", "Guardando...") ? t("common.actions.saving")
: t("customers.quick_create.submit", "Crear cliente")} : t("dialogs.quick_create_customer.actions.submit")}
</Button> </Button>
</div> </div>
</form> </form>

View File

@ -39,7 +39,7 @@ export function useCustomersGridColumns(
<DataTableColumnHeader <DataTableColumnHeader
className="text-left" className="text-left"
column={column} column={column}
title={t("pages.list.grid_columns.customer")} title={t("list.columns.customer")}
/> />
), ),
accessorFn: (row) => row.name, accessorFn: (row) => row.name,
@ -74,12 +74,12 @@ export function useCustomersGridColumns(
{customer.isCompany ? ( {customer.isCompany ? (
<> <>
<Building2Icon className="size-3" /> <Building2Icon className="size-3" />
{t("form_fields.customer_type.company")} {t("fields.customer_type.options.company")}
</> </>
) : ( ) : (
<> <>
<UserIcon className="size-3" /> <UserIcon className="size-3" />
{t("form_fields.customer_type.individual")} {t("fields.customer_type.options.individual")}
</> </>
)} )}
</Badge> </Badge>
@ -96,7 +96,7 @@ export function useCustomersGridColumns(
<DataTableColumnHeader <DataTableColumnHeader
className="text-left" className="text-left"
column={column} column={column}
title={t("pages.list.grid_columns.contact")} title={t("list.columns.contact")}
/> />
), ),
accessorFn: (row) => accessorFn: (row) =>
@ -112,7 +112,7 @@ export function useCustomersGridColumns(
<DataTableColumnHeader <DataTableColumnHeader
className="text-left" className="text-left"
column={column} column={column}
title={t("pages.list.grid_columns.address")} title={t("list.columns.address")}
/> />
), ),
accessorFn: (row) => accessorFn: (row) =>
@ -128,7 +128,7 @@ export function useCustomersGridColumns(
<DataTableColumnHeader <DataTableColumnHeader
className="text-right" className="text-right"
column={column} column={column}
title={t("pages.list.grid_columns.actions")} title={t("list.columns.actions")}
/> />
), ),
size: 64, size: 64,
@ -143,7 +143,7 @@ export function useCustomersGridColumns(
<div className="flex justify-end"> <div className="flex justify-end">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Button <Button
aria-label={t("pages.list.actions.view")} aria-label={t("list.actions.view")}
onClick={() => onViewClick?.(customer)} onClick={() => onViewClick?.(customer)}
size="icon" size="icon"
variant="ghost" variant="ghost"
@ -154,7 +154,7 @@ export function useCustomersGridColumns(
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger <DropdownMenuTrigger
render={ render={
<Button aria-label={t("pages.list.actions.more")} size="icon" variant="ghost"> <Button aria-label={t("list.actions.more")} size="icon" variant="ghost">
<MoreHorizontalIcon className="size-4" /> <MoreHorizontalIcon className="size-4" />
</Button> </Button>
} }
@ -162,16 +162,16 @@ export function useCustomersGridColumns(
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuGroup> <DropdownMenuGroup>
<DropdownMenuLabel>{t("pages.list.grid_columns.actions")}</DropdownMenuLabel> <DropdownMenuLabel>{t("list.columns.actions")}</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => onViewClick?.(customer)}> <DropdownMenuItem onClick={() => onViewClick?.(customer)}>
{t("pages.list.actions.view")} {t("list.actions.view")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => onEditClick?.(customer)}> <DropdownMenuItem onClick={() => onEditClick?.(customer)}>
{t("pages.list.actions.edit")} {t("list.actions.edit")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
@ -182,7 +182,7 @@ export function useCustomersGridColumns(
disabled={!primaryEmail} disabled={!primaryEmail}
onClick={() => navigator.clipboard.writeText(primaryEmail)} onClick={() => navigator.clipboard.writeText(primaryEmail)}
> >
{t("pages.list.actions.copy_email")} {t("list.actions.copy_email")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
</> </>
@ -192,7 +192,7 @@ export function useCustomersGridColumns(
className="text-destructive" className="text-destructive"
onClick={() => onDeleteClick?.(customer)} onClick={() => onDeleteClick?.(customer)}
> >
{t("pages.list.actions.delete")} {t("list.actions.delete")}
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuGroup> </DropdownMenuGroup>
</DropdownMenuContent> </DropdownMenuContent>

View File

@ -86,8 +86,8 @@ export const ListCustomersPage = () => {
return ( return (
<AppContent> <AppContent>
<ErrorAlert <ErrorAlert
message={(listCtrl.error as Error)?.message || t("pages.list.loadErrorMessage")} message={(listCtrl.error as Error)?.message || t("list.errors.load_message")}
title={t("pages.list.loadErrorTitle")} title={t("list.errors.load_title")}
/> />
<BackHistoryButton /> <BackHistoryButton />
</AppContent> </AppContent>
@ -98,18 +98,18 @@ export const ListCustomersPage = () => {
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
{/* Header */} {/* Header */}
<PageHeader <PageHeader
description={t("pages.proformas.list.description")} description={t("list.description")}
rightSlot={ rightSlot={
<Button <Button
aria-label={t("pages.proformas.create.title")} aria-label={t("common.actions.create")}
onClick={() => navigate("/proformas/create")} onClick={() => navigate("/customers/create")}
size={"default"} size={"default"}
> >
<PlusIcon aria-hidden className="mr-2 size-4" /> <PlusIcon aria-hidden className="mr-2 size-4" />
{t("pages.proformas.create.title")} {t("common.actions.create")}
</Button> </Button>
} }
title={t("pages.proformas.list.title")} title={t("list.title")}
/> />
{/* Table */} {/* Table */}

View File

@ -105,14 +105,14 @@ export const useCustomerUpdateController = (
const submitHandler = form.handleSubmit( const submitHandler = form.handleSubmit(
async (formData: CustomerUpdateForm) => { async (formData: CustomerUpdateForm) => {
if (!customerId) { if (!customerId) {
showErrorToast(t("pages.update.error.title"), "Falta el ID del cliente"); showErrorToast(t("update.errors.title"), t("update.errors.missing_id"));
return; return;
} }
if (!formHasAnyDirty(form.formState.dirtyFields)) { if (!formHasAnyDirty(form.formState.dirtyFields)) {
showWarningToast( showWarningToast(
t("customers.update.no_changes.title"), t("update.feedback.no_changes.title"),
t("customers.update.no_changes.message") t("update.feedback.no_changes.message")
); );
return; return;
} }
@ -123,8 +123,8 @@ export const useCustomerUpdateController = (
if (Object.keys(patchData).length === 0) { if (Object.keys(patchData).length === 0) {
showWarningToast( showWarningToast(
t("pages.update.no_changes.title", "Sin cambios"), t("update.feedback.no_changes.title"),
t("pages.update.no_changes.message", "No has realizado ningún cambio.") t("update.feedback.no_changes.message")
); );
return; return;
} }
@ -147,8 +147,8 @@ export const useCustomerUpdateController = (
if (options?.successToasts !== false) { if (options?.successToasts !== false) {
showSuccessToast( showSuccessToast(
t("pages.update.success.title", "Cliente modificado"), t("update.feedback.success.title"),
t("pages.update.success.message", "Se ha modificado correctamente.") t("update.feedback.success.message")
); );
} }
@ -172,11 +172,8 @@ export const useCustomerUpdateController = (
if (options?.errorToasts !== false) { if (options?.errorToasts !== false) {
showWarningToast( showWarningToast(
t("pages.update.validation_error.title", "Revisa los campos"), t("update.feedback.validation.title"),
t( t("update.feedback.validation.message")
"pages.update.validation_error.message",
"Hay errores de validación. Corrige los campos indicados."
)
); );
} }
@ -186,9 +183,8 @@ export const useCustomerUpdateController = (
if (options?.errorToasts !== false) { if (options?.errorToasts !== false) {
showErrorToast( showErrorToast(
t("pages.update.error.title", "No se pudo modificar el cliente"), t("update.errors.title"),
normalizedError.message || normalizedError.message || t("update.errors.unexpected")
t("common.errors.unexpected", "Ha ocurrido un error inesperado.")
); );
} }
@ -199,8 +195,8 @@ export const useCustomerUpdateController = (
focusFirstInputFormError(form); focusFirstInputFormError(form);
showWarningToast( showWarningToast(
t("forms.validation.title", "Revisa los campos"), t("common.feedback.validation.title"),
t("forms.validation.message", "Hay errores de validación en el formulario.") t("common.feedback.validation.message")
); );
} }
); );

View File

@ -16,7 +16,7 @@ export const CustomerEditorSkeleton = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<BackHistoryButton /> <BackHistoryButton />
<Button aria-busy disabled> <Button aria-busy disabled>
{t("pages.update.submit")} {t("update.actions.submit")}
</Button> </Button>
</div> </div>
</div> </div>
@ -25,7 +25,7 @@ export const CustomerEditorSkeleton = () => {
<div className="h-10 w-full rounded-md bg-muted animate-pulse" /> <div className="h-10 w-full rounded-md bg-muted animate-pulse" />
<div className="h-28 w-full rounded-md bg-muted animate-pulse" /> <div className="h-28 w-full rounded-md bg-muted animate-pulse" />
</div> </div>
<span className="sr-only">{t("pages.update.loading", "Cargando cliente...")}</span> <span className="sr-only">{t("update.loading")}</span>
</AppContent> </AppContent>
</> </>
); );

View File

@ -22,23 +22,23 @@ export const CustomerAdditionalConfigEditor = ({
<FormSectionGrid> <FormSectionGrid>
<SelectField <SelectField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.language_code.description")} description={t("fields.language_code.description")}
disabled={disabled} disabled={disabled}
items={[...LANGUAGE_OPTIONS]} items={[...LANGUAGE_OPTIONS]}
label={t("form_fields.language_code.label")} label={t("fields.language_code.label")}
name="languageCode" name="languageCode"
placeholder={t("form_fields.language_code.placeholder")} placeholder={t("fields.language_code.placeholder")}
readOnly={readOnly} readOnly={readOnly}
required required
/> />
<SelectField <SelectField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.currency_code.description")} description={t("fields.currency_code.description")}
disabled={disabled} disabled={disabled}
items={[...CURRENCY_OPTIONS]} items={[...CURRENCY_OPTIONS]}
label={t("form_fields.currency_code.label")} label={t("fields.currency_code.label")}
name="currencyCode" name="currencyCode"
placeholder={t("form_fields.currency_code.placeholder")} placeholder={t("fields.currency_code.placeholder")}
readOnly={readOnly} readOnly={readOnly}
required required
/> />

View File

@ -16,67 +16,67 @@ export const CustomerAddressEditor = ({
return ( return (
<FormSectionCard <FormSectionCard
description={t("form_groups.address.title")} description={t("form_groups.address.description")}
title={t("form_groups.address.description")} title={t("form_groups.address.title")}
> >
<FormSectionGrid> <FormSectionGrid>
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.street.description")} description={t("fields.street.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.street.label")} label={t("fields.street.label")}
name="street" name="street"
placeholder={t("form_fields.street.placeholder")} placeholder={t("fields.street.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.street2.description")} description={t("fields.street2.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.street2.label")} label={t("fields.street2.label")}
name="street2" name="street2"
placeholder={t("form_fields.street2.placeholder")} placeholder={t("fields.street2.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.city.description")} description={t("fields.city.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.city.label")} label={t("fields.city.label")}
name="city" name="city"
placeholder={t("form_fields.city.placeholder")} placeholder={t("fields.city.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.province.description")} description={t("fields.province.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.province.label")} label={t("fields.province.label")}
name="province" name="province"
placeholder={t("form_fields.province.placeholder")} placeholder={t("fields.province.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<TextField <TextField
className="md:col-span-2 md:col-start-1" className="md:col-span-2 md:col-start-1"
description={t("form_fields.postal_code.description")} description={t("fields.postal_code.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.postal_code.label")} label={t("fields.postal_code.label")}
name="postalCode" name="postalCode"
placeholder={t("form_fields.postal_code.placeholder")} placeholder={t("fields.postal_code.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<SelectField <SelectField
className="md:col-span-2" className="md:col-span-2"
description={t("form_fields.country.description")} description={t("fields.country.description")}
disabled={disabled} disabled={disabled}
items={[...COUNTRY_OPTIONS]} items={[...COUNTRY_OPTIONS]}
label={t("form_fields.country.label")} label={t("fields.country.label")}
name="country" name="country"
placeholder={t("form_fields.country.placeholder")} placeholder={t("fields.country.placeholder")}
readOnly={readOnly} readOnly={readOnly}
required required
/> />

View File

@ -40,11 +40,11 @@ export const CustomerBasicInfoEditor = ({
<FormSectionGrid> <FormSectionGrid>
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.name.description")} description={t("fields.name.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.name.label")} label={t("fields.name.label")}
name="name" name="name"
placeholder={t("form_fields.name.placeholder")} placeholder={t("fields.name.placeholder")}
readOnly={readOnly} readOnly={readOnly}
required required
/> />
@ -55,7 +55,7 @@ export const CustomerBasicInfoEditor = ({
render={({ field, fieldState }) => { render={({ field, fieldState }) => {
return ( return (
<Field className="md:col-span-3" data-invalid={fieldState.invalid}> <Field className="md:col-span-3" data-invalid={fieldState.invalid}>
<FormFieldLabel required>{t("form_fields.customer_type.label")}</FormFieldLabel> <FormFieldLabel required>{t("fields.customer_type.label")}</FormFieldLabel>
<RadioGroup <RadioGroup
className="gap-3" className="gap-3"
@ -78,7 +78,7 @@ export const CustomerBasicInfoEditor = ({
className="cursor-pointer text-sm font-medium leading-none" className="cursor-pointer text-sm font-medium leading-none"
htmlFor="customer-type-company" htmlFor="customer-type-company"
> >
{t("form_fields.customer_type.company")} {t("fields.customer_type.options.company")}
</FieldLabel> </FieldLabel>
</FieldContent> </FieldContent>
</Field> </Field>
@ -94,13 +94,13 @@ export const CustomerBasicInfoEditor = ({
className="cursor-pointer text-sm font-medium leading-none" className="cursor-pointer text-sm font-medium leading-none"
htmlFor="customer-type-individual" htmlFor="customer-type-individual"
> >
{t("form_fields.customer_type.individual")} {t("fields.customer_type.options.individual")}
</FieldLabel> </FieldLabel>
</FieldContent> </FieldContent>
</Field> </Field>
</RadioGroup> </RadioGroup>
<FieldDescription>{t("form_fields.customer_type.description")}</FieldDescription> <FieldDescription>{t("fields.customer_type.description")}</FieldDescription>
<FieldError errors={[fieldState.error]} /> <FieldError errors={[fieldState.error]} />
</Field> </Field>
); );
@ -109,32 +109,32 @@ export const CustomerBasicInfoEditor = ({
<TextField <TextField
className="md:col-start-1 md:col-span-4" className="md:col-start-1 md:col-span-4"
description={t("form_fields.tin.description")} description={t("fields.tin.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.tin.label")} label={t("fields.tin.label")}
name="tin" name="tin"
placeholder={t("form_fields.tin.placeholder")} placeholder={t("fields.tin.placeholder")}
readOnly={readOnly} readOnly={readOnly}
required required
/> />
<TextField <TextField
className="md:col-span-full" className="md:col-span-full"
description={t("form_fields.trade_name.description")} description={t("fields.trade_name.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.trade_name.label")} label={t("fields.trade_name.label")}
name="tradeName" name="tradeName"
placeholder={t("form_fields.trade_name.placeholder")} placeholder={t("fields.trade_name.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
<TextField <TextField
className="md:col-span-6 md:col-start-1" className="md:col-span-6 md:col-start-1"
description={t("form_fields.reference.description")} description={t("fields.reference.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.reference.label")} label={t("fields.reference.label")}
name="reference" name="reference"
placeholder={t("form_fields.reference.placeholder")} placeholder={t("fields.reference.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
@ -145,19 +145,19 @@ export const CustomerBasicInfoEditor = ({
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<Field className="gap-1" data-invalid={fieldState.invalid}> <Field className="gap-1" data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="defaultTaxes"> <FieldLabel htmlFor="defaultTaxes">
{t("form_fields.default_taxes.label")} {t("fields.default_taxes.label")}
</FieldLabel> </FieldLabel>
<CustomerTaxesMultiSelect <CustomerTaxesMultiSelect
description={t("form_fields.default_taxes.description")} description={t("fields.default_taxes.description")}
label={t("form_fields.default_taxes.label")} label={t("fields.default_taxes.label")}
onChange={field.onChange} onChange={field.onChange}
placeholder={t("form_fields.default_taxes.placeholder")} placeholder={t("fields.default_taxes.placeholder")}
required required
value={field.value} value={field.value}
/> />
<FieldDescription>{t("form_fields.default_taxes.description")}</FieldDescription> <FieldDescription>{t("fields.default_taxes.description")}</FieldDescription>
<FieldError errors={[fieldState.error]} /> <FieldError errors={[fieldState.error]} />
</Field> </Field>
)} )}
@ -166,11 +166,11 @@ export const CustomerBasicInfoEditor = ({
<TextAreaField <TextAreaField
className="md:col-span-full" className="md:col-span-full"
description={t("form_fields.legal_record.description")} description={t("fields.legal_record.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.legal_record.label")} label={t("fields.legal_record.label")}
name="legalRecord" name="legalRecord"
placeholder={t("form_fields.legal_record.placeholder")} placeholder={t("fields.legal_record.placeholder")}
readOnly={readOnly} readOnly={readOnly}
/> />
</FormSectionGrid> </FormSectionGrid>

View File

@ -24,42 +24,42 @@ export const CustomerContactEditor = ({
return ( return (
<FormSectionCard <FormSectionCard
description={"form_groups.contact_info.title"} description={t("form_groups.contact_info.description")}
title={t("form_groups.contact_info.description")} title={t("form_groups.contact_info.title")}
> >
<FormSectionGrid> <FormSectionGrid>
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.email_primary.description")} description={t("fields.email_primary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.email_primary.label")} label={t("fields.email_primary.label")}
leftIcon={<AtSignIcon className="size-4" strokeWidth={1.5} />} leftIcon={<AtSignIcon className="size-4" strokeWidth={1.5} />}
name="primaryEmail" name="primaryEmail"
placeholder={t("form_fields.email_primary.placeholder")} placeholder={t("fields.email_primary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="email" typePreset="email"
/> />
<TextField <TextField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.mobile_primary.description")} description={t("fields.mobile_primary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.mobile_primary.label")} label={t("fields.mobile_primary.label")}
leftIcon={<SmartphoneIcon className="size-4" strokeWidth={1.5} />} leftIcon={<SmartphoneIcon className="size-4" strokeWidth={1.5} />}
name="primaryMobile" name="primaryMobile"
placeholder={t("form_fields.mobile_primary.placeholder")} placeholder={t("fields.mobile_primary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="phone" typePreset="phone"
/> />
<TextField <TextField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.phone_primary.description")} description={t("fields.phone_primary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.phone_primary.label")} label={t("fields.phone_primary.label")}
leftIcon={<PhoneIcon className="size-4" strokeWidth={1.5} />} leftIcon={<PhoneIcon className="size-4" strokeWidth={1.5} />}
name="primaryPhone" name="primaryPhone"
placeholder={t("form_fields.phone_primary.placeholder")} placeholder={t("fields.phone_primary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="phone" typePreset="phone"
/> />
@ -68,35 +68,35 @@ export const CustomerContactEditor = ({
<TextField <TextField
className="md:col-span-6 md:col-start-1" className="md:col-span-6 md:col-start-1"
description={t("form_fields.email_secondary.description")} description={t("fields.email_secondary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.email_secondary.label")} label={t("fields.email_secondary.label")}
leftIcon={<AtSignIcon className="size-4" strokeWidth={1.5} />} leftIcon={<AtSignIcon className="size-4" strokeWidth={1.5} />}
name="secondaryEmail" name="secondaryEmail"
placeholder={t("form_fields.email_secondary.placeholder")} placeholder={t("fields.email_secondary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="email" typePreset="email"
/> />
<TextField <TextField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.mobile_secondary.description")} description={t("fields.mobile_secondary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.mobile_secondary.label")} label={t("fields.mobile_secondary.label")}
leftIcon={<SmartphoneIcon className="size-4" strokeWidth={1.5} />} leftIcon={<SmartphoneIcon className="size-4" strokeWidth={1.5} />}
name="secondaryMobile" name="secondaryMobile"
placeholder={t("form_fields.mobile_secondary.placeholder")} placeholder={t("fields.mobile_secondary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="phone" typePreset="phone"
/> />
<TextField <TextField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.phone_secondary.description")} description={t("fields.phone_secondary.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.phone_secondary.label")} label={t("fields.phone_secondary.label")}
leftIcon={<PhoneIcon className="size-4" strokeWidth={1.5} />} leftIcon={<PhoneIcon className="size-4" strokeWidth={1.5} />}
name="secondaryPhone" name="secondaryPhone"
placeholder={t("form_fields.phone_secondary.placeholder")} placeholder={t("fields.phone_secondary.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="phone" typePreset="phone"
/> />
@ -109,29 +109,29 @@ export const CustomerContactEditor = ({
open={open} open={open}
> >
<CollapsibleTrigger className="inline-flex items-center gap-1 text-sm text-primary hover:underline"> <CollapsibleTrigger className="inline-flex items-center gap-1 text-sm text-primary hover:underline">
{t("common.more_details")}{" "} {t("common.actions.more")}{" "}
<ChevronDown className={`h-4 w-4 transition-transform ${open ? "rotate-180" : ""}`} /> <ChevronDown className={`h-4 w-4 transition-transform ${open ? "rotate-180" : ""}`} />
</CollapsibleTrigger> </CollapsibleTrigger>
<CollapsibleContent> <CollapsibleContent>
<FormSectionGrid> <FormSectionGrid>
<TextField <TextField
className="md:col-span-6" className="md:col-span-6"
description={t("form_fields.website.description")} description={t("fields.website.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.website.label")} label={t("fields.website.label")}
leftIcon={<GlobeIcon className="size-4" strokeWidth={1.5} />} leftIcon={<GlobeIcon className="size-4" strokeWidth={1.5} />}
name="website" name="website"
placeholder={t("form_fields.website.placeholder")} placeholder={t("fields.website.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="text" typePreset="text"
/> />
<TextField <TextField
className="md:col-span-3" className="md:col-span-3"
description={t("form_fields.fax.description")} description={t("fields.fax.description")}
disabled={disabled} disabled={disabled}
label={t("form_fields.fax.label")} label={t("fields.fax.label")}
name="fax" name="fax"
placeholder={t("form_fields.fax.placeholder")} placeholder={t("fields.fax.placeholder")}
readOnly={readOnly} readOnly={readOnly}
typePreset="phone" typePreset="phone"
/> />

View File

@ -55,7 +55,7 @@ export const CustomerTaxesMultiSelect = ({
maxCount={3} maxCount={3}
onValueChange={onChange} onValueChange={onChange}
options={catalogLookup} options={catalogLookup}
placeholder={t("components.customer_invoice_taxes_multi_select.placeholder")} placeholder={t("components.taxes_multi_select.placeholder")}
value={value ?? []} value={value ?? []}
variant="secondary" variant="secondary"
{...otherProps} {...otherProps}

View File

@ -26,9 +26,9 @@ export const CustomerUpdatePage = () => {
message={ message={
updateCtrl.loadError instanceof Error updateCtrl.loadError instanceof Error
? updateCtrl.loadError.message ? updateCtrl.loadError.message
: t("pages.update.loadErrorMsg", "Inténtalo de nuevo más tarde.") : t("update.errors.load_message")
} }
title={t("pages.update.loadErrorTitle", "No se pudo cargar el cliente")} title={t("update.errors.load_title")}
/> />
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
@ -42,8 +42,8 @@ export const CustomerUpdatePage = () => {
return ( return (
<AppContent> <AppContent>
<NotFoundCard <NotFoundCard
message={t("pages.update.notFoundMsg", "Revisa el identificador o vuelve al listado.")} message={t("update.errors.not_found_message")}
title={t("pages.update.notFoundTitle", "Cliente no encontrado")} title={t("update.errors.not_found_title")}
/> />
</AppContent> </AppContent>
); );
@ -53,7 +53,7 @@ export const CustomerUpdatePage = () => {
<UnsavedChangesProvider isDirty={updateCtrl.form.formState.isDirty}> <UnsavedChangesProvider isDirty={updateCtrl.form.formState.isDirty}>
<AppHeader className="space-y-4 max-w-5xl mx-auto"> <AppHeader className="space-y-4 max-w-5xl mx-auto">
<PageHeader <PageHeader
description={t("pages.update.description")} description={t("update.description")}
onBackClick={() => navigate("/customers/list")} onBackClick={() => navigate("/customers/list")}
rightSlot={ rightSlot={
/* /*
@ -70,7 +70,7 @@ export const CustomerUpdatePage = () => {
/> />
*/ <></> */ <></>
} }
title={t("pages.update.title")} title={t("update.title")}
/> />
</AppHeader> </AppHeader>
<AppContent className="space-y-4 max-w-5xl mx-auto"> <AppContent className="space-y-4 max-w-5xl mx-auto">
@ -79,9 +79,9 @@ export const CustomerUpdatePage = () => {
<ErrorAlert <ErrorAlert
message={ message={
(updateCtrl.updateError as Error)?.message ?? (updateCtrl.updateError as Error)?.message ??
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.") t("update.errors.message")
} }
title={t("pages.update.errorTitle", "No se pudo guardar los cambios")} title={t("update.errors.title")}
/> />
)} )}

View File

@ -54,9 +54,9 @@ export const CustomerViewPage = () => {
<ErrorAlert <ErrorAlert
message={ message={
(error as Error)?.message ?? (error as Error)?.message ??
t("pages.update.loadErrorMsg", "Inténtalo de nuevo más tarde.") t("view.errors.load_message")
} }
title={t("pages.update.loadErrorTitle", "No se pudo cargar el cliente")} title={t("view.errors.load_title")}
/> />
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
@ -77,7 +77,11 @@ export const CustomerViewPage = () => {
<Badge className="font-mono" variant="secondary"> <Badge className="font-mono" variant="secondary">
{customerData?.tin} {customerData?.tin}
</Badge> </Badge>
<Badge variant="outline">{customerData?.isCompany ? "Empresa" : "Persona"}</Badge> <Badge variant="outline">
{customerData?.isCompany
? t("fields.customer_type.options.company")
: t("fields.customer_type.options.individual")}
</Badge>
</div> </div>
} }
rightSlot={ rightSlot={
@ -95,7 +99,7 @@ export const CustomerViewPage = () => {
onClick={() => navigate(`/customers/${customerId}/edit`)} onClick={() => navigate(`/customers/${customerId}/edit`)}
> >
<EditIcon className="mr-2 h-4 w-4" /> <EditIcon className="mr-2 h-4 w-4" />
Editar {t("view.actions.edit")}
</Button> </Button>
</div> </div>
} }
@ -117,26 +121,34 @@ export const CustomerViewPage = () => {
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-lg"> <CardTitle className="flex items-center gap-2 text-lg">
<FileText className="size-5 text-primary" /> <FileText className="size-5 text-primary" />
Información Básica {t("view.sections.basic_info")}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Nombre</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.name")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.name}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.name}</dd>
</div> </div>
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Referencia</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.reference")}
</dt>
<dd className="mt-1 font-mono text-base text-foreground"> <dd className="mt-1 font-mono text-base text-foreground">
{customerData?.reference} {customerData?.reference}
</dd> </dd>
</div> </div>
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Registro Legal</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.legal_record")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.legalRecord}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.legalRecord}</dd>
</div> </div>
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Impuestos por Defecto</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.default_taxes")}
</dt>
<dd className="mt-1"> <dd className="mt-1">
{customerData?.defaultTaxes.map((tax: string) => ( {customerData?.defaultTaxes.map((tax: string) => (
<Badge key={tax} variant={"secondary"}> <Badge key={tax} variant={"secondary"}>
@ -153,12 +165,14 @@ export const CustomerViewPage = () => {
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-lg"> <CardTitle className="flex items-center gap-2 text-lg">
<MapPin className="size-5 text-primary" /> <MapPin className="size-5 text-primary" />
Dirección {t("view.sections.address")}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Calle</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.street")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.street} {customerData?.street}
{customerData?.street2 && ( {customerData?.street2 && (
@ -171,21 +185,29 @@ export const CustomerViewPage = () => {
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Ciudad</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.city")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.city}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.city}</dd>
</div> </div>
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Código Postal</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.postal_code")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.postalCode}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.postalCode}</dd>
</div> </div>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">Provincia</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.province")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.province}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.province}</dd>
</div> </div>
<div> <div>
<dt className="text-sm font-medium text-muted-foreground">País</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.country")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.country}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.country}</dd>
</div> </div>
</div> </div>
@ -197,19 +219,23 @@ export const CustomerViewPage = () => {
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-lg"> <CardTitle className="flex items-center gap-2 text-lg">
<Mail className="size-5 text-primary" /> <Mail className="size-5 text-primary" />
Información de Contacto {t("view.sections.contact_info")}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 md:grid-cols-2">
{/* Contacto Principal */} {/* Contacto Principal */}
<div className="space-y-4"> <div className="space-y-4">
<h3 className="font-semibold text-foreground">Contacto Principal</h3> <h3 className="font-semibold text-foreground">
{t("view.sections.primary_contact")}
</h3>
{customerData?.primaryEmail && ( {customerData?.primaryEmail && (
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Mail className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Mail className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Email</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.email")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.primaryEmail} {customerData?.primaryEmail}
</dd> </dd>
@ -220,7 +246,9 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Smartphone className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Smartphone className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Móvil</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.mobile")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.primaryMobile} {customerData?.primaryMobile}
</dd> </dd>
@ -231,7 +259,9 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Phone className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Phone className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Teléfono</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.phone")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.primaryPhone} {customerData?.primaryPhone}
</dd> </dd>
@ -242,12 +272,16 @@ export const CustomerViewPage = () => {
{/* Contacto Secundario */} {/* Contacto Secundario */}
<div className="space-y-4"> <div className="space-y-4">
<h3 className="font-semibold text-foreground">Contacto Secundario</h3> <h3 className="font-semibold text-foreground">
{t("view.sections.secondary_contact")}
</h3>
{customerData?.secondaryEmail && ( {customerData?.secondaryEmail && (
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Mail className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Mail className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Email</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.email")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.secondaryEmail} {customerData?.secondaryEmail}
</dd> </dd>
@ -258,7 +292,9 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Smartphone className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Smartphone className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Móvil</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.mobile")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.secondaryMobile} {customerData?.secondaryMobile}
</dd> </dd>
@ -269,7 +305,9 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Phone className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Phone className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Teléfono</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.phone")}
</dt>
<dd className="mt-1 text-base text-foreground"> <dd className="mt-1 text-base text-foreground">
{customerData?.secondaryPhone} {customerData?.secondaryPhone}
</dd> </dd>
@ -281,13 +319,17 @@ export const CustomerViewPage = () => {
{/* Otros Contactos */} {/* Otros Contactos */}
{(customerData?.website || customerData?.fax) && ( {(customerData?.website || customerData?.fax) && (
<div className="space-y-4 md:col-span-2"> <div className="space-y-4 md:col-span-2">
<h3 className="font-semibold text-foreground">Otros</h3> <h3 className="font-semibold text-foreground">
{t("view.sections.other_contacts")}
</h3>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
{customerData?.website && ( {customerData?.website && (
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Globe className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Globe className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Sitio Web</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.website")}
</dt>
<dd className="mt-1 text-base text-primary hover:underline"> <dd className="mt-1 text-base text-primary hover:underline">
<a <a
href={customerData?.website} href={customerData?.website}
@ -304,7 +346,9 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Phone className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Phone className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Fax</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.fax")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.fax}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.fax}</dd>
</div> </div>
</div> </div>
@ -321,7 +365,7 @@ export const CustomerViewPage = () => {
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-lg"> <CardTitle className="flex items-center gap-2 text-lg">
<Languages className="size-5 text-primary" /> <Languages className="size-5 text-primary" />
Preferencias {t("view.sections.preferences")}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@ -329,14 +373,18 @@ export const CustomerViewPage = () => {
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Languages className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Languages className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Idioma Preferido</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.language_code")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.languageCode}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.languageCode}</dd>
</div> </div>
</div> </div>
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Banknote className="mt-0.5 h-4 w-4 text-muted-foreground" /> <Banknote className="mt-0.5 h-4 w-4 text-muted-foreground" />
<div className="flex-1"> <div className="flex-1">
<dt className="text-sm font-medium text-muted-foreground">Moneda Preferida</dt> <dt className="text-sm font-medium text-muted-foreground">
{t("view.fields.currency_code")}
</dt>
<dd className="mt-1 text-base text-foreground">{customerData?.currencyCode}</dd> <dd className="mt-1 text-base text-foreground">{customerData?.currencyCode}</dd>
</div> </div>
</div> </div>