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 */} -
- -
- + ); } }