'use client'
import { Note } from '@/lib/types'
import { NoteCard } from './note-card'
import { useState, useMemo, useEffect } from 'react'
import { NoteEditor } from './note-editor'
import { reorderNotes, getNotes } from '@/app/actions/notes'
import {
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent,
MouseSensor,
TouchSensor,
useSensor,
useSensors,
closestCenter,
PointerSensor,
} from '@dnd-kit/core'
import {
SortableContext,
rectSortingStrategy,
useSortable,
arrayMove,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { useRouter } from 'next/navigation'
interface NoteGridProps {
notes: Note[]
}
function SortableNote({ note, onEdit }: { note: Note; onEdit: (note: Note) => void }) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id: note.id })
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
zIndex: isDragging ? 1000 : 1,
}
return (
)
}
export function NoteGrid({ notes }: NoteGridProps) {
const router = useRouter()
const [editingNote, setEditingNote] = useState(null)
const [activeId, setActiveId] = useState(null)
const [localPinnedNotes, setLocalPinnedNotes] = useState([])
const [localUnpinnedNotes, setLocalUnpinnedNotes] = useState([])
// Sync local state with props
useEffect(() => {
setLocalPinnedNotes(notes.filter(note => note.isPinned).sort((a, b) => a.order - b.order))
setLocalUnpinnedNotes(notes.filter(note => !note.isPinned).sort((a, b) => a.order - b.order))
}, [notes])
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
}),
useSensor(MouseSensor, {
activationConstraint: {
distance: 8,
},
}),
useSensor(TouchSensor, {
activationConstraint: {
delay: 200,
tolerance: 6,
},
})
)
const handleDragStart = (event: DragStartEvent) => {
console.log('[DND-DEBUG] Drag started:', {
activeId: event.active.id,
activeData: event.active.data.current
})
setActiveId(event.active.id as string)
}
const handleDragEnd = async (event: DragEndEvent) => {
const { active, over } = event
console.log('[DND-DEBUG] Drag ended:', {
activeId: active.id,
overId: over?.id,
hasOver: !!over
})
setActiveId(null)
if (!over || active.id === over.id) {
console.log('[DND-DEBUG] Drag cancelled: no valid drop target or same element')
return
}
const activeIdStr = active.id as string
const overIdStr = over.id as string
// Determine which section the dragged note belongs to
const isInPinned = localPinnedNotes.some(n => n.id === activeIdStr)
const targetIsInPinned = localPinnedNotes.some(n => n.id === overIdStr)
console.log('[DND-DEBUG] Section check:', {
activeIdStr,
overIdStr,
isInPinned,
targetIsInPinned,
pinnedNotesCount: localPinnedNotes.length,
unpinnedNotesCount: localUnpinnedNotes.length
})
// Only allow reordering within the same section
if (isInPinned !== targetIsInPinned) {
console.log('[DND-DEBUG] Drag cancelled: crossing sections (pinned/unpinned)')
return
}
if (isInPinned) {
// Reorder pinned notes
const oldIndex = localPinnedNotes.findIndex(n => n.id === activeIdStr)
const newIndex = localPinnedNotes.findIndex(n => n.id === overIdStr)
console.log('[DND-DEBUG] Pinned reorder:', { oldIndex, newIndex })
if (oldIndex !== -1 && newIndex !== -1) {
const newOrder = arrayMove(localPinnedNotes, oldIndex, newIndex)
setLocalPinnedNotes(newOrder)
console.log('[DND-DEBUG] Calling reorderNotes for pinned notes')
await reorderNotes(activeIdStr, overIdStr)
// Refresh notes from server to sync state
console.log('[DND-DEBUG] Refreshing notes from server after reorder')
await refreshNotesFromServer()
} else {
console.log('[DND-DEBUG] Invalid indices for pinned reorder')
}
} else {
// Reorder unpinned notes
const oldIndex = localUnpinnedNotes.findIndex(n => n.id === activeIdStr)
const newIndex = localUnpinnedNotes.findIndex(n => n.id === overIdStr)
console.log('[DND-DEBUG] Unpinned reorder:', { oldIndex, newIndex })
if (oldIndex !== -1 && newIndex !== -1) {
const newOrder = arrayMove(localUnpinnedNotes, oldIndex, newIndex)
setLocalUnpinnedNotes(newOrder)
console.log('[DND-DEBUG] Calling reorderNotes for unpinned notes')
await reorderNotes(activeIdStr, overIdStr)
// Refresh notes from server to sync state
console.log('[DND-DEBUG] Refreshing notes from server after reorder')
await refreshNotesFromServer()
} else {
console.log('[DND-DEBUG] Invalid indices for unpinned reorder')
}
}
}
// Function to refresh notes from server without full page reload
const refreshNotesFromServer = async () => {
console.log('[DND-DEBUG] Fetching fresh notes from server...')
const freshNotes = await getNotes()
console.log('[DND-DEBUG] Received fresh notes:', freshNotes.length)
// Update local state with fresh data
const pinned = freshNotes.filter(note => note.isPinned).sort((a, b) => a.order - b.order)
const unpinned = freshNotes.filter(note => !note.isPinned).sort((a, b) => a.order - b.order)
setLocalPinnedNotes(pinned)
setLocalUnpinnedNotes(unpinned)
console.log('[DND-DEBUG] Local state updated with fresh server data')
}
// Find active note from either section
const activeNote = activeId
? localPinnedNotes.find(n => n.id === activeId) || localUnpinnedNotes.find(n => n.id === activeId)
: null
return (
<>
{localPinnedNotes.length > 0 && (
Pinned
n.id)} strategy={rectSortingStrategy}>
{localPinnedNotes.map((note) => (
))}
)}
{localUnpinnedNotes.length > 0 && (
{localPinnedNotes.length > 0 && (
Others
)}
n.id)} strategy={rectSortingStrategy}>
{localUnpinnedNotes.map((note) => (
))}
)}
{activeNote ? (
{}}
isDragging={true}
/>
) : null}
{notes.length === 0 && (
No notes yet
Create your first note to get started
)}
{editingNote && (
setEditingNote(null)} />
)}
>
)
}