Raid Instance Creator working

This commit is contained in:
2025-03-15 12:20:05 -04:00
parent cff5e098b8
commit 56236fd2ac
32 changed files with 1524 additions and 20 deletions

View File

@@ -0,0 +1,129 @@
import { ClassGroup } from "@/interface/ClassGroup";
import { GridLocation } from "@/interface/GridLocation";
import { PersonCharacter } from "@/interface/PersonCharacter";
import { RaidInstance } from "@/interface/RaidInstance";
import { RaidInstancePersonCharacterXref } from "@/interface/RaidInstancePersonCharacterXref";
import { useRaidInstanceContext } from "@/providers/RaidInstanceLayoutProvider";
import SelectClassGroupModal from "@/ui/classGroup/modal/SelectClassGroupModal";
import PersonCharacterSelectorModal from "@/ui/personCharacter/modal/PersonCharacterSelectorModal";
import { useState } from "react";
import RaidInstanceCreatorTable from "./RaidInstanceCreatorTable";
export default function RaidInstanceCreator(){
const {
raidGroup,
classGroups,
raidInstance, setRaidInstance,
roster,
setRaidLayout,
selectedClassGroups, setSelectedClassGroups,
personCharacters,
personCharacterXrefs, setPersonCharacterXrefs
} = useRaidInstanceContext();
const [ displayClassGroupsSelectorModal, setDisplayClassGroupsSelectorModal ] = useState(false);
const [ displayPersonCharacterSelectorModal, setDisplayPersonCharacterSelectorModal ] = useState(false);
const [ currentLocation, setCurrentLocation ] = useState<GridLocation>({row: 0, col: 0});
const updateClassGroupsLayout = (newSelectedClassGroup: ClassGroup | null | undefined) => {
//Get all existing xrefs, leaving out the current xref it it's been removed
const newClassGroups = selectedClassGroups;
newClassGroups[currentLocation.col] = newSelectedClassGroup ?? null;
//Update the raid layout to persist the changes
setSelectedClassGroups(newClassGroups);
setRaidInstance({...raidInstance, raidLayoutId: undefined} as RaidInstance);
setRaidLayout(undefined);
//Hide modal
setDisplayClassGroupsSelectorModal(false);
}
const setCharacterIdInCurrentSlot = (newPersonCharacterId: string | undefined) => {
let newPersonCharacterXrefs: RaidInstancePersonCharacterXref[] = personCharacterXrefs;
//Step through this to make sure it's working as expected
if(newPersonCharacterId && (newPersonCharacterId !== "")){
const existingXref = personCharacterXrefs?.find((xref) => xref.groupNumber === currentLocation.row && xref.positionNumber == currentLocation.col);
//If the ID exists and the xref also exists then update the xref
if(existingXref){
newPersonCharacterXrefs = personCharacterXrefs?.map((xref) => {
if((xref.groupNumber === currentLocation.row) && (xref.positionNumber === currentLocation.col)){
xref.personCharacterId = newPersonCharacterId;
}
return xref;
});
}
//If the ID exists and the xref doesn't add a new xref
else{
newPersonCharacterXrefs.push(
{
raidInstanceId: raidInstance?.raidInstanceId ?? "",
personCharacterId: newPersonCharacterId,
groupNumber: currentLocation.row,
positionNumber: currentLocation.col
}
);
}
}
else{
//If the ID doesn't exist then remove the xref if it exists
newPersonCharacterXrefs = personCharacterXrefs?.filter((xref) => !(xref.groupNumber === currentLocation.row && xref.positionNumber == currentLocation.col));
}
setPersonCharacterXrefs(newPersonCharacterXrefs);
}
const getCurrentRunCharacters = (): PersonCharacter[] => {
const characterIds = personCharacterXrefs?.filter((xref) => xref.groupNumber === currentLocation.row).map((xref) => xref.personCharacterId);
return personCharacters.filter((personCharacter) => characterIds.includes(personCharacter.personCharacterId ?? ""));
}
const getCharactersFromOtherRuns = (): PersonCharacter[] => {
const characterIds = personCharacterXrefs?.filter((xref) => xref.groupNumber !== currentLocation.row).map((xref) => xref.personCharacterId);
return personCharacters.filter((personCharacter) => characterIds.includes(personCharacter.personCharacterId ?? ""));
}
const getCharacterFromCell = () => {
const xref = personCharacterXrefs.find((xref) => xref.groupNumber === currentLocation.row && xref.positionNumber === currentLocation.col);
return personCharacters.find((personCharacter) => personCharacter.personCharacterId === xref?.personCharacterId);
}
const getPersonCharactersFromRoster = (): PersonCharacter[] => {
const personIds = roster.map((person) => person.personId);
return personCharacters.filter((personCharacter) => personIds.includes(personCharacter.personId));
}
return (
<div
className="flex flex-col items-center justify-center"
>
{/* Main Content */}
<RaidInstanceCreatorTable
onClickHeaderCell={(col) => { setCurrentLocation({row: 0, col: col}); setDisplayClassGroupsSelectorModal(true); }}
onClickBodyCell={(row, col) => { setCurrentLocation({row: row, col: col}); setDisplayPersonCharacterSelectorModal(true); }}
/>
{/* Modals */}
<SelectClassGroupModal
display={displayClassGroupsSelectorModal}
close={() => setDisplayClassGroupsSelectorModal(false)}
onChange={updateClassGroupsLayout}
selectedClassGroup={selectedClassGroups[currentLocation.col]}
raidGroupId={raidGroup?.raidGroupId ?? ""}
/>
<PersonCharacterSelectorModal
display={displayPersonCharacterSelectorModal}
close={() => setDisplayPersonCharacterSelectorModal(false)}
currentSlotClassGroup={classGroups[currentLocation.col]}
currentRunCharacters={getCurrentRunCharacters()}
otherRunsCharacters={getCharactersFromOtherRuns()}
personCharacters={getPersonCharactersFromRoster()}
selectedCharacterId={getCharacterFromCell()?.personCharacterId}
setInput={setCharacterIdInCurrentSlot}
/>
</div>
);
}

