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
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Upload, Download } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
import { ImportWizard } from '@/components/import-export/ImportWizard';
|
||||
import { ExportPanel } from '@/components/import-export/ExportPanel';
|
||||
|
||||
type Tab = 'import' | 'export';
|
||||
|
||||
export function ImportExportPage() {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<Tab>('import');
|
||||
|
||||
const tabs: { key: Tab; label: string; icon: React.ReactNode }[] = [
|
||||
{
|
||||
key: 'import',
|
||||
label: t('importExport.tabImport', 'Import'),
|
||||
icon: <Upload className="w-4 h-4" />,
|
||||
},
|
||||
{
|
||||
key: 'export',
|
||||
label: t('importExport.tabExport', 'Export'),
|
||||
icon: <Download className="w-4 h-4" />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Page header */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-secondary-900">
|
||||
{t('importExport.title', 'Import / Export')}
|
||||
</h1>
|
||||
<p className="text-sm text-secondary-500 mt-1">
|
||||
{t('importExport.subtitle', 'Daten importieren oder exportieren')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tab navigation */}
|
||||
<div className="border-b border-secondary-200">
|
||||
<nav className="flex gap-1" aria-label="Tabs">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => setActiveTab(tab.key)}
|
||||
className={clsx(
|
||||
'flex items-center gap-2 px-4 py-2.5 text-sm font-medium transition-colors border-b-2 -mb-px',
|
||||
activeTab === tab.key
|
||||
? 'border-primary-600 text-primary-700'
|
||||
: 'border-transparent text-secondary-500 hover:text-secondary-700 hover:border-secondary-300'
|
||||
)}
|
||||
aria-current={activeTab === tab.key ? 'page' : undefined}
|
||||
>
|
||||
{tab.icon}
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Tab content */}
|
||||
<div>
|
||||
{activeTab === 'import' && <ImportWizard />}
|
||||
{activeTab === 'export' && <ExportPanel />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user