@@ -669,4 +669,183 @@ function updateTask(taskId, taskData) {
669669 } ) ;
670670}
671671
672- module . exports = { createTask, fetchProjects, fetchTasks, markTaskDone, markTaskUndone, updateTaskDueDate, updateTask } ;
672+ function fetchProjectViews ( projectId ) {
673+ const config = getConfig ( ) ;
674+ if ( ! config ) {
675+ return Promise . resolve ( { success : false , error : 'Configuration not loaded' } ) ;
676+ }
677+
678+ const url = `${ config . vikunja_url } /api/v1/projects/${ projectId } /views` ;
679+
680+ const validation = validateHttpUrl ( url ) ;
681+ if ( ! validation . valid ) {
682+ return Promise . resolve ( { success : false , error : validation . error } ) ;
683+ }
684+
685+ return new Promise ( ( resolve ) => {
686+ const timeout = setTimeout ( ( ) => {
687+ resolve ( { success : false , error : 'Request timed out (5s)' } ) ;
688+ } , 5000 ) ;
689+
690+ try {
691+ const request = net . request ( {
692+ method : 'GET' ,
693+ url,
694+ } ) ;
695+
696+ request . setHeader ( 'Authorization' , `Bearer ${ config . api_token } ` ) ;
697+ request . setHeader ( 'Content-Type' , 'application/json' ) ;
698+
699+ let responseBody = '' ;
700+ let statusCode = 0 ;
701+
702+ request . on ( 'response' , ( response ) => {
703+ statusCode = response . statusCode ;
704+
705+ response . on ( 'data' , ( chunk ) => {
706+ responseBody += chunk . toString ( ) ;
707+ } ) ;
708+
709+ response . on ( 'end' , ( ) => {
710+ clearTimeout ( timeout ) ;
711+
712+ if ( statusCode >= 200 && statusCode < 300 ) {
713+ try {
714+ const views = JSON . parse ( responseBody ) ;
715+ resolve ( { success : true , data : views } ) ;
716+ } catch {
717+ resolve ( { success : false , error : 'Invalid response' } ) ;
718+ }
719+ } else {
720+ resolve ( { success : false , error : describeHttpError ( statusCode , responseBody ) } ) ;
721+ }
722+ } ) ;
723+ } ) ;
724+
725+ request . on ( 'error' , ( err ) => {
726+ clearTimeout ( timeout ) ;
727+ resolve ( { success : false , error : err . message || 'Network error' } ) ;
728+ } ) ;
729+
730+ request . end ( ) ;
731+ } catch ( err ) {
732+ clearTimeout ( timeout ) ;
733+ resolve ( { success : false , error : err . message || 'Request failed' } ) ;
734+ }
735+ } ) ;
736+ }
737+
738+ function fetchViewTasks ( projectId , viewId , filterParams ) {
739+ const config = getConfig ( ) ;
740+ if ( ! config ) {
741+ return Promise . resolve ( { success : false , error : 'Configuration not loaded' } ) ;
742+ }
743+
744+ const baseUrl = `${ config . vikunja_url } /api/v1/projects/${ projectId } /views/${ viewId } /tasks` ;
745+
746+ const validation = validateHttpUrl ( baseUrl ) ;
747+ if ( ! validation . valid ) {
748+ return Promise . resolve ( { success : false , error : validation . error } ) ;
749+ }
750+
751+ const params = new URLSearchParams ( ) ;
752+ params . set ( 'per_page' , String ( filterParams . per_page || 10 ) ) ;
753+ params . set ( 'page' , String ( filterParams . page || 1 ) ) ;
754+
755+ // Build filter string — always filter for open tasks
756+ let filterString = 'done = false' ;
757+
758+ // Due date filter
759+ const now = new Date ( ) ;
760+ const todayStart = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) ) ;
761+ const todayEnd = new Date ( now . getFullYear ( ) , now . getMonth ( ) , now . getDate ( ) , 23 , 59 , 59 ) ;
762+
763+ if ( filterParams . due_date_filter && filterParams . due_date_filter !== 'all' ) {
764+ switch ( filterParams . due_date_filter ) {
765+ case 'overdue' :
766+ filterString += ` && due_date < '${ todayStart . toISOString ( ) } ' && due_date != '0001-01-01T00:00:00Z'` ;
767+ break ;
768+ case 'today' :
769+ filterString += ` && due_date <= '${ todayEnd . toISOString ( ) } ' && due_date != '0001-01-01T00:00:00Z'` ;
770+ break ;
771+ case 'this_week' : {
772+ const weekEnd = new Date ( todayStart ) ;
773+ weekEnd . setDate ( weekEnd . getDate ( ) + ( 7 - weekEnd . getDay ( ) ) ) ;
774+ weekEnd . setHours ( 23 , 59 , 59 ) ;
775+ filterString += ` && due_date <= '${ weekEnd . toISOString ( ) } ' && due_date != '0001-01-01T00:00:00Z'` ;
776+ break ;
777+ }
778+ case 'this_month' : {
779+ const monthEnd = new Date ( now . getFullYear ( ) , now . getMonth ( ) + 1 , 0 , 23 , 59 , 59 ) ;
780+ filterString += ` && due_date <= '${ monthEnd . toISOString ( ) } ' && due_date != '0001-01-01T00:00:00Z'` ;
781+ break ;
782+ }
783+ case 'has_due_date' :
784+ filterString += ` && due_date != '0001-01-01T00:00:00Z'` ;
785+ break ;
786+ case 'no_due_date' :
787+ filterString += ` && due_date = '0001-01-01T00:00:00Z'` ;
788+ break ;
789+ }
790+ }
791+
792+ params . set ( 'filter' , filterString ) ;
793+ params . set ( 'sort_by' , 'position' ) ;
794+ params . set ( 'order_by' , filterParams . order_by || 'asc' ) ;
795+
796+ const fullUrl = `${ baseUrl } ?${ params . toString ( ) } ` ;
797+
798+ return new Promise ( ( resolve ) => {
799+ const timeout = setTimeout ( ( ) => {
800+ resolve ( { success : false , error : 'Request timed out (5s)' } ) ;
801+ } , 5000 ) ;
802+
803+ try {
804+ const request = net . request ( {
805+ method : 'GET' ,
806+ url : fullUrl ,
807+ } ) ;
808+
809+ request . setHeader ( 'Authorization' , `Bearer ${ config . api_token } ` ) ;
810+ request . setHeader ( 'Content-Type' , 'application/json' ) ;
811+
812+ let responseBody = '' ;
813+ let statusCode = 0 ;
814+
815+ request . on ( 'response' , ( response ) => {
816+ statusCode = response . statusCode ;
817+
818+ response . on ( 'data' , ( chunk ) => {
819+ responseBody += chunk . toString ( ) ;
820+ } ) ;
821+
822+ response . on ( 'end' , ( ) => {
823+ clearTimeout ( timeout ) ;
824+
825+ if ( statusCode >= 200 && statusCode < 300 ) {
826+ try {
827+ const tasks = JSON . parse ( responseBody ) ;
828+ resolve ( { success : true , data : tasks } ) ;
829+ } catch {
830+ resolve ( { success : false , error : 'Invalid response' } ) ;
831+ }
832+ } else {
833+ resolve ( { success : false , error : describeHttpError ( statusCode , responseBody ) } ) ;
834+ }
835+ } ) ;
836+ } ) ;
837+
838+ request . on ( 'error' , ( err ) => {
839+ clearTimeout ( timeout ) ;
840+ resolve ( { success : false , error : err . message || 'Network error' } ) ;
841+ } ) ;
842+
843+ request . end ( ) ;
844+ } catch ( err ) {
845+ clearTimeout ( timeout ) ;
846+ resolve ( { success : false , error : err . message || 'Request failed' } ) ;
847+ }
848+ } ) ;
849+ }
850+
851+ module . exports = { createTask, fetchProjects, fetchTasks, markTaskDone, markTaskUndone, updateTaskDueDate, updateTask, fetchProjectViews, fetchViewTasks } ;
0 commit comments