diff --git a/src/components/input/FileInput.tsx b/src/components/input/FileInput.tsx
index 05e6c98..b165f7d 100644
--- a/src/components/input/FileInput.tsx
+++ b/src/components/input/FileInput.tsx
@@ -1,5 +1,6 @@
import { BsCloudUpload } from "react-icons/bs";
+
export default function FileInput({
file,
setFile
diff --git a/src/hooks/AccountHooks.ts b/src/hooks/AccountHooks.ts
index b6ce569..611e9b2 100644
--- a/src/hooks/AccountHooks.ts
+++ b/src/hooks/AccountHooks.ts
@@ -11,7 +11,7 @@ export function useGetAccounts(page: number, pageSize: number, searchTerm?: stri
params.append("page", page.toString());
params.append("pageSize", pageSize.toString());
if(searchTerm){
- params.append("search", searchTerm ?? "");
+ params.append("searchTerm", searchTerm ?? "");
}
const response = await api.get(`/account?${params}`);
@@ -28,11 +28,18 @@ export function useGetAccounts(page: number, pageSize: number, searchTerm?: stri
});
}
-export function useGetAccountsCount(){
+export function useGetAccountsCount(searchTerm?: string){
+ const searchParams = new URLSearchParams();
+ if(searchTerm){
+ searchParams.append("searchTerm", searchTerm);
+ }
+
+
return useQuery({
- queryKey: [ "accounts", "count"],
+ queryKey: [ "accounts", "count", searchTerm],
queryFn: async () => {
- const response = await api.get("/account/count");
+
+ const response = await api.get(`/account/count?${searchParams}`);
if(response.status !== 200){
throw new Error("Failed to get accounts count");
diff --git a/src/hooks/GameHooks.ts b/src/hooks/GameHooks.ts
index e580217..1b85db3 100644
--- a/src/hooks/GameHooks.ts
+++ b/src/hooks/GameHooks.ts
@@ -11,7 +11,7 @@ export function useGetGames(page: number, pageSize: number, searchTerm?: string)
params.append("page", page.toString());
params.append("pageSize", pageSize.toString());
if(searchTerm){
- params.append("search", searchTerm);
+ params.append("searchTerm", searchTerm);
}
const response = await api.get(`/game?${params}`);
@@ -28,11 +28,17 @@ export function useGetGames(page: number, pageSize: number, searchTerm?: string)
});
}
-export function useGetGamesCount(){
+export function useGetGamesCount(searchTerm?: string){
+ const searchParams = new URLSearchParams();
+ if(searchTerm){
+ searchParams.append("searchTerm", searchTerm);
+ }
+
+
return useQuery({
- queryKey: ["games", "count"],
+ queryKey: ["games", "count", searchTerm],
queryFn: async () => {
- const response = await api.get("/game/count");
+ const response = await api.get(`/game/count?${searchParams}`);
if(response.status !== 200){
throw new Error("Failed to get games count");
diff --git a/src/pages/protected/AdminPage.tsx b/src/pages/protected/AdminPage.tsx
index 200c3d2..9f09480 100644
--- a/src/pages/protected/AdminPage.tsx
+++ b/src/pages/protected/AdminPage.tsx
@@ -1,17 +1,17 @@
import TabGroup, { Tab } from "@/components/tab/TabGroup";
-import AccountsLoader from "@/ui/account/AccountsLoader";
-import GamesLoader from "@/ui/game/GamesLoader";
+import AdminAccountsTab from "@/ui/account/AdminAccountsTab";
+import AdminGamesTab from "@/ui/game/AdminGamesTab";
export default function AdminPage(){
const tabs: Tab[] = [
{
tabHeader: "Accounts",
- tabContent:
+ tabContent:
},
{
tabHeader: "Games",
- tabContent:
+ tabContent:
}
];
diff --git a/src/ui/account/AccountsLoader.tsx b/src/ui/account/AccountsLoader.tsx
index a61f2f9..f7e28a7 100644
--- a/src/ui/account/AccountsLoader.tsx
+++ b/src/ui/account/AccountsLoader.tsx
@@ -1,31 +1,19 @@
-import PrimaryButton from "@/components/button/PrimaryButton";
-import TextInput from "@/components/input/TextInput";
import DangerMessage from "@/components/message/DangerMessage";
-import Pagination from "@/components/pagination/Pagination";
-import { useGetAccounts, useGetAccountsCount } from "@/hooks/AccountHooks";
-import { useEffect, useState } from "react";
+import { useGetAccounts } from "@/hooks/AccountHooks";
import AccountsList from "./AccountsList";
import AccountsListSkeleton from "./AccountsListSkeleton";
-import AccountModal from "./modals/AccountModal";
-export default function AccountsLoader(){
- const [ displayCreateAccountModal, setDisplayCreateAccountModal ] = useState(false);
- const [ page, setPage ] = useState(1);
- const [ totalPages, setTotalPages ] = useState(1);
- const [ searchTerm, setSearchTerm ] = useState("");
- const pageSize = 10;
- const modalId = crypto.randomUUID().replace("-", "");
-
- const accountsQuery = useGetAccounts(page - 1, pageSize);
- const accountsCountQuery = useGetAccountsCount();
-
-
- useEffect(() => {
- if(accountsCountQuery.status === "success"){
- setTotalPages(Math.ceil(accountsCountQuery.data / pageSize));
- }
- }, [ accountsCountQuery ]);
+export function AccountsLoader({
+ page,
+ pageSize,
+ searchTerm
+}:{
+ page: number;
+ pageSize: number;
+ searchTerm?: string;
+}){
+ const accountsQuery = useGetAccounts(page - 1, pageSize, searchTerm);
if(accountsQuery.status === "pending"){
@@ -36,60 +24,9 @@ export default function AccountsLoader(){
}
else{
return (
- <>
-
-
-
-
- {/* Add Account Button */}
-
-
setDisplayCreateAccountModal(true)}
- >
- Create Account
-
-
setDisplayCreateAccountModal(false)}
- account={undefined}
- />
-
- {/* Account Search Box */}
-
-
- setSearchTerm(e.target.value)}
- placeholder="Search"
- />
-
-
-
- {/* Account List */}
-
- {/* Pagination */}
-
- >
+
);
}
}
diff --git a/src/ui/account/AdminAccountsTab.tsx b/src/ui/account/AdminAccountsTab.tsx
new file mode 100644
index 0000000..aae09e4
--- /dev/null
+++ b/src/ui/account/AdminAccountsTab.tsx
@@ -0,0 +1,99 @@
+import PrimaryButton from "@/components/button/PrimaryButton";
+import TextInput from "@/components/input/TextInput";
+import Pagination from "@/components/pagination/Pagination";
+import { useGetAccountsCount } from "@/hooks/AccountHooks";
+import { useEffect, useState } from "react";
+import { useDebouncedCallback } from "use-debounce";
+import { AccountsLoader } from "./AccountsLoader";
+import AccountModal from "./modals/AccountModal";
+
+
+export default function AdminAccountsTab(){
+ const [ displayCreateAccountModal, setDisplayCreateAccountModal ] = useState(false);
+ const [ page, setPage ] = useState(1);
+ const [ totalPages, setTotalPages ] = useState(1);
+ const [ searchTerm, setSearchTerm ] = useState("");
+ const [ sentSearchTerm, setSentSearchTerm ] = useState();
+ const pageSize = 10;
+ const modalId = crypto.randomUUID().replace("-", "");
+
+
+ const accountsCountQuery = useGetAccountsCount(sentSearchTerm);
+
+
+ const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => {
+ setSentSearchTerm(newSearchTerm.length ? newSearchTerm : undefined);
+ }, 1000);
+
+
+ useEffect(() => {
+ updateSearchTerm(searchTerm ?? "");
+ }, [ searchTerm, updateSearchTerm ]);
+
+
+ useEffect(() => {
+ if(accountsCountQuery.status === "success"){
+ setTotalPages(Math.ceil(accountsCountQuery.data / pageSize));
+ }
+ }, [ accountsCountQuery ]);
+
+
+ return (
+ <>
+
+
+
+
+ {/* Add Account Button */}
+
+
setDisplayCreateAccountModal(true)}
+ >
+ Create Account
+
+
setDisplayCreateAccountModal(false)}
+ account={undefined}
+ />
+
+ {/* Account Search Box */}
+
+
+ setSearchTerm(e.target.value)}
+ placeholder="Search"
+ />
+
+
+
+ {/* Account List */}
+
+ {/* Pagination */}
+
+ >
+ );
+}
diff --git a/src/ui/game/AdminGamesTab.tsx b/src/ui/game/AdminGamesTab.tsx
new file mode 100644
index 0000000..73fb15a
--- /dev/null
+++ b/src/ui/game/AdminGamesTab.tsx
@@ -0,0 +1,98 @@
+import PrimaryButton from "@/components/button/PrimaryButton";
+import TextInput from "@/components/input/TextInput";
+import Pagination from "@/components/pagination/Pagination";
+import { useGetGamesCount } from "@/hooks/GameHooks";
+import { useEffect, useState } from "react";
+import { useDebouncedCallback } from "use-debounce";
+import { GamesLoader } from "./GamesLoader";
+import GameModal from "./modals/GameModal";
+
+
+export default function AdminGamesTab(){
+ const [ displayCreateGameModal, setDisplayCreateGameModal ] = useState(false);
+ const [ page, setPage ] = useState(1);
+ const [ totalPages, setTotalPages ] = useState(1);
+ const [ searchTerm, setSearchTerm ] = useState("");
+ const [ sentSearchTerm, setSentSearchTerm ] = useState();
+ const pageSize = 10;
+ const modalId = crypto.randomUUID().replace("-", "");
+
+ const gamesCountQuery = useGetGamesCount(sentSearchTerm);
+
+
+ const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => {
+ setSentSearchTerm(newSearchTerm.length ? newSearchTerm : undefined);
+ }, 1000);
+
+
+ useEffect(() => {
+ updateSearchTerm(searchTerm ?? "");
+ }, [ searchTerm, updateSearchTerm ]);
+
+
+ useEffect(() => {
+ if(gamesCountQuery.status === "success"){
+ setTotalPages(Math.ceil(gamesCountQuery.data / pageSize));
+ }
+ }, [ gamesCountQuery ]);
+
+
+ return (
+ <>
+
+
+
+
+ {/* Add Game Button */}
+
+
setDisplayCreateGameModal(true)}
+ >
+ Create Game
+
+
setDisplayCreateGameModal(false)}
+ game={undefined}
+ />
+
+ {/* Game Search Box */}
+
+
+ setSearchTerm(e.target.value)}
+ placeholder="Search"
+ />
+
+
+
+ {/* Game List */}
+
+ {/* Pagination */}
+
+ >
+ );
+}
diff --git a/src/ui/game/GamesListSkeleton.tsx b/src/ui/game/GamesListSkeleton.tsx
index f07251e..06fb1cf 100644
--- a/src/ui/game/GamesListSkeleton.tsx
+++ b/src/ui/game/GamesListSkeleton.tsx
@@ -1,8 +1,70 @@
+import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
+import Table from "@/components/table/Table";
+import { elementBg } from "@/util/SkeletonUtil";
+import React from "react";
+import GameAdminButtons from "./GameAdminButtons";
+
export default function GamesListSkeleton(){
- //TODO:
- return (
+ const headElements: React.ReactNode[] = [
- Game List Skeleton
+ Icon
+
,
+
+ Name
+
,
+
+ Actions
+ ];
+ const bodyElements: React.ReactNode[][] = [
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ GameSkeleton(),
+ ];
+
+
+ return (
+
);
}
+
+function GameSkeleton(): React.ReactNode[]{
+ const buttonsProps = {
+ buttonProps: {
+ variant: "ghost" as ButtonVariant,
+ size: "md" as ButtonSizeType,
+ shape: "square" as ButtonShape,
+ disabled: true
+ },
+ showEditGameModal: () => {},
+ showDeleteGameModal: () => {}
+ }
+ const elements: React.ReactNode[] = [
+ ,
+ ,
+
+ ];
+
+ return elements;
+}
diff --git a/src/ui/game/GamesLoader.tsx b/src/ui/game/GamesLoader.tsx
index c1c346f..de47fd2 100644
--- a/src/ui/game/GamesLoader.tsx
+++ b/src/ui/game/GamesLoader.tsx
@@ -1,31 +1,19 @@
-import PrimaryButton from "@/components/button/PrimaryButton";
-import TextInput from "@/components/input/TextInput";
import DangerMessage from "@/components/message/DangerMessage";
-import Pagination from "@/components/pagination/Pagination";
-import { useGetGames, useGetGamesCount } from "@/hooks/GameHooks";
-import { useEffect, useState } from "react";
+import { useGetGames } from "@/hooks/GameHooks";
import GamesList from "./GamesList";
import GamesListSkeleton from "./GamesListSkeleton";
-import GameModal from "./modals/GameModal";
-export default function GamesLoader(){
- const [ displayCreateGameModal, setDisplayCreateGameModal ] = useState(false);
- const [ page, setPage ] = useState(1);
- const [ totalPages, setTotalPages ] = useState(1);
- const [ searchTerm, setSearchTerm ] = useState("");
- const pageSize = 10;
- const modalId = crypto.randomUUID().replace("-", "");
-
- const gamesQuery = useGetGames(page - 1, pageSize);
- const gamesCountQuery = useGetGamesCount();
-
-
- useEffect(() => {
- if(gamesCountQuery.status === "success"){
- setTotalPages(Math.ceil(gamesCountQuery.data / pageSize));
- }
- }, [ gamesCountQuery ]);
+export function GamesLoader({
+ page,
+ pageSize,
+ searchTerm
+}:{
+ page: number;
+ pageSize: number;
+ searchTerm?: string;
+}){
+ const gamesQuery = useGetGames(page - 1, pageSize, searchTerm);
if(gamesQuery.status === "pending"){
@@ -36,60 +24,9 @@ export default function GamesLoader(){
}
else{
return (
- <>
-
-
-
-
- {/* Add Game Button */}
-
-
setDisplayCreateGameModal(true)}
- >
- Create Game
-
-
setDisplayCreateGameModal(false)}
- game={undefined}
- />
-
- {/* Game Search Box */}
-
-
- setSearchTerm(e.target.value)}
- placeholder="Search"
- />
-
-
-
- {/* Game List */}
-
- {/* Pagination */}
-
- >
+
);
}
}