feat: MVP-3 Sprint 1 - Usage tracking, smart dashboard, scoring boost

## Registro de Uso
- Nuevo modelo NoteUsage en Prisma
- Tipos de eventos: view, search_click, related_click, link_click, copy_command, copy_snippet
- Funciones: trackNoteUsage, getUsageStats, getRecentlyUsedNotes
- localStorage: recentlyViewed (últimas 10 notas)
- Rastreo de copias en markdown-content.tsx

## Dashboard Rediseñado
- 5 bloques: Recientes, Más usadas, Comandos recientes, Snippets recientes, Según actividad
- Nuevo src/lib/dashboard.ts con getDashboardData()
- Recomendaciones basadas en recentlyViewed

## Scoring con Uso Real
- search.ts: +1 per 5 views (max +3), +2 recency boost
- related.ts: mismo sistema de usage boost
- No eclipsa match textual fuerte

## Tests
- 110 tests pasando (usage, dashboard, related, search)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 16:03:14 -03:00
parent cc4b2453b1
commit 05b8f3910d
16 changed files with 2038 additions and 73 deletions

View File

@@ -4,6 +4,7 @@ import { RelatedNotes } from '@/components/related-notes'
import { getRelatedNotes } from '@/lib/related'
import { MarkdownContent } from '@/components/markdown-content'
import { DeleteNoteButton } from '@/components/delete-note-button'
import { TrackNoteView } from '@/components/track-note-view'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
@@ -35,6 +36,8 @@ export default async function NoteDetailPage({ params }: { params: Promise<{ id:
const noteType = note.type as NoteType
return (
<>
<TrackNoteView noteId={note.id} />
<main className="container mx-auto py-8 px-4 max-w-4xl">
<div className="mb-6">
<Link href="/notes">
@@ -82,12 +85,17 @@ export default async function NoteDetailPage({ params }: { params: Promise<{ id:
)}
<div className="mb-8">
<MarkdownContent content={note.content} className="bg-gray-50 p-4 rounded-lg border" />
<MarkdownContent
content={note.content}
noteType={noteType}
className="bg-gray-50 p-4 rounded-lg border"
/>
</div>
{related.length > 0 && (
<RelatedNotes notes={related} />
)}
</main>
</>
)
}