Files
leocrm/frontend/src/components/common/PrintButton.tsx
T
Agent Zero a3a5a10514 Phase 1: Workflows UI, Dedup/Merge UI, Import/Export UI, Print/PDF
- Workflows UI: full page with definitions/instances tabs, step editor, instance detail with approve/reject
- Dedup/Merge UI: duplicate detection, side-by-side comparison, field-level merge dialog, merge history
- Import/Export UI: import wizard (dry-run preview), export panel (CSV/XLSX), backend export route added
- Print/PDF: PrintButton component, print.css, integrated in Contacts/Calendar/Reports/ContactDetail
- Backend: GET /api/v1/export endpoint, export_companies_csv() service function
- Routes: /workflows, /contacts/dedup, /import-export registered
- Menu items: Workflows, Import/Export, Duplikate added to automation plugin manifest
- IMPLEMENTATION_PLAN.md: audit-corrected plan for all 14 remaining features
2026-07-26 02:35:44 +02:00

138 lines
4.3 KiB
TypeScript

/**
* PrintButton — reusable button with dropdown for Print and PDF export.
* Uses lucide-react icons and clsx for styling.
*/
import React, { useState, useRef, useEffect, useCallback } from 'react';
import clsx from 'clsx';
import { Printer, FileDown, ChevronDown } from 'lucide-react';
import { printElement, printCurrentPage, exportToPDF } from '@/utils/print';
export interface PrintButtonProps {
/** Element id to print. If omitted, prints the whole page. */
targetId?: string;
/** Filename suggestion for PDF export. */
filename?: string;
/** Additional Tailwind classes for the trigger button. */
className?: string;
}
export function PrintButton({
targetId,
filename = 'export',
className,
}: PrintButtonProps) {
const [open, setOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// Close dropdown when clicking outside
useEffect(() => {
if (!open) return;
const handleClickOutside = (e: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
setOpen(false);
}
};
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') setOpen(false);
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscape);
};
}, [open]);
const handlePrint = useCallback(() => {
setOpen(false);
if (targetId) {
printElement(targetId);
} else {
printCurrentPage();
}
}, [targetId]);
const handleExportPDF = useCallback(() => {
setOpen(false);
if (targetId) {
exportToPDF(targetId, filename);
} else {
// No specific element — use whole-page print which user can save as PDF
printCurrentPage();
}
}, [targetId, filename]);
return (
<div ref={containerRef} className="relative inline-block" data-no-print>
<button
type="button"
onClick={() => setOpen((v) => !v)}
className={clsx(
'inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-md',
'text-sm font-medium text-secondary-700',
'border border-secondary-300 bg-white',
'hover:bg-secondary-50 transition-colors',
'min-h-touch',
open && 'ring-2 ring-primary-500 ring-offset-1',
className,
)}
aria-haspopup="menu"
aria-expanded={open}
aria-label="Drucken oder als PDF exportieren"
title="Drucken / PDF"
data-testid="print-button-trigger"
>
<Printer className="w-4 h-4" aria-hidden="true" strokeWidth={2} />
<span className="hidden sm:inline">Drucken</span>
<ChevronDown
className={clsx('w-3.5 h-3.5 transition-transform', open && 'rotate-180')}
aria-hidden="true"
strokeWidth={2}
/>
</button>
{open && (
<div
role="menu"
className={clsx(
'absolute right-0 mt-1 z-50',
'min-w-[180px] rounded-md border border-secondary-200',
'bg-white shadow-lg py-1',
)}
data-testid="print-button-dropdown"
>
<button
type="button"
onClick={handlePrint}
className={clsx(
'flex items-center gap-2 w-full px-3 py-2',
'text-sm text-secondary-700 hover:bg-secondary-50',
'transition-colors text-left',
)}
role="menuitem"
data-testid="print-button-print"
>
<Printer className="w-4 h-4 text-secondary-500" aria-hidden="true" strokeWidth={2} />
<span>Drucken</span>
</button>
<button
type="button"
onClick={handleExportPDF}
className={clsx(
'flex items-center gap-2 w-full px-3 py-2',
'text-sm text-secondary-700 hover:bg-secondary-50',
'transition-colors text-left',
)}
role="menuitem"
data-testid="print-button-pdf"
>
<FileDown className="w-4 h-4 text-secondary-500" aria-hidden="true" strokeWidth={2} />
<span>Als PDF</span>
</button>
</div>
)}
</div>
);
}