View File

@@ -0,0 +1,51 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import { RaidInstance } from "@/interface/RaidInstance";
import { useRaidInstanceContext } from "@/providers/RaidInstanceLayoutProvider";
import RaidInstanceCreatorTableBody from "./RaidInstanceCreatorTableBody";
import RaidInstanceCreatorTableHeader from "./RaidInstanceCreatorTableHeader";
export default function RaidInstanceCreatorTable({
onClickHeaderCell,
onClickBodyCell
}:{
onClickHeaderCell: (col: number) => void;
onClickBodyCell: (row: number, col: number) => void;
}){
const { raidInstance, setRaidInstance } = useRaidInstanceContext();
const addRun = () => {
const newRaidInstance = {...raidInstance};
newRaidInstance.numberRuns = (newRaidInstance.numberRuns ?? 0) + 1;
setRaidInstance(newRaidInstance as RaidInstance);
}
return(
<div
className="flex flex-col items-center justify-center"
>
<div
className="flex flex-col items-start justify-start max-w-full overflow-auto pb-16"
>
<table>
{/* Header */}
<RaidInstanceCreatorTableHeader
onClickHeaderCell={onClickHeaderCell}
/>
{/* Body */}
<RaidInstanceCreatorTableBody
onClickBodyCell={onClickBodyCell}
/>
</table>
</div>
{/* Buttons */}
<PrimaryButton
onClick={addRun}
>
Add Run
</PrimaryButton>
</div>
);
}

View File

