added OrderBy functionality to Data Table

This commit is contained in:
PxlLoewe
2025-06-03 17:54:30 -07:00
parent 574472dcb9
commit 0eb3ba8104
11 changed files with 117 additions and 121 deletions

View File

@@ -47,7 +47,7 @@ export const SettingsBtn = () => {
setSelectedDevice(user.settingsMicDevice);
setMic(user.settingsMicDevice, user.settingsMicVolume || 1);
setMicVol(user.settingsMicVolume || 1);
setFunkVol(user.settingsFunkVolume || 0.8);
setFunkVol(user.settingsRadioVolume || 0.8);
setDmeVol(user.settingsDmeVolume || 0.8);
}
}, [user, setMic]);
@@ -205,7 +205,7 @@ export const SettingsBtn = () => {
user: {
settingsMicDevice: selectedDevice,
settingsMicVolume: micVol,
settingsFunkVolume: funkVolume,
settingsRadioVolume: funkVolume,
settingsDmeVolume: dmeVolume,
},
});

View File

@@ -1,6 +1,4 @@
import { PublicUser } from "@repo/db";
import { dispatchSocket } from "dispatch/socket";
import { serverApi } from "_helpers/axios";
import {
handleDisconnect,
handleLocalTrackUnpublished,

View File

@@ -49,14 +49,13 @@
"tailwindcss": "^4.1.8",
"zod": "^3.25.46",
"zustand": "^5.0.5",
"zustand-sync-tabs": "^0.2.2"
},
"devDependencies": {
"zustand-sync-tabs": "^0.2.2",
"@types/leaflet": "^1.9.18",
"@types/node": "^22.15.29",
"@types/react": "^19.1.6",
"@types/react-dom": "^19.1.5",
"daisyui": "^5.0.43",
"typescript": "^5.8.3"
}
},
"devDependencies": {}
}

View File

