develop #1

Merged
darroyo merged 25 commits from develop into main 2026-03-24 01:31:17 +00:00
3 changed files with 41 additions and 3 deletions
Showing only changes of commit e57927e37d - Show all commits

View File

@@ -2,6 +2,7 @@ import { prisma } from '@/lib/prisma'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import { getRelatedNotes } from '@/lib/related' import { getRelatedNotes } from '@/lib/related'
import { getBacklinksForNote, getOutgoingLinksForNote } from '@/lib/backlinks' import { getBacklinksForNote, getOutgoingLinksForNote } from '@/lib/backlinks'
import { getCoUsedNotes } from '@/lib/usage'
import { NoteConnections } from '@/components/note-connections' import { NoteConnections } from '@/components/note-connections'
import { MarkdownContent } from '@/components/markdown-content' import { MarkdownContent } from '@/components/markdown-content'
import { DeleteNoteButton } from '@/components/delete-note-button' import { DeleteNoteButton } from '@/components/delete-note-button'
@@ -36,6 +37,7 @@ export default async function NoteDetailPage({ params }: { params: Promise<{ id:
const related = await getRelatedNotes(id, 5) const related = await getRelatedNotes(id, 5)
const backlinks = await getBacklinksForNote(id) const backlinks = await getBacklinksForNote(id)
const outgoingLinks = await getOutgoingLinksForNote(id) const outgoingLinks = await getOutgoingLinksForNote(id)
const coUsedNotes = await getCoUsedNotes(id, 5)
const noteType = note.type as NoteType const noteType = note.type as NoteType
return ( return (
@@ -100,6 +102,7 @@ export default async function NoteDetailPage({ params }: { params: Promise<{ id:
backlinks={backlinks} backlinks={backlinks}
outgoingLinks={outgoingLinks} outgoingLinks={outgoingLinks}
relatedNotes={related} relatedNotes={related}
coUsedNotes={coUsedNotes}
/> />
</main> </main>
</> </>

View File

@@ -3,7 +3,7 @@
import Link from 'next/link' import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
import { ArrowRight, Link2, RefreshCw, ExternalLink } from 'lucide-react' import { ArrowRight, Link2, RefreshCw, ExternalLink, Users } from 'lucide-react'
interface BacklinkInfo { interface BacklinkInfo {
id: string id: string
@@ -30,6 +30,7 @@ interface NoteConnectionsProps {
backlinks: BacklinkInfo[] backlinks: BacklinkInfo[]
outgoingLinks: BacklinkInfo[] outgoingLinks: BacklinkInfo[]
relatedNotes: RelatedNote[] relatedNotes: RelatedNote[]
coUsedNotes: { noteId: string; title: string; type: string; weight: number }[]
} }
function ConnectionGroup({ function ConnectionGroup({
@@ -84,9 +85,10 @@ export function NoteConnections({
backlinks, backlinks,
outgoingLinks, outgoingLinks,
relatedNotes, relatedNotes,
coUsedNotes,
}: NoteConnectionsProps) { }: NoteConnectionsProps) {
const hasAnyConnections = const hasAnyConnections =
backlinks.length > 0 || outgoingLinks.length > 0 || relatedNotes.length > 0 backlinks.length > 0 || outgoingLinks.length > 0 || relatedNotes.length > 0 || coUsedNotes.length > 0
if (!hasAnyConnections) { if (!hasAnyConnections) {
return null return null
@@ -136,6 +138,18 @@ export function NoteConnections({
}))} }))}
emptyMessage="No hay notas relacionadas" emptyMessage="No hay notas relacionadas"
/> />
{/* Co-used notes - often viewed together */}
<ConnectionGroup
title="Co-usadas"
icon={Users}
notes={coUsedNotes.map((cu) => ({
id: cu.noteId,
title: cu.title,
type: cu.type,
}))}
emptyMessage="No hay notas co-usadas"
/>
</CardContent> </CardContent>
</Card> </Card>
) )

View File

@@ -7,6 +7,10 @@ import { Button } from '@/components/ui/button'
import { Search, Loader2 } from 'lucide-react' import { Search, Loader2 } from 'lucide-react'
import { ScoredNote } from '@/lib/search' import { ScoredNote } from '@/lib/search'
// Simple in-memory cache for search results
const searchCache = new Map<string, ScoredNote[]>()
const MAX_CACHE_SIZE = 50
export function SearchBar() { export function SearchBar() {
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
const [results, setResults] = useState<ScoredNote[]>([]) const [results, setResults] = useState<ScoredNote[]>([])
@@ -31,14 +35,31 @@ export function SearchBar() {
} }
debounceRef.current = setTimeout(async () => { debounceRef.current = setTimeout(async () => {
// Check cache first
if (searchCache.has(query)) {
setResults(searchCache.get(query)!)
setIsOpen(true)
setSelectedIndex(-1)
return
}
setIsLoading(true) setIsLoading(true)
try { try {
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`) const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`)
const json = await res.json() const json = await res.json()
if (json.success) { if (json.success) {
setResults(json.data.slice(0, 8)) const results = json.data.slice(0, 8)
setResults(results)
setIsOpen(true) setIsOpen(true)
setSelectedIndex(-1) setSelectedIndex(-1)
// Cache the results
if (searchCache.size >= MAX_CACHE_SIZE) {
// Remove oldest entry (first in Map)
const firstKey = searchCache.keys().next().value
if (firstKey) searchCache.delete(firstKey)
}
searchCache.set(query, results)
} }
} catch (error) { } catch (error) {
console.error('Search failed:', error) console.error('Search failed:', error)