fix(crm): align Phase 2.2–2.4 frontend to actual backend contracts

- Import: convert mapping Record→Array, entityType PERSON→contact
- Enrichment: handle suggestions as Record<field,{current,suggested,source}>
- Types: add availableTargetFields to ImportPreviewResponse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Reitz 2026-03-12 20:55:47 +01:00
parent 219863d538
commit dda672d41e
3 changed files with 35 additions and 9 deletions

View file

@ -613,11 +613,15 @@ export const customFieldsApi = {
// --- Import --- // --- Import ---
/** Backend erwartet lowercase entity-type-Bezeichner */
const toImportEntityType = (t: ImportEntityType): string =>
t === 'PERSON' ? 'contact' : t === 'COMPANY' ? 'company' : 'deal';
export const importApi = { export const importApi = {
preview: (file: File, entityType: ImportEntityType) => { preview: (file: File, entityType: ImportEntityType) => {
const form = new FormData(); const form = new FormData();
form.append('file', file); form.append('file', file);
form.append('entityType', entityType); form.append('entityType', toImportEntityType(entityType));
return api return api
.post<{ success: boolean; data: ImportPreviewResponse; meta: { timestamp: string } }>( .post<{ success: boolean; data: ImportPreviewResponse; meta: { timestamp: string } }>(
'/crm/import/preview', '/crm/import/preview',
@ -627,13 +631,22 @@ export const importApi = {
.then((r) => r.data); .then((r) => r.data);
}, },
execute: (data: ImportExecuteRequest) => execute: (data: ImportExecuteRequest) => {
api // Backend erwartet mapping als Array [{sourceColumn, targetField}]
const mappingArray = Object.entries(data.mapping)
.filter(([, target]) => target)
.map(([sourceColumn, targetField]) => ({ sourceColumn, targetField }));
return api
.post<{ success: boolean; data: ImportExecuteResponse; meta: { timestamp: string } }>( .post<{ success: boolean; data: ImportExecuteResponse; meta: { timestamp: string } }>(
'/crm/import/execute', '/crm/import/execute',
data, {
...data,
entityType: toImportEntityType(data.entityType),
mapping: mappingArray,
},
) )
.then((r) => r.data), .then((r) => r.data);
},
}; };
// --- Enrichment --- // --- Enrichment ---

View file

@ -197,7 +197,11 @@ export function CompanyDetailPage() {
onClick={() => { onClick={() => {
enrichCompany.mutate(company.id, { enrichCompany.mutate(company.id, {
onSuccess: (res) => { onSuccess: (res) => {
setEnrichSuggestions(res.data.suggestions); // Backend gibt suggestions als Record<field, {current, suggested, source}> zurück
const suggestions = Object.entries(res.data.suggestions).map(
([field, s]) => ({ field, ...s }),
);
setEnrichSuggestions(suggestions);
setEnrichOpen(true); setEnrichOpen(true);
}, },
}); });

View file

@ -860,6 +860,7 @@ export interface ImportPreviewResponse {
rows: Record<string, string>[]; rows: Record<string, string>[];
totalRows: number; totalRows: number;
format: string; format: string;
availableTargetFields?: string[];
} }
export interface ImportExecuteRequest { export interface ImportExecuteRequest {
@ -888,6 +889,7 @@ export interface ImportExecuteResponse {
// --- Enrichment --- // --- Enrichment ---
/** Einzelner Vorschlag (normalisiert für die UI) */
export interface EnrichmentSuggestion { export interface EnrichmentSuggestion {
field: string; field: string;
current: string | null; current: string | null;
@ -895,12 +897,19 @@ export interface EnrichmentSuggestion {
source: string; source: string;
} }
export interface EnrichmentResponse { /** Backend-Antwort: suggestions als Record<field, {...}> */
source: string; export interface EnrichmentResponseRaw {
suggestions: EnrichmentSuggestion[]; companyId?: string;
companyName?: string;
sources?: string[];
suggestions: Record<string, { current: string | null; suggested: string | null; source: string }>;
enrichedAt: string; enrichedAt: string;
warnings?: string[];
} }
/** Normalisierte Variante für die UI (backward-compat alias) */
export type EnrichmentResponse = EnrichmentResponseRaw;
export interface EnrichmentConfig { export interface EnrichmentConfig {
apiKey: string; apiKey: string;
configured: boolean; configured: boolean;