@@ -8,6 +8,7 @@ import { ColumnDef } from "@tanstack/react-table";
export default function ReportPage() {
return (
<PaginatedTable
initialOrderBy={[{ id: "timestamp", desc: true }]}
prismaModel="report"
include={{
Sender: true,
@@ -50,8 +51,7 @@ export default function ReportPage() {
{
accessorKey: "timestamp",
header: "Time",
cell: ({ row }) =>
new Date(row.getValue("timestamp")).toLocaleString(),
cell: ({ row }) => new Date(row.getValue("timestamp")).toLocaleString(),
},
{
accessorKey: "actions",

View File

@@ -15,7 +15,8 @@ export const EmailVerification = () => {
<div>
<h3 className="font-bold">E-Mail Adresse nicht bestätigt!</h3>
<div className="text-xs">
Wir haben dir bereits eine E-Mail gesendet. Wenn deine E-Mail Adresse nicht bestätigt ist, kannst du dich nicht mit der Leitstelle verbinden!
Wir haben dir bereits eine E-Mail gesendet. Wenn deine E-Mail Adresse nicht bestätigt ist,
kannst du dich nicht mit der Leitstelle verbinden!
</div>
</div>
<Button

View File

@@ -1,11 +1,5 @@
"use client";
import {
useEffect,
useState,
useCallback,
Ref,
useImperativeHandle,
} from "react";
import { useEffect, useState, useCallback, Ref, useImperativeHandle } from "react";
import SortableTable, { Pagination, SortableTableProps } from "./Table";
import { PrismaClient } from "@repo/db";
import { getData } from "./pagiantedTableActions";
@@ -14,8 +8,7 @@ export interface PaginatedTableRef {
refresh: () => void;
}
interface PaginatedTableProps<TData>
extends Omit<SortableTableProps<TData>, "data"> {
interface PaginatedTableProps<TData> extends Omit<SortableTableProps<TData>, "data"> {
prismaModel: keyof PrismaClient;
filter?: Record<string, any>;
rowsPerPage?: number;
@@ -48,6 +41,17 @@ export function PaginatedTable<TData>({
const [total, setTotal] = useState(0);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(searchTerm);
const [orderBy, setOrderBy] = useState<Record<string, "asc" | "desc">>(
restProps.initialOrderBy
? restProps.initialOrderBy.reduce(
(acc, sort) => {
acc[sort.id] = sort.desc ? "desc" : "asc";
return acc;
},
{} as Record<string, "asc" | "desc">,
)
: {},
);
const RefreshTableData = async () => {
getData(
@@ -58,6 +62,7 @@ export function PaginatedTable<TData>({
searchFields,
filter,
include,
orderBy,
).then((result) => {
if (result) {
setData(result.data);
@@ -68,7 +73,7 @@ export function PaginatedTable<TData>({
useEffect(() => {
RefreshTableData();
}, [filter]);
}, [filter, orderBy]);
useImperativeHandle(ref, () => ({
refresh: () => {
@@ -118,17 +123,14 @@ export function PaginatedTable<TData>({
data={data}
prismaModel={prismaModel}
showEditButton={showEditButton}
setOrderBy={setOrderBy}
{...restProps}
/>
)}
<div className="flex items-between">
{leftOfPagination}
{!hide && (
<Pagination
totalPages={Math.ceil(total / rowsPerPage)}
page={page}
setPage={setPage}
/>
<Pagination totalPages={Math.ceil(total / rowsPerPage)} page={page} setPage={setPage} />
)}
</div>
</div>

View File

@@ -1,5 +1,5 @@
"use client";
import { useState } from "react";
import { useEffect, useState } from "react";
import {
useReactTable,
getCoreRowModel,
@@ -17,15 +17,19 @@ export interface SortableTableProps<TData> {
columns: ColumnDef<TData>[];
showEditButton?: boolean;
prismaModel?: keyof PrismaClient;
setOrderBy?: (orderBy: Record<string, "asc" | "desc">) => void;
initialOrderBy?: SortingState;
}
export default function SortableTable<TData>({
data,
columns,
initialOrderBy = [],
prismaModel,
showEditButton,
setOrderBy,
}: SortableTableProps<TData>) {
const [sorting, setSorting] = useState<SortingState>([]);
const [sorting, setSorting] = useState<SortingState>(initialOrderBy);
const table = useReactTable({
data,
@@ -36,9 +40,7 @@ export default function SortableTable<TData>({
header: "Actions",
cell: ({ row }) => (
<div className="flex items-center gap-1">
<Link
href={`/admin/${prismaModel as string}/${(row.original as any).id}`}
>
<Link href={`/admin/${prismaModel as string}/${(row.original as any).id}`}>
<button className="btn btn-sm">Edit</button>
</Link>
</div>
@@ -52,6 +54,16 @@ export default function SortableTable<TData>({
state: { sorting },
});
useEffect(() => {
if (prismaModel) {
const orderBy: Record<string, "asc" | "desc"> = {};
sorting.forEach((sort) => {
orderBy[sort.id] = sort.desc ? "desc" : "asc";
});
setOrderBy?.(orderBy);
}
}, [sorting, prismaModel, setOrderBy]);
return (
<div className="overflow-x-auto">
<table className="table table-zebra w-full">
@@ -59,21 +71,11 @@ export default function SortableTable<TData>({
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
>
<th key={header.id} onClick={header.column.getToggleSortingHandler()}>
<div className="flex items-center gap-1 cursor-pointer">
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
{header.column.getIsSorted() === "asc" && (
<ChevronUp size={16} />
)}
{header.column.getIsSorted() === "desc" && (
<ChevronDown size={16} />
)}
{flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getIsSorted() === "asc" && <ChevronUp size={16} />}
{header.column.getIsSorted() === "desc" && <ChevronDown size={16} />}
</div>
</th>
))}
@@ -84,9 +86,7 @@ export default function SortableTable<TData>({
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
@@ -108,11 +108,7 @@ export const Pagination = ({
if (totalPages === 0) return null;
return (
<div className="join w-full justify-end">
<button
className="join-item btn"
disabled={page === 0}
onClick={() => setPage(page - 1)}
>
<button className="join-item btn" disabled={page === 0} onClick={() => setPage(page - 1)}>
<ArrowLeft size={16} />
</button>
<select

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use server";
import { prisma, PrismaClient } from "@repo/db";
@@ -9,6 +10,7 @@ export async function getData(
searchFields: string[],
filter?: Record<string, any>,
include?: Record<string, boolean>,
orderBy?: Record<string, "asc" | "desc">,
) {
if (!model || !prisma[model]) {
return { data: [], total: 0 };
@@ -34,6 +36,7 @@ export async function getData(
const data = await (prisma[model] as any).findMany({
where,
orderBy,
take: limit,
skip: offset,
include,

View File

@@ -40,9 +40,7 @@
"react-hot-toast": "^2.5.2",
"react-select": "^5.10.1",
"tailwind-merge": "^3.3.0",
"zod": "^3.25.46"
},
"devDependencies": {
"zod": "^3.25.46",
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4.1.8",
"@types/bcryptjs": "^3.0.0",
@@ -56,5 +54,6 @@
"postcss": "^8.5.4",
"tailwindcss": "^4.1.8",
"typescript": "^5.8.3"
}
},
"devDependencies": {}
}

View File

@@ -35,7 +35,7 @@ model User {
settingsMicDevice String? @map(name: "settings_mic_device")
settingsMicVolume Float? @map(name: "settings_mic_volume")
settingsDmeVolume Float? @map(name: "settings_dme_volume")
settingsFunkVolume Float? @map(name: "settings_funk_volume")
settingsRadioVolume Float? @map(name: "settings_funk_volume")
settingsHideLastname Boolean @default(false) @map(name: "settings_hide_lastname")
// email Verification:

124
pnpm-lock.yaml generated
View File

@@ -34,7 +34,7 @@ importers:
version: 0.5.7(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.13.3(@types/dom-mediacapture-record@1.0.22))
'@next-auth/prisma-adapter':
specifier: ^1.0.7
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@19.1.0)
@@ -56,12 +56,27 @@ importers:
'@types/jsonwebtoken':
specifier: ^9.0.9
version: 9.0.9
'@types/leaflet':
specifier: ^1.9.18
version: 1.9.18
'@types/node':
specifier: ^22.15.29
version: 22.15.29
'@types/react':
specifier: ^19.1.6
version: 19.1.6
'@types/react-dom':
specifier: ^19.1.5
version: 19.1.5(@types/react@19.1.6)
axios:
specifier: ^1.9.0
version: 1.9.0
clsx:
specifier: ^2.1.1
version: 2.1.1
daisyui:
specifier: ^5.0.43
version: 5.0.43
geojson:
specifier: ^0.5.0
version: 0.5.0
@@ -88,7 +103,7 @@ importers:
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next-auth:
specifier: ^4.24.11
version: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
version: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
npm:
specifier: ^11.4.1
version: 11.4.1
@@ -125,6 +140,9 @@ importers:
tailwindcss:
specifier: ^4.1.8
version: 4.1.8
typescript:
specifier: ^5.8.3
version: 5.8.3
zod:
specifier: ^3.25.46
version: 3.25.49
@@ -134,25 +152,6 @@ importers:
zustand-sync-tabs:
specifier: ^0.2.2
version: 0.2.2(zustand@5.0.5(@types/react@19.1.6)(react@19.1.0))
devDependencies:
'@types/leaflet':
specifier: ^1.9.18
version: 1.9.18
'@types/node':
specifier: ^22.15.29
version: 22.15.29
'@types/react':
specifier: ^19.1.6
version: 19.1.6
'@types/react-dom':
specifier: ^19.1.5
version: 19.1.5(@types/react@19.1.6)
daisyui:
specifier: ^5.0.43
version: 5.0.43
typescript:
specifier: ^5.8.3
version: 5.8.3
apps/dispatch-server:
dependencies:
@@ -241,12 +240,15 @@ importers:
apps/hub:
dependencies:
'@eslint/eslintrc':
specifier: ^3
version: 3.3.1
'@hookform/resolvers':
specifier: ^5.0.1
version: 5.0.1(react-hook-form@7.57.0(react@19.1.0))
'@next-auth/prisma-adapter':
specifier: ^1.0.7
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
version: 1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@19.1.0)
@@ -259,12 +261,30 @@ importers:
'@repo/typescript-config':
specifier: workspace:*
version: link:../../packages/typescript-config
'@tailwindcss/postcss':
specifier: ^4.1.8
version: 4.1.8
'@tanstack/react-query':
specifier: ^5.79.0
version: 5.79.2(react@19.1.0)
'@tanstack/react-table':
specifier: ^8.21.3
version: 8.21.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@types/bcryptjs':
specifier: ^3.0.0
version: 3.0.0
'@types/jsonwebtoken':
specifier: ^9.0.9
version: 9.0.9
'@types/node':
specifier: ^22.15.29
version: 22.15.29
'@types/react':
specifier: ^19.1.6
version: 19.1.6
'@types/react-dom':
specifier: ^19.1.5
version: 19.1.5(@types/react@19.1.6)
'@uiw/react-md-editor':
specifier: ^4.0.7
version: 4.0.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -277,9 +297,18 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
daisyui:
specifier: ^5.0.43
version: 5.0.43
date-fns:
specifier: ^4.1.0
version: 4.1.0
eslint:
specifier: ^9.15.0
version: 9.28.0(jiti@2.4.2)
eslint-config-next:
specifier: ^15.3.3
version: 15.3.3(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
i:
specifier: ^0.3.7
version: 0.3.7
@@ -297,13 +326,16 @@ importers:
version: 15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next-auth:
specifier: ^4.24.11
version: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
version: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next-remove-imports:
specifier: ^1.0.12
version: 1.0.12(webpack@5.99.9)
npm:
specifier: ^11.4.1
version: 11.4.1
postcss:
specifier: ^8.5.4
version: 8.5.4
react:
specifier: ^19.1.0
version: 19.1.0
@@ -331,49 +363,15 @@ importers:
tailwind-merge:
specifier: ^3.3.0
version: 3.3.0
zod:
specifier: ^3.25.46
version: 3.25.49
devDependencies:
'@eslint/eslintrc':
specifier: ^3
version: 3.3.1
'@tailwindcss/postcss':
specifier: ^4.1.8
version: 4.1.8
'@types/bcryptjs':
specifier: ^3.0.0
version: 3.0.0
'@types/jsonwebtoken':
specifier: ^9.0.9
version: 9.0.9
'@types/node':
specifier: ^22.15.29
version: 22.15.29
'@types/react':
specifier: ^19.1.6
version: 19.1.6
'@types/react-dom':
specifier: ^19.1.5
version: 19.1.5(@types/react@19.1.6)
daisyui:
specifier: ^5.0.43
version: 5.0.43
eslint:
specifier: ^9.15.0
version: 9.28.0(jiti@2.4.2)
eslint-config-next:
specifier: ^15.3.3
version: 15.3.3(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)
postcss:
specifier: ^8.5.4
version: 8.5.4
tailwindcss:
specifier: ^4.1.8
version: 4.1.8
typescript:
specifier: ^5.8.3
version: 5.8.3
zod:
specifier: ^3.25.46
version: 3.25.49
apps/hub-server:
dependencies:
@@ -5166,10 +5164,10 @@ snapshots:
'@tybys/wasm-util': 0.9.0
optional: true
'@next-auth/prisma-adapter@1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
'@next-auth/prisma-adapter@1.0.7(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))(next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
dependencies:
'@prisma/client': 6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)
next-auth: 4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next-auth: 4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@next/env@15.3.3': {}
@@ -8048,7 +8046,7 @@ snapshots:
neo-async@2.6.2: {}
next-auth@4.24.11(next@15.3.3(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
next-auth@4.24.11(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.27.4
'@panva/hkdf': 1.2.1