From a15cafd49b3a1c2368b0b331f9ca3ef16945da30 Mon Sep 17 00:00:00 2001 From: MichaelPesce Date: Fri, 28 Feb 2025 07:44:11 -0600 Subject: [PATCH 1/4] create monitor-activities-with-notes for testing adding the notes dialog; add notes dialog with some changes to remove api calls and whatnot; right now open the dialog when clicking add + --- .../experiment_detail.json | 92 +++ .../experiments.json | 303 ++++++++ .../monitor-activities-with-notes/notes.json | 27 + src/components/NotesDialog.tsx | 697 ++++++++++++++++++ src/components/PopupModal.tsx | 90 +++ .../_config/taskflow.config.ts | 36 + .../_config/taskflow.types.ts | 18 + .../monitor-activities-with-notes/_layout.tsx | 23 + .../_tests/monitor-activities.cy.ts | 24 + .../calendar.tsx | 35 + .../monitor-activities-with-notes/detail.tsx | 186 +++++ .../monitor-activities-with-notes/index.tsx | 85 +++ 12 files changed, 1616 insertions(+) create mode 100644 public/data/default/monitor-activities-with-notes/experiment_detail.json create mode 100644 public/data/default/monitor-activities-with-notes/experiments.json create mode 100644 public/data/default/monitor-activities-with-notes/notes.json create mode 100644 src/components/NotesDialog.tsx create mode 100644 src/components/PopupModal.tsx create mode 100644 src/pages/monitor-activities-with-notes/_config/taskflow.config.ts create mode 100644 src/pages/monitor-activities-with-notes/_config/taskflow.types.ts create mode 100644 src/pages/monitor-activities-with-notes/_layout.tsx create mode 100644 src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts create mode 100644 src/pages/monitor-activities-with-notes/calendar.tsx create mode 100644 src/pages/monitor-activities-with-notes/detail.tsx create mode 100644 src/pages/monitor-activities-with-notes/index.tsx diff --git a/public/data/default/monitor-activities-with-notes/experiment_detail.json b/public/data/default/monitor-activities-with-notes/experiment_detail.json new file mode 100644 index 00000000..f709181c --- /dev/null +++ b/public/data/default/monitor-activities-with-notes/experiment_detail.json @@ -0,0 +1,92 @@ +{ + "id": 45, + "experiment_name": "Experiment Cricket", + "start_time": "12/16/2023, 3:01:07 PM", + "end_time": null, + "status": "Running", + "events": [ + { + "id": 10, + "event_type": "Chirp", + "event_time": "12/17/2023, 9:09:15 PM", + "confidence": 97 + }, + { + "id": 9, + "event_type": "Chirp", + "event_time": "12/17/2023, 8:11:19 PM", + "confidence": 98 + }, + { + "id": 8, + "event_type": "Hop", + "event_time": "12/17/2023, 4:33:53 PM", + "confidence": 64 + }, + { + "id": 7, + "event_type": "Hop", + "event_time": "12/17/2023, 11:09:15 AM", + "confidence": 97 + }, + { + "id": 6, + "event_type": "Hop", + "event_time": "12/17/2023, 11:02:05 AM", + "confidence": 99 + }, + { + "id": 5, + "event_type": "Hop", + "event_time": "12/17/2023, 7:23:32 AM", + "confidence": 75 + }, + { + "id": 4, + "event_type": "Chirp", + "event_time": "12/17/2023, 7:03:44 PM", + "confidence": 99 + }, + { + "id": 3, + "event_type": "Chirp", + "event_time": "12/17/2023, 3:26:42 PM", + "confidence": 97 + }, + { + "id": 2, + "event_type": "Chirp", + "event_time": "12/16/2023, 11:55:21 PM", + "confidence": 97 + }, + { + "id": 1, + "event_type": "Chirp", + "event_time": "12/16/2023, 10:08:10 PM", + "confidence": 98 + }, + { + "id": 0, + "event_type": "Chirp", + "event_time": "12/16/2023, 9:45:10 PM", + "confidence": 97 + } + ], + "notes": [ + { + "id": 2, + "created_time": "12/17/2023, 4:00:29 PM", + "content": "Aute amet aute irure in consectetur enim incididunt aute consectetur." + }, + { + "id": 1, + "created_time": "12/17/2023, 7:00:25 AM", + "content": "Labore elit cupidatat eiusmod sunt aliquip pariatur quis." + }, + { + "id": 0, + "created_time": "12/16/2023, 11:59:20 PM", + "content": "Culpa id officia laborum cupidatat dolor mollit." + } + ] +} diff --git a/public/data/default/monitor-activities-with-notes/experiments.json b/public/data/default/monitor-activities-with-notes/experiments.json new file mode 100644 index 00000000..649a52fb --- /dev/null +++ b/public/data/default/monitor-activities-with-notes/experiments.json @@ -0,0 +1,303 @@ +[ + { + "id": 45, + "experiment_name": "Experiment Cricket", + "start_time": "12/16/2023, 3:01:07 PM", + "end_time": null, + "status": "Running" + }, + { + "id": 44, + "experiment_name": "Experiment Mouse", + "start_time": "12/15/2023, 1:40:10 PM", + "end_time": null, + "status": "Running" + }, + { + "id": 8, + "experiment_name": "Experiment Fox", + "start_time": "6/27/2023, 1:42:11 PM", + "end_time": "6/30/2023, 5:15:29 AM", + "status": "Complete" + }, + { + "id": 14, + "experiment_name": "Experiment Elephant", + "start_time": "10/15/2023, 1:42:11 PM", + "end_time": "10/18/2023, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 30, + "experiment_name": "Experiment Hippo", + "start_time": "6/27/2023, 1:42:11 PM", + "end_time": "6/30/2023, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 43, + "experiment_name": "Experiment Cherry", + "start_time": "5/20/2023, 9:34:45 AM", + "end_time": "5/22/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 23, + "experiment_name": "Experiment Lime", + "start_time": "4/22/2023, 9:34:45 AM", + "end_time": "4/24/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 39, + "experiment_name": "Experiment Giraffe", + "start_time": "4/28/2023, 9:34:45 AM", + "end_time": "4/30/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 26, + "experiment_name": "Experiment Horse", + "start_time": "2/17/2023, 1:42:11 PM", + "end_time": "2/20/2023, 5:15:29 AM", + "status": "Complete" + }, + { + "id": 11, + "experiment_name": "Experiment Sloth", + "start_time": "1/14/2023, 9:34:45 AM", + "end_time": "1/16/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 35, + "experiment_name": "Experiment Gargoyle", + "start_time": "7/10/2023, 9:34:45 AM", + "end_time": "7/12/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 19, + "experiment_name": "Experiment Lion", + "start_time": "8/6/2023, 9:34:45 AM", + "end_time": "8/8/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 3, + "experiment_name": "Experiment Jasmine", + "start_time": "6/5/2023, 9:34:45 AM", + "end_time": "6/7/2023, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 6, + "experiment_name": "Experiment Crocodile", + "start_time": "9/20/2022, 1:42:11 PM", + "end_time": "9/23/2022, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 32, + "experiment_name": "Experiment Lemon", + "start_time": "9/6/2022, 11:27:18 AM", + "end_time": "9/8/2022, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 17, + "experiment_name": "Experiment Iceberg", + "start_time": "7/8/2022, 9:11:04 AM", + "end_time": "7/10/2022, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 41, + "experiment_name": "Experiment Diamond", + "start_time": "6/19/2022, 9:11:04 AM", + "end_time": "6/21/2022, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 21, + "experiment_name": "Experiment Ruby", + "start_time": "6/12/2022, 9:11:04 AM", + "end_time": "6/14/2022, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 27, + "experiment_name": "Experiment Pigeon", + "start_time": "5/29/2022, 11:27:18 AM", + "end_time": "5/31/2022, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 13, + "experiment_name": "Experiment Oregono", + "start_time": "5/9/2022, 9:11:04 AM", + "end_time": "5/11/2022, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 28, + "experiment_name": "Experiment Otter", + "start_time": "5/1/2022, 11:27:18 AM", + "end_time": "5/3/2022, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 0, + "experiment_name": "Experiment Gecko", + "start_time": "3/14/2022, 11:27:18 AM", + "end_time": "3/16/2022, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 25, + "experiment_name": "Experiment Banana", + "start_time": "3/5/2022, 9:11:04 AM", + "end_time": "3/7/2022, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 34, + "experiment_name": "Experiment Thunder", + "start_time": "12/7/2021, 1:42:11 PM", + "end_time": "12/10/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 15, + "experiment_name": "Experiment Tornado", + "start_time": "12/1/2021, 9:34:45 AM", + "end_time": "12/3/2021, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 9, + "experiment_name": "Experiment Wind", + "start_time": "11/13/2021, 1:42:11 PM", + "end_time": "11/16/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 29, + "experiment_name": "Experiment Pufferfish", + "start_time": "10/14/2021, 9:11:04 AM", + "end_time": "10/16/2021, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 42, + "experiment_name": "Experiment Strawberry", + "start_time": "9/26/2021, 1:42:11 PM", + "end_time": "9/29/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 22, + "experiment_name": "Experiment Grape", + "start_time": "9/19/2021, 1:42:11 PM", + "end_time": "9/22/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 10, + "experiment_name": "Experiment Lightning", + "start_time": "8/30/2021, 1:42:11 PM", + "end_time": "9/2/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 2, + "experiment_name": "Experiment Leopard", + "start_time": "8/22/2021, 1:42:11 PM", + "end_time": "8/25/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 38, + "experiment_name": "Experiment Donkey", + "start_time": "8/15/2021, 1:42:11 PM", + "end_time": "8/18/2021, 5:15:29 AM", + "status": "Interrupted" + }, + { + "id": 7, + "experiment_name": "Experiment Walrus", + "start_time": "5/15/2021, 9:34:45 AM", + "end_time": "5/17/2021, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 16, + "experiment_name": "Experiment Basil", + "start_time": "3/24/2020, 11:27:18 AM", + "end_time": "3/26/2020, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 18, + "experiment_name": "Experiment Guava", + "start_time": "11/28/2020, 9:11:04 AM", + "end_time": "11/30/2020, 11:27:39 AM", + "status": "Interrupted" + }, + { + "id": 5, + "experiment_name": "Experiment Kiwi", + "start_time": "11/7/2020, 9:11:04 AM", + "end_time": "11/9/2020, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 12, + "experiment_name": "Experiment Shark", + "start_time": "6/25/2020, 11:27:18 AM", + "end_time": "6/27/2020, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 33, + "experiment_name": "Experiment Turtle", + "start_time": "5/2/2020, 9:11:04 AM", + "end_time": "5/4/2020, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 1, + "experiment_name": "Experiment Dragon", + "start_time": "4/28/2020, 9:11:04 AM", + "end_time": "4/30/2020, 11:27:39 AM", + "status": "Failed" + }, + { + "id": 31, + "experiment_name": "Experiment Lizard", + "start_time": "2/20/2020, 9:34:45 AM", + "end_time": "2/22/2020, 2:46:01 PM", + "status": "Complete" + }, + { + "id": 20, + "experiment_name": "Experiment Apple", + "start_time": "1/29/2020, 11:27:18 AM", + "end_time": "1/31/2020, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 36, + "experiment_name": "Experiment Avocado", + "start_time": "1/15/2020, 11:27:18 AM", + "end_time": "1/17/2020, 3:48:52 AM", + "status": "Complete" + }, + { + "id": 4, + "experiment_name": "Experiment Watermelon", + "start_time": "1/11/2020, 11:27:18 AM", + "end_time": "1/13/2020, 3:48:52 AM", + "status": "Complete" + } +] diff --git a/public/data/default/monitor-activities-with-notes/notes.json b/public/data/default/monitor-activities-with-notes/notes.json new file mode 100644 index 00000000..ff8168d5 --- /dev/null +++ b/public/data/default/monitor-activities-with-notes/notes.json @@ -0,0 +1,27 @@ +[ +{ + "text": "this is an interesting record", + "record_id": "67923c82d2ba1c43b091680a", + "timestamp": 1740423991.1088312, + "creator": "tony@gmail.com", + "resolved": false, + "deleted": false, + "lastUpdated": 1740423991.1088314, + "replies": [1], + "isReply": false, + "lastUpdatedUser": "tony@gmail.com" +}, +{ + "text": "I agree", + "record_id": "67923c82d2ba1c43b091680a", + "timestamp": 1740424029.0507326, + "creator": "bobby@gmail.com", + "resolved": false, + "deleted": false, + "lastUpdated": 1740424029.0507329, + "replies": [], + "isReply": true, + "lastUpdatedUser": "bobby@gmail.com", + "repliesTo": 1 +} +] diff --git a/src/components/NotesDialog.tsx b/src/components/NotesDialog.tsx new file mode 100644 index 00000000..31b2309e --- /dev/null +++ b/src/components/NotesDialog.tsx @@ -0,0 +1,697 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { Dialog, DialogTitle, DialogContent } from '@mui/material'; +import { + IconButton, + Tooltip, + TextField, + Button, + Stack, + Box, + Typography, + Divider, +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import EditIcon from '@mui/icons-material/Edit'; +import ReplyIcon from '@mui/icons-material/Reply'; +import CheckIcon from '@mui/icons-material/Check'; +import DeleteIcon from '@mui/icons-material/Delete'; +import DoneAllIcon from '@mui/icons-material/DoneAll'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import AutorenewIcon from '@mui/icons-material/Autorenew'; +import PopupModal from './PopupModal'; + +function formatDateTime(timestamp?: number): string { + if (timestamp === undefined) return 'unknown'; + if (timestamp === -1) return 'unknown'; + if (timestamp > 1e12) { + timestamp = Math.floor(timestamp / 1000); // Convert milliseconds to seconds + } + // Convert the timestamp to milliseconds (UNIX timestamps are in seconds) + const date = new Date(timestamp * 1000); + + // Options for formatting the date + const options: Intl.DateTimeFormatOptions = { + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit', + hour12: true, + }; + + // Format the date using Intl.DateTimeFormat + return new Intl.DateTimeFormat('en-US', options).format(date); +} + +interface IndividualNoteProps { + notes: Note[]; + note: Note; + idx: number; + editMode?: boolean; + handleClickAction: ( + idx: number, + action: string, + newText?: string, + event?: React.MouseEvent + ) => void; + userEmail: string; + replyToIdx?: number; + editIdx?: number; + childOfResolved?: boolean; +} + +const IndividualNote = ({ + notes, + note, + idx, + editMode, + handleClickAction, + userEmail, + replyToIdx, + editIdx, + childOfResolved, +}: IndividualNoteProps) => { + const [newText, setNewText] = useState(note.text); + const [disableSaveEdit, setDisableSaveEdit] = useState(false); + const [replyText, setReplyText] = useState(''); + if (note.deleted) return null; + const styles = { + outerDiv: { + backgroundColor: note.resolved || childOfResolved ? '#F5F5F6' : undefined, + }, + innerDiv: { + paddingX: 1, + paddingY: 1, + paddingBottom: 1, + marginLeft: note?.isReply ? 4 : 0, + borderRadius: 1, // Rounded corners + }, + metadata: { + opacity: 0.9, + fontSize: '13px', + }, + icon: { + fontSize: '14px', + color: 'black', + }, + indentedDivider: { + paddingLeft: '24px', + }, + resolvedTypography: { + paddingX: 1, + paddingY: 1, + paddingBottom: 1, + marginLeft: 4, + borderRadius: 1, // Rounded corners + }, + textfield: { + '& .MuiOutlinedInput-root': { + // Default border + '& fieldset': { + borderWidth: '1px', + borderColor: 'black', + }, + // On hover + '&:hover fieldset': { + borderWidth: '1.5px', + }, + // On focus + '&.Mui-focused fieldset': { + borderWidth: '2px', + borderColor: 'black', + }, + }, + }, + replyDiv: { + marginLeft: '32px', + marginTop: '8px', + }, + }; + + const clickCancel = () => { + setReplyText(''); + handleClickAction(idx, 'reply'); + }; + + const clickSubmit = () => { + handleClickAction(idx, 'submit reply', replyText); + setReplyText(''); + }; + + const handleUpdateText = (e: any) => { + let newValue = e.target.value; + setNewText(newValue); + if (newValue === '') setDisableSaveEdit(true); + else setDisableSaveEdit(false); + }; + return ( +
+
+ +
+ +
+ +
+ {editMode ? ( + + ) : ( + {note.text} + )} +
+ + + {note.resolved ? ( +
+ + + handleClickAction(idx, 'resolve', undefined, e) + } + > + + + +
+ ) : ( + !childOfResolved && ( +
+ {note.creator === userEmail && ( + + + handleClickAction(idx, 'edit', newText) + } + > + {editMode ? ( + + ) : ( + + )} + + + )} + {!note.isReply && ( + + handleClickAction(idx, 'resolve')} + > + + + + )} + {!note.isReply && ( + + handleClickAction(idx, 'reply')} + > + + + + )} + {note.creator === userEmail && ( + + handleClickAction(idx, 'delete')} + > + + + + )} +
+ ) + )} +
+
+ + + - {note.creator || 'unknown'},{' '} + {formatDateTime(note.timestamp || -1)} + + +
+
+ + {note.replies && + note.replies.map((replyIdx) => { + return ( + + ); + })} + {note.resolved && ( +
+
+ +
+ + + Resolved by {note.lastUpdatedUser || 'unknown'},{' '} + {formatDateTime(note.lastUpdated || -1)} + + +
+ )} + {replyToIdx === idx && ( +
+ setReplyText(e.target.value)} + sx={styles.textfield} + /> + +
+
+ + +
+
+
+ )} +
+ ); +}; + +interface NotesDialogProps { + record_id?: string; + open: boolean; + userEmail: string; + initialNotes: Note[]; + onClose: (record_id?: string, newNotes?: Note[], submitted?: boolean) => void; + onUpdateNote?: ( + updateType: string, + index: number, + text?: string, + isReply?: boolean + ) => void; +} + +interface Note { + text: string; + record_id: string; + isReply: boolean; + resolved: boolean; + timestamp: number; + deleted?: boolean; + creator?: string; + lastUpdated?: number; + lastUpdatedUser?: string; + replies?: number[]; // list of indexes of notes that reply to this guy + repliesTo?: number; // the index that this comment replies to, if this is a reply +} + +const NotesDialog = ({ + record_id, + open, + onClose, + userEmail, + initialNotes, + onUpdateNote, +}: NotesDialogProps) => { + const [notes, setNotes] = useState([] as Note[]); + const [replyToIdx, setReplyToIdx] = useState(); + const [editIdx, setEditIdx] = useState(); + const [deleteIdx, setDeleteIdx] = useState(); + const [newNoteText, setNewNoteText] = useState(''); + const [disableButton, setDisableButton] = useState(false); + const [loading, setLoading] = useState(true); + const [showResolved, setShowResolved] = useState(false); + const descriptionElementRef = useRef(null); + const dialogHeight = '80vh'; + const dialogWidth = '35vw'; + + const reset = (newNotes?: Note[]) => { + setReplyToIdx(undefined); + setEditIdx(undefined); + setDeleteIdx(undefined); + setNewNoteText(''); + setDisableButton(false); + setLoading(false); + if (newNotes === undefined) setNotes([]); + else setNotes(newNotes); + }; + + useEffect(() => { + if (open) { + const { current: descriptionElement } = descriptionElementRef; + if (descriptionElement !== null) { + descriptionElement.focus(); + } + if (initialNotes !== undefined) { + setNotes(initialNotes || []); + setLoading(false); + } + } else reset(); + }, [open, initialNotes]); + + const handleSuccessfulNoteUpdate = (data: Note[]) => { + reset(data); + }; + + const handleUpdateRecordNotes = ( + updateType: string, + index: number, + text?: string, + isReply?: boolean + ) => { + // If update function was provided, call that + if (onUpdateNote) onUpdateNote(updateType, index, text, isReply); + // update the note in javascript + else { + let tempNotes = [...notes]; + if (updateType === 'add') { + const newNote: Note = { + text: text || '', + record_id: record_id || '', + timestamp: Date.now(), + creator: userEmail, + resolved: false, + deleted: false, + lastUpdated: Date.now(), + replies: [], + isReply: isReply || false, + lastUpdatedUser: userEmail, + }; + if (isReply) { + newNote.repliesTo = replyToIdx; + if (replyToIdx !== undefined) + tempNotes[replyToIdx].replies?.push(index); + } + // add note + tempNotes.push(newNote); + } else if (updateType === 'edit') { + let editedNote = tempNotes[index]; + editedNote.text = text || ''; + editedNote.lastUpdated = Date.now(); + editedNote.lastUpdatedUser = userEmail; + } else if (updateType === 'resolve' || updateType === 'unresolve') { + let resolvedNote = tempNotes[index]; + resolvedNote.resolved = !resolvedNote.resolved; + resolvedNote.lastUpdated = Date.now(); + resolvedNote.lastUpdatedUser = userEmail; + } else if (updateType === 'delete') { + let deletedNote = tempNotes[index]; + deletedNote.deleted = true; + deletedNote.lastUpdated = Date.now(); + deletedNote.lastUpdatedUser = userEmail; + } + handleSuccessfulNoteUpdate(tempNotes); + } + }; + + const styles = { + dialogPaper: { + minHeight: dialogHeight, + maxHeight: dialogHeight, + minWidth: dialogWidth, + maxWidth: dialogWidth, + display: 'flex', + flexDirection: 'column', + }, + dialogContent: { + flex: 1, // Make content area take up available vertical space + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', // Ensure bottom section stays at the bottom + }, + boxTop: { + overflow: 'scroll', + }, + boxBottom: { + marginTop: 2, + }, + replyToText: { + opacity: 0.5, + fontStyle: 'italic', + overflow: 'hidden', + margin: 0, + }, + textfield: { + '& .MuiOutlinedInput-root': { + // Default border + '& fieldset': { + borderWidth: '1px', + borderColor: 'black', + }, + // On hover + '&:hover fieldset': { + borderWidth: '1.5px', + borderColor: 'black', + }, + // On focus + '&.Mui-focused fieldset': { + borderWidth: '2px', + borderColor: 'black', + }, + }, + }, + resolvedCommentsDiv: { + backgroundColor: '#E7E7E7', + borderRadius: '2px', + }, + resolvedCommentsText: { + // opacity: 0.6, + fontSize: '13px', + fontWeight: 'bold', + paddingLeft: '8px', + }, + icon: { + fontSize: '14px', + color: 'black', + }, + }; + + const handleAddNote = () => { + handleUpdateRecordNotes('add', notes.length, newNoteText); + }; + + const handleEditNote = (idx: number, newValue: string) => { + handleUpdateRecordNotes('edit', idx, newValue); + }; + + const handleResolveNote = (idx: number) => { + if (notes[idx].resolved) handleUpdateRecordNotes('unresolve', idx); + else handleUpdateRecordNotes('resolve', idx); + }; + + const handleDeleteNote = () => { + if (deleteIdx !== undefined) handleUpdateRecordNotes('delete', deleteIdx); + }; + + const handleClickAction = ( + idx: number, + action: string, + newValue?: string + ) => { + if (action === 'edit') { + if (editIdx === idx) { + handleEditNote(idx, newValue || ''); + } else setEditIdx(idx); + } else if (action === 'reply') { + if (replyToIdx === idx) setReplyToIdx(undefined); + else { + setTimeout(() => { + document.getElementById('reply-textfield')?.focus(); + }, 0); + + setReplyToIdx(idx); + } + } else if (action === 'resolve') { + handleResolveNote(idx); + } else if (action === 'delete') { + setDeleteIdx(idx); + } else if (action === 'submit reply') { + handleUpdateRecordNotes('add', notes.length, newValue, true); + } + }; + + const checkForResolved = () => { + for (let note of notes) { + if (note.resolved) return true; + } + return false; + }; + + return ( + onClose(record_id, notes)} + scroll={'paper'} + aria-labelledby="new-dg-dialog" + aria-describedby="new-dg-dialog-description" + PaperProps={{ + sx: styles.dialogPaper, + }} + > + Notes + onClose(record_id, notes)} + sx={{ + position: 'absolute', + right: 0, + top: 8, + }} + > + + + + {/* Top content */} + + {!loading && + notes.map((note, idx) => { + if (!note.isReply && !note.deleted && !note.resolved) { + return ( +
+ +
+ ); + } + })} + {!loading && checkForResolved() && ( +
+
+ + + + Resolved comments + + + setShowResolved(!showResolved)}> + {showResolved ? ( + + ) : ( + + )} + + + +
+ {showResolved && + notes.map((note, idx) => { + if (note.resolved) { + return ( +
+ +
+ ); + } + })} +
+ )} + + + + {loading ? ( +

loading...

+ ) : ( + notes.length === 0 && ( +

No notes added yet

+ ) + )} +
+ + {/* Bottom section */} + + setNewNoteText(e.target.value)} + multiline + minRows={2} + disabled={disableButton} + sx={styles.textfield} + /> + + + + + + setDeleteIdx(undefined)} + text="Are you sure you want to delete this comment?" + handleSave={handleDeleteNote} + buttonText="Delete" + buttonColor="error" + buttonVariant="contained" + width={400} + /> +
+
+ ); +}; + +export default NotesDialog; diff --git a/src/components/PopupModal.tsx b/src/components/PopupModal.tsx new file mode 100644 index 00000000..723e90dd --- /dev/null +++ b/src/components/PopupModal.tsx @@ -0,0 +1,90 @@ +import { Grid, Button, Modal, IconButton } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; + +interface PopupModalProps { + width?: number; + open: boolean; + text: string; + handleClose: () => void; + handleSave: () => void; + buttonVariant: 'text' | 'outlined' | 'contained'; + buttonColor: + | 'inherit' + | 'primary' + | 'secondary' + | 'error' + | 'info' + | 'success' + | 'warning'; + buttonText: string; +} + +const PopupModal = (props: PopupModalProps) => { + const { + width, + text, + open, + handleClose, + handleSave, + buttonVariant, + buttonColor, + buttonText, + } = props; + + const styles = { + modalStyle: { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: width !== undefined ? width : 400, + bgcolor: 'background.paper', + border: '2px solid #000', + boxShadow: 24, + p: 4, + }, + }; + + return ( + + + + + + +

{text}

+
+ <> + + + + + + +
+
+ ); +}; + +export default PopupModal; diff --git a/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts b/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts new file mode 100644 index 00000000..4094f9cd --- /dev/null +++ b/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts @@ -0,0 +1,36 @@ +import { MonitorActivitiesConfig } from './taskflow.types'; + +export const taskflow: MonitorActivitiesConfig = { + data: { + items: { + /** + * Source of the data for the initial list of items. + */ + source: 'data/default/monitor-activities/experiments.json', + /** + * Field name for the unique ID in the data source. + */ + idField: 'id', + }, + detail: { + /** + * Source of the data for the detail view of a single item. + */ + source: 'data/default/monitor-activities/experiment_detail.json', + /** + * Field name for the unique ID in the data source. + */ + idField: 'id', + }, + notes: { + /** + * Source of the data for the detail view of a single item. + */ + source: 'data/default/monitor-activities-with-notes/notes.json', + /** + * Field name for the unique ID in the data source. + */ + idField: 'record_id', + }, + }, +}; diff --git a/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts b/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts new file mode 100644 index 00000000..17b75625 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts @@ -0,0 +1,18 @@ +/** + * Type definitions for the Monitor Activities Task Flow config object + */ +export interface MonitorActivitiesConfig { + /** Attributes that are used across the Task Flow */ + properties?: any; + data: { + items: { + source: string; + idField: string; + }; + [key: string]: { + source: string; + idField: string; + }; + }; + pages?: any; +} diff --git a/src/pages/monitor-activities-with-notes/_layout.tsx b/src/pages/monitor-activities-with-notes/_layout.tsx new file mode 100644 index 00000000..138fb599 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/_layout.tsx @@ -0,0 +1,23 @@ +import { Box } from '@mui/material'; +import React from 'react'; +import { Outlet } from 'react-router-dom'; +import { TopBar } from '../../components/TopBar'; + +/** + * Top-level wrapper for the monitor-activities Task Flow templates. + * Inner pages are rendered inside the `` component + */ +const MonitorActivitiesLayout: React.FC = () => { + return ( + + + + + + + + + ); +}; + +export default MonitorActivitiesLayout; diff --git a/src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts b/src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts new file mode 100644 index 00000000..df2f362f --- /dev/null +++ b/src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts @@ -0,0 +1,24 @@ +describe('The Monitor Activities Task Flow', () => { + beforeEach(() => { + cy.visit('/monitor-activities'); + }); + + it('successfully loads', () => { + cy.get('h1').contains('Experiments'); + }); + + it('drills into a row and shows details', () => { + cy.get('.MuiDataGrid-columnHeader').should('have.length', 4); + cy.get('.MuiDataGrid-row').eq(1).click(); + cy.get('button[data-testid="mna-back-button"]'); + cy.get('.MuiDataGrid-columnHeader').should('have.length', 3); + cy.get('h2').contains('Notes'); + cy.get('.js-plotly-plot'); + }); + + it('drills into a row and goes back', () => { + cy.get('.MuiDataGrid-row').eq(1).click(); + cy.get('button[data-testid="mna-back-button"]').click(); + cy.get('.MuiDataGrid-columnHeader').should('have.length', 4); + }); +}); diff --git a/src/pages/monitor-activities-with-notes/calendar.tsx b/src/pages/monitor-activities-with-notes/calendar.tsx new file mode 100644 index 00000000..b8d526a0 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/calendar.tsx @@ -0,0 +1,35 @@ +import { Container, Link, Typography } from '@mui/material'; +import { DateCalendar, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import React from 'react'; +import { Link as RouterLink } from 'react-router-dom'; + +/** + * Work in Progress: + * + * Page to see all activities by day in a calendar view in the monitor-activities Task Flow. + */ +const ActivityCalendar: React.FC = () => { + return ( + + + 2023 Experiments (Work in Progress) + + {/* TODO: calendar visualization */} + + + + + List + + + ); +}; + +export default ActivityCalendar; diff --git a/src/pages/monitor-activities-with-notes/detail.tsx b/src/pages/monitor-activities-with-notes/detail.tsx new file mode 100644 index 00000000..baa65ff2 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/detail.tsx @@ -0,0 +1,186 @@ +import AddIcon from '@mui/icons-material/Add'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import { + Box, + Button, + Container, + Grid, + IconButton, + Link, + Paper, + Stack, + TextField, + Typography, +} from '@mui/material'; +import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; +import dayjs from 'dayjs'; +import React from 'react'; +import Plot from 'react-plotly.js'; +import { Link as RouterLink } from 'react-router-dom'; +import { LabelValueTable } from '../../components/LabelValueTable'; +import { useDataFromSource } from '../../utils/useDataFromSource'; +import { taskflow } from './_config/taskflow.config'; +import NotesDialog from '../../components/NotesDialog'; +import { useState } from 'react'; + +const dateComparator: GridComparatorFn = (v1, v2) => { + return dayjs(v1).isAfter(dayjs(v2)) ? 1 : 0; +}; + +const columns: GridColDef[] = [ + { + field: 'event_type', + headerName: 'Event Type', + width: 200, + }, + { + field: 'event_time', + headerName: 'Event Time', + sortComparator: dateComparator, + width: 200, + }, + { + field: 'confidence', + headerName: 'Confidence', + type: 'number', + width: 200, + }, +]; + +/** + * Detail view of the selected activity from `` in monitor-activities Task Flow. + * The two components are not currently hooked together. + */ +const ActivityDetail: React.FC = () => { + const experiment = useDataFromSource(taskflow.data.detail.source); + const defaultNotes = useDataFromSource(taskflow.data.notes.source); + const [openNotes, setOpenNotes] = useState(false); + + const getNoteRows = (notes: any[]) => { + return notes.map((note) => { + note.label = note.created_time; + note.value = note.content; + return note; + }); + }; + + /** + * Content to render on the page for this component + */ + return ( + + + + + + + + + + {experiment?.experiment_name} + + + + + + + {experiment && ( + row.id} + columns={columns} + initialState={{ + sorting: { + sortModel: [{ field: 'event_time', sort: 'desc' }], + }, + }} + disableColumnSelector + disableRowSelectionOnClick + /> + )} + + + + + + + + Notes + + {experiment && ( + + )} + + + + + + + + + + + + + + + setOpenNotes(false)} + /> + + ); +}; + +export default ActivityDetail; diff --git a/src/pages/monitor-activities-with-notes/index.tsx b/src/pages/monitor-activities-with-notes/index.tsx new file mode 100644 index 00000000..fccd8fa7 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/index.tsx @@ -0,0 +1,85 @@ +import { Container, Paper, Stack, Typography } from '@mui/material'; +import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; +import dayjs from 'dayjs'; +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useDataFromSource } from '../../utils/useDataFromSource'; +import { taskflow } from './_config/taskflow.config'; + +const dateComparator: GridComparatorFn = (v1, v2) => { + return dayjs(v1).isAfter(dayjs(v2)) ? 1 : 0; +}; + +const columns: GridColDef[] = [ + { + field: 'experiment_name', + headerName: 'Experiment Name', + width: 200, + }, + { + field: 'start_time', + headerName: 'Start Time', + sortComparator: dateComparator, + width: 200, + }, + { + field: 'end_time', + headerName: 'End Time', + width: 200, + }, + { + field: 'status', + headerName: 'Status', + width: 200, + }, +]; + +/** + * List view of all activities in the monitor-activites Task Flow. + */ +const ActivityList: React.FC = () => { + const experiments = useDataFromSource(taskflow.data.items.source); + const navigate = useNavigate(); + + /** + * Content to render on the page for this component + */ + return ( + + + + + Experiments test + + + + row.id} + columns={columns} + initialState={{ + sorting: { + sortModel: [{ field: 'start_time', sort: 'desc' }], + }, + }} + onRowClick={() => navigate('detail')} + disableColumnSelector + disableRowSelectionOnClick + /> + + + + ); +}; + +export default ActivityList; From c12a35dc86f34ef881519a2d742c3de46f0e1d25 Mon Sep 17 00:00:00 2001 From: MichaelPesce Date: Tue, 6 May 2025 12:50:15 -0400 Subject: [PATCH 2/4] convert dialog into box --- src/components/NotesDialog.tsx | 50 ++++---------- .../monitor-activities-with-notes/detail.tsx | 65 +++++-------------- 2 files changed, 31 insertions(+), 84 deletions(-) diff --git a/src/components/NotesDialog.tsx b/src/components/NotesDialog.tsx index 31b2309e..8501c236 100644 --- a/src/components/NotesDialog.tsx +++ b/src/components/NotesDialog.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState, useRef } from 'react'; -import { Dialog, DialogTitle, DialogContent } from '@mui/material'; import { IconButton, Tooltip, @@ -10,7 +9,6 @@ import { Typography, Divider, } from '@mui/material'; -import CloseIcon from '@mui/icons-material/Close'; import EditIcon from '@mui/icons-material/Edit'; import ReplyIcon from '@mui/icons-material/Reply'; import CheckIcon from '@mui/icons-material/Check'; @@ -317,7 +315,11 @@ interface NotesDialogProps { open: boolean; userEmail: string; initialNotes: Note[]; - onClose: (record_id?: string, newNotes?: Note[], submitted?: boolean) => void; + onClose?: ( + record_id?: string, + newNotes?: Note[], + submitted?: boolean + ) => void; onUpdateNote?: ( updateType: string, index: number, @@ -343,7 +345,6 @@ interface Note { const NotesDialog = ({ record_id, open, - onClose, userEmail, initialNotes, onUpdateNote, @@ -357,8 +358,6 @@ const NotesDialog = ({ const [loading, setLoading] = useState(true); const [showResolved, setShowResolved] = useState(false); const descriptionElementRef = useRef(null); - const dialogHeight = '80vh'; - const dialogWidth = '35vw'; const reset = (newNotes?: Note[]) => { setReplyToIdx(undefined); @@ -378,7 +377,7 @@ const NotesDialog = ({ descriptionElement.focus(); } if (initialNotes !== undefined) { - setNotes(initialNotes || []); + setNotes(initialNotes); setLoading(false); } } else reset(); @@ -441,10 +440,6 @@ const NotesDialog = ({ const styles = { dialogPaper: { - minHeight: dialogHeight, - maxHeight: dialogHeight, - minWidth: dialogWidth, - maxWidth: dialogWidth, display: 'flex', flexDirection: 'column', }, @@ -456,6 +451,7 @@ const NotesDialog = ({ }, boxTop: { overflow: 'scroll', + maxHeight: '25vh', }, boxBottom: { marginTop: 2, @@ -553,29 +549,11 @@ const NotesDialog = ({ }; return ( - onClose(record_id, notes)} - scroll={'paper'} - aria-labelledby="new-dg-dialog" - aria-describedby="new-dg-dialog-description" - PaperProps={{ - sx: styles.dialogPaper, - }} - > - Notes - onClose(record_id, notes)} - sx={{ - position: 'absolute', - right: 0, - top: 8, - }} - > - - - + + + Notes + + {/* Top content */} {!loading && @@ -689,8 +667,8 @@ const NotesDialog = ({ buttonVariant="contained" width={400} /> - - + + ); }; diff --git a/src/pages/monitor-activities-with-notes/detail.tsx b/src/pages/monitor-activities-with-notes/detail.tsx index baa65ff2..85426b2c 100644 --- a/src/pages/monitor-activities-with-notes/detail.tsx +++ b/src/pages/monitor-activities-with-notes/detail.tsx @@ -1,15 +1,15 @@ -import AddIcon from '@mui/icons-material/Add'; +// import AddIcon from '@mui/icons-material/Add'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { Box, - Button, + // Button, Container, Grid, IconButton, Link, Paper, Stack, - TextField, + // TextField, Typography, } from '@mui/material'; import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; @@ -17,11 +17,10 @@ import dayjs from 'dayjs'; import React from 'react'; import Plot from 'react-plotly.js'; import { Link as RouterLink } from 'react-router-dom'; -import { LabelValueTable } from '../../components/LabelValueTable'; +// import { LabelValueTable } from '../../components/LabelValueTable'; import { useDataFromSource } from '../../utils/useDataFromSource'; import { taskflow } from './_config/taskflow.config'; import NotesDialog from '../../components/NotesDialog'; -import { useState } from 'react'; const dateComparator: GridComparatorFn = (v1, v2) => { return dayjs(v1).isAfter(dayjs(v2)) ? 1 : 0; @@ -54,15 +53,14 @@ const columns: GridColDef[] = [ const ActivityDetail: React.FC = () => { const experiment = useDataFromSource(taskflow.data.detail.source); const defaultNotes = useDataFromSource(taskflow.data.notes.source); - const [openNotes, setOpenNotes] = useState(false); - const getNoteRows = (notes: any[]) => { - return notes.map((note) => { - note.label = note.created_time; - note.value = note.content; - return note; - }); - }; + // const getNoteRows = (notes: any[]) => { + // return notes.map((note) => { + // note.label = note.created_time; + // note.value = note.content; + // return note; + // }); + // }; /** * Content to render on the page for this component @@ -114,34 +112,12 @@ const ActivityDetail: React.FC = () => { }} > - - Notes - - {experiment && ( - - )} - - - - + { - setOpenNotes(false)} - /> ); }; From a7cdcf57e2faf4bc58aa10749b5fabb45e9ae919 Mon Sep 17 00:00:00 2001 From: MichaelPesce Date: Tue, 6 May 2025 13:09:19 -0400 Subject: [PATCH 3/4] merge main in; make all updates to (hopefully) sync monitor-activities-with-notes to the same as monitor-activities --- .../experiment_detail.json | 92 ------ .../experiments.json | 303 ------------------ .../default_notes.json} | 0 .../monitor-activities.cy.ts | 0 .../_config/taskflow.config.ts | 36 --- .../_config/taskflow.types.ts | 18 -- .../monitor-activities-with-notes/_layout.tsx | 23 -- .../calendar.tsx | 22 +- .../monitor-activities-with-notes/detail.tsx | 31 +- .../monitor-activities-with-notes/index.tsx | 29 +- src/routeTree.gen.ts | 82 +++++ 11 files changed, 127 insertions(+), 509 deletions(-) delete mode 100644 public/data/default/monitor-activities-with-notes/experiment_detail.json delete mode 100644 public/data/default/monitor-activities-with-notes/experiments.json rename public/{data/default/monitor-activities-with-notes/notes.json => dummy-data/default_notes.json} (100%) rename src/pages/monitor-activities-with-notes/{_tests => -tests}/monitor-activities.cy.ts (100%) delete mode 100644 src/pages/monitor-activities-with-notes/_config/taskflow.config.ts delete mode 100644 src/pages/monitor-activities-with-notes/_config/taskflow.types.ts delete mode 100644 src/pages/monitor-activities-with-notes/_layout.tsx diff --git a/public/data/default/monitor-activities-with-notes/experiment_detail.json b/public/data/default/monitor-activities-with-notes/experiment_detail.json deleted file mode 100644 index f709181c..00000000 --- a/public/data/default/monitor-activities-with-notes/experiment_detail.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "id": 45, - "experiment_name": "Experiment Cricket", - "start_time": "12/16/2023, 3:01:07 PM", - "end_time": null, - "status": "Running", - "events": [ - { - "id": 10, - "event_type": "Chirp", - "event_time": "12/17/2023, 9:09:15 PM", - "confidence": 97 - }, - { - "id": 9, - "event_type": "Chirp", - "event_time": "12/17/2023, 8:11:19 PM", - "confidence": 98 - }, - { - "id": 8, - "event_type": "Hop", - "event_time": "12/17/2023, 4:33:53 PM", - "confidence": 64 - }, - { - "id": 7, - "event_type": "Hop", - "event_time": "12/17/2023, 11:09:15 AM", - "confidence": 97 - }, - { - "id": 6, - "event_type": "Hop", - "event_time": "12/17/2023, 11:02:05 AM", - "confidence": 99 - }, - { - "id": 5, - "event_type": "Hop", - "event_time": "12/17/2023, 7:23:32 AM", - "confidence": 75 - }, - { - "id": 4, - "event_type": "Chirp", - "event_time": "12/17/2023, 7:03:44 PM", - "confidence": 99 - }, - { - "id": 3, - "event_type": "Chirp", - "event_time": "12/17/2023, 3:26:42 PM", - "confidence": 97 - }, - { - "id": 2, - "event_type": "Chirp", - "event_time": "12/16/2023, 11:55:21 PM", - "confidence": 97 - }, - { - "id": 1, - "event_type": "Chirp", - "event_time": "12/16/2023, 10:08:10 PM", - "confidence": 98 - }, - { - "id": 0, - "event_type": "Chirp", - "event_time": "12/16/2023, 9:45:10 PM", - "confidence": 97 - } - ], - "notes": [ - { - "id": 2, - "created_time": "12/17/2023, 4:00:29 PM", - "content": "Aute amet aute irure in consectetur enim incididunt aute consectetur." - }, - { - "id": 1, - "created_time": "12/17/2023, 7:00:25 AM", - "content": "Labore elit cupidatat eiusmod sunt aliquip pariatur quis." - }, - { - "id": 0, - "created_time": "12/16/2023, 11:59:20 PM", - "content": "Culpa id officia laborum cupidatat dolor mollit." - } - ] -} diff --git a/public/data/default/monitor-activities-with-notes/experiments.json b/public/data/default/monitor-activities-with-notes/experiments.json deleted file mode 100644 index 649a52fb..00000000 --- a/public/data/default/monitor-activities-with-notes/experiments.json +++ /dev/null @@ -1,303 +0,0 @@ -[ - { - "id": 45, - "experiment_name": "Experiment Cricket", - "start_time": "12/16/2023, 3:01:07 PM", - "end_time": null, - "status": "Running" - }, - { - "id": 44, - "experiment_name": "Experiment Mouse", - "start_time": "12/15/2023, 1:40:10 PM", - "end_time": null, - "status": "Running" - }, - { - "id": 8, - "experiment_name": "Experiment Fox", - "start_time": "6/27/2023, 1:42:11 PM", - "end_time": "6/30/2023, 5:15:29 AM", - "status": "Complete" - }, - { - "id": 14, - "experiment_name": "Experiment Elephant", - "start_time": "10/15/2023, 1:42:11 PM", - "end_time": "10/18/2023, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 30, - "experiment_name": "Experiment Hippo", - "start_time": "6/27/2023, 1:42:11 PM", - "end_time": "6/30/2023, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 43, - "experiment_name": "Experiment Cherry", - "start_time": "5/20/2023, 9:34:45 AM", - "end_time": "5/22/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 23, - "experiment_name": "Experiment Lime", - "start_time": "4/22/2023, 9:34:45 AM", - "end_time": "4/24/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 39, - "experiment_name": "Experiment Giraffe", - "start_time": "4/28/2023, 9:34:45 AM", - "end_time": "4/30/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 26, - "experiment_name": "Experiment Horse", - "start_time": "2/17/2023, 1:42:11 PM", - "end_time": "2/20/2023, 5:15:29 AM", - "status": "Complete" - }, - { - "id": 11, - "experiment_name": "Experiment Sloth", - "start_time": "1/14/2023, 9:34:45 AM", - "end_time": "1/16/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 35, - "experiment_name": "Experiment Gargoyle", - "start_time": "7/10/2023, 9:34:45 AM", - "end_time": "7/12/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 19, - "experiment_name": "Experiment Lion", - "start_time": "8/6/2023, 9:34:45 AM", - "end_time": "8/8/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 3, - "experiment_name": "Experiment Jasmine", - "start_time": "6/5/2023, 9:34:45 AM", - "end_time": "6/7/2023, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 6, - "experiment_name": "Experiment Crocodile", - "start_time": "9/20/2022, 1:42:11 PM", - "end_time": "9/23/2022, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 32, - "experiment_name": "Experiment Lemon", - "start_time": "9/6/2022, 11:27:18 AM", - "end_time": "9/8/2022, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 17, - "experiment_name": "Experiment Iceberg", - "start_time": "7/8/2022, 9:11:04 AM", - "end_time": "7/10/2022, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 41, - "experiment_name": "Experiment Diamond", - "start_time": "6/19/2022, 9:11:04 AM", - "end_time": "6/21/2022, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 21, - "experiment_name": "Experiment Ruby", - "start_time": "6/12/2022, 9:11:04 AM", - "end_time": "6/14/2022, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 27, - "experiment_name": "Experiment Pigeon", - "start_time": "5/29/2022, 11:27:18 AM", - "end_time": "5/31/2022, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 13, - "experiment_name": "Experiment Oregono", - "start_time": "5/9/2022, 9:11:04 AM", - "end_time": "5/11/2022, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 28, - "experiment_name": "Experiment Otter", - "start_time": "5/1/2022, 11:27:18 AM", - "end_time": "5/3/2022, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 0, - "experiment_name": "Experiment Gecko", - "start_time": "3/14/2022, 11:27:18 AM", - "end_time": "3/16/2022, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 25, - "experiment_name": "Experiment Banana", - "start_time": "3/5/2022, 9:11:04 AM", - "end_time": "3/7/2022, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 34, - "experiment_name": "Experiment Thunder", - "start_time": "12/7/2021, 1:42:11 PM", - "end_time": "12/10/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 15, - "experiment_name": "Experiment Tornado", - "start_time": "12/1/2021, 9:34:45 AM", - "end_time": "12/3/2021, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 9, - "experiment_name": "Experiment Wind", - "start_time": "11/13/2021, 1:42:11 PM", - "end_time": "11/16/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 29, - "experiment_name": "Experiment Pufferfish", - "start_time": "10/14/2021, 9:11:04 AM", - "end_time": "10/16/2021, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 42, - "experiment_name": "Experiment Strawberry", - "start_time": "9/26/2021, 1:42:11 PM", - "end_time": "9/29/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 22, - "experiment_name": "Experiment Grape", - "start_time": "9/19/2021, 1:42:11 PM", - "end_time": "9/22/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 10, - "experiment_name": "Experiment Lightning", - "start_time": "8/30/2021, 1:42:11 PM", - "end_time": "9/2/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 2, - "experiment_name": "Experiment Leopard", - "start_time": "8/22/2021, 1:42:11 PM", - "end_time": "8/25/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 38, - "experiment_name": "Experiment Donkey", - "start_time": "8/15/2021, 1:42:11 PM", - "end_time": "8/18/2021, 5:15:29 AM", - "status": "Interrupted" - }, - { - "id": 7, - "experiment_name": "Experiment Walrus", - "start_time": "5/15/2021, 9:34:45 AM", - "end_time": "5/17/2021, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 16, - "experiment_name": "Experiment Basil", - "start_time": "3/24/2020, 11:27:18 AM", - "end_time": "3/26/2020, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 18, - "experiment_name": "Experiment Guava", - "start_time": "11/28/2020, 9:11:04 AM", - "end_time": "11/30/2020, 11:27:39 AM", - "status": "Interrupted" - }, - { - "id": 5, - "experiment_name": "Experiment Kiwi", - "start_time": "11/7/2020, 9:11:04 AM", - "end_time": "11/9/2020, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 12, - "experiment_name": "Experiment Shark", - "start_time": "6/25/2020, 11:27:18 AM", - "end_time": "6/27/2020, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 33, - "experiment_name": "Experiment Turtle", - "start_time": "5/2/2020, 9:11:04 AM", - "end_time": "5/4/2020, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 1, - "experiment_name": "Experiment Dragon", - "start_time": "4/28/2020, 9:11:04 AM", - "end_time": "4/30/2020, 11:27:39 AM", - "status": "Failed" - }, - { - "id": 31, - "experiment_name": "Experiment Lizard", - "start_time": "2/20/2020, 9:34:45 AM", - "end_time": "2/22/2020, 2:46:01 PM", - "status": "Complete" - }, - { - "id": 20, - "experiment_name": "Experiment Apple", - "start_time": "1/29/2020, 11:27:18 AM", - "end_time": "1/31/2020, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 36, - "experiment_name": "Experiment Avocado", - "start_time": "1/15/2020, 11:27:18 AM", - "end_time": "1/17/2020, 3:48:52 AM", - "status": "Complete" - }, - { - "id": 4, - "experiment_name": "Experiment Watermelon", - "start_time": "1/11/2020, 11:27:18 AM", - "end_time": "1/13/2020, 3:48:52 AM", - "status": "Complete" - } -] diff --git a/public/data/default/monitor-activities-with-notes/notes.json b/public/dummy-data/default_notes.json similarity index 100% rename from public/data/default/monitor-activities-with-notes/notes.json rename to public/dummy-data/default_notes.json diff --git a/src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts b/src/pages/monitor-activities-with-notes/-tests/monitor-activities.cy.ts similarity index 100% rename from src/pages/monitor-activities-with-notes/_tests/monitor-activities.cy.ts rename to src/pages/monitor-activities-with-notes/-tests/monitor-activities.cy.ts diff --git a/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts b/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts deleted file mode 100644 index 4094f9cd..00000000 --- a/src/pages/monitor-activities-with-notes/_config/taskflow.config.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MonitorActivitiesConfig } from './taskflow.types'; - -export const taskflow: MonitorActivitiesConfig = { - data: { - items: { - /** - * Source of the data for the initial list of items. - */ - source: 'data/default/monitor-activities/experiments.json', - /** - * Field name for the unique ID in the data source. - */ - idField: 'id', - }, - detail: { - /** - * Source of the data for the detail view of a single item. - */ - source: 'data/default/monitor-activities/experiment_detail.json', - /** - * Field name for the unique ID in the data source. - */ - idField: 'id', - }, - notes: { - /** - * Source of the data for the detail view of a single item. - */ - source: 'data/default/monitor-activities-with-notes/notes.json', - /** - * Field name for the unique ID in the data source. - */ - idField: 'record_id', - }, - }, -}; diff --git a/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts b/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts deleted file mode 100644 index 17b75625..00000000 --- a/src/pages/monitor-activities-with-notes/_config/taskflow.types.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Type definitions for the Monitor Activities Task Flow config object - */ -export interface MonitorActivitiesConfig { - /** Attributes that are used across the Task Flow */ - properties?: any; - data: { - items: { - source: string; - idField: string; - }; - [key: string]: { - source: string; - idField: string; - }; - }; - pages?: any; -} diff --git a/src/pages/monitor-activities-with-notes/_layout.tsx b/src/pages/monitor-activities-with-notes/_layout.tsx deleted file mode 100644 index 138fb599..00000000 --- a/src/pages/monitor-activities-with-notes/_layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Box } from '@mui/material'; -import React from 'react'; -import { Outlet } from 'react-router-dom'; -import { TopBar } from '../../components/TopBar'; - -/** - * Top-level wrapper for the monitor-activities Task Flow templates. - * Inner pages are rendered inside the `` component - */ -const MonitorActivitiesLayout: React.FC = () => { - return ( - - - - - - - - - ); -}; - -export default MonitorActivitiesLayout; diff --git a/src/pages/monitor-activities-with-notes/calendar.tsx b/src/pages/monitor-activities-with-notes/calendar.tsx index b8d526a0..98a502b0 100644 --- a/src/pages/monitor-activities-with-notes/calendar.tsx +++ b/src/pages/monitor-activities-with-notes/calendar.tsx @@ -1,15 +1,21 @@ -import { Container, Link, Typography } from '@mui/material'; +import { Container, Typography } from '@mui/material'; import { DateCalendar, LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import React from 'react'; -import { Link as RouterLink } from 'react-router-dom'; +import { AppLink } from '../../components/AppLink'; +import { createFileRoute } from '@tanstack/react-router'; + +export const Route = createFileRoute('/monitor-activities-with-notes/calendar')( + { + component: ActivityCalendar, + } +); /** * Work in Progress: * * Page to see all activities by day in a calendar view in the monitor-activities Task Flow. */ -const ActivityCalendar: React.FC = () => { +function ActivityCalendar() { return ( { - - List - + List ); -}; - -export default ActivityCalendar; +} diff --git a/src/pages/monitor-activities-with-notes/detail.tsx b/src/pages/monitor-activities-with-notes/detail.tsx index 85426b2c..2a5f1594 100644 --- a/src/pages/monitor-activities-with-notes/detail.tsx +++ b/src/pages/monitor-activities-with-notes/detail.tsx @@ -1,31 +1,31 @@ -// import AddIcon from '@mui/icons-material/Add'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { Box, - // Button, Container, Grid, IconButton, - Link, Paper, Stack, - // TextField, Typography, } from '@mui/material'; import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; import dayjs from 'dayjs'; -import React from 'react'; import Plot from 'react-plotly.js'; -import { Link as RouterLink } from 'react-router-dom'; +import { createFileRoute } from '@tanstack/react-router'; +import { useDataFromSource } from '../../hooks/useDataFromSource'; +import { AppLink } from '../../components/AppLink'; // import { LabelValueTable } from '../../components/LabelValueTable'; -import { useDataFromSource } from '../../utils/useDataFromSource'; -import { taskflow } from './_config/taskflow.config'; import NotesDialog from '../../components/NotesDialog'; +export const Route = createFileRoute('/monitor-activities-with-notes/detail')({ + component: ActivityDetail, +}); + const dateComparator: GridComparatorFn = (v1, v2) => { return dayjs(v1).isAfter(dayjs(v2)) ? 1 : 0; }; +// CUSTOMIZE: events table columns const columns: GridColDef[] = [ { field: 'event_type', @@ -50,9 +50,10 @@ const columns: GridColDef[] = [ * Detail view of the selected activity from `` in monitor-activities Task Flow. * The two components are not currently hooked together. */ -const ActivityDetail: React.FC = () => { - const experiment = useDataFromSource(taskflow.data.detail.source); - const defaultNotes = useDataFromSource(taskflow.data.notes.source); +function ActivityDetail() { + // CUSTOMIZE: detail data source + const experiment = useDataFromSource('dummy-data/experiment_detail.json'); + const defaultNotes = useDataFromSource('dummy-data/default_notes.json'); // const getNoteRows = (notes: any[]) => { // return notes.map((note) => { @@ -75,12 +76,13 @@ const ActivityDetail: React.FC = () => { > - + - + + {/* CUSTOMIZE: title field */} {experiment?.experiment_name} @@ -91,6 +93,7 @@ const ActivityDetail: React.FC = () => { {experiment && ( row.id} columns={columns} initialState={{ @@ -150,6 +153,6 @@ const ActivityDetail: React.FC = () => { ); -}; +} export default ActivityDetail; diff --git a/src/pages/monitor-activities-with-notes/index.tsx b/src/pages/monitor-activities-with-notes/index.tsx index fccd8fa7..8dce4783 100644 --- a/src/pages/monitor-activities-with-notes/index.tsx +++ b/src/pages/monitor-activities-with-notes/index.tsx @@ -1,15 +1,17 @@ import { Container, Paper, Stack, Typography } from '@mui/material'; import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; import dayjs from 'dayjs'; -import React from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useDataFromSource } from '../../utils/useDataFromSource'; -import { taskflow } from './_config/taskflow.config'; +import { createFileRoute, useNavigate } from '@tanstack/react-router'; +import { useDataFromSource } from '../../hooks/useDataFromSource'; +export const Route = createFileRoute('/monitor-activities-with-notes/')({ + component: ActivityList, +}); const dateComparator: GridComparatorFn = (v1, v2) => { return dayjs(v1).isAfter(dayjs(v2)) ? 1 : 0; }; +// CUSTOMIZE: list view table columns const columns: GridColDef[] = [ { field: 'experiment_name', @@ -37,13 +39,12 @@ const columns: GridColDef[] = [ /** * List view of all activities in the monitor-activites Task Flow. */ -const ActivityList: React.FC = () => { - const experiments = useDataFromSource(taskflow.data.items.source); +function ActivityList() { + // CUSTOMIZE: list view data source + const experiments = useDataFromSource('dummy-data/experiments.json'); + const navigate = useNavigate(); - /** - * Content to render on the page for this component - */ return ( { row.id} columns={columns} + // CUSTOMIZE: initial sort field initialState={{ sorting: { sortModel: [{ field: 'start_time', sort: 'desc' }], }, }} - onRowClick={() => navigate('detail')} + onRowClick={() => + navigate({ to: '/monitor-activities-with-notes/detail' }) + } disableColumnSelector disableRowSelectionOnClick /> @@ -80,6 +85,4 @@ const ActivityList: React.FC = () => { ); -}; - -export default ActivityList; +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 62b8d1e0..4f2d965d 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -17,11 +17,14 @@ import { Route as IndexImport } from './pages/index'; import { Route as SearchDataRepositoriesIndexImport } from './pages/search-data-repositories/index'; import { Route as PlaygroundIndexImport } from './pages/playground/index'; import { Route as MonitorActivitiesIndexImport } from './pages/monitor-activities/index'; +import { Route as MonitorActivitiesWithNotesIndexImport } from './pages/monitor-activities-with-notes/index'; import { Route as ExploreDataIndexImport } from './pages/explore-data/index'; import { Route as SearchDataRepositoriesIdImport } from './pages/search-data-repositories/$id'; import { Route as RunComputationLayoutImport } from './pages/run-computation/_layout'; import { Route as MonitorActivitiesDetailImport } from './pages/monitor-activities/detail'; import { Route as MonitorActivitiesCalendarImport } from './pages/monitor-activities/calendar'; +import { Route as MonitorActivitiesWithNotesDetailImport } from './pages/monitor-activities-with-notes/detail'; +import { Route as MonitorActivitiesWithNotesCalendarImport } from './pages/monitor-activities-with-notes/calendar'; import { Route as ExploreDataIdImport } from './pages/explore-data/$id'; import { Route as ContributeDataLayoutImport } from './pages/contribute-data/_layout'; import { Route as CompareDataLayoutImport } from './pages/compare-data/_layout'; @@ -93,6 +96,13 @@ const MonitorActivitiesIndexRoute = MonitorActivitiesIndexImport.update({ getParentRoute: () => rootRoute, } as any); +const MonitorActivitiesWithNotesIndexRoute = + MonitorActivitiesWithNotesIndexImport.update({ + id: '/monitor-activities-with-notes/', + path: '/monitor-activities-with-notes/', + getParentRoute: () => rootRoute, + } as any); + const ExploreDataIndexRoute = ExploreDataIndexImport.update({ id: '/explore-data/', path: '/explore-data/', @@ -122,6 +132,20 @@ const MonitorActivitiesCalendarRoute = MonitorActivitiesCalendarImport.update({ getParentRoute: () => rootRoute, } as any); +const MonitorActivitiesWithNotesDetailRoute = + MonitorActivitiesWithNotesDetailImport.update({ + id: '/monitor-activities-with-notes/detail', + path: '/monitor-activities-with-notes/detail', + getParentRoute: () => rootRoute, + } as any); + +const MonitorActivitiesWithNotesCalendarRoute = + MonitorActivitiesWithNotesCalendarImport.update({ + id: '/monitor-activities-with-notes/calendar', + path: '/monitor-activities-with-notes/calendar', + getParentRoute: () => rootRoute, + } as any); + const ExploreDataIdRoute = ExploreDataIdImport.update({ id: '/explore-data/$id', path: '/explore-data/$id', @@ -276,6 +300,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExploreDataIdImport; parentRoute: typeof rootRoute; }; + '/monitor-activities-with-notes/calendar': { + id: '/monitor-activities-with-notes/calendar'; + path: '/monitor-activities-with-notes/calendar'; + fullPath: '/monitor-activities-with-notes/calendar'; + preLoaderRoute: typeof MonitorActivitiesWithNotesCalendarImport; + parentRoute: typeof rootRoute; + }; + '/monitor-activities-with-notes/detail': { + id: '/monitor-activities-with-notes/detail'; + path: '/monitor-activities-with-notes/detail'; + fullPath: '/monitor-activities-with-notes/detail'; + preLoaderRoute: typeof MonitorActivitiesWithNotesDetailImport; + parentRoute: typeof rootRoute; + }; '/monitor-activities/calendar': { id: '/monitor-activities/calendar'; path: '/monitor-activities/calendar'; @@ -318,6 +356,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ExploreDataIndexImport; parentRoute: typeof rootRoute; }; + '/monitor-activities-with-notes/': { + id: '/monitor-activities-with-notes/'; + path: '/monitor-activities-with-notes'; + fullPath: '/monitor-activities-with-notes'; + preLoaderRoute: typeof MonitorActivitiesWithNotesIndexImport; + parentRoute: typeof rootRoute; + }; '/monitor-activities/': { id: '/monitor-activities/'; path: '/monitor-activities'; @@ -567,11 +612,14 @@ export interface FileRoutesByFullPath { '/compare-data': typeof CompareDataLayoutRouteWithChildren; '/contribute-data': typeof ContributeDataLayoutRouteWithChildren; '/explore-data/$id': typeof ExploreDataIdRoute; + '/monitor-activities-with-notes/calendar': typeof MonitorActivitiesWithNotesCalendarRoute; + '/monitor-activities-with-notes/detail': typeof MonitorActivitiesWithNotesDetailRoute; '/monitor-activities/calendar': typeof MonitorActivitiesCalendarRoute; '/monitor-activities/detail': typeof MonitorActivitiesDetailRoute; '/run-computation': typeof RunComputationLayoutRouteWithChildren; '/search-data-repositories/$id': typeof SearchDataRepositoriesIdRoute; '/explore-data': typeof ExploreDataIndexRoute; + '/monitor-activities-with-notes': typeof MonitorActivitiesWithNotesIndexRoute; '/monitor-activities': typeof MonitorActivitiesIndexRoute; '/playground': typeof PlaygroundIndexRoute; '/search-data-repositories': typeof SearchDataRepositoriesIndexRoute; @@ -595,11 +643,14 @@ export interface FileRoutesByTo { '/compare-data': typeof CompareDataLayoutIndexRoute; '/contribute-data': typeof ContributeDataLayoutIndexRoute; '/explore-data/$id': typeof ExploreDataIdRoute; + '/monitor-activities-with-notes/calendar': typeof MonitorActivitiesWithNotesCalendarRoute; + '/monitor-activities-with-notes/detail': typeof MonitorActivitiesWithNotesDetailRoute; '/monitor-activities/calendar': typeof MonitorActivitiesCalendarRoute; '/monitor-activities/detail': typeof MonitorActivitiesDetailRoute; '/run-computation': typeof RunComputationLayoutIndexRoute; '/search-data-repositories/$id': typeof SearchDataRepositoriesIdRoute; '/explore-data': typeof ExploreDataIndexRoute; + '/monitor-activities-with-notes': typeof MonitorActivitiesWithNotesIndexRoute; '/monitor-activities': typeof MonitorActivitiesIndexRoute; '/playground': typeof PlaygroundIndexRoute; '/search-data-repositories': typeof SearchDataRepositoriesIndexRoute; @@ -623,12 +674,15 @@ export interface FileRoutesById { '/contribute-data': typeof ContributeDataRouteWithChildren; '/contribute-data/_layout': typeof ContributeDataLayoutRouteWithChildren; '/explore-data/$id': typeof ExploreDataIdRoute; + '/monitor-activities-with-notes/calendar': typeof MonitorActivitiesWithNotesCalendarRoute; + '/monitor-activities-with-notes/detail': typeof MonitorActivitiesWithNotesDetailRoute; '/monitor-activities/calendar': typeof MonitorActivitiesCalendarRoute; '/monitor-activities/detail': typeof MonitorActivitiesDetailRoute; '/run-computation': typeof RunComputationRouteWithChildren; '/run-computation/_layout': typeof RunComputationLayoutRouteWithChildren; '/search-data-repositories/$id': typeof SearchDataRepositoriesIdRoute; '/explore-data/': typeof ExploreDataIndexRoute; + '/monitor-activities-with-notes/': typeof MonitorActivitiesWithNotesIndexRoute; '/monitor-activities/': typeof MonitorActivitiesIndexRoute; '/playground/': typeof PlaygroundIndexRoute; '/search-data-repositories/': typeof SearchDataRepositoriesIndexRoute; @@ -655,11 +709,14 @@ export interface FileRouteTypes { | '/compare-data' | '/contribute-data' | '/explore-data/$id' + | '/monitor-activities-with-notes/calendar' + | '/monitor-activities-with-notes/detail' | '/monitor-activities/calendar' | '/monitor-activities/detail' | '/run-computation' | '/search-data-repositories/$id' | '/explore-data' + | '/monitor-activities-with-notes' | '/monitor-activities' | '/playground' | '/search-data-repositories' @@ -682,11 +739,14 @@ export interface FileRouteTypes { | '/compare-data' | '/contribute-data' | '/explore-data/$id' + | '/monitor-activities-with-notes/calendar' + | '/monitor-activities-with-notes/detail' | '/monitor-activities/calendar' | '/monitor-activities/detail' | '/run-computation' | '/search-data-repositories/$id' | '/explore-data' + | '/monitor-activities-with-notes' | '/monitor-activities' | '/playground' | '/search-data-repositories' @@ -708,12 +768,15 @@ export interface FileRouteTypes { | '/contribute-data' | '/contribute-data/_layout' | '/explore-data/$id' + | '/monitor-activities-with-notes/calendar' + | '/monitor-activities-with-notes/detail' | '/monitor-activities/calendar' | '/monitor-activities/detail' | '/run-computation' | '/run-computation/_layout' | '/search-data-repositories/$id' | '/explore-data/' + | '/monitor-activities-with-notes/' | '/monitor-activities/' | '/playground/' | '/search-data-repositories/' @@ -739,11 +802,14 @@ export interface RootRouteChildren { CompareDataRoute: typeof CompareDataRouteWithChildren; ContributeDataRoute: typeof ContributeDataRouteWithChildren; ExploreDataIdRoute: typeof ExploreDataIdRoute; + MonitorActivitiesWithNotesCalendarRoute: typeof MonitorActivitiesWithNotesCalendarRoute; + MonitorActivitiesWithNotesDetailRoute: typeof MonitorActivitiesWithNotesDetailRoute; MonitorActivitiesCalendarRoute: typeof MonitorActivitiesCalendarRoute; MonitorActivitiesDetailRoute: typeof MonitorActivitiesDetailRoute; RunComputationRoute: typeof RunComputationRouteWithChildren; SearchDataRepositoriesIdRoute: typeof SearchDataRepositoriesIdRoute; ExploreDataIndexRoute: typeof ExploreDataIndexRoute; + MonitorActivitiesWithNotesIndexRoute: typeof MonitorActivitiesWithNotesIndexRoute; MonitorActivitiesIndexRoute: typeof MonitorActivitiesIndexRoute; PlaygroundIndexRoute: typeof PlaygroundIndexRoute; SearchDataRepositoriesIndexRoute: typeof SearchDataRepositoriesIndexRoute; @@ -754,11 +820,15 @@ const rootRouteChildren: RootRouteChildren = { CompareDataRoute: CompareDataRouteWithChildren, ContributeDataRoute: ContributeDataRouteWithChildren, ExploreDataIdRoute: ExploreDataIdRoute, + MonitorActivitiesWithNotesCalendarRoute: + MonitorActivitiesWithNotesCalendarRoute, + MonitorActivitiesWithNotesDetailRoute: MonitorActivitiesWithNotesDetailRoute, MonitorActivitiesCalendarRoute: MonitorActivitiesCalendarRoute, MonitorActivitiesDetailRoute: MonitorActivitiesDetailRoute, RunComputationRoute: RunComputationRouteWithChildren, SearchDataRepositoriesIdRoute: SearchDataRepositoriesIdRoute, ExploreDataIndexRoute: ExploreDataIndexRoute, + MonitorActivitiesWithNotesIndexRoute: MonitorActivitiesWithNotesIndexRoute, MonitorActivitiesIndexRoute: MonitorActivitiesIndexRoute, PlaygroundIndexRoute: PlaygroundIndexRoute, SearchDataRepositoriesIndexRoute: SearchDataRepositoriesIndexRoute, @@ -778,11 +848,14 @@ export const routeTree = rootRoute "/compare-data", "/contribute-data", "/explore-data/$id", + "/monitor-activities-with-notes/calendar", + "/monitor-activities-with-notes/detail", "/monitor-activities/calendar", "/monitor-activities/detail", "/run-computation", "/search-data-repositories/$id", "/explore-data/", + "/monitor-activities-with-notes/", "/monitor-activities/", "/playground/", "/search-data-repositories/" @@ -825,6 +898,12 @@ export const routeTree = rootRoute "/explore-data/$id": { "filePath": "explore-data/$id.tsx" }, + "/monitor-activities-with-notes/calendar": { + "filePath": "monitor-activities-with-notes/calendar.tsx" + }, + "/monitor-activities-with-notes/detail": { + "filePath": "monitor-activities-with-notes/detail.tsx" + }, "/monitor-activities/calendar": { "filePath": "monitor-activities/calendar.tsx" }, @@ -851,6 +930,9 @@ export const routeTree = rootRoute "/explore-data/": { "filePath": "explore-data/index.tsx" }, + "/monitor-activities-with-notes/": { + "filePath": "monitor-activities-with-notes/index.tsx" + }, "/monitor-activities/": { "filePath": "monitor-activities/index.tsx" }, From 190a3ce584de2584fda5a690d01659a2f7019f23 Mon Sep 17 00:00:00 2001 From: MichaelPesce Date: Thu, 8 May 2025 07:29:12 -0400 Subject: [PATCH 4/4] NotesDialog -> Notes --- src/components/{NotesDialog.tsx => Notes.tsx} | 8 ++++---- src/pages/monitor-activities-with-notes/detail.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/components/{NotesDialog.tsx => Notes.tsx} (99%) diff --git a/src/components/NotesDialog.tsx b/src/components/Notes.tsx similarity index 99% rename from src/components/NotesDialog.tsx rename to src/components/Notes.tsx index 8501c236..f4be4379 100644 --- a/src/components/NotesDialog.tsx +++ b/src/components/Notes.tsx @@ -310,7 +310,7 @@ const IndividualNote = ({ ); }; -interface NotesDialogProps { +interface NotesProps { record_id?: string; open: boolean; userEmail: string; @@ -342,13 +342,13 @@ interface Note { repliesTo?: number; // the index that this comment replies to, if this is a reply } -const NotesDialog = ({ +const Notes = ({ record_id, open, userEmail, initialNotes, onUpdateNote, -}: NotesDialogProps) => { +}: NotesProps) => { const [notes, setNotes] = useState([] as Note[]); const [replyToIdx, setReplyToIdx] = useState(); const [editIdx, setEditIdx] = useState(); @@ -672,4 +672,4 @@ const NotesDialog = ({ ); }; -export default NotesDialog; +export default Notes; diff --git a/src/pages/monitor-activities-with-notes/detail.tsx b/src/pages/monitor-activities-with-notes/detail.tsx index 2a5f1594..78a8eb68 100644 --- a/src/pages/monitor-activities-with-notes/detail.tsx +++ b/src/pages/monitor-activities-with-notes/detail.tsx @@ -15,7 +15,7 @@ import { createFileRoute } from '@tanstack/react-router'; import { useDataFromSource } from '../../hooks/useDataFromSource'; import { AppLink } from '../../components/AppLink'; // import { LabelValueTable } from '../../components/LabelValueTable'; -import NotesDialog from '../../components/NotesDialog'; +import Notes from '../../components/Notes'; export const Route = createFileRoute('/monitor-activities-with-notes/detail')({ component: ActivityDetail, @@ -115,7 +115,7 @@ function ActivityDetail() { }} > -