Skip to content

Commit

Permalink
Refactor Admin Users Table
Browse files Browse the repository at this point in the history
  • Loading branch information
justin-phxm committed Jan 22, 2025
1 parent ce35585 commit 774a93c
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 315 deletions.
104 changes: 19 additions & 85 deletions src/app/admin/components/UsersTable.tsx
Original file line number Diff line number Diff line change
@@ -1,111 +1,45 @@
"use client";

import { useCallback, useMemo, useState } from "react";
import { toast } from "react-toastify";

import { type Schema } from "@/amplify/data/resource";
import { client } from "@/app/QueryProvider";
import tanstackTableHelper from "@/components/TanstackTableHelper";
import { useMutation, useQueryClient } from "@tanstack/react-query";

import SearchTeam from "../teams/components/SearchTeam";
import TableSearch from "../teams/components/TableSearch";
import TanstackTableBody from "../teams/components/TanstackTableBody";
import TableFooter from "../teams/components/TanstackTableFooter";
import TanstackTableHead from "../teams/components/TanstackTableHead";
import { type User, usersColumns } from "./UsersTableSetup";
import type { User } from "../users/UserTablePage";
import { usersColumns } from "./UsersTableSetup";

export default function UsersTable({ users }: { users: User[] }) {
const [data, setData] = useState(users);
const [globalFilter, setGlobalFilter] = useState("");
const queryClient = useQueryClient();
const deleteParticipant = useMutation({
mutationFn: async ({
rowIndex,
participantID,
}: {
rowIndex: number;
participantID: Schema["User"]["deleteType"];
}) => {
const prev = data;
setData((old) => old.filter((_, index) => index !== rowIndex));
try {
const response = await client.models.User.delete(participantID);
if (response.errors) {
throw new Error(response.errors[0].message);
}
} catch (error) {
setData(prev);
throw error;
}
return participantID;
},
onError: (error) => {
toast.error("Error updating teams: " + error.message);
},
onSuccess: (teamID) => {
queryClient.invalidateQueries({ queryKey: ["Teams"] });
toast.success(`Team ${teamID.id} deleted succesfully`);
},
});
const updateParticipant = useMutation({
mutationFn: async (updatedData: Schema["User"]["updateType"]) => {
try {
const response = await client.models.User.update(updatedData);
if (response.errors) {
throw new Error(response.errors[0].message);
}
} catch (error) {
throw error;
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["Participant"] });
toast.success("Table data updated succesfully");
},
onError: () => {
toast.error("Error updating participant");
},
});
const deleteUser = async (id: Schema["User"]["deleteType"]) =>
client.models.User.delete(id);
const updateUser = async (updatedData: Schema["User"]["updateType"]) => {
return client.models.User.update({
id: updatedData.id,
firstName: updatedData.firstName,
lastName: updatedData.lastName,
role: updatedData.role,
});
};
const table = tanstackTableHelper({
data,
columnData: usersColumns,
meta: {
updateData: (rowIndex, columnId, value) => {
setData((old) =>
old.map((row, index) => {
if (index !== rowIndex) return row;
return {
...old[rowIndex]!,
[columnId]: value,
};
}),
);
},
deleteData: (participant, rowIndex) => {
const participantID = {
id: participant.id,
};
deleteParticipant.mutate({ participantID, rowIndex });
},
saveData: (participant) => {
const updatedData = {
id: participant.id,
name: participant.firstName,
lastName: participant.lastName,
role: participant.role,
teamId: participant.teamId,
email: participant.email,
};
updateParticipant.mutate(updatedData);
},
},
columns: usersColumns,
globalFilter,
setGlobalFilter,
deleteElement: deleteUser,
updateElement: updateUser,
setData,
typeName: "User",
});
return (
<div className="flex flex-1 flex-col justify-between rounded-3xl bg-white p-2 text-xl outline outline-awesomer-purple">
<div>
<SearchTeam
<TableSearch
tableDataLength={table.getRowCount()}
handleSearchChange={useCallback(
(value: string) => setGlobalFilter(value),
Expand Down
36 changes: 28 additions & 8 deletions src/app/admin/components/UsersTableSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@

import { useState } from "react";

import type { Schema } from "@/amplify/data/resource";
import { createColumnHelper } from "@tanstack/react-table";

import DeleteButton from "../teams/components/DeleteButton";
import SaveEditButton from "../teams/components/SaveEditButton";

export type User = Pick<
Schema["User"]["type"],
"email" | "firstName" | "lastName" | "role" | "teamId" | "id"
>;
import type { User } from "../users/UserTablePage";

const Role = {
Participant: "Participant",
Expand All @@ -21,11 +16,24 @@ const Role = {
const columnHelper = createColumnHelper<User>();
export const usersColumns = [
columnHelper.accessor("id", {
cell: (info) => info.getValue(),
cell: (info) => {
return (
<div className="max-w-24 overflow-x-auto whitespace-nowrap lg:max-w-48 2xl:max-w-full">
{info.getValue()}
</div>
);
},
header: "ID",
sortingFn: "basic",
}),
columnHelper.accessor("email", {
cell: (info) => {
return (
<div className="max-w-24 overflow-x-auto whitespace-nowrap lg:max-w-48 2xl:max-w-full">
{info.getValue()}
</div>
);
},
header: "Email",
sortingFn: "basic",
}),
Expand Down Expand Up @@ -137,7 +145,19 @@ export const usersColumns = [
return (
<div className="grid auto-cols-auto grid-flow-col gap-2 ">
<SaveEditButton row={row} meta={meta} />
<DeleteButton row={row} meta={meta} />
<DeleteButton
row={row}
meta={meta}
headerText={
<>
<div>
{`${row.original.firstName} ${row.original.lastName}`}
</div>
<div className="text-lg">{row.original.email}</div>
<div className="text-xs">{row.original.id}</div>
</>
}
/>
</div>
);
},
Expand Down
42 changes: 18 additions & 24 deletions src/app/admin/teams/TeamTableSetup.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from "react";
import { useState } from "react";

import { createColumnHelper } from "@tanstack/react-table";

import DeleteButton from "./components/DeleteButton";
import SaveEditButton from "./components/SaveEditButton";
import type { Team } from "./components/TeamsTable";
import type { Team } from "./components/TeamsTablePage";
import ViewButton from "./components/ViewButton";

const columnHelper = createColumnHelper<Team>();
Expand All @@ -22,21 +22,13 @@ export const teamColumns = [
options: { meta },
},
}) => {
const initialValue = getValue() as string;
const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
if (!getIsSelected()) {
return getValue();
}
const onBlur = () => {
meta?.updateData(index, "name", value);
};
const [value, setValue] = useState(getValue());
if (!getIsSelected()) return getValue();
const onBlur = () => meta?.updateData(index, "name", value);
return (
<input
className="w-full rounded-md border border-awesomer-purple bg-white p-2 focus:outline-none focus:ring-1 focus:ring-awesomer-purple"
value={value as string}
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={onBlur}
/>
Expand Down Expand Up @@ -75,19 +67,12 @@ export const teamColumns = [
}) => {
const initialValue = getValue();
const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
if (!getIsSelected()) {
return getValue() ? "Approved" : "Not Approved";
}
if (!getIsSelected()) return getValue() ? "Approved" : "Not Approved";
const ApproveStatus = {
Approved: true,
"Not Approved": false,
} as const;
const onBlur = () => {
meta?.updateData(index, "approved", value);
};
const onBlur = () => meta?.updateData(index, "approved", value);
return (
<select
value={value ? "Approved" : "Not Approved"}
Expand Down Expand Up @@ -125,7 +110,16 @@ export const teamColumns = [
<div className="grid auto-cols-auto grid-flow-col gap-2 ">
<SaveEditButton row={row} meta={meta} />
<ViewButton team={team} />
<DeleteButton row={row} meta={meta} />
<DeleteButton
row={row}
meta={meta}
headerText={
<>
<div>{row.original.name}</div>
<div className="text-lg">{row.original.id}</div>
</>
}
/>
</div>
);
},
Expand Down
18 changes: 7 additions & 11 deletions src/app/admin/teams/components/DeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReactNode } from "react";
import { useState } from "react";

import type { Row, TableMeta } from "@tanstack/react-table";
Expand All @@ -7,46 +8,41 @@ import Modal from "./Modal";
export default function DeleteButton<T extends Record<string, any>>({
row,
meta,
headerText,
}: {
row: Row<T>;
meta?: TableMeta<T>;
headerText?: ReactNode;
}) {
const [showPopup, setShowPopup] = useState(false);
const header =
row.original.teamName ??
`${row.original.firstName} ${row.original.lastName}`;
const id = `- #${row.original.teamID ?? row.original.id}`;
return (
<>
<button className=" text-dark-pink" onClick={() => setShowPopup(true)}>
Delete
</button>
{showPopup && (
<Modal onClose={() => setShowPopup(false)}>
<h1 className="text-3xl font-semibold">
{header}
{id}
</h1>
<h1 className="text-3xl font-semibold">{headerText}</h1>
<div className="flex flex-col gap-2 bg-light-grey p-2">
<h1 className="text-xl font-bold">
Are you sure you want to delete this record?
</h1>
<h2 className="mb-2">
This record will be deleted{" "}
This record will be deleted
<i>
<b>permanently</b>
</i>
. You cannot undo this action.
</h2>
<div className="flex justify-end gap-2">
<button
className=" rounded-md border border-black p-2 px-6 hover:opacity-40"
className=" rounded-md border border-black p-2 px-6 transition-opacity hover:opacity-40"
onClick={() => setShowPopup(false)}
>
Cancel
</button>
<button
className="rounded-md border bg-dark-pink p-2 px-6 text-white transition-all hover:bg-pastel-pink"
className="rounded-md border bg-dark-pink p-2 px-6 text-white transition-opacity hover:opacity-40"
onClick={() => {
meta?.deleteData(row.original, row.index);
setShowPopup(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { memo, useEffect, useState } from "react";

import search_icon from "@/svgs/admin/search_icon.svg";

const SearchTeam = ({
const TableSearch = ({
tableDataLength,
handleSearchChange,
}: {
Expand Down Expand Up @@ -40,4 +40,4 @@ const SearchTeam = ({
);
};

export default memo(SearchTeam);
export default memo(TableSearch);
2 changes: 1 addition & 1 deletion src/app/admin/teams/components/TanstackTableBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { flexRender } from "@tanstack/react-table";

export default function TanstackTableBody<T>({ table }: { table: Table<T> }) {
return (
<tbody>
<tbody className="text-sm md:text-lg xl:text-xl">
{table.getRowModel().rows.map((row) => (
<tr key={row.id} className="w-full odd:bg-light-grey">
{row.getVisibleCells().map((cell) => (
Expand Down
Loading

0 comments on commit 774a93c

Please sign in to comment.