Person page working

This commit is contained in:
2025-03-10 22:55:16 -04:00
parent 5a2c8a8936
commit c9ceeea3b4
21 changed files with 757 additions and 47 deletions

View File

@@ -0,0 +1,32 @@
import { useGetGameClass } from "@/hooks/GameClassHooks";
import DangerMessage from "../message/DangerMessage";
export default function GameClassCellDisplay({
gameClassId
}:{
gameClassId: string;
}){
const gameClassQuery = useGetGameClass(gameClassId);
if(gameClassQuery.status === "pending"){
return <div>Loading...</div>;
}
else if(gameClassQuery.status === "error"){
return <DangerMessage>Error: {gameClassQuery.error.message}</DangerMessage>
}
else{
return (
<div
className="flex flex-row items-center justify-center"
>
<img
className="max-h-14 max-w-14 mr-2 -mt-4"
src={`${import.meta.env.VITE_ICON_URL}/gameClass/${gameClassQuery.data.gameClassIcon}`}
/>
{gameClassQuery.data.gameClassName}
</div>
);
}
}

View File

@@ -22,6 +22,7 @@ export default function RatingSelector({
return ( return (
<div> <div>
<label>
<select <select
onChange={(e) => setCurrentRating(parseInt(e.target.value))} onChange={(e) => setCurrentRating(parseInt(e.target.value))}
value={currentRating} value={currentRating}
@@ -38,6 +39,8 @@ export default function RatingSelector({
)) ))
} }
</select> </select>
<span>Character Rating</span>
</label>
</div> </div>
); );
} }

View File

