Collapse Sharing by default; allow creating tasks directly in a project
Sharing (tag/assign/reallocate) is now a collapsed-by-default section in the task drawer with a one-line summary, since it's a lesser-used function that was eating space on every task. list_id is now optional on task creation -- the task lands unfiled (same state assign/reallocate already use), scoped only by project_id. Projects are a cross-cutting grouping, not a list replacement, and the only way to add a task to a project was previously to create it elsewhere and set the Project field from the drawer. Added a "+ New task" input directly on ProjectDetailView. Verified against a throwaway Postgres: unfiled creation, distinct sort_order per unfiled task, project detail lists them, and existing validation (bad list_id, missing title) still rejects.
This commit is contained in:
parent
6172617df3
commit
4d19b37b8a
3 changed files with 90 additions and 52 deletions
|
|
@ -149,12 +149,18 @@ export async function taskRoutes(app) {
|
|||
app.post('/api/tasks', { preHandler: requireCap('use') }, async (req, reply) => {
|
||||
const { list_id, title, notes, priority, due_at, remind_at, recur_rule, category_id, project_id } = req.body || {}
|
||||
if (!title?.trim()) return reply.status(400).send({ error: 'title required' })
|
||||
// Verify the target list belongs to the caller.
|
||||
const { rows: [list] } = await pool.query(
|
||||
'SELECT id FROM lists WHERE id = $1 AND owner_id = $2',
|
||||
[list_id, req.user.email]
|
||||
)
|
||||
if (!list) return reply.status(400).send({ error: 'invalid list' })
|
||||
// list_id is optional — omitting it lands the task "unfiled" (list_id
|
||||
// NULL), the same state assign/reallocate already use. Projects need this
|
||||
// so a task can be created straight into a project without first needing
|
||||
// a personal list, since projects are a cross-cutting grouping, not a
|
||||
// list replacement.
|
||||
if (list_id) {
|
||||
const { rows: [list] } = await pool.query(
|
||||
'SELECT id FROM lists WHERE id = $1 AND owner_id = $2',
|
||||
[list_id, req.user.email]
|
||||
)
|
||||
if (!list) return reply.status(400).send({ error: 'invalid list' })
|
||||
}
|
||||
if (priority && !PRIORITIES.includes(priority)) return reply.status(400).send({ error: 'bad priority' })
|
||||
if (recur_rule && !RECUR_RULES.includes(recur_rule)) return reply.status(400).send({ error: 'bad recur_rule' })
|
||||
if (category_id) {
|
||||
|
|
@ -166,14 +172,16 @@ export async function taskRoutes(app) {
|
|||
if (!p) return reply.status(400).send({ error: 'invalid project' })
|
||||
}
|
||||
const { rows: [max] } = await pool.query(
|
||||
'SELECT COALESCE(MAX(sort_order), 0) AS m FROM tasks WHERE list_id = $1',
|
||||
[list_id]
|
||||
list_id
|
||||
? 'SELECT COALESCE(MAX(sort_order), 0) AS m FROM tasks WHERE list_id = $1'
|
||||
: 'SELECT COALESCE(MAX(sort_order), 0) AS m FROM tasks WHERE list_id IS NULL',
|
||||
list_id ? [list_id] : []
|
||||
)
|
||||
const { rows: [task] } = await pool.query(
|
||||
`INSERT INTO tasks (list_id, owner_id, created_by, title, notes, priority, due_at, remind_at, recur_rule, category_id, project_id, sort_order)
|
||||
VALUES ($1, $2, $2, $3, $4, COALESCE($5, 'medium'), $6, $7, $8, $9, $10, $11)
|
||||
RETURNING *`,
|
||||
[list_id, req.user.email, title.trim(), notes || null, priority || null,
|
||||
[list_id || null, req.user.email, title.trim(), notes || null, priority || null,
|
||||
due_at || null, remind_at || null, recur_rule || null, category_id || null, project_id || null, Number(max.m) + 1]
|
||||
)
|
||||
return task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue