feat: add drag-and-drop for tasks, workspace bullets, and list items
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 57s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 57s
This commit is contained in:
1 parent
c0661cddae
commit
35d4657a25
9 files changed
+814
-51
No files matched your search
+85
-13
@@ -319,6 +319,8 @@ func (s *Store) migrate() error {
|
||||
"ALTER TABLE thoughts ADD COLUMN notes TEXT NOT NULL DEFAULT '';",
|
||||
"ALTER TABLE thoughts ADD COLUMN alarm_sent INTEGER NOT NULL DEFAULT 0;",
|
||||
"ALTER TABLE thoughts ADD COLUMN subtasks TEXT NOT NULL DEFAULT '[]';",
|
||||
"ALTER TABLE thoughts ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;",
|
||||
"CREATE INDEX IF NOT EXISTS idx_thoughts_list_sort ON thoughts(task_list_id, sort_order ASC, created_at DESC);",
|
||||
|
||||
"ALTER TABLE documents ADD COLUMN folder_path TEXT NOT NULL DEFAULT '';",
|
||||
|
||||
@@ -835,16 +837,17 @@ func scanThought(rows interface {
|
||||
}) (*domain.Thought, error) {
|
||||
t := &domain.Thought{}
|
||||
var tagsStr, subtasksStr, statusStr string
|
||||
var isCompleted, alarmSent int
|
||||
var isCompleted, alarmSent, sortOrder int
|
||||
err := rows.Scan(
|
||||
&t.ID, &t.UserID, &t.Content, &tagsStr, &t.Priority, &t.Source,
|
||||
&statusStr, &isCompleted, &t.ConvertedType, &t.ConvertedRefID,
|
||||
&t.DueDate, &t.TaskListID, &t.KanbanStatus, &t.Notes, &alarmSent,
|
||||
&t.CreatedAt, &t.TriagedAt, &subtasksStr,
|
||||
&t.CreatedAt, &t.TriagedAt, &subtasksStr, &sortOrder,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.SortOrder = sortOrder
|
||||
t.Status = domain.ThoughtStatus(statusStr)
|
||||
t.IsCompleted = isCompleted != 0
|
||||
t.AlarmSent = alarmSent != 0
|
||||
@@ -866,7 +869,7 @@ func scanThought(rows interface {
|
||||
|
||||
func (s *Store) accessibleTaskListIDsQuery() string {
|
||||
return `SELECT id FROM task_lists
|
||||
WHERE user_id = ?
|
||||
WHERE user_id = ?
|
||||
OR (team_id != '' AND team_id IN (SELECT team_id FROM team_members WHERE user_id = ?))
|
||||
OR id IN (
|
||||
SELECT task_list_id FROM task_list_shares
|
||||
@@ -877,7 +880,7 @@ func (s *Store) accessibleTaskListIDsQuery() string {
|
||||
|
||||
func (s *Store) GetThoughtByID(userID, thoughtID string) (*domain.Thought, error) {
|
||||
row := s.db.QueryRow(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts WHERE id = ? AND (user_id = ? OR task_list_id IN (`+s.accessibleTaskListIDsQuery()+`))`,
|
||||
thoughtID, userID, userID, userID, userID, userID,
|
||||
)
|
||||
@@ -893,16 +896,16 @@ func (s *Store) GetThoughts(userID, statusFilter string, limit int) ([]domain.Th
|
||||
|
||||
if statusFilter != "" && statusFilter != "all" {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts WHERE user_id = ? AND status = ?
|
||||
ORDER BY CASE priority WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 WHEN 'normal' THEN 3 ELSE 4 END, created_at DESC LIMIT ?`,
|
||||
ORDER BY CASE priority WHEN 'urgent' THEN 1 WHEN 'high' THEN 2 WHEN 'normal' THEN 3 ELSE 4 END, sort_order ASC, created_at DESC LIMIT ?`,
|
||||
userID, statusFilter, limit,
|
||||
)
|
||||
} else {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts WHERE user_id = ?
|
||||
ORDER BY created_at DESC LIMIT ?`,
|
||||
ORDER BY sort_order ASC, created_at DESC LIMIT ?`,
|
||||
userID, limit,
|
||||
)
|
||||
}
|
||||
@@ -926,21 +929,21 @@ func (s *Store) GetTasks(userID string, taskListID string) ([]domain.Thought, er
|
||||
var err error
|
||||
if taskListID != "" {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts
|
||||
WHERE (user_id = ? OR task_list_id IN (`+s.accessibleTaskListIDsQuery()+`))
|
||||
AND task_list_id = ?
|
||||
AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task'))
|
||||
ORDER BY is_completed ASC, CASE WHEN due_date = '' THEN 1 ELSE 0 END, due_date ASC, created_at DESC`,
|
||||
ORDER BY is_completed ASC, sort_order ASC, created_at DESC`,
|
||||
userID, userID, userID, userID, userID, taskListID,
|
||||
)
|
||||
} else {
|
||||
rows, err = s.db.Query(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts
|
||||
WHERE (user_id = ? OR task_list_id IN (`+s.accessibleTaskListIDsQuery()+`))
|
||||
AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task'))
|
||||
ORDER BY is_completed ASC, CASE WHEN due_date = '' THEN 1 ELSE 0 END, due_date ASC, created_at DESC`,
|
||||
ORDER BY is_completed ASC, sort_order ASC, created_at DESC`,
|
||||
userID, userID, userID, userID, userID,
|
||||
)
|
||||
}
|
||||
@@ -959,6 +962,37 @@ func (s *Store) GetTasks(userID string, taskListID string) ([]domain.Thought, er
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Store) ReorderTasks(taskListID string, items []domain.ThoughtReorderItem) error {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
for _, it := range items {
|
||||
if it.KanbanStatus != nil && *it.KanbanStatus != "" {
|
||||
isCompleted := 0
|
||||
if *it.KanbanStatus == "done" {
|
||||
isCompleted = 1
|
||||
}
|
||||
_, err = tx.Exec(
|
||||
`UPDATE thoughts SET sort_order = ?, kanban_status = ?, is_completed = ? WHERE id = ?`,
|
||||
it.SortOrder, *it.KanbanStatus, isCompleted, it.ID,
|
||||
)
|
||||
} else {
|
||||
_, err = tx.Exec(
|
||||
`UPDATE thoughts SET sort_order = ? WHERE id = ?`,
|
||||
it.SortOrder, it.ID,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// UpdateThoughtTriage handles swipe left (discard), swipe right (keep), restore, and conversion to task/document/list_item
|
||||
func (s *Store) UpdateThoughtTriage(userID, thoughtID, action, convertTo, targetListID, dueDate, taskListID string) (*domain.Thought, error) {
|
||||
thought, err := s.GetThoughtByID(userID, thoughtID)
|
||||
@@ -2569,6 +2603,44 @@ func (s *Store) ClearCompletedListItems(listID string) (int64, error) {
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
func (s *Store) ReorderListItems(listID string, items []domain.ListItemReorderItem) error {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
for _, it := range items {
|
||||
if it.Category != nil && it.Content != nil {
|
||||
_, err = tx.Exec(
|
||||
`UPDATE list_items SET sort_order = ?, category = ?, content = ? WHERE id = ? AND list_id = ?`,
|
||||
it.SortOrder, *it.Category, *it.Content, it.ID, listID,
|
||||
)
|
||||
} else if it.Category != nil {
|
||||
_, err = tx.Exec(
|
||||
`UPDATE list_items SET sort_order = ?, category = ? WHERE id = ? AND list_id = ?`,
|
||||
it.SortOrder, *it.Category, it.ID, listID,
|
||||
)
|
||||
} else if it.Content != nil {
|
||||
_, err = tx.Exec(
|
||||
`UPDATE list_items SET sort_order = ?, content = ? WHERE id = ? AND list_id = ?`,
|
||||
it.SortOrder, *it.Content, it.ID, listID,
|
||||
)
|
||||
} else {
|
||||
_, err = tx.Exec(
|
||||
`UPDATE list_items SET sort_order = ? WHERE id = ? AND list_id = ?`,
|
||||
it.SortOrder, it.ID, listID,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = tx.Exec(`UPDATE lists SET updated_at = ? WHERE id = ?`, domain.NowISO(), listID)
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// --- API KEYS & WEBHOOKS ---
|
||||
|
||||
func (s *Store) CreateAPIKey(userID, name string) (*domain.APIKey, error) {
|
||||
@@ -2685,7 +2757,7 @@ func (s *Store) SearchAll(userID, query string) ([]domain.Thought, []domain.Docu
|
||||
q := "%" + strings.ToLower(strings.TrimSpace(query)) + "%"
|
||||
|
||||
tRows, err := s.db.Query(
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks
|
||||
`SELECT id, user_id, content, tags, priority, source, status, is_completed, converted_type, converted_ref_id, due_date, task_list_id, kanban_status, notes, alarm_sent, created_at, triaged_at, subtasks, sort_order
|
||||
FROM thoughts WHERE user_id = ? AND LOWER(content) LIKE ? ORDER BY created_at DESC LIMIT 30`,
|
||||
userID, q,
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user