From 0724f3b1e72723552adb5e72b3ae30f57f005021 Mon Sep 17 00:00:00 2001 From: Thatsaphorn Atchariyaphap Date: Fri, 11 Jul 2025 20:21:45 +0200 Subject: [PATCH] Remove `callApi`, refactor API integrations, and adjust error handling - Delete unused `callApi` utility and related imports across components. - Replace `callApi` with direct `fetch` usage in `validateCustomer` and `addCustomer`. - Update `customerRoutes` to include `/api` prefix for consistency. - Refactor `useErrorHandler` to ensure comprehensive state management during errors. - Improve `ErrorBoundary` component text for better clarity in fallback UI. - Align `CustomersPage` logic with `useCallback` for optimized dependency management. --- .../app/api/customers/customerRoutes.ts | 8 ++--- .../app/api/customers/validate/route.ts | 10 +++++++ internal_frontend/app/customers/page.tsx | 8 +++-- .../customers/modal/NewCustomerModal.tsx | 11 +++---- .../components/error-boundary.tsx | 13 ++++++-- internal_frontend/lib/api/callApi.ts | 28 ----------------- internal_frontend/lib/api/serverCall.ts | 2 +- .../customers/usecases/addCustomer.ts | 28 +++++++++++++---- .../customers/usecases/validateCustomer.ts | 30 +++++++++++++++---- 9 files changed, 84 insertions(+), 54 deletions(-) create mode 100644 internal_frontend/app/api/customers/validate/route.ts delete mode 100644 internal_frontend/lib/api/callApi.ts diff --git a/internal_frontend/app/api/customers/customerRoutes.ts b/internal_frontend/app/api/customers/customerRoutes.ts index 974626c..d07c2f7 100644 --- a/internal_frontend/app/api/customers/customerRoutes.ts +++ b/internal_frontend/app/api/customers/customerRoutes.ts @@ -1,5 +1,5 @@ export const customerRoutes = { - create: "/customers", - validate: "/customers/validate", - getById: (id: string) => `/customers/${id}`, -}; \ No newline at end of file + create: "/api/customers", + validate: "/api/customers/validate", + getById: (id: string) => `/api/customers/${id}`, +}; diff --git a/internal_frontend/app/api/customers/validate/route.ts b/internal_frontend/app/api/customers/validate/route.ts new file mode 100644 index 0000000..802b597 --- /dev/null +++ b/internal_frontend/app/api/customers/validate/route.ts @@ -0,0 +1,10 @@ +import {NextRequest, NextResponse} from "next/server"; +import {serverCall} from "@/lib/api/serverCall"; +import {customerRoutes} from "@/app/api/customers/customerRoutes"; + +export async function POST(req: NextRequest) { + const body = await req.json(); + const result = await serverCall(customerRoutes.validate, "POST", body); + const data = await result.json(); + return NextResponse.json(data); +} diff --git a/internal_frontend/app/customers/page.tsx b/internal_frontend/app/customers/page.tsx index a82c389..9974d55 100644 --- a/internal_frontend/app/customers/page.tsx +++ b/internal_frontend/app/customers/page.tsx @@ -26,6 +26,7 @@ import {Customer} from "@/services/customers/entities/customer"; import Link from "next/link"; import {useErrorHandler} from "@/components/error-boundary"; import {showError, showInfoToast} from "@/lib/ui/showToast"; +import {useCallback} from "react"; export default function CustomersPage() { const [customers, setCustomers] = useState([]); @@ -35,7 +36,8 @@ export default function CustomersPage() { const pageSize = 15; const handleError = useErrorHandler(); - const loadCustomers = async () => { + // Wrap the loadCustomers function with useCallback + const loadCustomers = useCallback(async () => { setLoading(true); try { const response = await fetch('/api/customers'); @@ -53,11 +55,11 @@ export default function CustomersPage() { } finally { setLoading(false); } - }; + }, [handleError]); // Include handleError as a dependency useEffect(() => { loadCustomers(); - }, [handleError]); + }, [loadCustomers]); // Add loadCustomers to the dependency array const filtered = useMemo(() => { if (customers.length === 0) return []; diff --git a/internal_frontend/components/customers/modal/NewCustomerModal.tsx b/internal_frontend/components/customers/modal/NewCustomerModal.tsx index c9d369b..9b127b6 100644 --- a/internal_frontend/components/customers/modal/NewCustomerModal.tsx +++ b/internal_frontend/components/customers/modal/NewCustomerModal.tsx @@ -46,9 +46,9 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) { city: string; }; - const emailExists = matches.some(m => m.email.toLowerCase() === email.toLowerCase()); - const companyExists = matches.some(m => m.companyName.toLowerCase() === companyName.toLowerCase()); - const addressExists = matches.some(m => + const emailExists = Array.isArray(matches) && matches.some(m => m.email.toLowerCase() === email.toLowerCase()); + const companyExists = Array.isArray(matches) && matches.some(m => m.companyName.toLowerCase() === companyName.toLowerCase()); + const addressExists = Array.isArray(matches) && matches.some(m => m.street.toLowerCase() === street.toLowerCase() && m.zip.toLowerCase() === zip.toLowerCase() && m.city.toLowerCase() === city.toLowerCase() @@ -57,9 +57,10 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) { const validateField = async () => { try { const result = await validateCustomer({email, companyName, street, zip, city}); - setMatches(result); + setMatches(result || []); showInfoToast("Datenvalidierung abgeschlossen"); } catch (err) { + setMatches([]); // Ensure matches is always an array handleError(err); } }; @@ -256,7 +257,7 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) { {step === 1 ? ( diff --git a/internal_frontend/components/error-boundary.tsx b/internal_frontend/components/error-boundary.tsx index e8d4219..af4b6d8 100644 --- a/internal_frontend/components/error-boundary.tsx +++ b/internal_frontend/components/error-boundary.tsx @@ -99,9 +99,9 @@ function ErrorDialog({error, onReset, open}: ErrorDialogProps) { }}> - Ein Fehler ist aufgetreten + Uncaught Error! - Es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es erneut. + Es ist ein unerwarteter Fehler aufgetreten. @@ -128,7 +128,16 @@ function ErrorDialog({error, onReset, open}: ErrorDialogProps) { // Hook for handling async errors export function useErrorHandler() { + const [errorState, setErrorState] = React.useState(); + + console.log('Current state:', errorState); + return React.useCallback((error: unknown) => { console.error("Async error caught:", error); + + // Trigger a re-render that will throw the error and be caught by the error boundary + setErrorState(() => { + throw error instanceof Error ? error : new Error(String(error)); + }); }, []); } diff --git a/internal_frontend/lib/api/callApi.ts b/internal_frontend/lib/api/callApi.ts deleted file mode 100644 index 1b89a1a..0000000 --- a/internal_frontend/lib/api/callApi.ts +++ /dev/null @@ -1,28 +0,0 @@ -// lib/api/callApi.ts -import {serverCall} from "@/lib/api/serverCall"; - -export async function callApi( - path: string, - method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", - body?: TRequest -): Promise { - const res = await serverCall(path, method, body); - - const contentType = res.headers.get("content-type") ?? ""; - const isJson = contentType.includes("application/json"); - - const rawBody = isJson ? await res.json() : await res.text(); - - console.log(`[api ${path}] Response:`, res.status, rawBody); - - if (!res.ok) { - const errorMessage = isJson - ? (rawBody?.message ?? rawBody?.errors?.join(", ")) ?? "Unbekannter Fehler" - : String(rawBody); - - console.error(`[api ${path}] Error:`, errorMessage); - throw new Error(errorMessage); - } - - return rawBody as TResponse; -} diff --git a/internal_frontend/lib/api/serverCall.ts b/internal_frontend/lib/api/serverCall.ts index a470764..2d329ed 100644 --- a/internal_frontend/lib/api/serverCall.ts +++ b/internal_frontend/lib/api/serverCall.ts @@ -7,7 +7,7 @@ export async function serverCall( method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", body?: unknown ): Promise { - const url = `${process.env.INTERNAL_BACKEND_URL ?? "http://localhost:8080"}/api${path}`; + const url = `${process.env.INTERNAL_BACKEND_URL ?? "http://localhost:8080"}${path}`; const session = await getServerSession(authOptions); const headers: Record = { diff --git a/internal_frontend/services/customers/usecases/addCustomer.ts b/internal_frontend/services/customers/usecases/addCustomer.ts index 82a0377..3d80309 100644 --- a/internal_frontend/services/customers/usecases/addCustomer.ts +++ b/internal_frontend/services/customers/usecases/addCustomer.ts @@ -1,8 +1,4 @@ -"use server"; - import {CreateCustomerDto} from "@/services/customers/dtos/createCustomer.dto"; -import {customerRoutes} from "@/app/api/customers/customerRoutes"; -import {callApi} from "@/lib/api/callApi"; import {UUID} from "node:crypto"; export async function addCustomer(params: CreateCustomerDto): Promise { @@ -19,6 +15,28 @@ export async function addCustomer(params: CreateCustomerDto): Promise { notes: notes.map(({text}) => ({text})), }; - const response = await callApi(customerRoutes.create, "POST", payload); + const res = await fetch('/api/customers', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + + const contentType = res.headers.get("content-type") ?? ""; + const isJson = contentType.includes("application/json"); + + const rawBody = isJson ? await res.json() : await res.text(); + + console.log(`[api /api/customers] Response:`, res.status, rawBody); + + if (!res.ok) { + const errorMessage = isJson + ? (rawBody?.message ?? rawBody?.errors?.join(", ")) ?? "Unbekannter Fehler" + : String(rawBody); + throw new Error(errorMessage); + } + + const response = rawBody as UUID; console.log(response); } diff --git a/internal_frontend/services/customers/usecases/validateCustomer.ts b/internal_frontend/services/customers/usecases/validateCustomer.ts index abcd497..d440c69 100644 --- a/internal_frontend/services/customers/usecases/validateCustomer.ts +++ b/internal_frontend/services/customers/usecases/validateCustomer.ts @@ -1,7 +1,3 @@ -"use server"; - -import {callApi} from "@/lib/api/callApi"; -import {customerRoutes} from "@/app/api/customers/customerRoutes"; import {Customer} from "@/services/customers/entities/customer"; export async function validateCustomer(input: { @@ -15,5 +11,27 @@ export async function validateCustomer(input: { return []; } - return await callApi(customerRoutes.validate, "POST", input); -} \ No newline at end of file + const res = await fetch('/api/customers/validate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(input), + }); + + const contentType = res.headers.get("content-type") ?? ""; + const isJson = contentType.includes("application/json"); + + const rawBody = isJson ? await res.json() : await res.text(); + + console.log(`[api /api/customers/validate] Response:`, res.status, rawBody); + + if (!res.ok) { + const errorMessage = isJson + ? (rawBody?.message ?? rawBody?.errors?.join(", ")) ?? "Unbekannter Fehler" + : String(rawBody); + throw new Error(errorMessage); + } + + return rawBody as Customer[]; +}