From 1ffa8a8fb3e16bd5a3338d18782ddda0c2ffca03 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 16 Mar 2025 21:15:00 -0400
Subject: a lot of code cleanup for doc creators templates

---
 src/client/views/Main.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index fc2d7f572..b335432c9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -65,8 +65,8 @@ import { PresBox, PresElementBox } from './nodes/trails';
 import { FaceRecognitionHandler } from './search/FaceRecognitionHandler';
 import { SearchBox } from './search/SearchBox';
 import { StickerPalette } from './smartdraw/StickerPalette';
-import { Field } from './nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field';
-import { FieldUtils } from './nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils';
+import { TemplateField } from './nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField';
+import { TemplateFieldUtils } from './nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateFieldUtils';
 
 dotenv.config();
 
@@ -102,7 +102,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
         new PingManager();
         new KeyManager();
         new FaceRecognitionHandler();
-        Field.initField = FieldUtils.initField; // set the init function for fields
+        TemplateField.initField = TemplateFieldUtils.initField; // set the init function for fields
 
         // initialize plugins and classes that require plugins
         CollectionDockingView.Init(TabDocView);
-- 
cgit v1.2.3-70-g09d2


From c54d3f4338d16c907273762758853a183b543630 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 17 Mar 2025 01:35:46 -0400
Subject: hopefully last changes doc creator templates.

---
 .../src/components/Template/Template.tsx           | 14 ++---
 src/client/views/Main.tsx                          |  2 +-
 .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx   | 11 ++--
 .../nodes/DataVizBox/DocCreatorMenu/Template.ts    | 62 ++++++++++----------
 .../TemplateFieldTypes/DecorationField.ts          | 10 +---
 .../TemplateFieldTypes/DynamicField.ts             | 24 +++-----
 .../TemplateFieldTypes/StaticContentField.ts       | 68 ++++++++--------------
 .../TemplateFieldTypes/TemplateField.ts            | 25 +++-----
 .../TemplateFieldTypes/TemplateFieldUtils.ts       | 24 ++++----
 .../DataVizBox/DocCreatorMenu/TemplateManager.ts   | 15 +----
 10 files changed, 97 insertions(+), 158 deletions(-)

(limited to 'src/client/views/Main.tsx')

diff --git a/packages/components/src/components/Template/Template.tsx b/packages/components/src/components/Template/Template.tsx
index 6c6b26516..50b0dec0e 100644
--- a/packages/components/src/components/Template/Template.tsx
+++ b/packages/components/src/components/Template/Template.tsx
@@ -1,12 +1,8 @@
-import * as React from 'react'
-import { IGlobalProps , getFormLabelSize } from '../../global'
+import * as React from 'react';
+import { IGlobalProps } from '../../global';
 
-export interface ITemplateProps extends IGlobalProps {
-
-}
+export interface ITemplateProps extends IGlobalProps {}
 
 export const Template = (props: ITemplateProps) => {
-    return <div className={`template-container`}>
-        Template Component
-    </div>
-}
\ No newline at end of file
+    return <div className={`template-container`}>Template Component</div>;
+};
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index b335432c9..660452d9d 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -102,7 +102,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
         new PingManager();
         new KeyManager();
         new FaceRecognitionHandler();
-        TemplateField.initField = TemplateFieldUtils.initField; // set the init function for fields
+        TemplateField.CreateField = TemplateFieldUtils.CreateField; // set the init function for fields
 
         // initialize plugins and classes that require plugins
         CollectionDockingView.Init(TabDocView);
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
index 444f0a29d..3253cefb9 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx
@@ -616,18 +616,15 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
         } else {
             this._dataViz?.updateColDefaults();
 
-            const cols = this.fieldsInfos;
-            templates.push(...this.templateManager.getValidTemplates(cols));
+            templates.push(...this.templateManager.getValidTemplates(this.fieldsInfos));
 
-            const assignments: [Template, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols);
+            const assignments = await this.assignColsToFields(templates, this.fieldsInfos);
 
-            const renderedTemplatePromises: Promise<Template | undefined>[] = assignments.map(([template, assgns]) => this.applyGPTContentToTemplate(template, assgns));
+            const renderedTemplatePromises = assignments.map(([template, assgns]) => this.applyGPTContentToTemplate(template, assgns));
 
             await Promise.all(renderedTemplatePromises);
         }
 
