From 8b8538a9685c01c3628c053ca37e091450fe3e62 Mon Sep 17 00:00:00 2001 From: Mattrixwv Date: Sun, 9 Mar 2025 12:15:29 -0400 Subject: [PATCH] Raid Layout page working --- .../classGroup/ClassGroupSelector.tsx | 68 ++++++++ .../ClassGroupsByRaidLayoutDisplay.tsx | 41 +++++ src/components/input/NumberInput.tsx | 86 +++++++++ src/hooks/ClassGroupHooks.ts | 19 ++ src/hooks/RaidLayoutHooks.ts | 130 ++++++++++++++ src/index.css | 1 + src/interface/RaidLayout.ts | 13 ++ src/pages/protected/RaidGroupPage.tsx | 5 + .../modal/SelectClassGroupModal.tsx | 95 ++++++++++ src/ui/raidLayout/RaidLayoutAdminButtons.tsx | 37 ++++ src/ui/raidLayout/RaidLayoutList.tsx | 109 ++++++++++++ src/ui/raidLayout/RaidLayoutLoader.tsx | 35 ++++ src/ui/raidLayout/RaidLayoutTab.tsx | 106 +++++++++++ .../modal/DeleteRaidLayoutModal.tsx | 64 +++++++ src/ui/raidLayout/modal/RaidLayoutModal.tsx | 164 ++++++++++++++++++ 15 files changed, 973 insertions(+) create mode 100644 src/components/classGroup/ClassGroupSelector.tsx create mode 100644 src/components/classGroup/ClassGroupsByRaidLayoutDisplay.tsx create mode 100644 src/components/input/NumberInput.tsx create mode 100644 src/hooks/RaidLayoutHooks.ts create mode 100644 src/interface/RaidLayout.ts create mode 100644 src/ui/classGroup/modal/SelectClassGroupModal.tsx create mode 100644 src/ui/raidLayout/RaidLayoutAdminButtons.tsx create mode 100644 src/ui/raidLayout/RaidLayoutList.tsx create mode 100644 src/ui/raidLayout/RaidLayoutLoader.tsx create mode 100644 src/ui/raidLayout/RaidLayoutTab.tsx create mode 100644 src/ui/raidLayout/modal/DeleteRaidLayoutModal.tsx create mode 100644 src/ui/raidLayout/modal/RaidLayoutModal.tsx diff --git a/src/components/classGroup/ClassGroupSelector.tsx b/src/components/classGroup/ClassGroupSelector.tsx new file mode 100644 index 0000000..067cd47 --- /dev/null +++ b/src/components/classGroup/ClassGroupSelector.tsx @@ -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 ( + <> +
+ { + classGroups.map((classGroup, index) => ( +
{ + setDisplaySelectClassGroupModal(true); + setSelectedCell(index); + }} + > + {classGroup?.classGroupName ?? "Any"} +
+ )) + } +
+ setDisplaySelectClassGroupModal(false)} + selectedClassGroup={classGroups[selectedCell]} + onChange={updateClassGroups} + raidGroupId={raidGroupId} + /> + + ); +} diff --git a/src/components/classGroup/ClassGroupsByRaidLayoutDisplay.tsx b/src/components/classGroup/ClassGroupsByRaidLayoutDisplay.tsx new file mode 100644 index 0000000..59fc941 --- /dev/null +++ b/src/components/classGroup/ClassGroupsByRaidLayoutDisplay.tsx @@ -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 (
Loading...
); + } + else if(classGroupsQuery.status === "error"){ + return (Error: {classGroupsQuery.error.message}); + } + else{ + return ( +
+ { + classGroupsQuery.data.map((classGroup, index) => ( +
+ {classGroup?.classGroupName ?? "Any"} +
+ )) + } + { + classGroupsQuery.data.length === 0 && + <>  + } +
+ ); + } +} diff --git a/src/components/input/NumberInput.tsx b/src/components/input/NumberInput.tsx new file mode 100644 index 0000000..0f29f3f --- /dev/null +++ b/src/components/input/NumberInput.tsx @@ -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 ( +
+
+ changeInput(parseInt(e.target.value || "1"))} + value={inputValue} + disabled={disabled} + /> + +
+
+ ); +} diff --git a/src/hooks/ClassGroupHooks.ts b/src/hooks/ClassGroupHooks.ts index 1ac8389..ca7cbac 100644 --- a/src/hooks/ClassGroupHooks.ts +++ b/src/hooks/ClassGroupHooks.ts @@ -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(); diff --git a/src/hooks/RaidLayoutHooks.ts b/src/hooks/RaidLayoutHooks.ts new file mode 100644 index 0000000..abccede --- /dev/null +++ b/src/hooks/RaidLayoutHooks.ts @@ -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] }); + } + }); +} + diff --git a/src/index.css b/src/index.css index 00f1520..9e4d088 100644 --- a/src/index.css +++ b/src/index.css @@ -10,6 +10,7 @@ :root.dark { --text-color: #FFFFFFDE; --bg-color: var(--color-neutral-825); + color-scheme: dark; } :root.light { diff --git a/src/interface/RaidLayout.ts b/src/interface/RaidLayout.ts new file mode 100644 index 0000000..0284b41 --- /dev/null +++ b/src/interface/RaidLayout.ts @@ -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; +} diff --git a/src/pages/protected/RaidGroupPage.tsx b/src/pages/protected/RaidGroupPage.tsx index 2899093..f11906f 100644 --- a/src/pages/protected/RaidGroupPage.tsx +++ b/src/pages/protected/RaidGroupPage.tsx @@ -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: + }, + { + tabHeader: "Raid Layout", + tabContent: } ]; diff --git a/src/ui/classGroup/modal/SelectClassGroupModal.tsx b/src/ui/classGroup/modal/SelectClassGroupModal.tsx new file mode 100644 index 0000000..e3cd6e1 --- /dev/null +++ b/src/ui/classGroup/modal/SelectClassGroupModal.tsx @@ -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
Loading...
+ } + else if(classGroupsQuery.status === "error"){ + return Error: {classGroupsQuery.error.message} + } + else{ + return ( + + { + classGroupsQuery.data?.map((classGroup) => ( +
+ setCurrentClassGroup(currentClassGroup === classGroup ? undefined : classGroup)} + onChange={() => {}} + /> + +
+ )) + } + + } + modalFooter={ + <> + { onChange(currentClassGroup); close(); }} + > + Select + + + Cancel + + + } + /> + ); + } +} diff --git a/src/ui/raidLayout/RaidLayoutAdminButtons.tsx b/src/ui/raidLayout/RaidLayoutAdminButtons.tsx new file mode 100644 index 0000000..a247ee1 --- /dev/null +++ b/src/ui/raidLayout/RaidLayoutAdminButtons.tsx @@ -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 ( +
+ + + + + + +
+ ); +} diff --git a/src/ui/raidLayout/RaidLayoutList.tsx b/src/ui/raidLayout/RaidLayoutList.tsx new file mode 100644 index 0000000..1528c3a --- /dev/null +++ b/src/ui/raidLayout/RaidLayoutList.tsx @@ -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(); + 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[] = [ +
+ Raid Layout Name +
, +
+ Raid Size +
, +
+ Class Groups +
, +
+ Actions +
+ ]; + + const bodyElements: React.ReactNode[][] = raidLayouts.map((raidLayout) => [ +
+ {raidLayout.raidLayoutName} +
, +
+ {raidLayout.raidSize} +
, +
+ +
, +
+
+   +
+ { setSelectedRaidLayout(raidLayout); showEditRaidLayoutModal(true); }} + showDeleteRaidLayoutModal={() => { setSelectedRaidLayout(raidLayout); showDeleteRaidLayoutModal(true); }} + /> +
+ ]); + + + return ( + <> + + { showEditRaidLayoutModal(false); setSelectedRaidLayout(undefined); }} + raidLayout={selectedRaidLayout} + raidGroup={raidGroup} + selectedClassGroups={selectedLayoutClassGroups} + /> + { showDeleteRaidLayoutModal(false); setSelectedRaidLayout(undefined); }} + raidLayout={selectedRaidLayout} + /> + + ); +} diff --git a/src/ui/raidLayout/RaidLayoutLoader.tsx b/src/ui/raidLayout/RaidLayoutLoader.tsx new file mode 100644 index 0000000..f34b1bb --- /dev/null +++ b/src/ui/raidLayout/RaidLayoutLoader.tsx @@ -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
Loading...
+ } + else if(raidLayoutsQuery.status === "error"){ + return Error: {raidLayoutsQuery.error.message} + } + else{ + return ( + + ); + } +} diff --git a/src/ui/raidLayout/RaidLayoutTab.tsx b/src/ui/raidLayout/RaidLayoutTab.tsx new file mode 100644 index 0000000..e3dd00c --- /dev/null +++ b/src/ui/raidLayout/RaidLayoutTab.tsx @@ -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(); + 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 ( + <> +
+
+   +
+ {/* Add Raid Layout Button */} +
+ setDisplayCreateRaidLayoutModal(true)} + > + Create Raid Layout + + setDisplayCreateRaidLayoutModal(false)} + raidLayout={undefined} + raidGroup={raidGroup} + selectedClassGroups={[]} + /> +
+ {/* Raid Layout Search Box */} +
+
+ setSearchTerm(e.target.value)} + placeholder="Search" + /> +
+
+
+ {/* Raid Layout List */} + + {/* Pagination */} +
+ +
+ + ); +} diff --git a/src/ui/raidLayout/modal/DeleteRaidLayoutModal.tsx b/src/ui/raidLayout/modal/DeleteRaidLayoutModal.tsx new file mode 100644 index 0000000..38f7e26 --- /dev/null +++ b/src/ui/raidLayout/modal/DeleteRaidLayoutModal.tsx @@ -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 ( + + + Delete + + + Cancel + + + } + /> + ); +} diff --git a/src/ui/raidLayout/modal/RaidLayoutModal.tsx b/src/ui/raidLayout/modal/RaidLayoutModal.tsx new file mode 100644 index 0000000..9236493 --- /dev/null +++ b/src/ui/raidLayout/modal/RaidLayoutModal.tsx @@ -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 ( + + setRaidLayoutName(e.target.value)} + /> + + + + } + modalFooter={ + <> + + {raidLayout ? "Update" : "Create"} + + + Cancel + + + } + /> + ); +}