+
@@ -48,7 +23,6 @@ export default async function Page({
-
- {/* TODO: Would very much like to not have "as any" here in the future */}
-
+
{userData && userData.length > 0 ? (
<>
-
+
>
) : (
No Results :(
)}
- {/*
*/}
-
);
}
diff --git a/apps/web/src/components/admin/users/DefaultPagination.tsx b/apps/web/src/components/admin/users/DefaultPagination.tsx
deleted file mode 100644
index a0d5771c..00000000
--- a/apps/web/src/components/admin/users/DefaultPagination.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-"use client";
-
-import {
- Pagination,
- PaginationContent,
- PaginationEllipsis,
- PaginationItem,
- PaginationLink,
- PaginationNext,
- PaginationPrevious,
-} from "@/components/shadcn/ui/pagination";
-import { useEffect, useRef, useState } from "react";
-import { usePathname, useSearchParams } from "next/navigation";
-import { createPath } from "@/lib/utils/shared/pageParams";
-
-export function DefaultPagination({ maxPages }: { maxPages: number }) {
- // FIXME: Come back and change after done testing
-
- const path = usePathname();
- const params = useSearchParams();
-
- const page = params.get("page") ?? "1";
-
- const [currPage, setCurrPage] = useState(+page);
- const pageRef = useRef(1);
-
- function incPage() {
- pageRef.current = Math.min(maxPages, pageRef.current + 1);
- setCurrPage(Math.min(maxPages, currPage + 1));
- }
-
- function decPage() {
- pageRef.current = Math.max(1, pageRef.current - 1);
- setCurrPage(Math.max(1, currPage - 1));
- }
-
- function createPaginationPath(reqPage: string) {
- const url = `${path}?${reqPage}&user=${
- params.get("user") ?? ""
- }&checkedBoxes=${params.get("checkedBoxes") ?? ""}`;
- console.log("Pagination", url);
- return url;
- }
-
- return (
-
-
-
- {
- decPage();
- }}
- />
-
- {currPage}
-
- {
- incPage();
- }}
- />
-
-
-
- );
-}
diff --git a/apps/web/src/components/admin/users/SearchBar.tsx b/apps/web/src/components/admin/users/SearchBar.tsx
deleted file mode 100644
index 2af198de..00000000
--- a/apps/web/src/components/admin/users/SearchBar.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-"use client";
-
-import { Input } from "@/components/shadcn/ui/input";
-import { useRouter, useSearchParams, usePathname } from "next/navigation";
-import { useRef, useState } from "react";
-import { useDebouncedCallback } from "use-debounce";
-
-import { X } from "lucide-react";
-export default function SearchBar() {
- const searchParams = useSearchParams();
- const { replace } = useRouter();
- const pathname = usePathname();
-
- // We use a debouncing strategy to prevent the search from querying every single keystroke and instead will run a time after the user completes typing
- const handleSearch = useDebouncedCallback((term) => {
- // @ts-ignore Works perfectly fine and is apprporiate accoriding to the docs. Might be a version issue?
- const params = new URLSearchParams(searchParams);
- if (term) {
- params.set("user", term);
- } else {
- params.delete("user");
- }
- replace(`${pathname}?${params.toString()}`);
- }, 100);
-
- return (
-
- {/* Needs to clear text */}
- handleSearch(e.target.value)}
- />
-
-
- );
-}
diff --git a/apps/web/src/components/admin/users/UserColumns.tsx b/apps/web/src/components/admin/users/UserColumns.tsx
index a65e1ec8..63ecc115 100644
--- a/apps/web/src/components/admin/users/UserColumns.tsx
+++ b/apps/web/src/components/admin/users/UserColumns.tsx
@@ -6,46 +6,139 @@ import { createSelectSchema } from "drizzle-zod";
import { userCommonData } from "db/schema";
import Link from "next/link";
import { Button } from "@/components/shadcn/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "../../shadcn/ui/dropdown-menu";
+import { Input } from "@/components/shadcn/ui/input";
+import { MoreHorizontal, ArrowUpDown, User } from "lucide-react";
+import type { Column, Row } from "@tanstack/react-table";
+import { dataTableFuzzyFilter } from "@/lib/utils/client/shared";
+import { Badge } from "@/components/shadcn/ui/badge";
const userValidator = createSelectSchema(userCommonData);
+// default fuzzy search and add filters by each column if possible
export type userValidatorType = Pick<
z.infer
,
| "clerkID"
| "signupTime"
| "firstName"
| "lastName"
- | "hackerTag"
| "email"
| "role"
+ | "isRSVPed"
+ | "hackerTag"
+ | "checkinTimestamp"
>;
+type UserColumnType = Column;
+
export const columns: ColumnDef[] = [
{
- accessorKey: "firstName",
- header: "Name",
- cell: ({ row }) => `${row.original.firstName} ${row.original.lastName}`,
+ accessorFn: (row) => `${row.firstName} ${row.lastName}`,
+ id: "name",
+ header: ({ column }) => (
+
+ ),
+ cell: (info) => info.getValue(),
+ // filterFn: (row, _columnId, filterValue) => {
+ // return row.original.firstName.toLocaleLowerCase().includes(filterValue.toLocaleLowerCase()) || row.original.lastName.toLocaleLowerCase().includes(filterValue.toLocaleLowerCase());
+ // },
+ filterFn: dataTableFuzzyFilter,
},
{
accessorKey: "email",
- header: "Email",
+ header: ({ column }) => (
+
+ ),
+ filterFn: "includesString",
+ cell: (info) => info.getValue(),
},
{
accessorKey: "hackerTag",
- header: "Hacker Tag",
+ header: ({ column }) => (
+
+ ),
cell: ({ row }) => `@${row.original.hackerTag}`,
+ filterFn: dataTableFuzzyFilter,
},
{
- accessorKey: "clerkID",
- header: "Account ID",
+ accessorKey: "role",
+ header: ({ column }) => (
+
+ ),
+ filterFn: "includesString",
},
{
- accessorKey: "role",
- header: "Role",
+ accessorKey: "isRSVPed",
+ header: ({ column }) => (
+
+ RSVP Status
+
+
+ ),
+ // row.original.isRSVPed ?
+ cell: ({ row }) => (
+
+
+
+ RSVP: {row.original.isRSVPed ? "Yes" : "No"}
+
+
+ ),
},
+ {
+ accessorKey: "checkinTimestamp",
+ header: ({ column }) => (
+
+ Checkin Time
+
+
+ ),
+ cell: ({ row }) => (
+
+ {row.original.checkinTimestamp
+ ? new Date(
+ row.original.checkinTimestamp,
+ ).toLocaleDateString() +
+ " " +
+ new Date(
+ row.original.checkinTimestamp,
+ ).toLocaleTimeString("en-US", {
+ hour: "2-digit",
+ minute: "2-digit",
+ })
+ : "Not Checked In"}
+
+ ),
+ },
+ // {
+ // accessorKey: "role",
+ // header: ({ column }) => (
+ //
+ // ),
+ // filterFn: "includesString",
+ // },
{
accessorKey: "signupTime",
- header: "Signup Date",
+ header: ({ column }) => (
+
+ Signup Time
+
+
+ ),
cell: ({ row }) => (
{new Date(row.original.signupTime).toLocaleDateString() + " "}
@@ -57,12 +150,85 @@ export const columns: ColumnDef[] = [
),
},
{
- accessorKey: "clerkID2",
- header: "View",
- cell: ({ row }) => (
-
-
-
- ),
+ id: "actions",
+ enableHiding: false,
+ cell: ({ row }) => {
+ return ;
+ },
},
];
+
+function UserDropDownActions({ row }: { row: Row }) {
+ const user = row.original;
+ return (
+
+
+
+
+
+
+ View User
+
+ navigator.clipboard.writeText(user.clerkID)}
+ className="cursor-pointer"
+ >
+ Copy Clerk ID
+
+
+
+ Email User
+
+
+
+
+ );
+}
+
+function UserTableHeader({
+ name,
+ column,
+ hasFilter,
+}: {
+ name: string;
+ column: UserColumnType;
+ hasFilter: boolean;
+}) {
+ return (
+
+
+ {name}
+ {hasFilter && }
+
+
column.setFilterValue(e.target.value)}
+ placeholder="search..."
+ />
+
+ );
+}
+
+function SortColumnButton({
+ name,
+ column,
+}: {
+ name: string;
+ column: UserColumnType;
+}) {
+ return (
+
+ );
+}
diff --git a/apps/web/src/components/admin/users/UserDataTable.tsx b/apps/web/src/components/admin/users/UserDataTable.tsx
index 5f4191b9..a9a0aa55 100644
--- a/apps/web/src/components/admin/users/UserDataTable.tsx
+++ b/apps/web/src/components/admin/users/UserDataTable.tsx
@@ -5,8 +5,12 @@ import {
flexRender,
getCoreRowModel,
useReactTable,
+ SortingState,
+ getSortedRowModel,
+ getPaginationRowModel,
+ ColumnFiltersState,
+ getFilteredRowModel,
} from "@tanstack/react-table";
-
import {
Table,
TableBody,
@@ -15,10 +19,10 @@ import {
TableHeader,
TableRow,
} from "@/components/shadcn/ui/table";
-
-import { useCallback } from "react";
-import { useRouter } from "next/navigation";
-
+import { Input } from "@/components/shadcn/ui/input";
+import { Button } from "@/components/shadcn/ui/button";
+import { useEffect, useState } from "react";
+import { dataTableFuzzyFilter } from "@/lib/utils/client/shared";
interface DataTableProps {
columns: ColumnDef[];
data: TData[];
@@ -28,68 +32,124 @@ export function DataTable({
columns,
data,
}: DataTableProps) {
- const formatTrProps = (state = {}) => {
- console.log("qua");
- return { onClick: () => console.log("state", state) };
- };
+ const [sorting, setSorting] = useState([]);
+ const [columnFilters, setColumnFilters] = useState([]);
+ const [globalFilter, setGlobalFilter] = useState("");
const table = useReactTable({
data,
columns,
+ filterFns: {
+ fuzzy: dataTableFuzzyFilter,
+ },
+ state: {
+ sorting,
+ columnFilters,
+ globalFilter,
+ },
+ onColumnFiltersChange: setColumnFilters,
+ globalFilterFn: dataTableFuzzyFilter,
getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onSortingChange: setSorting,
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
});
+ useEffect(() => {
+ console.log("column filters", columnFilters);
+ }, [columnFilters]);
+
return (
-
-
-
- {table.getHeaderGroups().map((headerGroup) => (
-
- {headerGroup.headers.map((header) => {
- return (
-
- {header.isPlaceholder
- ? null
- : flexRender(
- header.column.columnDef
- .header,
- header.getContext(),
- )}
-
- );
- })}
-
- ))}
-
-
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
+
+
+
+ {
+ // we want to set our global filter
+ setGlobalFilter(event.target.value);
+ }}
+ className="max-w-sm"
+ />
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
- {row.getVisibleCells().map((cell) => (
-
- {flexRender(
- cell.column.columnDef.cell,
- cell.getContext(),
- )}
-
- ))}
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef
+ .header,
+ header.getContext(),
+ )}
+
+ );
+ })}
- ))
- ) : (
-
-
- No results.
-
-
- )}
-
-
+ ))}
+
+
+ {table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext(),
+ )}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ No results.
+
+
+ )}
+
+
+
+
+
+
+
);
}
diff --git a/apps/web/src/components/dash/shared/ProfileButton.tsx b/apps/web/src/components/dash/shared/ProfileButton.tsx
index 5f2b8d13..f13cfcdd 100644
--- a/apps/web/src/components/dash/shared/ProfileButton.tsx
+++ b/apps/web/src/components/dash/shared/ProfileButton.tsx
@@ -17,7 +17,7 @@ import { auth, SignOutButton } from "@clerk/nextjs";
import Link from "next/link";
import { DropdownSwitcher } from "@/components/shared/ThemeSwitcher";
import { getUser } from "db/functions";
-import { clientLogOut } from "@/lib/utils/client/shared";
+import { clientLogOut } from "@/lib/utils/server/user";
export default async function ProfileButton() {
const clerkUser = auth();
diff --git a/apps/web/src/components/shared/ProfileButton.tsx b/apps/web/src/components/shared/ProfileButton.tsx
index 555924e7..f833ad99 100644
--- a/apps/web/src/components/shared/ProfileButton.tsx
+++ b/apps/web/src/components/shared/ProfileButton.tsx
@@ -19,7 +19,7 @@ import { DropdownSwitcher } from "@/components/shared/ThemeSwitcher";
import DefaultDropdownTrigger from "../dash/shared/DefaultDropDownTrigger";
import MobileNavBarLinks from "./MobileNavBarLinks";
import { getUser } from "db/functions";
-import { clientLogOut } from "@/lib/utils/client/shared";
+import { clientLogOut } from "@/lib/utils/server/user";
export default async function ProfileButton() {
const clerkUser = await auth();
diff --git a/apps/web/src/lib/utils/client/shared.ts b/apps/web/src/lib/utils/client/shared.ts
index db3df016..8030090f 100644
--- a/apps/web/src/lib/utils/client/shared.ts
+++ b/apps/web/src/lib/utils/client/shared.ts
@@ -1,8 +1,26 @@
+import { rankItem } from "@tanstack/match-sorter-utils";
+import { FilterFn } from "@tanstack/react-table";
import { redirect } from "next/navigation";
export function getClientTimeZone(vercelIPTimeZone: string | null) {
return vercelIPTimeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
}
-export async function clientLogOut() {
- "use server";
- redirect("/");
-}
+
+export const dataTableFuzzyFilter: FilterFn