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/calendarBox/CalendarBox.tsx | 42 +++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 6f1f58a4c..cc8614f66 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -66,6 +66,7 @@ export class CalendarBox extends CollectionSubView() {
     @computed get calendarEvents(): EventSourceInput | undefined {
         return this.childDocs.map(doc => {
             const { start, end } = dateRangeStrToDates(StrCast(doc.date_range));
+            const isCompleted = BoolCast(doc.completed); // AARAV ADD
             return {
                 title: StrCast(doc.title),
                 start,
@@ -74,7 +75,7 @@ export class CalendarBox extends CollectionSubView() {
                 startEditable: true,
                 endEditable: true,
                 allDay: BoolCast(doc.allDay),
-                classNames: ['mother'], // will determine the style
+                classNames: ['mother', isCompleted ? 'completed-task' : ''], // will determine the style
                 editable: true, // subject to change in the future
                 backgroundColor: this.eventToColor(doc),
                 borderColor: this.eventToColor(doc),
@@ -105,6 +106,10 @@ export class CalendarBox extends CollectionSubView() {
 
     // TODO: Return a different color based on the event type
     eventToColor = (event: Doc): string => {
+        const docType = StrCast(event.type);
+        if (docType === 'task') {
+            return '#20B2AA'; // teal for tasks
+        }
         return 'red';
     };
 
@@ -171,6 +176,41 @@ export class CalendarBox extends CollectionSubView() {
                   eventClick: this.handleEventClick,
                   eventDrop: this.handleEventDrop,
                   eventDidMount: arg => {
+
+                    const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
+                    if (!doc) return;
+
+                    if (doc.type === 'task') {
+                        const checkButton = document.createElement('button');
+                        checkButton.innerText = doc.completed ? '✅' : '⬜';
+                        checkButton.style.position = 'absolute';
+                        checkButton.style.right = '5px';
+                        checkButton.style.top = '50%';
+                        checkButton.style.transform = 'translateY(-50%)';
+                        checkButton.style.background = 'transparent';
+                        checkButton.style.border = 'none';
+                        checkButton.style.cursor = 'pointer';
+                        checkButton.style.fontSize = '18px';
+                        checkButton.style.zIndex = '1000';
+                        checkButton.style.padding = '0';
+                        checkButton.style.margin = '0';
+
+                        checkButton.onclick = ev => {
+                            ev.stopPropagation();
+                            doc.completed = !doc.completed;
+                            this._calendar?.refetchEvents();
+                        };
+
+                        // Make sure the parent box is positioned relative
+                        arg.el.style.position = 'relative';
+                        arg.el.appendChild(checkButton);
+                    }
+
+                    // (keep your other pointerup/contextmenu handlers here)
+
+
+
+
                       arg.el.addEventListener('pointerdown', ev => {
                           ev.button && ev.stopPropagation();
                       });
-- 
cgit v1.2.3-70-g09d2


From 9ec7df6af508eccdfda3a195a69c8bb1f86c41ea Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 30 Apr 2025 12:51:51 -0400
Subject: two way integration -- changes in calendar reflect on task node --
 and changes to node reflect on calendar

---
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 29 ++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index cc8614f66..4ee2413be 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -1,4 +1,5 @@
 import { Calendar, EventClickArg, EventDropArg, EventSourceInput } from '@fullcalendar/core';
+import { EventResizeDoneArg } from '@fullcalendar/interaction';
 import dayGridPlugin from '@fullcalendar/daygrid';
 import interactionPlugin from '@fullcalendar/interaction';
 import multiMonthPlugin from '@fullcalendar/multimonth';
@@ -17,6 +18,7 @@ import { ContextMenu } from '../../ContextMenu';
 import { DocumentView } from '../DocumentView';
 import { OpenWhere } from '../OpenWhere';
 import './CalendarBox.scss';
+import { DateField } from '../../../../fields/DateField';
 
 type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
 
@@ -127,9 +129,31 @@ export class CalendarBox extends CollectionSubView() {
         return false;
     };
 
-    handleEventDrop = (arg: EventDropArg) => {
+    handleEventDrop = (arg: EventDropArg | EventResizeDoneArg) => {
         const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
-        doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
+        // doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
+        if (!doc || !arg.event.start) return;
+
+        // get the new start and end dates
+        const startDate = new Date(arg.event.start);
+        const endDate = new Date(arg.event.end ?? arg.event.start);
+
+        // update date range, time range, and all day status
+        doc.date_range = `${startDate.toISOString()}|${endDate.toISOString()}`;
+
+        const allDayStatus = arg.event.allDay ?? false;
+        if (doc.allDay !== allDayStatus) {
+            doc.allDay = allDayStatus;
+        }
+
+        if (doc.allDay) {
+            delete doc.startTime;
+            delete doc.endTime;
+        } else {
+            doc.startTime = new DateField(startDate);
+            doc.endTime = new DateField(endDate);
+        }
+
     };
 
     handleEventClick = (arg: EventClickArg) => {
@@ -175,6 +199,7 @@ export class CalendarBox extends CollectionSubView() {
                   events: this.calendarEvents,
                   eventClick: this.handleEventClick,
                   eventDrop: this.handleEventDrop,
+                  eventResize: this.handleEventDrop,
                   eventDidMount: arg => {
 
                     const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
-- 
cgit v1.2.3-70-g09d2


From c1ebafcbe29140da7c1e260bab88921964dd82b0 Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 30 Apr 2025 20:45:16 -0400
Subject: fixed height / width restrictions and added date selector for all day
 tasks

---
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/views/nodes/TaskManagerTask.tsx         | 61 ++++++++++++++++++++--
 src/client/views/nodes/calendarBox/CalendarBox.tsx |  3 --
 3 files changed, 58 insertions(+), 8 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1288abd3e..d378ca02f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -407,7 +407,7 @@ pie title Minerals in my tap water
             {key: "DataViz",    creator: opts => Docs.Create.DataVizDocument("", opts),     opts: { _width: 300, _height: 300, }},
             // AARAV ADD //
             {key: "DailyJournal",creator:opts => Docs.Create.DailyJournalDocument("", opts),opts: { _width: 300, _height: 300, }},
-            {key: "Task",       creator: opts => Docs.Create.TaskDocument("", opts),        opts: { _width: 250, _height: 100, _layout_autoHeight: true, title: "Task", }},
+            {key: "Task",       creator: opts => Docs.Create.TaskDocument("", opts),        opts: { _width: 300, _height: 300, _layout_autoHeight: true, title: "Task", }},
             {key: "Scrapbook",  creator: opts => Docs.Create.ScrapbookDocument([], opts),   opts: { _width: 300, _height: 300}},
             //{key: "Scrapbook",creator:opts => Docs.Create.ScrapbookDocument([], opts),opts:{ _width: 300, _height: 300}},
             {key: "Chat",       creator: Docs.Create.ChatDocument,                          opts: { _width: 500, _height: 500, _layout_fitWidth: true, }},
diff --git a/src/client/views/nodes/TaskManagerTask.tsx b/src/client/views/nodes/TaskManagerTask.tsx
index 2d2444275..d8c1430d4 100644
--- a/src/client/views/nodes/TaskManagerTask.tsx
+++ b/src/client/views/nodes/TaskManagerTask.tsx
@@ -1,4 +1,4 @@
-import { action, observable, makeObservable } from 'mobx';
+import { action, observable, makeObservable, IReactionDisposer, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Docs } from '../../documents/Documents';
@@ -14,7 +14,7 @@ interface TaskManagerProps {
 }
 
 @observer
-export class TaskManagerTask extends React.Component<TaskManagerProps> {
+export class TaskManagerTask extends React.Component<Props> {
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(TaskManagerTask, fieldStr);
     }
@@ -125,10 +125,38 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
         makeObservable(this);
     }
 
+    _heightDisposer?: IReactionDisposer;
+    _widthDisposer?: IReactionDisposer;
+
     componentDidMount() {
         this.setTaskDateRange();
+
+        const doc = this.props.Document;
+        this._heightDisposer = reaction(
+            () => Number(doc._height),
+            height => {
+                const minHeight = Number(doc.height_min ?? 0);
+                if (!isNaN(height) && height < minHeight) {
+                    doc._height = minHeight;
+                }
+            }
+        );
+
+        this._widthDisposer = reaction(
+            () => Number(doc._width),
+            width => {
+                const minWidth = Number(doc.width_min ?? 0);
+                if (!isNaN(width) && width < minWidth) {
+                    doc._width = minWidth;
+                }
+            }
+        );
+    }
+
+    componentWillUnmount() {
+        this._heightDisposer?.();
+        this._widthDisposer?.();
     }
-    
 
     render() {
 
@@ -195,6 +223,29 @@ export class TaskManagerTask extends React.Component<TaskManagerProps> {
                             disabled={isCompleted}
                         />
                         All day
+                        
+                        {allDay && (
+                        <input
+                            type="date"
+                            value={(() => {
+                                const rawRange = doc.date_range;
+                                if (typeof rawRange !== 'string') return '';
+                                const datePart = rawRange.split('|')[0];
+                                if (!datePart) return '';
+                                const d = new Date(datePart);
+                                return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
+                            })()}
+                            onChange={e => {
+                                const newDate = new Date(e.target.value);
+                                if (!isNaN(newDate.getTime())) {
+                                    const iso = newDate.toISOString().split('T')[0];
+                                    doc.date_range = `${iso}T00:00:00.000Z|${iso}T00:00:00.000Z`;
+                                }
+                            }}
+                            disabled={isCompleted}
+                            style={{ marginLeft: '8px' }}
+                        />
+                    )}
                     </label>
 
                     <label className="task-manager-complete">
@@ -246,6 +297,8 @@ Docs.Prototypes.TemplateMap.set(DocumentType.TASK, {
         _layout_reflowVertical: true,
         _layout_reflowHorizontal: true,
         defaultDoubleClick: 'ignore',
-        systemIcon: 'BsCheckSquare', // or whatever icon you like
+        systemIcon: 'BsCheckSquare',
+        height_min: 300,
+        width_min: 300
     },
 });
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 84fa25dba..ce58ac9fa 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -225,13 +225,10 @@ export class CalendarBox extends CollectionSubView() {
                               this._calendar?.refetchEvents();
                           };
 
-                          // Make sure the parent box is positioned relative
                           arg.el.style.position = 'relative';
                           arg.el.appendChild(checkButton);
                       }
 
-                      // (keep your other pointerup/contextmenu handlers here)
-
                       arg.el.addEventListener('pointerdown', ev => {
                           ev.button && ev.stopPropagation();
                       });
-- 
cgit v1.2.3-70-g09d2


From 54b8c7e9ec258fd43ec523aaf3d967a646022cee Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 30 Apr 2025 22:54:16 -0400
Subject: made document options as data doc options ($), and fixed namign
 conventions

---
 src/client/documents/Documents.ts                  |  16 +-
 src/client/views/Main.tsx                          |   4 +-
 src/client/views/nodes/TaskBox.scss                |  72 +++++
 src/client/views/nodes/TaskBox.tsx                 | 298 ++++++++++++++++++++
 src/client/views/nodes/TaskManagerTask.scss        |  72 -----
 src/client/views/nodes/TaskManagerTask.tsx         | 301 ---------------------
 src/client/views/nodes/calendarBox/CalendarBox.tsx |  22 +-
 7 files changed, 389 insertions(+), 396 deletions(-)
 create mode 100644 src/client/views/nodes/TaskBox.scss
 create mode 100644 src/client/views/nodes/TaskBox.tsx
 delete mode 100644 src/client/views/nodes/TaskManagerTask.scss
 delete mode 100644 src/client/views/nodes/TaskManagerTask.tsx

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 217967c52..a11f56143 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -526,16 +526,12 @@ export class DocumentOptions {
     ai_firefly_seed?: number;
     ai_firefly_prompt?: string;
 
-    // AARAV ADD DOC OPTIONS -- TASK MANAGER
-
-    /** Task start date/time picker (metadata and default) */
-    startTime?: DateInfo | DateField = new DateInfo('start date and time', /*filterable*/ false);
-    /** Task end date/time picker (metadata and default) */
-    endTime?: DateInfo | DateField = new DateInfo('end date and time', /*filterable*/ false);
-    /** Treat this as an all-day task (metadata and default) */
-    allDay?: BoolInfo | boolean = new BoolInfo('all-day task', /*filterable*/ false);
-    /** Whether the task is completed */
-    completed?: BoolInfo | boolean = new BoolInfo('whether the task is completed', /*filterable*/ false);
+    // TASK MANAGER
+    $startTime?: DateInfo | DateField = new DateInfo('start date and time', /*filterable*/ false);
+    $endTime?: DateInfo | DateField = new DateInfo('end date and time', /*filterable*/ false);
+    $allDay?: BoolInfo | boolean = new BoolInfo('whether task is all-day or not', /*filterable*/ false);
+    $completed?: BoolInfo | boolean = new BoolInfo('whether the task is completed', /*filterable*/ false);
+
     /**
      * JSON‐stringified slot configuration for ScrapbookBox
      */
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index ecbef3497..17ed63b05 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -65,7 +65,7 @@ import { PresBox, PresSlideBox } from './nodes/trails';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { SearchBox } from './search/SearchBox';
 import { StickerPalette } from './smartdraw/StickerPalette';
-import { TaskManagerTask } from './nodes/TaskManagerTask';
+import { TaskBox } from './nodes/TaskBox';
 import { ScrapbookBox } from './nodes/scrapbook/ScrapbookBox';
 
 dotenv.config();
@@ -121,7 +121,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
             StickerPalette: StickerPalette,
             FormattedTextBox,
             DailyJournal, // AARAV
-            TaskManagerTask, // AARAV
+            TaskBox, // AARAV
             ImageBox,
             FontIconBox,
             LabelBox,
diff --git a/src/client/views/nodes/TaskBox.scss b/src/client/views/nodes/TaskBox.scss
new file mode 100644
index 000000000..0fcc2f955
--- /dev/null
+++ b/src/client/views/nodes/TaskBox.scss
@@ -0,0 +1,72 @@
+.task-manager-container {
+    display: flex;
+    flex-direction: column;
+    padding: 8px;
+    gap: 10px;
+    width: 100%;
+    height: 100%;
+    box-sizing: border-box;
+}
+
+.task-manager-title {
+    width: 100%;
+    font-size: 1.25rem;
+    font-weight: 600;
+    padding: 6px 10px;
+    border: 1px solid #ccc;
+    border-radius: 6px;
+    box-sizing: border-box;
+}
+
+.task-manager-description {
+    width: 100%;
+    font-size: 1rem;
+    padding: 8px 10px;
+    border: 1px solid #ccc;
+    border-radius: 6px;
+    min-height: 40px;
+    box-sizing: border-box;
+    vertical-align: top;
+    text-align: start;
+    resize: none;
+    line-height: 1.4;
+    resize: none;
+    flex-grow: 1
+}
+
+.task-manager-checkboxes {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+}
+
+.task-manager-allday, .task-manager-complete {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    font-size: 0.95rem;
+}
+
+.task-manager-times {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+    width: 100%;
+}
+
+.task-manager-times label {
+    display: flex;
+    flex-direction: column;
+    font-size: 0.9rem;
+    font-weight: 500;
+    gap: 4px;
+}
+
+input[type="datetime-local"] {
+    width: 100%;
+    font-size: 0.9rem;
+    padding: 6px 8px;
+    border: 1px solid #ccc;
+    border-radius: 6px;
+    box-sizing: border-box;
+}
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
new file mode 100644
index 000000000..dca01817f
--- /dev/null
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -0,0 +1,298 @@
+import { action, observable, makeObservable, IReactionDisposer, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { FieldView } from './FieldView';
+import { DateField } from '../../../fields/DateField';
+import { Doc } from '../../../fields/Doc';
+
+import './TaskBox.scss';
+
+interface TaskBoxProps {
+    Document: Doc;
+}
+
+@observer
+export class TaskBox extends React.Component<TaskBoxProps> {
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(TaskBox, fieldStr);
+    }
+
+    @action
+    updateText = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
+        this.props.Document.text = e.target.value;
+    };
+
+    @action
+    updateTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.Document.title = e.target.value;
+    };
+
+    @action
+    updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.Document.$allDay = e.target.checked;
+
+        if (e.target.checked) {
+            delete this.props.Document.$startTime;
+            delete this.props.Document.$endTime;
+        }
+
+        this.setTaskDateRange();
+    };
+
+    @action
+    updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
+        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>) => {
+        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();
+    };
+
+
+
+
+    @action
+    setTaskDateRange() {
+        const doc = this.props.Document;
+
+        if (doc.$allDay) {
+            const range = typeof doc.date_range === 'string' ? doc.date_range.split('|') : [];
+            const dateStr = range[0] ?? new Date().toISOString().split('T')[0]; // default to today
+
+            doc.date_range = `${dateStr}|${dateStr}`;
+            doc.$allDay = true;
+        } else {
+            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;
+            }
+        }
+    }
+
+    @action
+    toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.Document.$completed = e.target.checked;
+    };
+
+    constructor(props: TaskBoxProps) {
+        super(props);
+        makeObservable(this);
+    }
+
+    _heightDisposer?: IReactionDisposer;
+    _widthDisposer?: IReactionDisposer;
+
+    componentDidMount() {
+        this.setTaskDateRange();
+
+        const doc = this.props.Document;
+        this._heightDisposer = reaction(
+            () => Number(doc._height),
+            height => {
+                const minHeight = Number(doc.height_min ?? 0);
+                if (!isNaN(height) && height < minHeight) {
+                    doc._height = minHeight;
+                }
+            }
+        );
+
+        this._widthDisposer = reaction(
+            () => Number(doc._width),
+            width => {
+                const minWidth = Number(doc.width_min ?? 0);
+                if (!isNaN(width) && width < minWidth) {
+                    doc._width = minWidth;
+                }
+            }
+        );
+    }
+
+    componentWillUnmount() {
+        this._heightDisposer?.();
+        this._widthDisposer?.();
+    }
+
+    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)
+            : '';
+
+
+
+        return (
+            <div className="task-manager-container">
+                <input
+                    className="task-manager-title"
+                    type="text"
+                    placeholder="Task Title"
+                    value={taskTitle}
+                    onChange={this.updateTitle}
+                    disabled={isCompleted}
+                    style={{opacity: isCompleted ? 0.7 : 1,}}
+                />
+
+                <textarea
+                className="task-manager-description"
+                placeholder="What’s your task?"
+                value={taskDesc}
+                onChange={this.updateText}
+                disabled={isCompleted}
+                style={{opacity: isCompleted ? 0.7 : 1,}}
+                />
+
+                <div className="task-manager-checkboxes">
+
+                    <label className="task-manager-allday" style={{opacity: isCompleted ? 0.7 : 1,}}>
+                        <input 
+                            type="checkbox" 
+                            checked={allDay} 
+                            onChange={this.updateAllDay} 
+                            disabled={isCompleted}
+                        />
+                        All day
+                        
+                        {allDay && (
+                        <input
+                            type="date"
+                            value={(() => {
+                                const rawRange = doc.date_range;
+                                if (typeof rawRange !== 'string') return '';
+                                const datePart = rawRange.split('|')[0];
+                                if (!datePart) return '';
+                                const d = new Date(datePart);
+                                return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
+                            })()}
+                            onChange={e => {
+                                const newDate = new Date(e.target.value);
+                                if (!isNaN(newDate.getTime())) {
+                                    const dateStr = e.target.value;
+                                    if (dateStr) {
+                                        doc.date_range = `${dateStr}T00:00:00|${dateStr}T00:00:00`;
+                                    }
+                                }
+                            }}
+                            disabled={isCompleted}
+                            style={{ marginLeft: '8px' }}
+                        />
+                    )}
+                    </label>
+
+                    <label className="task-manager-complete">
+                        <input type="checkbox" checked={isCompleted} onChange={this.toggleComplete} />
+                        Complete
+                    </label>
+
+                </div>
+
+                {!allDay && (
+                <div
+                    className="task-manager-times"
+                    style={{ opacity: isCompleted ? 0.7 : 1 }}
+                >
+                    <label>
+                        Start:
+                        <input
+                            type="datetime-local"
+                            value={startTime}
+                            onChange={this.updateStart}
+                            disabled={isCompleted}
+                        />
+                    </label>
+                    <label>
+                        End:
+                        <input
+                            type="datetime-local"
+                            value={endTime}
+                            onChange={this.updateEnd}
+                            disabled={isCompleted}
+                        />
+                    </label>
+                </div>
+            )}
+            </div>
+        );
+    }
+}
+
+Docs.Prototypes.TemplateMap.set(DocumentType.TASK, {
+    layout: { view: TaskBox, dataField: 'text' },
+    options: {
+        acl: '',
+        _height: 35,
+        _xMargin: 10,
+        _yMargin: 10,
+        _layout_autoHeight: true,
+        _layout_nativeDimEditable: true,
+        _layout_reflowVertical: true,
+        _layout_reflowHorizontal: true,
+        defaultDoubleClick: 'ignore',
+        systemIcon: 'BsCheckSquare',
+        height_min: 300,
+        width_min: 300
+    },
+});
diff --git a/src/client/views/nodes/TaskManagerTask.scss b/src/client/views/nodes/TaskManagerTask.scss
deleted file mode 100644
index 0fcc2f955..000000000
--- a/src/client/views/nodes/TaskManagerTask.scss
+++ /dev/null
@@ -1,72 +0,0 @@
-.task-manager-container {
-    display: flex;
-    flex-direction: column;
-    padding: 8px;
-    gap: 10px;
-    width: 100%;
-    height: 100%;
-    box-sizing: border-box;
-}
-
-.task-manager-title {
-    width: 100%;
-    font-size: 1.25rem;
-    font-weight: 600;
-    padding: 6px 10px;
-    border: 1px solid #ccc;
-    border-radius: 6px;
-    box-sizing: border-box;
-}
-
-.task-manager-description {
-    width: 100%;
-    font-size: 1rem;
-    padding: 8px 10px;
-    border: 1px solid #ccc;
-    border-radius: 6px;
-    min-height: 40px;
-    box-sizing: border-box;
-    vertical-align: top;
-    text-align: start;
-    resize: none;
-    line-height: 1.4;
-    resize: none;
-    flex-grow: 1
-}
-
-.task-manager-checkboxes {
-    display: flex;
-    align-items: center;
-    gap: 16px;
-}
-
-.task-manager-allday, .task-manager-complete {
-    display: flex;
-    align-items: center;
-    gap: 6px;
-    font-size: 0.95rem;
-}
-
-.task-manager-times {
-    display: flex;
-    flex-direction: column;
-    gap: 6px;
-    width: 100%;
-}
-
-.task-manager-times label {
-    display: flex;
-    flex-direction: column;
-    font-size: 0.9rem;
-    font-weight: 500;
-    gap: 4px;
-}
-
-input[type="datetime-local"] {
-    width: 100%;
-    font-size: 0.9rem;
-    padding: 6px 8px;
-    border: 1px solid #ccc;
-    border-radius: 6px;
-    box-sizing: border-box;
-}
diff --git a/src/client/views/nodes/TaskManagerTask.tsx b/src/client/views/nodes/TaskManagerTask.tsx
deleted file mode 100644
index 913c46bfb..000000000
--- a/src/client/views/nodes/TaskManagerTask.tsx
+++ /dev/null
@@ -1,301 +0,0 @@
-import { action, observable, makeObservable, IReactionDisposer, reaction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { FieldView } from './FieldView';
-import { DateField } from '../../../fields/DateField';
-import { Doc } from '../../../fields/Doc';
-
-import './TaskManagerTask.scss';
-
-interface TaskManagerProps {
-    Document: Doc;
-}
-
-@observer
-export class TaskManagerTask extends React.Component<TaskManagerProps> {
-    public static LayoutString(fieldStr: string) {
-        return FieldView.LayoutString(TaskManagerTask, fieldStr);
-    }
-
-    @action
-    updateText = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
-        this.props.Document.text = e.target.value;
-    };
-
-    @action
-    updateTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.title = e.target.value;
-    };
-
-    @action
-    updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.allDay = e.target.checked;
-
-        if (e.target.checked) {
-            delete this.props.Document.startTime;
-            delete this.props.Document.endTime;
-        }
-
-        this.setTaskDateRange();
-    };
-
-    @action
-    updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
-        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>) => {
-        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();
-    };
-
-
-
-
-    @action
-    setTaskDateRange() {
-        const doc = this.props.Document;
-
-        if (doc.allDay) {
-            const range = typeof doc.date_range === 'string' ? doc.date_range.split('|') : [];
-            const dateStr = range[0] ?? new Date().toISOString().split('T')[0]; // default to today
-
-            doc.date_range = `${dateStr}|${dateStr}`;
-            doc.allDay = true;
-        } else {
-            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;
-            }
-        }
-    }
-
-
-
-
-    @action
-    toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.completed = e.target.checked;
-    };
-
-    constructor(props: TaskManagerProps) {
-        super(props);
-        makeObservable(this);
-    }
-
-    _heightDisposer?: IReactionDisposer;
-    _widthDisposer?: IReactionDisposer;
-
-    componentDidMount() {
-        this.setTaskDateRange();
-
-        const doc = this.props.Document;
-        this._heightDisposer = reaction(
-            () => Number(doc._height),
-            height => {
-                const minHeight = Number(doc.height_min ?? 0);
-                if (!isNaN(height) && height < minHeight) {
-                    doc._height = minHeight;
-                }
-            }
-        );
-
-        this._widthDisposer = reaction(
-            () => Number(doc._width),
-            width => {
-                const minWidth = Number(doc.width_min ?? 0);
-                if (!isNaN(width) && width < minWidth) {
-                    doc._width = minWidth;
-                }
-            }
-        );
-    }
-
-    componentWillUnmount() {
-        this._heightDisposer?.();
-        this._widthDisposer?.();
-    }
-
-    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)
-            : '';
-
-
-
-        return (
-            <div className="task-manager-container">
-                <input
-                    className="task-manager-title"
-                    type="text"
-                    placeholder="Task Title"
-                    value={taskTitle}
-                    onChange={this.updateTitle}
-                    disabled={isCompleted}
-                    style={{opacity: isCompleted ? 0.7 : 1,}}
-                />
-
-                <textarea
-                className="task-manager-description"
-                placeholder="What’s your task?"
-                value={taskDesc}
-                onChange={this.updateText}
-                disabled={isCompleted}
-                style={{opacity: isCompleted ? 0.7 : 1,}}
-                />
-
-                <div className="task-manager-checkboxes">
-
-                    <label className="task-manager-allday" style={{opacity: isCompleted ? 0.7 : 1,}}>
-                        <input 
-                            type="checkbox" 
-                            checked={allDay} 
-                            onChange={this.updateAllDay} 
-                            disabled={isCompleted}
-                        />
-                        All day
-                        
-                        {allDay && (
-                        <input
-                            type="date"
-                            value={(() => {
-                                const rawRange = doc.date_range;
-                                if (typeof rawRange !== 'string') return '';
-                                const datePart = rawRange.split('|')[0];
-                                if (!datePart) return '';
-                                const d = new Date(datePart);
-                                return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
-                            })()}
-                            onChange={e => {
-                                const newDate = new Date(e.target.value);
-                                if (!isNaN(newDate.getTime())) {
-                                    const dateStr = e.target.value;
-                                    if (dateStr) {
-                                        doc.date_range = `${dateStr}T00:00:00|${dateStr}T00:00:00`;
-                                    }
-                                }
-                            }}
-                            disabled={isCompleted}
-                            style={{ marginLeft: '8px' }}
-                        />
-                    )}
-                    </label>
-
-                    <label className="task-manager-complete">
-                        <input type="checkbox" checked={isCompleted} onChange={this.toggleComplete} />
-                        Complete
-                    </label>
-
-                </div>
-
-                {!allDay && (
-                <div
-                    className="task-manager-times"
-                    style={{ opacity: isCompleted ? 0.7 : 1 }}
-                >
-                    <label>
-                        Start:
-                        <input
-                            type="datetime-local"
-                            value={startTime}
-                            onChange={this.updateStart}
-                            disabled={isCompleted}
-                        />
-                    </label>
-                    <label>
-                        End:
-                        <input
-                            type="datetime-local"
-                            value={endTime}
-                            onChange={this.updateEnd}
-                            disabled={isCompleted}
-                        />
-                    </label>
-                </div>
-            )}
-            </div>
-        );
-    }
-}
-
-Docs.Prototypes.TemplateMap.set(DocumentType.TASK, {
-    layout: { view: TaskManagerTask, dataField: 'text' },
-    options: {
-        acl: '',
-        _height: 35,
-        _xMargin: 10,
-        _yMargin: 10,
-        _layout_autoHeight: true,
-        _layout_nativeDimEditable: true,
-        _layout_reflowVertical: true,
-        _layout_reflowHorizontal: true,
-        defaultDoubleClick: 'ignore',
-        systemIcon: 'BsCheckSquare',
-        height_min: 300,
-        width_min: 300
-    },
-});
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index ce58ac9fa..bebd86ac2 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -68,7 +68,7 @@ export class CalendarBox extends CollectionSubView() {
     @computed get calendarEvents(): EventSourceInput | undefined {
         return this.childDocs.map(doc => {
             const { start, end } = dateRangeStrToDates(StrCast(doc.date_range));
-            const isCompleted = BoolCast(doc.completed); // AARAV ADD
+            const isCompleted = BoolCast(doc.$completed); // AARAV ADD
             return {
                 title: StrCast(doc.title),
                 start,
@@ -76,7 +76,7 @@ export class CalendarBox extends CollectionSubView() {
                 groupId: doc[Id],
                 startEditable: true,
                 endEditable: true,
-                allDay: BoolCast(doc.allDay),
+                allDay: BoolCast(doc.$allDay),
                 classNames: ['mother', isCompleted ? 'completed-task' : ''], // will determine the style
                 editable: true, // subject to change in the future
                 backgroundColor: this.eventToColor(doc),
@@ -143,16 +143,16 @@ export class CalendarBox extends CollectionSubView() {
         doc.date_range = `${startDate.toISOString()}|${endDate.toISOString()}`;
 
         const allDayStatus = arg.event.allDay ?? false;
-        if (doc.allDay !== allDayStatus) {
-            doc.allDay = allDayStatus;
+        if (doc.$allDay !== allDayStatus) {
+            doc.$allDay = allDayStatus;
         }
 
-        if (doc.allDay) {
-            delete doc.startTime;
-            delete doc.endTime;
+        if (doc.$allDay) {
+            delete doc.$startTime;
+            delete doc.$endTime;
         } else {
-            doc.startTime = new DateField(startDate);
-            doc.endTime = new DateField(endDate);
+            doc.$startTime = new DateField(startDate);
+            doc.$endTime = new DateField(endDate);
         }
     };
 
@@ -206,7 +206,7 @@ export class CalendarBox extends CollectionSubView() {
 
                       if (doc.type === 'task') {
                           const checkButton = document.createElement('button');
-                          checkButton.innerText = doc.completed ? '✅' : '⬜';
+                          checkButton.innerText = doc.$completed ? '✅' : '⬜';
                           checkButton.style.position = 'absolute';
                           checkButton.style.right = '5px';
                           checkButton.style.top = '50%';
@@ -221,7 +221,7 @@ export class CalendarBox extends CollectionSubView() {
 
                           checkButton.onclick = ev => {
                               ev.stopPropagation();
-                              doc.completed = !doc.completed;
+                              doc.$completed = !doc.$completed;
                               this._calendar?.refetchEvents();
                           };
 
-- 
cgit v1.2.3-70-g09d2


From da0616f4f3719f14ba6fd1a9580730a576e198e2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 14 May 2025 23:31:31 -0400
Subject: fixed selecting allDay for calendar. fixed turning off allDay when
 moving calendar events.  fixed undoing calendar drags.

---
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 2b20a666d..c1198f328 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -17,6 +17,7 @@ import { ContextMenu } from '../../ContextMenu';
 import { DocumentView } from '../DocumentView';
 import { OpenWhere } from '../OpenWhere';
 import './CalendarBox.scss';
+import { undoable } from '../../../util/UndoManager';
 
 type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
 
@@ -27,15 +28,15 @@ export class CalendarBox extends CollectionSubView() {
     _observer: ResizeObserver | undefined;
     _eventsDisposer: IReactionDisposer | undefined;
     _selectDisposer: IReactionDisposer | undefined;
+    _isMultiMonth: boolean | undefined;
+
+    @observable _multiMonth = 0;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
         makeObservable(this);
     }
 
-    @observable _multiMonth = 0;
-    isMultiMonth: boolean | undefined;
-
     componentDidMount(): void {
         this._props.setContentViewBox?.(this);
         this._eventsDisposer = reaction(
@@ -52,7 +53,7 @@ export class CalendarBox extends CollectionSubView() {
                         type: 'CHANGE_DATE',
                         dateMarker: state.dateEnv.createMarker(initialDate.start),
                     });
-                setTimeout(() => (initialDate.start.toISOString() !== initialDate.end.toISOString() ? this._calendar?.select(initialDate.start, initialDate.end) : this._calendar?.select(initialDate.start)));
+                setTimeout(() => initialDate.start.toISOString() !== initialDate.end.toISOString() && this._calendar?.select(initialDate.start, initialDate.end));
             },
             { fireImmediately: true }
         );
@@ -95,7 +96,7 @@ export class CalendarBox extends CollectionSubView() {
     // Choose a calendar view based on the date range
     @computed get calendarViewType(): CalendarView {
         if (this.dataDoc[this.fieldKey + '_calendarType']) return StrCast(this.dataDoc[this.fieldKey + '_calendarType']) as CalendarView;
-        if (this.isMultiMonth) return 'multiMonth';
+        if (this._isMultiMonth) return 'multiMonth';
         const { start, end } = this.dateRangeStrDates;
         if (start.getFullYear() !== end.getFullYear() || start.getMonth() !== end.getMonth()) return 'multiMonth';
         if (Math.abs(start.getDay() - end.getDay()) > 7) return 'dayGridMonth';
@@ -122,10 +123,13 @@ export class CalendarBox extends CollectionSubView() {
         return false;
     };
 
-    handleEventDrop = (arg: EventDropArg) => {
+    handleEventDrop = undoable((arg: EventDropArg) => {
         const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
-        doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
-    };
+        if (doc && arg.event.start) {
+            doc.$allDay = false;
+            doc.$date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString();
+        }
+    }, 'change event date');
 
     handleEventClick = (arg: EventClickArg) => {
         const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
@@ -163,7 +167,7 @@ export class CalendarBox extends CollectionSubView() {
                   displayEventEnd: false,
                   select: info => {
                       const start = dateRangeStrToDates(info.startStr).start.toISOString();
-                      const end = dateRangeStrToDates(info.endStr).start.toISOString();
+                      const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
                       this.dataDoc.date = start + '|' + end;
                   },
                   aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height),
-- 
cgit v1.2.3-70-g09d2


From 3f144d7bad65af2424d27e2e3a58eecd0d0e114b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 May 2025 13:34:56 -0400
Subject: cleaned up calendarBox to have a more reactive UI and to fix some
 bugs with selecting and autoscroling.

---
 package-lock.json                                  |  18 ++-
 package.json                                       |   7 +-
 .../views/nodes/calendarBox/CalendarBox.scss       |   3 +-
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 141 ++++++++++++---------
 4 files changed, 102 insertions(+), 67 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 28b81dbc7..a8752cb5d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,9 +28,10 @@
                 "@fortawesome/free-regular-svg-icons": "^6.5.1",
                 "@fortawesome/free-solid-svg-icons": "^6.5.1",
                 "@fortawesome/react-fontawesome": "^0.2.2",
-                "@fullcalendar/core": "^6.1.15",
-                "@fullcalendar/daygrid": "^6.1.10",
-                "@fullcalendar/multimonth": "^6.1.10",
+                "@fullcalendar/core": "^6.1.17",
+                "@fullcalendar/daygrid": "^6.1.17",
+                "@fullcalendar/multimonth": "^6.1.17",
+                "@fullcalendar/react": "^6.1.17",
                 "@fullcalendar/timegrid": "^6.1.15",
                 "@internationalized/date": "^3.5.0",
                 "@mozilla/readability": "^0.6.0",
@@ -4581,6 +4582,17 @@
                 "@fullcalendar/core": "~6.1.17"
             }
         },
+        "node_modules/@fullcalendar/react": {
+            "version": "6.1.17",
+            "resolved": "https://registry.npmjs.org/@fullcalendar/react/-/react-6.1.17.tgz",
+            "integrity": "sha512-AA8soHhlfRH5dUeqHnfAtzDiXa2vrgWocJSK/F5qzw/pOxc9MqpuoS/nQBROWtHHg6yQUg3DoGqOOhi7dmylXQ==",
+            "license": "MIT",
+            "peerDependencies": {
+                "@fullcalendar/core": "~6.1.17",
+                "react": "^16.7.0 || ^17 || ^18 || ^19",
+                "react-dom": "^16.7.0 || ^17 || ^18 || ^19"
+            }
+        },
         "node_modules/@fullcalendar/timegrid": {
             "version": "6.1.17",
             "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.17.tgz",
diff --git a/package.json b/package.json
index 6a579efd2..382c71aed 100644
--- a/package.json
+++ b/package.json
@@ -112,9 +112,10 @@
         "@fortawesome/free-regular-svg-icons": "^6.5.1",
         "@fortawesome/free-solid-svg-icons": "^6.5.1",
         "@fortawesome/react-fontawesome": "^0.2.2",
-        "@fullcalendar/core": "^6.1.15",
-        "@fullcalendar/daygrid": "^6.1.10",
-        "@fullcalendar/multimonth": "^6.1.10",
+        "@fullcalendar/core": "^6.1.17",
+        "@fullcalendar/daygrid": "^6.1.17",
+        "@fullcalendar/multimonth": "^6.1.17",
+        "@fullcalendar/react": "^6.1.17",
         "@fullcalendar/timegrid": "^6.1.15",
         "@internationalized/date": "^3.5.0",
         "@mozilla/readability": "^0.6.0",
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
index f8ac4b2d1..e3e0bae4b 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.scss
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -3,7 +3,8 @@
     width: 100%;
     height: 100%;
     transform-origin: top left;
-    .calendarBox-wrapper {
+    overflow: auto;
+    > div {
         width: 100%;
         height: 100%;
         .fc-timegrid-body {
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index c1198f328..2d6c8c9a4 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -3,13 +3,14 @@ import dayGridPlugin from '@fullcalendar/daygrid';
 import interactionPlugin from '@fullcalendar/interaction';
 import multiMonthPlugin from '@fullcalendar/multimonth';
 import timeGrid from '@fullcalendar/timegrid';
-import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
+import FullCalendar from '@fullcalendar/react';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, trace, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { dateRangeStrToDates } from '../../../../ClientUtils';
 import { Doc } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, StrCast } from '../../../../fields/Types';
 import { DocServer } from '../../../DocServer';
 import { DragManager } from '../../../util/DragManager';
 import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
@@ -23,7 +24,7 @@ type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDa
 
 @observer
 export class CalendarBox extends CollectionSubView() {
-    _calendarRef: HTMLDivElement | null = null;
+    _calendarRef: FullCalendar | null = null;
     _calendar: Calendar | undefined;
     _observer: ResizeObserver | undefined;
     _eventsDisposer: IReactionDisposer | undefined;
@@ -148,56 +149,85 @@ export class CalendarBox extends CollectionSubView() {
     };
 
     // https://fullcalendar.io
-    renderCalendar = () => {
-        const cal = !this._calendarRef
-            ? null
-            : (this._calendar = new Calendar(this._calendarRef, {
-                  plugins: [multiMonthPlugin, dayGridPlugin, timeGrid, interactionPlugin],
-                  headerToolbar: {
-                      left: 'prev,next today',
-                      center: 'title',
-                      right: 'multiMonth dayGridMonth timeGridWeek timeGridDay',
-                  },
-                  selectable: true,
-                  initialView: this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType,
-                  initialDate: this.dateSelect.start,
-                  navLinks: true,
-                  editable: false,
-                  displayEventTime: false,
-                  displayEventEnd: false,
-                  select: info => {
-                      const start = dateRangeStrToDates(info.startStr).start.toISOString();
-                      const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
-                      this.dataDoc.date = start + '|' + end;
-                  },
-                  aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height),
-                  events: this.calendarEvents,
-                  eventClick: this.handleEventClick,
-                  eventDrop: this.handleEventDrop,
-                  eventDidMount: arg => {
-                      arg.el.addEventListener('pointerdown', ev => {
-                          ev.button && ev.stopPropagation();
-                      });
-                      if (navigator.userAgent.includes('Macintosh')) {
-                          arg.el.addEventListener('pointerup', ev => {
-                              ev.button && ev.stopPropagation();
-                              ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
-                          });
-                      }
-                      arg.el.addEventListener('contextmenu', ev => {
-                          if (!navigator.userAgent.includes('Macintosh')) {
-                              this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
+    @computed get renderCalendar() {
+        const availableWidth = this._props.PanelWidth() / (this._props.DocumentView?.().UIBtnScaling ?? 1);
+        const btn = (text: string, view: string | (() => void), hint: string) => ({ text, hint, click: typeof view === 'string' ? () => this._calendarRef?.getApi().changeView(view) : view });
+        return (
+            <FullCalendar
+                ref={r => (this._calendarRef = r)}
+                customButtons={{
+                    nowBtn: btn('Now', () => this._calendarRef?.getApi().gotoDate(new Date()), 'Go to Today'),
+                    multiBtn: btn('M+', 'multiMonth', 'Multiple Month View'),
+                    monthBtn: btn('M', 'dayGridMonth', 'Month View'),
+                    weekBtn: btn('W', 'timeGridWeek', 'Week View'),
+                    dayBtn: btn('D', 'timeGridDay', 'Day View'),
+                }}
+                headerToolbar={
+                    availableWidth > 450
+                        ? {
+                              left: 'prev,next nowBtn',
+                              center: 'title',
+                              right: 'multiBtn monthBtn weekBtn dayBtn',
                           }
-                          ev.stopPropagation();
-                          ev.preventDefault();
-                      });
-                  },
-              }));
-        cal?.render();
-        setTimeout(() => cal?.view.calendar.select(this.dateSelect.start, this.dateSelect.end));
-    };
+                        : availableWidth > 300
+                          ? {
+                                left: 'prev,next',
+                                center: 'title',
+                                right: '',
+                            }
+                          : {
+                                left: '',
+                                center: 'title',
+                                right: '',
+                            }
+                }
+                selectable={true}
+                initialView={this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType}
+                initialDate={untracked(() => this.dateSelect.start)}
+                navLinks={true}
+                editable={false}
+                // expandRows={true}
+                // handleWindowResize={true}
+                displayEventTime={false}
+                displayEventEnd={false}
+                plugins={[multiMonthPlugin, dayGridPlugin, timeGrid, interactionPlugin]}
+                aspectRatio={this._props.PanelWidth() / this._props.PanelHeight()}
+                weekends={false}
+                events={this.calendarEvents}
+                eventClick={this.handleEventClick}
+                eventDrop={this.handleEventDrop}
+                unselectAuto={false}
+                // unselect={() => {}}
+                select={info => {
+                    const start = dateRangeStrToDates(info.startStr).start.toISOString();
+                    const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
+                    this.dataDoc.date = start + '|' + end;
+                }}
+                // eventContent={() => {
+                //     return null;
+                // }}
+                eventDidMount={arg => {
+                    arg.el.addEventListener('pointerdown', ev => ev.button && ev.stopPropagation());
+                    if (navigator.userAgent.includes('Macintosh')) {
+                        arg.el.addEventListener('pointerup', ev => {
+                            ev.button && ev.stopPropagation();
+                            ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
+                        });
+                    }
+                    arg.el.addEventListener('contextmenu', ev => {
+                        if (!navigator.userAgent.includes('Macintosh')) {
+                            this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
+                        }
+                        ev.stopPropagation();
+                        ev.preventDefault();
+                    });
+                }}
+            />
+        );
+    }
 
     render() {
+        trace();
         return (
             <div
                 key={this.calendarViewType}
@@ -221,17 +251,8 @@ export class CalendarBox extends CollectionSubView() {
                 ref={r => {
                     this.createDashEventsTarget(r);
                     this.fixWheelEvents(r, this._props.isContentActive);
-
-                    if (r) {
-                        this._observer?.disconnect();
-                        (this._observer = new ResizeObserver(() => {
-                            this._calendar?.setOption('aspectRatio', NumCast(this.Document.width) / NumCast(this.Document.height));
-                            this._calendar?.updateSize();
-                        })).observe(r);
-                        this.renderCalendar();
-                    }
                 }}>
-                <div className="calendarBox-wrapper" ref={r => (this._calendarRef = r)} />
+                {this.renderCalendar}
             </div>
         );
     }
-- 
cgit v1.2.3-70-g09d2


From 3741baee864a8bec757b4e50a58698a0064293be Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 May 2025 14:21:32 -0400
Subject: scroll wheel fixes for calendarBox

---
 src/client/views/nodes/calendarBox/CalendarBox.scss | 7 +++++++
 src/client/views/nodes/calendarBox/CalendarBox.tsx  | 5 ++---
 2 files changed, 9 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
index e3e0bae4b..a607846df 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.scss
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -1,3 +1,4 @@
+.calendarBox-interactive,
 .calendarBox {
     display: flex;
     width: 100%;
@@ -5,6 +6,7 @@
     transform-origin: top left;
     overflow: auto;
     > div {
+        pointer-events: none;
         width: 100%;
         height: 100%;
         .fc-timegrid-body {
@@ -24,3 +26,8 @@
         }
     }
 }
+.calendarBox-interactive {
+    > div {
+        pointer-events: unset;
+    }
+}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 2d6c8c9a4..504dc2559 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -4,7 +4,7 @@ import interactionPlugin from '@fullcalendar/interaction';
 import multiMonthPlugin from '@fullcalendar/multimonth';
 import timeGrid from '@fullcalendar/timegrid';
 import FullCalendar from '@fullcalendar/react';
-import { IReactionDisposer, action, computed, makeObservable, observable, reaction, trace, untracked } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, untracked } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { dateRangeStrToDates } from '../../../../ClientUtils';
@@ -227,11 +227,10 @@ export class CalendarBox extends CollectionSubView() {
     }
 
     render() {
-        trace();
         return (
             <div
                 key={this.calendarViewType}
-                className="calendarBox"
+                className={`calendarBox${this._props.isContentActive() ? '-interactive' : ''}`}
                 onPointerDown={e => {
                     setTimeout(
                         action(() => {
-- 
cgit v1.2.3-70-g09d2


From a7afce10c47724156510a8665e7e1841566082e8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Jun 2025 18:04:33 -0400
Subject: from last

---
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 40 ++++++++++++----------
 1 file changed, 22 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 40064ad4d..f21eec604 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -1,4 +1,4 @@
-import { Calendar, EventClickArg, EventDropArg, EventSourceInput } from '@fullcalendar/core';
+import { Calendar, DateSelectArg, EventClickArg, EventDropArg, EventMountArg, EventSourceInput } from '@fullcalendar/core';
 import { EventResizeDoneArg } from '@fullcalendar/interaction';
 import dayGridPlugin from '@fullcalendar/daygrid';
 import interactionPlugin from '@fullcalendar/interaction';
@@ -21,6 +21,7 @@ import { OpenWhere } from '../OpenWhere';
 import './CalendarBox.scss';
 import { DateField } from '../../../../fields/DateField';
 import { undoable } from '../../../util/UndoManager';
+import { DocumentType } from '../../../documents/DocumentTypes';
 
 type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
 
@@ -40,6 +41,10 @@ export class CalendarBox extends CollectionSubView() {
         makeObservable(this);
     }
 
+    @computed get calTypeFieldKey() {
+        return this.fieldKey + '_calendarType';
+    }
+
     componentDidMount(): void {
         this._props.setContentViewBox?.(this);
         this._eventsDisposer = reaction(
@@ -99,7 +104,7 @@ export class CalendarBox extends CollectionSubView() {
 
     // Choose a calendar view based on the date range
     @computed get calendarViewType(): CalendarView {
-        if (this.dataDoc[this.fieldKey + '_calendarType']) return StrCast(this.dataDoc[this.fieldKey + '_calendarType']) as CalendarView;
+        if (this.dataDoc[this.calTypeFieldKey]) return StrCast(this.dataDoc[this.calTypeFieldKey]) as CalendarView;
         if (this._isMultiMonth) return 'multiMonth';
         const { start, end } = this.dateRangeStrDates;
         if (start.getFullYear() !== end.getFullYear() || start.getMonth() !== end.getMonth()) return 'multiMonth';
@@ -109,11 +114,9 @@ export class CalendarBox extends CollectionSubView() {
 
     // TODO: Return a different color based on the event type
     eventToColor = (event: Doc): string => {
-        const docType = StrCast(event.type);
-        if (docType === 'task') {
-            return '#20B2AA'; // teal for tasks
-        }
-        return 'red';
+        return StrCast(event.type) === DocumentType.TASK
+            ? '#20B2AA' // teal for tasks
+            : 'red';
     };
 
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -131,7 +134,7 @@ export class CalendarBox extends CollectionSubView() {
         return false;
     };
 
-    handleEventDrop = undoable((arg: EventDropArg | EventResizeDoneArg ) => {
+    handleEventDrop = undoable((arg: EventDropArg | EventResizeDoneArg) => {
         const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
         // doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
         if (!doc || !arg.event.start) return;
@@ -179,7 +182,7 @@ export class CalendarBox extends CollectionSubView() {
         const btn = (text: string, view: string | (() => void), hint: string) => ({ text, hint, click: typeof view === 'string' ? () => this._calendarRef?.getApi().changeView(view) : view });
         return (
             <FullCalendar
-                ref={(r:any) => (this._calendarRef = r)}
+                ref={(r: unknown) => (this._calendarRef = r as FullCalendar)}
                 customButtons={{
                     nowBtn: btn('Now', () => this._calendarRef?.getApi().gotoDate(new Date()), 'Go to Today'),
                     multiBtn: btn('M+', 'multiMonth', 'Multiple Month View'),
@@ -223,7 +226,7 @@ export class CalendarBox extends CollectionSubView() {
                 eventDrop={this.handleEventDrop}
                 unselectAuto={false}
                 // unselect={() => {}}
-                select={(info:any) => {
+                select={(info: DateSelectArg) => {
                     const start = dateRangeStrToDates(info.startStr).start.toISOString();
                     const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
                     this.dataDoc.date = start + '|' + end;
@@ -231,7 +234,8 @@ export class CalendarBox extends CollectionSubView() {
                 // eventContent={() => {
                 //     return null;
                 // }}
-                eventDidMount={(arg:any) => { const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
+                eventDidMount={(arg: EventMountArg) => {
+                    const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
                     if (!doc) return;
 
                     if (doc.type === 'task') {
@@ -258,14 +262,14 @@ export class CalendarBox extends CollectionSubView() {
                         arg.el.style.position = 'relative';
                         arg.el.appendChild(checkButton);
                     }
-                    arg.el.addEventListener('pointerdown', (ev:any) => ev.button && ev.stopPropagation());
+                    arg.el.addEventListener('pointerdown', ev => ev.button && ev.stopPropagation());
                     if (navigator.userAgent.includes('Macintosh')) {
-                        arg.el.addEventListener('pointerup', (ev:any) => {
+                        arg.el.addEventListener('pointerup', ev => {
                             ev.button && ev.stopPropagation();
                             ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
                         });
                     }
-                    arg.el.addEventListener('contextmenu', (ev:any) => {
+                    arg.el.addEventListener('contextmenu', ev => {
                         if (!navigator.userAgent.includes('Macintosh')) {
                             this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
                         }
@@ -286,10 +290,10 @@ export class CalendarBox extends CollectionSubView() {
                     setTimeout(
                         action(() => {
                             const cname = (e.nativeEvent.target as HTMLButtonElement)?.className ?? '';
-                            if (cname.includes('multiMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'multiMonth';
-                            if (cname.includes('dayGridMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'dayGridMonth';
-                            if (cname.includes('timeGridWeek')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridWeek';
-                            if (cname.includes('timeGridDay')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridDay';
+                            if (cname.includes('multiMonth')) this.dataDoc[this.calTypeFieldKey] = 'multiMonth';
+                            if (cname.includes('dayGridMonth')) this.dataDoc[this.calTypeFieldKey] = 'dayGridMonth';
+                            if (cname.includes('timeGridWeek')) this.dataDoc[this.calTypeFieldKey] = 'timeGridWeek';
+                            if (cname.includes('timeGridDay')) this.dataDoc[this.calTypeFieldKey] = 'timeGridDay';
                         })
                     );
                 }}
-- 
cgit v1.2.3-70-g09d2


From 57e1862e58e89a505547d817123c04079854814f Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Jun 2025 18:58:18 -0400
Subject: changed field names for tasks to start with task_.  changed calendar
 date and range to start with _calendar

---
 src/client/documents/Documents.ts                  |  18 +-
 src/client/util/CalendarManager.tsx                |   2 +-
 src/client/views/nodes/TaskBox.tsx                 | 197 ++++++++-------------
 src/client/views/nodes/calendarBox/CalendarBox.tsx |  41 ++---
 .../views/nodes/formattedText/DailyJournal.tsx     |  70 ++------
 5 files changed, 124 insertions(+), 204 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0b6385466..4ad9c9bd8 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -11,7 +11,7 @@ import { List } from '../../fields/List';
 import { RichTextField } from '../../fields/RichTextField';
 import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
 import { ComputedField, ScriptField } from '../../fields/ScriptField';
-import { DocCast, ScriptCast, StrCast } from '../../fields/Types';
+import { ScriptCast, StrCast } from '../../fields/Types';
 import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField';
 import { SharingPermissions } from '../../fields/util';
 import { PointData } from '../../pen-gestures/GestureTypes';
@@ -182,8 +182,6 @@ export class DocumentOptions {
     identifier?: STRt = new StrInfo('documentIcon displayed for each doc as "d[x]"', false);
     _rotation?: NUMt = new NumInfo('Amount of rotation on a document in degrees', false);
 
-    date_range?: STRt = new StrInfo('date range for calendar', false);
-
     chat?: STRt = new StrInfo('fields related to chatBox', false);
     chat_history?: STRt = new StrInfo('chat history for chatbox', false);
     chat_thread_id?: STRt = new StrInfo('thread id for chatbox', false);
@@ -525,10 +523,14 @@ export class DocumentOptions {
     ai_generatedDocs?: List<Doc>; // list of documents generated by GAI engine
 
     // TASK MANAGER
-    $startTime?: DateInfo | DateField = new DateInfo('start date and time', /*filterable*/ false);
-    $endTime?: DateInfo | DateField = new DateInfo('end date and time', /*filterable*/ false);
-    $allDay?: BoolInfo | boolean = new BoolInfo('whether task is all-day or not', /*filterable*/ false);
-    $completed?: BoolInfo | boolean = new BoolInfo('whether the task is completed', /*filterable*/ false);
+    $task_dateRange?: STRt = new StrInfo('date range for calendar', false);
+    $task_startTime?: DateInfo | DateField = new DateInfo('start date and time', /*filterable*/ false);
+    $task_endTime?: DateInfo | DateField = new DateInfo('end date and time', /*filterable*/ false);
+    $task_allDay?: BoolInfo | boolean = new BoolInfo('whether task is all-day or not', /*filterable*/ false);
+    $task_completed?: BoolInfo | boolean = new BoolInfo('whether the task is completed', /*filterable*/ false);
+
+    _calendar_date?: DateInfo | DateField = new DateInfo('current selected date of a calendar', /*filterable*/ false);
+    _calendar_dateRange?: STRt = new StrInfo('date range shown on a calendar', false);
 
     /**
      * JSON‐stringified slot configuration for ScrapbookBox
@@ -978,6 +980,7 @@ export namespace Docs {
             );
         }
 
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         export function TaskDocument(text = '', options: DocumentOptions = {}, fieldKey = 'text') {
             return InstanceFromProto(
                 Prototypes.get(DocumentType.TASK),
@@ -1070,6 +1073,7 @@ export namespace Docs {
                 _layout_nativeDimEditable: true,
                 _layout_reflowHorizontal: true,
                 _layout_reflowVertical: true,
+                calendar: '',
                 ...options,
                 _type_collection: CollectionViewType.Calendar,
             });
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx
index b50e39c02..a8460ed4b 100644
--- a/src/client/util/CalendarManager.tsx
+++ b/src/client/util/CalendarManager.tsx
@@ -156,7 +156,7 @@ export class CalendarManager extends ObservableReactComponent<object> {
             const subDocEmbedding = Doc.MakeEmbedding(targetDoc); // embedding
             console.log('subdoc embedding', subDocEmbedding);
             subDocEmbedding.embedContainer = calendar; // set embed container
-            subDocEmbedding.date_range = `${startDateStr}|${endDateStr}`; // set subDoc date range
+            subDocEmbedding.task_dateRange = `${startDateStr}|${endDateStr}`; // set subDoc date range
 
             Doc.AddDocToList(calendar, 'data', subDocEmbedding); // add embedded subDoc to calendar
 
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
index ff1c70b90..9d59746f8 100644
--- a/src/client/views/nodes/TaskBox.tsx
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -1,4 +1,4 @@
-import { action, observable, makeObservable, IReactionDisposer, reaction } from 'mobx';
+import { action, makeObservable, IReactionDisposer, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Docs } from '../../documents/Documents';
@@ -22,11 +22,10 @@ interface TaskBoxProps {
  */
 @observer
 export class TaskBox extends React.Component<TaskBoxProps> {
-
     /**
-     * Method to reuturn the 
-     * @param fieldStr 
-     * @returns 
+     * Method to reuturn the
+     * @param fieldStr
+     * @returns
      */
     public static LayoutString(fieldStr: string) {
         return FieldView.LayoutString(TaskBox, fieldStr);
@@ -57,11 +56,11 @@ export class TaskBox extends React.Component<TaskBoxProps> {
      */
     @action
     updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.$allDay = e.target.checked;
+        this.props.Document.$task_allDay = e.target.checked;
 
         if (e.target.checked) {
-            delete this.props.Document.$startTime;
-            delete this.props.Document.$endTime;
+            delete this.props.Document.$task_startTime;
+            delete this.props.Document.$task_endTime;
         }
 
         this.setTaskDateRange();
@@ -75,16 +74,16 @@ export class TaskBox extends React.Component<TaskBoxProps> {
     updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
         const newStart = new Date(e.target.value);
 
-        this.props.Document.$startTime = new DateField(newStart);
+        this.props.Document.$task_startTime = new DateField(newStart);
 
-        const endDate = this.props.Document.$endTime instanceof DateField ? this.props.Document.$endTime.date : undefined;
+        const endDate = this.props.Document.$task_endTime instanceof DateField ? this.props.Document.$task_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.props.Document.$task_endTime = new DateField(adjustedEnd);
         }
 
         this.setTaskDateRange();
@@ -98,16 +97,16 @@ export class TaskBox extends React.Component<TaskBoxProps> {
     updateEnd = (e: React.ChangeEvent<HTMLInputElement>) => {
         const newEnd = new Date(e.target.value);
 
-        this.props.Document.$endTime = new DateField(newEnd);
+        this.props.Document.$task_endTime = new DateField(newEnd);
 
-        const startDate = this.props.Document.$startTime instanceof DateField ? this.props.Document.$startTime.date : undefined;
+        const startDate = this.props.Document.$task_startTime instanceof DateField ? this.props.Document.$task_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.props.Document.$task_startTime = new DateField(adjustedStart);
         }
 
         this.setTaskDateRange();
@@ -120,21 +119,21 @@ export class TaskBox extends React.Component<TaskBoxProps> {
     setTaskDateRange() {
         const doc = this.props.Document;
 
-        if (doc.$allDay) {
-            const range = typeof doc.date_range === 'string' ? doc.date_range.split('|') : [];
+        if (doc.$task_allDay) {
+            const range = typeof doc.$task_dateRange === 'string' ? doc.$task_dateRange.split('|') : [];
             const dateStr = range[0] ?? new Date().toISOString().split('T')[0]; // default to today
 
-            doc.date_range = `${dateStr}|${dateStr}`;
-            doc.$allDay = true;
+            doc.$task_dateRange = `${dateStr}|${dateStr}`;
+            doc.$task_allDay = true;
         } else {
-            const startField = doc.$startTime;
-            const endField = doc.$endTime;
+            const startField = doc.$task_startTime;
+            const endField = doc.$task_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;
+                doc.$task_dateRange = `${startDate.toISOString()}|${endDate.toISOString()}`;
+                doc.$task_allDay = false;
             }
         }
     }
@@ -146,12 +145,12 @@ export class TaskBox extends React.Component<TaskBoxProps> {
 
     @action
     toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
-        this.props.Document.$completed = e.target.checked;
+        this.props.Document.$task_completed = e.target.checked;
     };
 
     /**
      * Constructor for the task box
-     * @param props - props containing the document reference 
+     * @param props - props containing the document reference
      */
 
     constructor(props: TaskBoxProps) {
@@ -193,134 +192,81 @@ export class TaskBox extends React.Component<TaskBoxProps> {
     }
 
     /**
-     * Method to render the task box 
+     * Method to render the task box
      * @returns - HTML with taskbox components
      */
 
     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())
-            );
+            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 allDay = !!doc.$task_allDay;
+        const isCompleted = !!this.props.Document.$task_completed;
 
+        const startTime = doc.$task_startTime instanceof DateField && doc.$task_startTime.date instanceof Date ? toLocalDateTimeString(doc.$task_startTime.date) : '';
 
+        const endTime = doc.$task_endTime instanceof DateField && doc.$task_endTime.date instanceof Date ? toLocalDateTimeString(doc.$task_endTime.date) : '';
 
         return (
             <div className="task-manager-container">
-                <input
-                    className="task-manager-title"
-                    type="text"
-                    placeholder="Task Title"
-                    value={taskTitle}
-                    onChange={this.updateTitle}
-                    disabled={isCompleted}
-                    style={{opacity: isCompleted ? 0.7 : 1,}}
-                />
-
-                <textarea
-                className="task-manager-description"
-                placeholder="What’s your task?"
-                value={taskDesc}
-                onChange={this.updateText}
-                disabled={isCompleted}
-                style={{opacity: isCompleted ? 0.7 : 1,}}
-                />
+                <input className="task-manager-title" type="text" placeholder="Task Title" value={taskTitle} onChange={this.updateTitle} disabled={isCompleted} style={{ opacity: isCompleted ? 0.7 : 1 }} />
 
-                <div className="task-manager-checkboxes">
+                <textarea className="task-manager-description" placeholder="What’s your task?" value={taskDesc} onChange={this.updateText} disabled={isCompleted} style={{ opacity: isCompleted ? 0.7 : 1 }} />
 
-                    <label className="task-manager-allday" style={{opacity: isCompleted ? 0.7 : 1,}}>
-                        <input 
-                            type="checkbox" 
-                            checked={allDay} 
-                            onChange={this.updateAllDay} 
-                            disabled={isCompleted}
-                        />
+                <div className="task-manager-checkboxes">
+                    <label className="task-manager-allday" style={{ opacity: isCompleted ? 0.7 : 1 }}>
+                        <input type="checkbox" checked={allDay} onChange={this.updateAllDay} disabled={isCompleted} />
                         All day
-                        
                         {allDay && (
-                        <input
-                            type="date"
-                            value={(() => {
-                                const rawRange = doc.date_range;
-                                if (typeof rawRange !== 'string') return '';
-                                const datePart = rawRange.split('|')[0];
-                                if (!datePart) return '';
-                                const d = new Date(datePart);
-                                return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
-                            })()}
-                            onChange={e => {
-                                const newDate = new Date(e.target.value);
-                                if (!isNaN(newDate.getTime())) {
-                                    const dateStr = e.target.value;
-                                    if (dateStr) {
-                                        doc.date_range = `${dateStr}T00:00:00|${dateStr}T00:00:00`;
+                            <input
+                                type="date"
+                                value={(() => {
+                                    const rawRange = doc.$task_dateRange;
+                                    if (typeof rawRange !== 'string') return '';
+                                    const datePart = rawRange.split('|')[0];
+                                    if (!datePart) return '';
+                                    const d = new Date(datePart);
+                                    return !isNaN(d.getTime()) ? d.toISOString().split('T')[0] : '';
+                                })()}
+                                onChange={e => {
+                                    const newDate = new Date(e.target.value);
+                                    if (!isNaN(newDate.getTime())) {
+                                        const dateStr = e.target.value;
+                                        if (dateStr) {
+                                            doc.$task_dateRange = `${dateStr}T00:00:00|${dateStr}T00:00:00`;
+                                        }
                                     }
-                                }
-                            }}
-                            disabled={isCompleted}
-                            style={{ marginLeft: '8px' }}
-                        />
-                    )}
+                                }}
+                                disabled={isCompleted}
+                                style={{ marginLeft: '8px' }}
+                            />
+                        )}
                     </label>
 
                     <label className="task-manager-complete">
                         <input type="checkbox" checked={isCompleted} onChange={this.toggleComplete} />
                         Complete
                     </label>
-
                 </div>
 
                 {!allDay && (
-                <div
-                    className="task-manager-times"
-                    style={{ opacity: isCompleted ? 0.7 : 1 }}
-                >
-                    <label>
-                        Start:
-                        <input
-                            type="datetime-local"
-                            value={startTime}
-                            onChange={this.updateStart}
-                            disabled={isCompleted}
-                        />
-                    </label>
-                    <label>
-                        End:
-                        <input
-                            type="datetime-local"
-                            value={endTime}
-                            onChange={this.updateEnd}
-                            disabled={isCompleted}
-                        />
-                    </label>
-                </div>
-            )}
+                    <div className="task-manager-times" style={{ opacity: isCompleted ? 0.7 : 1 }}>
+                        <label>
+                            Start:
+                            <input type="datetime-local" value={startTime} onChange={this.updateStart} disabled={isCompleted} />
+                        </label>
+                        <label>
+                            End:
+                            <input type="datetime-local" value={endTime} onChange={this.updateEnd} disabled={isCompleted} />
+                        </label>
+                    </div>
+                )}
             </div>
         );
     }
@@ -337,9 +283,10 @@ Docs.Prototypes.TemplateMap.set(DocumentType.TASK, {
         _layout_nativeDimEditable: true,
         _layout_reflowVertical: true,
         _layout_reflowHorizontal: true,
+        task: '',
         defaultDoubleClick: 'ignore',
         systemIcon: 'BsCheckSquare',
         height_min: 300,
-        width_min: 300
+        width_min: 300,
     },
 });
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index f21eec604..835d58337 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -46,6 +46,7 @@ export class CalendarBox extends CollectionSubView() {
     }
 
     componentDidMount(): void {
+        this.Document.$calendar = ''; // needed only to make the keyvalue view look nice.
         this._props.setContentViewBox?.(this);
         this._eventsDisposer = reaction(
             () => ({ events: this.calendarEvents }),
@@ -73,8 +74,8 @@ export class CalendarBox extends CollectionSubView() {
 
     @computed get calendarEvents(): EventSourceInput | undefined {
         return this.childDocs.map(doc => {
-            const { start, end } = dateRangeStrToDates(StrCast(doc.date_range));
-            const isCompleted = BoolCast(doc.$completed); // AARAV ADD
+            const { start, end } = dateRangeStrToDates(StrCast(doc.$task_dateRange));
+            const isCompleted = BoolCast(doc.$task_completed); // AARAV ADD
             return {
                 title: StrCast(doc.title),
                 start,
@@ -82,7 +83,7 @@ export class CalendarBox extends CollectionSubView() {
                 groupId: doc[Id],
                 startEditable: true,
                 endEditable: true,
-                allDay: BoolCast(doc.$allDay),
+                allDay: BoolCast(doc.$task_allDay),
                 classNames: ['mother', isCompleted ? 'completed-task' : ''], // will determine the style
                 editable: true, // subject to change in the future
                 backgroundColor: this.eventToColor(doc),
@@ -96,10 +97,10 @@ export class CalendarBox extends CollectionSubView() {
     }
 
     @computed get dateRangeStrDates() {
-        return dateRangeStrToDates(StrCast(this.Document.date_range));
+        return dateRangeStrToDates(StrCast(this.Document._calendar_dateRange));
     }
     get dateSelect() {
-        return dateRangeStrToDates(StrCast(this.Document.date));
+        return dateRangeStrToDates(StrCast(this.Document._calendar_date));
     }
 
     // Choose a calendar view based on the date range
@@ -124,7 +125,7 @@ export class CalendarBox extends CollectionSubView() {
         if (!super.onInternalDrop(e, de)) return false;
         de.complete.docDragData?.droppedDocuments.forEach(doc => {
             const today = new Date().toISOString();
-            if (!doc.date_range) doc.$date_range = `${today}|${today}`;
+            if (!doc.$task_dateRange) doc.$task_dateRange = `${today}|${today}`;
         });
         return true;
     };
@@ -136,7 +137,7 @@ export class CalendarBox extends CollectionSubView() {
 
     handleEventDrop = undoable((arg: EventDropArg | EventResizeDoneArg) => {
         const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
-        // doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
+        // doc && arg.event.start && (doc.$task_dateRange = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
         if (!doc || !arg.event.start) return;
 
         // get the new start and end dates
@@ -144,19 +145,19 @@ export class CalendarBox extends CollectionSubView() {
         const endDate = new Date(arg.event.end ?? arg.event.start);
 
         // update date range, time range, and all day status
-        doc.date_range = `${startDate.toISOString()}|${endDate.toISOString()}`;
+        doc.$task_dateRange = `${startDate.toISOString()}|${endDate.toISOString()}`;
 
         const allDayStatus = arg.event.allDay ?? false;
-        if (doc.$allDay !== allDayStatus) {
-            doc.$allDay = allDayStatus;
+        if (doc.$task_allDay !== allDayStatus) {
+            doc.$task_allDay = allDayStatus;
         }
 
-        if (doc.$allDay) {
-            delete doc.$startTime;
-            delete doc.$endTime;
+        if (doc.$task_allDay) {
+            delete doc.$task_startTime;
+            delete doc.$task_endTime;
         } else {
-            doc.$startTime = new DateField(startDate);
-            doc.$endTime = new DateField(endDate);
+            doc.$task_startTime = new DateField(startDate);
+            doc.task_endTime = new DateField(endDate);
         }
     }, 'change event date');
 
@@ -220,7 +221,7 @@ export class CalendarBox extends CollectionSubView() {
                 displayEventEnd={false}
                 plugins={[multiMonthPlugin, dayGridPlugin, timeGrid, interactionPlugin]}
                 aspectRatio={this._props.PanelWidth() / this._props.PanelHeight()}
-                weekends={false}
+                weekends={true}
                 events={this.calendarEvents}
                 eventClick={this.handleEventClick}
                 eventDrop={this.handleEventDrop}
@@ -229,7 +230,7 @@ export class CalendarBox extends CollectionSubView() {
                 select={(info: DateSelectArg) => {
                     const start = dateRangeStrToDates(info.startStr).start.toISOString();
                     const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
-                    this.dataDoc.date = start + '|' + end;
+                    this.Document._calendar_date = start + '|' + end;
                 }}
                 // eventContent={() => {
                 //     return null;
@@ -238,9 +239,9 @@ export class CalendarBox extends CollectionSubView() {
                     const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
                     if (!doc) return;
 
-                    if (doc.type === 'task') {
+                    if (doc.type === DocumentType.TASK) {
                         const checkButton = document.createElement('button');
-                        checkButton.innerText = doc.$completed ? '✅' : '⬜';
+                        checkButton.innerText = doc.$task_completed ? '✅' : '⬜';
                         checkButton.style.position = 'absolute';
                         checkButton.style.right = '5px';
                         checkButton.style.top = '50%';
@@ -255,7 +256,7 @@ export class CalendarBox extends CollectionSubView() {
 
                         checkButton.onclick = ev => {
                             ev.stopPropagation();
-                            doc.$completed = !doc.$completed;
+                            doc.$task_completed = !doc.$task_completed;
                             this._calendar?.refetchEvents();
                         };
 
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index ae5582ef7..564609494 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -12,7 +12,6 @@ import { RTFCast } from '../../../../fields/Types';
 import { Mark } from 'prosemirror-model';
 import { observer } from 'mobx-react';
 
-
 export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
     @observable journalDate: string;
     @observable typingTimeout: NodeJS.Timeout | null = null; // track typing delay
@@ -22,8 +21,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @observable inlinePromptsEnabled = true;
     @observable askPromptsEnabled = true;
 
-
-
     _ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
     predictiveTextRange: { from: number; to: number } | null = null; // where predictive text starts and ends
     private predictiveText: string | null = ' ... why?';
@@ -114,7 +111,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     @action.bound toggleAskPrompts() {
         this.askPromptsEnabled = !this.askPromptsEnabled;
     }
-    
+
     /**
      * Method to handle click on document (to close prompt menu)
      * @param e - a click on the document
@@ -123,33 +120,27 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     handleDocumentClick(e: MouseEvent) {
         const menu = document.getElementById('prompts-menu');
         const button = document.getElementById('prompts-button');
-        if (
-            this.showPromptMenu &&
-            menu &&
-            !menu.contains(e.target as Node) &&
-            button &&
-            !button.contains(e.target as Node)
-        ) {
+        if (this.showPromptMenu && menu && !menu.contains(e.target as Node) && button && !button.contains(e.target as Node)) {
             this.showPromptMenu = false;
         }
     }
 
-
     /**
      * Method to set initial date of document in the calendar view
      */
 
     @action setInitialDateRange() {
-        if (!this.dataDoc.date_range && this.journalDate) {
+        if (!this.dataDoc.$task_dateRange && this.journalDate) {
             const parsedDate = new Date(this.journalDate);
             if (!isNaN(parsedDate.getTime())) {
                 const localStart = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
                 const localEnd = new Date(localStart); // same day
 
-                this.dataDoc.date_range = `${localStart.toISOString()}|${localEnd.toISOString()}`;
-                this.dataDoc.allDay = true;
+                this.dataDoc.$task_dateRange = `${localStart.toISOString()}|${localEnd.toISOString()}`;
+                this.dataDoc.$task_allDay = true;
+                this.dataDoc.$task = ''; // needed only to make the keyvalue view look good.
 
-                // console.log('Set date_range and allDay on journal (from local date):', this.dataDoc.date_range);
+                // console.log('Set task_dateRange and task_allDay on journal (from local date):', this.dataDoc.$task_dateRange);
             } else {
                 // console.log('Could not parse journalDate:', this.journalDate);
             }
@@ -187,7 +178,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
             if (this.inlinePromptsEnabled) {
                 this.insertPredictiveQuestion();
             }
-
         }, 3500);
     };
 
@@ -196,7 +186,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
      */
 
     @action insertPredictiveQuestion = async () => {
-
         const editorView = this._ref.current?.EditorView;
         if (!editorView) return;
 
@@ -347,7 +336,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
      */
     @action handleGeneratePrompts = async () => {
         if (this.isLoadingPrompts) {
-            return
+            return;
         }
 
         this.isLoadingPrompts = true;
@@ -391,8 +380,6 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                 // Insert formatted text
                 const transaction = state.tr.insert(state.selection.from, headerText).insert(state.selection.from + headerText.nodeSize, responseText);
                 dispatch(transaction);
-                (this._props as any)?.updateLayout?.();
-
             }
         } catch (err) {
             console.error('Error calling GPT:', err);
@@ -402,7 +389,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
     };
 
     /**
-     * Method to render the styled DailyJournal 
+     * Method to render the styled DailyJournal
      * @returns - the HTML component for the journal
      */
     render() {
@@ -438,8 +425,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                         cursor: 'pointer',
                         zIndex: 10,
                     }}
-                    onClick={this.togglePromptMenu}
-                >
+                    onClick={this.togglePromptMenu}>
                     Prompts
                 </button>
                 {this.showPromptMenu && (
@@ -458,16 +444,14 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                             minWidth: '170px',
                             maxWidth: 'fit-content',
                             overflow: 'auto',
-                        }}
-                    >
+                        }}>
                         <div
                             style={{
                                 display: 'flex',
                                 justifyContent: 'flex-end',
                                 alignItems: 'center',
                                 marginBottom: '10px',
-                            }}
-                        >
+                            }}>
                             <label
                                 style={{
                                     display: 'flex',
@@ -476,26 +460,19 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                     fontSize: '14px',
                                     justifyContent: 'flex-end',
                                     width: '100%',
-                                }}
-                            >
+                                }}>
                                 /ask
-                                <input
-                                    type="checkbox"
-                                    checked={this.askPromptsEnabled}
-                                    onChange={this.toggleAskPrompts}
-                                    style={{ margin: 0 }}
-                                />
+                                <input type="checkbox" checked={this.askPromptsEnabled} onChange={this.toggleAskPrompts} style={{ margin: 0 }} />
                             </label>
                         </div>
-                        
+
                         <div
                             style={{
                                 display: 'flex',
                                 justifyContent: 'flex-end',
                                 alignItems: 'center',
                                 marginBottom: '10px',
-                            }}
-                        >
+                            }}>
                             <label
                                 style={{
                                     display: 'flex',
@@ -504,15 +481,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                     fontSize: '14px',
                                     justifyContent: 'flex-end',
                                     width: '100%',
-                                }}
-                            >
+                                }}>
                                 Inline Prompting
-                                <input
-                                    type="checkbox"
-                                    checked={this.inlinePromptsEnabled}
-                                    onChange={this.toggleInlinePrompts}
-                                    style={{ margin: 0 }}
-                                />
+                                <input type="checkbox" checked={this.inlinePromptsEnabled} onChange={this.toggleInlinePrompts} style={{ margin: 0 }} />
                             </label>
                         </div>
 
@@ -531,15 +502,12 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
                                 opacity: this.isLoadingPrompts ? 0.6 : 1,
                                 padding: '5px 10px',
                                 float: 'right',
-                            }}
-                        >
+                            }}>
                             Generate Prompts
                         </button>
                     </div>
                 )}
 
-
-
                 <FormattedTextBox ref={this._ref} {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From 0f0e55075b001334134b7cadbac06b262466f287 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 2 Jun 2025 19:05:08 -0400
Subject: added view for multimonth to make it do something.

---
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 6 ++++++
 1 file changed, 6 insertions(+)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 835d58337..26aed72c3 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -212,6 +212,12 @@ export class CalendarBox extends CollectionSubView() {
                 }
                 selectable={true}
                 initialView={this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType}
+                views={{
+                    multiMonth: {
+                        type: 'multiMonth',
+                        duration: { months: 12 },
+                    },
+                }}
                 initialDate={untracked(() => this.dateSelect.start)}
                 navLinks={true}
                 editable={false}
-- 
cgit v1.2.3-70-g09d2


From 0b4659e2d85c5311835651d3f1aa54c48f3849de Mon Sep 17 00:00:00 2001
From: Skitty1238 <157652284+Skitty1238@users.noreply.github.com>
Date: Tue, 3 Jun 2025 11:09:32 -0400
Subject: fix for task resizing not working after merge, and fix for all-day
 view for initial task-nodes (i.e. if you just created task node and checked
 all-day, cal view was previously showing task 1 day early)

---
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 26aed72c3..92b3224e9 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -22,6 +22,7 @@ import './CalendarBox.scss';
 import { DateField } from '../../../../fields/DateField';
 import { undoable } from '../../../util/UndoManager';
 import { DocumentType } from '../../../documents/DocumentTypes';
+import { truncate } from 'fs/promises';
 
 type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
 
@@ -74,8 +75,20 @@ export class CalendarBox extends CollectionSubView() {
 
     @computed get calendarEvents(): EventSourceInput | undefined {
         return this.childDocs.map(doc => {
-            const { start, end } = dateRangeStrToDates(StrCast(doc.$task_dateRange));
-            const isCompleted = BoolCast(doc.$task_completed); // AARAV ADD
+            // const { start, end } = dateRangeStrToDates(StrCast(doc.$task_dateRange));
+            const isCompleted = BoolCast(doc.$task_completed);
+
+            const rangeStr = StrCast(doc.$task_dateRange);
+            const [startStr, endStr] = rangeStr.split('|');
+            let start: string | Date, end: string | Date;
+
+            if (BoolCast(doc.$task_allDay)) {
+                start = startStr;
+                end = endStr;
+            } else {
+                ({ start, end } = dateRangeStrToDates(rangeStr));
+            }
+
             return {
                 title: StrCast(doc.title),
                 start,
@@ -157,7 +170,7 @@ export class CalendarBox extends CollectionSubView() {
             delete doc.$task_endTime;
         } else {
             doc.$task_startTime = new DateField(startDate);
-            doc.task_endTime = new DateField(endDate);
+            doc.$task_endTime = new DateField(endDate);
         }
     }, 'change event date');
 
@@ -220,7 +233,7 @@ export class CalendarBox extends CollectionSubView() {
                 }}
                 initialDate={untracked(() => this.dateSelect.start)}
                 navLinks={true}
-                editable={false}
+                editable={true}
                 // expandRows={true}
                 // handleWindowResize={true}
                 displayEventTime={false}
@@ -231,6 +244,7 @@ export class CalendarBox extends CollectionSubView() {
                 events={this.calendarEvents}
                 eventClick={this.handleEventClick}
                 eventDrop={this.handleEventDrop}
+                eventResize={this.handleEventDrop}
                 unselectAuto={false}
                 // unselect={() => {}}
                 select={(info: DateSelectArg) => {
-- 
cgit v1.2.3-70-g09d2


From 6e7a29470e2c9e2fad287b4851b9eb561821ea9e Mon Sep 17 00:00:00 2001
From: Skitty1238 <157652284+Skitty1238@users.noreply.github.com>
Date: Tue, 3 Jun 2025 13:10:05 -0400
Subject: fixed drag and drop of calendar events not following cursor properly

---
 .../views/nodes/calendarBox/CalendarBox.scss       |  9 +++
 src/client/views/nodes/calendarBox/CalendarBox.tsx | 77 +++++++++++++++++++---
 src/server/apis/google/GoogleTasksHandler.ts       | 54 +++++++++++++++
 3 files changed, 131 insertions(+), 9 deletions(-)
 create mode 100644 src/server/apis/google/GoogleTasksHandler.ts

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

diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
index 891db9d90..89dc294a5 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.scss
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -50,3 +50,12 @@
         pointer-events: unset;
     }
 }
+
+.custom-drag-mirror {
+    transition: none !important;
+    transform: none !important;
+}
+
+.fc-event-dragging {
+    opacity: 0 !important;
+}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index 92b3224e9..a4183a11a 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -298,15 +298,74 @@ export class CalendarBox extends CollectionSubView() {
                         ev.preventDefault();
                     });
                 }}
+
+                // for dragging and dropping (mirror)
+
+                eventDragStart={(arg) => {
+                    const mirror = arg.el.cloneNode(true) as HTMLElement;
+                    const rect = arg.el.getBoundingClientRect();
+
+                    mirror.style.position = 'fixed';
+                    mirror.style.pointerEvents = 'none';
+                    mirror.style.opacity = '0.8';
+                    mirror.style.zIndex = '10000';
+                    mirror.classList.add('custom-drag-mirror');
+                    mirror.style.width = `${rect.width}px`;
+                    mirror.style.height = `${rect.height}px`;
+                  
+                    document.body.appendChild(mirror);
+                  
+                    const moveListener = (ev: MouseEvent) => {
+                      mirror.style.left = `${ev.clientX}px`;
+                      mirror.style.top = `${ev.clientY}px`;
+                    };
+                  
+                    window.addEventListener('mousemove', moveListener);
+
+                    // hide the actual box
+                    arg.el.style.visibility = 'hidden';
+                    arg.el.style.opacity = '0';
+                  
+                    (arg.el as any)._mirrorElement = mirror;
+                    (arg.el as any)._moveListener = moveListener;
+                }}
+                
+                eventDragStop={(arg) => {
+                    const el = arg.el as any;
+                    const mirror = el._mirrorElement;
+                    const moveListener = el._moveListener;
+
+                    // show the actual box
+                    el.style.visibility = 'visible';
+                    el.style.opacity = '1';
+                    
+                    if (mirror) document.body.removeChild(mirror);
+                    if (moveListener) window.removeEventListener('mousemove', moveListener);
+                }}
+                  
             />
         );
     }
 
     render() {
+        const scale = this._props.ScreenToLocalTransform().Scale;
+        const scaledWidth = this._props.PanelWidth();
+        const scaledHeight = this._props.PanelHeight();
+
         return (
             <div
                 key={this.calendarViewType}
                 className={`calendarBox${this._props.isContentActive() ? '-interactive' : ''}`}
+                style={{
+                    width: scaledWidth,
+                    height: scaledHeight,
+                    overflow: 'hidden',
+                    position: 'relative',
+                  }}
+                ref={r => {
+                    this.createDashEventsTarget(r);
+                    this.fixWheelEvents(r, this._props.isContentActive);
+                }}
                 onPointerDown={e => {
                     setTimeout(
                         action(() => {
@@ -317,17 +376,17 @@ export class CalendarBox extends CollectionSubView() {
                             if (cname.includes('timeGridDay')) this.dataDoc[this.calTypeFieldKey] = 'timeGridDay';
                         })
                     );
-                }}
-                style={{
-                    width: this._props.PanelWidth() / this._props.ScreenToLocalTransform().Scale,
-                    height: this._props.PanelHeight() / this._props.ScreenToLocalTransform().Scale,
-                    transform: `scale(${this._props.ScreenToLocalTransform().Scale})`,
-                }}
-                ref={r => {
-                    this.createDashEventsTarget(r);
-                    this.fixWheelEvents(r, this._props.isContentActive);
                 }}>
+                <div
+                    style={{
+                        transform: `scale(${scale})`,
+                        transformOrigin: 'top left',
+                        width: scaledWidth / scale,
+                        height: scaledHeight / scale,
+                    }}
+                >
                 {this.renderCalendar}
+                </div>
             </div>
         );
     }
diff --git a/src/server/apis/google/GoogleTasksHandler.ts b/src/server/apis/google/GoogleTasksHandler.ts
new file mode 100644
index 000000000..a8af86fe2
--- /dev/null
+++ b/src/server/apis/google/GoogleTasksHandler.ts
@@ -0,0 +1,54 @@
+import express from 'express';
+import { google } from 'googleapis';
+import { GoogleCredentialsLoader } from './CredentialsLoader';
+import User from '../../authentication/DashUserModel';
+import { DashUserModel } from '../../authentication/DashUserModel';
+
+
+const router = express.Router();
+
+router.post('/tasks/create', async (req, res) => {
+  try {
+    const { title, notes, due } = req.body;
+
+    // Make sure user is authenticated
+    if (!req.user) {
+      return res.status(401).json({ error: 'User not authenticated' });
+    }
+
+    // Assuming access token is stored in user model
+    const user = req.user as typeof User; // replace with your actual User type if needed
+    const accessToken = user.googleAccessToken; // <-- change this based on where you store it
+
+    if (!accessToken) {
+      return res.status(400).json({ error: 'Google access token not found for user' });
+    }
+
+    const credentials = GoogleCredentialsLoader.ProjectCredentials;
+    const auth = new google.auth.OAuth2(
+      credentials.client_id,
+      credentials.client_secret,
+      credentials.redirect_uris[0]
+    );
+
+    auth.setCredentials({ access_token: accessToken });
+
+    const tasks = google.tasks({ version: 'v1', auth });
+
+    const result = await tasks.tasks.insert({
+      tasklist: '@default',
+      requestBody: {
+        title,
+        notes,
+        due,
+      },
+    });
+
+    res.status(200).json(result.data);
+  } catch (err) {
+    console.error('Error creating Google Task:', err);
+    res.status(500).json({ error: 'Failed to create task' });
+  }
+});
+
+export default router;
-- 
cgit v1.2.3-70-g09d2

