Implement comprehensive rich text editing capabilities using Tiptap editor with full formatting toolbar and functionality. Features: - Bold, italic, underline, strikethrough text formatting - Highlight text with yellow marker - Bullet and numbered lists - Heading styles (H2, H3) - Text alignment (left, center, right) - Undo/redo support - Clear formatting option - Intuitive toolbar with icons and tooltips - Responsive font size support Technical changes: - Added Tiptap dependencies to package.json (@tiptap/vue-3, starter-kit, extensions) - Created RichTextEditor component with full toolbar and formatting options - Integrated editor into sermon notes section replacing textarea - Updated download API to convert HTML to formatted plain text - Updated email API to handle HTML content properly - Notes now stored as HTML with rich formatting preserved The editor provides a professional writing experience with all standard formatting tools while maintaining automatic save functionality and seamless integration with email/download features. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
138 lines
3.7 KiB
TypeScript
138 lines
3.7 KiB
TypeScript
import { getSermonNote, getUserByUsername, getDatabase } from '~/server/utils/database'
|
|
import { getSessionUsername } from '~/server/utils/auth'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const username = await getSessionUsername(event)
|
|
|
|
if (!username) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Unauthorized'
|
|
})
|
|
}
|
|
|
|
const sermonId = parseInt(event.context.params?.sermonId || '')
|
|
|
|
if (!sermonId) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: 'Invalid sermon ID'
|
|
})
|
|
}
|
|
|
|
// Get user info
|
|
const user = getUserByUsername(username)
|
|
if (!user) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: 'User not found'
|
|
})
|
|
}
|
|
|
|
// Get sermon info
|
|
const db = getDatabase()
|
|
const sermon = db.prepare('SELECT * FROM sermons WHERE id = ?').get(sermonId) as any
|
|
|
|
if (!sermon) {
|
|
throw createError({
|
|
statusCode: 404,
|
|
message: 'Sermon not found'
|
|
})
|
|
}
|
|
|
|
// Get user's notes
|
|
const noteRecord = getSermonNote(user.id!, sermonId)
|
|
// Convert HTML to plain text for download
|
|
const htmlToText = (html: string) => {
|
|
if (!html) return 'No notes taken'
|
|
return html
|
|
.replace(/<br\s*\/?>/gi, '\n')
|
|
.replace(/<\/p>/gi, '\n')
|
|
.replace(/<p[^>]*>/gi, '')
|
|
.replace(/<\/h[1-6]>/gi, '\n')
|
|
.replace(/<h[1-6][^>]*>/gi, '')
|
|
.replace(/<li[^>]*>/gi, '• ')
|
|
.replace(/<\/li>/gi, '\n')
|
|
.replace(/<[^>]+>/g, '')
|
|
.replace(/ /g, ' ')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.trim()
|
|
}
|
|
const userNotes = htmlToText(noteRecord?.notes || '')
|
|
|
|
// Format bible references
|
|
let bibleReferencesText = ''
|
|
try {
|
|
const refs = JSON.parse(sermon.bible_references)
|
|
bibleReferencesText = refs.map((ref: any) =>
|
|
`${ref.reference} (${ref.version})\n${ref.text}`
|
|
).join('\n\n')
|
|
} catch {
|
|
bibleReferencesText = sermon.bible_references
|
|
}
|
|
|
|
// Format date
|
|
const formatDate = (dateString: string) => {
|
|
const date = new Date(dateString + 'T00:00:00')
|
|
return date.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
}
|
|
|
|
const dates = [sermon.date]
|
|
if (sermon.dates) {
|
|
try {
|
|
const additionalDates = JSON.parse(sermon.dates)
|
|
dates.push(...additionalDates)
|
|
} catch {}
|
|
}
|
|
const sermonDate = dates.map(formatDate).join(' - ')
|
|
|
|
// Create formatted text content
|
|
const textContent = `
|
|
================================================================================
|
|
New Life Christian Church - SERMON NOTES
|
|
================================================================================
|
|
|
|
Title: ${sermon.title}
|
|
Date: ${sermonDate}
|
|
|
|
================================================================================
|
|
BIBLE REFERENCES
|
|
================================================================================
|
|
|
|
${bibleReferencesText}
|
|
|
|
================================================================================
|
|
PERSONAL APPLIANCE
|
|
================================================================================
|
|
|
|
${sermon.personal_appliance}
|
|
|
|
================================================================================
|
|
PASTOR'S CHALLENGE
|
|
================================================================================
|
|
|
|
${sermon.pastors_challenge}
|
|
|
|
================================================================================
|
|
MY NOTES
|
|
================================================================================
|
|
|
|
${userNotes}
|
|
`.trim()
|
|
|
|
// Set headers for file download
|
|
setResponseHeaders(event, {
|
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
'Content-Disposition': `attachment; filename="${sermon.slug}-notes.txt"`
|
|
})
|
|
|
|
return textContent
|
|
})
|