feat(T01): auth + user management + RBAC + base frontend + i18n
- Backend: FastAPI, JWT auth (HS256), bcrypt, RBAC middleware - User CRUD (admin-only), soft-delete, pagination - Pydantic BaseSettings config, async SQLAlchemy 2.0 - 50 backend tests, 88% coverage - Frontend: Next.js 14 App Router, Tailwind, design tokens - Login page, auth context, API client with auto-refresh - i18n: next-intl, DE/EN (31 keys each) - 6 base UI components (Button, Input, Card, Table, Modal, Toast) - 12 frontend tests, npm build success
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface TableProps<T> {
|
||||
columns: { key: string; label: string; render?: (row: T) => ReactNode }[];
|
||||
data: T[];
|
||||
rowKey: (row: T) => string;
|
||||
}
|
||||
|
||||
export function Table<T extends Record<string, any>>({ columns, data, rowKey }: TableProps<T>) {
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-border">
|
||||
{columns.map((col) => (
|
||||
<th key={col.key} className="text-left py-3 px-4 font-medium text-text-muted text-sm">
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((row) => (
|
||||
<tr key={rowKey(row)} className="border-b border-border hover:bg-background/50">
|
||||
{columns.map((col) => (
|
||||
<td key={col.key} className="py-3 px-4 text-text">
|
||||
{col.render ? col.render(row) : String(row[col.key] ?? '')}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
{data.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={columns.length} className="py-8 text-center text-text-muted">
|
||||
No data available
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user