diff --git a/src/app/contracts/[id]/page.js b/src/app/contracts/[id]/page.js index 43f1fa7..6685671 100644 --- a/src/app/contracts/[id]/page.js +++ b/src/app/contracts/[id]/page.js @@ -3,6 +3,12 @@ import { useEffect, useState } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; +import { Card, CardHeader, CardContent } from "@/components/ui/Card"; +import Button from "@/components/ui/Button"; +import Badge from "@/components/ui/Badge"; +import PageContainer from "@/components/ui/PageContainer"; +import PageHeader from "@/components/ui/PageHeader"; +import { LoadingState } from "@/components/ui/States"; export default function ContractDetailsPage() { const params = useParams(); @@ -41,166 +47,352 @@ export default function ContractDetailsPage() { fetchContractDetails(); } }, [contractId]); - if (loading) { return ( -
-
Ładowanie szczegółów umowy...
-
+ + + ); } if (!contract) { return ( -
-
Nie znaleziono umowy.
-
- - ← Powrót do listy umów - -
-
+ + + +

Contract not found.

+ + + +
+
+
); } - return ( -
- {/* Header */} -
-

Umowa {contract.contract_number}

- - ← Powrót do listy umów - -
+ + + + + + + + +
+ } + /> {/* Contract Details */} -
-

Szczegóły umowy

-
-
- -

{contract.contract_number}

-
- - {contract.contract_name && ( -
- -

{contract.contract_name}

-
- )} - - {contract.customer_contract_number && ( -
- -

{contract.customer_contract_number}

-
- )} - - {contract.customer && ( -
- -

{contract.customer}

-
- )} - - {contract.investor && ( -
- -

{contract.investor}

-
- )} - - {contract.date_signed && ( -
- -

- {new Date(contract.date_signed).toLocaleDateString("pl-PL")} -

-
- )} - - {contract.finish_date && ( -
- -

- {new Date(contract.finish_date).toLocaleDateString("pl-PL")} -

-
- )} -
-
- - {/* Linked Projects */} -
-
-

- Projekty w ramach umowy ({projects.length}) -

- - ➕ Dodaj projekt - -
- - {projects.length === 0 ? ( -

- Brak projektów przypisanych do tej umowy. -

- ) : ( -
- {projects.map((project) => ( -
-
-
-

{project.project_name}

-

- {project.project_number} -

- {project.address && ( -

- 📍 {project.address} -

- )} - {project.finish_date && ( -

- ⏰ Termin:{" "} - {new Date(project.finish_date).toLocaleDateString( - "pl-PL" - )} -

- )} -
- - Szczegóły → - +
+
+ + +

+ Contract Information +

+
+ +
+
+ + Contract Number + +

+ {contract.contract_number} +

+ + {contract.contract_name && ( +
+ + Contract Name + +

+ {contract.contract_name} +

+
+ )} + + {contract.customer_contract_number && ( +
+ + Customer Contract Number + +

+ {contract.customer_contract_number} +

+
+ )} + + {contract.customer && ( +
+ + Customer + +

+ {contract.customer} +

+
+ )} + + {contract.investor && ( +
+ + Investor + +

+ {contract.investor} +

+
+ )} + + {contract.date_signed && ( +
+ + Date Signed + +

+ {new Date(contract.date_signed).toLocaleDateString( + "en-US" + )} +

+
+ )} + + {contract.finish_date && ( +
+ + Finish Date + +

+ {new Date(contract.finish_date).toLocaleDateString( + "en-US" + )} +

+
+ )}
- ))} -
- )} + + +
+ + {/* Contract Summary Sidebar */} +
+ + +

Summary

+
+ +
+ + Projects Count + + + {projects.length} Projects + +
+ + {contract.finish_date && ( +
+ + Contract Status + + new Date() + ? "success" + : "warning" + } + size="md" + > + {new Date(contract.finish_date) > new Date() + ? "Active" + : "Expired"} + +
+ )} +
+
+
-
+ + {/* Associated Projects */} + + +
+

+ Associated Projects ({projects.length}) +

