Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 186 additions & 24 deletions packages/alea-frontend/pages/my-notes.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import { Box, CircularProgress, Typography } from '@mui/material';
import {
Box,
CircularProgress,
Typography,
TextField,
Select,
FormControl,
InputLabel,
List,
ListItemButton,
ListItemText,
ListItemIcon,
MenuItem,
useTheme,
} from '@mui/material';
import type { Theme } from '@mui/material/styles';
import FolderIcon from '@mui/icons-material/Folder';
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
import { getMyNotesSections } from '@alea/spec';
import { NotesView } from '@alea/comments';
import type { NextPage } from 'next';
import { useEffect, useState } from 'react';
import MainLayout from '../layouts/MainLayout';
import { SafeFTMLFragment } from '@alea/stex-react-renderer';

export interface NotesSection {
uri: string;
Expand All @@ -20,9 +36,12 @@ interface GroupedNotes {
}

const MyNotesPage: NextPage = () => {
const theme = useTheme();
const [sections, setSections] = useState<NotesSection[]>([]);
const [groupedNotes, setGroupedNotes] = useState<GroupedNotes>({});
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [selectedCourse, setSelectedCourse] = useState('all');

useEffect(() => {
getMyNotesSections()
Expand Down Expand Up @@ -52,33 +71,176 @@ const MyNotesPage: NextPage = () => {

return (
<MainLayout title="My Notes | ALeA">
<Box p="10px" m="0 auto" maxWidth="800px">
{Object.entries(groupedNotes).map(([courseId, instances]) => (
<Box key={courseId} mb={4}>
{Object.entries(instances).map(([instanceId, sections]) => (
<Box key={instanceId} mb={3}>
<Typography variant="h5" sx={{ mb: 2, color: 'primary.main' }}>
{courseId.toUpperCase()} ({instanceId})
</Typography>
{sections.map((section) => (
<Box
key={`${section.uri}-${instanceId}`}
border="1px solid #CCC"
p="10px"
m="10px"
>
{/* TODO ALeA4-N8: FTMLFragment won't render slides using URI - it uses HTML. This case will be handled later */}
<SafeFTMLFragment fragment={{ type: 'FromBackend', uri: section.uri }} />
<NotesView uri={section.uri} allNotesMode={true} />
</Box>
))}
</Box>
<Box display="flex" minHeight="calc(100vh - 64px)" bgcolor="background.default">
<Box bgcolor="background.default" p={3} sx={myNotesPageStyles.sidebar}>
<Typography variant="h6" mb={3} fontWeight={700} color="text.primary">
Courses
</Typography>
<List sx={{ px: 0 }}>
<ListItemButton
selected={selectedCourse === 'all'}
onClick={() => setSelectedCourse('all')}
sx={myNotesPageStyles.listItemButton}
>
<ListItemIcon
sx={[
myNotesPageStyles.listItemIcon,
selectedCourse === 'all' && { color: 'inherit' },
]}
>
<LibraryBooksIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography fontWeight={selectedCourse === 'all' ? 700 : 500}>
All Courses
</Typography>
}
/>
</ListItemButton>
{Object.keys(groupedNotes).map((courseId) => (
<ListItemButton
key={courseId}
selected={selectedCourse === courseId}
onClick={() => setSelectedCourse(courseId)}
sx={myNotesPageStyles.listItemButton}
>
<ListItemIcon
sx={[
myNotesPageStyles.listItemIcon,
selectedCourse === courseId && { color: 'inherit' },
]}
>
<FolderIcon />
</ListItemIcon>
<ListItemText
primary={
<Typography fontWeight={selectedCourse === courseId ? 700 : 500}>
{courseId.toUpperCase()}
</Typography>
}
/>
</ListItemButton>
))}
</List>
</Box>
<Box flex={1} p={{ xs: 2, md: 5 }} bgcolor="background.paper">
<Box maxWidth="1000px" mx="auto">
<Typography
variant="h3"
fontWeight={800}
color="text.primary"
mb={4}
letterSpacing="-0.02em"
>
{selectedCourse === 'all' ? 'All Notes' : `${selectedCourse.toUpperCase()} Notes`}
</Typography>
<Box mb={5} display="flex" flexDirection="column" gap={2}>
<TextField
fullWidth
placeholder="Search my notes..."
variant="outlined"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
sx={myNotesPageStyles.searchField(theme)}
/>
<FormControl sx={{ display: { xs: 'block', md: 'none' } }} fullWidth>
<InputLabel>Course</InputLabel>
<Select
value={selectedCourse}
label="Course"
onChange={(e) => setSelectedCourse(e.target.value)}
sx={myNotesPageStyles.mobileSelect}
>
<MenuItem value="all">All Courses</MenuItem>
{Object.keys(groupedNotes).map((courseId) => (
<MenuItem key={courseId} value={courseId}>
{courseId.toUpperCase()}
</MenuItem>
))}
</Select>
</FormControl>
</Box>

{Object.entries(groupedNotes)
.filter(([courseId]) => selectedCourse === 'all' || courseId === selectedCourse)
.map(([courseId, instances]) => (
<Box key={courseId} mb={6}>
{Object.entries(instances).map(([instanceId, sections]) => (
<Box
key={instanceId}
mb={4}
sx={{
display: 'block',
'@supports selector(:has(a))': {
'&:not(:has(.alea-note-card))': {
display: 'none',
},
},
}}
>
<Typography
variant="h5"
sx={{
mb: 3,
color: 'text.primary',
fontWeight: 700,
letterSpacing: '-0.01em',
}}
>
{courseId.toUpperCase()} ({instanceId})
</Typography>
{sections.map((section) => (
<NotesView
key={`${section.uri}-${instanceId}`}
uri={section.uri}
allNotesMode={true}
searchQuery={searchQuery}
courseId={courseId}
courseTerm={instanceId}
/>
))}
</Box>
))}
</Box>
))}
</Box>
))}
</Box>
</Box>
</MainLayout>
);
};

const myNotesPageStyles = {
sidebar: {
width: 240,
borderRight: '1px solid',
borderColor: 'divider',
display: { xs: 'none', md: 'block' },
},
listItemButton: {
borderRadius: 2,
mb: 1,
'&.Mui-selected': {
bgcolor: 'action.selected',
color: 'primary.main',
'&:hover': { bgcolor: 'action.hover' },
},
},
listItemIcon: {
minWidth: 40,
color: 'text.secondary',
},
searchField: (theme: Theme) => ({
'& .MuiOutlinedInput-root': {
borderRadius: 4,
bgcolor: 'background.paper',
boxShadow: theme.shadows[1],
},
}),
mobileSelect: {
borderRadius: 3,
},
};

export default MyNotesPage;
26 changes: 20 additions & 6 deletions packages/comments/src/lib/CommentView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Comment } from '@alea/spec';
import { useState } from 'react';
import { Box } from '@mui/material';
import { CommentLabel } from './CommentLabel';
import { CommentReply } from './CommentReply';
import { EditView } from './EditView';
Expand All @@ -14,22 +15,22 @@ export function CommentView({ comment, onUpdate }: { comment: Comment; onUpdate:
const [editingComment, setEditingComment] = useState(false);

return (
<>
<Box sx={commentViewStyles.card}>
<CommentLabel
comment={comment}
setEditingComment={setEditingComment}
setOpenReply={setCommentReplyOpen}
onUpdate={onUpdate}
/>
<div style={{ display: 'flex' }}>
<Box sx={{ display: 'flex' }}>
<div className={styles['stretchy_div']}>
{!comment.isDeleted && (
<div>
<SelectedInfo text={comment.selectedText} />
{!editingComment && (
<div style={{ margin: '-1em 0 0' }}>
<Box sx={{ mt: 1 }}>
<MdViewer content={comment.statement || ''} />
</div>
</Box>
)}
<EditView
uri={comment.uri}
Expand Down Expand Up @@ -61,7 +62,20 @@ export function CommentView({ comment, onUpdate }: { comment: Comment; onUpdate:
</div>
)}
</div>
</div>
</>
</Box>
</Box>
);
}

const commentViewStyles = {
card: {
mb: 1,
p: 0.5,
bgcolor: 'background.default',
borderRadius: 3,
border: '1px solid',
borderColor: 'divider',
transition: 'background-color 0.2s',
'&:hover': { bgcolor: 'action.hover' },
},
};
Loading
Loading