refactor(ux): consolidate BMAD skills, update design system, and clean up Prisma generated client
This commit is contained in:
@@ -24,6 +24,7 @@ import { NoteCard } from './note-card';
|
||||
import { updateFullOrderWithoutRevalidation } from '@/app/actions/notes';
|
||||
import { useNotebookDrag } from '@/context/notebook-drag-context';
|
||||
import { useLanguage } from '@/lib/i18n';
|
||||
import { useCardSizeMode } from '@/hooks/use-card-size-mode';
|
||||
import dynamic from 'next/dynamic';
|
||||
import './masonry-grid.css';
|
||||
|
||||
@@ -36,6 +37,8 @@ const NoteEditor = dynamic(
|
||||
interface MasonryGridProps {
|
||||
notes: Note[];
|
||||
onEdit?: (note: Note, readOnly?: boolean) => void;
|
||||
onSizeChange?: (noteId: string, size: 'small' | 'medium' | 'large') => void;
|
||||
isTrashView?: boolean;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
@@ -49,6 +52,7 @@ interface SortableNoteProps {
|
||||
onDragEndNote?: () => void;
|
||||
isDragging?: boolean;
|
||||
isOverlay?: boolean;
|
||||
isTrashView?: boolean;
|
||||
}
|
||||
|
||||
const SortableNoteItem = memo(function SortableNoteItem({
|
||||
@@ -59,6 +63,7 @@ const SortableNoteItem = memo(function SortableNoteItem({
|
||||
onDragEndNote,
|
||||
isDragging,
|
||||
isOverlay,
|
||||
isTrashView,
|
||||
}: SortableNoteProps) {
|
||||
const {
|
||||
attributes,
|
||||
@@ -91,6 +96,7 @@ const SortableNoteItem = memo(function SortableNoteItem({
|
||||
onDragStart={onDragStartNote}
|
||||
onDragEnd={onDragEndNote}
|
||||
isDragging={isDragging}
|
||||
isTrashView={isTrashView}
|
||||
onSizeChange={(newSize) => onSizeChange(note.id, newSize)}
|
||||
/>
|
||||
</div>
|
||||
@@ -107,6 +113,7 @@ interface SortableGridSectionProps {
|
||||
draggedNoteId: string | null;
|
||||
onDragStartNote: (noteId: string) => void;
|
||||
onDragEndNote: () => void;
|
||||
isTrashView?: boolean;
|
||||
}
|
||||
|
||||
const SortableGridSection = memo(function SortableGridSection({
|
||||
@@ -116,6 +123,7 @@ const SortableGridSection = memo(function SortableGridSection({
|
||||
draggedNoteId,
|
||||
onDragStartNote,
|
||||
onDragEndNote,
|
||||
isTrashView,
|
||||
}: SortableGridSectionProps) {
|
||||
const ids = useMemo(() => notes.map(n => n.id), [notes]);
|
||||
|
||||
@@ -131,6 +139,7 @@ const SortableGridSection = memo(function SortableGridSection({
|
||||
onDragStartNote={onDragStartNote}
|
||||
onDragEndNote={onDragEndNote}
|
||||
isDragging={draggedNoteId === note.id}
|
||||
isTrashView={isTrashView}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -141,16 +150,28 @@ const SortableGridSection = memo(function SortableGridSection({
|
||||
// ─────────────────────────────────────────────
|
||||
// Main MasonryGrid component
|
||||
// ─────────────────────────────────────────────
|
||||
export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
export function MasonryGrid({ notes, onEdit, onSizeChange, isTrashView }: MasonryGridProps) {
|
||||
const { t } = useLanguage();
|
||||
const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null);
|
||||
const { startDrag, endDrag, draggedNoteId } = useNotebookDrag();
|
||||
const cardSizeMode = useCardSizeMode();
|
||||
const isUniformMode = cardSizeMode === 'uniform';
|
||||
|
||||
// Local notes state for optimistic size/order updates
|
||||
const [localNotes, setLocalNotes] = useState<Note[]>(notes);
|
||||
|
||||
useEffect(() => {
|
||||
setLocalNotes(notes);
|
||||
setLocalNotes(prev => {
|
||||
const prevIds = prev.map(n => n.id).join(',')
|
||||
const incomingIds = notes.map(n => n.id).join(',')
|
||||
if (prevIds === incomingIds) {
|
||||
const localSizeMap = new Map(prev.map(n => [n.id, n.size]))
|
||||
return notes.map(n => ({ ...n, size: localSizeMap.get(n.id) ?? n.size }))
|
||||
}
|
||||
// Notes added/removed: full sync but preserve local sizes
|
||||
const localSizeMap = new Map(prev.map(n => [n.id, n.size]))
|
||||
return notes.map(n => ({ ...n, size: localSizeMap.get(n.id) ?? n.size }))
|
||||
})
|
||||
}, [notes]);
|
||||
|
||||
const pinnedNotes = useMemo(() => localNotes.filter(n => n.isPinned), [localNotes]);
|
||||
@@ -172,7 +193,8 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
|
||||
const handleSizeChange = useCallback((noteId: string, newSize: 'small' | 'medium' | 'large') => {
|
||||
setLocalNotes(prev => prev.map(n => n.id === noteId ? { ...n, size: newSize } : n));
|
||||
}, []);
|
||||
onSizeChange?.(noteId, newSize);
|
||||
}, [onSizeChange]);
|
||||
|
||||
// @dnd-kit sensors — pointer (desktop) + touch (mobile)
|
||||
const sensors = useSensors(
|
||||
@@ -225,7 +247,7 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className="masonry-container">
|
||||
<div className="masonry-container" data-card-size-mode={cardSizeMode}>
|
||||
{pinnedNotes.length > 0 && (
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3 px-2">
|
||||
@@ -238,6 +260,7 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
draggedNoteId={draggedNoteId}
|
||||
onDragStartNote={startDrag}
|
||||
onDragEndNote={endDrag}
|
||||
isTrashView={isTrashView}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -256,6 +279,7 @@ export function MasonryGrid({ notes, onEdit }: MasonryGridProps) {
|
||||
draggedNoteId={draggedNoteId}
|
||||
onDragStartNote={startDrag}
|
||||
onDragEndNote={endDrag}
|
||||
isTrashView={isTrashView}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user