| 1406 | * Upload a CSV file to create a new table with inferred schema. |
| 1407 | */ |
| 1408 | export function useUploadCsvToTable() { |
| 1409 | const queryClient = useQueryClient() |
| 1410 | |
| 1411 | return useMutation({ |
| 1412 | mutationFn: async ({ workspaceId, file }: UploadCsvParams) => { |
| 1413 | // Text fields must precede the file part: the server parses the body as a |
| 1414 | // stream and needs workspaceId before it reaches the (large) file. |
| 1415 | const formData = new FormData() |
| 1416 | formData.append('workspaceId', workspaceId) |
| 1417 | formData.append('file', file) |
| 1418 | |
| 1419 | // boundary-raw-fetch: multipart/form-data CSV upload, requestJson only supports JSON bodies |
| 1420 | const response = await fetch('/api/table/import-csv', { |
| 1421 | method: 'POST', |
| 1422 | body: formData, |
| 1423 | }) |
| 1424 | |
| 1425 | if (!response.ok) { |
| 1426 | const data = await response.json().catch(() => ({})) |
| 1427 | throw new Error(data.error || 'CSV import failed') |
| 1428 | } |
| 1429 | |
| 1430 | return response.json() |
| 1431 | }, |
| 1432 | onError: (error) => { |
| 1433 | logger.error('Failed to upload CSV:', error) |
| 1434 | toast.error(error.message, { duration: 5000 }) |
| 1435 | }, |
| 1436 | onSettled: () => { |
| 1437 | queryClient.invalidateQueries({ queryKey: tableKeys.lists() }) |
| 1438 | }, |
| 1439 | }) |
| 1440 | } |
| 1441 | |
| 1442 | interface ImportCsvAsyncParams { |
| 1443 | workspaceId: string |