import React, { useState, useEffect, useCallback, useMemo } from 'react';
const DataGrid = ({ columns, fetchUrl, pageSize = 25 }) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [page, setPage] = useState(0);
const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' });
const fetchData = useCallback(async () => {
try {
setLoading(true);
const url = `${fetchUrl}?page=${page}&size=${pageSize}`;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const json = await response.json();
setData(json.items ?? []);
} catch (err) {
setError(err.message || 'Failed to fetch');
} finally {
setLoading(false);
}
}, [fetchUrl, page, pageSize]);
useEffect(() => {
fetchData();
}, [fetchData]);
const sortedData = useMemo(() => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
const aVal = a[sortConfig.key];
const bVal = b[sortConfig.key];
if (aVal === bVal) return 0;
const result = aVal < bVal ? -1 : 1;
return sortConfig.direction === 'desc' ? -result : result;
});
}, [data, sortConfig]);
const handleSort = (key) => {
setSortConfig(prev => ({
key,
direction: prev.key === key && prev.direction === 'asc' ? 'desc' : 'asc',
}));
};
if (loading && data.length === 0) return <div className="loading">Loading...</div>;
if (error) return <div className="error">Error: {error}</div>;
return (
<div className="data-grid">
<table>
<thead>
<tr>
{columns.map(col => (
<th key={col.key} onClick={() => handleSort(col.key)}>
{col.label}
{sortConfig.key === col.key && (
<span>{sortConfig.direction === 'asc' ? ' ↑' : ' ↓'}</span>
)}
</th>
))}
</tr>
</thead>
<tbody>
{sortedData.map((row, i) => (
<tr key={row.id ?? i}>
{columns.map(col => (
<td key={col.key}>{row[col.key] ?? '—'}</td>
))}
</tr>
))}
</tbody>
</table>
<div className="pagination">
<button disabled={page === 0} onClick={() => setPage(p => p - 1)}>
Previous
</button>
<span>Page {page + 1}</span>
<button onClick={() => setPage(p => p + 1)}>Next</button>
</div>
</div>
);
};
export default DataGrid;