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:
@@ -5,13 +5,25 @@ import { Note } from '@/types/note'
|
||||
import { NoteList } from './note-list'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SearchBar } from './search-bar'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { ArrowRight, TrendingUp, Terminal, Code, Zap } from 'lucide-react'
|
||||
|
||||
export function Dashboard({ recentNotes, favoriteNotes, pinnedNotes }: {
|
||||
interface DashboardProps {
|
||||
recentNotes: Note[]
|
||||
favoriteNotes: Note[]
|
||||
pinnedNotes: Note[]
|
||||
}) {
|
||||
mostUsedNotes: Note[]
|
||||
recentCommands: Note[]
|
||||
recentSnippets: Note[]
|
||||
activityBasedNotes: Note[]
|
||||
hasActivity: boolean
|
||||
}
|
||||
|
||||
export function Dashboard({
|
||||
recentNotes,
|
||||
mostUsedNotes,
|
||||
recentCommands,
|
||||
recentSnippets,
|
||||
activityBasedNotes,
|
||||
hasActivity,
|
||||
}: DashboardProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-end mb-3">
|
||||
@@ -19,27 +31,12 @@ export function Dashboard({ recentNotes, favoriteNotes, pinnedNotes }: {
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
|
||||
{pinnedNotes.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3 flex items-center gap-2">
|
||||
📌 Pineadas
|
||||
</h2>
|
||||
<NoteList notes={pinnedNotes} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{favoriteNotes.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3 flex items-center gap-2">
|
||||
❤️ Favoritas
|
||||
</h2>
|
||||
<NoteList notes={favoriteNotes} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Recientes */}
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-xl font-semibold">Recientes</h2>
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
<span>Recientes</span>
|
||||
</h2>
|
||||
<Link href="/notes">
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Ver todas <ArrowRight className="h-4 w-4" />
|
||||
@@ -49,15 +46,89 @@ export function Dashboard({ recentNotes, favoriteNotes, pinnedNotes }: {
|
||||
{recentNotes.length > 0 ? (
|
||||
<NoteList notes={recentNotes} />
|
||||
) : (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<p>No hay notas todavía.</p>
|
||||
<Link href="/new">
|
||||
<Button className="mt-4">Crea tu primera nota</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<EmptyState />
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Más usadas */}
|
||||
{mostUsedNotes.length > 0 && (
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
<TrendingUp className="h-5 w-5 text-orange-500" />
|
||||
<span>Más usadas</span>
|
||||
</h2>
|
||||
<Link href="/notes">
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Ver todas <ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<NoteList notes={mostUsedNotes} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Comandos recientes */}
|
||||
{recentCommands.length > 0 && (
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5 text-green-500" />
|
||||
<span>Comandos recientes</span>
|
||||
</h2>
|
||||
<Link href="/notes?type=command">
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Ver todas <ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<NoteList notes={recentCommands} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Snippets recientes */}
|
||||
{recentSnippets.length > 0 && (
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
<Code className="h-5 w-5 text-blue-500" />
|
||||
<span>Snippets recientes</span>
|
||||
</h2>
|
||||
<Link href="/notes?type=snippet">
|
||||
<Button variant="ghost" size="sm" className="gap-1">
|
||||
Ver todas <ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<NoteList notes={recentSnippets} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Según tu actividad */}
|
||||
{hasActivity && activityBasedNotes.length > 0 && (
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
<Zap className="h-5 w-5 text-purple-500" />
|
||||
<span>Según tu actividad</span>
|
||||
</h2>
|
||||
</div>
|
||||
<NoteList notes={activityBasedNotes} />
|
||||
</section>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function EmptyState() {
|
||||
return (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
<p>No hay notas todavía.</p>
|
||||
<Link href="/new">
|
||||
<Button className="mt-4">Crea tu primera nota</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user