feat(#359): W4a Phase 2 — zentraler Import/Export-Dialog (Frontend)
- ImportExportDialog.tsx (neu): Modal lg/xl nach bestehendem ui/Modal-Muster
- Export-Tab: Formatauswahl (csv/xlsx/json), Download, Fehler-Handling
- Import-Tab: 4 Schritte (Datei -> Mapping -> Dry-Run -> Ausführung+Report),
Mapping-Vorschau mit Modul-Heuristik, Background-Job-Polling ab 1000 Zeilen
- i18n: 24 importexport.*-Keys in de.json + en.json (keine hardcoded Strings)
- Integration: ContactsList Toolbar-Button (contacts:read-Gate, Upload-Icon,
entityType=contacts vorgewählt) über bestehendes pluginToolbarStore-Muster
Gates: Vitest 12/12 (routePermissions + importExportDialog), tsc exit 0,
Production-Build exit 0 (vor Commit). 6 Failures in contacts/shell Suiten
als Vorbestand bewiesen (Stash-Test: identisch auf clean HEAD f27f047).
fixes #359 (Phase 2)
This commit is contained in:
@@ -19,7 +19,8 @@ import { SavedFilters } from '@/components/SavedFilters';
|
||||
import { SavedFilterBar } from '@/components/common/SavedFilterBar';
|
||||
import { TagSelector } from '@/components/tags/TagSelector';
|
||||
import type { Tag } from '@/api/tags';
|
||||
import { ArrowDownAZ, ArrowUpZA, Bookmark, ChevronLeft, ExternalLink, LayoutGrid, List, Plus, Printer, Table2, X } from 'lucide-react';
|
||||
import { ArrowDownAZ, ArrowUpZA, Bookmark, ChevronLeft, ExternalLink, LayoutGrid, List, Plus, Printer, Table2, Upload, X } from 'lucide-react';
|
||||
import { ImportExportDialog } from '@/components/importexport/ImportExportDialog';
|
||||
import { FilterPanel, applyFilters, emptyFilterState, type FilterState, type SavedFilter } from '@/components/contacts/FilterPanel';
|
||||
import { SortPanel, applySorting, emptySortState, type SortState } from '@/components/contacts/SortPanel';
|
||||
import { GroupPanel, applyGrouping, emptyGroupState, type GroupState, type GroupedContacts } from '@/components/contacts/GroupPanel';
|
||||
@@ -222,6 +223,9 @@ export function ContactsListPage() {
|
||||
setActiveView('detail');
|
||||
}, []);
|
||||
|
||||
// Import/Export dialog (W4a #359)
|
||||
const [importExportOpen, setImportExportOpen] = useState(false);
|
||||
|
||||
// Handle create
|
||||
const handleCreate = useCallback(() => {
|
||||
const windowId = openWindow({
|
||||
@@ -374,6 +378,16 @@ export function ContactsListPage() {
|
||||
icon: <Plus className="w-3.5 h-3.5" strokeWidth={2} />,
|
||||
onClick: handleCreate,
|
||||
}] : []),
|
||||
// Import/Export dialog (W4a #359)
|
||||
...(canAccess('contacts:read') ? [{
|
||||
id: 'import-export',
|
||||
plugin: 'contacts',
|
||||
label: t('importexport.dialogTitle'),
|
||||
type: 'button' as const,
|
||||
group: 'create',
|
||||
icon: <Upload className="w-3.5 h-3.5" strokeWidth={2} />,
|
||||
onClick: () => setImportExportOpen(true),
|
||||
}] : []),
|
||||
// View mode dropdown (list / table / cards)
|
||||
{
|
||||
id: 'view-mode',
|
||||
@@ -782,6 +796,13 @@ export function ContactsListPage() {
|
||||
hasSort={sortState.conditions.length > 0}
|
||||
hasFolder={selectedFilter !== 'all' || multiSelectFolders.length > 0}
|
||||
/>
|
||||
|
||||
{/* Import/Export Dialog (W4a #359) */}
|
||||
<ImportExportDialog
|
||||
open={importExportOpen}
|
||||
onClose={() => setImportExportOpen(false)}
|
||||
entityType="contacts"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user