This commit is contained in:
nmemmert
2026-06-02 14:13:37 -04:00
parent 5b448aef0f
commit 3e050c8909
+136 -21
View File
@@ -1227,6 +1227,8 @@ export function ColossiansStudySectionPage({ content }: Props) {
const [noteLoading, setNoteLoading] = useState(false) const [noteLoading, setNoteLoading] = useState(false)
const [noteSaving, setNoteSaving] = useState(false) const [noteSaving, setNoteSaving] = useState(false)
const [noteMessage, setNoteMessage] = useState('') const [noteMessage, setNoteMessage] = useState('')
const [notesModalOpen, setNotesModalOpen] = useState(false)
const [contextNoteMenu, setContextNoteMenu] = useState<{ x: number; y: number; text: string } | null>(null)
const [completedSectionIds, setCompletedSectionIds] = useState<string[]>([]) const [completedSectionIds, setCompletedSectionIds] = useState<string[]>([])
const [progressLoading, setProgressLoading] = useState(false) const [progressLoading, setProgressLoading] = useState(false)
const [progressSaving, setProgressSaving] = useState(false) const [progressSaving, setProgressSaving] = useState(false)
@@ -1392,6 +1394,35 @@ export function ColossiansStudySectionPage({ content }: Props) {
} }
} }
function closeNotesModal() {
setNotesModalOpen(false)
}
function appendSelectedTextToNotes(selectedText: string) {
if (!selectedText.trim()) return
setNoteText(prev => prev ? `${prev.trim()}\n\n${selectedText.trim()}` : selectedText.trim())
setNotesModalOpen(true)
}
function handleLessonContentContextMenu(e: React.MouseEvent<HTMLDivElement>) {
if (!auth.checked || !auth.authenticated || !isEnrolled || !study || !section) return
const selection = window.getSelection()
const selectedText = selection?.toString().trim() ?? ''
if (!selectedText) {
setContextNoteMenu(null)
return
}
e.preventDefault()
setContextNoteMenu({ x: e.clientX + 4, y: e.clientY + 4, text: selectedText })
}
useEffect(() => {
if (!contextNoteMenu) return
const handleClick = () => setContextNoteMenu(null)
window.addEventListener('mousedown', handleClick)
return () => window.removeEventListener('mousedown', handleClick)
}, [contextNoteMenu])
async function markLessonComplete() { async function markLessonComplete() {
if (!study || !section?.id || progressSaving) return if (!study || !section?.id || progressSaving) return
setProgressSaving(true) setProgressSaving(true)
@@ -1548,7 +1579,7 @@ export function ColossiansStudySectionPage({ content }: Props) {
return ( return (
<main className="study-section-page" aria-label={section.title}> <main className="study-section-page" aria-label={section.title}>
<section className="section-study-classroom"> <section className="section-study-classroom" onContextMenu={handleLessonContentContextMenu}>
<div className="section-inner study-classroom-shell"> <div className="section-inner study-classroom-shell">
<div className="study-classroom-main"> <div className="study-classroom-main">
<Link to={`/study/${study.slug}`} className="study-detail-back">Back to {study.title}</Link> <Link to={`/study/${study.slug}`} className="study-detail-back">Back to {study.title}</Link>
@@ -1685,28 +1716,11 @@ export function ColossiansStudySectionPage({ content }: Props) {
<div className="study-notes-box"> <div className="study-notes-box">
<p className="study-notes-user">Signed in as {auth.username}</p> <p className="study-notes-user">Signed in as {auth.username}</p>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '0.5rem' }}> <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap', marginBottom: '0.5rem' }}>
<Link to={`/study/${study.slug}/notes`} className="btn-secondary">Open My Notes</Link> <button type="button" className="btn-primary" onClick={() => setNotesModalOpen(true)}>Open Notes</button>
<Link to="/study/account" className="btn-secondary">My Account</Link> <Link to="/study/account" className="btn-secondary">My Account</Link>
</div> </div>
<textarea <p style={{ margin: '0.5rem 0 0', color: '#b9b09b', fontSize: '0.9rem' }}>Highlight scripture, commentary, or Greek word text, right click, and add it to your notes.</p>
rows={8} {noteLoading ? <p style={{ marginTop: '0.75rem' }}>Loading notes</p> : null}
value={noteText}
onChange={e => setNoteText(e.target.value)}
placeholder="Write your lesson notes here..."
disabled={noteLoading || noteSaving}
/>
<div className="study-auth-actions" style={{ flexWrap: 'wrap', gap: '0.5rem' }}>
<button type="button" className="btn-primary" disabled={!canSaveNote} onClick={saveNote}>{noteSaving ? 'Saving...' : 'Save Notes'}</button>
<button type="button" className="btn-secondary" disabled={authBusy} onClick={logoutStudyUser}>Sign Out</button>
<button
type="button"
className={lessonCompleted ? 'btn-secondary' : 'btn-primary'}
disabled={!isEnrolled || progressSaving || progressLoading}
onClick={lessonCompleted ? unmarkLessonCompletion : markLessonComplete}
>
{progressSaving ? 'Updating...' : lessonCompleted ? 'Mark Incomplete' : 'Mark Complete'}
</button>
</div>
</div> </div>
)} )}
{(authMessage || noteMessage || progressMessage) && <p className="study-note-status">{authMessage || noteMessage || progressMessage}</p>} {(authMessage || noteMessage || progressMessage) && <p className="study-note-status">{authMessage || noteMessage || progressMessage}</p>}
@@ -1726,6 +1740,107 @@ export function ColossiansStudySectionPage({ content }: Props) {
</div> </div>
</article> </article>
</aside> </aside>
{contextNoteMenu && (
<div
style={{
position: 'fixed',
left: contextNoteMenu.x,
top: contextNoteMenu.y,
zIndex: 50,
backgroundColor: '#1f1d19',
border: '1px solid #3f3b2f',
borderRadius: '12px',
padding: '0.75rem',
boxShadow: '0 10px 30px rgba(0,0,0,0.25)',
minWidth: '220px',
}}
onMouseDown={e => e.stopPropagation()}
>
<p style={{ margin: '0 0 0.75rem', color: '#ece3c6', fontSize: '0.95rem' }}>Add selected text to notes:</p>
<div style={{ marginBottom: '0.75rem', maxHeight: '100px', overflow: 'auto', color: '#f0ead8', fontSize: '0.9rem' }}>
{contextNoteMenu.text}
</div>
<button
type="button"
className="btn-primary"
onClick={() => {
appendSelectedTextToNotes(contextNoteMenu.text)
setContextNoteMenu(null)
}}
>
Add to Notes
</button>
</div>
)}
{notesModalOpen && (
<div
className="study-modal-overlay"
role="dialog"
aria-modal="true"
aria-label="Lesson notes editor"
onClick={closeNotesModal}
style={{
position: 'fixed',
inset: 0,
backgroundColor: 'rgba(12, 11, 10, 0.75)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 40,
padding: '1rem',
}}
>
<div
className="study-modal"
onClick={e => e.stopPropagation()}
style={{
width: 'min(760px, 100%)',
maxHeight: 'min(90vh, 900px)',
overflowY: 'auto',
backgroundColor: '#1b1a16',
border: '1px solid #3c3a31',
borderRadius: '18px',
padding: '1.5rem',
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '1rem', marginBottom: '1rem' }}>
<div>
<h2 style={{ margin: 0 }}>Lesson Notes</h2>
<p style={{ margin: '0.5rem 0 0', color: '#b9b09b' }}>Save notes for this lesson and paste selections from the page.</p>
</div>
<button type="button" className="btn-secondary" onClick={closeNotesModal}>Close</button>
</div>
<textarea
rows={12}
value={noteText}
onChange={e => setNoteText(e.target.value)}
placeholder="Write your notes here..."
style={{
width: '100%',
minHeight: '280px',
padding: '1rem',
borderRadius: '16px',
border: '1px solid #3f3b2f',
background: '#14130f',
color: '#f0ead8',
fontSize: '1rem',
lineHeight: '1.6',
}}
/>
<div style={{ display: 'flex', gap: '0.75rem', flexWrap: 'wrap', justifyContent: 'flex-end', marginTop: '1rem' }}>
<button type="button" className="btn-primary" onClick={saveNote} disabled={!canSaveNote}>
{noteSaving ? 'Saving...' : 'Save Notes'}
</button>
<button type="button" className="btn-secondary" onClick={closeNotesModal}>Cancel</button>
</div>
{(noteMessage || progressMessage) && <p className="study-note-status" style={{ marginTop: '1rem' }}>{noteMessage || progressMessage}</p>}
</div>
</div>
)}
</div> </div>
</section> </section>
</main> </main>