feat: add task_category field to tasks with validation and update related forms

This commit is contained in:
2025-10-07 22:22:10 +02:00
parent 952caf10d1
commit a6ef325813
6 changed files with 72 additions and 13 deletions

View File

@@ -30,19 +30,23 @@ async function getTaskHandler(req, { params }) {
async function updateTaskHandler(req, { params }) {
const { id } = await params;
try {
const { name, max_wait_days, description } = await req.json();
const { name, max_wait_days, description, task_category } = await req.json();
if (!name) {
return NextResponse.json({ error: "Name is required" }, { status: 400 });
}
if (task_category && !['design', 'construction'].includes(task_category)) {
return NextResponse.json({ error: "Invalid task_category (must be design or construction)" }, { status: 400 });
}
const result = db
.prepare(
`UPDATE tasks
SET name = ?, max_wait_days = ?, description = ?
SET name = ?, max_wait_days = ?, description = ?, task_category = ?
WHERE task_id = ? AND is_standard = 1`
)
.run(name, max_wait_days || 0, description || null, id);
.run(name, max_wait_days || 0, description || null, task_category, id);
if (result.changes === 0) {
return NextResponse.json(

View File

@@ -5,18 +5,22 @@ import { getAllTaskTemplates } from "@/lib/queries/tasks";
// POST: create new template
async function createTaskHandler(req) {
const { name, max_wait_days, description } = await req.json();
const { name, max_wait_days, description, task_category } = await req.json();
if (!name) {
return NextResponse.json({ error: "Name is required" }, { status: 400 });
}
if (!task_category || !['design', 'construction'].includes(task_category)) {
return NextResponse.json({ error: "Valid task_category is required (design or construction)" }, { status: 400 });
}
db.prepare(
`
INSERT INTO tasks (name, max_wait_days, description, is_standard)
VALUES (?, ?, ?, 1)
INSERT INTO tasks (name, max_wait_days, description, is_standard, task_category)
VALUES (?, ?, ?, 1, ?)
`
).run(name, max_wait_days || 0, description || null);
).run(name, max_wait_days || 0, description || null, task_category);
return NextResponse.json({ success: true });
}

View File

@@ -16,7 +16,7 @@ export default function NewTaskSetPage() {
const [formData, setFormData] = useState({
name: "",
description: "",
project_type: "design",
task_category: "design",
selectedTemplates: []
});
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -61,7 +61,7 @@ export default function NewTaskSetPage() {
body: JSON.stringify({
name: formData.name.trim(),
description: formData.description.trim(),
project_type: formData.project_type
task_category: formData.task_category
})
});

View File

@@ -34,6 +34,19 @@ export default function TaskTemplatesPage() {
fetchTemplates();
}, []);
const getTaskCategoryBadge = (taskCategory) => {
const colors = {
design: "bg-blue-100 text-blue-800",
construction: "bg-green-100 text-green-800"
};
return (
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${colors[taskCategory] || "bg-gray-100 text-gray-800"}`}>
{taskCategory === "design" ? "Projektowe" : taskCategory === "construction" ? "Budowlane" : taskCategory}
</span>
);
};
if (loading) {
return (
<PageContainer>
@@ -170,9 +183,12 @@ export default function TaskTemplatesPage() {
<h3 className="text-lg font-semibold text-gray-900 truncate pr-2">
{template.name}
</h3>
<Badge variant="primary" size="sm">
{template.max_wait_days} {t('common.days')}
</Badge>
<div className="flex flex-col gap-1">
<Badge variant="primary" size="sm">
{template.max_wait_days} {t('common.days')}
</Badge>
{getTaskCategoryBadge(template.task_category)}
</div>
</div>
{template.description && (
<p className="text-gray-600 text-sm mb-4 line-clamp-2">

View File

@@ -14,6 +14,7 @@ export default function TaskTemplateForm({
const [name, setName] = useState("");
const [max_wait_days, setRequiredWaitDays] = useState("");
const [description, setDescription] = useState("");
const [task_category, setTaskCategory] = useState("design");
const [loading, setLoading] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const router = useRouter();
@@ -26,6 +27,7 @@ export default function TaskTemplateForm({
setName(initialData.name || "");
setRequiredWaitDays(initialData.max_wait_days?.toString() || "");
setDescription(initialData.description || "");
setTaskCategory(initialData.task_category || "design");
}
}
}, [templateId, initialData]);
@@ -45,6 +47,7 @@ export default function TaskTemplateForm({
name,
max_wait_days: parseInt(max_wait_days, 10) || 0,
description: description || null,
task_category,
}),
});
@@ -78,6 +81,21 @@ export default function TaskTemplateForm({
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Kategoria zadania *
</label>
<select
value={task_category}
onChange={(e) => setTaskCategory(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
>
<option value="design">Zadania projektowe</option>
<option value="construction">Zadania budowlane</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
{t('taskTemplates.estimatedDuration')}

View File

@@ -40,7 +40,8 @@ export default function initializeDatabase() {
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
max_wait_days INTEGER DEFAULT 0,
is_standard INTEGER NOT NULL DEFAULT 0
is_standard INTEGER NOT NULL DEFAULT 0,
task_category TEXT CHECK(task_category IN ('design', 'construction')) NOT NULL DEFAULT 'design'
);
-- Table: task_sets
@@ -382,6 +383,22 @@ export default function initializeDatabase() {
console.warn("Migration warning:", e.message);
}
// Migration: Add task_category column to tasks table
try {
const tableInfo = db.prepare("PRAGMA table_info(tasks)").all();
const hasTaskCategory = tableInfo.some(col => col.name === 'task_category');
if (!hasTaskCategory) {
// Add the task_category column
db.exec(`
ALTER TABLE tasks ADD COLUMN task_category TEXT CHECK(task_category IN ('design', 'construction')) NOT NULL DEFAULT 'design';
`);
console.log("✅ Added task_category column to tasks table");
}
} catch (e) {
console.warn("Migration warning:", e.message);
}
// Generic file attachments table
db.exec(`
CREATE TABLE IF NOT EXISTS file_attachments (