'use server' import { revalidatePath } from 'next/cache' import prisma from '@/lib/prisma' import { Note, CheckItem } from '@/lib/types' // Helper function to parse JSON strings from database function parseNote(dbNote: any): Note { return { ...dbNote, checkItems: dbNote.checkItems ? JSON.parse(dbNote.checkItems) : null, labels: dbNote.labels ? JSON.parse(dbNote.labels) : null, images: dbNote.images ? JSON.parse(dbNote.images) : null, } } // Get all notes (non-archived by default) export async function getNotes(includeArchived = false) { try { const notes = await prisma.note.findMany({ where: includeArchived ? {} : { isArchived: false }, orderBy: [ { isPinned: 'desc' }, { order: 'asc' }, { updatedAt: 'desc' } ] }) return notes.map(parseNote) } catch (error) { console.error('Error fetching notes:', error) return [] } } // Get archived notes only export async function getArchivedNotes() { try { const notes = await prisma.note.findMany({ where: { isArchived: true }, orderBy: { updatedAt: 'desc' } }) return notes.map(parseNote) } catch (error) { console.error('Error fetching archived notes:', error) return [] } } // Search notes export async function searchNotes(query: string) { try { if (!query.trim()) { return await getNotes() } const notes = await prisma.note.findMany({ where: { isArchived: false, OR: [ { title: { contains: query } }, { content: { contains: query } }, { labels: { contains: query } }, { checkItems: { contains: query } } ] }, orderBy: [ { isPinned: 'desc' }, { updatedAt: 'desc' } ] }) // Enhanced ranking: prioritize title matches const rankedNotes = notes.map(note => { const parsedNote = parseNote(note) let score = 0 // Title match gets highest score if (parsedNote.title?.toLowerCase().includes(query.toLowerCase())) { score += 10 } // Content match if (parsedNote.content.toLowerCase().includes(query.toLowerCase())) { score += 5 } // Label match if (parsedNote.labels?.some(label => label.toLowerCase().includes(query.toLowerCase()))) { score += 3 } // CheckItems match if (parsedNote.checkItems?.some(item => item.text.toLowerCase().includes(query.toLowerCase()))) { score += 2 } return { note: parsedNote, score } }) // Sort by score descending, then by existing order (pinned/updated) return rankedNotes .sort((a, b) => b.score - a.score) .map(item => item.note) } catch (error) { console.error('Error searching notes:', error) return [] } } // Create a new note export async function createNote(data: { title?: string content: string color?: string type?: 'text' | 'checklist' checkItems?: CheckItem[] labels?: string[] images?: string[] isArchived?: boolean reminder?: Date | null isMarkdown?: boolean }) { try { const note = await prisma.note.create({ data: { title: data.title || null, content: data.content, color: data.color || 'default', type: data.type || 'text', checkItems: data.checkItems ? JSON.stringify(data.checkItems) : null, labels: data.labels ? JSON.stringify(data.labels) : null, images: data.images ? JSON.stringify(data.images) : null, isArchived: data.isArchived || false, reminder: data.reminder || null, isMarkdown: data.isMarkdown || false, } }) revalidatePath('/') return parseNote(note) } catch (error) { console.error('Error creating note:', error) throw new Error('Failed to create note') } } // Update a note export async function updateNote(id: string, data: { title?: string | null content?: string color?: string isPinned?: boolean isArchived?: boolean type?: 'text' | 'checklist' checkItems?: CheckItem[] | null labels?: string[] | null images?: string[] | null reminder?: Date | null isMarkdown?: boolean }) { try { // Stringify JSON fields if they exist const updateData: any = { ...data } if ('checkItems' in data) { updateData.checkItems = data.checkItems ? JSON.stringify(data.checkItems) : null } if ('labels' in data) { updateData.labels = data.labels ? JSON.stringify(data.labels) : null } if ('images' in data) { updateData.images = data.images ? JSON.stringify(data.images) : null } updateData.updatedAt = new Date() const note = await prisma.note.update({ where: { id }, data: updateData }) revalidatePath('/') return parseNote(note) } catch (error) { console.error('Error updating note:', error) throw new Error('Failed to update note') } } // Delete a note export async function deleteNote(id: string) { try { await prisma.note.delete({ where: { id } }) revalidatePath('/') return { success: true } } catch (error) { console.error('Error deleting note:', error) throw new Error('Failed to delete note') } } // Toggle pin status export async function togglePin(id: string, isPinned: boolean) { return updateNote(id, { isPinned }) } // Toggle archive status export async function toggleArchive(id: string, isArchived: boolean) { return updateNote(id, { isArchived }) } // Update note color export async function updateColor(id: string, color: string) { return updateNote(id, { color }) } // Update note labels export async function updateLabels(id: string, labels: string[]) { return updateNote(id, { labels }) } // Get all unique labels export async function getAllLabels() { try { const notes = await prisma.note.findMany({ select: { labels: true } }) const labelsSet = new Set() notes.forEach(note => { const labels = note.labels ? JSON.parse(note.labels) : null if (labels) { labels.forEach((label: string) => labelsSet.add(label)) } }) return Array.from(labelsSet).sort() } catch (error) { console.error('Error fetching labels:', error) return [] } } // Reorder notes (drag and drop) export async function reorderNotes(draggedId: string, targetId: string) { console.log('[REORDER-DEBUG] reorderNotes called:', { draggedId, targetId }) try { const draggedNote = await prisma.note.findUnique({ where: { id: draggedId } }) const targetNote = await prisma.note.findUnique({ where: { id: targetId } }) console.log('[REORDER-DEBUG] Notes found:', { draggedNote: draggedNote ? { id: draggedNote.id, title: draggedNote.title, isPinned: draggedNote.isPinned, order: draggedNote.order } : null, targetNote: targetNote ? { id: targetNote.id, title: targetNote.title, isPinned: targetNote.isPinned, order: targetNote.order } : null }) if (!draggedNote || !targetNote) { console.error('[REORDER-DEBUG] Notes not found') throw new Error('Notes not found') } // Get all notes in the same category (pinned or unpinned) const allNotes = await prisma.note.findMany({ where: { isPinned: draggedNote.isPinned, isArchived: false }, orderBy: { order: 'asc' } }) console.log('[REORDER-DEBUG] All notes in category:', allNotes.map(n => ({ id: n.id, title: n.title, order: n.order }))) // Create new order array const reorderedNotes = allNotes.filter(n => n.id !== draggedId) const targetIndex = reorderedNotes.findIndex(n => n.id === targetId) reorderedNotes.splice(targetIndex, 0, draggedNote) console.log('[REORDER-DEBUG] New order:', reorderedNotes.map((n, i) => ({ id: n.id, title: n.title, newOrder: i }))) // Update all notes with new order const updates = reorderedNotes.map((note, index) => prisma.note.update({ where: { id: note.id }, data: { order: index } }) ) console.log('[REORDER-DEBUG] Executing transaction with', updates.length, 'updates') await prisma.$transaction(updates) console.log('[REORDER-DEBUG] Transaction completed successfully') revalidatePath('/') return { success: true } } catch (error) { console.error('[REORDER-DEBUG] Error reordering notes:', error) throw new Error('Failed to reorder notes') } }