diff --git a/src/app/api/contracts/[id]/route.js b/src/app/api/contracts/[id]/route.js index b5dcc55..d0f98c6 100644 --- a/src/app/api/contracts/[id]/route.js +++ b/src/app/api/contracts/[id]/route.js @@ -1,6 +1,6 @@ import db from "@/lib/db"; import { NextResponse } from "next/server"; -import { withReadAuth, withTeamLeadAuth } from "@/lib/middleware/auth"; +import { withReadAuth, withTeamLeadAuth, withUserAuth } from "@/lib/middleware/auth"; async function getContractHandler(req, { params }) { const { id } = await params; @@ -21,6 +21,79 @@ async function getContractHandler(req, { params }) { return NextResponse.json(contract); } +async function updateContractHandler(req, { params }) { + const { id } = await params; + + try { + const body = await req.json(); + const { + contract_number, + contract_name, + customer_contract_number, + customer, + investor, + date_signed, + finish_date, + } = body; + + // Check if contract exists + const existingContract = db + .prepare("SELECT * FROM contracts WHERE contract_id = ?") + .get(id); + + if (!existingContract) { + return NextResponse.json( + { error: "Contract not found" }, + { status: 404 } + ); + } + + // Update the contract + const result = db + .prepare( + `UPDATE contracts + SET contract_number = ?, + contract_name = ?, + customer_contract_number = ?, + customer = ?, + investor = ?, + date_signed = ?, + finish_date = ? + WHERE contract_id = ?` + ) + .run( + contract_number, + contract_name || null, + customer_contract_number || null, + customer || null, + investor || null, + date_signed || null, + finish_date || null, + id + ); + + if (result.changes === 0) { + return NextResponse.json( + { error: "Failed to update contract" }, + { status: 500 } + ); + } + + // Fetch and return the updated contract + const updatedContract = db + .prepare("SELECT * FROM contracts WHERE contract_id = ?") + .get(id); + + return NextResponse.json(updatedContract); + } catch (error) { + console.error("Error updating contract:", error); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 } + ); + } +} + async function deleteContractHandler(req, { params }) { const { id } = params; @@ -61,4 +134,5 @@ async function deleteContractHandler(req, { params }) { // Protected routes - require authentication export const GET = withReadAuth(getContractHandler); +export const PUT = withUserAuth(updateContractHandler); export const DELETE = withTeamLeadAuth(deleteContractHandler); diff --git a/src/app/contracts/[id]/edit/page.js b/src/app/contracts/[id]/edit/page.js new file mode 100644 index 0000000..fe0d6cc --- /dev/null +++ b/src/app/contracts/[id]/edit/page.js @@ -0,0 +1,139 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useParams } from "next/navigation"; +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"; +import { LoadingState } from "@/components/ui/States"; +import { useTranslation } from "@/lib/i18n"; + +export default function EditContractPage() { + const params = useParams(); + const id = params.id; + const [contract, setContract] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const { t } = useTranslation(); + + useEffect(() => { + async function fetchContract() { + setLoading(true); + setError(null); + try { + const res = await fetch(`/api/contracts/${id}`); + + if (!res.ok) { + throw new Error("Failed to fetch contract"); + } + + const data = await res.json(); + setContract(data); + } catch (error) { + console.error("Error fetching contract:", error); + setError("Nie udało się pobrać danych umowy."); + } finally { + setLoading(false); + } + } + + if (id) { + fetchContract(); + } + }, [id]); + + if (loading) { + return ( + + + + + ); + } + + if (error || !contract) { + return ( + + + + + + + + + {error || "Nie znaleziono umowy."} + + + + + + + + + {t('contracts.backToContracts')} + + + + ); + } + + return ( + + + + + + + {t('contracts.backToContract')} + + + } + /> + + + + + ); +} diff --git a/src/app/contracts/[id]/page.js b/src/app/contracts/[id]/page.js index b0977d0..87ed93a 100644 --- a/src/app/contracts/[id]/page.js +++ b/src/app/contracts/[id]/page.js @@ -113,6 +113,24 @@ export default function ContractDetailsPage() { {t('contracts.backToContracts')} + + + + + + {t('contracts.editContract')} + + { + if (initialData) { + setForm({ + contract_number: initialData.contract_number || "", + contract_name: initialData.contract_name || "", + customer_contract_number: initialData.customer_contract_number || "", + customer: initialData.customer || "", + investor: initialData.investor || "", + date_signed: initialData.date_signed || "", + finish_date: initialData.finish_date || "", + }); + } + }, [initialData]); + function handleChange(e) { setForm({ ...form, [e.target.name]: e.target.value }); } @@ -34,21 +50,32 @@ export default function ContractForm() { try { console.log("Submitting form:", form); - const res = await fetch("/api/contracts", { - method: "POST", + const url = isEdit + ? `/api/contracts/${initialData.contract_id}` + : "/api/contracts"; + const method = isEdit ? "PUT" : "POST"; + + const res = await fetch(url, { + method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(form), }); if (res.ok) { const contract = await res.json(); - router.push(`/contracts/${contract.contract_id}`); + router.push(`/contracts/${contract.contract_id || initialData.contract_id}`); } else { - alert(t('contracts.failedToCreateContract')); + const errorMessage = isEdit + ? t('contracts.failedToUpdateContract') + : t('contracts.failedToCreateContract'); + alert(errorMessage); } } catch (error) { - console.error("Error creating contract:", error); - alert(t('contracts.failedToCreateContract')); + console.error(`Error ${isEdit ? 'updating' : 'creating'} contract:`, error); + const errorMessage = isEdit + ? t('contracts.failedToUpdateContract') + : t('contracts.failedToCreateContract'); + alert(errorMessage); } finally { setLoading(false); } @@ -189,7 +216,7 @@ export default function ContractForm() { d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" > - {t('common.creating')} + {isEdit ? t('common.updating') : t('common.creating')} > ) : ( <> @@ -203,10 +230,10 @@ export default function ContractForm() { strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} - d="M12 4v16m8-8H4" + d={isEdit ? "M5 13l4 4L19 7" : "M12 4v16m8-8H4"} /> - {t('contracts.createContract')} + {isEdit ? t('contracts.updateContract') : t('contracts.createContract')} > )} diff --git a/src/lib/i18n.js b/src/lib/i18n.js index e003336..61e5c95 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -287,6 +287,11 @@ const translations = { contract: "Umowa", newContract: "Nowa umowa", editContract: "Edytuj umowę", + editContractDescription: "Edycja szczegółów umowy", + editing: "Edycja umowy", + backToContract: "Powrót do umowy", + updateContract: "Zaktualizuj umowę", + failedToUpdateContract: "Nie udało się zaktualizować umowy. Sprawdź dane i spróbuj ponownie.", deleteContract: "Usuń umowę", contractNumber: "Numer umowy", contractName: "Nazwa umowy", @@ -940,8 +945,14 @@ const translations = { contracts: { title: "Contracts", subtitle: "Manage your contracts and agreements", + contract: "Contract", newContract: "New Contract", editContract: "Edit Contract", + editContractDescription: "Edit contract details", + editing: "Editing contract", + backToContract: "Back to Contract", + updateContract: "Update Contract", + failedToUpdateContract: "Failed to update contract. Please check your data and try again.", deleteContract: "Delete Contract", contractNumber: "Contract Number", contractName: "Contract Name", @@ -968,7 +979,40 @@ const translations = { signedOn: "Signed:", finishOn: "Finish:", customerLabel: "Customer:", - investorLabel: "Investor:" + investorLabel: "Investor:", + loadingContractDetails: "Loading contract details...", + contractNotFound: "Contract not found.", + backToContracts: "Back to Contracts", + addProject: "Add Project", + contractInformation: "Contract Information", + summary: "Summary", + projectsCount: "Projects Count", + projects: "projects", + contractStatus: "Contract Status", + contractDocuments: "Contract Documents", + uploadDocument: "Upload Document", + associatedProjects: "Associated Projects", + noProjectsYet: "No projects yet", + getStartedMessage: "Get started by creating the first project for this contract", + createFirstProject: "Create First Project", + viewDetails: "View Details", + createNewContract: "Create New Contract", + addNewContractDescription: "Add a new contract to your portfolio", + contractDetails: "Contract Details", + failedToCreateContract: "Failed to create contract. Please check your data and try again.", + uploadDocumentTitle: "Upload Document", + descriptionOptional: "Description (optional)", + descriptionPlaceholder: "Short description of the document...", + uploading: "Uploading...", + dropFilesHere: "Drop files here or click to browse", + supportedFiles: "PDF, DOC, XLS, Images up to 10MB", + chooseFile: "Choose File", + failedToUploadFile: "Failed to upload file", + loadingFiles: "Loading files...", + noDocumentsUploaded: "No documents uploaded", + download: "Download", + confirmDeleteFile: "Are you sure you want to delete this file?", + failedToDeleteFile: "Failed to delete file" }, tasks: {
+ {error || "Nie znaleziono umowy."} +