Skip to content

Commit

Permalink
Huge note revamp; added markdown and note insertion into board
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinmonisit committed Jan 16, 2025
1 parent 9609a74 commit fba7309
Show file tree
Hide file tree
Showing 13 changed files with 917 additions and 114 deletions.
111 changes: 111 additions & 0 deletions app/components/NotesEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useEffect, useRef, useState } from 'react';
import Markdown from 'react-markdown';
import { useNotesStore } from '@/lib/hooks/stores/useNotesStore';

interface NotesEditorProps {
id: string | number;
showDisplayOption?: boolean;
className?: string;
title?: string;
}

export default function NotesEditor({
id,
showDisplayOption = false,
title,
}: NotesEditorProps) {
const { notes, setNote } = useNotesStore();
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState(notes[id]?.note || '');
const [displayNextToSemester, setDisplayNextToSemester] = useState(
notes[id]?.displayNextToSemester || false
);
const textAreaRef = useRef<HTMLTextAreaElement>(null);

useEffect(() => {
if (textAreaRef.current) {
textAreaRef.current.style.height = 'auto';
textAreaRef.current.style.height =
textAreaRef.current.scrollHeight + 'px';
}
}, [editValue, isEditing]);

const handleSave = () => {
setNote(id, {
note: editValue,
displayNextToSemester,
});
setIsEditing(false);
};

return (
<div>
<div className='mb-2 flex items-center justify-between'>
{title && <h2 className='text-lg font-bold'>{title}</h2>}
{!title && <h3 className='text-sm font-medium'>Notes:</h3>}
<button
onClick={isEditing ? handleSave : () => setIsEditing(true)}
className='text-sm text-blue-600 hover:text-blue-800'
>
{isEditing ? 'Save' : 'Edit'}
</button>
</div>

{showDisplayOption && (
<div className='mb-2 flex items-center'>
<input
type='checkbox'
checked={displayNextToSemester}
onChange={(e) => {
setDisplayNextToSemester(e.target.checked);
setNote(id, {
note: editValue,
displayNextToSemester: e.target.checked,
});
}}
className='mr-2'
/>
<label className='text-sm text-gray-600'>
Display next to semester
</label>
</div>
)}

{isEditing ? (
<div className='space-y-2'>
<textarea
ref={textAreaRef}
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
className='h-32 w-full rounded-md border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500'
placeholder='Add your notes here...'
/>
<div className='text-xs text-gray-500'>
Markdown supported: **bold**, *italic*, # heading, - list,
[link](url)
<br />
<a
href='https://www.markdownguide.org/basic-syntax/'
target='_blank'
rel='noopener noreferrer'
className='text-blue-600 hover:text-blue-800'
>
Learn more about Markdown
</a>
</div>
</div>
) : (
<article
className='prose prose-sm cursor-pointer overflow-scroll rounded-md p-2 hover:bg-gray-100'
onClick={() => setIsEditing(true)}
>
{notes[id]?.note ? (
<Markdown>{notes[id].note}</Markdown>
) : (
<span className='text-gray-500'>No notes added yet</span>
)}
</article>
)}
</div>
);
}
94 changes: 51 additions & 43 deletions app/features/middlePanel/dashboard/ScheduleBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
calculateRunningCredits,
} from './utils/credits';
import { getColor, dropAnimation } from './utils/dnd';
import NotesBox from './components/NotesBox';

