Xianbao QIAN
nice views
58154f8
raw
history blame
7.51 kB
'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('');
const [isLoading, setIsLoading] = useState(true);
const [orderBy, setOrderBy] = useState<'all_children' | 'direct_children'>('all_children');
useEffect(() => {
async function fetchData() {
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
// Select a bundle based on browser checks
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
const worker_url = URL.createObjectURL(
new Blob([`importScripts("${bundle.mainWorker!}");`], { type: 'text/javascript' })
);
// Instantiate the asynchronous version of DuckDB-Wasm
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);
// Register the Parquet file using the URL
await db.registerFileURL(
'ancestor_children.parquet',
`${window.location.origin}/ancestor_children.parquet`,
duckdb.DuckDBDataProtocol.HTTP,
false
);
// Execute the SQL query using the registered Parquet file
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);
// Convert the result to a JavaScript array
const data: ModelData[] = result.toArray();
// Close the connection and terminate the worker
await conn.close();
await db.terminate();
setAllModels(data);
setIsLoading(false);
}
fetchData();
}, []);
const filteredModels = allModels.filter((model) =>
model.ancestor.toLowerCase().includes(filterText.toLowerCase())
);
const sortedModels = filteredModels.sort((a, b) => {
if (orderBy === 'all_children') {
return b.all_children_count - a.all_children_count;
} else {
return (b.direct_children_count ?? 0) - (a.direct_children_count ?? 0);
}
});
const totalPages = Math.ceil(sortedModels.length / pageSize);
const paginatedModels = sortedModels.slice(
(currentPage - 1) * pageSize,
currentPage * pageSize
);
const handleOrderByClick = (column: 'all_children' | 'direct_children') => {
setOrderBy(column);
setCurrentPage(1);
};
return (
<main className="container mx-auto py-8 bg-white dark:bg-gray-900 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 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
/>
</div>
{isLoading ? (
<p>Loading data...</p>
) : 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 cursor-pointer"
onClick={() => handleOrderByClick('direct_children')}
>
Direct Children {orderBy === 'direct_children' && '▼'}
</th>
<th
className="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-right cursor-pointer"
onClick={() => handleOrderByClick('all_children')}
>
All Children {orderBy === '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 flex items-center justify-between">
<button
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
disabled={currentPage === 1}
className="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded-md mr-2"
>
Previous
</button>
<div className="flex items-center space-x-2">
{currentPage > 1 && (
<>
<button
onClick={() => setCurrentPage(1)}
className="px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md"
>
1
</button>
{currentPage > 2 && <span className="text-gray-500">...</span>}
</>
)}
{[...Array(5)].map((_, i) => {
const page = currentPage + i - 2;
if (page >= 1 && page <= totalPages) {
return (
<button
key={page}
onClick={() => setCurrentPage(page)}
className={`px-2 py-1 ${
page === currentPage
? 'bg-blue-500 dark:bg-blue-600 text-white'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200'
} rounded-md`}
>
{page}
</button>
);
}
return null;
})}
{currentPage < totalPages && (
<>
{currentPage < totalPages - 1 && <span className="text-gray-500">...</span>}
<button
onClick={() => setCurrentPage(totalPages)}
className="px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md"
>
{totalPages}
</button>
</>
)}
</div>
<button
onClick={() => setCurrentPage((prev) => Math.min(prev + 1, totalPages))}
disabled={currentPage === totalPages}
className="px-4 py-2 bg-blue-500 dark:bg-blue-600 text-white rounded-md"
>
Next
</button>
</div>
</>
) : (
<p>No data found.</p>
)}
</main>
);
}