From 184ecaad616fc018b3f0e0c2c8fca9a372ce791e Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Sun, 19 Apr 2026 17:17:41 +0200 Subject: [PATCH] feat(receipt-import): refactor product creation and category update to use server actions --- frontend/app/kvitto/ReceiptImportClient.tsx | 21 +++-------- frontend/app/kvitto/actions.ts | 41 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 frontend/app/kvitto/actions.ts diff --git a/frontend/app/kvitto/ReceiptImportClient.tsx b/frontend/app/kvitto/ReceiptImportClient.tsx index f08eee86..c593a83a 100644 --- a/frontend/app/kvitto/ReceiptImportClient.tsx +++ b/frontend/app/kvitto/ReceiptImportClient.tsx @@ -195,26 +195,15 @@ export default function ReceiptImportClient({ isAdmin }: { isAdmin: boolean }) { // eslint-disable-next-line no-console console.log('handleCreateProduct: isAdmin =', isAdmin, 'endpoint = /api/products'); try { - // Admin skapar aktiv produkt direkt - const createRes = await fetch('/api/products', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: row.rawName }), - }); - if (!createRes.ok) { - const e = await createRes.json().catch(() => ({})); - throw new Error(e.message ?? `HTTP ${createRes.status}`); - } - const product = await createRes.json() as { id: number; name: string; canonicalName: string | null }; + const { createProductAction, updateProductCategoryAction } = await import('./actions'); + + // Admin skapar aktiv produkt direkt via Server Action + const product = await createProductAction(row.rawName); // Sätt kategori: AI-förslag har prioritet, annars manuellt val const categoryId = row.categorySuggestion?.categoryId ?? (row.selectedCategoryId !== '' ? row.selectedCategoryId : null); if (categoryId) { - await fetch(`/api/products/${product.id}`, { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ categoryId }), - }); + await updateProductCategoryAction(product.id, categoryId); } // Uppdatera produktlistan lokalt diff --git a/frontend/app/kvitto/actions.ts b/frontend/app/kvitto/actions.ts new file mode 100644 index 00000000..ca4f5815 --- /dev/null +++ b/frontend/app/kvitto/actions.ts @@ -0,0 +1,41 @@ +'use server'; + +import { getAuthHeaders } from '@/lib/auth-headers'; + +const API_BASE = process.env.NEXT_PUBLIC_API_URL_INTERNAL || 'http://recipe-api:8080'; + +export async function createProductAction(name: string) { + const authHeaders = await getAuthHeaders(); + console.log('[createProductAction] Creating product with name:', name); + console.log('[createProductAction] Auth headers:', authHeaders ? 'YES' : 'NO'); + + const res = await fetch(`${API_BASE}/api/products`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...authHeaders }, + body: JSON.stringify({ name }), + }); + + if (!res.ok) { + const e = await res.json().catch(() => ({})); + throw new Error(e.message ?? `HTTP ${res.status}`); + } + + return res.json(); +} + +export async function updateProductCategoryAction(productId: number, categoryId: number) { + const authHeaders = await getAuthHeaders(); + + const res = await fetch(`${API_BASE}/api/products/${productId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json', ...authHeaders }, + body: JSON.stringify({ categoryId }), + }); + + if (!res.ok) { + const e = await res.json().catch(() => ({})); + throw new Error(e.message ?? `HTTP ${res.status}`); + } + + return res.json(); +}