Skip to content

Commit

Permalink
AdminUserTable & Perf
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy committed Sep 24, 2024
1 parent 5909f3e commit 1441506
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 15 deletions.
3 changes: 2 additions & 1 deletion src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import RankList from "./pages/RankList";
const Problem = lazy(() => import("@/pages/problem/Problem"));
const AdminProblemList = lazy(() => import("@/pages/admin/ProblemList"));
const AdminCreateProblem = lazy(() => import("@/pages/admin/CreateProblem"));
const AdminUserList = lazy(() => import("@/pages/admin/UserList"));
const ProblemList = lazy(() => import("@/pages/problem/ProblemList"));
const JudgeList = lazy(() => import("@/pages//judge/JudgeList"));
const Judge = lazy(() => import("@/pages/judge/Judge"));
Expand Down Expand Up @@ -37,7 +38,7 @@ const Router: React.FC = () => {
/>
<Route path="problems" element={<AdminProblemList />} />
<Route path="problems/create" element={<AdminCreateProblem />} />
<Route path="users" element={<div>user</div>} />
<Route path="users" element={<AdminUserList />} />
</Route>
</Route>
</Routes>
Expand Down
27 changes: 27 additions & 0 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as UserServiceModel from "@/models/service/user";
import { axiosClient } from "@/utils/axiosClient";

export async function getUserInfoList(
limit?: number,
offset?: number,
): Promise<{
total: number;
list: UserServiceModel.UserInfo[];
}> {
limit = limit || 10;
offset = offset || 0;

let res = await axiosClient.get<{
total: number;
list: UserServiceModel.UserInfo[];
}>(`/api/v1/user`, {
params: {
limit,
offset,
},
});
if (res.status !== 200) {
throw Error("failed to get problem list");
}
return res.data;
}
10 changes: 4 additions & 6 deletions src/components/display/JudgeTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ const JudgeTable: React.FC<JudgeTableProps> = (props) => {
<td>{judge.problem?.title}</td>
<td className="flex items-center gap-3 py-2">
<div className="avatar">
<div className="w-8 rounded-full">
<UserAvatar
alt={judge.user?.name}
avatarUrl={judge.user?.avatarUrl}
/>
</div>
<UserAvatar
alt={judge.user?.name}
avatarUrl={judge.user?.avatarUrl}
/>
</div>
<span>{judge.user?.name}</span>
</td>
Expand Down
10 changes: 4 additions & 6 deletions src/components/display/RankTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ const RankTable: React.FC<RankTableProps> = (props) => {
<th>{rank.rank}</th>
<td className="flex items-center gap-3 py-2">
<div className="avatar">
<div className="w-8 rounded-full">
<UserAvatar
alt={rank.user?.name}
avatarUrl={rank.user?.avatarUrl}
/>
</div>
<UserAvatar
alt={rank.user?.name}
avatarUrl={rank.user?.avatarUrl}
/>
</div>
<span>{rank.user?.name}</span>
</td>
Expand Down
72 changes: 72 additions & 0 deletions src/components/display/UserTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as UserServiceModel from "@/models/service/user";
import React from "react";
import { useTranslation } from "react-i18next";
import UserAvatar from "./UserAvatar";

export interface UserTableProps {
data: UserServiceModel.UserInfo[];
showActions?: boolean;
className?: string;
}

const UserTable: React.FC<UserTableProps> = (props) => {
const { t } = useTranslation();

return (
<>
<div className={props.className}>
<table className="table" aria-label="Problem Table">
<thead>
<tr className="border-base-content/10">
<th key="avatar">{t("Avatar")}</th>
<th key="name">{t("Name")}</th>
<th key="account">{t("Account")}</th>
<th key="roles">{t("Roles")}</th>
{/* {props.showActions && <th key="actions">{t("Actions")}</th>} */}
</tr>
</thead>
<tbody>
{props.data.map((userInfo) => (
<tr key={userInfo.account} className="border-base-content/10">
<td className="flex items-center gap-3 py-2">
<div className="avatar">
<UserAvatar
alt={userInfo.name}
avatarUrl={userInfo.avatarUrl}
/>
</div>
</td>
<th>{userInfo.name}</th>
<td>{userInfo.account}</td>
<td>
<UserRoles roles={userInfo.roles ?? []} />
</td>
</tr>
))}
</tbody>
</table>
</div>
</>
);
};

const UserRoles: React.FC<{ roles: string[] | undefined }> = (props) => {
if (props.roles === undefined) {
return <></>;
}

return (
<div className="space-x-2">
{props.roles.map((role) => (
<div
key={role}
className="badge border-0 bg-base-300 font-semibold text-base-content/80"
>
{role}
</div>
))}
</div>
);
};

export default UserTable;
34 changes: 34 additions & 0 deletions src/hooks/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as UserServiceModel from "@/models/service/user";
import * as UserService from "@/apis/user";
import { useEffect, useState } from "react";

export const useUserInfoList = () => {
const [userInfoList, setUserInfoList] = useState<UserServiceModel.UserInfo[]>(
[],
);
const [total, setTotal] = useState<number>(0);
const [limit, setLimit] = useState<number>(10);
const [offset, setOffset] = useState<number>(0);

useEffect(() => {
UserService.getUserInfoList(limit, offset).then((res) => {
setUserInfoList(res.list);
setTotal(res.total);
});
}, [limit, offset]);

function getUserInfoList() {
return userInfoList;
}

function getPageCount(limit: number) {
return Math.ceil(total / limit);
}

function setPagenation(limit: number, offset: number) {
setLimit(limit);
setOffset(offset);
}

return { getUserInfoList, getPageCount, setPagenation };
};
14 changes: 14 additions & 0 deletions src/mocks/data/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as UserServiceModel from "@/models/service/user";

export const UserInfoList: UserServiceModel.UserInfo[] = [
{
name: "mock-user",
account: "mock-user",
roles: ["admin", "user"],
},
{
name: "mock-user-2",
account: "mock-user-2",
roles: ["user"],
},
];
4 changes: 2 additions & 2 deletions src/mocks/handlers/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const getProblemInfo = http.get(
},
);

export const getProblemInfoList = http.get("/api/v1/problem", ({ request }) => {
const url = new URL(request.url);
export const getProblemInfoList = http.get("/api/v1/problem", (info) => {
const url = new URL(info.request.url);

const limit = url.searchParams.get("limit");
const offset = url.searchParams.get("offset");
Expand Down
20 changes: 20 additions & 0 deletions src/mocks/handlers/user.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HttpResponse, http } from "msw";
import * as UserMockData from "../data/user";

export const getCurrentUser = http.get("/api/v1/user/current", (info) => {
const authToken = info.cookies["auth-token"];
Expand Down Expand Up @@ -41,3 +42,22 @@ export const postSignOut = http.post("/api/v1/user/logout", async (info) => {
},
});
});

export const getUserInfoList = http.get("/api/v1/user", (info) => {
const url = new URL(info.request.url);
const limit = url.searchParams.get("limit");
const offset = url.searchParams.get("offset");

let userInfoList = UserMockData.UserInfoList.slice(
Number(offset),
Number(offset) + Number(limit),
);

return new Response(
JSON.stringify({
total: UserMockData.UserInfoList.length,
list: userInfoList,
}),
{ status: 200 },
);
});
2 changes: 2 additions & 0 deletions src/mocks/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import {
getCurrentUser,
postPasswordLogin,
postSignOut,
getUserInfoList,
} from "./handlers/user";
import { getRankList } from "./handlers/rank";

const restHandlers = [
getCurrentUser,
postPasswordLogin,
postSignOut,
getUserInfoList,
putProblem,
getProblemInfo,
getProblemInfoList,
Expand Down
43 changes: 43 additions & 0 deletions src/pages/admin/UserList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useEffect } from "react";
import Pagination from "@/components/control/Pagination";
import { useUserInfoList } from "@/hooks/user";
import UserTable from "@/components/display/UserTable";

const countPerPageSelections = [10, 25, 50];

const UserList: React.FC = () => {
const { getUserInfoList, getPageCount, setPagenation } = useUserInfoList();
const [countPerPage, setCountPerPage] = React.useState(
countPerPageSelections[0],
);
const [page, setPage] = React.useState(0);

useEffect(() => {
setPagenation(countPerPage, page * countPerPage);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [countPerPage, page]);

return (
<div className="card card-bordered flex w-full flex-col rounded border-base-content/10 bg-base-100">
<UserTable
data={getUserInfoList()}
showActions={true}
className={
getUserInfoList().length === 1
? ""
: "border-b border-base-content/10"
}
/>
<Pagination
page={page}
pageCount={getPageCount(countPerPage)}
setCountPerPage={setCountPerPage}
countPerPage={countPerPage}
countPerPageSelections={countPerPageSelections}
setPage={setPage}
/>
</div>
);
};

export default UserList;

0 comments on commit 1441506

Please sign in to comment.