aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/TaskBox.tsx57
-rw-r--r--src/client/views/nodes/formattedText/DailyJournal.tsx181
2 files changed, 213 insertions, 25 deletions
diff --git a/src/client/views/nodes/TaskBox.tsx b/src/client/views/nodes/TaskBox.tsx
index dca01817f..ff1c70b90 100644
--- a/src/client/views/nodes/TaskBox.tsx
+++ b/src/client/views/nodes/TaskBox.tsx
@@ -9,26 +9,52 @@ import { Doc } from '../../../fields/Doc';
import './TaskBox.scss';
+/**
+ * Props (reference to document) for Task Box
+ */
+
interface TaskBoxProps {
Document: Doc;
}
+/**
+ * TaskBox class for adding task information + completing tasks
+ */
@observer
export class TaskBox extends React.Component<TaskBoxProps> {
+
+ /**
+ * Method to reuturn the
+ * @param fieldStr
+ * @returns
+ */
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(TaskBox, fieldStr);
}
+ /**
+ * Method to update the task description
+ * @param e - event of changing the description box input
+ */
+
@action
updateText = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
this.props.Document.text = e.target.value;
};
+ /**
+ * Method to update the task title
+ * @param e - event of changing the title box input
+ */
@action
updateTitle = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.Document.title = e.target.value;
};
+ /**
+ * Method to update the all day status
+ * @param e - event of changing the all day checkbox
+ */
@action
updateAllDay = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.Document.$allDay = e.target.checked;
@@ -41,6 +67,10 @@ export class TaskBox extends React.Component<TaskBoxProps> {
this.setTaskDateRange();
};
+ /**
+ * Method to update the task start time
+ * @param e - event of changing the start time input
+ */
@action
updateStart = (e: React.ChangeEvent<HTMLInputElement>) => {
const newStart = new Date(e.target.value);
@@ -60,8 +90,10 @@ export class TaskBox extends React.Component<TaskBoxProps> {
this.setTaskDateRange();
};
-
-
+ /**
+ * Method to update the task end time
+ * @param e - event of changing the end time input
+ */
@action
updateEnd = (e: React.ChangeEvent<HTMLInputElement>) => {
const newEnd = new Date(e.target.value);
@@ -81,9 +113,9 @@ export class TaskBox extends React.Component<TaskBoxProps> {
this.setTaskDateRange();
};
-
-
-
+ /**
+ * Method to update the task date range
+ */
@action
setTaskDateRange() {
const doc = this.props.Document;
@@ -107,11 +139,21 @@ export class TaskBox extends React.Component<TaskBoxProps> {
}
}
+ /**
+ * Method to set task's completion status
+ * @param e - event of changing the "completed" input checkbox
+ */
+
@action
toggleComplete = (e: React.ChangeEvent<HTMLInputElement>) => {
this.props.Document.$completed = e.target.checked;
};
+ /**
+ * Constructor for the task box
+ * @param props - props containing the document reference
+ */
+
constructor(props: TaskBoxProps) {
super(props);
makeObservable(this);
@@ -150,6 +192,11 @@ export class TaskBox extends React.Component<TaskBoxProps> {
this._widthDisposer?.();
}
+ /**
+ * Method to render the task box
+ * @returns - HTML with taskbox components
+ */
+
render() {
function toLocalDateTimeString(date: Date): string {
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index d6d30dc13..269a609ef 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -10,11 +10,18 @@ import { RichTextField } from '../../../../fields/RichTextField';
import { Plugin } from 'prosemirror-state';
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
- @observable lastUserText: string = ''; // Store last user-entered text
+ @observable typingTimeout: NodeJS.Timeout | null = null; // track typing delay
+ @observable lastUserText: string = ''; // store last user-entered text
+ @observable isLoadingPrompts: boolean = false; // track if prompts are loading
+ @observable showPromptMenu = false;
+ @observable inlinePromptsEnabled = 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?';
@@ -42,7 +49,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
month: 'long',
day: 'numeric',
});
- console.log('getFormattedDate():', date);
+ // console.log('getFormattedDate():', date);
return date;
}
@@ -51,15 +58,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
*/
@action
setDailyTitle() {
- console.log('setDailyTitle() called...');
- console.log('Current title before update:', this.dataDoc.title);
+ // console.log('setDailyTitle() called...');
+ // console.log('Current title before update:', this.dataDoc.title);
if (!this.dataDoc.title || this.dataDoc.title !== this.journalDate) {
- console.log('Updating title to:', this.journalDate);
+ // console.log('Updating title to:', this.journalDate);
this.dataDoc.title = this.journalDate;
}
- console.log('New title after update:', this.dataDoc.title);
+ // console.log('New title after update:', this.dataDoc.title);
}
/**
@@ -70,7 +77,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
const placeholderText = 'Start writing here...';
const dateText = `${this.journalDate}\n`;
- console.log('Checking if dataDoc has text field...');
+ // console.log('Checking if dataDoc has text field...');
this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
[
@@ -82,9 +89,43 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
placeholderText.length
);
- console.log('Current text field:', this.dataDoc[this.fieldKey]);
+ // console.log('Current text field:', this.dataDoc[this.fieldKey]);
+ }
+
+ /**
+ * Method to show/hide the prompts menu
+ */
+ @action.bound togglePromptMenu() {
+ this.showPromptMenu = !this.showPromptMenu;
+ }
+
+ /**
+ * Method to toggle on/off inline predictive prompts
+ */
+ @action.bound toggleInlinePrompts() {
+ this.inlinePromptsEnabled = !this.inlinePromptsEnabled;
+ }
+
+ /**
+ * Method to handle click on document (to close prompt menu)
+ * @param e - a click on the document
+ */
+ @action.bound
+ 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)
+ ) {
+ this.showPromptMenu = false;
+ }
}
+
/**
* Method to set initial date of document in the calendar view
*/
@@ -99,9 +140,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.dataDoc.date_range = `${localStart.toISOString()}|${localEnd.toISOString()}`;
this.dataDoc.allDay = true;
- console.log('Set date_range and allDay on journal (from local date):', this.dataDoc.date_range);
+ // console.log('Set date_range and allDay on journal (from local date):', this.dataDoc.date_range);
} else {
- console.log('Could not parse journalDate:', this.journalDate);
+ // console.log('Could not parse journalDate:', this.journalDate);
}
}
}
@@ -143,6 +184,8 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
*/
@action insertPredictiveQuestion = async () => {
+ if (!this.inlinePromptsEnabled) return;
+
const editorView = this._ref.current?.EditorView;
if (!editorView) return;
@@ -196,6 +239,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.predictiveText = text;
};
+ /**
+ * Method to remove the predictive question upon type/click
+ * @returns - once predictive text is found, or all text has been checked
+ */
createPredictiveCleanupPlugin = () => {
return new Plugin({
view: () => {
@@ -213,7 +260,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (node.isText && node.text === textToRemove) {
const tr = state.tr.delete(pos, pos + node.nodeSize);
- // Set the desired default marks for future input
+ // default marks for input
const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' });
const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' });
tr.setStoredMarks([]);
@@ -244,8 +291,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
};
componentDidMount(): void {
- console.log('componentDidMount() triggered...');
- console.log('Text: ' + RTFCast(this.Document.text)?.Text);
+ // console.log('componentDidMount() triggered...');
+ document.addEventListener('mousedown', this.handleDocumentClick);
+ // console.log('Text: ' + RTFCast(this.Document.text)?.Text);
const editorView = this._ref.current?.EditorView;
if (editorView) {
@@ -264,16 +312,17 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
const isDefaultTitle = isTitleString && currentTitle.includes('Untitled DailyJournal');
if (isTextEmpty && isDefaultTitle) {
- console.log('Journal title and text are default. Initializing...');
+ // console.log('Journal title and text are default. Initializing...');
this.setDailyTitle();
this.setDailyText();
this.setInitialDateRange();
} else {
- console.log('Journal already has content. Skipping initialization.');
+ // console.log('Journal already has content. Skipping initialization.');
}
}
componentWillUnmount(): void {
+ document.removeEventListener('mousedown', this.handleDocumentClick);
const editorView = this._ref.current?.EditorView;
if (editorView) {
editorView.dom.removeEventListener('input', this.onTextInput);
@@ -281,10 +330,20 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (this.typingTimeout) clearTimeout(this.typingTimeout);
}
+ /**
+ * Method to generate pormpts via GPT
+ * @returns - if failed
+ */
@action handleGeneratePrompts = async () => {
+ if (this.isLoadingPrompts) {
+ return
+ }
+
+ this.isLoadingPrompts = true;
+
const rawText = RTFCast(this.Document.text)?.Text ?? '';
- console.log('Extracted Journal Text:', rawText);
- console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
+ // console.log('Extracted Journal Text:', rawText);
+ // console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
if (!rawText.trim()) {
alert('Journal is empty! Write something first.');
@@ -321,12 +380,20 @@ 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);
+ } finally {
+ this.isLoadingPrompts = false;
}
};
+ /**
+ * Method to render the styled DailyJournal
+ * @returns - the HTML component for the journal
+ */
render() {
return (
<div
@@ -347,6 +414,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}}>
{/* GPT Button */}
<button
+ id="prompts-button"
style={{
position: 'absolute',
bottom: '5px',
@@ -359,9 +427,80 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
cursor: 'pointer',
zIndex: 10,
}}
- onClick={this.handleGeneratePrompts}>
+ onClick={this.togglePromptMenu}
+ >
Prompts
</button>
+ {this.showPromptMenu && (
+ <div
+ id="prompts-menu"
+ style={{
+ position: 'absolute',
+ bottom: '45px',
+ right: '5px',
+ backgroundColor: 'white',
+ border: '1px solid #ccc',
+ borderRadius: '4px',
+ padding: '10px',
+ boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
+ zIndex: 20,
+ minWidth: '170px',
+ maxWidth: 'fit-content',
+ overflow: 'auto',
+ }}
+ >
+ <div
+ style={{
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginBottom: '10px',
+ }}
+ >
+ <label
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '6px',
+ fontSize: '14px',
+ justifyContent: 'flex-end',
+ width: '100%',
+ }}
+ >
+ Inline Prompting
+ <input
+ type="checkbox"
+ checked={this.inlinePromptsEnabled}
+ onChange={this.toggleInlinePrompts}
+ style={{ margin: 0 }}
+ />
+ </label>
+
+ </div>
+
+ <button
+ onClick={() => {
+ this.showPromptMenu = false;
+ this.handleGeneratePrompts();
+ }}
+ disabled={this.isLoadingPrompts}
+ style={{
+ backgroundColor: '#9EAD7C',
+ color: 'white',
+ border: 'none',
+ borderRadius: '4px',
+ cursor: this.isLoadingPrompts ? 'not-allowed' : 'pointer',
+ 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>
@@ -369,8 +508,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
}
+const ObservedDailyJournal = observer(DailyJournal);
+
Docs.Prototypes.TemplateMap.set(DocumentType.JOURNAL, {
- layout: { view: DailyJournal, dataField: 'text' },
+ layout: { view: ObservedDailyJournal, dataField: 'text' },
options: {
acl: '',
_height: 35,