feat: Add project type and status fields to project forms and views, including database migrations

This commit is contained in:
Chop
2025-06-04 22:53:33 +02:00
parent e136c9f0e8
commit 49d49b42f1
6 changed files with 138 additions and 6 deletions

View File

@@ -12,8 +12,8 @@ import { differenceInCalendarDays, parseISO } from "date-fns";
import PageContainer from "@/components/ui/PageContainer"; import PageContainer from "@/components/ui/PageContainer";
import PageHeader from "@/components/ui/PageHeader"; import PageHeader from "@/components/ui/PageHeader";
export default async function ProjectViewPage({ params }) { export default function ProjectViewPage({ params }) {
const { id } = await params; const { id } = params;
const project = getProjectWithContract(id); const project = getProjectWithContract(id);
const notes = getNotesForProject(id); const notes = getNotesForProject(id);
const daysRemaining = differenceInCalendarDays( const daysRemaining = differenceInCalendarDays(
@@ -141,6 +141,36 @@ export default async function ProjectViewPage({ params }) {
<p className="text-gray-900">{project.notes}</p> <p className="text-gray-900">{project.notes}</p>
</div> </div>
)} )}
<div>
<span className="text-sm font-medium text-gray-500">
Typ projektu
</span>
<p className="text-gray-900">
{project.project_type === "design"
? "Projektowanie"
: project.project_type === "construction"
? "Realizacja"
: project.project_type === "design+construction"
? "Projektowanie + Realizacja"
: "-"}
</p>
</div>
<div>
<span className="text-sm font-medium text-gray-500">
Status projektu
</span>
<p className="text-gray-900">
{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"
: "-"}
</p>
</div>
</CardContent> </CardContent>
</Card> </Card>
<Card> <Card>

View File

@@ -192,6 +192,26 @@ export default function ProjectListPage() {
<div className="col-span-1 text-sm text-gray-600 truncate"> <div className="col-span-1 text-sm text-gray-600 truncate">
{project.finish_date || "N/A"} {project.finish_date || "N/A"}
</div> </div>
<div className="col-span-1 text-sm text-gray-600 truncate">
{project.project_type === "design"
? "Projektowanie"
: project.project_type === "construction"
? "Realizacja"
: project.project_type === "design+construction"
? "Projektowanie + Realizacja"
: "-"}
</div>
<div className="col-span-1 text-sm text-gray-600 truncate">
{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"
: "-"}
</div>
<div className="col-span-1"> <div className="col-span-1">
<Link href={`/projects/${project.project_id}`}> <Link href={`/projects/${project.project_id}`}>
<Button variant="outline" size="sm"> <Button variant="outline" size="sm">

View File

@@ -17,6 +17,8 @@ export default function ProjectForm({ initialData = null }) {
wp: "", wp: "",
contact: "", contact: "",
notes: "", notes: "",
project_type: initialData?.project_type || "design",
// project_status is not included in the form for creation or editing
...initialData, ...initialData,
}); });
@@ -74,6 +76,24 @@ export default function ProjectForm({ initialData = null }) {
</select> </select>
</div> </div>
{/* Project Type Dropdown */}
<div>
<label className="block font-medium">Typ projektu</label>
<select
name="project_type"
value={form.project_type}
onChange={handleChange}
className="border p-2 w-full"
required
>
<option value="design">Projektowanie</option>
<option value="construction">Realizacja</option>
<option value="design+construction">
Projektowanie + Realizacja
</option>
</select>
</div>
{/* Other fields */} {/* Other fields */}
{[ {[
["project_name", "Nazwa projektu"], ["project_name", "Nazwa projektu"],

View File

@@ -0,0 +1,38 @@
"use client";
import { useState } from "react";
export default function ProjectStatusDropdown({ project, onStatusChange }) {
const [status, setStatus] = useState(project.project_status);
const [loading, setLoading] = useState(false);
const handleChange = async (e) => {
const newStatus = e.target.value;
setStatus(newStatus);
setLoading(true);
await fetch(`/api/projects/${project.project_id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ...project, project_status: newStatus }),
});
setLoading(false);
if (onStatusChange) onStatusChange(newStatus);
};
return (
<select
name="project_status"
value={status}
onChange={handleChange}
className="ml-2 border p-1 rounded"
disabled={loading}
>
<option value="registered">Zarejestrowany</option>
<option value="in_progress_design">W realizacji (projektowanie)</option>
<option value="in_progress_construction">
W realizacji (realizacja)
</option>
<option value="fulfilled">Zakończony</option>
</select>
);
}

View File

@@ -30,6 +30,8 @@ export default function initializeDatabase() {
wp TEXT, wp TEXT,
contact TEXT, contact TEXT,
notes TEXT, notes TEXT,
project_type TEXT CHECK(project_type IN ('design', 'construction', 'design+construction')) DEFAULT 'design',
project_status TEXT CHECK(project_status IN ('registered', 'in_progress_design', 'in_progress_construction', 'fulfilled')) DEFAULT 'registered',
FOREIGN KEY (contract_id) REFERENCES contracts(contract_id) FOREIGN KEY (contract_id) REFERENCES contracts(contract_id)
); );
@@ -98,4 +100,22 @@ export default function initializeDatabase() {
} catch (e) { } catch (e) {
// Column already exists, ignore error // Column already exists, ignore error
} }
// Migration: Add project_type column to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN project_type TEXT CHECK(project_type IN ('design', 'construction', 'design+construction')) DEFAULT 'design';
`);
} catch (e) {
// Column already exists, ignore error
}
// Migration: Add project_status column to projects table
try {
db.exec(`
ALTER TABLE projects ADD COLUMN project_status TEXT CHECK(project_status IN ('registered', 'in_progress_design', 'in_progress_construction', 'fulfilled')) DEFAULT 'registered';
`);
} catch (e) {
// Column already exists, ignore error
}
} }

View File

@@ -42,8 +42,8 @@ export function createProject(data) {
const stmt = db.prepare(` const stmt = db.prepare(`
INSERT INTO projects ( INSERT INTO projects (
contract_id, project_name, project_number, address, plot, district, unit, city, investment_number, finish_date, contract_id, project_name, project_number, address, plot, district, unit, city, investment_number, finish_date,
wp, contact, notes wp, contact, notes, project_type, project_status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`); `);
stmt.run( stmt.run(
data.contract_id, data.contract_id,
@@ -58,7 +58,9 @@ export function createProject(data) {
data.finish_date, data.finish_date,
data.wp, data.wp,
data.contact, data.contact,
data.notes data.notes,
data.project_type || "design",
data.project_status || "registered"
); );
} }
@@ -66,7 +68,7 @@ export function updateProject(id, data) {
const stmt = db.prepare(` const stmt = db.prepare(`
UPDATE projects SET UPDATE projects SET
contract_id = ?, project_name = ?, project_number = ?, address = ?, plot = ?, district = ?, unit = ?, city = ?, contract_id = ?, project_name = ?, project_number = ?, address = ?, plot = ?, district = ?, unit = ?, city = ?,
investment_number = ?, finish_date = ?, wp = ?, contact = ?, notes = ? investment_number = ?, finish_date = ?, wp = ?, contact = ?, notes = ?, project_type = ?, project_status = ?
WHERE project_id = ? WHERE project_id = ?
`); `);
stmt.run( stmt.run(
@@ -83,6 +85,8 @@ export function updateProject(id, data) {
data.wp, data.wp,
data.contact, data.contact,
data.notes, data.notes,
data.project_type || "design",
data.project_status || "registered",
id id
); );
} }