Class Groups tab working

This commit is contained in:
2025-03-08 18:45:52 -05:00
parent 90337f92ab
commit c2f13d9900
13 changed files with 841 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
import { useGetGameClassesByClassGroup } from "@/hooks/GameClassHooks";
import DangerMessage from "../message/DangerMessage";
export default function GameClassByClassGroupDisplay({
classGroupId
}:{
classGroupId: string;
}){
const gameClassesQuery = useGetGameClassesByClassGroup(classGroupId);
const displayId = crypto.randomUUID().replaceAll("-", "");
if(gameClassesQuery.status === "pending"){
return (<div>Loading...</div>);
}
else if(gameClassesQuery.status === "error"){
return (<DangerMessage>Error: {gameClassesQuery.error.message}</DangerMessage>);
}
else{
return (
<div
className="flex flex-row flex-wrap items-center justify-center gap-x-4"
>
{
gameClassesQuery.data.map((gameClass) => (
<div
key={`gameClassByClassGroupDisplay${classGroupId}${gameClass.gameClassId}${displayId}`}
className="flex flex-row items-center justify-center"
>
{
gameClass.gameClassIcon &&
<img
className="max-h-6 max-w-6 mr-2"
src={`${import.meta.env.VITE_ICON_URL}/gameClass/${gameClass.gameClassIcon}`}
/>
}
{gameClass.gameClassName}
</div>
))
}
</div>
);
}
}

View File

@@ -0,0 +1,82 @@
import { useGetGameClasses } from "@/hooks/GameClassHooks";
import { useEffect, useState } from "react";
import DangerMessage from "../message/DangerMessage";
export function GameClassesSelector({
gameId,
gameClassIds,
onChange
}:{
gameId: string;
gameClassIds?: string[];
onChange?: (gameClassIds: string[]) => void;
}){
const [ selectedGameClassIds, setSelectedGameClassIds ] = useState<string[]>(gameClassIds ?? []);
const selectorId = crypto.randomUUID().replaceAll("-", "");
const gameClassesQuery = useGetGameClasses(gameId, 0, 100, undefined);
const updateSelectedGameClassIds = (selectedGameClassId: string) => {
if(selectedGameClassIds.includes(selectedGameClassId)){
setSelectedGameClassIds(selectedGameClassIds.filter((id) => id !== selectedGameClassId));
}
else{
setSelectedGameClassIds([...selectedGameClassIds, selectedGameClassId]);
}
}
useEffect(() => {
onChange?.(selectedGameClassIds);
}, [ selectedGameClassIds, onChange ]);
if(gameClassesQuery.status === "pending"){
return <div>Loading...</div>
}
else if(gameClassesQuery.status === "error"){
return <DangerMessage>Error loading Game Classes: {gameClassesQuery.error.message}</DangerMessage>
}
else{
return (
<div
className="grid grid-cols-3 gap-4"
style={{flex: "0 0 33.333333333%"}}
>
{
gameClassesQuery.data.map((gameClass) => (
<div
key={gameClass.gameClassId}
className="flex flex-row"
>
<input
id={`gameClassSelector${gameClass.gameClassId}${selectorId}`}
className="cursor-pointer"
type="checkbox"
name="gameClassId"
value={gameClass.gameClassId}
checked={selectedGameClassIds.includes(gameClass.gameClassId ?? "")}
onChange={(e) => updateSelectedGameClassIds(e.target.value)}
/>
<label
className="ml-2 flex flex-row flex-nowrap justify-center items-center text-nowrap cursor-pointer"
htmlFor={`gameClassSelector${gameClass.gameClassId}${selectorId}`}
>
{
gameClass.gameClassIcon &&
<img
className="m-auto max-h-6 max-w-6 mr-2"
src={`${import.meta.env.VITE_ICON_URL}/gameClass/${gameClass.gameClassIcon}`}
/>
}
{gameClass.gameClassName}
</label>
</div>
))
}
</div>
);
}
}

View File

