From b763a1c7bd8b1dfd51917b7f9151754526a028f0 Mon Sep 17 00:00:00 2001 From: Mattrixwv Date: Sat, 8 Mar 2025 13:26:39 -0500 Subject: [PATCH] People tab working --- .../gameClass/GameClassSelector.tsx | 73 +++++++++ .../PersonCharacterDisplay.tsx | 49 +++++++ .../personCharacter/RatingSelector.tsx | 43 ++++++ src/hooks/PersonCharacterHooks.ts | 88 +++++++++++ src/hooks/PersonHooks.ts | 138 ++++++++++++++++++ src/interface/Person.ts | 6 + src/interface/PersonCharacter.ts | 8 + src/pages/protected/RaidGroupPage.tsx | 5 + src/ui/account/AdminAccountsTab.tsx | 4 +- src/ui/calendar/RaidGroupHeader.tsx | 6 +- src/ui/game/GameHeader.tsx | 6 +- src/ui/game/GamesList.tsx | 6 +- src/ui/gameClass/GameClassList.tsx | 6 +- src/ui/person/PersonAdminButtons.tsx | 51 +++++++ src/ui/person/PersonList.tsx | 122 ++++++++++++++++ src/ui/person/PersonListSkeleton.tsx | 7 + src/ui/person/PersonLoader.tsx | 36 +++++ src/ui/person/PersonTab.tsx | 106 ++++++++++++++ src/ui/person/modals/DeletePersonModal.tsx | 65 +++++++++ src/ui/person/modals/PersonModal.tsx | 110 ++++++++++++++ .../modal/PersonCharacterModal.tsx | 131 +++++++++++++++++ src/ui/raidGroup/RaidGroupsList.tsx | 6 +- 22 files changed, 1050 insertions(+), 22 deletions(-) create mode 100644 src/components/gameClass/GameClassSelector.tsx create mode 100644 src/components/personCharacter/PersonCharacterDisplay.tsx create mode 100644 src/components/personCharacter/RatingSelector.tsx create mode 100644 src/hooks/PersonCharacterHooks.ts create mode 100644 src/hooks/PersonHooks.ts create mode 100644 src/interface/Person.ts create mode 100644 src/interface/PersonCharacter.ts create mode 100644 src/ui/person/PersonAdminButtons.tsx create mode 100644 src/ui/person/PersonList.tsx create mode 100644 src/ui/person/PersonListSkeleton.tsx create mode 100644 src/ui/person/PersonLoader.tsx create mode 100644 src/ui/person/PersonTab.tsx create mode 100644 src/ui/person/modals/DeletePersonModal.tsx create mode 100644 src/ui/person/modals/PersonModal.tsx create mode 100644 src/ui/personCharacter/modal/PersonCharacterModal.tsx diff --git a/src/components/gameClass/GameClassSelector.tsx b/src/components/gameClass/GameClassSelector.tsx new file mode 100644 index 0000000..2371b35 --- /dev/null +++ b/src/components/gameClass/GameClassSelector.tsx @@ -0,0 +1,73 @@ +import { useGetGameClasses } from "@/hooks/GameClassHooks"; +import { useEffect, useState } from "react"; +import DangerMessage from "../message/DangerMessage"; + + +export function GameClassSelector({ + gameId, + gameClassId, + onChange +}:{ + gameId: string; + gameClassId?: string; + onChange?: (gameClassId?: string) => void; +}){ + const [ selectedGameClassId, setSelectedGameClassId ] = useState(gameClassId); + const selectorId = crypto.randomUUID().replaceAll("-", ""); + + + const gameClassesQuery = useGetGameClasses(gameId, 0, 100, undefined); + + + useEffect(() => { + onChange?.(selectedGameClassId); + }, [ selectedGameClassId, onChange ]); + + + if(gameClassesQuery.status === "pending"){ + return
Loading...
+ } + else if(gameClassesQuery.status === "error"){ + return Error loading Game Classes: {gameClassesQuery.error.message} + } + else{ + return ( +
+ { + gameClassesQuery.data.map((gameClass) => ( +
+ setSelectedGameClassId(e.target.value)} + /> + +
+ )) + } +
+ ); + } +} diff --git a/src/components/personCharacter/PersonCharacterDisplay.tsx b/src/components/personCharacter/PersonCharacterDisplay.tsx new file mode 100644 index 0000000..215c288 --- /dev/null +++ b/src/components/personCharacter/PersonCharacterDisplay.tsx @@ -0,0 +1,49 @@ +import { useGetPersonCharactersByPersonId } from "@/hooks/PersonCharacterHooks"; +import DangerMessage from "../message/DangerMessage"; + + +export default function PersonCharacterDisplay({ + personId, + raidGroupId +}:{ + personId: string; + raidGroupId: string; +}){ + const personCharacterQuery = useGetPersonCharactersByPersonId(personId, raidGroupId); + + + if(personCharacterQuery.status === "pending"){ + return (
Loading...
); + } + else if(personCharacterQuery.status === "error"){ + return (Error loading characters: {personCharacterQuery.error.message}); + } + else{ + return ( +
+ { + personCharacterQuery.data.map((personCharacter) => { + return ( +
+ + {personCharacter.characterName} +
+ ); + }) + } + { + personCharacterQuery.data.length === 0 && +
No characters
+ } +
+ ); + } +} diff --git a/src/components/personCharacter/RatingSelector.tsx b/src/components/personCharacter/RatingSelector.tsx new file mode 100644 index 0000000..bc2cab8 --- /dev/null +++ b/src/components/personCharacter/RatingSelector.tsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; + +export default function RatingSelector({ + rating, + onChange +}:{ + rating?: number; + onChange?: (rating?: number) => void; +}){ + const ratings = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + const [ currentRating, setCurrentRating ] = useState(rating); + + + useEffect(() => { + setCurrentRating(rating); + }, [ rating, setCurrentRating ]); + + useEffect(() => { + onChange?.(currentRating); + }, [ currentRating, onChange ]); + + + return ( +
+ +
+ ); +} diff --git a/src/hooks/PersonCharacterHooks.ts b/src/hooks/PersonCharacterHooks.ts new file mode 100644 index 0000000..76f5097 --- /dev/null +++ b/src/hooks/PersonCharacterHooks.ts @@ -0,0 +1,88 @@ +import { PersonCharacter } from "@/interface/PersonCharacter"; +import { api } from "@/util/AxiosUtil"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + + +export function useGetPersonCharactersByPersonId(personId: string, raidGroupId: string){ + return useQuery({ + queryKey: ["personCharacters", personId], + queryFn: async () => { + const response = await api.get(`/raidGroup/${raidGroupId}/person/${personId}/character`); + + if(response.status !== 200){ + throw new Error("Failed to get person characters"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + + return response.data as PersonCharacter[]; + } + }); +} + +export function useCreatePersonCharacter(raidGroupId: string, personId: string){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["createPersonCharacter"], + mutationFn: async (personCharacter: PersonCharacter) => { + const response = await api.post(`/raidGroup/${raidGroupId}/person/${personId}/character`, personCharacter); + + if(response.status !== 200){ + throw new Error("Failed to create person character"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["personCharacters"] }); + } + }); +} + +export function useUpdatePersonCharacter(raidGroupId: string, personId: string){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["updatePersonCharacter"], + mutationFn: async (personCharacter: PersonCharacter) => { + const response = await api.put(`/raidGroup/${raidGroupId}/person/${personId}/character/${personCharacter.personCharacterId}`, personCharacter); + + if(response.status !== 200){ + throw new Error("Failed to update person character"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["personCharacters"] }); + } + }); +} + +export function useDeletePersonCharacter(raidGroupId: string, personId: string){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["deletePersonCharacter"], + mutationFn: async (personCharacterId: string) => { + const response = await api.delete(`/raidGroup/${raidGroupId}/person/${personId}/character/${personCharacterId}`); + + if(response.status !== 200){ + throw new Error("Failed to delete person character"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["personCharacters"] }); + } + }); +} diff --git a/src/hooks/PersonHooks.ts b/src/hooks/PersonHooks.ts new file mode 100644 index 0000000..cfbd177 --- /dev/null +++ b/src/hooks/PersonHooks.ts @@ -0,0 +1,138 @@ +import { Person } from "@/interface/Person"; +import { api } from "@/util/AxiosUtil"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + + +export function useGetPerson(raidGroupId: string, personId: string, disabled: boolean){ + return useQuery({ + queryKey: ["people", raidGroupId, personId], + queryFn: async () => { + const response = await api.get(`/raidGroup/${raidGroupId}/person/${personId}`); + + if(response.status !== 200){ + throw new Error("Failed to get person"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + + return response.data as Person; + }, + enabled: !disabled + }); +} + +export function useGetPeopleByRaidGroup(raidGroupId: string, page: number, pageSize: number, searchTerm?: string){ + return useQuery({ + queryKey: ["people", raidGroupId, { page, pageSize, searchTerm }], + queryFn: async () => { + const searchParams = new URLSearchParams(); + searchParams.append("page", page.toString()); + searchParams.append("pageSize", pageSize.toString()); + if(searchTerm){ + searchParams.append("searchTerm", searchTerm); + } + + const response = await api.get(`/raidGroup/${raidGroupId}/person?${searchParams}`); + + if(response.status !== 200){ + throw new Error("Failed to get people"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + + return response.data as Person[]; + } + }); +} + +export function useGetPeopleByRaidGroupCount(raidGroupId: string, searchTerm?: string){ + const searchParams = new URLSearchParams(); + if(searchTerm){ + searchParams.append("searchTerm", searchTerm); + } + + + return useQuery({ + queryKey: ["people", raidGroupId, "count", searchTerm], + queryFn: async () => { + const response = await api.get(`/raidGroup/${raidGroupId}/person/count?${searchParams}`); + + if(response.status !== 200){ + throw new Error("Failed to get people count"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + + return response.data.count as number; + } + }); +} + +export function useCreatePerson(){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["createPerson"], + mutationFn: async ({raidGroupId, personName, discordId}:{raidGroupId: string; personName: string; discordId?: string;}) => { + const response = await api.post(`/raidGroup/${raidGroupId}/person`, {raidGroupId, personName, discordId}); + + if(response.status !== 200){ + throw new Error("Failed to create person"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["people"] }); + } + }); +} + +export function useUpdatePerson(){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["updatePerson"], + mutationFn: async (person: Person) => { + const response = await api.put(`/raidGroup/${person.raidGroupId}/person/${person.personId}`, person); + + if(response.status !== 200){ + throw new Error("Failed to update person"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["people"] }); + } + }); +} + +export function useDeletePerson(){ + const queryClient = useQueryClient(); + + + return useMutation({ + mutationKey: ["deletePerson"], + mutationFn: async ({raidGroupId, personId}:{raidGroupId: string; personId: string;}) => { + const response = await api.delete(`/raidGroup/${raidGroupId}/person/${personId}`); + + if(response.status !== 200){ + throw new Error("Failed to delete person"); + } + else if(response.data.errors){ + throw new Error(response.data.errors.join(", ")); + } + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["people"] }); + } + }); +} diff --git a/src/interface/Person.ts b/src/interface/Person.ts new file mode 100644 index 0000000..5ba607b --- /dev/null +++ b/src/interface/Person.ts @@ -0,0 +1,6 @@ +export interface Person { + personId?: string; + raidGroupId: string; + personName: string; + discordId?: string; +} diff --git a/src/interface/PersonCharacter.ts b/src/interface/PersonCharacter.ts new file mode 100644 index 0000000..433875d --- /dev/null +++ b/src/interface/PersonCharacter.ts @@ -0,0 +1,8 @@ +export interface PersonCharacter { + personCharacterId?: string; + personId: string; + gameClassId: string; + characterName: string; + characterRating?: number; + characterComments?: string; +} diff --git a/src/pages/protected/RaidGroupPage.tsx b/src/pages/protected/RaidGroupPage.tsx index 9d611b8..390370a 100644 --- a/src/pages/protected/RaidGroupPage.tsx +++ b/src/pages/protected/RaidGroupPage.tsx @@ -3,6 +3,7 @@ import { useGetRaidGroup } from "@/hooks/RaidGroupHooks"; import { RaidGroup } from "@/interface/RaidGroup"; import RaidGroupCalendarDisplay from "@/ui/calendar/RaidGroupCalendarDisplay"; import RaidGroupHeader from "@/ui/calendar/RaidGroupHeader"; +import PersonTab from "@/ui/person/PersonTab"; import { useEffect, useState } from "react"; import { Navigate, useParams } from "react-router"; @@ -40,6 +41,10 @@ export default function RaidGroupPage(){ { tabHeader: "Calendar", tabContent: + }, + { + tabHeader: "People", + tabContent: } ]; diff --git a/src/ui/account/AdminAccountsTab.tsx b/src/ui/account/AdminAccountsTab.tsx index 676d4ad..5680d22 100644 --- a/src/ui/account/AdminAccountsTab.tsx +++ b/src/ui/account/AdminAccountsTab.tsx @@ -17,7 +17,7 @@ export default function AdminAccountsTab(){ const pageSize = 10; const modalId = crypto.randomUUID().replaceAll("-", ""); - + const accountsCountQuery = useGetAccountsCount(sentSearchTerm); @@ -53,7 +53,7 @@ export default function AdminAccountsTab(){ className="flex flex-row items-center justify-center w-full" > setDisplayCreateAccountModal(true)} > Create Account diff --git a/src/ui/calendar/RaidGroupHeader.tsx b/src/ui/calendar/RaidGroupHeader.tsx index ef78281..7a413f3 100644 --- a/src/ui/calendar/RaidGroupHeader.tsx +++ b/src/ui/calendar/RaidGroupHeader.tsx @@ -31,10 +31,8 @@ export default function RaidGroupHeader({ { raidGroup.raidGroupIcon && } {raidGroup.raidGroupName} diff --git a/src/ui/game/GameHeader.tsx b/src/ui/game/GameHeader.tsx index 534e84d..d475e1d 100644 --- a/src/ui/game/GameHeader.tsx +++ b/src/ui/game/GameHeader.tsx @@ -31,10 +31,8 @@ export default function GameHeader({ { game.gameIcon && } {game.gameName} diff --git a/src/ui/game/GamesList.tsx b/src/ui/game/GamesList.tsx index ff3028b..9f62210 100644 --- a/src/ui/game/GamesList.tsx +++ b/src/ui/game/GamesList.tsx @@ -47,10 +47,8 @@ export default function GamesList({ className="absolute -my-4" > } diff --git a/src/ui/gameClass/GameClassList.tsx b/src/ui/gameClass/GameClassList.tsx index 5f26a07..c8bc868 100644 --- a/src/ui/gameClass/GameClassList.tsx +++ b/src/ui/gameClass/GameClassList.tsx @@ -43,13 +43,11 @@ export default function GameClassList({ { gameClass.gameClassIcon &&
} diff --git a/src/ui/person/PersonAdminButtons.tsx b/src/ui/person/PersonAdminButtons.tsx new file mode 100644 index 0000000..3580ae1 --- /dev/null +++ b/src/ui/person/PersonAdminButtons.tsx @@ -0,0 +1,51 @@ +import { ButtonProps } from "@/components/button/Button"; +import DangerButton from "@/components/button/DangerButton"; +import PrimaryButton from "@/components/button/PrimaryButton"; +import SuccessButton from "@/components/button/SuccessButton"; +import { BsPencilFill, BsPlusLg, BsTrash3 } from "react-icons/bs"; + + +export default function PersonAdminButtons({ + buttonProps, + showUpdatePersonModal, + showDeletePersonModal, + showCreatePersonCharacterModal +}:{ + buttonProps: ButtonProps; + showUpdatePersonModal: () => void; + showDeletePersonModal: () => void; + showCreatePersonCharacterModal: () => void; +}){ + return ( +
+ + + + + + + + + +
+ ); +} diff --git a/src/ui/person/PersonList.tsx b/src/ui/person/PersonList.tsx new file mode 100644 index 0000000..a47cf5a --- /dev/null +++ b/src/ui/person/PersonList.tsx @@ -0,0 +1,122 @@ +import { ButtonProps } from "@/components/button/Button"; +import PersonCharacterDisplay from "@/components/personCharacter/PersonCharacterDisplay"; +import Table from "@/components/table/Table"; +import { Person } from "@/interface/Person"; +import { RaidGroup } from "@/interface/RaidGroup"; +import { useState } from "react"; +import PersonCharacterModal from "../personCharacter/modal/PersonCharacterModal"; +import DeletePersonModal from "./modals/DeletePersonModal"; +import PersonModal from "./modals/PersonModal"; +import PersonAdminButtons from "./PersonAdminButtons"; + + +export default function PersonList({ + people, + raidGroup +}:{ + people: Person[]; + raidGroup: RaidGroup; +}){ + const [ selectedPerson, setSelectedPerson ] = useState(); + const [ displayUpdatePersonModal, setDisplayUpdatePersonModal ] = useState(false); + const [ displayDeletePersonModal, setDisplayDeletePersonModal ] = useState(false); + const [ displayCreatePersonCharacterModal, setDisplayCreatePersonCharacterModal ] = useState(false); + + + const buttonProps: ButtonProps = { + variant: "ghost", + size: "md", + shape: "square" + }; + + + const headElements: React.ReactNode[] = [ +
+ Person Name +
, +
+ Discord ID +
, +
+ Characters +
, +
+ Actions +
+ ]; + + const bodyElements: React.ReactNode[][] = people.map((person) => [ +
+ {person.personName} +
, +
+ {person.discordId} +
, +
+ +
, +
+
+   +
+ { + setSelectedPerson(person); + setDisplayUpdatePersonModal(true); + }} + showDeletePersonModal={() => { + setSelectedPerson(person); + setDisplayDeletePersonModal(true); + }} + showCreatePersonCharacterModal={() => { + setSelectedPerson(person); + setDisplayCreatePersonCharacterModal(true); + }} + /> +
+ ]); + + + return ( + <> + + {setDisplayUpdatePersonModal(false); setSelectedPerson(undefined);}} + person={selectedPerson} + raidGroupId={raidGroup.raidGroupId ?? ""} + /> + {setDisplayDeletePersonModal(false); setSelectedPerson(undefined);}} + person={selectedPerson} + raidGroupId={raidGroup.raidGroupId ?? ""} + /> + {setDisplayCreatePersonCharacterModal(false); setSelectedPerson(undefined);}} + personId={selectedPerson?.personId ?? ""} + personCharacter={undefined} + gameId={raidGroup.gameId} + raidGroupId={raidGroup.raidGroupId ?? ""} + /> + + ); +} diff --git a/src/ui/person/PersonListSkeleton.tsx b/src/ui/person/PersonListSkeleton.tsx new file mode 100644 index 0000000..e722147 --- /dev/null +++ b/src/ui/person/PersonListSkeleton.tsx @@ -0,0 +1,7 @@ +export default function PersonListSkeleton(){ + return ( +
+ Person List Skeleton +
+ ); +} diff --git a/src/ui/person/PersonLoader.tsx b/src/ui/person/PersonLoader.tsx new file mode 100644 index 0000000..48ccea2 --- /dev/null +++ b/src/ui/person/PersonLoader.tsx @@ -0,0 +1,36 @@ +import DangerMessage from "@/components/message/DangerMessage"; +import { useGetPeopleByRaidGroup } from "@/hooks/PersonHooks"; +import { RaidGroup } from "@/interface/RaidGroup"; +import PersonList from "./PersonList"; +import PersonListSkeleton from "./PersonListSkeleton"; + + +export default function PersonLoader({ + raidGroup, + page, + pageSize, + searchTerm +}:{ + raidGroup: RaidGroup; + page: number; + pageSize: number; + searchTerm?: string; +}){ + const peopleQuery = useGetPeopleByRaidGroup(raidGroup.raidGroupId ?? "", page - 1, pageSize, searchTerm); + + + if(peopleQuery.status === "pending"){ + return + } + else if(peopleQuery.status === "error"){ + return Error: {peopleQuery.error.message} + } + else{ + return ( + + ); + } +} diff --git a/src/ui/person/PersonTab.tsx b/src/ui/person/PersonTab.tsx new file mode 100644 index 0000000..f472c47 --- /dev/null +++ b/src/ui/person/PersonTab.tsx @@ -0,0 +1,106 @@ +import PrimaryButton from "@/components/button/PrimaryButton"; +import TextInput from "@/components/input/TextInput"; +import Pagination from "@/components/pagination/Pagination"; +import { useGetPeopleByRaidGroupCount } from "@/hooks/PersonHooks"; +import { RaidGroup } from "@/interface/RaidGroup"; +import { useEffect, useState } from "react"; +import { useDebouncedCallback } from "use-debounce"; +import PersonModal from "./modals/PersonModal"; +import PersonLoader from "./PersonLoader"; + + +export default function PersonTab({ + raidGroup +}:{ + raidGroup: RaidGroup; +}){ + const [ displayCreatePersonModal, setDisplayCreatePersonModal ] = 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().replaceAll("-", ""); + + + const personCountQuery = useGetPeopleByRaidGroupCount(raidGroup?.raidGroupId ?? "", sentSearchTerm); + + + const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => { + setSentSearchTerm(newSearchTerm); + }, 1000); + + + useEffect(() => { + updateSearchTerm(searchTerm ?? ""); + }, [ searchTerm, updateSearchTerm ]); + + + useEffect(() => { + if(personCountQuery.status === "success"){ + setTotalPages(Math.ceil(personCountQuery.data / pageSize)); + } + }, [ personCountQuery ]); + + + return ( + <> +
+
+   +
+ {/* Add Person Button */} +
+ setDisplayCreatePersonModal(true)} + > + Create Person + + setDisplayCreatePersonModal(false)} + person={undefined} + raidGroupId={raidGroup.raidGroupId ?? ""} + /> +
+ {/* Person Search Box */} +
+
+ setSearchTerm(e.target.value)} + placeholder="Search" + /> +
+
+
+ { /* Person List */} + + {/* Pagination */} +
+ +
+ + ); +} diff --git a/src/ui/person/modals/DeletePersonModal.tsx b/src/ui/person/modals/DeletePersonModal.tsx new file mode 100644 index 0000000..76f90ef --- /dev/null +++ b/src/ui/person/modals/DeletePersonModal.tsx @@ -0,0 +1,65 @@ +import DangerButton from "@/components/button/DangerButton"; +import SecondaryButton from "@/components/button/SecondaryButton"; +import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; +import { useDeletePerson } from "@/hooks/PersonHooks"; +import { Person } from "@/interface/Person"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect } from "react"; + + +export default function DeletePersonModal({ + display, + close, + raidGroupId, + person +}:{ + display: boolean; + close: () => void; + raidGroupId: string; + person?: Person; +}){ + const deletePersonMutate = useDeletePerson(); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); + + + const deletePerson = () => { + deletePersonMutate.mutate({raidGroupId, personId: person?.personId ?? ""}); + } + + useEffect(() => { + if(deletePersonMutate.status === "success"){ + deletePersonMutate.reset(); + addSuccessMessage(`Person ${person?.personName} deleted`); + close(); + } + else if(deletePersonMutate.status === "error"){ + deletePersonMutate.reset(); + addErrorMessage(`Error deleting person ${person?.personName}: ${deletePersonMutate.error.message}`); + console.log(deletePersonMutate.error); + } + }) + + + return ( + + + Delete + + + Cancel + + + } + /> + ); +} diff --git a/src/ui/person/modals/PersonModal.tsx b/src/ui/person/modals/PersonModal.tsx new file mode 100644 index 0000000..bd12398 --- /dev/null +++ b/src/ui/person/modals/PersonModal.tsx @@ -0,0 +1,110 @@ +import PrimaryButton from "@/components/button/PrimaryButton"; +import SecondaryButton from "@/components/button/SecondaryButton"; +import TextInput from "@/components/input/TextInput"; +import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; +import { useCreatePerson, useUpdatePerson } from "@/hooks/PersonHooks"; +import { Person } from "@/interface/Person"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect, useState } from "react"; + + +export default function PersonModal({ + display, + close, + person, + raidGroupId +}:{ + display: boolean; + close: () => void; + person: Person | undefined; + raidGroupId: string; +}){ + const [ personName, setPersonName ] = useState(person?.personName ?? ""); + const [ discordId, setDiscordId ] = useState(person?.discordId ?? ""); + const modalId = crypto.randomUUID().replaceAll("-", ""); + + + useEffect(() => { + setPersonName(person?.personName ?? ""); + setDiscordId(person?.discordId ?? ""); + }, [ person, raidGroupId, setPersonName, setDiscordId ]); + + + const createPersonMutate = useCreatePerson(); + const updatePersonMutate = useUpdatePerson(); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); + + + useEffect(() => { + if(createPersonMutate.status === "success"){ + createPersonMutate.reset(); + addSuccessMessage("Person created successfully"); + close(); + } + else if(updatePersonMutate.status === "success"){ + updatePersonMutate.reset(); + addSuccessMessage("Person updated successfully"); + close(); + } + else if(createPersonMutate.status === "error"){ + createPersonMutate.reset(); + addErrorMessage(`Error creating person ${personName}: ${createPersonMutate.error.message}`); + console.log(createPersonMutate.error); + } + else if(updatePersonMutate.status === "error"){ + updatePersonMutate.reset(); + addErrorMessage(`Error updating person ${personName}: ${updatePersonMutate.error.message}`); + console.log(updatePersonMutate.error); + } + }); + + + const updatePerson = () => { + updatePersonMutate.mutate({raidGroupId, personId: person?.personId, personName, discordId} as Person); + } + + const createPerson = () => { + createPersonMutate.mutate({raidGroupId, personName, discordId} as Person); + } + + + return ( + + setPersonName(e.target.value)} + /> + setDiscordId(e.target.value)} + /> + + } + modalFooter={ + <> + + {person ? "Update" : "Create"} + + + Cancel + + + } + /> + ); +} diff --git a/src/ui/personCharacter/modal/PersonCharacterModal.tsx b/src/ui/personCharacter/modal/PersonCharacterModal.tsx new file mode 100644 index 0000000..bc36328 --- /dev/null +++ b/src/ui/personCharacter/modal/PersonCharacterModal.tsx @@ -0,0 +1,131 @@ +import PrimaryButton from "@/components/button/PrimaryButton"; +import SecondaryButton from "@/components/button/SecondaryButton"; +import { GameClassSelector } from "@/components/gameClass/GameClassSelector"; +import TextInput from "@/components/input/TextInput"; +import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; +import RatingSelector from "@/components/personCharacter/RatingSelector"; +import { useCreatePersonCharacter, useUpdatePersonCharacter } from "@/hooks/PersonCharacterHooks"; +import { PersonCharacter } from "@/interface/PersonCharacter"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect, useState } from "react"; + + +export default function PersonCharacterModal({ + display, + close, + personCharacter, + personId, + gameId, + raidGroupId +}:{ + display: boolean; + close: () => void; + personCharacter?: PersonCharacter; + personId: string; + gameId: string; + raidGroupId: string; +}){ + const [ characterName, setCharacterName ] = useState(personCharacter?.characterName ?? ""); + const [ gameClassId, setGameClassId ] = useState(personCharacter?.gameClassId); + const [ characterRating, setCharacterRating ] = useState(); + const [ characterComments, setCharacterComments ] = useState(); + const modalId = crypto.randomUUID().replaceAll("-", ""); + + + useEffect(() => { + if(personCharacter){ + setCharacterName(personCharacter.characterName); + setGameClassId(personCharacter.gameClassId); + setCharacterRating(personCharacter.characterRating); + setCharacterComments(personCharacter.characterComments); + } + }, [ personCharacter, setCharacterName, setGameClassId, setCharacterRating, setCharacterComments ]); + + + const createPersonCharacterMutate = useCreatePersonCharacter(raidGroupId, personId); + const updatePersonCharacterMutate = useUpdatePersonCharacter(raidGroupId, personId); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); + + + useEffect(() => { + if(createPersonCharacterMutate.status === "success"){ + createPersonCharacterMutate.reset(); + addSuccessMessage(`Character ${characterName} created successfully`); + close(); + } + else if(updatePersonCharacterMutate.status === "success"){ + updatePersonCharacterMutate.reset(); + addSuccessMessage(`Character ${characterName} updated successfully`); + close(); + } + else if(createPersonCharacterMutate.status === "error"){ + createPersonCharacterMutate.reset(); + addErrorMessage(`Error creating character ${characterName}: ${createPersonCharacterMutate.error.message}`); + console.log(createPersonCharacterMutate.error); + } + else if(updatePersonCharacterMutate.status === "error"){ + updatePersonCharacterMutate.reset(); + addErrorMessage(`Error updating character ${characterName}: ${updatePersonCharacterMutate.error.message}`); + console.log(updatePersonCharacterMutate.error); + } + }); + + + const createPersonCharacter = () => { + createPersonCharacterMutate.mutate({personId, characterName, gameClassId, characterRating, characterComments} as PersonCharacter); + } + + const updatePersonCharacter = () => { + updatePersonCharacterMutate.mutate({personCharacterId: personCharacter?.personCharacterId, personId, characterName, gameClassId, characterRating, characterComments} as PersonCharacter); + } + + + return ( + + setCharacterName(e.target.value)} + /> + + + setCharacterComments(e.target.value)} + /> + + } + modalFooter={ + <> + + {personCharacter ? "Update" : "Create"} + + + Cancel + + + } + /> + ); +} diff --git a/src/ui/raidGroup/RaidGroupsList.tsx b/src/ui/raidGroup/RaidGroupsList.tsx index 968a50e..f3feab9 100644 --- a/src/ui/raidGroup/RaidGroupsList.tsx +++ b/src/ui/raidGroup/RaidGroupsList.tsx @@ -47,10 +47,8 @@ export default function RaidGroupsList({ className="absolute -my-4" > }