Skip to content

Commit 774a93c

Browse files
committed
Refactor Admin Users Table
1 parent ce35585 commit 774a93c

14 files changed

+273
-315
lines changed

src/app/admin/components/UsersTable.tsx

+19-85
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,45 @@
11
"use client";
22

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

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

11-
import SearchTeam from "../teams/components/SearchTeam";
9+
import TableSearch from "../teams/components/TableSearch";
1210
import TanstackTableBody from "../teams/components/TanstackTableBody";
1311
import TableFooter from "../teams/components/TanstackTableFooter";
1412
import TanstackTableHead from "../teams/components/TanstackTableHead";
15-
import { type User, usersColumns } from "./UsersTableSetup";
13+
import type { User } from "../users/UserTablePage";
14+
import { usersColumns } from "./UsersTableSetup";
1615

1716
export default function UsersTable({ users }: { users: User[] }) {
1817
const [data, setData] = useState(users);
1918
const [globalFilter, setGlobalFilter] = useState("");
20-
const queryClient = useQueryClient();
21-
const deleteParticipant = useMutation({
22-
mutationFn: async ({
23-
rowIndex,
24-
participantID,
25-
}: {
26-
rowIndex: number;
27-
participantID: Schema["User"]["deleteType"];
28-
}) => {
29-
const prev = data;
30-
setData((old) => old.filter((_, index) => index !== rowIndex));
31-
try {
32-
const response = await client.models.User.delete(participantID);
33-
if (response.errors) {
34-
throw new Error(response.errors[0].message);
35-
}
36-
} catch (error) {
37-
setData(prev);
38-
throw error;
39-
}
40-
return participantID;
41-
},
42-
onError: (error) => {
43-
toast.error("Error updating teams: " + error.message);
44-
},
45-
onSuccess: (teamID) => {
46-
queryClient.invalidateQueries({ queryKey: ["Teams"] });
47-
toast.success(`Team ${teamID.id} deleted succesfully`);
48-
},
49-
});
50-
const updateParticipant = useMutation({
51-
mutationFn: async (updatedData: Schema["User"]["updateType"]) => {
52-
try {
53-
const response = await client.models.User.update(updatedData);
54-
if (response.errors) {
55-
throw new Error(response.errors[0].message);
56-
}
57-
} catch (error) {
58-
throw error;
59-
}
60-
},
61-
onSuccess: () => {
62-
queryClient.invalidateQueries({ queryKey: ["Participant"] });
63-
toast.success("Table data updated succesfully");
64-
},
65-
onError: () => {
66-
toast.error("Error updating participant");
67-
},
68-
});
19+
const deleteUser = async (id: Schema["User"]["deleteType"]) =>
20+
client.models.User.delete(id);
21+
const updateUser = async (updatedData: Schema["User"]["updateType"]) => {
22+
return client.models.User.update({
23+
id: updatedData.id,
24+
firstName: updatedData.firstName,
25+
lastName: updatedData.lastName,
26+
role: updatedData.role,
27+
});
28+
};
6929
const table = tanstackTableHelper({
7030
data,
71-
columnData: usersColumns,
72-
meta: {
73-
updateData: (rowIndex, columnId, value) => {
74-
setData((old) =>
75-
old.map((row, index) => {
76-
if (index !== rowIndex) return row;
77-
return {
78-
...old[rowIndex]!,
79-
[columnId]: value,
80-
};
81-
}),
82-
);
83-
},
84-
deleteData: (participant, rowIndex) => {
85-
const participantID = {
86-
id: participant.id,
87-
};
88-
deleteParticipant.mutate({ participantID, rowIndex });
89-
},
90-
saveData: (participant) => {
91-
const updatedData = {
92-
id: participant.id,
93-
name: participant.firstName,
94-
lastName: participant.lastName,
95-
role: participant.role,
96-
teamId: participant.teamId,
97-
email: participant.email,
98-
};
99-
updateParticipant.mutate(updatedData);
100-
},
101-
},
31+
columns: usersColumns,
10232
globalFilter,
10333
setGlobalFilter,
34+
deleteElement: deleteUser,
35+
updateElement: updateUser,
36+
setData,
37+
typeName: "User",
10438
});
10539
return (
10640
<div className="flex flex-1 flex-col justify-between rounded-3xl bg-white p-2 text-xl outline outline-awesomer-purple">
10741
<div>
108-
<SearchTeam
42+
<TableSearch
10943
tableDataLength={table.getRowCount()}
11044
handleSearchChange={useCallback(
11145
(value: string) => setGlobalFilter(value),

src/app/admin/components/UsersTableSetup.tsx

+28-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22

33
import { useState } from "react";
44

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

87
import DeleteButton from "../teams/components/DeleteButton";
98
import SaveEditButton from "../teams/components/SaveEditButton";
10-
11-
export type User = Pick<
12-
Schema["User"]["type"],
13-
"email" | "firstName" | "lastName" | "role" | "teamId" | "id"
14-
>;
9+
import type { User } from "../users/UserTablePage";
1510

1611
const Role = {
1712
Participant: "Participant",
@@ -21,11 +16,24 @@ const Role = {
2116
const columnHelper = createColumnHelper<User>();
2217
export const usersColumns = [
2318
columnHelper.accessor("id", {
24-
cell: (info) => info.getValue(),
19+
cell: (info) => {
20+
return (
21+
<div className="max-w-24 overflow-x-auto whitespace-nowrap lg:max-w-48 2xl:max-w-full">
22+
{info.getValue()}
23+
</div>
24+
);
25+
},
2526
header: "ID",
2627
sortingFn: "basic",
2728
}),
2829
columnHelper.accessor("email", {
30+
cell: (info) => {
31+
return (
32+
<div className="max-w-24 overflow-x-auto whitespace-nowrap lg:max-w-48 2xl:max-w-full">
33+
{info.getValue()}
34+
</div>
35+
);
36+
},
2937
header: "Email",
3038
sortingFn: "basic",
3139
}),
@@ -137,7 +145,19 @@ export const usersColumns = [
137145
return (
138146
<div className="grid auto-cols-auto grid-flow-col gap-2 ">
139147
<SaveEditButton row={row} meta={meta} />
140-
<DeleteButton row={row} meta={meta} />
148+
<DeleteButton
149+
row={row}
150+
meta={meta}
151+
headerText={
152+
<>
153+
<div>
154+
{`${row.original.firstName} ${row.original.lastName}`}
155+
</div>
156+
<div className="text-lg">{row.original.email}</div>
157+
<div className="text-xs">{row.original.id}</div>
158+
</>
159+
}
160+
/>
141161
</div>
142162
);
143163
},

src/app/admin/teams/TeamTableSetup.tsx

+18-24
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { useEffect, useState } from "react";
1+
import { useState } from "react";
22

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

55
import DeleteButton from "./components/DeleteButton";
66
import SaveEditButton from "./components/SaveEditButton";
7-
import type { Team } from "./components/TeamsTable";
7+
import type { Team } from "./components/TeamsTablePage";
88
import ViewButton from "./components/ViewButton";
99

1010
const columnHelper = createColumnHelper<Team>();
@@ -22,21 +22,13 @@ export const teamColumns = [
2222
options: { meta },
2323
},
2424
}) => {
25-
const initialValue = getValue() as string;
26-
const [value, setValue] = useState(initialValue);
27-
useEffect(() => {
28-
setValue(initialValue);
29-
}, [initialValue]);
30-
if (!getIsSelected()) {
31-
return getValue();
32-
}
33-
const onBlur = () => {
34-
meta?.updateData(index, "name", value);
35-
};
25+
const [value, setValue] = useState(getValue());
26+
if (!getIsSelected()) return getValue();
27+
const onBlur = () => meta?.updateData(index, "name", value);
3628
return (
3729
<input
3830
className="w-full rounded-md border border-awesomer-purple bg-white p-2 focus:outline-none focus:ring-1 focus:ring-awesomer-purple"
39-
value={value as string}
31+
value={value}
4032
onChange={(e) => setValue(e.target.value)}
4133
onBlur={onBlur}
4234
/>
@@ -75,19 +67,12 @@ export const teamColumns = [
7567
}) => {
7668
const initialValue = getValue();
7769
const [value, setValue] = useState(initialValue);
78-
useEffect(() => {
79-
setValue(initialValue);
80-
}, [initialValue]);
81-
if (!getIsSelected()) {
82-
return getValue() ? "Approved" : "Not Approved";
83-
}
70+
if (!getIsSelected()) return getValue() ? "Approved" : "Not Approved";
8471
const ApproveStatus = {
8572
Approved: true,
8673
"Not Approved": false,
8774
} as const;
88-
const onBlur = () => {
89-
meta?.updateData(index, "approved", value);
90-
};
75+
const onBlur = () => meta?.updateData(index, "approved", value);
9176
return (
9277
<select
9378
value={value ? "Approved" : "Not Approved"}
@@ -125,7 +110,16 @@ export const teamColumns = [
125110
<div className="grid auto-cols-auto grid-flow-col gap-2 ">
126111
<SaveEditButton row={row} meta={meta} />
127112
<ViewButton team={team} />
128-
<DeleteButton row={row} meta={meta} />
113+
<DeleteButton
114+
row={row}
115+
meta={meta}
116+
headerText={
117+
<>
118+
<div>{row.original.name}</div>
119+
<div className="text-lg">{row.original.id}</div>
120+
</>
121+
}
122+
/>
129123
</div>
130124
);
131125
},

src/app/admin/teams/components/DeleteButton.tsx

+7-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ReactNode } from "react";
12
import { useState } from "react";
23

34
import type { Row, TableMeta } from "@tanstack/react-table";
@@ -7,46 +8,41 @@ import Modal from "./Modal";
78
export default function DeleteButton<T extends Record<string, any>>({
89
row,
910
meta,
11+
headerText,
1012
}: {
1113
row: Row<T>;
1214
meta?: TableMeta<T>;
15+
headerText?: ReactNode;
1316
}) {
1417
const [showPopup, setShowPopup] = useState(false);
15-
const header =
16-
row.original.teamName ??
17-
`${row.original.firstName} ${row.original.lastName}`;
18-
const id = `- #${row.original.teamID ?? row.original.id}`;
1918
return (
2019
<>
2120
<button className=" text-dark-pink" onClick={() => setShowPopup(true)}>
2221
Delete
2322
</button>
2423
{showPopup && (
2524
<Modal onClose={() => setShowPopup(false)}>
26-
<h1 className="text-3xl font-semibold">
27-
{header}
28-
{id}
29-
</h1>
25+
<h1 className="text-3xl font-semibold">{headerText}</h1>
3026
<div className="flex flex-col gap-2 bg-light-grey p-2">
3127
<h1 className="text-xl font-bold">
3228
Are you sure you want to delete this record?
3329
</h1>
3430
<h2 className="mb-2">
35-
This record will be deleted{" "}
31+
This record will be deleted
3632
<i>
3733
<b>permanently</b>
3834
</i>
3935
. You cannot undo this action.
4036
</h2>
4137
<div className="flex justify-end gap-2">
4238
<button
43-
className=" rounded-md border border-black p-2 px-6 hover:opacity-40"
39+
className=" rounded-md border border-black p-2 px-6 transition-opacity hover:opacity-40"
4440
onClick={() => setShowPopup(false)}
4541
>
4642
Cancel
4743
</button>
4844
<button
49-
className="rounded-md border bg-dark-pink p-2 px-6 text-white transition-all hover:bg-pastel-pink"
45+
className="rounded-md border bg-dark-pink p-2 px-6 text-white transition-opacity hover:opacity-40"
5046
onClick={() => {
5147
meta?.deleteData(row.original, row.index);
5248
setShowPopup(false);

src/app/admin/teams/components/SearchTeam.tsx renamed to src/app/admin/teams/components/TableSearch.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { memo, useEffect, useState } from "react";
55

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

8-
const SearchTeam = ({
8+
const TableSearch = ({
99
tableDataLength,
1010
handleSearchChange,
1111
}: {
@@ -40,4 +40,4 @@ const SearchTeam = ({
4040
);
4141
};
4242

43-
export default memo(SearchTeam);
43+
export default memo(TableSearch);

src/app/admin/teams/components/TanstackTableBody.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { flexRender } from "@tanstack/react-table";
33

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

0 commit comments

Comments
 (0)