-        templates.forEach(template => template.mainField.initializeDocument({ title: template.title, opts: {}, viewType: ViewType.FREEFORM, tl: [0, 0], br: [900, 900] }, []));
-
         setTimeout(() => {
             this.setSuggestedTemplates(templates);
             this._GPTLoading = false;
@@ -731,7 +728,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps>
                     }
                 });
 
-            return Promise.all(gptPromises).then(() => (this._DOCCC = templateCopy.mainField.renderedDoc));
+            return Promise.all(gptPromises).then(() => (this._DOCCC = templateCopy._mainField?.renderedDoc));
         };
 
         const rowContents = this.DEBUG_MODE
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
index b13ec46f0..cf6e145cb 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts
@@ -5,52 +5,33 @@ import { DynamicField } from './TemplateFieldTypes/DynamicField';
 import { FieldSettings, TemplateField } from './TemplateFieldTypes/TemplateField';
 
 export class Template {
-    mainField: DynamicField;
-    private settings: FieldSettings;
+    _mainField: DynamicField | undefined;
 
-    constructor(templateInfo: FieldSettings, mainField?: DynamicField) {
+    /**
+     * A Template can be created from a description of its fields (FieldSettings) or from a DynamicField
+     * @param definition definition of template as settings or DynamicField
+     */
+    constructor(definition: FieldSettings | DynamicField) {
         makeAutoObservable(this);
-        this.settings = templateInfo;
-        this.mainField = mainField ?? this.setupMainField(templateInfo);
+        this._mainField = definition instanceof DynamicField ? definition : this.setupMainField(definition);
     }
 
     get childFields(): TemplateField[] {
-        return this.mainField.getSubfields;
+        return this._mainField?.getSubfields ?? [];
     }
     get allFields(): TemplateField[] {
-        return this.mainField.getAllSubfields;
+        return this._mainField?.getAllSubfields ?? [];
     }
     get contentFields(): TemplateField[] {
         return this.allFields.filter(field => field.isContentField);
     }
     get doc() {
-        return this.mainField.renderedDoc;
+        return this._mainField?.renderedDoc;
     }
     get title() {
-        return this.mainField.getTitle();
+        return this._mainField?.getTitle();
     }
 
-    cleanup = () => {
-        //dispose each subfields disposers, etc.
-    };
-
-    cloneBase = () => new Template(TemplateLayouts.BasicSettings, this.mainField.makeClone(undefined));
-
-    printFieldInfo = () => {
-        this.allFields.forEach(field => {
-            const doc = field.renderedDoc;
-            console.log('title: ', field.getTitle(), ' width: ', doc?.width);
-        });
-    };
-
-    getRenderedDoc = () => this.doc;
-
-    getFieldByID = (id: number): TemplateField => this.allFields.filter(field => field.getID === id)[0];
-
-    getFieldByTitle = (title: string) => this.allFields.filter(field => field.getTitle() === title)[0];
-
-    setupMainField = (templateInfo: FieldSettings) => DynamicField.Create(templateInfo, 1);
-
     get descriptionSummary(): string {
         let summary: string = '';
         this.contentFields.forEach(field => {
@@ -67,6 +48,27 @@ export class Template {
         return summary;
     }
 
+    cleanup = () => {
+        //dispose each subfields disposers, etc.
+    };
+
+    cloneBase = () => new Template(this._mainField?.makeClone(undefined) ?? TemplateLayouts.BasicSettings);
+
+    getRenderedDoc = () => this.doc;
+
+    getFieldByID = (id: number): TemplateField => this.allFields.filter(field => field.getID === id)[0];
+
+    getFieldByTitle = (title: string) => this.allFields.filter(field => field.getTitle() === title)[0];
+
+    setupMainField = (templateInfo: FieldSettings) => TemplateField.CreateField(templateInfo, 1, undefined) as DynamicField;
+
+    printFieldInfo = () => {
+        this.allFields.forEach(field => {
+            const doc = field.renderedDoc;
+            console.log('title: ', field.getTitle(), ' width: ', doc?.width);
+        });
+    };
+
     isValidTemplate = (cols: Col[]) => {
         const maxMatches = this.maxMatches(this.getMatches(cols));
         return maxMatches === this.contentFields.length;
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DecorationField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DecorationField.ts
index 4f5ad89b6..98a9dc7a6 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DecorationField.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DecorationField.ts
@@ -1,11 +1,3 @@
 import { DynamicField } from './DynamicField';
-import { FieldSettings, TemplateField } from './TemplateField';
 
-export class DecorationField extends DynamicField {
-    private constructor(settings: FieldSettings, parent?: TemplateField, id: number = 1) {
-        super(settings, parent, id);
-    }
-    static Create(settings: FieldSettings, id: number | undefined, parent?: TemplateField) {
-        return DynamicField.Create(settings, id, parent);
-    }
-}
+export class DecorationField extends DynamicField {}
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DynamicField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DynamicField.ts
index 0baf321a2..b5e73ff18 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DynamicField.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/DynamicField.ts
@@ -12,17 +12,6 @@ export class DynamicField extends TemplateField {
     protected _disposers: { [name: string]: IDisposer } = {};
     protected _subfields: TemplateField[] | undefined;
 
-    protected constructor(settings: FieldSettings, parent?: TemplateField, id: number = 1) {
-        super(settings, parent, id);
-    }
-    static Create(settings: FieldSettings, id: number | undefined, parent?: TemplateField) {
-        const field = new DynamicField(settings, parent, id);
-        field._subfields = settings.subfields?.map((fieldSettings, index) => TemplateField.initField(fieldSettings, index, field)) || [];
-        field._renderDoc = field.initializeDocument(settings, field._subfields);
-        field._disposers.fieldList = reaction(() => DocListCast(field._renderDoc?.[Doc.LayoutFieldKey(field._renderDoc)]), field.handleFieldUpdate);
-        return field;
-    }
-
     get getSubfields() {
         return this._subfields ?? [];
     }
@@ -51,7 +40,7 @@ export class DynamicField extends TemplateField {
             opts: {},
         };
 
-        this._subfields?.push(TemplateField.initField(settings, this._subfields.length, this));
+        this._subfields?.push(TemplateField.CreateField(settings, this._subfields.length, this));
     };
 
     addField = (field: TemplateField) => {
@@ -97,13 +86,16 @@ export class DynamicField extends TemplateField {
         return dynClone;
     }
 
-    initializeDocument = (settings: FieldSettings, subFields: TemplateField[]) => {
-        const renderedSubfields = subFields.filter(field => field.renderedDoc).map(field => field.renderedDoc!);
+    initRenderDoc = (settings: FieldSettings) => {
+        this._disposers.fieldList = reaction(() => DocListCast(this._renderDoc?.[Doc.LayoutFieldKey(this._renderDoc)]), this.handleFieldUpdate);
+        this._subfields = settings.subfields?.map((fieldSettings, index) => TemplateField.CreateField(fieldSettings, index, this)) || [];
+        const renderedSubfields = this._subfields.filter(field => field.renderedDoc).map(field => field.renderedDoc!);
         settings.opts.title = settings.title;
-        switch (settings.viewType) {
+        this._renderDoc = (() => { switch (settings.viewType) {
             case ViewType.CAROUSEL3D: return Docs.Create.Carousel3DDocument(renderedSubfields, settings.opts);
             case ViewType.FREEFORM:   
             default:                  return Docs.Create.FreeformDocument(renderedSubfields, settings.opts);
-        } // prettier-ignore
+        }})(); // prettier-ignore
+        return this;
     };
 }
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts
index b8839efab..7049bb56f 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts
@@ -1,3 +1,4 @@
+import { FieldResult } from '../../../../../../fields/Doc';
 import { DocData } from '../../../../../../fields/DocSymbols';
 import { RichTextField } from '../../../../../../fields/RichTextField';
 import { ImageField } from '../../../../../../fields/URLField';
@@ -12,61 +13,40 @@ export abstract class StaticContentField extends TemplateField {
     get isContentField(): boolean {
         return true;
     }
-}
+    protected setDataContent(viewType: ViewType, fieldKey: string, data: FieldResult, content: string, type?: ViewType) {
+        super.setContent(content, type);
 
-export class ImageTemplateField extends StaticContentField {
-    private constructor(settings: FieldSettings, parent?: TemplateField, id: number = 1) {
-        super(settings, parent, id);
-    }
-    static Create(settings: FieldSettings, id: number | undefined, parent: TemplateField | undefined) {
-        const field = new ImageTemplateField(settings, parent, id);
-        field._renderDoc = field.initializeDocument(settings);
-        return field;
+        if (type === viewType || type === undefined) {
+            this._content = content;
+            this._renderDoc && (this._renderDoc[DocData][fieldKey] = data);
+        } else {
+            this.changeFieldType(type).setContent(content, type);
+        }
     }
+}
 
+export class ImageTemplateField extends StaticContentField {
     setContent(url: string, type?: ViewType) {
-        super.setContent(url, type);
-
-        if (type === ViewType.IMG || type === undefined) {
-            this._renderDoc && (this._renderDoc[DocData].data = new ImageField(url));
-            this._content = url;
-        } else {
-            this.changeFieldType(type).setContent(url, type);
-        }
+        this.setDataContent(ViewType.IMG, 'data', new ImageField(url), url, type);
     }
 
-    initializeDocument = (settings: FieldSettings) => {
-        settings.opts.title = settings.title;
+    initRenderDoc(settings: FieldSettings) {
+        settings.opts.title = settings.title ?? '';
         settings.opts._layout_fitWidth = false;
-
-        return Docs.Create.ImageDocument('', settings.opts);
-    };
+        this._renderDoc = Docs.Create.ImageDocument('', settings.opts);
+        return this;
+    }
 }
 
 export class TextTemplateField extends StaticContentField {
-    private constructor(settings: FieldSettings, parent?: TemplateField, id: number = 1) {
-        super(settings, parent, id);
-    }
-    static Create(settings: FieldSettings, id: number, parent?: TemplateField) {
-        const field = new TextTemplateField(settings, parent, id);
-        field._renderDoc = field.initializeDocument(settings);
-        field._renderDoc.text_fontSize = `${TemplateFieldUtils.calculateFontSize(field._dimensions?.width ?? 10, field._dimensions?.height ?? 10, '', true)}`;
-        return field;
-    }
     setContent(text: string, type?: ViewType) {
-        super.setContent(text, type);
-
-        if (type === ViewType.TEXT || type === undefined) {
-            this._content = text;
-            this._renderDoc && (this._renderDoc[DocData].text = RichTextField.textToRtf(text));
-        } else {
-            this.changeFieldType(type).setContent(text, type);
-        }
+        this.setDataContent(ViewType.TEXT, 'text', RichTextField.textToRtf(text), text, type);
     }
 
-    initializeDocument = (settings: FieldSettings | undefined) => {
-        const opts = settings?.opts;
-        opts && (opts.title = settings?.title ?? '');
-        return Docs.Create.TextDocument('', opts);
-    };
+    initRenderDoc(settings: FieldSettings) {
+        settings.opts.title = settings.title ?? '';
+        settings.opts.text_fontSize = TemplateFieldUtils.calculateFontSize(this._dimensions?.width ?? 10, this._dimensions?.height ?? 10, '', true) + '';
+        this._renderDoc = Docs.Create.TextDocument('', settings.opts);
+        return this;
+    }
 }
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
index d86254586..74d73e692 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateField.ts
@@ -1,9 +1,5 @@
 /* eslint-disable no-use-before-define */
-import { computed } from 'mobx';
 import { Doc } from '../../../../../../fields/Doc';
-import { DocData } from '../../../../../../fields/DocSymbols';
-import { Copy } from '../../../../../../fields/FieldSymbols';
-import { ObjectField } from '../../../../../../fields/ObjectField';
 import { DocumentOptions } from '../../../../../documents/Documents';
 import { Col } from '../DocCreatorMenu';
 import { TemplateFieldSize, TemplateFieldType } from '../TemplateBackend';
@@ -20,7 +16,7 @@ export abstract class TemplateField {
      * @param sameId -
      * @returns TemplateField
      */
-    static initField: (settings: FieldSettings | undefined, index: number | undefined, parent: TemplateField | undefined, sameId?: boolean) => TemplateField;
+    static CreateField: (settings: FieldSettings, index: number, parent: TemplateField | undefined, sameId?: boolean) => TemplateField;
 
     protected _parent?: TemplateField;
     protected _id: number;
@@ -29,7 +25,7 @@ export abstract class TemplateField {
     protected _renderDoc: Doc | undefined;
     protected _dimensions: FieldDimensions | undefined;
 
-    constructor(settings: FieldSettings, parent?: TemplateField, id: number = 1) {
+    constructor(settings: FieldSettings, id: number = 1, parent?: TemplateField) {
         this._id = id;
         this._parent = parent;
         this._settings = settings;
@@ -56,8 +52,9 @@ export abstract class TemplateField {
     }
 
     abstract get isContentField(): boolean;
-
+    abstract initRenderDoc(settings: FieldSettings): TemplateField;
     abstract getContent(): string;
+
     setContent(content: string, type?: ViewType) {
         this._settings && (this._settings.viewType = type ?? this._settings.viewType);
     }
@@ -69,27 +66,19 @@ export abstract class TemplateField {
     getTitle = () => this._title;
 
     makeClone(parent?: TemplateField) {
-        const cloned = TemplateField.initField(this._settings, this._id, parent, true); // create a value for this.Document/subfields that we want to ignore
-        if (this._renderDoc) Doc.MakeClone(this._renderDoc).then(({ clone }) => (cloned._renderDoc = clone));
+        const cloned = TemplateField.CreateField(this._settings, this._id, parent, true); // create a value for this.Document/subfields that we want to ignore
+        this._renderDoc && Doc.MakeClone(this._renderDoc).then(({ clone }) => (cloned._renderDoc = clone));
         cloned._title = this._title;
         cloned._dimensions = this._dimensions;
         return cloned;
     }
 
-    @computed get documentOptions(): DocumentOptions {
-        const opts: DocumentOptions = {};
-        Object.assign(opts, this._renderDoc?.[DocData]);
-        Object.entries(opts).forEach(([key, field]) => {
-            Object.assign(opts, { [key]: field instanceof Object ? ObjectField.MakeCopy(field) : field[Copy]() });
-        });
-        return opts;
-    }
     exchangeFields(newField: TemplateField, oldField: TemplateField) {
         throw new Error('Only DynamicField can exchange fields.' + newField._title + ' ' + oldField._title);
     }
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     changeFieldType = (newType: ViewType): TemplateField => {
-        const newField = TemplateField.initField(this._settings, this._id, this._parent, true);
+        const newField = TemplateField.CreateField(this._settings, this._id, this._parent, true);
         this._parent?.exchangeFields(newField, this);
         return newField;
     };
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateFieldUtils.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateFieldUtils.ts
index c38d769cd..b0b531b57 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateFieldUtils.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/TemplateFieldUtils.ts
@@ -11,21 +11,21 @@ export class TemplateFieldUtils {
      *
      * @param settings - specification of the field type and other parameters
      * @param index -
-     * @param parent - TemplateField that contains the new field
+     * @param parent - optional TemplateField that contains the new field
      * @param sameId -
      * @returns TemplateField
      */
-    public static initField = (settings: FieldSettings, index: number, parent: TemplateField | undefined, sameId: boolean = false): TemplateField => {
-        const id = sameId ? index : parent ? Number(`${parent.getID}${index}`) : 1;
-        switch (settings?.viewType) {
-            case ViewType.FREEFORM:
-            case ViewType.CAROUSEL3D: return DynamicField.Create(settings, id, parent);
-            case ViewType.IMG:        return ImageTemplateField.Create(settings, id, parent);
-            case ViewType.TEXT:       return TextTemplateField.Create(settings, id, parent);
-            case ViewType.DEC:        return DecorationField.Create(settings, id, parent);
-            default:                  return TextTemplateField.Create(settings, id, parent);
-        } // prettier-ignore
-    };
+    public static CreateField = (settings: FieldSettings, index: number, parent?: TemplateField, sameId: boolean = false): TemplateField =>
+        ((...args) => {
+            switch (settings?.viewType) {
+                case ViewType.FREEFORM:
+                case ViewType.CAROUSEL3D: return new DynamicField(...args).initRenderDoc(settings);
+                case ViewType.IMG:        return new ImageTemplateField(...args).initRenderDoc(settings);
+                case ViewType.TEXT:       return new TextTemplateField(...args).initRenderDoc(settings);
+                case ViewType.DEC:        return new DecorationField(...args).initRenderDoc(settings);
+                default:                  return new TextTemplateField(...args).initRenderDoc(settings);
+            } // prettier-ignore
+        })(settings, sameId ? index : parent ? Number(`${parent.getID}${index}`) : 1, parent);
 
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => {
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.ts
index 1959cf9d6..0978444e3 100644
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.ts
+++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.ts
@@ -11,20 +11,11 @@ export class TemplateManager {
         this.templates = this.initializeTemplates(templateSettings);
     }
 
-    initializeTemplates = (templateSettings: FieldSettings[]): Template[] => {
-        const initializedTemplates: Template[] = [];
-        templateSettings.forEach(settings => initializedTemplates.push(new Template(settings)));
-        return initializedTemplates;
-    };
+    initializeTemplates = (templateSettings: FieldSettings[]) => templateSettings.map(settings => new Template(settings));
 
-    getValidTemplates = (cols: Col[]): Template[] => {
-        console.log('called in manager with templates: ', this.templates);
-        return this.templates.filter(template => template.isValidTemplate(cols));
-    };
+    getValidTemplates = (cols: Col[]) => this.templates.filter(template => template.isValidTemplate(cols));
 
-    addTemplate = (newTemplate: Template) => {
-        this.templates.push(newTemplate);
-    };
+    addTemplate = (newTemplate: Template) => this.templates.push(newTemplate);
 
     removeTemplate = (template: Template) => {
         this.templates.splice(this.templates.indexOf(template), 1);
-- 
cgit v1.2.3-70-g09d2


From 27f4f85a49f3aaf31e8ff1f2c8aa9d5020eee6ac Mon Sep 17 00:00:00 2001
From: aaravkumar <aarav.kumar1510@gmail.com>
Date: Wed, 23 Apr 2025 23:03:16 -0400
Subject: added basic task nodes with descritpion, start and end times -- added
 doc options for starttime/endtime/allday

---
 src/client/documents/DocumentTypes.ts      |  3 +-
 src/client/documents/Documents.ts          | 70 ++++++++++------------
 src/client/util/CurrentUserUtils.ts        |  2 +
 src/client/views/Main.tsx                  |  2 +
 src/client/views/nodes/TaskManagerTask.tsx | 96 ++++++++++++++++++++++++++++++
 5 files changed, 132 insertions(+), 41 deletions(-)
 create mode 100644 src/client/views/nodes/TaskManagerTask.tsx

(limited to 'src/client/views/Main.tsx')

diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 5c6559836..83faf6ed1 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -44,7 +44,8 @@ export enum DocumentType {
     SCRIPTDB = 'scriptdb', // database of scripts
     GROUPDB = 'groupdb', // database of groups
 
-    JOURNAL = 'journal', // AARAV ADD
+    JOURNAL = 'journal', // AARAV ADD JOURNAL
+    TASK = 'task', // AARAV ADD TASK
 }
 export enum CollectionViewType {
     // general collections
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index bf9cc5bd4..1b0c422da 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 { ScriptCast, StrCast } from '../../fields/Types';
+import { DocCast, 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';
@@ -525,6 +525,17 @@ export class DocumentOptions {
     ai?: string; // to mark items as ai generated
     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);
 }
 
 export const DocOptions = new DocumentOptions();
@@ -586,6 +597,17 @@ export namespace Docs {
                     },
                 },
             ],
+
+            [
+                DocumentType.TASK,
+                {
+                    layout: { view: EmptyBox, dataField: 'text' },
+                    options: {
+                        title: 'Task',
+                        acl_Guest: SharingPermissions.View,
+                    },
+                },
+            ],
             // AARAV ADD //
         ]);
 
@@ -922,31 +944,6 @@ export namespace Docs {
         // AARAV ADD //
 
         export function DailyJournalDocument(text: string | RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
-            // const getFormattedDate = () => {
-            //     const date = new Date().toLocaleDateString(undefined, {
-            //         weekday: 'long',
-            //         year: 'numeric',
-            //         month: 'long',
-            //         day: 'numeric',
-            //     });
-            //     return date;
-            // };
-
-            // const getDailyText = () => {
-            //     const placeholderText = 'Start writing here...';
-            //     const dateText = `${getFormattedDate()}`;
-
-            //     return RichTextField.textToRtfFormat(
-            //         [
-            //             { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
-            //             { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
-            //             { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
-            //         ],
-            //         undefined,
-            //         placeholderText.length
-            //     );
-            // };
-
             return InstanceFromProto(
                 Prototypes.get(DocumentType.JOURNAL),
                 '',
@@ -959,24 +956,17 @@ export namespace Docs {
             );
         }
 
-        // AARAV ADD //
-
-        export function LinkDocument(source: Doc, target: Doc, options: DocumentOptions = {}, id?: string) {
-            const linkDoc = InstanceFromProto(
-                Prototypes.get(DocumentType.LINK),
-                undefined,
+        export function TaskDocument(text = '', options: DocumentOptions = {}, fieldKey = 'text') {
+            return InstanceFromProto(
+                Prototypes.get(DocumentType.TASK),
+                '',
                 {
-                    link_anchor_1: source,
-                    link_anchor_2: target,
+                    title: '',
                     ...options,
                 },
-                id,
-                'link'
+                undefined,
+                fieldKey
             );
-
-            Doc.AddLink(linkDoc);
-
-            return linkDoc;
         }
 
         export function InkDocument(points: PointData[], options: DocumentOptions = {}, strokeWidth: number, color: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, isInkMask: boolean) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 99a67ebb2..121af72ed 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -407,6 +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: "Chat",       creator: Docs.Create.ChatDocument,                          opts: { _width: 500, _height: 500, _layout_fitWidth: true, }},
             {key: "MetaNote",   creator: metaNoteTemplate,                                  opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: true}},
             {key: "ViewSlide",  creator: slideView,                                         opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}},
@@ -450,6 +451,7 @@ pie title Minerals in my tap water
             { toolTip: "Tap or drag to create a scripting box",   title: "Script",      icon: "terminal",        dragFactory: doc.emptyScript as Doc,     clickFactory: DocCast(doc.emptyScript),                                             funcs: { hidden: "IsNoviceMode()"}},
             { toolTip: "Tap or drag to create a data viz node",   title: "DataViz",     icon: "chart-bar",       dragFactory: doc.emptyDataViz as Doc,    clickFactory: DocCast(doc.emptyDataViz)},
             { toolTip: "Tap or drag to create a journal entry",   title: "Journal",     icon: "book",            dragFactory:doc.emptyDailyJournal as Doc,clickFactory: DocCast(doc.emptyDataJournal), },
+            { toolTip: "Tap or drag to create a task",            title: "Task",        icon: "check-square",    dragFactory: doc.emptyTask as Doc,       clickFactory: DocCast(doc.emptyTask), },
             { toolTip: "Tap or drag to create a bullet slide",    title: "PPT Slide",   icon:"person-chalkboard",dragFactory: doc.emptySlide as Doc,      clickFactory: DocCast(doc.emptySlide),     openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
             { toolTip: "Tap or drag to create a view slide",      title: "View Slide",  icon: "address-card",    dragFactory: doc.emptyViewSlide as Doc,  clickFactory: DocCast(doc.emptyViewSlide), openFactoryLocation: OpenWhere.overlay, funcs: { hidden: "IsNoviceMode()"}},
             { toolTip: "Tap or drag to create a data note",       title: "MetaNote",    icon: "window-maximize", dragFactory: doc.emptyMetaNote as Doc,   clickFactory: DocCast(doc.emptyMetaNote),  openFactoryAsDelegate: true,            funcs: { hidden: "IsNoviceMode()"} },
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index e4bbb1c0f..b0bed927e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -65,6 +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';
 
 dotenv.config();
 
@@ -119,6 +120,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
             StickerPalette: StickerPalette,
             FormattedTextBox,
             DailyJournal, // AARAV
+            TaskManagerTask, // AARAV
             ImageBox,
             FontIconBox,
             LabelBox,
diff --git a/src/client/views/nodes/TaskManagerTask.tsx b/src/client/views/nodes/TaskManagerTask.tsx
new file mode 100644
index 000000000..29acba75e
--- /dev/null
+++ b/src/client/views/nodes/TaskManagerTask.tsx
@@ -0,0 +1,96 @@
+import { makeObservable, action, observable } 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, FieldType } from '../../../fields/Doc';
+
+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>) => {
+    this.props.Document.text = e.target.value;
+};
+
+@action
+updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
+    this.props.Document.allDay = e.target.checked;
+};
+
+@action
+updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
+    this.props.Document.startTime = new DateField(new Date(e.target.value));
+};
+
+@action
+updateEnd = (e: React.ChangeEvent<HTMLInputElement>) => {
+    this.props.Document.endTime = new DateField(new Date(e.target.value));
+};
+
+render() {
+    const doc = this.props.Document;
+
+    const taskDesc = typeof doc.text === 'string' ? doc.text : '';
+    const allDay = !!doc.allDay;
+
+    const startTime = doc.startTime instanceof DateField ? doc.startTime.date.toISOString().slice(0, 16) : '';
+
+    const endTime = doc.endTime instanceof DateField ? doc.endTime.date.toISOString().slice(0, 16) : '';
+
+    return (
+    <div style={{ padding: 8, display: 'flex', flexDirection: 'column', gap: 8 }}>
+        <input
+        type="text"
+        placeholder="What’s your task?"
+        value={taskDesc}
+        onChange={this.updateText}
+        />
+
+        <label>
+        <input type="checkbox" checked={allDay} onChange={this.updateAllDay} />
+        All day
+        </label>
+
+        {!allDay && (
+        <div style={{ display: 'flex', gap: 8 }}>
+            <label>
+            Start:
+            <input type="datetime-local" value={startTime} onChange={this.updateStart} />
+            </label>
+            <label>
+            End:
+            <input type="datetime-local" value={endTime} onChange={this.updateEnd} />
+            </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', // or whatever icon you like
+    },
+});
-- 
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/Main.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

