Raid Layout page working

This commit is contained in:
2025-03-09 12:15:29 -04:00
parent c2f13d9900
commit 8b8538a968
15 changed files with 973 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
import { ClassGroup } from "@/interface/ClassGroup";
import SelectClassGroupModal from "@/ui/classGroup/modal/SelectClassGroupModal";
import { useEffect, useState } from "react";
export default function ClassGroupSelector({
raidGroupId,
selectedClassGroups,
onChange
}:{
raidGroupId: string;
selectedClassGroups: (ClassGroup | null)[];
onChange: (classGroups: (ClassGroup | null)[]) => void;
}){
const [ classGroups, setClassGroups ] = useState(selectedClassGroups);
const [ displaySelectClassGroupModal, setDisplaySelectClassGroupModal ] = useState(false);
const [ selectedCell, setSelectedCell ] = useState(0);
useEffect(() => {
setClassGroups(selectedClassGroups);
}, [selectedClassGroups]);
const updateClassGroups = (classGroup?: ClassGroup | null) => {
const newClassGroups = [...classGroups];
if(classGroup){
newClassGroups[selectedCell] = classGroup;
}
else{
newClassGroups[selectedCell] = null;
}
setClassGroups(newClassGroups);
onChange(newClassGroups);
}
return (
<>
<div
className="grid grid-cols-3 gap-4"
style={{flex: "0 0 33.333333333%"}}
>
{
classGroups.map((classGroup, index) => (
<div
key={`${index}`}
className="cursor-pointer border px-2 py-1"
onClick={() => {
setDisplaySelectClassGroupModal(true);
setSelectedCell(index);
}}
>
{classGroup?.classGroupName ?? "Any"}
</div>
))
}
</div>
<SelectClassGroupModal
display={displaySelectClassGroupModal}
close={() => setDisplaySelectClassGroupModal(false)}
selectedClassGroup={classGroups[selectedCell]}
onChange={updateClassGroups}
raidGroupId={raidGroupId}
/>
</>
);
}

View File

@@ -0,0 +1,41 @@
import { useGetClassGroupsByRaidLayout } from "@/hooks/ClassGroupHooks";
import DangerMessage from "../message/DangerMessage";
export default function ClassGroupsByRaidLayoutDisplay({
raidGroupId,
raidLayoutId
}:{
raidGroupId: string;
raidLayoutId: string;
}){
const classGroupsQuery = useGetClassGroupsByRaidLayout(raidGroupId, raidLayoutId);
if(classGroupsQuery.status === "pending"){
return (<div>Loading...</div>);
}
else if(classGroupsQuery.status === "error"){
return (<DangerMessage>Error: {classGroupsQuery.error.message}</DangerMessage>);
}
else{
return (
<div
className="flex flex-row gap-x-4"
>
{
classGroupsQuery.data.map((classGroup, index) => (
<div
key={classGroup?.classGroupId ?? index}
>
{classGroup?.classGroupName ?? "Any"}
</div>
))
}
{
classGroupsQuery.data.length === 0 &&
<>&nbsp;</>
}
</div>
);
}
}

View File

