Presupuestador_web/client/src/app/quotes/components/PDFGenerationLoading.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

79 lines
2.8 KiB
TypeScript

"use client";
import { ErrorMessage } from "@/lib/hooks/useDownloader/types";
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;
isInProgress: boolean;
percentage: number;
elapsed: number;
cancel: () => void;
error: ErrorMessage;
}
export const PDFGenerationLoading = ({
isInProgress,
percentage,
elapsed,
cancel,
error,
className,
}: PDFGenerationLoadingProps) => {
const [estimatedTime, setEstimatedTime] = useState(10);
useEffect(() => {
if (isInProgress && percentage > 0) {
const estimatedTotal = (elapsed / percentage) * 100;
setEstimatedTime(Math.max(0, Math.round(estimatedTotal - elapsed)));
}
}, [isInProgress, percentage, elapsed]);
if (!isInProgress && !error?.errorMessage) return null;
return (
<Card className={cn("overflow-hidden", className)}>
<CardHeader>
<CardTitle className='flex items-center justify-between'>
{error?.errorMessage ? t("quotes.pdf_generation.error") : t("quotes.pdf_generation.title")}
{isInProgress && <Loader2Icon className='w-4 h-4 animate-spin' />}
{error?.errorMessage && <XCircleIcon className='w-4 h-4 text-destructive' />}
</CardTitle>
</CardHeader>
<CardContent>
{error?.errorMessage ? (
<div className='text-destructive'>{error?.errorMessage}</div>
) : (
<div className='flex flex-col items-center space-y-4'>
<div className='relative w-32 h-40 overflow-hidden rounded-lg bg-muted'>
<div
className='absolute bottom-0 left-0 right-0 transition-all duration-1000 ease-out bg-primary'
style={{ height: `${percentage}%` }}
/>
<FileTextIcon
className='absolute transform -translate-x-1/2 -translate-y-1/2 animate-pulse top-1/2 left-1/2 text-muted-foreground'
size={48}
/>
</div>
<Progress value={percentage} className='w-full' />
<div className='text-sm text-center text-muted-foreground'>
<p>{t("quotes.pdf_generation.progress", { percentage })}</p>
<div className='hidden'>
<p>{t("quotes.pdf_generation.elapsed", { elapsed })}</p>
{estimatedTime > 0 && <p>{t("quotes.pdf_generation.remaining", { estimatedTime })}</p>}
</div>
</div>
<Button variant='destructive' onClick={cancel} className='hidden'>
{t("quotes.pdf_generation.cancel")}
</Button>
</div>
)}
</CardContent>
</Card>
);
};