@@ -0,0 +1,135 @@
import { ClassGroup } from "@/interface/ClassGroup";
import { api } from "@/util/AxiosUtil";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export function useGetClassGroups(raidGroupId: string, page: number, pageSize: number, searchTerm?: string){
return useQuery({
queryKey: ["classGroups", raidGroupId, { page, pageSize, searchTerm }],
queryFn: async () => {
const params = new URLSearchParams();
params.append("page", page.toString());
params.append("pageSize", pageSize.toString());
if(searchTerm){
params.append("searchTerm", searchTerm);
}
const response = await api.get(`/raidGroup/${raidGroupId}/classGroup?${params}`);
if(response.status !== 200){
throw new Error("Failed to get class groups");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data as ClassGroup[];
}
});
}
export function useGetClassGroupsCount(raidGroupId: string, searchTerm?: string){
const searchParams = new URLSearchParams();
if(searchTerm){
searchParams.append("searchTerm", searchTerm);
}
return useQuery({
queryKey: ["classGroups", "count", searchTerm],
queryFn: async () => {
const response = await api.get(`/raidGroup/${raidGroupId}/classGroup/count?${searchParams}`);
if(response.status !== 200){
throw new Error("Failed to get class groups count");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data.count as number;
}
});
}
export function useCreateClassGroup(raidGroupId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["createClassGroup"],
mutationFn: async ({classGroupName, gameClassIds}:{classGroupName: string; gameClassIds: string[];}) => {
const response = await api.post(`/raidGroup/${raidGroupId}/classGroup`,
{
classGroup: {
classGroupName: classGroupName,
raidGroupId: raidGroupId
},
gameClassIds
}
);
if(response.status !== 200){
throw new Error("Failed to create class group");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({queryKey: ["classGroups"]});
}
});
}
export function useUpdateClassGroup(raidGroupId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["updateClassGroup"],
mutationFn: async ({classGroup, gameClassIds}:{classGroup: ClassGroup; gameClassIds: string[];}) => {
console.log("Hit");
const response = await api.put(`/raidGroup/${raidGroupId}/classGroup/${classGroup.classGroupId}`,
{
classGroup,
gameClassIds
}
);
if(response.status !== 200){
throw new Error("Failed to update class group");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({queryKey: ["gameClasses", "classGroups"]});
queryClient.invalidateQueries({queryKey: ["classGroups"]});
}
});
}
export function useDeleteClassGroup(raidGroupId: string, classGroupId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["deleteClassGroup", classGroupId, raidGroupId],
mutationFn: async () => {
const response = await api.delete(`/raidGroup/${raidGroupId}/classGroup/${classGroupId}`);
if(response.status !== 200){
throw new Error("Failed to delete class group");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({queryKey: ["classGroups"]});
}
});
}

View File