@@ -0,0 +1,132 @@
import DangerButton from "@/components/button/DangerButton";
import TertiaryButton from "@/components/button/TertiaryButton";
import { PersonCharacter } from "@/interface/PersonCharacter";
import { RaidInstance } from "@/interface/RaidInstance";
import { useRaidInstanceContext } from "@/providers/RaidInstanceLayoutProvider";
import { getPersonCharactersFromXrefs } from "@/util/PersonCharacterUtil";
import moment from "moment";
import { BsDiscord, BsXLg } from "react-icons/bs";
export default function RaidInstanceCreatorTableBody({
onClickBodyCell
}:{
onClickBodyCell: (run: number, slot: number) => void;
}){
const {
raidInstance, setRaidInstance,
people,
personCharacterXrefs, setPersonCharacterXrefs,
personCharacters,
selectedClassGroups
} = useRaidInstanceContext();
const characterGrid: (PersonCharacter | null)[][] = getPersonCharactersFromXrefs(personCharacterXrefs, personCharacters, raidInstance);
const removeRun = (runNumber: number) => {
const newXrefs = personCharacterXrefs.filter((xref) => xref.groupNumber !== runNumber)?.map((xref) => {return {...xref, groupNumber: xref.groupNumber >= runNumber ? xref.groupNumber - 1 : xref.groupNumber}});
setPersonCharacterXrefs(newXrefs);
setRaidInstance({...raidInstance, numberRuns: (raidInstance?.numberRuns ?? 1) - 1} as RaidInstance);
}
const copyDiscordStringToClipBoard = (run: number) => {
let discordString = "";
//Instance name
discordString += `${raidInstance?.raidInstanceName}\n`;
//Start time
discordString += moment(raidInstance?.raidStartDate).format("MM/DD/YYYY HH:mm") + " - ";
//End time
discordString += moment(raidInstance?.raidEndDate).format("MM/DD/YYYY HH:mm") + "\n";
//Characters
characterGrid[run].forEach((ch, index) => {
const person = people.find((p) => p.personId === ch?.personId);
if(person){
//Discord ID / name
discordString += (person.discordId && (person.discordId !== "")) ? "@" + person.discordId : person.personName;
discordString += ": ";
//Character Name
discordString += ch?.characterName;
}
else{
const classGroup = selectedClassGroups[index];
//Class Group
discordString += classGroup?.classGroupName ?? "Any Class";
discordString += ": ";
//Any
discordString += "None";
}
discordString += "\n";
});
navigator.clipboard.writeText(discordString);
}
return (
<tbody
id="instanceTableBody"
>
{
characterGrid.map((run, runIndex) => (
<tr
key={runIndex}
>
{
run.map((ch, chIndex) => (
<td
key={chIndex}
className="px-4 py-2 border-2 cursor-default"
onClick={() => onClickBodyCell(runIndex, chIndex)}
>
<div
className="flex flex-row justify-start flex-nowrap"
>
{
ch?.gameClassId &&
<img
className="mr-2 max-h-8 max-w-8"
src={`${import.meta.env.VITE_ICON_URL}/gameClass/id/${ch.gameClassId}`}
/>
}
{ch ? ch.characterName : "None"}
</div>
</td>
))
}
<td
className="pl-2"
>
<div
className="flex flex-row justify-center items-center cursor-pointer p-2 space-x-2"
>
<DangerButton
variant="ghost"
shape="square"
onClick={() => removeRun(runIndex)}
>
<BsXLg
size={22}
strokeWidth={2}
/>
</DangerButton>
<TertiaryButton
variant="ghost"
shape="square"
onClick={() => copyDiscordStringToClipBoard(runIndex)}
>
<BsDiscord
size={22}
/>
</TertiaryButton>
</div>
</td>
</tr>
))
}
</tbody>
);
}

View File

@@ -0,0 +1,36 @@
import { useRaidInstanceContext } from "@/providers/RaidInstanceLayoutProvider";
export default function RaidInstanceCreatorTableHeader({
onClickHeaderCell
}:{
onClickHeaderCell: (slot: number) => void;
}){
const { selectedClassGroups } = useRaidInstanceContext();
return (
<thead>
<tr
id="instanceTableHead"
>
{
selectedClassGroups.map((classGroup, index) => (
<th
key={index}
className="px-4 py-2 border-2 cursor-pointer"
onClick={() => onClickHeaderCell(index)}
>
{classGroup?.classGroupName ?? "Any"}
</th>
))
}
<th
className="px-4 py-2"
>
&nbsp;
</th>
</tr>
</thead>
);
}

View File

@@ -0,0 +1,12 @@
import RaidInstanceHeader from "../RaidInstanceHeader";
import RaidInstanceCreator from "./RaidInstanceCreator";
export default function RaidInstanceCreatorUI(){
return (
<>
<RaidInstanceHeader/>
<RaidInstanceCreator/>
</>
);
}