@@ -768,7 +768,65 @@ class Database {
768768 return this . _query ( sql , values ) . map ( r => this . _rowToNode ( r ) )
769769 }
770770
771- getChildren ( id , type = null ) {
771+ /**
772+ * Get tasks with optional filtering
773+ * @param {Object } params - Filter parameters
774+ * @param {string } params.workspaceId - Filter by workspace
775+ * @param {boolean } params.completed - Filter by completion status
776+ * @param {string } params.dueDateFrom - Filter by due date >= this value (YYYY-MM-DD)
777+ * @param {string } params.dueDateTo - Filter by due date <= this value (YYYY-MM-DD)
778+ * @param {number } params.importance - Filter by importance level (1-5)
779+ * @param {number } params.parentId - Filter by parent/project ID
780+ * @returns {Array } List of task nodes
781+ */
782+ getTasks ( params = { } ) {
783+ let sql = "SELECT * FROM nodes WHERE type = 'task' AND deleted_at IS NULL"
784+ const values = [ ]
785+
786+ // Workspace filtering
787+ if ( params . workspaceId === null || params . workspaceId === 'null' ) {
788+ sql += ' AND workspace_id IS NULL'
789+ } else if ( params . workspaceId !== undefined ) {
790+ sql += ' AND workspace_id = ?'
791+ values . push ( params . workspaceId )
792+ }
793+
794+ // Completion filtering
795+ if ( params . completed !== undefined ) {
796+ sql += ' AND completed = ?'
797+ values . push ( params . completed ? 1 : 0 )
798+ }
799+
800+ // Due date range filtering
801+ if ( params . dueDateFrom ) {
802+ sql += ' AND due_date >= ?'
803+ values . push ( params . dueDateFrom )
804+ }
805+ if ( params . dueDateTo ) {
806+ sql += ' AND due_date <= ?'
807+ values . push ( params . dueDateTo )
808+ }
809+
810+ // Importance filtering
811+ if ( params . importance !== undefined ) {
812+ sql += ' AND importance = ?'
813+ values . push ( params . importance )
814+ }
815+
816+ // Parent/project filtering
817+ if ( params . parentId !== undefined ) {
818+ sql += ' AND parent_id = ?'
819+ values . push ( params . parentId )
820+ }
821+
822+ // Order: due_date first (nulls last), then sort_order, then created_at
823+ sql += ' ORDER BY CASE WHEN due_date IS NULL THEN 1 ELSE 0 END, due_date, sort_order, created_at'
824+
825+ return this . _query ( sql , values ) . map ( r => this . _rowToNode ( r ) )
826+ }
827+
828+ getChildren ( id , type = null , workspaceId = undefined ) {
829+ const parent = this . getNode ( id )
772830 let sql = 'SELECT * FROM nodes WHERE parent_id = ? AND deleted_at IS NULL'
773831 const values = [ id ]
774832
@@ -777,6 +835,24 @@ class Database {
777835 values . push ( type )
778836 }
779837
838+ // Filter by workspace if provided or if parent has a workspace
839+ if ( workspaceId !== undefined ) {
840+ if ( workspaceId === null ) {
841+ sql += ' AND workspace_id IS NULL'
842+ } else {
843+ sql += ' AND workspace_id = ?'
844+ values . push ( workspaceId )
845+ }
846+ } else if ( parent ) {
847+ // Use parent's workspace for consistency
848+ if ( parent . workspace_id === null ) {
849+ sql += ' AND workspace_id IS NULL'
850+ } else if ( parent . workspace_id ) {
851+ sql += ' AND workspace_id = ?'
852+ values . push ( parent . workspace_id )
853+ }
854+ }
855+
780856 sql += ' ORDER BY sort_order, created_at'
781857 return this . _query ( sql , values ) . map ( r => this . _rowToNode ( r ) )
782858 }
@@ -790,6 +866,14 @@ class Database {
790866 let sql = "SELECT * FROM nodes WHERE (path = ? OR path LIKE ?) AND deleted_at IS NULL"
791867 const values = [ pathPrefix , `${ pathPrefix } /%` ]
792868
869+ // Filter by workspace to ensure descendants match parent's workspace
870+ if ( node . workspace_id === null ) {
871+ sql += ' AND workspace_id IS NULL'
872+ } else if ( node . workspace_id ) {
873+ sql += ' AND workspace_id = ?'
874+ values . push ( node . workspace_id )
875+ }
876+
793877 if ( maxDepth !== null ) {
794878 sql += ' AND depth <= ?'
795879 values . push ( node . depth + maxDepth )
@@ -808,7 +892,7 @@ class Database {
808892
809893 const placeholders = ancestorIds . map ( ( ) => '?' ) . join ( ', ' )
810894 return this . _query (
811- `SELECT * FROM nodes WHERE id IN (${ placeholders } ) ORDER BY depth` ,
895+ `SELECT * FROM nodes WHERE id IN (${ placeholders } ) AND deleted_at IS NULL ORDER BY depth` ,
812896 ancestorIds
813897 ) . map ( r => this . _rowToNode ( r ) )
814898 }
@@ -1101,6 +1185,37 @@ class Database {
11011185 [ `%"${ tag } "%` ]
11021186 ) . map ( r => this . _rowToNode ( r ) )
11031187 }
1188+
1189+ // Repair workspace_id for all descendants to match their root ancestor
1190+ repairWorkspaces ( ) {
1191+ const roots = this . _query ( 'SELECT * FROM nodes WHERE parent_id IS NULL AND deleted_at IS NULL' )
1192+ let fixed = 0
1193+
1194+ for ( const root of roots ) {
1195+ const rootWorkspace = root . workspace_id
1196+ // Get all descendants of this root
1197+ const pathPrefix = root . path ? `${ root . path } /${ root . id } ` : `${ root . id } `
1198+ const descendants = this . _query (
1199+ "SELECT id, workspace_id FROM nodes WHERE (path = ? OR path LIKE ?) AND deleted_at IS NULL" ,
1200+ [ pathPrefix , `${ pathPrefix } /%` ]
1201+ )
1202+
1203+ for ( const desc of descendants ) {
1204+ // Fix if workspace_id doesn't match root
1205+ const descWorkspace = desc . workspace_id
1206+ const needsFix = ( rootWorkspace === null && descWorkspace !== null ) ||
1207+ ( rootWorkspace !== null && descWorkspace !== rootWorkspace )
1208+ if ( needsFix ) {
1209+ this . _run ( 'UPDATE nodes SET workspace_id = ? WHERE id = ?' , [ rootWorkspace , desc . id ] )
1210+ fixed ++
1211+ }
1212+ }
1213+ }
1214+
1215+ console . log ( `repairWorkspaces: fixed ${ fixed } nodes` )
1216+ if ( fixed > 0 ) this . _save ( )
1217+ return { fixed }
1218+ }
11041219}
11051220
11061221module . exports = Database
0 commit comments