Presupuestador_web/client/src/app/quotes/list.tsx
David Arranz c77a3a3af5 feat: internationalize button labels and UI components
- Updated CancelButton and SubmitButton to use i18next for localization.
- Modified DataTableColumnHeader and DataTableFacetedFilter to include translations for sorting and filtering messages.
- Enhanced DataTableSelectionColumn with localized aria-labels for accessibility.
- Localized titles and messages in SimpleEmptyState and ErrorOverlay components.
- Updated LayoutHeader to use i18next for navigation toggle label.
- Integrated i18next into useLogout for error messages.
- Localized alert dialog actions in useRemoveAlertDialog.
- Added new translations in en.json and es.json for various UI components.
- Updated breadcrumb and carousel components to use localized strings.
- Enhanced pagination component with i18next for navigation labels.
- Localized sheet component close button text.
2026-10-05 13:58:03 +02:00

155 lines
5.3 KiB
TypeScript

import { DataTableProvider } from "@/lib/hooks";
import { Trans, useTranslation } from "react-i18next";
import { QuotesDataTable } from "./components";
import { HelpButton } from "@/components";
import {
Button,
Label,
ScrollArea,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
Toggle,
} from "@/ui";
import { useToggle } from "@wojtekmaj/react-hooks";
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);
const quoteStatuses = [
{ value: "all", label: t("quotes.list.tabs.all") },
{ value: "draft", label: t("quotes.list.tabs.draft") },
{ value: "ready", label: t("quotes.list.tabs.ready") },
{ value: "delivered", label: t("quotes.list.tabs.delivered") },
{ value: "accepted", label: t("quotes.list.tabs.accepted") },
{ value: "rejected", label: t("quotes.list.tabs.rejected") },
{ value: "archived", label: t("quotes.list.tabs.archived") },
];
return (
<DataTableProvider>
<div className='flex items-center justify-between space-y-2'>
<div>
<h2 className='text-2xl font-bold tracking-tight'>
<Trans i18nKey='quotes.list.title' />
</h2>
<p className='text-muted-foreground'>
<Trans i18nKey='quotes.list.subtitle' />
</p>
</div>
<div className='flex items-center space-x-2'>
<Button onClick={() => navigate("/quotes/add")}>
<PlusIcon className='w-4 h-4 mr-2' />
{t("quotes.create.title")}
</Button>
</div>
</div>
<Tabs value={status} onValueChange={setStatus}>
<div className='flex flex-col items-start justify-between mb-4 sm:flex-row sm:items-center'>
<div className='w-full mb-4 sm:w-auto sm:mb-0'>
<TabsList className='hidden sm:flex'>
{quoteStatuses.map((s) => (
<TabsTrigger key={s.value} value={s.value}>
{s.label}
</TabsTrigger>
))}
</TabsList>
<div className='flex items-center w-full space-x-2 sm:hidden'>
<Label>{t("quotes.list.tabs_title")}</Label>
<Select value={status} onValueChange={setStatus}>
<SelectTrigger>
<SelectValue placeholder={t("quotes.list.select_status")} />
</SelectTrigger>
<SelectContent>
{quoteStatuses.map((s) => (
<SelectItem key={s.value} value={s.value}>
{s.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className='flex items-baseline justify-center mr-4 font-medium'>
<HelpButton
buttonText={t("common.help")}
title={t("quotes.list.status_help.title")}
content={<HelpContent />}
/>
</div>
<div className='flex items-center gap-2 ml-auto'>
<Toggle
aria-label={t("quotes.list.preview_aria")}
variant={"outline"}
defaultPressed={false}
pressed={enabledPreview}
onPressedChange={toggleEnabledPreview}
>
{enabledPreview ? (
<>
<EyeOffIcon className='w-4 h-4 mr-2' />
{t("common.disable_preview")}
</>
) : (
<>
<EyeIcon className='w-4 h-4 mr-2' />
{t("common.enable_preview")}
</>
)}
</Toggle>
</div>
</div>
{quoteStatuses.map((s) => (
<TabsContent key={s.value} value={s.value}>
<QuotesDataTable status={s.value} preview={enabledPreview} />
</TabsContent>
))}
</Tabs>
</DataTableProvider>
);
};
const HelpContent = () => {
const { t } = useTranslation();
return <ScrollArea className='grid gap-4 py-4'>
<p>{t("quotes.list.status_help.intro")}</p>
<p>{t("quotes.list.status_help.possible")}</p>
<ul className='pl-6 space-y-2 list-disc'>
<li>
<strong>{t("quotes.status.draft")}:</strong> {t("quotes.quote_status_editor.status.draft.description")}
</li>
<li>
<strong>{t("quotes.status.ready")}:</strong> {t("quotes.quote_status_editor.status.ready.description")}
</li>
<li>
<strong>{t("quotes.status.delivered")}:</strong> {t("quotes.quote_status_editor.status.delivered.description")}
</li>
<li>
<strong>{t("quotes.status.accepted")}:</strong> {t("quotes.quote_status_editor.status.accepted.description")}
</li>
<li>
<strong>{t("quotes.status.rejected")}:</strong> {t("quotes.quote_status_editor.status.rejected.description")}
</li>
<li>
<strong>{t("quotes.status.archived")}:</strong> {t("quotes.quote_status_editor.status.archived.description")}
</li>
</ul>
<p>{t("quotes.list.status_help.outro")}</p>
</ScrollArea>;
};