Add project management support and integrate customer-project functionality
This commit is contained in:
37
internal_frontend/app/api/projects/[id]/route.ts
Normal file
37
internal_frontend/app/api/projects/[id]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
import {serverCall} from "@/lib/api/serverCall";
|
||||
import {projectRoutes} from "@/app/api/projects/projectRoutes";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// Extract project ID from the URL
|
||||
const segments = request.url.split("/");
|
||||
const projectId = segments.pop();
|
||||
|
||||
if (!projectId) {
|
||||
return NextResponse.json(
|
||||
{error: "Project ID is required"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
|
||||
// Perform server call to fetch the project details
|
||||
const response = await serverCall(projectRoutes.getById(projectId), "GET");
|
||||
|
||||
if (!response.ok) {
|
||||
return NextResponse.json(
|
||||
{error: "Project not found"},
|
||||
{status: response.status}
|
||||
);
|
||||
}
|
||||
|
||||
const project = await response.json();
|
||||
return NextResponse.json(project);
|
||||
} catch (error) {
|
||||
console.error("Error fetching project:", error);
|
||||
return NextResponse.json(
|
||||
{error: "Failed to fetch project"},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
import {serverCall} from "@/lib/api/serverCall";
|
||||
import {projectRoutes} from "@/app/api/projects/projectRoutes";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const segments = request.url.split('/');
|
||||
const id = segments[segments.indexOf('customer') + 1];
|
||||
const response = await serverCall(projectRoutes.getProjectByCustomerId(id), "GET");
|
||||
|
||||
if (!response.ok) {
|
||||
return NextResponse.json({error: "Customer not found"}, {status: 404});
|
||||
}
|
||||
|
||||
const customer = await response.json();
|
||||
return NextResponse.json(customer);
|
||||
}
|
||||
6
internal_frontend/app/api/projects/projectRoutes.ts
Normal file
6
internal_frontend/app/api/projects/projectRoutes.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const projectRoutes = {
|
||||
'create': '/api/projects',
|
||||
getById: (id: string) => `/api/projects/${id}`,
|
||||
getProjectByCustomerId: (customerId: string) => `/api/projects/customer/${customerId}`
|
||||
}
|
||||
;
|
||||
21
internal_frontend/app/api/projects/route.ts
Normal file
21
internal_frontend/app/api/projects/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {NextRequest, NextResponse} from "next/server";
|
||||
import {serverCall} from "@/lib/api/serverCall";
|
||||
import {projectRoutes} from "@/app/api/projects/projectRoutes";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
// Parse the incoming JSON payload
|
||||
const body = await req.json();
|
||||
|
||||
// Make a POST request to the backend using serverCall
|
||||
const response = await serverCall(projectRoutes.create, "POST", body);
|
||||
|
||||
// Parse and return the backend response
|
||||
const result = await response.json();
|
||||
return NextResponse.json(result);
|
||||
} catch (error) {
|
||||
// Handle errors gracefully
|
||||
console.error("Error creating project:", error);
|
||||
return NextResponse.json({error: "Failed to create project"}, {status: 500});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
apps
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import CustomerDetailContent from "@/components/customers/details/CustomerDetail
|
||||
import CustomerInformationContent from "@/components/customers/details/sub/ContactInformationContent";
|
||||
import CustomerPhoneNumberContent from "@/components/customers/details/sub/CustomerPhoneNumberContent";
|
||||
import CustomerNotesContent from "@/components/customers/details/sub/CustomerNotesContent";
|
||||
import CustomerProjectsContent from "@/components/customers/details/sub/CustomerProjectsContent";
|
||||
import {Customer} from "@/services/customers/entities/customer";
|
||||
|
||||
export default function CustomerDetailPage() {
|
||||
@@ -51,7 +52,7 @@ export default function CustomerDetailPage() {
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!isMounted) return;
|
||||
console.error('Error fetching customer:', error);
|
||||
console.error("Error fetching customer:", error);
|
||||
setCustomer(null);
|
||||
setError("Fehler beim Laden der Kundendaten");
|
||||
})
|
||||
@@ -73,17 +74,17 @@ export default function CustomerDetailPage() {
|
||||
|
||||
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'
|
||||
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;
|
||||
return formattedDate === "Invalid Date" ? "-" : formattedDate;
|
||||
} catch (error) {
|
||||
console.error('Error formatting date:', error);
|
||||
return '-';
|
||||
console.error("Error formatting date:", error);
|
||||
return "-";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,7 +94,7 @@ export default function CustomerDetailPage() {
|
||||
: "Erstellt von - am -",
|
||||
lastActivityInfo: customer
|
||||
? `Letzte Aktivität: ${customer.updatedBy || "-"} am ${formatDate(customer.updatedAt)}`
|
||||
: "Letzte Aktivität: - am -"
|
||||
: "Letzte Aktivität: - am -",
|
||||
};
|
||||
|
||||
const renderMetadata = () => {
|
||||
@@ -140,26 +141,29 @@ export default function CustomerDetailPage() {
|
||||
<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}
|
||||
/>
|
||||
]
|
||||
: []
|
||||
informationSection={
|
||||
<CustomerInformationContent
|
||||
customer={customer!}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>
|
||||
}
|
||||
phoneNumberSection={
|
||||
<CustomerPhoneNumberContent
|
||||
customer={customer!}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>
|
||||
}
|
||||
notesSection={
|
||||
<CustomerNotesContent
|
||||
customer={customer!}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>
|
||||
}
|
||||
projectsSection={
|
||||
<CustomerProjectsContent
|
||||
customer={customer!}
|
||||
handleOpenDialog={handleOpenDialog}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
@@ -169,4 +173,4 @@ export default function CustomerDetailPage() {
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
102
internal_frontend/app/projects/[id]/page.tsx
Normal file
102
internal_frontend/app/projects/[id]/page.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
"use client";
|
||||
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useParams, useRouter} from "next/navigation";
|
||||
import {ChevronLeft} from "lucide-react";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Skeleton} from "@/components/ui/skeleton";
|
||||
import {CustomerProject} from "@/services/projects/entities/customer-project";
|
||||
|
||||
export default function ProjectDetailPage() {
|
||||
const router = useRouter();
|
||||
const {id} = useParams<{ id: string }>();
|
||||
const [project, setProject] = useState<CustomerProject | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
setError("No project ID provided");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let isMounted = true;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
fetch(`/api/projects/${id}`)
|
||||
.then(async (response) => {
|
||||
if (!isMounted) return;
|
||||
|
||||
if (response.status === 404) {
|
||||
setError("Project not found");
|
||||
setProject(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch project data");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
setProject(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!isMounted) return;
|
||||
console.error("Error fetching project:", err);
|
||||
setProject(null);
|
||||
setError("Error loading project data");
|
||||
})
|
||||
.finally(() => {
|
||||
if (isMounted) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<Skeleton className="w-full h-8 mb-4"/>
|
||||
<Skeleton className="w-full h-6 mb-2"/>
|
||||
<Skeleton className="w-3/4 h-6"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="p-6 text-red-500">
|
||||
<p>{error}</p>
|
||||
<Button variant="ghost" onClick={() => router.back()}>
|
||||
<ChevronLeft className="w-4 h-4 mr-1"/>
|
||||
Go back
|
||||
</Button>
|
||||
</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.back()}
|
||||
aria-label="Go back"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4 mr-1"/>
|
||||
Zurück
|
||||
</Button>
|
||||
</div>
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">{project?.name}</h1>
|
||||
<p className="text-lg">{project?.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
internal_frontend/app/projects/page.tsx
Normal file
7
internal_frontend/app/projects/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function ProjectsPage() {
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold">Project Overview</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user