From dda672d41eedd349367b0b00b1fc1bfb911c9e5d Mon Sep 17 00:00:00 2001 From: Thomas Reitz Date: Thu, 12 Mar 2026 20:55:47 +0100 Subject: [PATCH] =?UTF-8?q?fix(crm):=20align=20Phase=202.2=E2=80=932.4=20f?= =?UTF-8?q?rontend=20to=20actual=20backend=20contracts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import: convert mapping Record→Array, entityType PERSON→contact - Enrichment: handle suggestions as Record - Types: add availableTargetFields to ImportPreviewResponse Co-Authored-By: Claude Sonnet 4.6 --- packages/frontend/src/crm/api.ts | 23 +++++++++++++++---- .../src/crm/companies/CompanyDetailPage.tsx | 6 ++++- packages/frontend/src/crm/types.ts | 15 +++++++++--- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/crm/api.ts b/packages/frontend/src/crm/api.ts index 250ea2a..5c8eb0b 100644 --- a/packages/frontend/src/crm/api.ts +++ b/packages/frontend/src/crm/api.ts @@ -613,11 +613,15 @@ export const customFieldsApi = { // --- Import --- +/** Backend erwartet lowercase entity-type-Bezeichner */ +const toImportEntityType = (t: ImportEntityType): string => + t === 'PERSON' ? 'contact' : t === 'COMPANY' ? 'company' : 'deal'; + export const importApi = { preview: (file: File, entityType: ImportEntityType) => { const form = new FormData(); form.append('file', file); - form.append('entityType', entityType); + form.append('entityType', toImportEntityType(entityType)); return api .post<{ success: boolean; data: ImportPreviewResponse; meta: { timestamp: string } }>( '/crm/import/preview', @@ -627,13 +631,22 @@ export const importApi = { .then((r) => r.data); }, - execute: (data: ImportExecuteRequest) => - api + execute: (data: ImportExecuteRequest) => { + // 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 } }>( '/crm/import/execute', - data, + { + ...data, + entityType: toImportEntityType(data.entityType), + mapping: mappingArray, + }, ) - .then((r) => r.data), + .then((r) => r.data); + }, }; // --- Enrichment --- diff --git a/packages/frontend/src/crm/companies/CompanyDetailPage.tsx b/packages/frontend/src/crm/companies/CompanyDetailPage.tsx index 6e94e3c..3683005 100644 --- a/packages/frontend/src/crm/companies/CompanyDetailPage.tsx +++ b/packages/frontend/src/crm/companies/CompanyDetailPage.tsx @@ -197,7 +197,11 @@ export function CompanyDetailPage() { onClick={() => { enrichCompany.mutate(company.id, { onSuccess: (res) => { - setEnrichSuggestions(res.data.suggestions); + // Backend gibt suggestions als Record zurück + const suggestions = Object.entries(res.data.suggestions).map( + ([field, s]) => ({ field, ...s }), + ); + setEnrichSuggestions(suggestions); setEnrichOpen(true); }, }); diff --git a/packages/frontend/src/crm/types.ts b/packages/frontend/src/crm/types.ts index 2de2a99..a8f2575 100644 --- a/packages/frontend/src/crm/types.ts +++ b/packages/frontend/src/crm/types.ts @@ -860,6 +860,7 @@ export interface ImportPreviewResponse { rows: Record[]; totalRows: number; format: string; + availableTargetFields?: string[]; } export interface ImportExecuteRequest { @@ -888,6 +889,7 @@ export interface ImportExecuteResponse { // --- Enrichment --- +/** Einzelner Vorschlag (normalisiert für die UI) */ export interface EnrichmentSuggestion { field: string; current: string | null; @@ -895,12 +897,19 @@ export interface EnrichmentSuggestion { source: string; } -export interface EnrichmentResponse { - source: string; - suggestions: EnrichmentSuggestion[]; +/** Backend-Antwort: suggestions als Record */ +export interface EnrichmentResponseRaw { + companyId?: string; + companyName?: string; + sources?: string[]; + suggestions: Record; enrichedAt: string; + warnings?: string[]; } +/** Normalisierte Variante für die UI (backward-compat alias) */ +export type EnrichmentResponse = EnrichmentResponseRaw; + export interface EnrichmentConfig { apiKey: string; configured: boolean;