From c53ab98d37e68653057f12ff02e00fbe131ae930 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Mon, 28 Apr 2025 23:48:41 -0400
Subject: added task nodes calendar intergation, and ability to complete tasks
 directly via calendar view

---
 src/client/views/nodes/TaskManagerTask.tsx | 138 +++++++++++++++++++++++++----
 1 file changed, 121 insertions(+), 17 deletions(-)

(limited to 'src/client/views/nodes/TaskManagerTask.tsx')

diff --git a/src/client/views/nodes/TaskManagerTask.tsx b/src/client/views/nodes/TaskManagerTask.tsx
index 1a9205ada..2d2444275 100644
--- a/src/client/views/nodes/TaskManagerTask.tsx
+++ b/src/client/views/nodes/TaskManagerTask.tsx
@@ -37,24 +37,87 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
             delete this.props.Document.startTime;
             delete this.props.Document.endTime;
         }
+
+        this.setTaskDateRange();
     };
 
     @action
     updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.startTime = new DateField(new Date(e.target.value));
+        const newStart = new Date(e.target.value);
+
+        this.props.Document.startTime = new DateField(newStart);
+
+        const endDate = this.props.Document.endTime instanceof DateField ? this.props.Document.endTime.date : undefined;
+        if (endDate && newStart > endDate) {
+            // Alert user
+            alert('Start time cannot be after end time. End time has been adjusted.');
+            
+            // Fix end time
+            const adjustedEnd = new Date(newStart.getTime() + 60 * 60 * 1000);
+            this.props.Document.endTime = new DateField(adjustedEnd);
+        }
+
+        this.setTaskDateRange();
     };
 
+
+
     @action
     updateEnd = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.endTime = new DateField(new Date(e.target.value));
+        const newEnd = new Date(e.target.value);
+
+        this.props.Document.endTime = new DateField(newEnd);
+
+        const startDate = this.props.Document.startTime instanceof DateField ? this.props.Document.startTime.date : undefined;
+        if (startDate && newEnd < startDate) {
+            // Alert user
+            alert('End time cannot be before start time. Start time has been adjusted.');
+            
+            // Fix start time
+            const adjustedStart = new Date(newEnd.getTime() - 60 * 60 * 1000);
+            this.props.Document.startTime = new DateField(adjustedStart);
+        }
+
+        this.setTaskDateRange();
     };
 
-    @observable
-    completed = false;
+
+
+
+    @action
+    setTaskDateRange() {
+        const doc = this.props.Document;
+
+        if (doc.allDay) {
+            // All-day task → use date only
+            if (!doc.title) return;
+
+            const parsedDate = new Date(doc.title as string);
+            if (!isNaN(parsedDate.getTime())) {
+                const localStart = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
+                const localEnd = new Date(localStart);
+                doc.date_range = `${localStart.toISOString()}|${localEnd.toISOString()}`;
+                doc.allDay = true;
+            }
+        } else {
+            // Timed task → use full startTime and endTime
+            const startField = doc.startTime;
+            const endField = doc.endTime;
+            const startDate = startField instanceof DateField ? startField.date : null;
+            const endDate = endField instanceof DateField ? endField.date : null;
+
+            if (startDate && endDate && !isNaN(startDate.getTime()) && !isNaN(endDate.getTime())) {
+                doc.date_range = `${startDate.toISOString()}|${endDate.toISOString()}`;
+                doc.allDay = false;
+            } else {
+                console.warn('startTime or endTime is invalid');
+            }
+        }
+    }
 
     @action
     toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.completed = e.target.checked;
+        this.props.Document.completed = e.target.checked;
     };
 
     constructor(props: TaskManagerProps) {
@@ -62,15 +125,44 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
         makeObservable(this);
     }
 
+    componentDidMount() {
+        this.setTaskDateRange();
+    }
+    
+
     render() {
+
+        function toLocalDateTimeString(date: Date): string {
+            const pad = (n: number) => n.toString().padStart(2, '0');
+            return (
+                date.getFullYear() +
+                '-' +
+                pad(date.getMonth() + 1) +
+                '-' +
+                pad(date.getDate()) +
+                'T' +
+                pad(date.getHours()) +
+                ':' +
+                pad(date.getMinutes())
+            );
+        }
+        
         const doc = this.props.Document;
 
         const taskDesc = typeof doc.text === 'string' ? doc.text : '';
         const taskTitle = typeof doc.title === 'string' ? doc.title : '';
         const allDay = !!doc.allDay;
+        const isCompleted = !!this.props.Document.completed;
+
+        const startTime = doc.startTime instanceof DateField && doc.startTime.date instanceof Date
+            ? toLocalDateTimeString(doc.startTime.date)
+            : '';
+
+        const endTime = doc.endTime instanceof DateField && doc.endTime.date instanceof Date
+            ? toLocalDateTimeString(doc.endTime.date)
+            : '';
+
 
-        const startTime = doc.startTime instanceof DateField ? doc.startTime.date.toISOString().slice(0, 16) : '';
-        const endTime = doc.endTime instanceof DateField ? doc.endTime.date.toISOString().slice(0, 16) : '';
 
         return (
             <div className="task-manager-container">
@@ -80,8 +172,8 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
                     placeholder="Task Title"
                     value={taskTitle}
                     onChange={this.updateTitle}
-                    disabled={this.completed}
-                    style={{opacity: this.completed ? 0.7 : 1,}}
+                    disabled={isCompleted}
+                    style={{opacity: isCompleted ? 0.7 : 1,}}
                 />
 
                 <textarea
@@ -89,42 +181,54 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
                 placeholder="What’s your task?"
                 value={taskDesc}
                 onChange={this.updateText}
-                disabled={this.completed}
-                style={{opacity: this.completed ? 0.7 : 1,}}
+                disabled={isCompleted}
+                style={{opacity: isCompleted ? 0.7 : 1,}}
                 />
 
                 <div className="task-manager-checkboxes">
 
-                    <label className="task-manager-allday" style={{opacity: this.completed ? 0.7 : 1,}}>
+                    <label className="task-manager-allday" style={{opacity: isCompleted ? 0.7 : 1,}}>
                         <input 
                             type="checkbox" 
                             checked={allDay} 
                             onChange={this.updateAllDay} 
-                            disabled={this.completed}
+                            disabled={isCompleted}
                         />
                         All day
                     </label>
 
                     <label className="task-manager-complete">
-                        <input type="checkbox" checked={this.completed} onChange={this.toggleComplete} />
+                        <input type="checkbox" checked={isCompleted} onChange={this.toggleComplete} />
                         Complete
                     </label>
 
                 </div>
 
+                {!allDay && (
                 <div
                     className="task-manager-times"
-                    style={{ visibility: allDay ? 'hidden' : 'visible', opacity: this.completed ? 0.7 : 1}}
+                    style={{ opacity: isCompleted ? 0.7 : 1 }}
                 >
                     <label>
                         Start:
-                        <input type="datetime-local" value={startTime} onChange={this.updateStart} disabled={this.completed}/>
+                        <input
+                            type="datetime-local"
+                            value={startTime}
+                            onChange={this.updateStart}
+                            disabled={isCompleted}
+                        />
                     </label>
                     <label>
                         End:
-                        <input type="datetime-local" value={endTime} onChange={this.updateEnd} disabled={this.completed} />
+                        <input
+                            type="datetime-local"
+                            value={endTime}
+                            onChange={this.updateEnd}
+                            disabled={isCompleted}
+                        />
                     </label>
                 </div>
+            )}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2

