feat(tasks): write a task straight into a section
The box at the top puts things in the project with no section, which is right for capturing. But a project has sections because the work belongs somewhere, and dragging every new task down into the right one is a second gesture for something you already knew when you typed it. Two ways in, because they answer different moments. A field at the foot of each section and each board column — standing there is enough, no syntax to know. And `/section` in quick add, for the box at the top and for Today and Upcoming, where you are not standing in a section at all. The field goes through quick add rather than createTask, so a line written there still parses its dates, priorities and labels. The sigil needed care: '#' and '@' never turn up in a Danish task line by accident and '/' turns up constantly. It has to follow whitespace and be followed immediately by the name, which "km/t", "3/4" and "20 kr / stk" all fail. The bare form is greedy, because "Kunder/Ordrer" is a real section name and the sigil is inside it. A section id from the client is checked against the project the task actually landed in. A `#project` in the same line can move the task elsewhere, and the id would otherwise follow it into a project that section does not belong to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9cc7df69f0
commit
0dbf7a40bf
@@ -630,3 +630,36 @@ func (db *DB) ReorderProjects(ctx context.Context, userID string, ids []string)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// SectionByName resolves a "/name" from quick add within one project.
|
||||
//
|
||||
// Case-insensitive, and scoped to the project the task is landing in — a section
|
||||
// belongs to exactly one project, so the name cannot be looked up before that is
|
||||
// decided. Two projects may both have a "Produktion" and they are different
|
||||
// sections; asking without the project would be asking the wrong question.
|
||||
func (db *DB) SectionByName(ctx context.Context, projectID, name string) (string, error) {
|
||||
var id string
|
||||
err := db.QueryRowContext(ctx, `
|
||||
SELECT id FROM sections
|
||||
WHERE project_id = ? AND lower(name) = lower(?) AND deleted_at IS NULL
|
||||
ORDER BY sort_order
|
||||
LIMIT 1`, projectID, name).Scan(&id)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", ErrNotFound
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
// SectionInProject reports whether a section id belongs to the given project.
|
||||
//
|
||||
// Quick add takes a section id from the client — the field at the foot of a
|
||||
// section passes the one it sits in — and a "#project" in the same line can move
|
||||
// the task somewhere else entirely. Without this the id would follow it, and the
|
||||
// task would land in a section belonging to a project it is not in.
|
||||
func (db *DB) SectionInProject(ctx context.Context, sectionID, projectID string) (bool, error) {
|
||||
var n int
|
||||
err := db.QueryRowContext(ctx,
|
||||
`SELECT count(*) FROM sections WHERE id = ? AND project_id = ? AND deleted_at IS NULL`,
|
||||
sectionID, projectID).Scan(&n)
|
||||
return n == 1, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user