@@ -0,0 +1,86 @@
import clsx from "clsx";
import { useEffect, useState } from "react";
export default function NumberInput({
id,
name,
label,
defaultValue,
value,
min,
max,
onChange,
disabled
}:{
id: string;
name?: string;
accepted?: boolean;
label?: string;
defaultValue?: number;
value?: number;
min?: number;
max?: number;
onChange?: (value: number) => void;
disabled?: boolean;
}){
const [ inputValue, setInputValue ] = useState(value ?? 1);
const [ minValue, setMinValue ] = useState(min ?? Number.MIN_VALUE);
const [ maxValue, setMaxValue ] = useState(max ?? Number.MAX_VALUE);
const inputId = crypto.randomUUID().replaceAll("-", "");
useEffect(() => {
setMinValue(min ?? Number.MIN_VALUE);
setMaxValue(max ?? Number.MAX_VALUE);
setInputValue(value ?? defaultValue ?? 0);
}, [ id, min, max, defaultValue, value ]);
const changeInput = (value: number) => {
if(value < minValue){
value = maxValue;
}
else if(value > maxValue){
value = minValue;
}
setInputValue(value);
onChange?.(value);
}
return (
<div
className="bg-inherit px-4 rounded-sm w-full md:flex md:justify-center"
>
<div
className="relative bg-inherit w-18"
>
<input
id={`${id}NumberInput${inputId}`}
type="number"
name={name}
className={clsx(
"peer px-2 py-1 w-16 rounded-lg ring-2 ring-gray-500 focus:ring-sky-600 outline-hidden",
)}
onChange={(e) => changeInput(parseInt(e.target.value || "1"))}
value={inputValue}
disabled={disabled}
/>
<label
htmlFor={`${id}NumberInput${inputId}`}
id={`${id}NumberInput${inputId}Label`}
className={clsx(
"absolute cursor-pointer left-0 -top-3 mx-1 px-1",
"bg-white dark:bg-neutral-800 text-sm",
"text-gray-500 peer-focus:text-sky-600"
)}
style={{transitionProperty: "top, font-size, line-height", transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", transitionDuration: "150ms"}}
>
{label}
</label>
</div>
</div>
);
}

View File

@@ -52,6 +52,25 @@ export function useGetClassGroupsCount(raidGroupId: string, searchTerm?: string)
});
}
export function useGetClassGroupsByRaidLayout(raidGroupId: string, raidLayoutId: string | undefined){
return useQuery({
queryKey: ["classGroups", "raidLayout", raidLayoutId],
queryFn: async () => {
const response = await api.get(`/raidGroup/${raidGroupId}/classGroup/raidLayout/${raidLayoutId}`);
if(response.status !== 200){
throw new Error("Failed to get class groups");
}
else if(response.data.error){
throw new Error(response.data.errors.join(", "));
}
return response.data as (ClassGroup | null)[];
},
enabled: !!raidLayoutId
})
}
export function useCreateClassGroup(raidGroupId: string){
const queryClient = useQueryClient();

View File

@@ -0,0 +1,130 @@
import { RaidLayout, RaidLayoutClassGroupXref } from "@/interface/RaidLayout";
import { api } from "@/util/AxiosUtil";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export function useGetRaidLayoutsByRaidGroup(raidGroupId: string, page: number, pageSize: number, searchTerm?: string){
return useQuery({
queryKey: ["raidLayouts", 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}/raidLayout?${params}`);
if(response.status !== 200){
throw new Error("Failed to get accounts");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data as RaidLayout[];
}
});
}
export function useGetRaidLayoutsByRaidGroupCount(raidGroupId: string, searchTerm?: string){
return useQuery({
queryKey: ["raidLayouts", raidGroupId, "count", searchTerm],
queryFn: async () => {
const params = new URLSearchParams();
if(searchTerm){
params.append("searchTerm", searchTerm);
}
const response = await api.get(`/raidGroup/${raidGroupId}/raidLayout/count?${params}`);
if(response.status !== 200){
throw new Error("Failed to get accounts");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
return response.data.count as number;
}
});
}
export function useCreateRaidLayout(raidGroupId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["createRaidLayout", raidGroupId],
mutationFn: async ({raidLayout, raidLayoutClassGroupXrefs}:{raidLayout: RaidLayout; raidLayoutClassGroupXrefs: RaidLayoutClassGroupXref[];}) => {
const response = await api.post(`/raidGroup/${raidGroupId}/raidLayout`,
{
raidLayout,
raidLayoutClassGroupXrefs
}
);
if(response.status !== 200){
throw new Error("Failed to create raid layout");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["raidLayouts", raidGroupId] });
}
});
}
export function useUpdateRaidLayout(raidGroupId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["updateRaidLayout", raidGroupId],
mutationFn: async ({raidLayout, raidLayoutClassGroupXrefs}:{raidLayout: RaidLayout; raidLayoutClassGroupXrefs: RaidLayoutClassGroupXref[];}) => {
const response = await api.put(`/raidGroup/${raidGroupId}/raidLayout/${raidLayout.raidLayoutId}`,
{
raidLayout,
raidLayoutClassGroupXrefs
}
);
if(response.status !== 200){
throw new Error("Failed to update raid layout");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["raidLayouts", raidGroupId] });
queryClient.invalidateQueries({ queryKey: ["classGroups", "raidLayout"] });
}
});
}
export function useDeleteRaidLayout(raidGroupId: string, raidLayoutId: string){
const queryClient = useQueryClient();
return useMutation({
mutationKey: ["deleteRaidLayout", raidGroupId, raidLayoutId],
mutationFn: async () => {
const response = await api.delete(`/raidGroup/${raidGroupId}/raidLayout/${raidLayoutId}`);
if(response.status !== 200){
throw new Error("Failed to delete raid layout");
}
else if(response.data.errors){
throw new Error(response.data.errors.join(", "));
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["raidLayouts", raidGroupId] });
}
});
}

View File

@@ -10,6 +10,7 @@
:root.dark {
--text-color: #FFFFFFDE;
--bg-color: var(--color-neutral-825);
color-scheme: dark;
}
:root.light {

View File

@@ -0,0 +1,13 @@
export interface RaidLayout {
raidLayoutId?: string;
raidGroupId: string;
raidLayoutName: string;
raidSize: number;
}
export interface RaidLayoutClassGroupXref {
raidLayoutClassGroupXrefId?: string;
raidLayoutId: string;
classGroupId?: string;
layoutLocation: number;
}

View File

@@ -5,6 +5,7 @@ import RaidGroupCalendarDisplay from "@/ui/calendar/RaidGroupCalendarDisplay";
import RaidGroupHeader from "@/ui/calendar/RaidGroupHeader";
import ClassGroupsTab from "@/ui/classGroup/ClassGroupsTab";
import PersonTab from "@/ui/person/PersonTab";
import RaidLayoutTab from "@/ui/raidLayout/RaidLayoutTab";
import { useEffect, useState } from "react";
import { Navigate, useParams } from "react-router";
@@ -50,6 +51,10 @@ export default function RaidGroupPage(){
{
tabHeader: "Class Groups",
tabContent: <ClassGroupsTab raidGroup={raidGroup}/>
},
{
tabHeader: "Raid Layout",
tabContent: <RaidLayoutTab raidGroup={raidGroup}/>
}
];

View File

@@ -0,0 +1,95 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import DangerMessage from "@/components/message/DangerMessage";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useGetClassGroups } from "@/hooks/ClassGroupHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { useEffect, useState } from "react";
export default function SelectClassGroupModal({
display,
close,
selectedClassGroup,
onChange,
raidGroupId
}:{
display: boolean;
close: () => void;
selectedClassGroup?: ClassGroup | null;
onChange: (classGroup?: ClassGroup | null) => void;
raidGroupId: string;
}){
const [ currentClassGroup, setCurrentClassGroup ] = useState(selectedClassGroup);
const selectorId = crypto.randomUUID().replaceAll("-", "");
const classGroupsQuery = useGetClassGroups(raidGroupId, 0, 100);
useEffect(() => {
setCurrentClassGroup(selectedClassGroup);
}, [ selectedClassGroup ]);
if(classGroupsQuery.status === "pending"){
return <div>Loading...</div>
}
else if(classGroupsQuery.status === "error"){
return <DangerMessage>Error: {classGroupsQuery.error.message}</DangerMessage>
}
else{
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader="Select Class Group"
modalBody={
<div
className="grid grid-cols-3 gap-4 w-[36rem]"
style={{flex: "0 0 33.333333333%"}}
>
{
classGroupsQuery.data?.map((classGroup) => (
<div
key={classGroup.classGroupId}
className="flex flex-row items-center justify-center"
>
<input
id={`classGroup${classGroup.classGroupId}${selectorId}`}
className="cursor-pointer"
type="radio"
name="classGroupId"
value={classGroup.classGroupId}
checked={classGroup.classGroupId === currentClassGroup?.classGroupId}
onClick={() => setCurrentClassGroup(currentClassGroup === classGroup ? undefined : classGroup)}
onChange={() => {}}
/>
<label
className="flex flex-row flex-nowrap justify-center items-center text-nowrap cursor-pointer ml-2"
htmlFor={`classGroup${classGroup.classGroupId}${selectorId}`}
>
{classGroup.classGroupName}
</label>
</div>
))
}
</div>
}
modalFooter={
<>
<PrimaryButton
onClick={() => { onChange(currentClassGroup); close(); }}
>
Select
</PrimaryButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}
}

View File

@@ -0,0 +1,37 @@
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 RaidLayoutAdminButtons({
buttonProps,
showRaidLayoutModal,
showDeleteRaidLayoutModal
}:{
buttonProps: ButtonProps;
showRaidLayoutModal: () => void;
showDeleteRaidLayoutModal: () => void;
}){
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
onClick={showRaidLayoutModal}
>
<BsPencilFill
size={22}
/>
</PrimaryButton>
<DangerButton
{...buttonProps}
onClick={showDeleteRaidLayoutModal}
>
<BsTrash3
size={22}
/>
</DangerButton>
</div>
);
}

View File

@@ -0,0 +1,109 @@
import { ButtonProps } from "@/components/button/Button";
import ClassGroupsByRaidLayoutDisplay from "@/components/classGroup/ClassGroupsByRaidLayoutDisplay";
import Table from "@/components/table/Table";
import { useGetClassGroupsByRaidLayout } from "@/hooks/ClassGroupHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { RaidGroup } from "@/interface/RaidGroup";
import { RaidLayout } from "@/interface/RaidLayout";
import { useEffect, useState } from "react";
import DeleteRaidLayoutModal from "./modal/DeleteRaidLayoutModal";
import RaidLayoutModal from "./modal/RaidLayoutModal";
import RaidLayoutAdminButtons from "./RaidLayoutAdminButtons";
export default function RaidLayoutList({
raidLayouts,
raidGroup
}:{
raidLayouts: RaidLayout[];
raidGroup: RaidGroup;
}){
const [ selectedRaidLayout, setSelectedRaidLayout ] = useState<RaidLayout>();
const [ displayEditRaidLayoutModal, showEditRaidLayoutModal ] = useState(false);
const [ displayDeleteRaidLayoutModal, showDeleteRaidLayoutModal ] = useState(false);
const [ selectedLayoutClassGroups, setSelectedLayoutClassGroups ] = useState<(ClassGroup | null)[]>([]);
const classGroupsQuery = useGetClassGroupsByRaidLayout(raidGroup.raidGroupId ?? "", selectedRaidLayout?.raidLayoutId);
useEffect(() => {
setSelectedLayoutClassGroups(classGroupsQuery.data ?? []);
}, [ classGroupsQuery.data ]);
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
};
const headElements: React.ReactNode[] = [
<div
className="text-nowrap"
>
Raid Layout Name
</div>,
<div
className="text-nowrap"
>
Raid Size
</div>,
<div
className="text-nowrap"
>
Class Groups
</div>,
<div
className="pl-16"
>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = raidLayouts.map((raidLayout) => [
<div>
{raidLayout.raidLayoutName}
</div>,
<div>
{raidLayout.raidSize}
</div>,
<div>
<ClassGroupsByRaidLayoutDisplay raidGroupId={raidGroup.raidGroupId ?? ""} raidLayoutId={raidLayout.raidLayoutId ?? ""}/>
</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>
<RaidLayoutAdminButtons
buttonProps={buttonProps}
showRaidLayoutModal={() => { setSelectedRaidLayout(raidLayout); showEditRaidLayoutModal(true); }}
showDeleteRaidLayoutModal={() => { setSelectedRaidLayout(raidLayout); showDeleteRaidLayoutModal(true); }}
/>
</div>
]);
return (
<>
<Table
tableHeadElements={headElements}
tableBodyElements={bodyElements}
/>
<RaidLayoutModal
display={displayEditRaidLayoutModal}
close={() => { showEditRaidLayoutModal(false); setSelectedRaidLayout(undefined); }}
raidLayout={selectedRaidLayout}
raidGroup={raidGroup}
selectedClassGroups={selectedLayoutClassGroups}
/>
<DeleteRaidLayoutModal
display={displayDeleteRaidLayoutModal}
close={() => { showDeleteRaidLayoutModal(false); setSelectedRaidLayout(undefined); }}
raidLayout={selectedRaidLayout}
/>
</>
);
}

View File

@@ -0,0 +1,35 @@
import DangerMessage from "@/components/message/DangerMessage";
import { useGetRaidLayoutsByRaidGroup } from "@/hooks/RaidLayoutHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import RaidLayoutList from "./RaidLayoutList";
export default function RaidLayoutLoader({
page,
pageSize,
searchTerm,
raidGroup
}:{
page: number;
pageSize: number;
searchTerm?: string;
raidGroup: RaidGroup;
}){
const raidLayoutsQuery = useGetRaidLayoutsByRaidGroup(raidGroup.raidGroupId ?? "", page - 1, pageSize, searchTerm);
if(raidLayoutsQuery.status === "pending"){
return <div>Loading...</div>
}
else if(raidLayoutsQuery.status === "error"){
return <DangerMessage>Error: {raidLayoutsQuery.error.message}</DangerMessage>
}
else{
return (
<RaidLayoutList
raidLayouts={raidLayoutsQuery.data ?? []}
raidGroup={raidGroup}
/>
);
}
}

View File

@@ -0,0 +1,106 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetRaidLayoutsByRaidGroupCount } from "@/hooks/RaidLayoutHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import RaidLayoutModal from "./modal/RaidLayoutModal";
import RaidLayoutLoader from "./RaidLayoutLoader";
export default function RaidLayoutTab({
raidGroup
}:{
raidGroup: RaidGroup;
}){
const [ displayCreateRaidLayoutModal, setDisplayCreateRaidLayoutModal ] = 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 raidLayoutCountQuery = useGetRaidLayoutsByRaidGroupCount(raidGroup.raidGroupId!, sentSearchTerm);
const updateSearchTerm = useDebouncedCallback((newSearchTerm: string) => {
setSentSearchTerm(newSearchTerm);
}, 1000);
useEffect(() => {
updateSearchTerm(searchTerm);
}, [ searchTerm, updateSearchTerm ]);
useEffect(() => {
if(raidLayoutCountQuery.status === "success"){
setTotalPages(Math.ceil(raidLayoutCountQuery.data / pageSize));
}
}, [ raidLayoutCountQuery ]);
return (
<>
<div
className="flex flex-row items-center justify-between w-full"
>
<div
className="flex flex-row items-center justify-start w-full"
>
&nbsp;
</div>
{/* Add Raid Layout Button */}
<div
className="flex flex-row items-center justify-center w-full"
>
<PrimaryButton
className="mb-8 text-nowrap"
onClick={() => setDisplayCreateRaidLayoutModal(true)}
>
Create Raid Layout
</PrimaryButton>
<RaidLayoutModal
display={displayCreateRaidLayoutModal}
close={() => setDisplayCreateRaidLayoutModal(false)}
raidLayout={undefined}
raidGroup={raidGroup}
selectedClassGroups={[]}
/>
</div>
{/* Raid Layout Search Box */}
<div
className="flex flex-row items-center justify-end w-full"
>
<div>
<TextInput
id={`raidLayoutSearchBox${modalId}`}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
</div>
</div>
</div>
{/* Raid Layout List */}
<RaidLayoutLoader
page={page}
pageSize={pageSize}
searchTerm={sentSearchTerm}
raidGroup={raidGroup}
/>
{/* Pagination */}
<div
className="my-12"
>
<Pagination
currentPage={page}
totalPages={totalPages}
onChange={setPage}
/>
</div>
</>
);
}

View File

@@ -0,0 +1,64 @@
import DangerButton from "@/components/button/DangerButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useDeleteRaidLayout } from "@/hooks/RaidLayoutHooks";
import { RaidLayout } from "@/interface/RaidLayout";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function DeleteRaidLayoutModal({
display,
close,
raidLayout
}:{
display: boolean;
close: () => void;
raidLayout?: RaidLayout;
}){
const deleteRaidLayoutMutate = useDeleteRaidLayout(raidLayout?.raidGroupId ?? "", raidLayout?.raidLayoutId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const deleteRaidLayout = () => {
deleteRaidLayoutMutate.mutate();
}
useEffect(() => {
if(deleteRaidLayoutMutate.status === "success"){
deleteRaidLayoutMutate.reset();
addSuccessMessage("Raid Layout Deleted");
close();
}
else if(deleteRaidLayoutMutate.status === "error"){
deleteRaidLayoutMutate.reset();
addErrorMessage(`Error deleting raid layout ${raidLayout?.raidLayoutName}: ${deleteRaidLayoutMutate.error.message}`);
console.log(deleteRaidLayoutMutate.error);
}
});
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader={"Delete Raid Layout"}
modalBody={`Are you sure you want to delete ${raidLayout?.raidLayoutName}`}
modalFooter={
<>
<DangerButton
onClick={deleteRaidLayout}
>
Delete
</DangerButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}

View File

@@ -0,0 +1,164 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import ClassGroupSelector from "@/components/classGroup/ClassGroupSelector";
import NumberInput from "@/components/input/NumberInput";
import TextInput from "@/components/input/TextInput";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useCreateRaidLayout, useUpdateRaidLayout } from "@/hooks/RaidLayoutHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { RaidGroup } from "@/interface/RaidGroup";
import { RaidLayout, RaidLayoutClassGroupXref } from "@/interface/RaidLayout";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect, useState } from "react";
export default function RaidLayoutModal({
display,
close,
raidLayout,
raidGroup,
selectedClassGroups
}:{
display: boolean;
close: () => void;
raidLayout?: RaidLayout;
raidGroup: RaidGroup;
selectedClassGroups: (ClassGroup | null)[];
}){
const [ raidLayoutName, setRaidLayoutName ] = useState(raidLayout?.raidLayoutName ?? "");
const [ raidLayoutSize, setRaidLayoutSize ] = useState(raidLayout?.raidSize ?? 0);
const [ classGroups, setClassGroups ] = useState(selectedClassGroups ?? []);
const modalId = crypto.randomUUID().replaceAll("-", "");
useEffect(() => {
setRaidLayoutName(raidLayout?.raidLayoutName ?? "");
setRaidLayoutSize(raidLayout?.raidSize ?? 0);
setClassGroups(selectedClassGroups);
}, [ raidLayout, selectedClassGroups ]);
const updateRaidLayoutSize = (newLayoutSize: number) => {
console.log("newLayoutSize = " + newLayoutSize + ", " + classGroups.length);
setRaidLayoutSize(newLayoutSize);
if(newLayoutSize < classGroups.length){
console.log("Removing");
setClassGroups(classGroups.slice(0, newLayoutSize));
}
else if(newLayoutSize > classGroups.length){
console.log("Adding");
setClassGroups([
...classGroups,
null
]);
}
}
console.log("classGroups");
console.log(classGroups);
const createRaidLayoutMutate = useCreateRaidLayout(raidGroup.raidGroupId ?? "");
const updateRaidLayoutMutate = useUpdateRaidLayout(raidGroup.raidGroupId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
useEffect(() => {
if(createRaidLayoutMutate.status === "success"){
createRaidLayoutMutate.reset();
addSuccessMessage("Raid Layout Created");
close();
}
else if(createRaidLayoutMutate.status === "error"){
createRaidLayoutMutate.reset();
addErrorMessage(`Error creating raid layout ${raidLayoutName}: ${createRaidLayoutMutate.error.message}`);
console.log(createRaidLayoutMutate.error);
}
else if(updateRaidLayoutMutate.status === "success"){
updateRaidLayoutMutate.reset();
addSuccessMessage("Raid Layout Updated");
close();
}
else if(updateRaidLayoutMutate.status === "error"){
updateRaidLayoutMutate.reset();
addErrorMessage(`Error updating raid layout ${raidLayoutName}: ${updateRaidLayoutMutate.error.message}`);
console.log(updateRaidLayoutMutate.error);
}
});
const createRaidLayout = () => {
createRaidLayoutMutate.mutate({
raidLayout: {
raidLayoutName,
raidSize: raidLayoutSize,
raidGroupId: raidGroup.raidGroupId ?? ""
},
raidLayoutClassGroupXrefs: classGroups.map((cg, index) => ({
raidLayoutId: raidLayout?.raidLayoutId ?? "",
classGroupId: cg?.classGroupId,
layoutLocation: index
} as RaidLayoutClassGroupXref))
});
}
const updateRaidLayout = () => {
updateRaidLayoutMutate.mutate({
raidLayout: {
raidLayoutId: raidLayout?.raidLayoutId,
raidLayoutName,
raidSize: raidLayoutSize,
raidGroupId: raidGroup.raidGroupId ?? ""
},
raidLayoutClassGroupXrefs: classGroups.map((cg, index) => ({
raidLayoutId: raidLayout?.raidLayoutId ?? "",
classGroupId: cg?.classGroupId,
layoutLocation: index
} as RaidLayoutClassGroupXref))
});
}
return (
<RaidBuilderModal
display={display}
close={close}
modalHeader={raidLayout ? "Update Raid Layout" : "Create Raid Layout"}
modalBody={
<div
className="flex flex-col items-center justify-center gap-4"
>
<TextInput
id={`raidLayoutName${modalId}`}
placeholder="Raid Layout Name"
value={raidLayoutName}
onChange={(e) => setRaidLayoutName(e.target.value)}
/>
<NumberInput
id="raidLayoutSizeInput"
min={1}
max={100}
value={raidLayoutSize}
onChange={updateRaidLayoutSize}
/>
<ClassGroupSelector
raidGroupId={raidGroup.raidGroupId ?? ""}
selectedClassGroups={classGroups}
onChange={setClassGroups}
/>
</div>
}
modalFooter={
<>
<PrimaryButton
onClick={raidLayout ? updateRaidLayout : createRaidLayout}
>
{raidLayout ? "Update" : "Create"}
</PrimaryButton>
<SecondaryButton
onClick={close}
>
Cancel
</SecondaryButton>
</>
}
/>
);
}