## 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>
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { Note } from '@/types/note'
|
|
import { NoteList } from './note-list'
|
|
import { Button } from '@/components/ui/button'
|
|
import { SearchBar } from './search-bar'
|
|
import { ArrowRight, TrendingUp, Terminal, Code, Zap } from 'lucide-react'
|
|
|
|
interface DashboardProps {
|
|
recentNotes: 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">
|
|
<SearchBar />
|
|
</div>
|
|
<div className="space-y-8">
|
|
|
|
{/* Recientes */}
|
|
<section>
|
|
<div className="flex items-center justify-between mb-3">
|
|
<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" />
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
{recentNotes.length > 0 ? (
|
|
<NoteList notes={recentNotes} />
|
|
) : (
|
|
<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>
|
|
)
|
|
}
|