Customer Detail Page and Enhance dynamic breadcrumbs
This commit is contained in:
170
internal_frontend/app/customers/[id]/page.tsx
Normal file
170
internal_frontend/app/customers/[id]/page.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
"use client";
|
||||
|
||||
import React, {useEffect, useState, useMemo} from "react";
|
||||
import {useParams, useRouter} from "next/navigation";
|
||||
import {ChevronLeft} from "lucide-react";
|
||||
import axios, {AxiosError} from "axios";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Dialog} from "@/components/ui/dialog";
|
||||
import {Skeleton} from "@/components/ui/skeleton";
|
||||
import CustomerDetailContent from "@/components/customers/details/CustomerDetailContent";
|
||||
import {Customer} from "@/services/customers/entities/customer";
|
||||
import CustomerInformationContent from "@/components/customers/details/sub/ContactInformationContent";
|
||||
import CustomerPhoneNumberContent from "@/components/customers/details/sub/CustomerPhoneNumberContent";
|
||||
import CustomerNotesContent from "@/components/customers/details/sub/CustomerNotesContent";
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api';
|
||||
|
||||
export default function CustomerDetailPage() {
|
||||
const router = useRouter();
|
||||
const {id} = useParams<{ id: string }>();
|
||||
const [customer, setCustomer] = useState<Customer | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [dialogContent, setDialogContent] = useState<React.ReactNode | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
setError("Keine Kunden-ID angegeben");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let isMounted = true;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
axios
|
||||
.get<Customer>(`${API_BASE_URL}/customers/${id}`)
|
||||
.then((res) => {
|
||||
if (isMounted) {
|
||||
setCustomer(res.data);
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (isMounted) {
|
||||
console.error('Error fetching customer:', error);
|
||||
setCustomer(null);
|
||||
setError(
|
||||
error.response?.status === 404
|
||||
? "Kunde nicht gefunden"
|
||||
: "Fehler beim Laden der Kundendaten"
|
||||
);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (isMounted) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
const handleOpenDialog = (content: React.ReactNode) => {
|
||||
setDialogContent(content);
|
||||
setOpenDialog(true);
|
||||
};
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
try {
|
||||
const formattedDate = new Date(date).toLocaleDateString('de-DE', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
return formattedDate === 'Invalid Date' ? '-' : formattedDate;
|
||||
} catch (error) {
|
||||
console.error('Error formatting date:', error);
|
||||
return '-';
|
||||
}
|
||||
};
|
||||
|
||||
const customerMetadata = useMemo(() => ({
|
||||
createdInfo: customer
|
||||
? `Erstellt von ${customer.createdBy || "-"} am ${formatDate(customer.createdAt)}`
|
||||
: "Erstellt von - am -",
|
||||
lastActivityInfo: customer
|
||||
? `Letzte Aktivität: ${customer.updatedBy || "-"} am ${formatDate(customer.updatedAt)}`
|
||||
: "Letzte Aktivität: - am -"
|
||||
}), [customer]);
|
||||
|
||||
const renderMetadata = () => {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<div className="space-y-1">
|
||||
<Skeleton className="h-4 w-[250px]"/>
|
||||
<Skeleton className="h-4 w-[280px]"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-end gap-1 text-xs text-muted-foreground">
|
||||
<div>{customerMetadata.createdInfo}</div>
|
||||
<div>{customerMetadata.lastActivityInfo}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-full p-6 space-y-4 text-sm">
|
||||
<div className="flex justify-between items-start">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => router.push("/customers")}
|
||||
disabled={loading}
|
||||
aria-label="Zurück zur Kundenliste"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 mr-1"/>
|
||||
Zurück
|
||||
</Button>
|
||||
|
||||
{renderMetadata()}
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<div className="text-center text-red-500 p-4" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
) : (
|
||||
<CustomerDetailContent
|
||||
loading={loading}
|
||||
customer={customer}
|
||||
sections={
|
||||
customer
|
||||
? [
|
||||
<CustomerInformationContent
|
||||
key="customerInformation"
|
||||
customer={customer}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>,
|
||||
<CustomerPhoneNumberContent
|
||||
key="customerPhoneNumberInfo"
|
||||
customer={customer}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>,
|
||||
<CustomerNotesContent
|
||||
key="customerNotes"
|
||||
customer={customer}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>
|
||||
]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
|
||||
{dialogContent}
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user