Merge branch 'main' of github.com:lfnovo/open-notebook
This commit is contained in:
commit
6fca61c671
4 changed files with 84 additions and 19 deletions
|
|
@ -24,6 +24,8 @@ class NotebookResponse(BaseModel):
|
||||||
archived: bool
|
archived: bool
|
||||||
created: str
|
created: str
|
||||||
updated: str
|
updated: str
|
||||||
|
source_count: int
|
||||||
|
note_count: int
|
||||||
|
|
||||||
|
|
||||||
# Search models
|
# Search models
|
||||||
|
|
|
||||||
|
|
@ -18,22 +18,33 @@ async def get_notebooks(
|
||||||
):
|
):
|
||||||
"""Get all notebooks with optional filtering and ordering."""
|
"""Get all notebooks with optional filtering and ordering."""
|
||||||
try:
|
try:
|
||||||
notebooks = await Notebook.get_all(order_by=order_by)
|
# Build the query with counts
|
||||||
|
query = f"""
|
||||||
|
SELECT *,
|
||||||
|
count(<-reference.in) as source_count,
|
||||||
|
count(<-artifact.in) as note_count
|
||||||
|
FROM notebook
|
||||||
|
ORDER BY {order_by}
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = await repo_query(query)
|
||||||
|
|
||||||
# Filter by archived status if specified
|
# Filter by archived status if specified
|
||||||
if archived is not None:
|
if archived is not None:
|
||||||
notebooks = [nb for nb in notebooks if nb.archived == archived]
|
result = [nb for nb in result if nb.get("archived") == archived]
|
||||||
|
|
||||||
return [
|
return [
|
||||||
NotebookResponse(
|
NotebookResponse(
|
||||||
id=nb.id or "",
|
id=str(nb.get("id", "")),
|
||||||
name=nb.name,
|
name=nb.get("name", ""),
|
||||||
description=nb.description,
|
description=nb.get("description", ""),
|
||||||
archived=nb.archived or False,
|
archived=nb.get("archived", False),
|
||||||
created=str(nb.created),
|
created=str(nb.get("created", "")),
|
||||||
updated=str(nb.updated),
|
updated=str(nb.get("updated", "")),
|
||||||
|
source_count=nb.get("source_count", 0),
|
||||||
|
note_count=nb.get("note_count", 0),
|
||||||
)
|
)
|
||||||
for nb in notebooks
|
for nb in result
|
||||||
]
|
]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error fetching notebooks: {str(e)}")
|
logger.error(f"Error fetching notebooks: {str(e)}")
|
||||||
|
|
@ -59,6 +70,8 @@ async def create_notebook(notebook: NotebookCreate):
|
||||||
archived=new_notebook.archived or False,
|
archived=new_notebook.archived or False,
|
||||||
created=str(new_notebook.created),
|
created=str(new_notebook.created),
|
||||||
updated=str(new_notebook.updated),
|
updated=str(new_notebook.updated),
|
||||||
|
source_count=0, # New notebook has no sources
|
||||||
|
note_count=0, # New notebook has no notes
|
||||||
)
|
)
|
||||||
except InvalidInputError as e:
|
except InvalidInputError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
@ -73,17 +86,28 @@ async def create_notebook(notebook: NotebookCreate):
|
||||||
async def get_notebook(notebook_id: str):
|
async def get_notebook(notebook_id: str):
|
||||||
"""Get a specific notebook by ID."""
|
"""Get a specific notebook by ID."""
|
||||||
try:
|
try:
|
||||||
notebook = await Notebook.get(notebook_id)
|
# Query with counts for single notebook
|
||||||
if not notebook:
|
query = """
|
||||||
|
SELECT *,
|
||||||
|
count(<-reference.in) as source_count,
|
||||||
|
count(<-artifact.in) as note_count
|
||||||
|
FROM $notebook_id
|
||||||
|
"""
|
||||||
|
result = await repo_query(query, {"notebook_id": ensure_record_id(notebook_id)})
|
||||||
|
|
||||||
|
if not result:
|
||||||
raise HTTPException(status_code=404, detail="Notebook not found")
|
raise HTTPException(status_code=404, detail="Notebook not found")
|
||||||
|
|
||||||
|
nb = result[0]
|
||||||
return NotebookResponse(
|
return NotebookResponse(
|
||||||
id=notebook.id or "",
|
id=str(nb.get("id", "")),
|
||||||
name=notebook.name,
|
name=nb.get("name", ""),
|
||||||
description=notebook.description,
|
description=nb.get("description", ""),
|
||||||
archived=notebook.archived or False,
|
archived=nb.get("archived", False),
|
||||||
created=str(notebook.created),
|
created=str(nb.get("created", "")),
|
||||||
updated=str(notebook.updated),
|
updated=str(nb.get("updated", "")),
|
||||||
|
source_count=nb.get("source_count", 0),
|
||||||
|
note_count=nb.get("note_count", 0),
|
||||||
)
|
)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
|
|
@ -112,6 +136,29 @@ async def update_notebook(notebook_id: str, notebook_update: NotebookUpdate):
|
||||||
|
|
||||||
await notebook.save()
|
await notebook.save()
|
||||||
|
|
||||||
|
# Query with counts after update
|
||||||
|
query = """
|
||||||
|
SELECT *,
|
||||||
|
count(<-reference.in) as source_count,
|
||||||
|
count(<-artifact.in) as note_count
|
||||||
|
FROM $notebook_id
|
||||||
|
"""
|
||||||
|
result = await repo_query(query, {"notebook_id": ensure_record_id(notebook_id)})
|
||||||
|
|
||||||
|
if result:
|
||||||
|
nb = result[0]
|
||||||
|
return NotebookResponse(
|
||||||
|
id=str(nb.get("id", "")),
|
||||||
|
name=nb.get("name", ""),
|
||||||
|
description=nb.get("description", ""),
|
||||||
|
archived=nb.get("archived", False),
|
||||||
|
created=str(nb.get("created", "")),
|
||||||
|
updated=str(nb.get("updated", "")),
|
||||||
|
source_count=nb.get("source_count", 0),
|
||||||
|
note_count=nb.get("note_count", 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fallback if query fails
|
||||||
return NotebookResponse(
|
return NotebookResponse(
|
||||||
id=notebook.id or "",
|
id=notebook.id or "",
|
||||||
name=notebook.name,
|
name=notebook.name,
|
||||||
|
|
@ -119,6 +166,8 @@ async def update_notebook(notebook_id: str, notebook_update: NotebookUpdate):
|
||||||
archived=notebook.archived or False,
|
archived=notebook.archived or False,
|
||||||
created=str(notebook.created),
|
created=str(notebook.created),
|
||||||
updated=str(notebook.updated),
|
updated=str(notebook.updated),
|
||||||
|
source_count=0,
|
||||||
|
note_count=0,
|
||||||
)
|
)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { NotebookResponse } from '@/lib/types/api'
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { MoreHorizontal, Archive, ArchiveRestore, Trash2 } from 'lucide-react'
|
import { MoreHorizontal, Archive, ArchiveRestore, Trash2, FileText, StickyNote } from 'lucide-react'
|
||||||
import { formatDistanceToNow } from 'date-fns'
|
import { formatDistanceToNow } from 'date-fns'
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|
@ -112,6 +112,18 @@ export function NotebookCard({ notebook }: NotebookCardProps) {
|
||||||
<div className="mt-3 text-xs text-muted-foreground">
|
<div className="mt-3 text-xs text-muted-foreground">
|
||||||
Updated {formatDistanceToNow(new Date(notebook.updated), { addSuffix: true })}
|
Updated {formatDistanceToNow(new Date(notebook.updated), { addSuffix: true })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Item counts footer */}
|
||||||
|
<div className="mt-3 flex items-center gap-1.5 border-t pt-3">
|
||||||
|
<Badge variant="outline" className="text-xs flex items-center gap-1 px-1.5 py-0.5 text-primary border-primary/50">
|
||||||
|
<FileText className="h-3 w-3" />
|
||||||
|
<span>{notebook.source_count}</span>
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline" className="text-xs flex items-center gap-1 px-1.5 py-0.5 text-primary border-primary/50">
|
||||||
|
<StickyNote className="h-3 w-3" />
|
||||||
|
<span>{notebook.note_count}</span>
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ export interface NotebookResponse {
|
||||||
archived: boolean
|
archived: boolean
|
||||||
created: string
|
created: string
|
||||||
updated: string
|
updated: string
|
||||||
|
source_count: number
|
||||||
|
note_count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NoteResponse {
|
export interface NoteResponse {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue