feat: add more roles (tech_lead, scrum_master, product_owner, designer, qa)
- Registration form: added 5 new role options to dropdown - Sidebar: new roles get proper nav access via ALL_ROLES/LEADER_ROLES - Dashboard: isLeader check expanded to include new leadership roles - Shared/Pages: role badge colors added for all new roles - Invite modal: expanded role dropdown
This commit is contained in:
@@ -4,7 +4,7 @@ import { randomUUID } from 'crypto';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Helper: fetch full task with subtasks, comments, activities, tags
|
||||
// Helper: fetch full task with subtasks, comments, activities, tags, dependencies
|
||||
async function getFullTask(taskId) {
|
||||
const [taskRows] = await pool.query('SELECT * FROM tasks WHERE id = ?', [taskId]);
|
||||
if (taskRows.length === 0) return null;
|
||||
@@ -14,6 +14,7 @@ async function getFullTask(taskId) {
|
||||
const [comments] = await pool.query('SELECT id, user_id AS userId, text, timestamp FROM comments WHERE task_id = ? ORDER BY timestamp', [taskId]);
|
||||
const [activities] = await pool.query('SELECT id, text, timestamp FROM activities WHERE task_id = ? ORDER BY timestamp', [taskId]);
|
||||
const [tagRows] = await pool.query('SELECT tag FROM task_tags WHERE task_id = ?', [taskId]);
|
||||
const [depRows] = await pool.query('SELECT id, depends_on_user_id AS dependsOnUserId, description, resolved FROM dependencies WHERE task_id = ? ORDER BY created_at', [taskId]);
|
||||
|
||||
return {
|
||||
id: task.id,
|
||||
@@ -28,6 +29,7 @@ async function getFullTask(taskId) {
|
||||
subtasks: subtasks.map(s => ({ id: s.id, title: s.title, done: !!s.done })),
|
||||
comments: comments.map(c => ({ id: c.id, userId: c.userId, text: c.text, timestamp: c.timestamp?.toISOString() || '' })),
|
||||
activity: activities.map(a => ({ id: a.id, text: a.text, timestamp: a.timestamp?.toISOString() || '' })),
|
||||
dependencies: depRows.map(d => ({ id: d.id, dependsOnUserId: d.dependsOnUserId || '', description: d.description, resolved: !!d.resolved })),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ router.post('/', async (req, res) => {
|
||||
const conn = await pool.getConnection();
|
||||
try {
|
||||
await conn.beginTransaction();
|
||||
const { title, description, status, priority, assignee, reporter, dueDate, tags, subtasks } = req.body;
|
||||
const { title, description, status, priority, assignee, reporter, dueDate, tags, subtasks, dependencies } = req.body;
|
||||
const id = randomUUID();
|
||||
|
||||
await conn.query(
|
||||
@@ -70,6 +72,16 @@ router.post('/', async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Insert dependencies
|
||||
if (dependencies && dependencies.length > 0) {
|
||||
for (const dep of dependencies) {
|
||||
await conn.query(
|
||||
'INSERT INTO dependencies (id, task_id, depends_on_user_id, description, resolved) VALUES (?, ?, ?, ?, ?)',
|
||||
[randomUUID(), id, dep.dependsOnUserId || null, dep.description, false]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add creation activity
|
||||
const actId = randomUUID();
|
||||
await conn.query('INSERT INTO activities (id, task_id, text) VALUES (?, ?, ?)',
|
||||
@@ -188,6 +200,49 @@ router.post('/:id/activity', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// --- DEPENDENCY ROUTES ---
|
||||
|
||||
// POST /api/tasks/:id/dependencies
|
||||
router.post('/:id/dependencies', async (req, res) => {
|
||||
try {
|
||||
const { dependsOnUserId, description } = req.body;
|
||||
const id = randomUUID();
|
||||
await pool.query(
|
||||
'INSERT INTO dependencies (id, task_id, depends_on_user_id, description, resolved) VALUES (?, ?, ?, ?, ?)',
|
||||
[id, req.params.id, dependsOnUserId || null, description, false]
|
||||
);
|
||||
res.status(201).json({ id, dependsOnUserId: dependsOnUserId || '', description, resolved: false });
|
||||
} catch (err) {
|
||||
console.error('Add dependency error:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/tasks/:id/dependencies/:depId
|
||||
router.put('/:id/dependencies/:depId', async (req, res) => {
|
||||
try {
|
||||
const { resolved } = req.body;
|
||||
await pool.query('UPDATE dependencies SET resolved = ? WHERE id = ? AND task_id = ?',
|
||||
[resolved, req.params.depId, req.params.id]);
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('Update dependency error:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/tasks/:id/dependencies/:depId
|
||||
router.delete('/:id/dependencies/:depId', async (req, res) => {
|
||||
try {
|
||||
await pool.query('DELETE FROM dependencies WHERE id = ? AND task_id = ?',
|
||||
[req.params.depId, req.params.id]);
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('Delete dependency error:', err);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/tasks/:id
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user