diff --git a/src/components/modal/Modal.tsx b/src/components/modal/Modal.tsx index 3de180b..010ed41 100644 --- a/src/components/modal/Modal.tsx +++ b/src/components/modal/Modal.tsx @@ -20,6 +20,7 @@ export default function Modal(props: ModalProps){ delete divProps["backgroundClassName"]; delete divProps["close"]; delete divProps["className"]; + delete divProps["top"]; return ( diff --git a/src/providers/TimedModalProvider.tsx b/src/providers/TimedModalProvider.tsx index a0b8040..4cd8069 100644 --- a/src/providers/TimedModalProvider.tsx +++ b/src/providers/TimedModalProvider.tsx @@ -1,15 +1,21 @@ +import DangerMessage from "@/components/message/DangerMessage"; +import SuccessMessage from "@/components/message/SuccessMessage"; import Modal from "@/components/modal/Modal"; import ModalBody from "@/components/modal/ModalBody"; import { createContext, useContext, useEffect, useRef, useState } from "react"; type TimedModalProviderState = { - addMessage: (timeout: number, message: React.ReactNode) => void; + addMessage: (message: React.ReactNode, timeout: number) => void; + addSuccessMessage: (message: string) => void; + addErrorMessage: (message: string) => void; } const initialState: TimedModalProviderState = { - addMessage: () => null + addMessage: () => null, + addSuccessMessage: () => null, + addErrorMessage: () => null } const TimedModalProviderContext = createContext(initialState); @@ -27,8 +33,6 @@ export function TimedModalProvider({ useEffect(() => { - console.log("effect"); - console.log(messages); if(messages.length > 0){ setDisplay(true); } @@ -38,16 +42,30 @@ export function TimedModalProvider({ }, [ messages ]); - const addMessage = (timeout: number, message: React.ReactNode) => { + const addMessage = (message: React.ReactNode, timeout: number) => { setMessages([...messages, message]); setTimeout(() => { setMessages(messagesRef.current.filter((m) => m !== message)); }, timeout); } + const addSuccessMessage = (message: string) => { + addMessage({message}, 5000); + } + + const addErrorMessage = (message: string) => { + addMessage({message}, 5000); + } + + const currentContext = { + addMessage, + addSuccessMessage, + addErrorMessage + } + return ( - + - Accounts List Skeleton + ID + , +
+ Username +
, +
+ Email +
, +
+ Login Date +
, +
+ Status +
, +
+ Actions
+ ]; + const bodyElements: React.ReactNode[][] = [ + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton(), + AccountSkeleton() + ]; + + + return ( + ); } + +function AccountSkeleton(): React.ReactNode[]{ + const buttonsProps = { + buttonProps: { + variant: "ghost" as ButtonVariant, + size: "md" as ButtonSizeType, + shape: "square" as ButtonShape, + disabled: true + }, + showForcePasswordResetModal: () => {}, + showAccountPasswordSetModal: () => {}, + showRevokeRefreshTokenModal: () => {}, + showUpdateAccountModal: () => {}, + showDeleteAccountModal: () => {} + } + const elements: React.ReactNode[] = [ +
, +
, +
, +
, +
, +
+
 
+ +
+ ]; + + return elements; +} diff --git a/src/ui/account/AccountsLoader.tsx b/src/ui/account/AccountsLoader.tsx index 24acc2c..7956918 100644 --- a/src/ui/account/AccountsLoader.tsx +++ b/src/ui/account/AccountsLoader.tsx @@ -1,4 +1,5 @@ import PrimaryButton from "@/components/button/PrimaryButton"; +import DangerMessage from "@/components/message/DangerMessage"; import { useGetAccounts } from "@/hooks/AccountHooks"; import { useState } from "react"; import AccountsList from "./AccountsList"; @@ -16,13 +17,12 @@ export default function AccountsLoader(){ return } else if(accountsQuery.isError){ - //TODO: - return
Error: {accountsQuery.error.message}
+ return Error: {accountsQuery.error.message} } else{ return ( <> - {/* TODO: Add Account Button */} + {/* Add Account Button */} setDisplayCreateAccountModal(true)} diff --git a/src/ui/account/modals/AccountModal.tsx b/src/ui/account/modals/AccountModal.tsx index b410a89..9d950ad 100644 --- a/src/ui/account/modals/AccountModal.tsx +++ b/src/ui/account/modals/AccountModal.tsx @@ -6,6 +6,7 @@ import TextInput from "@/components/input/TextInput"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import { useCreateAccount, useUpdateAccount } from "@/hooks/AccountHooks"; import { Account, AccountStatus } from "@/interface/Account"; +import { useTimedModal } from "@/providers/TimedModalProvider"; import { useEffect, useState } from "react"; @@ -35,17 +36,31 @@ export default function AccountModal({ const updateAccountMutate = useUpdateAccount(); const createAccountMutate = useCreateAccount(); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); - if((updateAccountMutate.isSuccess) || (createAccountMutate.isSuccess)){ - updateAccountMutate.reset(); - createAccountMutate.reset(); - close(); - } - else if((updateAccountMutate.isError) || (updateAccountMutate.isError)){ - //TODO: Add message modal here - console.log(updateAccountMutate.error); - console.log(createAccountMutate.error); - } + + useEffect(() => { + if(createAccountMutate.isSuccess){ + createAccountMutate.reset(); + addSuccessMessage(`Account ${username} created successfully`); + close(); + } + else if(updateAccountMutate.isSuccess){ + updateAccountMutate.reset(); + addSuccessMessage(`Account ${username} updated successfully`); + close(); + } + else if(createAccountMutate.isError){ + createAccountMutate.reset(); + addErrorMessage(`Error creating account ${username}: ${createAccountMutate.error.message}`); + console.log(createAccountMutate.error); + } + else if(updateAccountMutate.isError){ + updateAccountMutate.reset(); + addErrorMessage(`Error updating account ${username}: ${updateAccountMutate.error.message}`); + console.log(updateAccountMutate.error); + } + }); const updateAccount = () => { diff --git a/src/ui/account/modals/AccountPasswordResetModal.tsx b/src/ui/account/modals/AccountPasswordResetModal.tsx index 61352bb..d757f5c 100644 --- a/src/ui/account/modals/AccountPasswordResetModal.tsx +++ b/src/ui/account/modals/AccountPasswordResetModal.tsx @@ -4,7 +4,8 @@ import PasswordInput from "@/components/input/PasswordInput"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import { useResetPassword } from "@/hooks/AccountHooks"; import { Account } from "@/interface/Account"; -import { useState } from "react"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect, useState } from "react"; export default function AccountPasswordRestModal({ @@ -17,6 +18,7 @@ export default function AccountPasswordRestModal({ account: Account | undefined; }){ const [ newPassword, setNewPassword ] = useState(""); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); const passwordResetMutate = useResetPassword(account?.accountId ?? ""); @@ -27,14 +29,18 @@ export default function AccountPasswordRestModal({ passwordResetMutate.mutate(newPassword); } - if(passwordResetMutate.isSuccess){ - passwordResetMutate.reset(); - close(); - } - else if(passwordResetMutate.isError){ - //TODO: Add message modal here - console.log(passwordResetMutate.error); - } + useEffect(() => { + if(passwordResetMutate.isSuccess){ + passwordResetMutate.reset(); + addSuccessMessage(`Successfully reset password for ${account?.username}`); + close(); + } + else if(passwordResetMutate.isError){ + passwordResetMutate.reset(); + addErrorMessage(`Failed to reset password for ${account?.username}: ${passwordResetMutate.error.message}`); + console.log(passwordResetMutate.error); + } + }); return ( diff --git a/src/ui/account/modals/DeleteAccountModal.tsx b/src/ui/account/modals/DeleteAccountModal.tsx index 58d461c..8469600 100644 --- a/src/ui/account/modals/DeleteAccountModal.tsx +++ b/src/ui/account/modals/DeleteAccountModal.tsx @@ -3,6 +3,8 @@ import SecondaryButton from "@/components/button/SecondaryButton"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import { useDeleteAccount } from "@/hooks/AccountHooks"; import { Account } from "@/interface/Account"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect } from "react"; export default function DeleteAccountModal({ @@ -15,20 +17,26 @@ export default function DeleteAccountModal({ account: Account | undefined; }){ const deleteAccountMutate = useDeleteAccount(account?.accountId ?? ""); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); const deleteAccount = () => { deleteAccountMutate.mutate(); } - if(deleteAccountMutate.isSuccess){ - deleteAccountMutate.reset(); - close(); - } - else if(deleteAccountMutate.isError){ - //TODO: Add message modal here - console.log(deleteAccountMutate.error); - } + useEffect(() => { + if(deleteAccountMutate.isSuccess){ + deleteAccountMutate.reset(); + addSuccessMessage(`Successfully deleted ${account?.username}`); + close(); + } + else if(deleteAccountMutate.isError){ + deleteAccountMutate.reset(); + addErrorMessage(`Error deleting ${account?.username}: ${deleteAccountMutate.error.message}`); + console.log(deleteAccountMutate.error); + } + }); + return ( void; account: Account | undefined; }){ - const accountMutate = useForcePasswordReset(account?.accountId ?? ""); + const forcePasswordResetMutate = useForcePasswordReset(account?.accountId ?? ""); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); const forcePasswordReset = () => { - accountMutate.mutate(); + forcePasswordResetMutate.mutate(); } - if(accountMutate.isSuccess){ - accountMutate.reset(); - close(); - } - else if(accountMutate.isError){ - //TODO: Add message modal here - console.log(accountMutate.error); - } + useEffect(() => { + if(forcePasswordResetMutate.isSuccess){ + forcePasswordResetMutate.reset(); + addSuccessMessage(`Successfully forced password reset for ${account?.username}`); + close(); + } + else if(forcePasswordResetMutate.isError){ + forcePasswordResetMutate.reset(); + addErrorMessage(`Error forcing password reset for ${account?.username}: ${forcePasswordResetMutate.error.message}`); + console.log(forcePasswordResetMutate.error); + } + }); return ( diff --git a/src/ui/account/modals/RevokeRefreshTokenModal.tsx b/src/ui/account/modals/RevokeRefreshTokenModal.tsx index 7670aac..8e3f86d 100644 --- a/src/ui/account/modals/RevokeRefreshTokenModal.tsx +++ b/src/ui/account/modals/RevokeRefreshTokenModal.tsx @@ -3,6 +3,8 @@ import SecondaryButton from "@/components/button/SecondaryButton"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import { useRevokeRefreshToken } from "@/hooks/AccountHooks"; import { Account } from "@/interface/Account"; +import { useTimedModal } from "@/providers/TimedModalProvider"; +import { useEffect } from "react"; export default function RevokeRefreshTokenModal({ @@ -15,20 +17,25 @@ export default function RevokeRefreshTokenModal({ account: Account | undefined; }){ const revokeRefreshTokenMutate = useRevokeRefreshToken(account?.accountId ?? ""); + const { addSuccessMessage, addErrorMessage } = useTimedModal(); const revokeRefreshToken = () => { revokeRefreshTokenMutate.mutate(); } - if(revokeRefreshTokenMutate.isSuccess){ - revokeRefreshTokenMutate.reset(); - close(); - } - else if(revokeRefreshTokenMutate.isError){ - //TODO: Add message modal here - console.log(revokeRefreshTokenMutate.error); - } + useEffect(() => { + if(revokeRefreshTokenMutate.isSuccess){ + revokeRefreshTokenMutate.reset(); + addSuccessMessage(`Refresh token for ${account?.username} was successfully revoked`); + close(); + } + else if(revokeRefreshTokenMutate.isError){ + revokeRefreshTokenMutate.reset(); + addErrorMessage(`Error revoking refresh token for ${account?.username}: ${revokeRefreshTokenMutate.error.message}`); + console.log(revokeRefreshTokenMutate.error); + } + }); return ( diff --git a/src/util/SkeletonUtil.ts b/src/util/SkeletonUtil.ts new file mode 100644 index 0000000..cca866c --- /dev/null +++ b/src/util/SkeletonUtil.ts @@ -0,0 +1 @@ +export const elementBg = "rounded-lg bg-neutral-700 dark:bg-neutral-200 animate-pulse";