- 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.
155 lines
5.3 KiB
TypeScript
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>;
|
|
};
|