+ + + +
+
+ + {projects.length === 0 ? ( +
+
+ + + +
+

+ No projects yet +

+

+ Get started by creating your first project for this contract +

+ + + +
+ ) : ( +
+ {projects.map((project) => ( +
+
+
+
+

+ {project.project_name} +

+ + {project.project_number} + +
+ +
+ {project.address && ( +
+ + + + {project.address} +
+ )} + {project.finish_date && ( +
+ + + + {new Date(project.finish_date).toLocaleDateString( + "en-US" + )} +
+ )} +
+ + {project.project_status === "registered" + ? "Registered" + : project.project_status === "in_progress_design" + ? "In Progress (Design)" + : project.project_status === + "in_progress_construction" + ? "In Progress (Construction)" + : project.project_status === "fulfilled" + ? "Completed" + : "Unknown"} + +
+
+
+ + + +
+
+ ))} +
+ )} +
+
+ ); } diff --git a/src/app/contracts/new/page.js b/src/app/contracts/new/page.js index 953814f..ffa0503 100644 --- a/src/app/contracts/new/page.js +++ b/src/app/contracts/new/page.js @@ -1,10 +1,39 @@ import ContractForm from "@/components/ContractForm"; +import PageContainer from "@/components/ui/PageContainer"; +import PageHeader from "@/components/ui/PageHeader"; +import Button from "@/components/ui/Button"; +import Link from "next/link"; export default function NewContractPage() { return ( -
-

Nowa umowa

- -
+ + + + + } + /> +
+ +
+
); } diff --git a/src/app/projects/[id]/edit/page.js b/src/app/projects/[id]/edit/page.js index adc6680..aae2915 100644 --- a/src/app/projects/[id]/edit/page.js +++ b/src/app/projects/[id]/edit/page.js @@ -1,4 +1,8 @@ import ProjectForm from "@/components/ProjectForm"; +import PageContainer from "@/components/ui/PageContainer"; +import PageHeader from "@/components/ui/PageHeader"; +import Button from "@/components/ui/Button"; +import Link from "next/link"; export default async function EditProjectPage({ params }) { const { id } = await params; @@ -7,10 +11,68 @@ export default async function EditProjectPage({ params }) { }); const project = await res.json(); + if (!project) { + return ( + +
+

Project not found.

+ + + +
+
+ ); + } + return ( -
-

Edit Project

- -
+ + + + + + + + +
+ } + /> +
+ +
+ ); } diff --git a/src/app/projects/[id]/page.js b/src/app/projects/[id]/page.js index 79e64ee..7c676cd 100644 --- a/src/app/projects/[id]/page.js +++ b/src/app/projects/[id]/page.js @@ -18,7 +18,7 @@ export default async function ProjectViewPage({ params }) { const { id } = await params; const project = getProjectWithContract(id); const notes = getNotesForProject(id); - + if (!project) { return ( @@ -34,7 +34,7 @@ export default async function ProjectViewPage({ params }) { ); } - const daysRemaining = project.finish_date + const daysRemaining = project.finish_date ? differenceInCalendarDays(parseISO(project.finish_date), new Date()) : null; @@ -50,13 +50,16 @@ export default async function ProjectViewPage({ params }) { description={`${project.city} • ${project.address}`} action={
- - {daysRemaining === 0 - ? "Due Today" - : daysRemaining > 0 - ? `${daysRemaining} days left` - : `${Math.abs(daysRemaining)} days overdue`} - + {daysRemaining !== null && ( + + {daysRemaining === 0 + ? "Due Today" + : daysRemaining > 0 + ? `${daysRemaining} days left` + : `${Math.abs(daysRemaining)} days overdue`} + + )} +
} - /> + />{" "} +
+ {/* Main Project Information */} +
+ + +
+

+ Project Information +

+ + {project.project_type === "design" + ? "Design (P)" + : project.project_type === "construction" + ? "Construction (R)" + : project.project_type === "design+construction" + ? "Design + Construction (P+R)" + : "Unknown"} + +
+
+ +
+
+ + Location + +

+ {project.city || "N/A"} +

+
+
+ + Address + +

+ {project.address || "N/A"} +

+
+
+ + Plot + +

+ {project.plot || "N/A"} +

+
+
+ + District + +

+ {project.district || "N/A"} +

+
+
+ + Unit + +

+ {project.unit || "N/A"} +

+
+
+ + Deadline + +

+ {project.finish_date || "N/A"} +

+
+
+ + WP + +

+ {project.wp || "N/A"} +

+
+
+ + Investment Number + +

+ {project.investment_number || "N/A"} +

+
+
-
- - -

- Project Information -

-
- -
+ {project.contact && ( +
+ + Contact + +

{project.contact}

+
+ )} + + {project.coordinates && ( +
+ + Coordinates + +

+ {project.coordinates} +

+
+ )} + + {project.notes && ( +
+ + Notes + +

{project.notes}

+
+ )} + + + + {/* Contract Details */} + + +

+ Contract Details +

+
+ +
+
+ + Contract Number + +

+ {project.contract_number || "N/A"} +

+
+
+ + Contract Name + +

+ {project.contract_name || "N/A"} +

+
+
+ + Customer + +

+ {project.customer || "N/A"} +

+
+
+ + Investor + +

+ {project.investor || "N/A"} +

+
+
+
+
+
+ + {/* Status Sidebar */} +
+ + +

+ Project Status +

+
+
- - Location + + Current Status -

{project.city}

+
+ + {project.project_status === "registered" + ? "Registered" + : project.project_status === "in_progress_design" + ? "In Progress (Design)" + : project.project_status === "in_progress_construction" + ? "In Progress (Construction)" + : project.project_status === "fulfilled" + ? "Completed" + : "Unknown"} + +
+
+ +
-
- - Address - -

{project.address}

-
-
- Plot -

{project.plot}

-
-
- - District - -

{project.district}

-
-
- Unit -

{project.unit}

-
-
- - Deadline - -

{project.finish_date}

-
-
- WP -

{project.wp}

-
-
- - Investment Number - -

{project.investment_number}

-
-
- Contact -

{project.contact}

-
{project.coordinates && ( -
- Coordinates -

{project.coordinates}

-
- )} - {project.notes && ( -
- Notes -

{project.notes}

-
- )} -
- - Typ projektu - -

- {project.project_type === "design" - ? "Projektowanie" - : project.project_type === "construction" - ? "Realizacja" - : project.project_type === "design+construction" - ? "Projektowanie + Realizacja" - : "-"} -

-
-
- - Status projektu - -
- - {project.project_status === "registered" - ? "Zarejestrowany" - : project.project_status === "in_progress_design" - ? "W realizacji (projektowanie)" - : project.project_status === "in_progress_construction" - ? "W realizacji (realizacja)" - : project.project_status === "fulfilled" - ? "Zakończony" - : "-"} - - -
-
-
-
- - -

- Contract Details -

-
- -
- - Contract Number - -

{project.contract_number}

-
-
- - Contract Name - -

{project.contract_name}

-
-
- - Customer - -

{project.customer}

-
-
- - Investor - -

{project.investor}

-
-
{" "} -
{/* Project Location Map */} + + {daysRemaining !== null && ( +
+ + Timeline + +
+ + {daysRemaining === 0 + ? "Due Today" + : daysRemaining > 0 + ? `${daysRemaining} days remaining` + : `${Math.abs(daysRemaining)} days overdue`} + +
+
+ )} +
+
+ + {/* Quick Actions */} + + +

+ Quick Actions +

+
+ + + + + + + + +
+
+
{" "} + {/* Project Location Map */} {project.coordinates && ( - - -

- Project Location -

-
- - - -
+
+ + +

+ Project Location +

+
+ + + +
+
)} - - - + {/* Project Tasks Section */} +
+ +
+ {/* Notes Section */}

Notes

@@ -245,8 +427,8 @@ export default async function ProjectViewPage({ params }) {
{notes.length === 0 ? ( -
-
+
+
-

No notes yet.

+

+ No notes yet +

+

+ Add your first note using the form above. +

) : ( -
+
{notes.map((n) => (
-

{n.note_date}

-

{n.note}

+
+ + {n.note_date} + +
+

{n.note}

))}
diff --git a/src/app/projects/new/page.js b/src/app/projects/new/page.js index de53bce..1254c36 100644 --- a/src/app/projects/new/page.js +++ b/src/app/projects/new/page.js @@ -1,10 +1,39 @@ import ProjectForm from "@/components/ProjectForm"; +import PageContainer from "@/components/ui/PageContainer"; +import PageHeader from "@/components/ui/PageHeader"; +import Button from "@/components/ui/Button"; +import Link from "next/link"; export default function NewProjectPage() { return ( -
-

New Project

- -
+ + + + + } + /> +
+ +
+
); } diff --git a/src/app/projects/page.js b/src/app/projects/page.js index 1c0907f..e592696 100644 --- a/src/app/projects/page.js +++ b/src/app/projects/page.js @@ -145,83 +145,128 @@ export default function ProjectListPage() { ) : (
- {/* Header Row */} -
- {" "} -
Number
-
Project Name
-
WP
-
City
-
Address
-
Plot
{" "} -
Finish Date
-
Actions
-
{" "} - {/* Data Rows */} - {filteredProjects.map((project, index) => ( -
-
- - {project.project_number} - -
{" "} -
- + + + + No. + + + Project Name + + + WP + + + City + + + Address + + + Plot + + + Finish + + + Type + + + Status + + + Actions + + + {" "} + + {filteredProjects.map((project, index) => ( + - {project.project_name} - -
-
- {project.wp || "N/A"} -
-
- {project.city || "N/A"} -
-
- {project.address || "N/A"} -
-
- {project.plot || "N/A"} -
{" "} -
- {project.finish_date || "N/A"} -
-
- {project.project_type === "design" - ? "Projektowanie" - : project.project_type === "construction" - ? "Realizacja" - : project.project_type === "design+construction" - ? "Projektowanie + Realizacja" - : "-"} -
-
- {project.project_status === "registered" - ? "Zarejestrowany" - : project.project_status === "in_progress_design" - ? "W realizacji (projektowanie)" - : project.project_status === "in_progress_construction" - ? "W realizacji (realizacja)" - : project.project_status === "fulfilled" - ? "Zakończony" - : "-"} -
-
- - - -
{" "} -
- ))} + + + {project.project_number} + + + + + {project.project_name} + + + + {project.wp || "N/A"} + + + {project.city || "N/A"} + + + {project.address || "N/A"} + + + {project.plot || "N/A"} + + + {project.finish_date || "N/A"} + + + {project.project_type === "design" + ? "P" + : project.project_type === "construction" + ? "R" + : project.project_type === "design+construction" + ? "P+R" + : "-"} + + + {project.project_status === "registered" + ? "Zarejestr." + : project.project_status === "in_progress_design" + ? "W real. (P)" + : project.project_status === "in_progress_construction" + ? "W real. (R)" + : project.project_status === "fulfilled" + ? "Zakończony" + : "-"} + + + + + + + + ))} + +
)} diff --git a/src/components/ContractForm.js b/src/components/ContractForm.js index 610c07e..5d12dd4 100644 --- a/src/components/ContractForm.js +++ b/src/components/ContractForm.js @@ -2,6 +2,9 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; +import { Card, CardHeader, CardContent } from "@/components/ui/Card"; +import Button from "@/components/ui/Button"; +import { Input } from "@/components/ui/Input"; export default function ContractForm() { const [form, setForm] = useState({ @@ -14,6 +17,7 @@ export default function ContractForm() { finish_date: "", }); + const [loading, setLoading] = useState(false); const router = useRouter(); function handleChange(e) { @@ -22,53 +26,192 @@ export default function ContractForm() { async function handleSubmit(e) { e.preventDefault(); + setLoading(true); - console.log("Submitting form:", form); + try { + console.log("Submitting form:", form); - const res = await fetch("/api/contracts", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(form), - }); + const res = await fetch("/api/contracts", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), + }); - if (res.ok) { - router.push("/projects"); // or /contracts if you plan a listing - } else { - alert( - "Wystąpił błąd podczas dodawania umowy. Sprawdź dane i spróbuj ponownie." - ); + if (res.ok) { + const contract = await res.json(); + router.push(`/contracts/${contract.contract_id}`); + } else { + alert( + "Failed to create contract. Please check the data and try again." + ); + } + } catch (error) { + console.error("Error creating contract:", error); + alert("Failed to create contract. Please try again."); + } finally { + setLoading(false); } } return ( -
- {[ - ["contract_number", "Numer Umowy"], - ["contract_name", "Nazwa Umowy"], - ["customer_contract_number", "Numer Umowy (Klienta)"], - ["customer", "Zleceniodawca"], - ["investor", "Inwestor"], - ["date_signed", "Data zawarcia"], - ["finish_date", "Data zakończenia"], - ].map(([name, label]) => ( -
- - -
- ))} + + +

+ Contract Details +

+
+ + + {/* Basic Information Section */} +
+
+ + +
- - +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ + {/* Form Actions */} +
+ + +
+ +
+
); } diff --git a/src/components/ProjectForm.js b/src/components/ProjectForm.js index 3691d50..abc66ec 100644 --- a/src/components/ProjectForm.js +++ b/src/components/ProjectForm.js @@ -2,6 +2,9 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; +import { Card, CardHeader, CardContent } from "@/components/ui/Card"; +import Button from "@/components/ui/Button"; +import { Input } from "@/components/ui/Input"; export default function ProjectForm({ initialData = null }) { const [form, setForm] = useState({ @@ -15,7 +18,8 @@ export default function ProjectForm({ initialData = null }) { investment_number: "", finish_date: "", wp: "", - contact: "", notes: "", + contact: "", + notes: "", coordinates: "", project_type: initialData?.project_type || "design", // project_status is not included in the form for creation or editing @@ -23,6 +27,7 @@ export default function ProjectForm({ initialData = null }) { }); const [contracts, setContracts] = useState([]); + const [loading, setLoading] = useState(false); const router = useRouter(); const isEdit = !!initialData; @@ -38,94 +43,340 @@ export default function ProjectForm({ initialData = null }) { async function handleSubmit(e) { e.preventDefault(); + setLoading(true); - const res = await fetch( - isEdit ? `/api/projects/${initialData.project_id}` : "/api/projects", - { - method: isEdit ? "PUT" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(form), + try { + const res = await fetch( + isEdit ? `/api/projects/${initialData.project_id}` : "/api/projects", + { + method: isEdit ? "PUT" : "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), + } + ); + + if (res.ok) { + const project = await res.json(); + if (isEdit) { + router.push(`/projects/${project.project_id}`); + } else { + router.push("/projects"); + } + } else { + alert("Failed to save project."); } - ); - - if (res.ok) { - router.push("/projects"); - } else { + } catch (error) { + console.error("Error saving project:", error); alert("Failed to save project."); + } finally { + setLoading(false); } } - return ( -
- {/* Contract Dropdown */} -
- - -
+ + +

+ {isEdit ? "Edit Project Details" : "Project Details"} +

+
+ + + {/* Contract and Project Type Section */} +
+
+ + +
- {/* Project Type Dropdown */} -
- - -
+
+ + +
+
- {/* Other fields */} {[ - ["project_name", "Nazwa projektu"], - ["address", "Lokalizacja"], - ["plot", "Działka"], - ["district", "Obręb ewidencyjny"], - ["unit", "Jednostka ewidencyjna"], - ["city", "Miejscowość"], - ["investment_number", "Numer inwestycjny"], - ["finish_date", "Termin realizacji"], - ["wp", "WP"], ["contact", "Dane kontaktowe"], - ["coordinates", "Coordinates"], - ["notes", "Notatki"], - ].map(([name, label]) => ( -
- - -
- ))} + {/* Basic Information Section */} +
+

+ Basic Information +

+
+
+ + +
- - +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Additional Information Section */} +
+

+ Additional Information +

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +