Redesigned Search, removed Unused Admin Route

This commit is contained in:
PxlLoewe
2025-12-27 15:33:00 +01:00
parent e9a4c50a12
commit b16b719c74
16 changed files with 209 additions and 178 deletions

View File

@@ -9,12 +9,13 @@ export interface PaginatedTableRef {
refresh: () => void;
}
interface PaginatedTableProps<TData> extends Omit<SortableTableProps<TData>, "data"> {
interface PaginatedTableProps<TData, TWhere extends object>
extends Omit<SortableTableProps<TData>, "data"> {
prismaModel: keyof PrismaClient;
stickyHeaders?: boolean;
filter?: Record<string, unknown>;
initialRowsPerPage?: number;
searchFields?: string[];
showSearch?: boolean;
getFilter?: (searchTerm: string) => TWhere;
include?: Record<string, boolean>;
strictQuery?: boolean;
leftOfSearch?: React.ReactNode;
@@ -24,11 +25,11 @@ interface PaginatedTableProps<TData> extends Omit<SortableTableProps<TData>, "da
ref?: Ref<PaginatedTableRef>;
}
export function PaginatedTable<TData>({
export function PaginatedTable<TData, TWhere extends object>({
prismaModel,
initialRowsPerPage = 30,
searchFields = [],
filter,
getFilter,
showSearch = false,
include,
ref,
strictQuery = false,
@@ -38,7 +39,7 @@ export function PaginatedTable<TData>({
leftOfPagination,
supressQuery,
...restProps
}: PaginatedTableProps<TData>) {
}: PaginatedTableProps<TData, TWhere>) {
const [data, setData] = useState<TData[]>([]);
const [rowsPerPage, setRowsPerPage] = useState(initialRowsPerPage);
const [page, setPage] = useState(0);
@@ -63,16 +64,14 @@ export function PaginatedTable<TData>({
return;
}
setLoading(true);
getData(
prismaModel,
rowsPerPage,
page * rowsPerPage,
searchTerm,
searchFields,
filter,
getData({
model: prismaModel,
limit: rowsPerPage,
offset: page * rowsPerPage,
where: getFilter ? getFilter(searchTerm) : undefined,
include,
orderBy,
strictQuery
select: strictQuery
? restProps.columns
.filter(
(col): col is { accessorKey: string } =>
@@ -84,7 +83,7 @@ export function PaginatedTable<TData>({
return acc;
}, {})
: undefined,
)
})
.then((result) => {
if (result) {
setData(result.data);
@@ -100,8 +99,7 @@ export function PaginatedTable<TData>({
rowsPerPage,
page,
searchTerm,
searchFields,
filter,
getFilter,
include,
orderBy,
strictQuery,
@@ -119,19 +117,19 @@ export function PaginatedTable<TData>({
if (supressQuery) return;
setLoading(true);
}, [searchTerm, page, rowsPerPage, orderBy, filter, setLoading, supressQuery]);
}, [searchTerm, page, rowsPerPage, orderBy, getFilter, setLoading, supressQuery]);
useDebounce(
() => {
refreshTableData();
},
500,
[searchTerm, page, rowsPerPage, orderBy, filter],
[searchTerm, page, rowsPerPage, orderBy, getFilter],
);
return (
<div className="m-4 space-y-4">
{(rightOfSearch || leftOfSearch || searchFields.length > 0) && (
{(rightOfSearch || leftOfSearch || showSearch) && (
<div
className={cn(
"sticky z-20 flex items-center gap-2 py-2",
@@ -142,7 +140,7 @@ export function PaginatedTable<TData>({
<div>{leftOfSearch}</div>
<div>{loading && <span className="loading loading-dots loading-md" />}</div>
</div>
{searchFields.length > 0 && (
{showSearch && (
<input
type="text"
placeholder="Suchen..."

View File

@@ -2,56 +2,30 @@
"use server";
import { prisma, PrismaClient } from "@repo/db";
export async function getData(
model: keyof PrismaClient,
limit: number,
offset: number,
searchTerm: string,
searchFields: string[],
filter?: Record<string, any>,
include?: Record<string, boolean>,
orderBy?: Record<string, "asc" | "desc">,
select?: Record<string, any>,
) {
if (!model || !prisma[model]) {
export async function getData<Twhere>({
model,
limit,
offset,
where,
include,
orderBy,
select,
}: {
model: keyof PrismaClient;
limit: number;
offset: number;
where: Twhere;
include?: Record<string, boolean>;
orderBy?: Record<string, "asc" | "desc">;
select?: Record<string, any>;
}) {
if (!model || !(prisma as any)[model]) {
return { data: [], total: 0 };
}
const formattedId = searchTerm.match(/^VAR(\d+)$/)?.[1];
const delegate = (prisma as any)[model];
const where = searchTerm
? {
OR: [
formattedId ? { id: formattedId } : undefined,
...searchFields.map((field) => {
if (field.includes(".")) {
const parts: string[] = field.split(".");
// Helper function to build nested object
const buildNestedFilter = (parts: string[], index = 0): any => {
if (index === parts.length - 1) {
// Reached the last part - add the contains filter
return { [parts[index] as string]: { contains: searchTerm } };
}
// For intermediate levels, nest the next level
return { [parts[index] as string]: buildNestedFilter(parts, index + 1) };
};
return buildNestedFilter(parts);
}
return { [field]: { contains: searchTerm } };
}),
].filter(Boolean),
...filter,
}
: { ...filter };
if (!prisma[model]) {
return { data: [], total: 0 };
}
const data = await (prisma[model] as any).findMany({
const data = await delegate.findMany({
where,
orderBy,
take: limit,
@@ -60,7 +34,7 @@ export async function getData(
select,
});
const total = await (prisma[model] as any).count({ where });
const total = await delegate.count({ where });
return { data, total };
}