Buttons hidden by permissions

This commit is contained in:
2025-03-15 16:51:13 -04:00
parent 56236fd2ac
commit a842c24d0d
44 changed files with 624 additions and 94 deletions

View File

@@ -3,10 +3,14 @@ import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import TertiaryButton from "@/components/button/TertiaryButton";
import WarningButton from "@/components/button/WarningButton";
import { Account } from "@/interface/Account";
import { useTheme } from "@/providers/ThemeProvider";
import { BsKeyFill, BsLockFill, BsPencilFill, BsTrash3, BsXCircle } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function AccountAdminButtons({
account,
buttonProps,
showForcePasswordResetModal,
showAccountPasswordSetModal,
@@ -14,6 +18,7 @@ export default function AccountAdminButtons({
showUpdateAccountModal,
showDeleteAccountModal
}:{
account?: Account;
buttonProps: ButtonProps;
showForcePasswordResetModal: () => void;
showAccountPasswordSetModal: () => void;
@@ -21,13 +26,20 @@ export default function AccountAdminButtons({
showUpdateAccountModal: () => void;
showDeleteAccountModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
>
<WarningButton
{...buttonProps}
id={`accountAdminButtonsForceReset${componentId}`}
onClick={showForcePasswordResetModal}
aria-label={`Force Password Reset for ${account?.username}`}
data-tooltip-delay-show={750}
>
<BsLockFill
size={22}
@@ -35,7 +47,10 @@ export default function AccountAdminButtons({
</WarningButton>
<DangerButton
{...buttonProps}
id={`accountAdminButtonsPasswordSet${componentId}`}
onClick={showAccountPasswordSetModal}
aria-label={`Set Password for ${account?.username}`}
data-tooltip-delay-show={750}
>
<BsKeyFill
size={22}
@@ -43,7 +58,10 @@ export default function AccountAdminButtons({
</DangerButton>
<TertiaryButton
{...buttonProps}
id={`accountAdminButtonsRevokeRefreshToken${componentId}`}
onClick={showRevokeRefreshTokenModal}
aria-label={`Revoke Refresh Token for ${account?.username}`}
data-tooltip-delay-show={750}
>
<BsXCircle
size={22}
@@ -51,7 +69,10 @@ export default function AccountAdminButtons({
</TertiaryButton>
<PrimaryButton
{...buttonProps}
id={`accountAdminButtonsUpdate${componentId}`}
onClick={showUpdateAccountModal}
aria-label={`Update ${account?.username}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -59,12 +80,50 @@ export default function AccountAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`accountAdminButtonsDelete${componentId}`}
onClick={showDeleteAccountModal}
aria-label={`Delete ${account?.username}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#accountAdminButtonsForceReset${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Force Password Reset
</Tooltip>
<Tooltip
anchorSelect={`#accountAdminButtonsPasswordSet${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Set Password
</Tooltip>
<Tooltip
anchorSelect={`#accountAdminButtonsRevokeRefreshToken${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Revoke Refresh Token
</Tooltip>
<Tooltip
anchorSelect={`#accountAdminButtonsUpdate${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Update {account?.username}
</Tooltip>
<Tooltip
anchorSelect={`#accountAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {account?.username}
</Tooltip>
</div>
);
}

View File

@@ -2,6 +2,8 @@ import { ButtonProps } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { Account } from "@/interface/Account";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isSiteAdmin } from "@/util/PermissionUtil";
import moment from "moment";
import { useState } from "react";
import AccountAdminButtons from "./AccountAdminButtons";
@@ -22,6 +24,7 @@ export default function AccountsList({
accounts: Account[];
raidGroup?: RaidGroup;
}){
const { accountPermissions } = useAuth();
const [ selectedAccount, setSelectedAccount ] = useState<Account | undefined>(undefined);
const [ displayForcePasswordResetModal, setDisplayForcePasswordResetModal ] = useState(false);
const [ displayAccountPasswordSetModal, setDisplayAccountPasswordSetModal ] = useState(false);
@@ -40,12 +43,10 @@ export default function AccountsList({
const headElements: React.ReactNode[] = [
<div>
ID
</div>,
<div>
Username
</div>,
isSiteAdmin(accountPermissions) &&
<div>
Email
</div>,
@@ -64,20 +65,18 @@ export default function AccountsList({
const bodyElements: React.ReactNode[][] = accounts.map((account) => [
<div
className="text-nowrap"
className="text-nowrap pl-2 text-start"
>
{account.accountId}
</div>,
<div>
{account.username}
</div>,
isSiteAdmin(accountPermissions) &&
<div>
{account.email}
</div>,
<div
className="text-nowrap"
>
{moment(account.loginDate).format("MM-DD-YYYY HH:mm")}
{account.loginDate ? moment(account.loginDate).format("MM-DD-YYYY HH:mm") : <>&nbsp;</>}
</div>,
<div>
{account.accountStatus}
@@ -90,29 +89,33 @@ export default function AccountsList({
>
&nbsp;
</div>
<AccountAdminButtons
buttonProps={buttonProps}
showForcePasswordResetModal={() => {
setSelectedAccount(account);
setDisplayForcePasswordResetModal(true);
}}
showAccountPasswordSetModal={() => {
setSelectedAccount(account);
setDisplayAccountPasswordSetModal(true);
}}
showRevokeRefreshTokenModal={() => {
setSelectedAccount(account);
setDisplayRevokeRefreshTokenModal(true);
}}
showUpdateAccountModal={() => {
setSelectedAccount(account);
setDisplayAccountModal(true);
}}
showDeleteAccountModal={() => {
setSelectedAccount(account);
setDisplayDeleteAccountModal(true);
}}
/>
{
isSiteAdmin(accountPermissions) &&
<AccountAdminButtons
account={account}
buttonProps={buttonProps}
showForcePasswordResetModal={() => {
setSelectedAccount(account);
setDisplayForcePasswordResetModal(true);
}}
showAccountPasswordSetModal={() => {
setSelectedAccount(account);
setDisplayAccountPasswordSetModal(true);
}}
showRevokeRefreshTokenModal={() => {
setSelectedAccount(account);
setDisplayRevokeRefreshTokenModal(true);
}}
showUpdateAccountModal={() => {
setSelectedAccount(account);
setDisplayAccountModal(true);
}}
showDeleteAccountModal={() => {
setSelectedAccount(account);
setDisplayDeleteAccountModal(true);
}}
/>
}
{
raidGroup &&
<RaidGroupAccountAdminButtons

View File

@@ -1,7 +1,9 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import WarningButton from "@/components/button/WarningButton";
import { useTheme } from "@/providers/ThemeProvider";
import { BsKeyFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function RaidGroupAccountAdminButtons({
@@ -13,13 +15,20 @@ export default function RaidGroupAccountAdminButtons({
showRaidGroupPermissionsModal: () => void;
showRemoveFromRaidGroupModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
>
<WarningButton
{...buttonProps}
id={`raidGroupAccountAdminButtonsPermissions${componentId}`}
onClick={showRaidGroupPermissionsModal}
aria-label="Edit Permissions"
data-tooltip-delay-show={750}
>
<BsKeyFill
size={22}
@@ -27,12 +36,29 @@ export default function RaidGroupAccountAdminButtons({
</WarningButton>
<DangerButton
{...buttonProps}
id={`raidGroupAccountAdminButtonsRemove${componentId}`}
onClick={showRemoveFromRaidGroupModal}
aria-label="Remove from Raid Group"
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#raidGroupAccountAdminButtonsPermissions${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit Permissions
</Tooltip>
<Tooltip
anchorSelect={`#raidGroupAccountAdminButtonsRemove${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Remove from Raid Group
</Tooltip>
</div>
);
}

View File

@@ -43,7 +43,7 @@ export default function RaidGroupAccountsTab({
return (
<>
<div
className="flex flex-row items-center justify-between w-full"
className="flex flex-row items-center justify-between w-full mb-8"
>
<div
className="flex flex-row items-center justify-center w-full"

View File

@@ -31,7 +31,7 @@ export default function AccountModal({
setEmail(account?.email ?? "");
setPassword(account?.password ?? "");
setAccountStatus(account?.accountStatus ?? AccountStatus.ACTIVE);
}, [ account, setUsername, setEmail, setPassword, setAccountStatus ]);
}, [ display, account, setUsername, setEmail, setPassword, setAccountStatus ]);
const updateAccountMutate = useUpdateAccount();
@@ -103,10 +103,13 @@ export default function AccountModal({
/>
)
}
<AccountStatusSelector
value={accountStatus}
onChange={(e) => setAccountStatus(e.currentTarget.value as AccountStatus)}
/>
{
account &&
<AccountStatusSelector
value={accountStatus}
onChange={(e) => setAccountStatus(e.currentTarget.value as AccountStatus)}
/>
}
</div>
}
modalFooter={

View File

@@ -1,6 +1,8 @@
import { useCreateGameCalendarEvent, useCreateRaidGroupCalendarEvent, useDeleteGameCalendarEvent, useDeleteRaidGroupCalendarEvent, useUpdateGameCalendarEvent, useUpdateRaidGroupCalendarEvent } from "@/hooks/CalendarHooks";
import { CalendarEvent } from "@/interface/Calendar";
import { useAuth } from "@/providers/AuthProvider";
import { calendarEventToFullCalendarEvent } from "@/util/CalendarUtil";
import { isGameAdmin, isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { EventClickArg } from "@fullcalendar/core/index.js";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin, { DateClickArg } from "@fullcalendar/interaction";
@@ -19,6 +21,7 @@ export default function CalendarDisplay({
raidGroupId?: string;
gameId?: string;
}){
const { accountPermissions, gamePermissions, raidGroupPermissions } = useAuth();
const [ displayCalendarEventModal, setDisplayCalendarEventModal ] = useState(false);
const [ alterCalendarEvent, setAlterCalendarEvent ] = useState<CalendarEvent>();
const [ disableModal, setDisableModal ] = useState(false);
@@ -39,6 +42,15 @@ export default function CalendarDisplay({
eventStartDate: dateClickArg.date,
eventEndDate: moment(dateClickArg.date).add(1, "hours").toDate()
} as CalendarEvent);
if(gameId && !isGameAdmin(gameId, gamePermissions, accountPermissions)){
setDisableModal(true);
}
if(raidGroupId && !isRaidGroupAdmin(raidGroupId, raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroupId, raidGroupPermissions, accountPermissions)){
setDisableModal(true);
}
setDisplayCalendarEventModal(true);
}
@@ -47,6 +59,12 @@ export default function CalendarDisplay({
if(raidGroupId && calendarEvent?.gameId){
setDisableModal(true);
}
else if(calendarEvent?.gameId && !isGameAdmin(calendarEvent?.gameId, gamePermissions, accountPermissions)){
setDisableModal(true);
}
else if(calendarEvent?.raidGroupId && !isRaidGroupAdmin(calendarEvent?.raidGroupId, raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(calendarEvent?.raidGroupId, raidGroupPermissions, accountPermissions)){
setDisableModal(true);
}
setAlterCalendarEvent(calendarEvent);
setDisplayCalendarEventModal(true);

View File

@@ -1,5 +1,7 @@
import { ButtonProps } from "@/components/button/Button";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import RaidGroupAdminButtons from "../raidGroup/RaidGroupAdminButtons";
import DeleteRaidGroupModal from "../raidGroup/modals/DeleteRaidGroupModal";
@@ -11,6 +13,7 @@ export default function RaidGroupHeader({
}:{
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ displayEditRaidGroupModal, setDisplayEditRaidGroupModal ] = useState(false);
const [ displayDeleteRaidGroupModal, setDisplayDeleteRaidGroupModal ] = useState(false);
@@ -39,7 +42,11 @@ export default function RaidGroupHeader({
</div>
<div>
<RaidGroupAdminButtons
buttonProps={buttonProps}
raidGroup={raidGroup}
buttonProps={{
...buttonProps,
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)
}}
showEditRaidGroupModal={() => setDisplayEditRaidGroupModal(true)}
showDeleteRaidGroupModal={() => setDisplayDeleteRaidGroupModal(true)}
hasRaidGroupPermissions={true}

View File

@@ -182,6 +182,7 @@ export default function CalendarEventModal({
variant="ghost"
shape="square"
onClick={deleteCalendarEvent}
disabled={disabled}
>
<BsTrash3
size={22}

View File

@@ -1,25 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { ClassGroup } from "@/interface/ClassGroup";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function ClassGroupButtons({
classGroup,
buttonProps,
showClassGroupModal,
showDeleteClassGroupModal
}:{
classGroup: ClassGroup;
buttonProps: ButtonProps;
showClassGroupModal: () => void;
showDeleteClassGroupModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
id={`classGroupAdminButtonsEdit${componentId}`}
onClick={showClassGroupModal}
aria-label={`Edit ${classGroup.classGroupName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -27,12 +39,29 @@ export default function ClassGroupButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`classGroupAdminButtonsDelete${componentId}`}
onClick={showDeleteClassGroupModal}
aria-label={`Delete ${classGroup.classGroupName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#classGroupAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {classGroup.classGroupName}
</Tooltip>
<Tooltip
anchorSelect={`#classGroupAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {classGroup.classGroupName}
</Tooltip>
</div>
);
}

View File

@@ -4,6 +4,8 @@ import Table from "@/components/table/Table";
import { useGetGameClassesByClassGroup } from "@/hooks/GameClassHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useState } from "react";
import ClassGroupButtons from "./ClassGroupButtons";
import ClassGroupModal from "./modal/ClassGroupModal";
@@ -17,6 +19,7 @@ export default function ClassGroupList({
classGroups: ClassGroup[];
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ selectedClassGroup, setSelectedClassGroup ] = useState<ClassGroup>();
const [ displayClassGroupModal, setDisplayClassGroupModal ] = useState(false);
const [ displayDeleteClassGroupModal, setDisplayDeleteClassGroupModal ] = useState(false);
@@ -26,7 +29,8 @@ export default function ClassGroupList({
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
shape: "square",
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId!, raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId!, raidGroupPermissions, accountPermissions)
};
@@ -64,6 +68,7 @@ export default function ClassGroupList({
&nbsp;
</div>
<ClassGroupButtons
classGroup={classGroup}
buttonProps={buttonProps}
showClassGroupModal={() => {
setSelectedClassGroup(classGroup);

View File

@@ -3,6 +3,8 @@ import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetClassGroupsCount } from "@/hooks/ClassGroupHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import ClassGroupsLoader from "./ClassGroupsLoader";
@@ -14,6 +16,7 @@ export default function ClassGroupsTab({
}:{
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ displayCreateClassGroupModal, setDisplayCreateClassGroupModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
@@ -60,6 +63,7 @@ export default function ClassGroupsTab({
<PrimaryButton
className="text-nowrap"
onClick={() => setDisplayCreateClassGroupModal(true)}
disabled={!isRaidGroupAdmin(raidGroup.raidGroupId!, raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId!, raidGroupPermissions, accountPermissions)}
>
Create Class Group
</PrimaryButton>

View File

@@ -2,6 +2,8 @@ import PrimaryButton from "@/components/button/PrimaryButton";
import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetGamesCount } from "@/hooks/GameHooks";
import { useAuth } from "@/providers/AuthProvider";
import { isSiteAdmin } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import { GamesLoader } from "./GamesLoader";
@@ -9,6 +11,7 @@ import GameModal from "./modals/GameModal";
export default function AllGamesDisplay(){
const { accountPermissions } = useAuth();
const [ displayCreateGameModal, setDisplayCreateGameModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
@@ -54,6 +57,7 @@ export default function AllGamesDisplay(){
<PrimaryButton
className="mb-8"
onClick={() => setDisplayCreateGameModal(true)}
disabled={!isSiteAdmin(accountPermissions)}
>
Create Game
</PrimaryButton>

View File

@@ -1,25 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { Game } from "@/interface/Game";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function GameAdminButtons({
game,
buttonProps,
showEditGameModal,
showDeleteGameModal
}:{
game: Game;
buttonProps: ButtonProps;
showEditGameModal: () => void;
showDeleteGameModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row items-center justify-center gap-2"
>
<PrimaryButton
{...buttonProps}
id={`gameAdminButtonsEdit${componentId}`}
onClick={showEditGameModal}
aria-label={`Edit ${game.gameName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -27,12 +39,29 @@ export default function GameAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`gameAdminButtonsDelete${componentId}`}
onClick={showDeleteGameModal}
aria-label={`Delete ${game.gameName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#gameAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {game.gameName}
</Tooltip>
<Tooltip
anchorSelect={`#gameAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {game.gameName}
</Tooltip>
</div>
);
}

View File

@@ -1,5 +1,7 @@
import { ButtonProps } from "@/components/button/Button";
import { Game } from "@/interface/Game";
import { useAuth } from "@/providers/AuthProvider";
import { isGameAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import GameAdminButtons from "./GameAdminButtons";
import DeleteGameModal from "./modals/DeleteGameModal";
@@ -11,13 +13,15 @@ export default function GameHeader({
}:{
game: Game;
}){
const { gamePermissions, accountPermissions } = useAuth();
const [ displayEditGameModal, setDisplayEditGameModal ] = useState(false);
const [ displayDeleteGameModal, setDisplayDeleteGameModal ] = useState(false);
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
shape: "square",
disabled: !isGameAdmin(game.gameId ?? "", gamePermissions, accountPermissions)
};
@@ -39,6 +43,7 @@ export default function GameHeader({
</div>
<div>
<GameAdminButtons
game={game}
buttonProps={buttonProps}
showEditGameModal={() => setDisplayEditGameModal(true)}
showDeleteGameModal={() => setDisplayDeleteGameModal(true)}

View File

@@ -1,6 +1,8 @@
import { ButtonProps } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { Game } from "@/interface/Game";
import { useAuth } from "@/providers/AuthProvider";
import { isGameAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import { Link } from "react-router";
import GameAdminButtons from "./GameAdminButtons";
@@ -13,6 +15,7 @@ export default function GamesList({
}:{
games: Game[];
}){
const { accountPermissions, gamePermissions } = useAuth();
const [ selectedGame, setSelectedGame ] = useState<Game>();
const [ displayEditGameModal, setDisplayEditGameModal ] = useState(false);
const [ displayDeleteGameModal, setDisplayDeleteGameModal ] = useState(false);
@@ -68,7 +71,11 @@ export default function GamesList({
&nbsp;
</div>
<GameAdminButtons
buttonProps={buttonProps}
game={game}
buttonProps={{
...buttonProps,
disabled: !isGameAdmin(game.gameId ?? "", gamePermissions, accountPermissions)
}}
showEditGameModal={() => {
setSelectedGame(game);
setDisplayEditGameModal(true);

View File

@@ -1,5 +1,6 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { Game } from "@/interface/Game";
import { elementBg } from "@/util/SkeletonUtil";
import React from "react";
import GameAdminButtons from "./GameAdminButtons";
@@ -64,7 +65,7 @@ function GameSkeleton(): React.ReactNode[]{
className={`flex flex-row items-center justify-center gap-2 pl-16`}
>
<div className="py-4 border-l border-neutral-500">&nbsp;</div>
<GameAdminButtons {...buttonsProps}/>
<GameAdminButtons game={{} as Game} {...buttonsProps}/>
</div>
];

View File

@@ -1,25 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { GameClass } from "@/interface/GameClass";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function GameClassAdminButtons({
gameClass,
buttonProps,
showEditGameClassModal,
showDeleteGameClassModal
}:{
gameClass: GameClass;
buttonProps: ButtonProps;
showEditGameClassModal: () => void;
showDeleteGameClassModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row items-center justify-center gap-2"
>
<PrimaryButton
{...buttonProps}
id={`gameClassAdminButtonsEdit${componentId}`}
onClick={showEditGameClassModal}
aria-label={`Edit ${gameClass.gameClassName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -27,12 +39,29 @@ export default function GameClassAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`gameClassAdminButtonsDelete${componentId}`}
onClick={showDeleteGameClassModal}
aria-label={`Delete ${gameClass.gameClassName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#gameClassAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {gameClass.gameClassName}
</Tooltip>
<Tooltip
anchorSelect={`#gameClassAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {gameClass.gameClassName}
</Tooltip>
</div>
);
}

View File

@@ -1,5 +1,7 @@
import PrimaryButton from "@/components/button/PrimaryButton";
import TextInput from "@/components/input/TextInput";
import { useAuth } from "@/providers/AuthProvider";
import { isGameAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import GameClassModal from "./modals/GameClassModal";
@@ -13,6 +15,7 @@ export default function GameClassCreateAndSearch({
searchTerm: string;
setSearchTerm: (searchTerm: string) => void;
}){
const { gamePermissions, accountPermissions } = useAuth();
const [ displayGameClassModal, setDisplayGameClassModal ] = useState(false);
const modalId = crypto.randomUUID().replaceAll("-", "");
@@ -33,6 +36,7 @@ export default function GameClassCreateAndSearch({
<PrimaryButton
className="mb-8"
onClick={() => setDisplayGameClassModal(true)}
disabled={!isGameAdmin(gameId, gamePermissions, accountPermissions)}
>
Create Game Class
</PrimaryButton>

View File

@@ -1,6 +1,8 @@
import { ButtonProps } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { GameClass } from "@/interface/GameClass";
import { useAuth } from "@/providers/AuthProvider";
import { isGameAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import GameClassAdminButtons from "./GameClassAdminButtons";
import DeleteGameClassModal from "./modals/DeleteGameClassModal";
@@ -12,6 +14,7 @@ export default function GameClassList({
}:{
gameClasses: GameClass[];
}){
const { gamePermissions, accountPermissions } = useAuth();
const [ selectedGameClass, setSelectedGameClass ] = useState<GameClass>();
const [ displayGameClassModal, setDisplayGameClassModal ] = useState(false);
const [ displayDeleteGameClassModal, setDisplayDeleteGameClassModal ] = useState(false);
@@ -65,7 +68,11 @@ export default function GameClassList({
&nbsp;
</div>
<GameClassAdminButtons
buttonProps={buttonProps}
gameClass={gameClass}
buttonProps={{
...buttonProps,
disabled: !isGameAdmin(gameClass.gameId, gamePermissions, accountPermissions)
}}
showEditGameClassModal={() => {
setSelectedGameClass(gameClass);
setDisplayGameClassModal(true);

View File

@@ -1,5 +1,6 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { GameClass } from "@/interface/GameClass";
import { elementBg } from "@/util/SkeletonUtil";
import GameClassAdminButtons from "./GameClassAdminButtons";
@@ -49,6 +50,7 @@ function GameClassSkeleton(): React.ReactNode[]{
shape: "square" as ButtonShape,
disabled: true
},
gameClass:{} as GameClass,
showEditGameClassModal: () => {},
showDeleteGameClassModal: () => {}
}

View File

@@ -2,20 +2,29 @@ import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import SuccessButton from "@/components/button/SuccessButton";
import { Person } from "@/interface/Person";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsPlusLg, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function PersonAdminButtons({
person,
buttonProps,
showUpdatePersonModal,
showDeletePersonModal,
showCreatePersonCharacterModal
}:{
person?: Person;
buttonProps: ButtonProps;
showUpdatePersonModal: () => void;
showDeletePersonModal: () => void;
showCreatePersonCharacterModal?: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
@@ -24,8 +33,11 @@ export default function PersonAdminButtons({
showCreatePersonCharacterModal &&
<SuccessButton
{...buttonProps}
id={`personAdminButtonsAddCharacter${componentId}`}
size="sm"
onClick={showCreatePersonCharacterModal}
aria-label={`Add Character to ${person?.personName}`}
data-tooltip-delay-show={750}
>
<BsPlusLg
size={30}
@@ -35,7 +47,10 @@ export default function PersonAdminButtons({
}
<PrimaryButton
{...buttonProps}
id={`personAdminButtonsEdit${componentId}`}
onClick={showUpdatePersonModal}
aria-label={`Edit ${person?.personName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -43,12 +58,36 @@ export default function PersonAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`personAdminButtonsDelete${componentId}`}
onClick={showDeletePersonModal}
aria-label={`Delete ${person?.personName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#personAdminButtonsAddCharacter${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Add Character to {person?.personName}
</Tooltip>
<Tooltip
anchorSelect={`#personAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {person?.personName}
</Tooltip>
<Tooltip
anchorSelect={`#personAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {person?.personName}
</Tooltip>
</div>
);
}

View File

@@ -3,6 +3,8 @@ import PersonCharacterDisplay from "@/components/personCharacter/PersonCharacter
import Table from "@/components/table/Table";
import { Person } from "@/interface/Person";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useState } from "react";
import { Link } from "react-router";
import PersonCharacterModal from "../personCharacter/modal/PersonCharacterModal";
@@ -18,6 +20,7 @@ export default function PersonList({
people: Person[];
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ selectedPerson, setSelectedPerson ] = useState<Person>();
const [ displayUpdatePersonModal, setDisplayUpdatePersonModal ] = useState(false);
const [ displayDeletePersonModal, setDisplayDeletePersonModal ] = useState(false);
@@ -27,7 +30,8 @@ export default function PersonList({
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
shape: "square",
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)
};
@@ -78,6 +82,7 @@ export default function PersonList({
&nbsp;
</div>
<PersonAdminButtons
person={person}
buttonProps={buttonProps}
showUpdatePersonModal={() => {
setSelectedPerson(person);

View File

@@ -3,6 +3,8 @@ import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetPeopleByRaidGroupCount } from "@/hooks/PersonHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import PersonModal from "./modals/PersonModal";
@@ -14,6 +16,7 @@ export default function PersonTab({
}:{
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ displayCreatePersonModal, setDisplayCreatePersonModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
@@ -60,6 +63,7 @@ export default function PersonTab({
<PrimaryButton
className="text-nowrap"
onClick={() => setDisplayCreatePersonModal(true)}
disabled={!isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)}
>
Create Person
</PrimaryButton>

View File

@@ -2,10 +2,14 @@ import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import TertiaryButton from "@/components/button/TertiaryButton";
import { RaidGroup } from "@/interface/RaidGroup";
import { useTheme } from "@/providers/ThemeProvider";
import { BsChatRightText, BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function RaidGroupAdminButtons({
raidGroup,
buttonProps,
showRaidGroupRequestModal,
showEditRaidGroupModal,
@@ -13,6 +17,7 @@ export default function RaidGroupAdminButtons({
hasRaidGroupPermissions,
hasRaidGroupRequest
}:{
raidGroup: RaidGroup;
buttonProps: ButtonProps;
showRaidGroupRequestModal?: () => void;
showEditRaidGroupModal: () => void;
@@ -20,6 +25,10 @@ export default function RaidGroupAdminButtons({
hasRaidGroupPermissions: boolean;
hasRaidGroupRequest: boolean;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row items-center justify-center gap-2"
@@ -27,9 +36,12 @@ export default function RaidGroupAdminButtons({
{
!hasRaidGroupPermissions &&
<TertiaryButton
id={`raidGroupRequestButton${componentId}`}
{...buttonProps}
onClick={showRaidGroupRequestModal}
disabled={hasRaidGroupRequest}
aria-label={`Request to join ${raidGroup.raidGroupName}`}
data-tooltip-delay-show={750}
>
<BsChatRightText
size={22}
@@ -37,21 +49,48 @@ export default function RaidGroupAdminButtons({
</TertiaryButton>
}
<PrimaryButton
id={`raidGroupEditButton${componentId}`}
{...buttonProps}
onClick={showEditRaidGroupModal}
aria-label={`Edit ${raidGroup.raidGroupName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
/>
</PrimaryButton>
<DangerButton
id={`raidGroupDeleteButton${componentId}`}
{...buttonProps}
onClick={showDeleteRaidGroupModal}
aria-label={`Delete ${raidGroup.raidGroupName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#raidGroupRequestButton${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Request to join {raidGroup.raidGroupName}
</Tooltip>
<Tooltip
anchorSelect={`#raidGroupEditButton${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {raidGroup.raidGroupName}
</Tooltip>
<Tooltip
anchorSelect={`#raidGroupDeleteButton${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {raidGroup.raidGroupName}
</Tooltip>
</div>
);
}

View File

@@ -2,7 +2,7 @@ import { ButtonProps } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { containsRaidGroupPermission, containsRaidGroupRequest } from "@/util/PermissionUtil";
import { containsRaidGroupPermission, containsRaidGroupRequest, isRaidGroupAdmin } from "@/util/PermissionUtil";
import { useState } from "react";
import { Link } from "react-router";
import RaidGroupRequestModal from "../raidGroupRequest/modal/RaidGroupRequestModal";
@@ -20,7 +20,7 @@ export default function RaidGroupsList({
const [ displayRaidGroupRequestModal, setDisplayRaidGroupRequestModal ] = useState(false);
const [ displayEditRaidGroupModal, setDisplayEditRaidGroupModal ] = useState(false);
const [ displayDeleteRaidGroupModal, setDisplayDeleteRaidGroupModal ] = useState(false);
const { raidGroupPermissions, raidGroupRequests } = useAuth();
const { accountPermissions, raidGroupPermissions, raidGroupRequests } = useAuth();
const buttonProps: ButtonProps = {
@@ -73,7 +73,11 @@ export default function RaidGroupsList({
&nbsp;
</div>
<RaidGroupAdminButtons
buttonProps={buttonProps}
raidGroup={raidGroup}
buttonProps={{
...buttonProps,
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !containsRaidGroupRequest(raidGroup.raidGroupId ?? "", raidGroupRequests)
}}
showRaidGroupRequestModal={() => {
setSelectedRaidGroup(raidGroup);
setDisplayRaidGroupRequestModal(true);
@@ -86,7 +90,7 @@ export default function RaidGroupsList({
setSelectedRaidGroup(raidGroup);
setDisplayDeleteRaidGroupModal(true);
}}
hasRaidGroupPermissions={containsRaidGroupPermission(raidGroup.raidGroupId ?? "", raidGroupPermissions)}
hasRaidGroupPermissions={containsRaidGroupPermission(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)}
hasRaidGroupRequest={containsRaidGroupRequest(raidGroup.raidGroupId ?? "", raidGroupRequests)}
/>
</div>

View File

@@ -1,5 +1,6 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { RaidGroup } from "@/interface/RaidGroup";
import { elementBg } from "@/util/SkeletonUtil";
import RaidGroupAdminButtons from "./RaidGroupAdminButtons";
@@ -49,8 +50,11 @@ function RaidGroupSkeleton(): React.ReactNode[]{
shape: "square" as ButtonShape,
disabled: true
},
showRaidGroupRequestModal: () => {},
showEditRaidGroupModal: () => {},
showDeleteRaidGroupModal: () => {}
showDeleteRaidGroupModal: () => {},
hasRaidGroupPermissions: false,
hasRaidGroupRequest: false
}
const elements: React.ReactNode[] = [
<div
@@ -63,7 +67,7 @@ function RaidGroupSkeleton(): React.ReactNode[]{
className={`flex flex-row items-center justify-center gap-2 pl-16`}
>
<div className="py-4 border-l border-neutral-500">&nbsp;</div>
<RaidGroupAdminButtons {...buttonsProps}/>
<RaidGroupAdminButtons raidGroup={{} as RaidGroup} {...buttonsProps}/>
</div>
];

View File

@@ -32,7 +32,7 @@ export default function RaidGroupModal({
setRaidGroupName(raidGroup?.raidGroupName ?? "");
setRaidGroupIcon(raidGroup?.raidGroupIcon ?? "");
setIconFile(null);
}, [ raidGroup, setRaidGroupName, setRaidGroupIcon ]);
}, [ display, raidGroup, setRaidGroupName, setRaidGroupIcon ]);
const updateRaidGroupMutate = useUpdateRaidGroup();
@@ -47,25 +47,25 @@ export default function RaidGroupModal({
useEffect(() => {
if(updateRaidGroupMutate.status === "success"){
updateRaidGroupMutate.reset();
addSuccessMessage(`Updated raid group ${raidGroupName}`);
addSuccessMessage(`Updated raid group ${raidGroup?.raidGroupName}`);
close();
}
else if(updateRaidGroupMutate.status === "error"){
updateRaidGroupMutate.reset();
addErrorMessage(`Error updating raid group ${raidGroupName}: ${updateRaidGroupMutate.error.message}`);
addErrorMessage(`Error updating raid group ${raidGroup?.raidGroupName}: ${updateRaidGroupMutate.error.message}`);
console.log(updateRaidGroupMutate.error);
}
else if(createRaidGroupMutate.status === "success"){
createRaidGroupMutate.reset();
addSuccessMessage(`Created raid group ${raidGroupName}`);
addSuccessMessage(`Created raid group ${raidGroup?.raidGroupName}`);
close();
}
else if(createRaidGroupMutate.status === "error"){
createRaidGroupMutate.reset();
addErrorMessage(`Error creating raid group ${raidGroupName}: ${createRaidGroupMutate.error.message}`);
addErrorMessage(`Error creating raid group ${raidGroup?.raidGroupName}: ${createRaidGroupMutate.error.message}`);
console.log(createRaidGroupMutate.error);
}
}, [ updateRaidGroupMutate, createRaidGroupMutate, raidGroupName, close, addSuccessMessage, addErrorMessage ]);
}, [ updateRaidGroupMutate, createRaidGroupMutate, raidGroup, close, addSuccessMessage, addErrorMessage ]);
const updateRaidGroup = () => {

View File

@@ -1,25 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { RaidGroupRequest } from "@/interface/RaidGroupRequest";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function RaidGroupRequestButtons({
request,
buttonProps,
showRaidGroupRequestModal,
showDeleteRaidGroupRequestModal
}:{
request: RaidGroupRequest;
buttonProps: ButtonProps;
showRaidGroupRequestModal: () => void;
showDeleteRaidGroupRequestModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row items-center justify-center w-full gap-2"
>
<PrimaryButton
{...buttonProps}
id={`raidGroupRequestButtonsResolve${componentId}`}
onClick={showRaidGroupRequestModal}
aria-label={`Resolve raid group request from ${request.username}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -27,12 +39,29 @@ export default function RaidGroupRequestButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`raidGroupRequestButtonsDelete${componentId}`}
onClick={showDeleteRaidGroupRequestModal}
aria-label={`Delete raid group request from ${request.username}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#raidGroupRequestButtonsResolve${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Resolve raid group request from {request.username}
</Tooltip>
<Tooltip
anchorSelect={`#raidGroupRequestButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete raid group request from {request.username}
</Tooltip>
</div>
);
}

View File

@@ -31,14 +31,22 @@ export default function RaidGroupRequestList({
<div>
Username
</div>,
<div>
Message
</div>,
<div>
Actions
</div>
];
const bodyElements: React.ReactNode[][] = raidGroupRequests.map((request) => [
<div>
<div
className="text-nowrap"
>
{request.username}
</div>,
<div>
{request.requestMessage || <>&nbsp;</>}
</div>,
<div
className="flex flex-row items-center justify-center gap-2"
>
@@ -50,6 +58,7 @@ export default function RaidGroupRequestList({
{
raidGroup &&
<RaidGroupRequestButtons
request={request}
buttonProps={buttonProps}
showRaidGroupRequestModal={() => { setSelectedRequest(request); setDisplayRequestModal(true); }}
showDeleteRaidGroupRequestModal={() => { setSelectedRequest(request); setDisplayDeleteRequestModal(true); }}

View File

@@ -42,7 +42,7 @@ export default function RaidGroupRequestTab({
return (
<>
<div
className="flex flex-row items-center justify-between w-full"
className="flex flex-row items-center justify-between w-full mb-8"
>
<div
className="flex flex-row items-center justify-start w-full"

View File

@@ -1,25 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { RaidInstance } from "@/interface/RaidInstance";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function RaidInstanceAdminButtons({
raidInstance,
buttonProps,
showRaidInstanceModal,
showDeleteRaidInstanceModal
}:{
raidInstance?: RaidInstance;
buttonProps: ButtonProps;
showRaidInstanceModal: () => void;
showDeleteRaidInstanceModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
id={`raidInstanceAdminButtonsEdit${componentId}`}
onClick={showRaidInstanceModal}
aria-label={`Edit ${raidInstance?.raidInstanceName}`}
data-tooltip-delay-show={750}
>
<BsPencilFill
size={22}
@@ -27,12 +39,29 @@ export default function RaidInstanceAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`raidInstanceAdminButtonsDelete${componentId}`}
onClick={showDeleteRaidInstanceModal}
aria-label={`Delete ${raidInstance?.raidInstanceName}`}
data-tooltip-delay-show={750}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#raidInstanceAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {raidInstance?.raidInstanceName}
</Tooltip>
<Tooltip
anchorSelect={`#raidInstanceAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {raidInstance?.raidInstanceName}
</Tooltip>
</div>
);
}

View File

@@ -2,6 +2,8 @@ import { ButtonProps } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { RaidGroup } from "@/interface/RaidGroup";
import { RaidInstance } from "@/interface/RaidInstance";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import moment from "moment";
import { useState } from "react";
import { Link } from "react-router";
@@ -17,6 +19,7 @@ export default function RaidInstanceList({
raidInstances: RaidInstance[];
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ selectedRaidInstance, setSelectedRaidInstance ] = useState<RaidInstance>();
const [ displayEditRaidInstanceModal, setDisplayEditRaidInstanceModal ] = useState(false);
const [ displayDeleteRaidInstanceModal, setDisplayDeleteRaidInstanceModal ] = useState(false);
@@ -25,7 +28,8 @@ export default function RaidInstanceList({
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
shape: "square",
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) || !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)
};
@@ -94,6 +98,7 @@ export default function RaidInstanceList({
&nbsp;
</div>
<RaidInstanceAdminButtons
raidInstance={raidInstance}
buttonProps={buttonProps}
showRaidInstanceModal={() => { setSelectedRaidInstance(raidInstance); setDisplayEditRaidInstanceModal(true); }}
showDeleteRaidInstanceModal={() => { setSelectedRaidInstance(raidInstance); setDisplayDeleteRaidInstanceModal(true); }}

View File

@@ -3,6 +3,8 @@ import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetRaidInstancesByRaidGroupCount } from "@/hooks/RaidInstanceHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import RaidInstanceLoader from "./RaidInstanceLoader";
@@ -14,6 +16,7 @@ export default function RaidInstanceTab({
}:{
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ displayCreateRaidInstanceModal, setDisplayCreateRaidInstanceModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
@@ -59,6 +62,7 @@ export default function RaidInstanceTab({
<PrimaryButton
className="text-nowrap"
onClick={() => setDisplayCreateRaidInstanceModal(true)}
disabled={!isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)}
>
Create Raid Instance
</PrimaryButton>

View File

@@ -1,24 +1,37 @@
import { ButtonProps } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import { RaidLayout } from "@/interface/RaidLayout";
import { useTheme } from "@/providers/ThemeProvider";
import { BsPencilFill, BsTrash3 } from "react-icons/bs";
import { Tooltip } from "react-tooltip";
export default function RaidLayoutAdminButtons({
raidLayout,
buttonProps,
showRaidLayoutModal,
showDeleteRaidLayoutModal
}:{
raidLayout: RaidLayout;
buttonProps: ButtonProps;
showRaidLayoutModal: () => void;
showDeleteRaidLayoutModal: () => void;
}){
const { theme } = useTheme();
const componentId = crypto.randomUUID().replaceAll("-", "");
return (
<div
className="flex flex-row gap-2"
>
<PrimaryButton
{...buttonProps}
id={`raidLayoutAdminButtonsEdit${componentId}`}
onClick={showRaidLayoutModal}
aria-label={`Edit ${raidLayout.raidLayoutName}`}
data-tooltip-id={`raidLayoutAdminButtonsEdit${componentId}`}
>
<BsPencilFill
size={22}
@@ -26,12 +39,29 @@ export default function RaidLayoutAdminButtons({
</PrimaryButton>
<DangerButton
{...buttonProps}
id={`raidLayoutAdminButtonsDelete${componentId}`}
onClick={showDeleteRaidLayoutModal}
aria-label={`Delete ${raidLayout.raidLayoutName}`}
data-tooltip-id={`raidLayoutAdminButtonsDelete${componentId}`}
>
<BsTrash3
size={22}
/>
</DangerButton>
<Tooltip
anchorSelect={`#raidLayoutAdminButtonsEdit${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Edit {raidLayout.raidLayoutName}
</Tooltip>
<Tooltip
anchorSelect={`#raidLayoutAdminButtonsDelete${componentId}`}
place="top"
variant={theme === "dark" ? "light" : "dark"}
>
Delete {raidLayout.raidLayoutName}
</Tooltip>
</div>
);
}

View File

@@ -5,6 +5,8 @@ import { useGetClassGroupsByRaidLayout } from "@/hooks/ClassGroupHooks";
import { ClassGroup } from "@/interface/ClassGroup";
import { RaidGroup } from "@/interface/RaidGroup";
import { RaidLayout } from "@/interface/RaidLayout";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import DeleteRaidLayoutModal from "./modal/DeleteRaidLayoutModal";
import RaidLayoutModal from "./modal/RaidLayoutModal";
@@ -18,6 +20,7 @@ export default function RaidLayoutList({
raidLayouts: RaidLayout[];
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ selectedRaidLayout, setSelectedRaidLayout ] = useState<RaidLayout>();
const [ displayEditRaidLayoutModal, showEditRaidLayoutModal ] = useState(false);
const [ displayDeleteRaidLayoutModal, showDeleteRaidLayoutModal ] = useState(false);
@@ -32,7 +35,8 @@ export default function RaidLayoutList({
const buttonProps: ButtonProps = {
variant: "ghost",
size: "md",
shape: "square"
shape: "square",
disabled: !isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)
};
@@ -78,6 +82,7 @@ export default function RaidLayoutList({
&nbsp;
</div>
<RaidLayoutAdminButtons
raidLayout={raidLayout}
buttonProps={buttonProps}
showRaidLayoutModal={() => { setSelectedRaidLayout(raidLayout); showEditRaidLayoutModal(true); }}
showDeleteRaidLayoutModal={() => { setSelectedRaidLayout(raidLayout); showDeleteRaidLayoutModal(true); }}

View File

@@ -3,6 +3,8 @@ import TextInput from "@/components/input/TextInput";
import Pagination from "@/components/pagination/Pagination";
import { useGetRaidLayoutsByRaidGroupCount } from "@/hooks/RaidLayoutHooks";
import { RaidGroup } from "@/interface/RaidGroup";
import { useAuth } from "@/providers/AuthProvider";
import { isRaidGroupAdmin, isRaidGroupLeader } from "@/util/PermissionUtil";
import { useEffect, useState } from "react";
import { useDebouncedCallback } from "use-debounce";
import RaidLayoutModal from "./modal/RaidLayoutModal";
@@ -14,6 +16,7 @@ export default function RaidLayoutTab({
}:{
raidGroup: RaidGroup;
}){
const { accountPermissions, raidGroupPermissions } = useAuth();
const [ displayCreateRaidLayoutModal, setDisplayCreateRaidLayoutModal ] = useState(false);
const [ page, setPage ] = useState(1);
const [ totalPages, setTotalPages ] = useState(1);
@@ -59,6 +62,7 @@ export default function RaidLayoutTab({
<PrimaryButton
className="text-nowrap"
onClick={() => setDisplayCreateRaidLayoutModal(true)}
disabled={!isRaidGroupAdmin(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions) && !isRaidGroupLeader(raidGroup.raidGroupId ?? "", raidGroupPermissions, accountPermissions)}
>
Create Raid Layout
</PrimaryButton>