function UndoRedoControls() {
const { undo, redo, past, future } = useHistoryStore();
Expand Down Expand Up @@ -160,51 +161,58 @@ export function ScheduleBoard({
<UndoRedoControls />
<div className='grid w-full grid-cols-[repeat(auto-fit,minmax(330px,1fr))] gap-x-8 gap-y-4 px-4'>
{semesterOrder.map((containerId) => (
<DroppableContainer
key={containerId}
id={containerId}
label={
minimal
? undefined
: `${
useScheduleStore.getState().semesterByID[containerId]
?.title || containerId
}
(${calculateSemesterCredits(coursesBySemesterID[containerId] || [], courses)} credits,
Total: ${calculateRunningCredits(semesterOrder, coursesBySemesterID, courses, containerId)})`
}
columns={columns}
items={coursesBySemesterID[containerId]}
scrollable={scrollable}
style={containerStyle}
unstyled={minimal}
onRemove={() => handleEditSemester(containerId)}
>
<SortableContext
<React.Fragment key={containerId}>
<DroppableContainer
key={containerId}
id={containerId}
label={
minimal
? undefined
: `${
useScheduleStore.getState().semesterByID[
containerId
]?.title || containerId
}
(${calculateSemesterCredits(coursesBySemesterID[containerId] || [], courses)} credits,
Total: ${calculateRunningCredits(semesterOrder, coursesBySemesterID, courses, containerId)})`
}
columns={columns}
items={coursesBySemesterID[containerId]}
strategy={strategy}
scrollable={scrollable}
style={containerStyle}
unstyled={minimal}
onRemove={() => handleEditSemester(containerId)}
>
{coursesBySemesterID[containerId].map((value, index) => {
return (
<SortableItem
disabled={isSortingContainer}
key={value}
id={value}
index={index}
handle={handle}
style={getItemStyles}
wrapperStyle={wrapperStyle}
renderItem={renderItem}
containerId={containerId}
showCores={false}
getIndex={(id) => {
return 0;
}}
/>
);
})}
</SortableContext>
</DroppableContainer>
<SortableContext
items={coursesBySemesterID[containerId]}
strategy={strategy}
>
{coursesBySemesterID[containerId].map((value, index) => {
return (
<SortableItem
disabled={isSortingContainer}
key={value}
id={value}
index={index}
handle={handle}
style={getItemStyles}
wrapperStyle={wrapperStyle}
renderItem={renderItem}
containerId={containerId}
showCores={false}
getIndex={(id) => {
return 0;
}}
/>
);
})}
</SortableContext>
</DroppableContainer>
<NotesBox
semesterID={containerId}
key={containerId + '-notes'}
/>
</React.Fragment>
))}
{minimal ? undefined : (
<>
Expand Down
17 changes: 17 additions & 0 deletions app/features/middlePanel/dashboard/components/NotesBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useNotesStore } from '@/lib/hooks/stores/useNotesStore';
import { useScheduleStore } from '@/lib/hooks/stores/useScheduleStore';
import { SemesterID } from '@/types/models';
import NotesEditor from '@/app/components/NotesEditor';

function NotesBox({ semesterID }: { semesterID: SemesterID }) {
const notes = useNotesStore((state) => state.notes);
const semester = useScheduleStore((state) => state.semesterByID[semesterID]);
const { title } = semester;

if (!notes[semesterID]) return null;
if (!notes[semesterID].displayNextToSemester) return null;

return <NotesEditor id={semesterID} title={title} />;
}

export default NotesBox;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { forwardRef } from 'react';
import classNames from 'classnames';

import styles from './Container.module.scss';
import { Handle } from '../Item/components/Handle/Handle';

type BaseProps = {
children: React.ReactNode;
Expand Down Expand Up @@ -78,6 +79,9 @@ export const Container = forwardRef(
{label ? (
<div className={styles.Header}>
{label}

{handleProps ? <Handle {...handleProps} /> : null}

{onRemove ? (
<button
onClick={onRemove}
Expand Down
4 changes: 2 additions & 2 deletions app/features/rightPanel/courseInfoDisplay/SemesterInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useScheduleStore } from '@/lib/hooks/stores/useScheduleStore';
import { useState, useRef } from 'react';
import NotesArea from './components/NotesArea';
import NotesEditor from '@/app/components/NotesEditor';
import useAuxiliaryStore from '@/lib/hooks/stores/useAuxiliaryStore';
import {
calculateSemesterCredits,
Expand Down Expand Up @@ -221,7 +221,7 @@ export default function SemesterInfo({ id }: SemesterInfoProps) {
Remove Semester
</button>
</div>
<NotesArea id={id} />
<NotesEditor id={id} showDisplayOption={true} />
</div>
);
}
54 changes: 0 additions & 54 deletions app/features/rightPanel/courseInfoDisplay/components/NotesArea.tsx

This file was deleted.

6 changes: 5 additions & 1 deletion lib/defaultSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
},
"visuals": {
"showGrades": false,
"showGPAsInSemesterTitles": false
"showGPAsInSemesterTitles": false,
"goalCreditsForGraduation": 120,
"progressivelyDarkenSemestersBasedOnCreditGoal": true,
"showCreditCountOnCourseTitles": false,
"showQuarterlyStudentTitlesOnSemesterTitles": true
}
}
24 changes: 17 additions & 7 deletions lib/hooks/stores/useNotesStore.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
'use client';

import { SemesterID, CourseID } from '@/types/models';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

type validID = SemesterID | CourseID;

export interface Note {
note: string;
displayNextToSemester: boolean;
}

interface NotesState {
notes: Record<string, string>;
setNote: (id: string, content: string) => void;
deleteNote: (id: string) => void;
notes: Record<validID, Note>;
setNote: (id: validID, content: Note) => void;
deleteNote: (id: validID) => void;
}

export const useNotesStore = create<NotesState>()(
persist(
(set) => ({
notes: {},
setNote: (id: string, content: string) =>
setNote: (id: validID, note: Note) => {
set((state) => ({
notes: {
...state.notes,
[id]: content,
[id]: note,
},
})),
deleteNote: (id: string) =>
}));
return true;
},
deleteNote: (id: validID) =>
set((state) => {
const { [id]: _, ...rest } = state.notes;
return { notes: rest };
Expand Down
1 change: 1 addition & 0 deletions lib/hooks/stores/useSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const useSettingsStore = create<SettingsStore>()(
}),
{
name: 'settings-storage',
version: 1,
}
)
);
Loading

0 comments on commit fba7309

Please sign in to comment.