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.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
export const customerRoutes = {
|
export const customerRoutes = {
|
||||||
create: "/customers",
|
create: "/api/customers",
|
||||||
validate: "/customers/validate",
|
validate: "/api/customers/validate",
|
||||||
getById: (id: string) => `/customers/${id}`,
|
getById: (id: string) => `/api/customers/${id}`,
|
||||||
};
|
};
|
||||||
10
internal_frontend/app/api/customers/validate/route.ts
Normal file
10
internal_frontend/app/api/customers/validate/route.ts
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import {Customer} from "@/services/customers/entities/customer";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {useErrorHandler} from "@/components/error-boundary";
|
import {useErrorHandler} from "@/components/error-boundary";
|
||||||
import {showError, showInfoToast} from "@/lib/ui/showToast";
|
import {showError, showInfoToast} from "@/lib/ui/showToast";
|
||||||
|
import {useCallback} from "react";
|
||||||
|
|
||||||
export default function CustomersPage() {
|
export default function CustomersPage() {
|
||||||
const [customers, setCustomers] = useState<Customer[]>([]);
|
const [customers, setCustomers] = useState<Customer[]>([]);
|
||||||
@@ -35,7 +36,8 @@ export default function CustomersPage() {
|
|||||||
const pageSize = 15;
|
const pageSize = 15;
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
const loadCustomers = async () => {
|
// Wrap the loadCustomers function with useCallback
|
||||||
|
const loadCustomers = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/customers');
|
const response = await fetch('/api/customers');
|
||||||
@@ -53,11 +55,11 @@ export default function CustomersPage() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}, [handleError]); // Include handleError as a dependency
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadCustomers();
|
loadCustomers();
|
||||||
}, [handleError]);
|
}, [loadCustomers]); // Add loadCustomers to the dependency array
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
if (customers.length === 0) return [];
|
if (customers.length === 0) return [];
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) {
|
|||||||
city: string;
|
city: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const emailExists = matches.some(m => m.email.toLowerCase() === email.toLowerCase());
|
const emailExists = Array.isArray(matches) && matches.some(m => m.email.toLowerCase() === email.toLowerCase());
|
||||||
const companyExists = matches.some(m => m.companyName.toLowerCase() === companyName.toLowerCase());
|
const companyExists = Array.isArray(matches) && matches.some(m => m.companyName.toLowerCase() === companyName.toLowerCase());
|
||||||
const addressExists = matches.some(m =>
|
const addressExists = Array.isArray(matches) && matches.some(m =>
|
||||||
m.street.toLowerCase() === street.toLowerCase() &&
|
m.street.toLowerCase() === street.toLowerCase() &&
|
||||||
m.zip.toLowerCase() === zip.toLowerCase() &&
|
m.zip.toLowerCase() === zip.toLowerCase() &&
|
||||||
m.city.toLowerCase() === city.toLowerCase()
|
m.city.toLowerCase() === city.toLowerCase()
|
||||||
@@ -57,9 +57,10 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) {
|
|||||||
const validateField = async () => {
|
const validateField = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await validateCustomer({email, companyName, street, zip, city});
|
const result = await validateCustomer({email, companyName, street, zip, city});
|
||||||
setMatches(result);
|
setMatches(result || []);
|
||||||
showInfoToast("Datenvalidierung abgeschlossen");
|
showInfoToast("Datenvalidierung abgeschlossen");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
setMatches([]); // Ensure matches is always an array
|
||||||
handleError(err);
|
handleError(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -256,7 +257,7 @@ export function NewCustomerModal({ onCustomerCreated }: NewCustomerModalProps) {
|
|||||||
{step === 1 ? (
|
{step === 1 ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setStep(2)}
|
onClick={() => setStep(2)}
|
||||||
disabled={!email || !name || !companyName || phoneNumbers.length === 0 || !phoneNumbers.some(p => p.number.trim()) || emailExists}
|
// disabled={!email || !name || !companyName || phoneNumbers.length === 0 || !phoneNumbers.some(p => p.number.trim()) || emailExists}
|
||||||
>
|
>
|
||||||
Weiter
|
Weiter
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -99,9 +99,9 @@ function ErrorDialog({error, onReset, open}: ErrorDialogProps) {
|
|||||||
}}>
|
}}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Ein Fehler ist aufgetreten</DialogTitle>
|
<DialogTitle>Uncaught Error!</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es erneut.
|
Es ist ein unerwarteter Fehler aufgetreten.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -128,7 +128,16 @@ function ErrorDialog({error, onReset, open}: ErrorDialogProps) {
|
|||||||
|
|
||||||
// Hook for handling async errors
|
// Hook for handling async errors
|
||||||
export function useErrorHandler() {
|
export function useErrorHandler() {
|
||||||
|
const [errorState, setErrorState] = React.useState();
|
||||||
|
|
||||||
|
console.log('Current state:', errorState);
|
||||||
|
|
||||||
return React.useCallback((error: unknown) => {
|
return React.useCallback((error: unknown) => {
|
||||||
console.error("Async error caught:", error);
|
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));
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// lib/api/callApi.ts
|
|
||||||
import {serverCall} from "@/lib/api/serverCall";
|
|
||||||
|
|
||||||
export async function callApi<TResponse, TRequest = unknown>(
|
|
||||||
path: string,
|
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
|
||||||
body?: TRequest
|
|
||||||
): Promise<TResponse> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ export async function serverCall(
|
|||||||
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
|
||||||
body?: unknown
|
body?: unknown
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
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 session = await getServerSession(authOptions);
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
import {CreateCustomerDto} from "@/services/customers/dtos/createCustomer.dto";
|
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";
|
import {UUID} from "node:crypto";
|
||||||
|
|
||||||
export async function addCustomer(params: CreateCustomerDto): Promise<void> {
|
export async function addCustomer(params: CreateCustomerDto): Promise<void> {
|
||||||
@@ -19,6 +15,28 @@ export async function addCustomer(params: CreateCustomerDto): Promise<void> {
|
|||||||
notes: notes.map(({text}) => ({text})),
|
notes: notes.map(({text}) => ({text})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await callApi<UUID>(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);
|
console.log(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
import {Customer} from "@/services/customers/entities/customer";
|
||||||
|
|
||||||
export async function validateCustomer(input: {
|
export async function validateCustomer(input: {
|
||||||
@@ -15,5 +11,27 @@ export async function validateCustomer(input: {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return await callApi<Customer[]>(customerRoutes.validate, "POST", input);
|
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[];
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user