@@ -28,6 +28,26 @@ export function useGetGameClasses(gameId: string, page: number, pageSize: number
}); });
} }
export function useGetGameClassesByClassGroup(classGroupId: string){
return useQuery({
queryKey: ["gameClasses", "classGroups", classGroupId],
queryFn: async () => {
const response = await api.get(`/gameClass/classGroup/${classGroupId}`);
if(response.status !== 200){
throw new Error("Failed to get game classes");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data as GameClass[];
},
enabled: !!classGroupId
});
}
export function useGetGameClassesCount(gameId: string, searchTerm?: string){ export function useGetGameClassesCount(gameId: string, searchTerm?: string){
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
if(searchTerm){ if(searchTerm){

View File

@@ -0,0 +1,5 @@
export interface ClassGroup {
classGroupId?: string;
raidGroupId: string;
classGroupName: string;
}

View File

@@ -3,6 +3,7 @@ import { useGetRaidGroup } from "@/hooks/RaidGroupHooks";
import { RaidGroup } from "@/interface/RaidGroup"; import { RaidGroup } from "@/interface/RaidGroup";
import RaidGroupCalendarDisplay from "@/ui/calendar/RaidGroupCalendarDisplay"; import RaidGroupCalendarDisplay from "@/ui/calendar/RaidGroupCalendarDisplay";
import RaidGroupHeader from "@/ui/calendar/RaidGroupHeader"; import RaidGroupHeader from "@/ui/calendar/RaidGroupHeader";
import ClassGroupsTab from "@/ui/classGroup/ClassGroupsTab";
import PersonTab from "@/ui/person/PersonTab"; import PersonTab from "@/ui/person/PersonTab";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Navigate, useParams } from "react-router"; import { Navigate, useParams } from "react-router";
@@ -45,6 +46,10 @@ export default function RaidGroupPage(){
{ {
tabHeader: "People", tabHeader: "People",
tabContent: <PersonTab raidGroup={raidGroup}/> tabContent: <PersonTab raidGroup={raidGroup}/>
},
{
tabHeader: "Class Groups",
tabContent: <ClassGroupsTab raidGroup={raidGroup}/>
} }
]; ];

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 ClassGroupButtons({
buttonProps,
showClassGroupModal,
showDeleteClassGroupModal
}:{
buttonProps: ButtonProps;
showClassGroupModal: () => void;
showDeleteClassGroupModal: () => void;
}){
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
onClick={showClassGroupModal}
>
<BsPencilFill
size={22}
/>
</PrimaryButton>
<DangerButton
{...buttonProps}
onClick={showDeleteClassGroupModal}
>
<BsTrash3
size={22}
/>
</DangerButton>
</div>
);
}

View File

@@ -0,0 +1,102 @@
import { ButtonProps } from "@/components/button/Button";
import GameClassByClassGroupDisplay from "@/components/gameClass/GameClassByClassGroupDisplay";
import Table from "@/components/table/Table";
import { useGetGameClassesByClassGroup } from "@/hooks/GameClassHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { RaidGroup } from "@/interface/RaidGroup";
import { useState } from "react";
import ClassGroupButtons from "./ClassGroupButtons";
import ClassGroupModal from "./modal/ClassGroupModal";
import DeleteClassGroupModal from "./modal/DeleteClassGroupModal";
export default function ClassGroupList({
classGroups,
raidGroup
}:{
classGroups: ClassGroup[];
raidGroup: RaidGroup;
}){
const [ selectedClassGroup, setSelectedClassGroup ] = useState<ClassGroup>();
const [ displayClassGroupModal, setDisplayClassGroupModal ] = useState(false);
const [ displayDeleteClassGroupModal, setDisplayDeleteClassGroupModal ] = useState(false);
const gameClassesQuery = useGetGameClassesByClassGroup(selectedClassGroup?.classGroupId ?? "");
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
};
const headElements: React.ReactNode[] = [
<div>
Name
</div>,
<div>
Classes
</div>,
<div
className="pl-16"
>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = classGroups.map((classGroup) => [
<div
className="text-nowrap"
>
{classGroup.classGroupName}
</div>,
<div>
<GameClassByClassGroupDisplay
classGroupId={classGroup.classGroupId ?? ""}
/>
</div>,
<div
className="flex flex-row items-center justify-center gap-2 pl-16"
>
<div
className="py-4 border-l border-neutral-500"
>
&nbsp;
</div>
<ClassGroupButtons
buttonProps={buttonProps}
showClassGroupModal={() => {
setSelectedClassGroup(classGroup);
setDisplayClassGroupModal(true);
}}
showDeleteClassGroupModal={() => {
setSelectedClassGroup(classGroup);
setDisplayDeleteClassGroupModal(true);
}}
/>
</div>
]);
return (
<>
<Table
tableHeadElements={headElements}
tableBodyElements={bodyElements}
/>
<ClassGroupModal
display={displayClassGroupModal}
close={() => { setDisplayClassGroupModal(false); setSelectedClassGroup(undefined); }}
classGroup={selectedClassGroup}
raidGroup={raidGroup}
selectedGameClasses={gameClassesQuery.data ?? []}
/>
<DeleteClassGroupModal
display={displayDeleteClassGroupModal}
close={() => { setDisplayDeleteClassGroupModal(false); setSelectedClassGroup(undefined); }}
raidGroupId={selectedClassGroup?.raidGroupId ?? ""}
classGroup={selectedClassGroup}
/>
</>
);
}

View File

@@ -0,0 +1,72 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { elementBg } from "@/util/SkeletonUtil";
import ClassGroupButtons from "./ClassGroupButtons";
export default function ClassGroupListSkeleton(){
const headerElements: React.ReactElement[] = [
<div>
Name
</div>,
<div>
Classes
</div>,
<div
className="pl-16"
>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = [
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton(),
ClassGroupSkeleton()
];
return (
<Table
tableHeadElements={headerElements}
tableBodyElements={bodyElements}
/>
);
}
function ClassGroupSkeleton(): React.ReactNode[]{
const buttonsProps = {
buttonProps: {
variant: "ghost" as ButtonVariant,
size: "md" as ButtonSizeType,
shape: "square" as ButtonShape,
disabled: true
},
showClassGroupModal: () => {},
showDeleteClassGroupModal: () => {}
}
const elements: React.ReactNode[] = [
<div
className={`h-6 w-72 mr-2 ${elementBg}`}
/>,
<div
className={`h-6 w-[64rem] ${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>
<ClassGroupButtons {...buttonsProps}/>
</div>
];
return elements;
}

View File

@@ -0,0 +1,42 @@
import DangerMessage from "@/components/message/DangerMessage";
import { useGetClassGroups } from "@/hooks/ClassGroupHooks";
import { useGetGameClasses } from "@/hooks/GameClassHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import ClassGroupList from "./ClassGroupList";
export default function ClassGroupsLoader({
page,
pageSize,
searchTerm,
raidGroup,
gameId
}:{
page: number;
pageSize: number;
searchTerm?: string;
raidGroup: RaidGroup;
gameId: string;
}){
const classGroupsQuery = useGetClassGroups(raidGroup?.raidGroupId ?? "", page - 1, pageSize, searchTerm);
const gameClassesQuery = useGetGameClasses(gameId, 0, 100);
if((classGroupsQuery.status === "pending") || (gameClassesQuery.status === "pending")){
return <div>Loading...</div>
}
else if(classGroupsQuery.status === "error"){
return <DangerMessage>Error: {classGroupsQuery.error.message}</DangerMessage>
}
else if(gameClassesQuery.status === "error"){
return <DangerMessage>Error: {gameClassesQuery.error.message}</DangerMessage>
}
else{
return (
<ClassGroupList
classGroups={classGroupsQuery.data ?? []}
raidGroup={raidGroup}
/>
);
}
}

View File

@@ -0,0 +1,108 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetClassGroupsCount } from "@/hooks/ClassGroupHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import ClassGroupsLoader from "./ClassGroupsLoader";
import ClassGroupModal from "./modal/ClassGroupModal";
export default function ClassGroupsTab({
raidGroup
}:{
raidGroup: RaidGroup;
}){
const [ displayCreateClassGroupModal, setDisplayCreateClassGroupModal ] = 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 classGroupsCountQuery = useGetClassGroupsCount(raidGroup.raidGroupId!, sentSearchTerm);
const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => {
setSentSearchTerm(newSearchTerm);
}, 1000);
useEffect(() => {
updateSearchTerm(searchTerm ?? "");
}, [ searchTerm, updateSearchTerm ]);
useEffect(() => {
if(classGroupsCountQuery.status === "success"){
setTotalPages(Math.ceil(classGroupsCountQuery.data / pageSize));
}
}, [ classGroupsCountQuery ]);
return (
<>
<div
className="flex flex-row justify-between items-center w-full"
>
<div
className="flex flex-row items-center justify-start w-full"
>
&nbsp;
</div>
{/* Add Class Group Button */}
<div
className="flex flex-row items-center justify-center w-full"
>
<PrimaryButton
className="mb-8 text-nowrap"
onClick={() => setDisplayCreateClassGroupModal(true)}
>
Create Class Group
</PrimaryButton>
<ClassGroupModal
display={displayCreateClassGroupModal}
close={() => setDisplayCreateClassGroupModal(false)}
classGroup={undefined}
selectedGameClasses={[]}
raidGroup={raidGroup}
/>
</div>
{/* Class Group Search Box */}
<div
className="flex flex-row items-center justify-end w-full"
>
<div>
<TextInput
id={`classGroupSearchBox${modalId}`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
</div>
</div>
</div>
{/* Class Group List */}
<ClassGroupsLoader
page={page}
pageSize={pageSize}
searchTerm={sentSearchTerm}
raidGroup={raidGroup}
gameId={raidGroup.gameId}
/>
{/* Pagination */}
<div
className="my-12"
>
<Pagination
currentPage={page}
totalPages={totalPages}
onChange={setPage}
/>
</div>
</>
);
}

View File

@@ -0,0 +1,121 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import { GameClassesSelector } from "@/components/gameClass/GameClassesSelector";
import TextInput from "@/components/input/TextInput";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useCreateClassGroup, useUpdateClassGroup } from "@/hooks/ClassGroupHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { GameClass } from "@/interface/GameClass";
import { RaidGroup } from "@/interface/RaidGroup";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect, useState } from "react";
export default function ClassGroupModal({
display,
close,
classGroup,
raidGroup,
selectedGameClasses
}:{
display: boolean;
close: () => void;
classGroup: ClassGroup | undefined;
raidGroup: RaidGroup;
selectedGameClasses: GameClass[];
}){
const [ classGroupName, setClassGroupName ] = useState(classGroup?.classGroupName ?? "");
const [ selectedGameClassIds, setSelectedGameClassIds ] = useState<string[]>(selectedGameClasses.map(gc => gc.gameClassId ?? ""));
const modalId = crypto.randomUUID().replace("-", "");
useEffect(() => {
setClassGroupName(classGroup?.classGroupName ?? "");
setSelectedGameClassIds(selectedGameClasses.map(gc => gc.gameClassId ?? ""));
}, [classGroup, selectedGameClasses]);
const createClassGroupMutate = useCreateClassGroup(raidGroup.raidGroupId ?? "");
const updateClassGroupMutate = useUpdateClassGroup(raidGroup.raidGroupId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
useEffect(() => {
if(createClassGroupMutate.status === "success"){
createClassGroupMutate.reset();
addSuccessMessage("Class Group Created");
close();
}
else if(createClassGroupMutate.status === "error"){
createClassGroupMutate.reset();
addErrorMessage(`Error creating class group ${classGroupName}: ${createClassGroupMutate.error.message}`);
console.log(createClassGroupMutate.error);
}
else if(updateClassGroupMutate.status === "success"){
updateClassGroupMutate.reset();
addSuccessMessage("Class Group Updated");
close();
}
else if(updateClassGroupMutate.status === "error"){
updateClassGroupMutate.reset();
addErrorMessage(`Error updating class group ${classGroupName}: ${updateClassGroupMutate.error.message}`);
console.log(updateClassGroupMutate.error);
}
});
const createClassGroup = () => {
createClassGroupMutate.mutate({ classGroupName, gameClassIds: selectedGameClassIds });
}
const updateClassGroup = () => {
updateClassGroupMutate.mutate({
classGroup: {
classGroupId: classGroup?.classGroupId,
raidGroupId: raidGroup.raidGroupId ?? "",
classGroupName
},
gameClassIds: selectedGameClassIds
});
}
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader={classGroup ? "Update Class Group" : "Create Class Group"}
modalBody={
<div
className="flex flex-col items-center justify-center gap-4"
>
<TextInput
id={`classGroupName${modalId}`}
placeholder="Class Group Name"
value={classGroupName}
onChange={(e) => setClassGroupName(e.target.value)}
/>
<GameClassesSelector
gameId={raidGroup.gameId}
gameClassIds={selectedGameClasses.map(gc => gc.gameClassId ?? "")}
onChange={(gameClassIds) => setSelectedGameClassIds(gameClassIds)}
/>
</div>
}
modalFooter={
<>
<PrimaryButton
onClick={classGroup ? updateClassGroup : createClassGroup}
>
{classGroup ? "Update" : "Create"}
</PrimaryButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}

View File

@@ -0,0 +1,66 @@
import DangerButton from "@/components/button/DangerButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useDeleteClassGroup } from "@/hooks/ClassGroupHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function DeleteClassGroupModal({
display,
close,
classGroup,
raidGroupId
}:{
display: boolean;
close: () => void;
classGroup?: ClassGroup;
raidGroupId: string;
}){
const deleteClassGroupMutate = useDeleteClassGroup(raidGroupId, classGroup?.classGroupId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const deleteAccount = () => {
deleteClassGroupMutate.mutate();
}
useEffect(() => {
if(deleteClassGroupMutate.status === "success"){
deleteClassGroupMutate.reset();
addSuccessMessage("Class Group Deleted");
close();
}
else if(deleteClassGroupMutate.status === "error"){
deleteClassGroupMutate.reset();
addErrorMessage(`Error deleting class group: ${deleteClassGroupMutate.error.message}`);
console.log(deleteClassGroupMutate.error);
}
});
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader="Delete Class Group"
modalBody={`Are you sure you want to delete ${classGroup?.classGroupName}`}
modalFooter={
<>
<DangerButton
onClick={deleteAccount}
>
Delete
</DangerButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}