@@ -25,7 +25,7 @@ export default function TableBody({
> >
<div <div
className={clsx( className={clsx(
"bg-neutral-200 dark:bg-neutral-700", "bg-neutral-200 dark:bg-neutral-700 h-14",
{ {
"py-4 my-2": elementIndex < row.length - 1, "py-4 my-2": elementIndex < row.length - 1,
"rounded-l pl-2": elementIndex === 0, "rounded-l pl-2": elementIndex === 0,

View File

@@ -3,6 +3,25 @@ import { api } from "@/util/AxiosUtil";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export function useGetGameClass(gameClassId: string){
return useQuery({
queryKey: ["gameClasses", gameClassId],
queryFn: async () => {
const response = await api.get(`/gameClass/${gameClassId}`);
if(response.status !== 200){
throw new Error("Failed to get game class");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data as GameClass;
},
enabled: !!gameClassId
})
}
export function useGetGameClasses(gameId: string, page: number, pageSize: number, searchTerm?: string){ export function useGetGameClasses(gameId: string, page: number, pageSize: number, searchTerm?: string){
return useQuery({ return useQuery({
queryKey: ["gameClasses", gameId, { page, pageSize, searchTerm }], queryKey: ["gameClasses", gameId, { page, pageSize, searchTerm }],

View File

@@ -21,6 +21,58 @@ export function useGetPersonCharactersByPersonId(personId: string, raidGroupId:
}); });
} }
export function useGetPersonCharactersByPersonIdSearch(personId: string, raidGroupId: string, page: number, pageSize: number, searchTerm?: string){
const params = new URLSearchParams();
params.append("page", page.toString());
params.append("pageSize", pageSize.toString());
if(searchTerm){
params.append("searchTerm", searchTerm);
}
return useQuery({
queryKey: ["personCharacters", personId, { page, pageSize, searchTerm }],
queryFn: async () => {
const response = await api.get(`/raidGroup/${raidGroupId}/person/${personId}/character/page?${params}`);
if(response.status !== 200){
throw new Error("Failed to get person characters");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
console.log("person characters");
console.log(response.data);
return response.data as PersonCharacter[];
}
});
}
export function useGetPersonCharactersCountByPersonIdSearch(personId: string, raidGroupId: string, searchTerm?: string){
const params = new URLSearchParams();
if(searchTerm){
params.append("searchTerm", searchTerm);
}
return useQuery({
queryKey: ["personCharactersCount", personId, { searchTerm }],
queryFn: async () => {
const response = await api.get(`/raidGroup/${raidGroupId}/person/${personId}/character/count?${params}`);
if(response.status !== 200){
throw new Error("Failed to get person characters count");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data.count as number;
}
});
}
export function useCreatePersonCharacter(raidGroupId: string, personId: string){ export function useCreatePersonCharacter(raidGroupId: string, personId: string){
const queryClient = useQueryClient(); const queryClient = useQueryClient();

View File

@@ -1,10 +1,72 @@
import DangerMessage from "@/components/message/DangerMessage";
import { useGetPerson } from "@/hooks/PersonHooks";
import { useGetRaidGroup } from "@/hooks/RaidGroupHooks";
import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup";
import PersonDisplay from "@/ui/person/PersonDisplay";
import { PersonHeader } from "@/ui/person/PersonHeader";
import { useEffect, useState } from "react";
import { useParams } from "react-router";
export default function PersonPage(){ export default function PersonPage(){
//TODO: const { raidGroupId, personId } = useParams();
const [ raidGroup, setRaidGroup ] = useState<RaidGroup>();
const [ person, setPerson ] = useState<Person>();
const raidGroupQuery = useGetRaidGroup(raidGroupId ?? "", false);
const personQuery = useGetPerson(raidGroupId ?? "", personId ?? "", false);
useEffect(() => {
if(raidGroupQuery.status === "success"){
setRaidGroup(raidGroupQuery.data);
}
}, [ raidGroupQuery ]);
useEffect(() => {
if(personQuery.status === "success"){
setPerson(personQuery.data);
}
}, [ personQuery ]);
if(raidGroupQuery.status === "pending" || personQuery.status === "pending"){
return ( return (
<div> <div>Loading...</div>
Person Page
</div>
); );
} }
else if(raidGroupQuery.status === "error"){
return (
<DangerMessage>Error: {raidGroupQuery.error.message}</DangerMessage>
);
}
else if (personQuery.status === "error"){
return (
<DangerMessage>Error: {personQuery.error.message}</DangerMessage>
);
}
else if(raidGroupQuery.status === "success" && raidGroupQuery.data === undefined){
return (
<DangerMessage>Raid Group not found</DangerMessage>
);
}
else if(personQuery.status === "success" && personQuery.data === undefined){
return (
<DangerMessage>Person not found</DangerMessage>
);
}
else if(raidGroup && person){
return (
<main
className="flex flex-col items-center justify-center gap-y-8"
>
<PersonHeader
raidGroup={raidGroup}
person={person}
/>
<PersonDisplay
raidGroup={raidGroup}
person={person}
/>
</main>
);
}
}

View File

@@ -41,7 +41,7 @@ export default function AdminAccountsTab(){
return ( return (
<> <>
<div <div
className="flex flex-row justify-between items-center w-full" className="flex flex-row justify-between items-center w-full mb-8"
> >
<div <div
className="flex flex-row items-center justify-start w-full" className="flex flex-row items-center justify-start w-full"
@@ -53,7 +53,7 @@ export default function AdminAccountsTab(){
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full"
> >
<PrimaryButton <PrimaryButton
className="mb-8 text-nowrap" className="text-nowrap"
onClick={() => setDisplayCreateAccountModal(true)} onClick={() => setDisplayCreateAccountModal(true)}
> >
Create Account Create Account

View File

@@ -46,7 +46,7 @@ export default function ClassGroupsTab({
return ( return (
<> <>
<div <div
className="flex flex-row justify-between items-center w-full" className="flex flex-row justify-between items-center w-full mb-8"
> >
<div <div
className="flex flex-row items-center justify-start w-full" className="flex flex-row items-center justify-start w-full"
@@ -58,7 +58,7 @@ export default function ClassGroupsTab({
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full"
> >
<PrimaryButton <PrimaryButton
className="mb-8 text-nowrap" className="text-nowrap"
onClick={() => setDisplayCreateClassGroupModal(true)} onClick={() => setDisplayCreateClassGroupModal(true)}
> >
Create Class Group Create Class Group

View File

@@ -14,12 +14,14 @@ export default function PersonAdminButtons({
buttonProps: ButtonProps; buttonProps: ButtonProps;
showUpdatePersonModal: () => void; showUpdatePersonModal: () => void;
showDeletePersonModal: () => void; showDeletePersonModal: () => void;
showCreatePersonCharacterModal: () => void; showCreatePersonCharacterModal?: () => void;
}){ }){
return ( return (
<div <div
className="flex flex-row gap-2" className="flex flex-row gap-2"
> >
{
showCreatePersonCharacterModal &&
<SuccessButton <SuccessButton
{...buttonProps} {...buttonProps}
size="sm" size="sm"
@@ -30,6 +32,7 @@ export default function PersonAdminButtons({
strokeWidth={1} strokeWidth={1}
/> />
</SuccessButton> </SuccessButton>
}
<PrimaryButton <PrimaryButton
{...buttonProps} {...buttonProps}
onClick={showUpdatePersonModal} onClick={showUpdatePersonModal}

View File

@@ -0,0 +1,122 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetPersonCharactersCountByPersonIdSearch } from "@/hooks/PersonCharacterHooks";
import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup";
import { useEffect, useState } from "react";
import { BsArrowLeft } from "react-icons/bs";
import { useNavigate } from "react-router";
import { useDebouncedCallback } from "use-debounce";
import PersonCharacterModal from "../personCharacter/modal/PersonCharacterModal";
import PersonCharacterLoader from "../personCharacter/PersonCharacterLoader";
export default function PersonDisplay({
raidGroup,
person
}:{
raidGroup: RaidGroup;
person: Person;
}){
const [ displayCreatePersonCharacterModal, setDisplayCreatePersonCharacterModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
const [ searchTerm, setSearchTerm ] = useState("");
const [ sentSearchTerm, setSentSearchTerm ] = useState<string>();
const pageSize = 10;
const modalId = crypto.randomUUID().replaceAll("-", "");
const navigate = useNavigate();
const characterCountQuery = useGetPersonCharactersCountByPersonIdSearch(person.personId ?? "", raidGroup.raidGroupId ?? "", sentSearchTerm);
const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => {
setSentSearchTerm(newSearchTerm);
}, 1000);
useEffect(() => {
updateSearchTerm(searchTerm ?? "");
}, [ searchTerm, updateSearchTerm ]);
useEffect(() => {
if(characterCountQuery.status === "success"){
setTotalPages(Math.ceil(characterCountQuery.data / pageSize));
}
}, [ characterCountQuery ]);
return (
<>
<div
className="flex flex-row items-center justify-center w-full mb-8"
>
<div
className="flex flex-row items-center justify-start w-full"
>
<PrimaryButton
shape="square"
onClick={() => navigate(`/raidGroup/${raidGroup.raidGroupId}`)}
>
<BsArrowLeft
size={22}
/>
</PrimaryButton>
</div>
{/* Add Character Button */}
<div
className="flex flex-row items-center justify-center w-full"
>
<PrimaryButton
className="text-nowrap"
onClick={() => setDisplayCreatePersonCharacterModal(true)}
>
Create Character
</PrimaryButton>
<PersonCharacterModal
display={displayCreatePersonCharacterModal}
close={() => setDisplayCreatePersonCharacterModal(false)}
personId={person.personId ?? ""}
raidGroupId={raidGroup.raidGroupId ?? ""}
gameId={raidGroup.gameId}
/>
</div>
{/* Character Search Box */}
<div
className="flex flex-row items-center justify-end w-full"
>
<div>
<TextInput
id={`characterSearchBox${modalId}`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
</div>
</div>
</div>
{/* Character List */}
<PersonCharacterLoader
raidGroup={raidGroup}
person={person}
page={page}
pageSize={pageSize}
searchTerm={sentSearchTerm}
/>
{/* Pagination */}
<div
className="my-12"
>
<Pagination
currentPage={page}
totalPages={totalPages}
onChange={setPage}
/>
</div>
</>
);
}

View File

@@ -0,0 +1,57 @@
import { ButtonProps } from "@/components/button/Button";
import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup";
import { useState } from "react";
import PersonAdminButtons from "./PersonAdminButtons";
import DeletePersonModal from "./modals/DeletePersonModal";
import PersonModal from "./modals/PersonModal";
export function PersonHeader({
raidGroup,
person
}:{
raidGroup: RaidGroup;
person: Person;
}){
const [ displayEditPersonModal, setDisplayEditPersonModal ] = useState(false);
const [ displayDeletePersonModal, setDisplayDeletePersonModal ] = useState(false);
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
};
return (
<h1
className="flex flex-col items-center justify-center"
>
<div
className="flex flex-row items-center justify-center text-4xl"
>
{person.personName}
</div>
<div>
<PersonAdminButtons
buttonProps={buttonProps}
showUpdatePersonModal={() => setDisplayEditPersonModal(true)}
showDeletePersonModal={() => setDisplayDeletePersonModal(true)}
/>
</div>
<PersonModal
display={displayEditPersonModal}
close={() => setDisplayEditPersonModal(false)}
person={person}
raidGroupId={raidGroup.raidGroupId ?? ""}
/>
<DeletePersonModal
display={displayDeletePersonModal}
close={() => setDisplayDeletePersonModal(false)}
person={person}
raidGroupId={raidGroup.raidGroupId ?? ""}
/>
</h1>
);
}

View File

@@ -4,6 +4,7 @@ import Table from "@/components/table/Table";
import { Person } from "@/interface/Person"; import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup"; import { RaidGroup } from "@/interface/RaidGroup";
import { useState } from "react"; import { useState } from "react";
import { Link } from "react-router";
import PersonCharacterModal from "../personCharacter/modal/PersonCharacterModal"; import PersonCharacterModal from "../personCharacter/modal/PersonCharacterModal";
import DeletePersonModal from "./modals/DeletePersonModal"; import DeletePersonModal from "./modals/DeletePersonModal";
import PersonModal from "./modals/PersonModal"; import PersonModal from "./modals/PersonModal";
@@ -52,18 +53,22 @@ export default function PersonList({
]; ];
const bodyElements: React.ReactNode[][] = people.map((person) => [ const bodyElements: React.ReactNode[][] = people.map((person) => [
<div> <Link
to={`/raidGroup/${raidGroup.raidGroupId}/person/${person.personId}`}
>
{person.personName} {person.personName}
</div>, </Link>,
<div> <div>
{person.discordId} {person.discordId}
</div>, </div>,
<div> <Link
to={`/raidGroup/${raidGroup.raidGroupId}/person/${person.personId}`}
>
<PersonCharacterDisplay <PersonCharacterDisplay
personId={person.personId ?? ""} personId={person.personId ?? ""}
raidGroupId={raidGroup.raidGroupId ?? ""} raidGroupId={raidGroup.raidGroupId ?? ""}
/> />
</div>, </Link>,
<div <div
className="flex flex-row items-center justify-center gap-2 pl-16" className="flex flex-row items-center justify-center gap-2 pl-16"
> >

View File

@@ -4,6 +4,7 @@ import { elementBg } from "@/util/SkeletonUtil";
import React from "react"; import React from "react";
import PersonAdminButtons from "./PersonAdminButtons"; import PersonAdminButtons from "./PersonAdminButtons";
export default function PersonListSkeleton(){ export default function PersonListSkeleton(){
const headElements: React.ReactNode[] = [ const headElements: React.ReactNode[] = [
<div> <div>

View File

@@ -46,7 +46,7 @@ export default function PersonTab({
return ( return (
<> <>
<div <div
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full mb-8"
> >
<div <div
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full"
@@ -58,7 +58,7 @@ export default function PersonTab({
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full"
> >
<PrimaryButton <PrimaryButton
className="mb-8 text-nowrap" className="text-nowrap"
onClick={() => setDisplayCreatePersonModal(true)} onClick={() => setDisplayCreatePersonModal(true)}
> >
Create Person Create Person

View File

@@ -37,7 +37,7 @@ export default function DeletePersonModal({
addErrorMessage(`Error deleting person ${person?.personName}: ${deletePersonMutate.error.message}`); addErrorMessage(`Error deleting person ${person?.personName}: ${deletePersonMutate.error.message}`);
console.log(deletePersonMutate.error); console.log(deletePersonMutate.error);
} }
}) });
return ( return (

View File

@@ -0,0 +1,38 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
export default function PersonCharacterAdminButtons({
buttonProps,
showUpdatePersonCharacterModal,
showDeletePersonCharacterModal
}:{
buttonProps: ButtonProps;
showUpdatePersonCharacterModal: () => void;
showDeletePersonCharacterModal: () => void;
}){
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
onClick={showUpdatePersonCharacterModal}
>
<BsPencilFill
size={22}
/>
</PrimaryButton>
<DangerButton
{...buttonProps}
onClick={showDeletePersonCharacterModal}
>
<BsTrash3
size={22}
/>
</DangerButton>
</div>
);
}

View File

@@ -0,0 +1,125 @@
import { ButtonProps } from "@/components/button/Button";
import GameClassCellDisplay from "@/components/gameClass/GameClassCellDisplay";
import Table from "@/components/table/Table";
import { Person } from "@/interface/Person";
import { PersonCharacter } from "@/interface/PersonCharacter";
import { RaidGroup } from "@/interface/RaidGroup";
import { useState } from "react";
import DeletePersonCharacterModal from "./modal/DeletePersonCharacterModal";
import PersonCharacterModal from "./modal/PersonCharacterModal";
import PersonCharacterAdminButtons from "./PersonCharacterAdminButtons";
export default function PersonCharacterList({
personCharacters,
person,
raidGroup
}:{
personCharacters: PersonCharacter[];
person: Person;
raidGroup: RaidGroup;
}){
const [ selectedCharacter, setSelectedCharacter ] = useState<PersonCharacter>();
const [ displayUpdatePersonCharacterModal, setDisplayUpdatePersonCharacterModal ] = useState(false);
const [ displayDeletePersonCharacterModal, setDisplayDeletePersonCharacterModal ] = useState(false);
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
};
const headElements: React.ReactNode[] = [
<div
className="text-nowrap"
>
Character Name
</div>,
<div>
Class
</div>,
<div>
Rating
</div>,
<div>
Comments
</div>,
<div
className="pl-16"
>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = personCharacters.map((personCharacter) => [
<div
className="text-nowrap"
>
{personCharacter.characterName}
</div>,
<div>
<GameClassCellDisplay
gameClassId={personCharacter.gameClassId}
/>
</div>,
<div>
{
personCharacter.characterRating && personCharacter.characterRating > 0 ?
personCharacter.characterRating :
<>&nbsp;</>
}
</div>,
<div>
{
personCharacter.characterComments || <>&nbsp;</>
}
</div>,
<div
className="flex flex-row flex-nowrap items-center justify-center gap-2 pl-16"
>
<div
className="py-4 border-l border-neutral-500"
>
&nbsp;
</div>
<PersonCharacterAdminButtons
buttonProps={buttonProps}
showUpdatePersonCharacterModal={() => {
setSelectedCharacter(personCharacter);
setDisplayUpdatePersonCharacterModal(true);
}}
showDeletePersonCharacterModal={() => {
setSelectedCharacter(personCharacter);
setDisplayDeletePersonCharacterModal(true);
}}
/>
</div>
]);
return (
<>
<Table
tableHeadElements={headElements}
tableBodyElements={bodyElements}
/>
<PersonCharacterModal
display={displayUpdatePersonCharacterModal}
close={() => {setDisplayUpdatePersonCharacterModal(false); setSelectedCharacter(undefined);}}
personCharacter={selectedCharacter}
personId={person.personId ?? ""}
gameId={raidGroup.gameId}
raidGroupId={raidGroup.raidGroupId ?? ""}
/>
<DeletePersonCharacterModal
display={displayDeletePersonCharacterModal}
close={() => {setDisplayDeletePersonCharacterModal(false); setSelectedCharacter(undefined);}}
personCharacter={selectedCharacter}
raidGroupId={raidGroup.raidGroupId ?? ""}
personId={person.personId ?? ""}
/>
</>
);
}

View File

@@ -0,0 +1,84 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { elementBg } from "@/util/SkeletonUtil";
import PersonCharacterAdminButtons from "./PersonCharacterAdminButtons";
export default function PersonCharacterListSkeleton(){
const headElements: React.ReactNode[] = [
<div>
Character Name
</div>,
<div>
Class
</div>,
<div>
Rating
</div>,
<div>
Comments
</div>,
<div
className="pl-16"
>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = [
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton(),
PersonCharacterSkeleton()
];
return (
<Table
tableHeadElements={headElements}
tableBodyElements={bodyElements}
/>
);
}
function PersonCharacterSkeleton(): React.ReactNode[]{
const buttonsProps = {
buttonProps: {
variant: "ghost" as ButtonVariant,
size: "md" as ButtonSizeType,
shape: "square" as ButtonShape,
disabled: true
},
showUpdatePersonCharacterModal: () => {},
showDeletePersonCharacterModal: () => {}
};
const elements: React.ReactNode[] = [
<div
className={`h-6 w-96 mr-1 ${elementBg}`}
/>,
<div
className={`h-6 w-72 ml-2 ${elementBg}`}
/>,
<div
className={`h-6 w-32 ${elementBg}`}
/>,
<div
className={`h-6 w-[32rem] ${elementBg}`}
/>,
<div
className={`flex flex-row items-center justify-center gap-2 pl-16`}
>
<div className="py-4 border-l border-neutral-500">&nbsp;</div>
<PersonCharacterAdminButtons {...buttonsProps}/>
</div>
];
return elements;
}

View File

@@ -0,0 +1,40 @@
import DangerMessage from "@/components/message/DangerMessage";
import { useGetPersonCharactersByPersonIdSearch } from "@/hooks/PersonCharacterHooks";
import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup";
import PersonCharacterList from "./PersonCharacterList";
import PersonCharacterListSkeleton from "./PersonCharacterListSkeleton";
export default function PersonCharacterLoader({
raidGroup,
person,
page,
pageSize,
searchTerm
}:{
raidGroup: RaidGroup;
person: Person;
page: number;
pageSize: number;
searchTerm?: string;
}){
const personCharactersQuery = useGetPersonCharactersByPersonIdSearch(person.personId ?? "", raidGroup.raidGroupId ?? "", page - 1, pageSize, searchTerm);
if(personCharactersQuery.status === "pending"){
<PersonCharacterListSkeleton/>
}
else if(personCharactersQuery.status === "error"){
return <DangerMessage>Error: {personCharactersQuery.error.message}</DangerMessage>
}
else{
return (
<PersonCharacterList
personCharacters={personCharactersQuery.data ?? []}
raidGroup={raidGroup}
person={person}
/>
);
}
}

View File

@@ -0,0 +1,67 @@
import DangerButton from "@/components/button/DangerButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useDeletePersonCharacter } from "@/hooks/PersonCharacterHooks";
import { PersonCharacter } from "@/interface/PersonCharacter";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function DeletePersonCharacterModal({
display,
close,
personCharacter,
raidGroupId,
personId
}:{
display: boolean;
close: () => void;
personCharacter?: PersonCharacter;
raidGroupId: string;
personId: string;
}){
const deletePersonCharacterMutate = useDeletePersonCharacter(raidGroupId, personId);
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const deletePersonCharacter = () => {
deletePersonCharacterMutate.mutate(personCharacter?.personCharacterId ?? "");
}
useEffect(() => {
if(deletePersonCharacterMutate.status === "success"){
deletePersonCharacterMutate.reset();
addSuccessMessage("Person Character Deleted");
close();
}
else if(deletePersonCharacterMutate.status === "error"){
deletePersonCharacterMutate.reset();
addErrorMessage("Failed to delete person character");
console.log(deletePersonCharacterMutate.error);
}
});
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader={`Delete Character ${personCharacter?.characterName}`}
modalBody={`Are you sure you want to delete ${personCharacter?.characterName}`}
modalFooter={
<>
<DangerButton
onClick={deletePersonCharacter}
>
Delete
</DangerButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}

View File

@@ -45,7 +45,7 @@ export default function RaidLayoutTab({
return ( return (
<> <>
<div <div
className="flex flex-row items-center justify-between w-full" className="flex flex-row items-center justify-between w-full mb-8"
> >
<div <div
className="flex flex-row items-center justify-start w-full" className="flex flex-row items-center justify-start w-full"
@@ -57,7 +57,7 @@ export default function RaidLayoutTab({
className="flex flex-row items-center justify-center w-full" className="flex flex-row items-center justify-center w-full"
> >
<PrimaryButton <PrimaryButton
className="mb-8 text-nowrap" className="text-nowrap"
onClick={() => setDisplayCreateRaidLayoutModal(true)} onClick={() => setDisplayCreateRaidLayoutModal(true)}
> >
Create Raid Layout Create Raid Layout