diff --git a/public/dummy-data/default_notes.json b/public/dummy-data/default_notes.json new file mode 100644 index 00000000..ff8168d5 --- /dev/null +++ b/public/dummy-data/default_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/Notes.tsx b/src/components/Notes.tsx new file mode 100644 index 00000000..f4be4379 --- /dev/null +++ b/src/components/Notes.tsx @@ -0,0 +1,675 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { + IconButton, + Tooltip, + TextField, + Button, + Stack, + Box, + Typography, + Divider, +} from '@mui/material'; +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 NotesProps { + 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 Notes = ({ + record_id, + open, + userEmail, + initialNotes, + onUpdateNote, +}: NotesProps) => { + 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 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: { + 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', + maxHeight: '25vh', + }, + 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 ( + + + Notes + + + {/* 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 Notes; 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/-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..98a502b0 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/calendar.tsx @@ -0,0 +1,37 @@ +import { Container, Typography } from '@mui/material'; +import { DateCalendar, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +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. + */ +function ActivityCalendar() { + return ( + + + 2023 Experiments (Work in Progress) + + {/* TODO: calendar visualization */} + + + + List + + ); +} 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..78a8eb68 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/detail.tsx @@ -0,0 +1,158 @@ +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import { + Box, + Container, + Grid, + IconButton, + Paper, + Stack, + Typography, +} from '@mui/material'; +import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; +import dayjs from 'dayjs'; +import Plot from 'react-plotly.js'; +import { createFileRoute } from '@tanstack/react-router'; +import { useDataFromSource } from '../../hooks/useDataFromSource'; +import { AppLink } from '../../components/AppLink'; +// import { LabelValueTable } from '../../components/LabelValueTable'; +import Notes from '../../components/Notes'; + +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', + 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. + */ +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) => { + // note.label = note.created_time; + // note.value = note.content; + // return note; + // }); + // }; + + /** + * Content to render on the page for this component + */ + return ( + + + + + + + + + + {/* CUSTOMIZE: title field */} + {experiment?.experiment_name} + + + + + + + {experiment && ( + row.id} + columns={columns} + initialState={{ + sorting: { + sortModel: [{ field: 'event_time', sort: 'desc' }], + }, + }} + disableColumnSelector + disableRowSelectionOnClick + /> + )} + + + + + + + + + + + + + + + + + + + ); +} + +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..8dce4783 --- /dev/null +++ b/src/pages/monitor-activities-with-notes/index.tsx @@ -0,0 +1,88 @@ +import { Container, Paper, Stack, Typography } from '@mui/material'; +import { DataGrid, GridColDef, GridComparatorFn } from '@mui/x-data-grid'; +import dayjs from 'dayjs'; +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', + 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. + */ +function ActivityList() { + // CUSTOMIZE: list view data source + const experiments = useDataFromSource('dummy-data/experiments.json'); + + const navigate = useNavigate(); + + return ( + + + + + Experiments test + + + + row.id} + columns={columns} + // CUSTOMIZE: initial sort field + initialState={{ + sorting: { + sortModel: [{ field: 'start_time', sort: 'desc' }], + }, + }} + onRowClick={() => + navigate({ to: '/monitor-activities-with-notes/detail' }) + } + disableColumnSelector + disableRowSelectionOnClick + /> + + + + ); +} 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" },