diff --git a/client/src/app/auth/LoginPageWithLanguageSelector.tsx b/client/src/app/auth/LoginPageWithLanguageSelector.tsx index 0175c9c..732d428 100644 --- a/client/src/app/auth/LoginPageWithLanguageSelector.tsx +++ b/client/src/app/auth/LoginPageWithLanguageSelector.tsx @@ -73,7 +73,7 @@ export const LoginPageWithLanguageSelector = () => { }, onError: (error) => { const { message } = error; - toast({ description: message, title: "Error" }); + toast({ description: message, title: t("common.error") }); console.log(error); }, }); diff --git a/client/src/app/catalog/components/CatalogDataTable.tsx b/client/src/app/catalog/components/CatalogDataTable.tsx index 774ad6d..37eab55 100644 --- a/client/src/app/catalog/components/CatalogDataTable.tsx +++ b/client/src/app/catalog/components/CatalogDataTable.tsx @@ -100,7 +100,7 @@ export const CatalogDataTable = () => { if (data?.total_items === 0 && !isFiltered) { return ( navigate("/catalog/add")} /> diff --git a/client/src/app/catalog/components/CatalogDataTableFilter.tsx b/client/src/app/catalog/components/CatalogDataTableFilter.tsx index ec6db6f..a4e73bb 100644 --- a/client/src/app/catalog/components/CatalogDataTableFilter.tsx +++ b/client/src/app/catalog/components/CatalogDataTableFilter.tsx @@ -80,10 +80,10 @@ export function CatalogDataTableFilter({ -

{t("catalog.filter.badget_remove_tooltip")}

+

{t("common.filter.badget_remove_tooltip")}

- {t("catalog.filter.badget_remove_tooltip")} + {t("common.filter.badget_remove_tooltip")} ))} diff --git a/client/src/app/dealers/components/DealerDataTable.tsx b/client/src/app/dealers/components/DealerDataTable.tsx index 1d0f108..2fba56c 100644 --- a/client/src/app/dealers/components/DealerDataTable.tsx +++ b/client/src/app/dealers/components/DealerDataTable.tsx @@ -7,6 +7,7 @@ import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar"; import { useDataTable, useDataTableContext } from "@/lib/hooks"; import { IListArticles_Response_DTO } from "@shared/contexts"; import { ColumnDef } from "@tanstack/react-table"; +import { t } from "i18next"; import { useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { useDealerList } from "../hooks"; @@ -70,7 +71,7 @@ export const DealerDataTable = () => { if (data?.total_items === 0 && !isFiltered) { return ( navigate("/catalog/add")} /> diff --git a/client/src/app/quotes/components/CatalogPickerDataTable.tsx b/client/src/app/quotes/components/CatalogPickerDataTable.tsx index 2006623..ee41709 100644 --- a/client/src/app/quotes/components/CatalogPickerDataTable.tsx +++ b/client/src/app/quotes/components/CatalogPickerDataTable.tsx @@ -254,7 +254,7 @@ export const CatalogPickerDataTable = ({ if (data?.total_items === 0 && !isFiltered) { return ( navigate("/catalog/add")} /> diff --git a/client/src/app/quotes/components/PDFGenerationLoading.tsx b/client/src/app/quotes/components/PDFGenerationLoading.tsx index 04bacfa..1304c77 100644 --- a/client/src/app/quotes/components/PDFGenerationLoading.tsx +++ b/client/src/app/quotes/components/PDFGenerationLoading.tsx @@ -5,6 +5,7 @@ import { cn } from "@/lib/utils"; import { Button, Card, CardContent, CardHeader, CardTitle, Progress } from "@/ui"; import { FileTextIcon, Loader2Icon, XCircleIcon } from "lucide-react"; import { useEffect, useState } from "react"; +import { t } from "i18next"; interface PDFGenerationLoadingProps { className?: string; @@ -38,7 +39,7 @@ export const PDFGenerationLoading = ({ - {error?.errorMessage ? "Error en la generación del PDF" : "Generando PDF"} + {error?.errorMessage ? t("quotes.pdf_generation.error") : t("quotes.pdf_generation.title")} {isInProgress && } {error?.errorMessage && } @@ -60,14 +61,14 @@ export const PDFGenerationLoading = ({
-

Progreso: {percentage}%

+

{t("quotes.pdf_generation.progress", { percentage })}

-

Tiempo transcurrido: {elapsed} segundos

- {estimatedTime > 0 &&

Tiempo estimado restante: {estimatedTime} segundos

} +

{t("quotes.pdf_generation.elapsed", { elapsed })}

+ {estimatedTime > 0 &&

{t("quotes.pdf_generation.remaining", { estimatedTime })}

}
)} diff --git a/client/src/app/quotes/components/QuotePDFPreview.tsx b/client/src/app/quotes/components/QuotePDFPreview.tsx index b2a8b1b..583bc63 100644 --- a/client/src/app/quotes/components/QuotePDFPreview.tsx +++ b/client/src/app/quotes/components/QuotePDFPreview.tsx @@ -3,6 +3,7 @@ import { cn } from "@/lib/utils"; import { Card, CardContent } from "@/ui"; import { IListQuotes_Response_DTO } from "@shared/contexts"; import { useCallback, useEffect, useState } from "react"; +import { t } from "i18next"; import { useQuotes } from "../hooks"; import { PDFGenerationLoading } from "./PDFGenerationLoading"; @@ -60,7 +61,7 @@ const QuotePDFPreview = ({ return ( -

Select a quote

+

{t("quotes.list.resume.select_quote")}

); diff --git a/client/src/app/quotes/components/QuoteResume.tsx b/client/src/app/quotes/components/QuoteResume.tsx index f754cb1..73d028b 100644 --- a/client/src/app/quotes/components/QuoteResume.tsx +++ b/client/src/app/quotes/components/QuoteResume.tsx @@ -134,7 +134,7 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => { return ( -

Select a quote

+

{t("quotes.list.resume.select_quote")}

); @@ -204,11 +204,11 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => { }} > - Editar + {t("common.edit")} -

Editar

+

{t("common.edit")}

@@ -223,11 +223,11 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => { }} > - Duplicar + {t("common.duplicate")} -

Duplicar

+

{t("common.duplicate")}

@@ -235,11 +235,11 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => { -

Descargar

+

{t("common.download")}

@@ -255,11 +255,11 @@ export const QuoteResume = ({ quoteId, className }: QuoteResumeProps) => { }} > - Archivar + {t("common.archive")} -

Archivar

+

{t("common.archive")}

diff --git a/client/src/app/quotes/components/QuotesDataTable.tsx b/client/src/app/quotes/components/QuotesDataTable.tsx index 07567cd..75e14a9 100644 --- a/client/src/app/quotes/components/QuotesDataTable.tsx +++ b/client/src/app/quotes/components/QuotesDataTable.tsx @@ -277,11 +277,11 @@ export const QuotesDataTable = ({ }} > - Editar + {t("common.edit")} -

Editar

+

{t("common.edit")}

@@ -296,11 +296,11 @@ export const QuotesDataTable = ({ }} > - Duplicar + {t("common.duplicate")} -

Duplicar

+

{t("common.duplicate")}

@@ -314,11 +314,11 @@ export const QuotesDataTable = ({ }} > - Descargar + {t("common.download")} -

Descargar

+

{t("common.download")}

@@ -334,11 +334,11 @@ export const QuotesDataTable = ({ }} > - Archivar + {t("common.archive")} -

Archivar

+

{t("common.archive")}

@@ -398,7 +398,7 @@ export const QuotesDataTable = ({ navigate("add", { relative: "path" })} /> diff --git a/client/src/app/quotes/components/editors/QuoteDocumentsCardEditor.tsx b/client/src/app/quotes/components/editors/QuoteDocumentsCardEditor.tsx index 4861e78..d60ff62 100644 --- a/client/src/app/quotes/components/editors/QuoteDocumentsCardEditor.tsx +++ b/client/src/app/quotes/components/editors/QuoteDocumentsCardEditor.tsx @@ -24,7 +24,7 @@ export const QuoteDocumentsCardEditor = () => { render={() => (
- Images + {t("quotes.create.form_groups.documents.images")} @@ -32,7 +32,7 @@ export const QuoteDocumentsCardEditor = () => { )} />
diff --git a/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx b/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx index 7cd864d..db16ac4 100644 --- a/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx +++ b/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx @@ -77,15 +77,15 @@ export const QuoteGeneralCardEditor = () => { >
- +
diff --git a/client/src/app/quotes/create.tsx b/client/src/app/quotes/create.tsx index eb0290d..eba08c3 100644 --- a/client/src/app/quotes/create.tsx +++ b/client/src/app/quotes/create.tsx @@ -69,7 +69,7 @@ export const QuoteCreate = () => { onError: (error) => { console.debug(error); toast({ - title: "Error", + title: t("common.error"), description: error.message, variant: "destructive", }); @@ -77,7 +77,7 @@ export const QuoteCreate = () => { onSuccess: (data) => { reset(getValues()); toast({ - title: "Cotización creada", + title: t("quotes.notifications.created"), variant: "default", }); navigate(`/quotes/edit/${data.id}`, { relative: "path" }); @@ -89,7 +89,7 @@ export const QuoteCreate = () => { }; if (isSubmitting) { - return ; + return ; } /*if (isError) { diff --git a/client/src/app/quotes/edit.tsx b/client/src/app/quotes/edit.tsx index 1251e3b..d21de69 100644 --- a/client/src/app/quotes/edit.tsx +++ b/client/src/app/quotes/edit.tsx @@ -314,13 +314,13 @@ export const QuoteEdit = () => { mutate(data, { onError: (error) => { console.error(error); - toast({ title: "Error", description: error.message, variant: "destructive" }); + toast({ title: t("common.error"), description: error.message, variant: "destructive" }); //alert(error.message); }, //onSettled: () => {}, onSuccess: () => { reset(getValues()); - toast({ title: "Cotización guardada", variant: "success" }); + toast({ title: t("quotes.notifications.saved"), variant: "success" }); if (shouldRedirect) { navigate("/quotes"); } @@ -354,7 +354,7 @@ export const QuoteEdit = () => { return ( <> - {(isSubmitting || isPending) && } + {(isSubmitting || isPending) && }
onSubmit(data, false))}>
diff --git a/client/src/app/quotes/hooks/useDetailColumns.tsx b/client/src/app/quotes/hooks/useDetailColumns.tsx index 804c5d9..b8ce014 100644 --- a/client/src/app/quotes/hooks/useDetailColumns.tsx +++ b/client/src/app/quotes/hooks/useDetailColumns.tsx @@ -6,6 +6,7 @@ import { Checkbox } from "@/ui"; import { ColumnDef, Row } from "@tanstack/react-table"; import { useMemo } from "react"; +import { t } from "i18next"; import { QuoteItemsSortableDragHandleCell } from "../components/QuoteItemsSortableDragHandleCell"; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -53,7 +54,7 @@ export function useDetailColumns( (table.getIsSomePageRowsSelected() && "indeterminate") } onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)} - aria-label='Seleccionar todo' + aria-label={t("common.select_all")} className='translate-y-[0px]' /> ), @@ -64,7 +65,7 @@ export function useDetailColumns( checked={row.getIsSelected()} disabled={!row.getCanSelect()} onCheckedChange={row.getToggleSelectedHandler()} - aria-label='Seleccionar fila' + aria-label={t("common.select_row")} className='mt-2' /> ), diff --git a/client/src/app/quotes/list.tsx b/client/src/app/quotes/list.tsx index 3fb9896..ea5accb 100644 --- a/client/src/app/quotes/list.tsx +++ b/client/src/app/quotes/list.tsx @@ -1,5 +1,5 @@ import { DataTableProvider } from "@/lib/hooks"; -import { Trans } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { QuotesDataTable } from "./components"; import { HelpButton } from "@/components"; @@ -19,12 +19,12 @@ import { Toggle, } from "@/ui"; import { useToggle } from "@wojtekmaj/react-hooks"; -import { t } from "i18next"; import { EyeIcon, EyeOffIcon, PlusIcon } from "lucide-react"; import { useState } from "react"; import { useNavigate } from "react-router-dom"; export const QuotesList = () => { + const { t } = useTranslation(); const navigate = useNavigate(); const [status, setStatus] = useState("all"); const [enabledPreview, toggleEnabledPreview] = useToggle(true); @@ -72,7 +72,7 @@ export const QuotesList = () => { )} diff --git a/client/src/app/support/components/SupportModal.tsx b/client/src/app/support/components/SupportModal.tsx index 00d5b8c..cc6e3a4 100644 --- a/client/src/app/support/components/SupportModal.tsx +++ b/client/src/app/support/components/SupportModal.tsx @@ -30,12 +30,12 @@ import Joi from "joi"; import { HelpCircleIcon } from "lucide-react"; import { useSupport } from "../hooks"; -const formSchema = Joi.object({ +const createFormSchema = () => Joi.object({ incidence: Joi.string().min(10).required().messages({ - "string.empty": "Debe escribir algo antes de enviar", - "string.min": "El texto es demasiado corto. Debe tener al menos 10 caracteres", - "string.max": "El texto es demasiado largo.", - "any.required": "La descripción es requerida", + "string.empty": t("support.modal.validation.empty"), + "string.min": t("support.modal.validation.min"), + "string.max": t("support.modal.validation.max"), + "any.required": t("support.modal.validation.required"), }), }); @@ -49,7 +49,7 @@ export default function SupportModal() { const form = useForm({ mode: "onBlur", - resolver: joiResolver(formSchema), + resolver: joiResolver(createFormSchema()), defaultValues: { incidence: "", }, @@ -63,8 +63,8 @@ export default function SupportModal() { mutateOptions: { onSuccess: () => { toast({ - title: "Incidencia enviada", - description: "La incidencia se ha enviado correctamente", + title: t("support.modal.notifications.sent_title"), + description: t("support.modal.notifications.sent_description"), variant: "success", }); setIsOpen(false); @@ -72,9 +72,8 @@ export default function SupportModal() { }, onError: () => { toast({ - title: "Error en el envío", - description: - "No se ha podido enviar la incidencia correctamente. Por favor, inténtalo de nuevo.", + title: t("support.modal.notifications.error_title"), + description: t("support.modal.notifications.error_description"), variant: "destructive", }); }, @@ -111,7 +110,7 @@ export default function SupportModal() { onClick={() => setIsOpen(true)} > - Abrir ventana de soporte + {t("support.modal.open")} @@ -123,14 +122,14 @@ export default function SupportModal() { - + @@ -140,17 +139,16 @@ export default function SupportModal() { - ¿Estás seguro de que quieres cancelar? + {t("support.modal.cancel_confirmation.title")} - Has escrito texto en el campo de descripción. Si cierras la ventana, perderás los - cambios no guardados. + {t("support.modal.cancel_confirmation.description")} setShowConfirmDialog(false)}> - Volver al formulario + {t("support.modal.cancel_confirmation.back")} - Sí, cerrar + {t("support.modal.cancel_confirmation.confirm")} diff --git a/client/src/components/Buttons/CancelButton.tsx b/client/src/components/Buttons/CancelButton.tsx index 5f4af99..2a2cb7f 100644 --- a/client/src/components/Buttons/CancelButton.tsx +++ b/client/src/components/Buttons/CancelButton.tsx @@ -1,10 +1,11 @@ import { Button, ButtonProps } from "@/ui"; +import { t } from "i18next"; export interface CancelButtonProps extends ButtonProps { label?: string; } -export const CancelButton = ({ label = "Cancelar", ...props }: CancelButtonProps): JSX.Element => { +export const CancelButton = ({ label = t("common.cancel"), ...props }: CancelButtonProps): JSX.Element => { return ( - Columnas + {t("components.data_table.columns")} {table .getAllColumns() diff --git a/client/src/components/EmptyState/SimpleEmptyState.tsx b/client/src/components/EmptyState/SimpleEmptyState.tsx index 15629b7..0455ddf 100644 --- a/client/src/components/EmptyState/SimpleEmptyState.tsx +++ b/client/src/components/EmptyState/SimpleEmptyState.tsx @@ -1,10 +1,11 @@ import { Button } from "@/ui"; import { PlusIcon } from "lucide-react"; +import { t } from "i18next"; export const SimpleEmptyState = ({ - title = "Esto está muy vacío", - subtitle = "Empieza solicitando la carga del catálogo", - buttonText = "Solicitar carga del catálogo", + title = t("components.empty_state.title"), + subtitle = t("components.empty_state.subtitle"), + buttonText = t("components.empty_state.button"), onButtonClick = () => {}, actions = () => <>, }) => { diff --git a/client/src/components/ErrorOverlay/ErrorOverlay.tsx b/client/src/components/ErrorOverlay/ErrorOverlay.tsx index d0caac7..46403f1 100644 --- a/client/src/components/ErrorOverlay/ErrorOverlay.tsx +++ b/client/src/components/ErrorOverlay/ErrorOverlay.tsx @@ -1,5 +1,6 @@ import { Button } from "@/ui"; import { HomeIcon } from "lucide-react"; +import { t } from "i18next"; import { useNavigate } from "react-router-dom"; import { BackHistoryButton } from "../CustomButtons"; @@ -18,7 +19,7 @@ interface ErrorOverlayProps { };*/ export const ErrorOverlay = ({ - title = "Se ha producido un error", + title = t("components.error_overlay.title"), subtitle = undefined, description = undefined, errorMessage = undefined, @@ -42,7 +43,7 @@ ErrorOverlayProps): JSX.Element => {
diff --git a/client/src/components/Layout/LayoutHeader.tsx b/client/src/components/Layout/LayoutHeader.tsx index 032b66d..faf4cc5 100644 --- a/client/src/components/Layout/LayoutHeader.tsx +++ b/client/src/components/Layout/LayoutHeader.tsx @@ -3,6 +3,7 @@ import { Button, Sheet, SheetContent, SheetTrigger } from "@/ui"; import SupportModal from "@/app/support/components/SupportModal"; import { cn } from "@/lib/utils"; import { MenuIcon, Package2Icon } from "lucide-react"; +import { t } from "i18next"; import { useCallback } from "react"; import { Trans } from "react-i18next"; import { Link, useLocation } from "react-router-dom"; @@ -58,7 +59,7 @@ export const LayoutHeader = () => { diff --git a/client/src/lib/hooks/useAuth/useLogout.tsx b/client/src/lib/hooks/useAuth/useLogout.tsx index c8fca56..b9b9270 100644 --- a/client/src/lib/hooks/useAuth/useLogout.tsx +++ b/client/src/lib/hooks/useAuth/useLogout.tsx @@ -4,6 +4,7 @@ import { useNavigate } from "react-router-dom"; import { useToast } from "@/ui/use-toast"; import { useQueryKey } from "../useQueryKey"; +import { t } from "i18next"; export const useLogout = (params?: UseMutationOptions) => { const { onSuccess, onError, ...restParams } = params || {}; @@ -31,7 +32,7 @@ export const useLogout = (params?: UseMutationOptions onError: (error, variables, context) => { const { message } = error; toast({ - title: "Error", + title: t("common.error"), description: message, variant: "destructive", }); diff --git a/client/src/lib/hooks/useRemoveAlertDialog/useRemoveAlertDialog.tsx b/client/src/lib/hooks/useRemoveAlertDialog/useRemoveAlertDialog.tsx index 5e2f235..4f5e154 100644 --- a/client/src/lib/hooks/useRemoveAlertDialog/useRemoveAlertDialog.tsx +++ b/client/src/lib/hooks/useRemoveAlertDialog/useRemoveAlertDialog.tsx @@ -12,6 +12,7 @@ import { } from "@/ui/alert-dialog"; import { useCallback, useState } from "react"; +import { t } from "i18next"; interface UseRemoveAlertDialogProps { isOpen?: boolean; @@ -75,7 +76,7 @@ export const useRemoveAlertDialog = ({ {description} - Cancelar + {t("common.cancel")} (onRemove ? onRemove(payload, event) : null)} > - Eliminar + {t("common.remove")} diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 1b0667c..71c8529 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -1,14 +1,12 @@ { "translation": { "common": { - "loading": "Loading...", "required": "required", "cancel": "Cancel", "no": "No", "yes": "Yes", "save": "Save", "save_close": "Save and Close", - "accept": "OK", "discard": "Discard", "hide": "Hide", "back": "Back", @@ -16,7 +14,19 @@ "continue": "Continue", "close": "Close", "more": "More", + "pagination": "Pagination", + "breadcrumb": "Breadcrumb", + "previous": "Previous", + "next": "Next", + "more_pages": "More pages", + "previous_slide": "Previous slide", + "next_slide": "Next slide", "add": "Add", + "submit": "Submit", + "download": "Download", + "help": "Help", + "select_all": "Select all", + "select_row": "Select row", "sort_asc": "Asc", "sort_asc_description": "In ascending order. Click to sort descending order.", "sort_desc": "Desc", @@ -52,21 +62,18 @@ "insert_row_below": "Insert row below", "pick_date": "Select a date", "required_field": "This field is required", - "unsaved_changes_prompt": "There are unsaved changes. If you leave, you'll lose your changes.", "edit": "Edit", "remove": "Remove", "archive": "Archive", "duplicate": "Duplicate", - "print": "Print", - "disable_preview": "Disable preview", - "enable_preview": "Enable preview", + "disable_preview": "Hide preview", + "enable_preview": "Show preview", "filter": { "reset_filter": "Reset filter", "badget_remove_tooltip": "Remove this filter term", "input_placeholder": "Type here to filter...", "button_add_term": "Add filter term", "button_add_term_tooltip": "Add filter term (or press Enter)", - "button_remove_all": "Remove all terms from filter", "help_text": "Press Enter or click the + button to add a term to the filter. Use multiple terms for a broader search." } }, @@ -77,6 +84,21 @@ "loading_overlay": { "title": "Loading...", "subtitle": "This may take a few seconds. Please do not close this page." + }, + "error_overlay": { + "title": "An error occurred", + "go_home": "Go home" + }, + "empty_state": { + "title": "There is nothing here yet", + "subtitle": "Start by requesting the catalog upload", + "button": "Request catalog upload" + }, + "data_table": { + "columns": "Columns", + "no_results": "No results found.", + "clear_filters": "Clear filters", + "selected_count": "{{count}} selected" } }, "hooks": { @@ -88,13 +110,11 @@ } }, "main_menu": { - "home": "Home", + "toggle_navigation": "Toggle navigation menu", "settings": "Settings", - "dealers": "Dealers", "catalog": "Catalog", "quotes": "Quotes", - "orders": "Orders", - "search_placeholder": "Type here for search quotes and articles", + "search_placeholder": "Search products, quotes, etc.", "user": { "user_menu": "User menu", "my_account": "My account", @@ -104,14 +124,14 @@ "logout": "Logout" }, "logout_dialog": { - "title": "Confirm", + "title": "Log out", "description": "Are you sure you want to log out?", "confirm_label": "Log out", "cancel_label": "Cancel" } }, "login_page": { - "title": "Partner intranet", + "title": "Quoting tool for distributors", "description": "Enter your email address and password to login", "email_label": "Email", "email_placeholder": "myemail@sample.com", @@ -127,11 +147,14 @@ "catalog": { "list": { "title": "Catalog of articles", + "empty_state": { + "subtitle": "Start by loading catalog items" + }, "columns": { "description": "Description", "points": "Points", "retail_price": "Retail price", - "quantity": "quantity" + "quantity": "Quantity" } } }, @@ -139,7 +162,18 @@ "list": { "title": "Quotes", "subtitle": "", - "tabs_title": "Status", + "tabs_title": "Statuses", + "select_status": "Select status", + "preview_aria": "Show quote preview", + "empty_state": { + "subtitle": "Start by creating a quote" + }, + "status_help": { + "title": "Quote status help", + "intro": "A quote status indicates its current position in the business process.", + "possible": "The possible statuses are:", + "outro": "Use these statuses to track quotes efficiently and improve your sales process. The Archived status helps keep history without interfering with active quotes." + }, "tabs": { "all": "All", "draft": "Draft", @@ -156,58 +190,49 @@ "status": "Status", "customer_reference": "Customer Ref.", "customer_information": "Customer", - "total_price": "Imp. total", - "actions": { - "edit": "Edit quote", - "sent_to": "Send to Uecko" - } + "total_price": "Total amount" }, "resume": { "title": "Quote", - "download_quote": "Download quote", "tabs": { - "resume": "Resume", + "resume": "Summary", "preview": "Preview" }, "quote_information": "Quote Information", "customer_information": "Customer Information", - "payment_information": "Payment Information", - "price_information": "Quote totals" + "price_information": "Quote totals", + "select_quote": "Select a quote" } }, + "notifications": { + "created": "Quote created", + "saved": "Quote saved", + "saving": "Saving quote" + }, + "pdf_generation": { + "title": "Generating PDF", + "error": "Error generating PDF", + "progress": "Progress: {{percentage}}%", + "elapsed": "Elapsed time: {{elapsed}} seconds", + "remaining": "Estimated time remaining: {{estimatedTime}} seconds", + "cancel": "Cancel generation" + }, "create": { "title": "New quote", - "tabs": { - "general": "General data", - "items": "Quote items", - "preview": "Quote preview", - "documents": "Documents", - "history": "History" - }, "form_groups": { "general": { "title": "General Data", - "desc": "General data and quote customer" + "desc": "General information and the quote's customer" }, "status": { "title": "Status", "desc": "Quote status" }, - "items": { - "title": "Quote Items", - "desc": "Quote detail lines. Use the catalog to make it easier to fill in the content." - }, "documents": { "title": "Attached Documents", - "desc": "Attach documents such as photos, drawings, sketches, etc. to your quotation." - }, - "history": { - "title": "History", - "desc": "Quote history" + "desc": "Attach documents such as photos, drawings, sketches, etc. to your quotation.", + "images": "Images" } - }, - "edit": { - "title": "Quote" } }, "edit": { @@ -225,8 +250,7 @@ "progress": { "label": "Download is in {{status}}", "status_in_progress": "in progress", - "status_stopped": "stopped", - "value_label": "Elapsed time in seconds {{elapsed}}" + "status_stopped": "stopped" }, "cancel_button": "Cancel the download", "toast_success": "Quote downloaded" @@ -238,9 +262,15 @@ }, "blocks_picker_dialog": { "title": "Select a text block", - "description": "To complete your quote, you can default text blocks.", + "description": "To help complete your quote, you can add predefined text blocks from this list.", "toast_article_added": "Text block added:" }, + "quote_sent_to_editor": { + "trigger_button": "Send to Uecko", + "title": "Send quote to Uecko", + "description": "Do you want to send this quote to Uecko? This action cannot be undone.", + "toast_status_changed": "Quote sent to Uecko" + }, "quote_status_editor": { "trigger_button": "Change quote status", "title": "Change quote status", @@ -267,7 +297,7 @@ }, "archived": { "title": "Archived", - "description": "The quote is archived." + "description": "The quote has been saved for future reference." } }, "submit_button": "Change status", @@ -308,11 +338,6 @@ "desc": "Quote reference", "placeholder": "" }, - "status": { - "label": "Status", - "desc": "Quote status", - "placeholder": "" - }, "lang_code": { "label": "Language", "desc": "Quote language", @@ -329,13 +354,13 @@ "placeholder": "" }, "customer_information": { - "label": "Customer's contact data", + "label": "Customer details", "desc": "Recommendation: enter the customer's name on the first line, the address on the second line, and the zip code and city/state on the third line.", "placeholder": "Name and surname\nStreet and number\nzip code and city or state..." }, "payment_method": { "label": "Payment method", - "placeholder": "", + "placeholder": "placeholder", "desc": "Method of payment for this quote" }, "notes": { @@ -364,14 +389,14 @@ "desc": "Percentage discount" }, "discount_price": { - "label": "Discount price", + "label": "Discount amount", "placeholder": "", - "desc": "Percentage discount price" + "desc": "Discount amount" }, "before_tax_price": { - "label": "Before tax price", + "label": "Taxable amount", "placeholder": "", - "desc": "Before tax price" + "desc": "Taxable amount" }, "tax": { "label": "Tax (%)", @@ -386,7 +411,7 @@ "tax_price": { "label": "Tax amount", "placeholder": "", - "desc": "Percentage tax price" + "desc": "Tax amount" }, "total_price": { "label": "Total price", @@ -415,7 +440,7 @@ "desc": "" }, "discount": { - "label": "Dto (%)", + "label": "Discount (%)", "placeholder": "", "desc": "Percentage discount" }, @@ -430,7 +455,6 @@ "settings": { "edit": { "title": "Settings", - "subtitle": "", "tabs": { "profile": "Profile settings", "quotes": "Quote settings", @@ -441,12 +465,19 @@ "language": { "label": "Language", "placeholder": "Select a language", - "desc": "Preferred language for the interface" + "desc": "Preferred language for the interface", + "options": { + "es": "Spanish", + "en": "English" + } }, - "image": { - "label": "Logotype", - "placeholder": "", - "desc": "" + "logo": { + "label": "Logo", + "desc": "This logo will appear in PDF-exported proposals", + "requirements": "JPG or PNG files are allowed. The logo dimensions must be 200x100 pixels or proportional.", + "upload": "Upload logo", + "invalid_format": "Only JPG or PNG files are allowed", + "invalid_dimensions": "The logo dimensions must be 200x100 pixels or proportional." }, "contact_information": { "label": "Your contact information", @@ -459,7 +490,6 @@ "desc": "Default tax rate for your quotes" }, "default_tax_label": { - "label": "Tax label", "placeholder": "% VAT", "desc": "Name or label shown alongside the tax" }, @@ -470,7 +500,7 @@ "default_legal_terms": { "label": "Legal terms", "placeholder": "", - "desc": "Legal information to be included at the end of your quotes" + "desc": "Legal text to include in all new quotes" }, "default_payment_method": { "label": "Payment method", @@ -487,7 +517,39 @@ "placeholder": "", "desc": "Default validity time to be used for new quotes" } + }, + "notifications": { + "saved": "Settings saved", + "saving": "Saving settings" } + }, + "support": { + "modal": { + "title": "Send an issue", + "subtitle": "Use this form to report any problem you encountered while using the application. Our development team will review your issue and try to resolve it.", + "open": "Open support window", + "placeholder": "Describe the issue here...", + "submit": "Send issue", + "validation": { + "empty": "Write something before sending", + "min": "The text is too short. It must contain at least 10 characters", + "max": "The text is too long.", + "required": "A description is required" + }, + "notifications": { + "sent_title": "Issue sent", + "sent_description": "The issue was sent successfully", + "error_title": "Sending error", + "error_description": "The issue could not be sent. Please try again." + }, + "cancel_confirmation": { + "title": "Are you sure you want to cancel?", + "description": "You have entered text in the description field. Closing the window will discard your unsaved changes.", + "back": "Return to form", + "confirm": "Yes, close" + } + }, + "form_fields": {} } } } diff --git a/client/src/locales/es.json b/client/src/locales/es.json index 1842d3d..31d3464 100644 --- a/client/src/locales/es.json +++ b/client/src/locales/es.json @@ -1,14 +1,12 @@ { "translation": { "common": { - "loading": "Cargando...", "required": "obligatorio", "cancel": "Cancelar", "no": "No", "yes": "Sí", "save": "Guardar", "save_close": "Guardar y Cerrar", - "accept": "Aceptar", "discard": "Descartar", "hide": "Ocultar", "back": "Volver", @@ -16,7 +14,19 @@ "continue": "Continuar", "close": "Cerrar", "more": "More", + "pagination": "Paginación", + "breadcrumb": "Migas de pan", + "previous": "Anterior", + "next": "Siguiente", + "more_pages": "Más páginas", + "previous_slide": "Diapositiva anterior", + "next_slide": "Diapositiva siguiente", "add": "Añadir", + "submit": "Enviar", + "download": "Descargar", + "help": "Ayuda", + "select_all": "Seleccionar todo", + "select_row": "Seleccionar fila", "sort_asc": "Asc", "sort_asc_description": "En order ascendente. Click para ordenar descendentemente.", "sort_desc": "Desc", @@ -52,12 +62,10 @@ "insert_row_below": "Insertar fila debajo", "pick_date": "Elige una fecha", "required_field": "Este campo es obligatorio", - "unsaved_changes_prompt": "Los últimos cambios no se han guardado. Si continúas, se perderán.", "edit": "Editar", "remove": "Eliminar", "archive": "Archivar", "duplicate": "Duplicar", - "print": "Imprimir", "disable_preview": "Ocultar vista previa", "enable_preview": "Mostrar vista previa", "filter": { @@ -66,17 +74,31 @@ "input_placeholder": "Escribe aquí para filtrar...", "button_add_term": "Añadir término al filtro", "button_add_term_tooltip": "Añadir término al filtro (o pulsa Enter)", - "button_remove_all": "Quitar todos los términos del filtro", "help_text": "Presiona Enter o haz clic en el botón + para añadir un término al filtro. Usa múltiples términos para una búsqueda más amplia." } }, "components": { - "LoadingIndicator": { + "loading_indicator": { "title": "Cargando..." }, "loading_overlay": { "title": "Cargando...", "subtitle": "Esto puede tardar unos segundos. Por favor, no cierre esta página." + }, + "error_overlay": { + "title": "Se ha producido un error", + "go_home": "Ir al inicio" + }, + "empty_state": { + "title": "Esto está muy vacío", + "subtitle": "Empieza solicitando la carga del catálogo", + "button": "Solicitar carga del catálogo" + }, + "data_table": { + "columns": "Columnas", + "no_results": "No se han encontrado resultados.", + "clear_filters": "Limpiar filtros", + "selected_count": "{{count}} seleccionados" } }, "hooks": { @@ -88,12 +110,10 @@ } }, "main_menu": { - "home": "Inicio", + "toggle_navigation": "Alternar el menú de navegación", "settings": "Ajustes", - "dealers": "Distribuidores", "catalog": "Catálogo", "quotes": "Cotizaciones", - "orders": "Pedidos", "search_placeholder": "Buscar productos, cotizaciones, etc...", "user": { "user_menu": "Menú del usuario", @@ -127,6 +147,9 @@ "catalog": { "list": { "title": "Catálogo de artículos", + "empty_state": { + "subtitle": "Empieza cargando los artículos del catálogo" + }, "columns": { "description": "Descripción", "points": "Puntos", @@ -140,6 +163,17 @@ "title": "Cotizaciones", "subtitle": "", "tabs_title": "Estados", + "select_status": "Seleccionar estado", + "preview_aria": "Mostrar vista previa de la cotización", + "empty_state": { + "subtitle": "Empieza creando alguna cotización" + }, + "status_help": { + "title": "Ayuda sobre el estado de las cotizaciones", + "intro": "El estado de una cotización indica su posición actual en el proceso de negocio.", + "possible": "Los estados posibles son:", + "outro": "Utiliza estos estados para hacer un seguimiento eficiente de tus cotizaciones y optimizar tu proceso de ventas. El estado Archivado ayuda a conservar el historial sin interferir con las cotizaciones activas." + }, "tabs": { "all": "Todas", "draft": "Borradores", @@ -156,34 +190,35 @@ "status": "Estado", "customer_reference": "Ref. cliente", "customer_information": "Cliente", - "total_price": "Imp. total", - "actions": { - "edit": "Editar", - "sent_to": "Enviar a Uecko" - } + "total_price": "Imp. total" }, "resume": { "title": "Cotización", - "download_quote": "Descargar", "tabs": { "resume": "Resumen", "preview": "Previsualización" }, "quote_information": "Información", "customer_information": "Cliente", - "payment_information": "Pago", - "price_information": "Importes" + "price_information": "Importes", + "select_quote": "Selecciona una cotización" } }, + "notifications": { + "created": "Cotización creada", + "saved": "Cotización guardada", + "saving": "Guardando cotización" + }, + "pdf_generation": { + "title": "Generando PDF", + "error": "Error al generar el PDF", + "progress": "Progreso: {{percentage}}%", + "elapsed": "Tiempo transcurrido: {{elapsed}} segundos", + "remaining": "Tiempo estimado restante: {{estimatedTime}} segundos", + "cancel": "Cancelar generación" + }, "create": { "title": "Nueva cotización", - "tabs": { - "general": "Datos generales", - "items": "Contenido", - "preview": "Vista previa", - "documents": "Documentos", - "history": "Historial" - }, "form_groups": { "general": { "title": "Datos generales", @@ -193,21 +228,11 @@ "title": "Estado", "desc": "Estado de la cotización" }, - "items": { - "title": "Contenido de la cotización", - "desc": "Líneas de detalle de la cotización. Ayúdese del catálogo para rellenar más fácilmente el contenido." - }, "documents": { "title": "Documentos", - "desc": "Añada adjuntar con su cotización documentos como fotos, planos, croquis, etc." - }, - "history": { - "title": "Historial", - "desc": "Historial de la cotización" + "desc": "Añada adjuntar con su cotización documentos como fotos, planos, croquis, etc.", + "images": "Imágenes" } - }, - "edit": { - "title": "Cotización" } }, "edit": { @@ -225,8 +250,7 @@ "progress": { "label": "La descarga está {{status}}", "status_in_progress": "en marcha", - "status_stopped": "parada", - "value_label": "Elapsed time in seconds {{elapsed}}" + "status_stopped": "parada" }, "cancel_button": "Cancelar la descarga", "toast_success": "Cotización descargada" @@ -245,7 +269,6 @@ "trigger_button": "Enviar a Uecko", "title": "Enviar la cotización a Uecko", "description": "¿Desea enviar esta cotización a Uecko? Esta acción no se puede deshacer.", - "submit_button": "Enviar", "toast_status_changed": "Cotización enviada a Uecko" }, "quote_status_editor": { @@ -432,7 +455,6 @@ "settings": { "edit": { "title": "Ajustes", - "subtitle": "", "tabs": { "profile": "Ajustes de perfil", "quotes": "Ajustes para cotizaciones", @@ -443,11 +465,14 @@ "language": { "label": "Idioma", "placeholder": "Selecciona un idioma", - "desc": "Idioma preferido para la interfaz" + "desc": "Idioma preferido para la interfaz", + "options": { + "es": "Español", + "en": "Inglés" + } }, "logo": { "label": "Logotipo", - "placeholder": "", "desc": "Este logotipo aparecerá en las propuestas exportadas en PDF", "requirements": "Se permiten ficheros JPG o PNG. Las dimensiones del logotipo deben ser 200x100 píxeles o proporcionales.", "upload": "Subir logotipo", @@ -465,7 +490,6 @@ "desc": "Porcentaje de impuesto por defecto en las cotizaciones" }, "default_tax_label": { - "label": "Etiqueta del impuesto", "placeholder": "% IVA", "desc": "Nombre o etiqueta que se mostrará junto al impuesto" }, @@ -493,12 +517,37 @@ "placeholder": "", "desc": "Texto para indicar el tiempo de validez de la cotización" } + }, + "notifications": { + "saved": "Ajustes guardados", + "saving": "Guardando ajustes" } }, "support": { "modal": { "title": "Enviar una incidencia", - "subtitle": "Utiliza este formulario para informar sobre cualquier problema que hayas encontrado mientras usabas la aplicación. Nuestro equipo de desarrollo revisará tu incidencia y tratará de resolverla." + "subtitle": "Utiliza este formulario para informar sobre cualquier problema que hayas encontrado mientras usabas la aplicación. Nuestro equipo de desarrollo revisará tu incidencia y tratará de resolverla.", + "open": "Abrir ventana de soporte", + "placeholder": "Describe la incidencia aquí...", + "submit": "Enviar incidencia", + "validation": { + "empty": "Debe escribir algo antes de enviar", + "min": "El texto es demasiado corto. Debe tener al menos 10 caracteres", + "max": "El texto es demasiado largo.", + "required": "La descripción es requerida" + }, + "notifications": { + "sent_title": "Incidencia enviada", + "sent_description": "La incidencia se ha enviado correctamente", + "error_title": "Error en el envío", + "error_description": "No se ha podido enviar la incidencia correctamente. Por favor, inténtalo de nuevo." + }, + "cancel_confirmation": { + "title": "¿Estás seguro de que quieres cancelar?", + "description": "Has escrito texto en el campo de descripción. Si cierras la ventana, perderás los cambios no guardados.", + "back": "Volver al formulario", + "confirm": "Sí, cerrar" + } }, "form_fields": {} } diff --git a/client/src/ui/breadcrumb.tsx b/client/src/ui/breadcrumb.tsx index 71a5c32..1d88942 100644 --- a/client/src/ui/breadcrumb.tsx +++ b/client/src/ui/breadcrumb.tsx @@ -1,6 +1,7 @@ import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { ChevronRight, MoreHorizontal } from "lucide-react" +import { t } from "i18next" import { cn } from "@/lib/utils" @@ -9,7 +10,7 @@ const Breadcrumb = React.forwardRef< React.ComponentPropsWithoutRef<"nav"> & { separator?: React.ReactNode } ->(({ ...props }, ref) =>