|
'use client'; |
|
|
|
import { useState, useEffect } from 'react'; |
|
import * as duckdb from '@duckdb/duckdb-wasm'; |
|
|
|
type ModelData = { |
|
ancestor: string; |
|
direct_children: string[] | null; |
|
all_children: string[]; |
|
all_children_count: number; |
|
direct_children_count: number | null; |
|
}; |
|
|
|
export default function Home() { |
|
const [allModels, setAllModels] = useState<ModelData[]>([]); |
|
const [currentPage, setCurrentPage] = useState(1); |
|
const [pageSize, setPageSize] = useState(100); |
|
const [filterText, setFilterText] = useState(''); |
|
|
|
useEffect(() => { |
|
async function fetchData() { |
|
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles(); |
|
|
|
|
|
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES); |
|
|
|
const worker_url = URL.createObjectURL( |
|
new Blob([`importScripts("${bundle.mainWorker!}");`], { type: 'text/javascript' }) |
|
); |
|
|
|
|
|
const worker = new Worker(worker_url); |
|
const logger = new duckdb.ConsoleLogger(); |
|
const db = new duckdb.AsyncDuckDB(logger, worker); |
|
await db.instantiate(bundle.mainModule, bundle.pthreadWorker); |
|
|
|
|
|
await db.registerFileURL( |
|
'ancestor_children.parquet', |
|
`${window.location.origin}/ancestor_children.parquet`, |
|
duckdb.DuckDBDataProtocol.HTTP, |
|
false |
|
); |
|
|
|
|
|
const query = ` |
|
SELECT |
|
ancestor, |
|
direct_children, |
|
all_children, |
|
CAST(all_children_count AS INTEGER) AS all_children_count, |
|
CAST(direct_children_count AS INTEGER) AS direct_children_count |
|
FROM 'ancestor_children.parquet' |
|
`; |
|
const conn = await db.connect(); |
|
const result = await conn.query(query); |
|
|
|
|
|
const data: ModelData[] = result.toArray(); |
|
|
|
|
|
await conn.close(); |
|
await db.terminate(); |
|
|
|
setAllModels(data); |
|
} |
|
fetchData(); |
|
}, []); |
|
|
|
const filteredModels = allModels.filter((model) => |
|
model.ancestor.toLowerCase().includes(filterText.toLowerCase()) |
|
); |
|
|
|
const totalPages = Math.ceil(filteredModels.length / pageSize); |
|
|
|
const paginatedModels = filteredModels.slice( |
|
(currentPage - 1) * pageSize, |
|
currentPage * pageSize |
|
); |
|
|
|
return ( |
|
<main className="container mx-auto py-8 text-gray-900 dark:text-white"> |
|
<h1 className="text-4xl font-bold mb-4">All Models</h1> |
|
<div className="mb-4"> |
|
<input |
|
type="text" |
|
placeholder="Filter by model name" |
|
value={filterText} |
|
onChange={(e) => setFilterText(e.target.value)} |
|
className="px-4 py-2 border border-gray-300 rounded-md" |
|
/> |
|
</div> |
|
{paginatedModels.length > 0 ? ( |
|
<> |
|
<table className="table-auto border-collapse w-full"> |
|
<thead> |
|
<tr> |
|
<th className="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-left">Model</th> |
|
<th className="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-right">Direct Children</th> |
|
<th className="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-right">All Children</th> |
|
</tr> |
|
</thead> |
|
<tbody> |
|
{paginatedModels.map((model, index) => ( |
|
<tr key={index} className="border-t border-gray-200 dark:border-gray-700"> |
|
<td className="px-4 py-2">{model.ancestor}</td> |
|
<td className="px-4 py-2 text-right">{model.direct_children_count ?? 0}</td> |
|
<td className="px-4 py-2 text-right">{model.all_children_count}</td> |
|
</tr> |
|
))} |
|
</tbody> |
|
</table> |
|
<div className="mt-4"> |
|
<button |
|
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))} |
|
disabled={currentPage === 1} |
|
className="px-4 py-2 bg-blue-500 text-white rounded-md mr-2" |
|
> |
|
Previous |
|
</button> |
|
<button |
|
onClick={() => setCurrentPage((prev) => Math.min(prev + 1, totalPages))} |
|
disabled={currentPage === totalPages} |
|
className="px-4 py-2 bg-blue-500 text-white rounded-md" |
|
> |
|
Next |
|
</button> |
|
</div> |
|
</> |
|
) : ( |
|
<p>No data found.</p> |
|
)} |
|
</main> |
|
); |
|
} |
|
|