feat(db): mesh data model — meshes, members, invites, audit log

- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
import { useTranslation } from "@turbostarter/i18n";
import { cn } from "@turbostarter/ui";
import type { Table } from "@tanstack/react-table";
import { Button } from "#components/button";
import { Icons } from "#components/icons";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "#components/select";
interface DataTablePaginationProps<TData> extends React.ComponentProps<"div"> {
table: Table<TData>;
pageSizeOptions?: number[];
}
export function DataTablePagination<TData>({
table,
pageSizeOptions = [10, 20, 30, 40, 50],
className,
...props
}: DataTablePaginationProps<TData>) {
const { t } = useTranslation("common");
return (
<div
className={cn(
"flex w-full flex-col-reverse items-center justify-between gap-4 overflow-auto p-1 sm:flex-row sm:gap-8",
className,
)}
{...props}
>
{table.options.enableRowSelection && (
<div className="text-muted-foreground flex-1 text-sm whitespace-nowrap">
{t("rowsSelected", {
selected: table.getFilteredSelectedRowModel().rows.length,
total: table.getFilteredRowModel().rows.length,
})}
</div>
)}
<div className="flex flex-col-reverse items-center gap-4 sm:ml-auto sm:flex-row sm:gap-6 lg:gap-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium whitespace-nowrap">
{t("rowsPerPage")}
</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[4.5rem] [&[data-size]]:h-8">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{pageSizeOptions.map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-center text-sm font-medium">
{t("pageOf", {
page: table.getState().pagination.pageIndex + 1,
total: table.getPageCount() || 1,
})}
</div>
<div className="flex items-center space-x-2">
<Button
aria-label={t("first")}
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<Icons.ChevronsLeft className="size-4" />
</Button>
<Button
aria-label={t("previous")}
variant="outline"
size="icon"
className="size-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<Icons.ChevronLeft className="size-4" />
</Button>
<Button
aria-label={t("next")}
variant="outline"
size="icon"
className="size-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<Icons.ChevronRight className="size-4" />
</Button>
<Button
aria-label={t("last")}
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<Icons.ChevronsRight className="size-4" />
</Button>
</div>
</div>
</div>
);
}