All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m32s
- note-card.tsx: parent/child notebook dropdown with indentation - notes-tabs-view.tsx: tree-structured notebook picker with check marks - agent-detail-view.tsx: source/target notebook selects with optgroup tree - Add notebook-tree-select.tsx shared utility component
95 lines
2.9 KiB
TypeScript
95 lines
2.9 KiB
TypeScript
'use client'
|
|
|
|
import { Notebook } from '@/lib/types'
|
|
|
|
interface NotebookTreeSelectProps {
|
|
notebooks: Notebook[]
|
|
value: string | null
|
|
onChange: (notebookId: string | null) => void
|
|
placeholder?: string
|
|
inboxLabel?: string
|
|
className?: string
|
|
}
|
|
|
|
export function buildNotebookTree(notebooks: Notebook[]): { roots: Notebook[]; children: Map<string, Notebook[]> } {
|
|
const roots: Notebook[] = []
|
|
const children = new Map<string, Notebook[]>()
|
|
for (const nb of notebooks) {
|
|
if (nb.parentId) {
|
|
const arr = children.get(nb.parentId) || []
|
|
arr.push(nb)
|
|
children.set(nb.parentId, arr)
|
|
} else {
|
|
roots.push(nb)
|
|
}
|
|
}
|
|
return { roots, children }
|
|
}
|
|
|
|
export function NotebookTreeSelect({ notebooks, value, onChange, placeholder, inboxLabel, className }: NotebookTreeSelectProps) {
|
|
const { roots, children } = buildNotebookTree(notebooks)
|
|
|
|
return (
|
|
<select
|
|
value={value || ''}
|
|
onChange={e => onChange(e.target.value || null)}
|
|
className={className}
|
|
>
|
|
{placeholder && <option value="">{placeholder}</option>}
|
|
{inboxLabel && <option value="">{inboxLabel}</option>}
|
|
{roots.map(nb => (
|
|
<optgroup key={nb.id} label={nb.name}>
|
|
<option value={nb.id}>{nb.name}</option>
|
|
{(children.get(nb.id) || []).map(child => (
|
|
<option key={child.id} value={child.id}> └ {child.name}</option>
|
|
))}
|
|
</optgroup>
|
|
))}
|
|
</select>
|
|
)
|
|
}
|
|
|
|
export function NotebookTreeList({ notebooks, activeNotebookId, onSelect, inboxLabel }: {
|
|
notebooks: Notebook[]
|
|
activeNotebookId: string | null
|
|
onSelect: (notebookId: string | null) => void
|
|
inboxLabel?: string
|
|
}) {
|
|
const { roots, children } = buildNotebookTree(notebooks)
|
|
|
|
return (
|
|
<>
|
|
{inboxLabel && (
|
|
<button
|
|
type="button"
|
|
onClick={() => onSelect(null)}
|
|
className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-[12px] font-medium hover:bg-muted transition-colors text-foreground/70"
|
|
>
|
|
{activeNotebookId === null ? '✓ ' : ' '}{inboxLabel}
|
|
</button>
|
|
)}
|
|
{roots.map(nb => (
|
|
<div key={nb.id}>
|
|
<button
|
|
type="button"
|
|
onClick={() => onSelect(nb.id)}
|
|
className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-[12px] font-medium hover:bg-muted transition-colors text-foreground/70"
|
|
>
|
|
{activeNotebookId === nb.id ? '✓ ' : ' '}{nb.name}
|
|
</button>
|
|
{(children.get(nb.id) || []).map(child => (
|
|
<button
|
|
key={child.id}
|
|
type="button"
|
|
onClick={() => onSelect(child.id)}
|
|
className="flex w-full items-center gap-2 rounded pl-6 pr-2 py-1.5 text-[12px] font-medium hover:bg-muted transition-colors text-foreground/70"
|
|
>
|
|
{activeNotebookId === child.id ? '✓ ' : ' '}└ {child.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
))}
|
|
</>
|
|
)
|
|
}
|