feat: add task_category field to tasks with validation and update related forms
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in New Issue
Block a user