Add AccountList skeleton

This commit is contained in:
2025-03-02 14:39:39 -05:00
parent 3d06d8189d
commit d06d421d03
10 changed files with 203 additions and 56 deletions

View File

@@ -20,6 +20,7 @@ export default function Modal(props: ModalProps){
delete divProps["backgroundClassName"]; delete divProps["backgroundClassName"];
delete divProps["close"]; delete divProps["close"];
delete divProps["className"]; delete divProps["className"];
delete divProps["top"];
return ( return (

View File

@@ -1,15 +1,21 @@
import DangerMessage from "@/components/message/DangerMessage";
import SuccessMessage from "@/components/message/SuccessMessage";
import Modal from "@/components/modal/Modal"; import Modal from "@/components/modal/Modal";
import ModalBody from "@/components/modal/ModalBody"; import ModalBody from "@/components/modal/ModalBody";
import { createContext, useContext, useEffect, useRef, useState } from "react"; import { createContext, useContext, useEffect, useRef, useState } from "react";
type TimedModalProviderState = { 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 = { const initialState: TimedModalProviderState = {
addMessage: () => null addMessage: () => null,
addSuccessMessage: () => null,
addErrorMessage: () => null
} }
const TimedModalProviderContext = createContext<TimedModalProviderState>(initialState); const TimedModalProviderContext = createContext<TimedModalProviderState>(initialState);
@@ -27,8 +33,6 @@ export function TimedModalProvider({
useEffect(() => { useEffect(() => {
console.log("effect");
console.log(messages);
if(messages.length > 0){ if(messages.length > 0){
setDisplay(true); setDisplay(true);
} }
@@ -38,16 +42,30 @@ export function TimedModalProvider({
}, [ messages ]); }, [ messages ]);
const addMessage = (timeout: number, message: React.ReactNode) => { const addMessage = (message: React.ReactNode, timeout: number) => {
setMessages([...messages, message]); setMessages([...messages, message]);
setTimeout(() => { setTimeout(() => {
setMessages(messagesRef.current.filter((m) => m !== message)); setMessages(messagesRef.current.filter((m) => m !== message));
}, timeout); }, timeout);
} }
const addSuccessMessage = (message: string) => {
addMessage(<SuccessMessage>{message}</SuccessMessage>, 5000);
}
const addErrorMessage = (message: string) => {
addMessage(<DangerMessage>{message}</DangerMessage>, 5000);
}
const currentContext = {
addMessage,
addSuccessMessage,
addErrorMessage
}
return ( return (
<TimedModalProviderContext.Provider value={{addMessage}}> <TimedModalProviderContext.Provider value={currentContext}>
<Modal <Modal
display={display} display={display}
backgroundType="none" backgroundType="none"

View File

@@ -1,7 +1,91 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import Table from "@/components/table/Table";
import { elementBg } from "@/util/SkeletonUtil";
import AccountAdminButtons from "./AccountAdminButtons";
export default function AccountsListSkeleton(){ export default function AccountsListSkeleton(){
return ( const headElements: React.ReactNode[] = [
<div> <div>
Accounts List Skeleton ID
</div>,
<div>
Username
</div>,
<div>
Email
</div>,
<div>
Login Date
</div>,
<div>
Status
</div>,
<div
className="pl-16"
>
Actions
</div> </div>
];
const bodyElements: React.ReactNode[][] = [
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton(),
AccountSkeleton()
];
return (
<Table
tableHeadElements={headElements}
tableBodyElements={bodyElements}
/>
); );
} }
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[] = [
<div
className={`h-6 w-72 mr-1 ${elementBg}`}
/>,
<div
className={`h-6 w-48 ${elementBg}`}
/>,
<div
className={`h-6 w-80 ${elementBg}`}
/>,
<div
className={`h-6 w-64 ${elementBg}`}
/>,
<div
className={`h-6 w-28 ${elementBg}`}
/>,
<div
className={`flex flex-row items-center justify-center gap-2 pl-16`}
>
<div className="py-4 border-l border-neutral-500">&nbsp;</div>
<AccountAdminButtons {...buttonsProps}/>
</div>
];
return elements;
}

View File

@@ -1,4 +1,5 @@
import PrimaryButton from "@/components/button/PrimaryButton"; import PrimaryButton from "@/components/button/PrimaryButton";
import DangerMessage from "@/components/message/DangerMessage";
import { useGetAccounts } from "@/hooks/AccountHooks"; import { useGetAccounts } from "@/hooks/AccountHooks";
import { useState } from "react"; import { useState } from "react";
import AccountsList from "./AccountsList"; import AccountsList from "./AccountsList";
@@ -16,13 +17,12 @@ export default function AccountsLoader(){
return <AccountsListSkeleton/> return <AccountsListSkeleton/>
} }
else if(accountsQuery.isError){ else if(accountsQuery.isError){
//TODO: return <DangerMessage>Error: {accountsQuery.error.message}</DangerMessage>
return <div>Error: {accountsQuery.error.message}</div>
} }
else{ else{
return ( return (
<> <>
{/* TODO: Add Account Button */} {/* Add Account Button */}
<PrimaryButton <PrimaryButton
className="mb-8" className="mb-8"
onClick={() => setDisplayCreateAccountModal(true)} onClick={() => setDisplayCreateAccountModal(true)}

View File

@@ -6,6 +6,7 @@ import TextInput from "@/components/input/TextInput";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useCreateAccount, useUpdateAccount } from "@/hooks/AccountHooks"; import { useCreateAccount, useUpdateAccount } from "@/hooks/AccountHooks";
import { Account, AccountStatus } from "@/interface/Account"; import { Account, AccountStatus } from "@/interface/Account";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@@ -35,17 +36,31 @@ export default function AccountModal({
const updateAccountMutate = useUpdateAccount(); const updateAccountMutate = useUpdateAccount();
const createAccountMutate = useCreateAccount(); const createAccountMutate = useCreateAccount();
const { addSuccessMessage, addErrorMessage } = useTimedModal();
if((updateAccountMutate.isSuccess) || (createAccountMutate.isSuccess)){
updateAccountMutate.reset(); useEffect(() => {
if(createAccountMutate.isSuccess){
createAccountMutate.reset(); createAccountMutate.reset();
addSuccessMessage(`Account ${username} created successfully`);
close(); close();
} }
else if((updateAccountMutate.isError) || (updateAccountMutate.isError)){ else if(updateAccountMutate.isSuccess){
//TODO: Add message modal here updateAccountMutate.reset();
console.log(updateAccountMutate.error); addSuccessMessage(`Account ${username} updated successfully`);
close();
}
else if(createAccountMutate.isError){
createAccountMutate.reset();
addErrorMessage(`Error creating account ${username}: ${createAccountMutate.error.message}`);
console.log(createAccountMutate.error); console.log(createAccountMutate.error);
} }
else if(updateAccountMutate.isError){
updateAccountMutate.reset();
addErrorMessage(`Error updating account ${username}: ${updateAccountMutate.error.message}`);
console.log(updateAccountMutate.error);
}
});
const updateAccount = () => { const updateAccount = () => {

View File

@@ -4,7 +4,8 @@ import PasswordInput from "@/components/input/PasswordInput";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useResetPassword } from "@/hooks/AccountHooks"; import { useResetPassword } from "@/hooks/AccountHooks";
import { Account } from "@/interface/Account"; import { Account } from "@/interface/Account";
import { useState } from "react"; import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect, useState } from "react";
export default function AccountPasswordRestModal({ export default function AccountPasswordRestModal({
@@ -17,6 +18,7 @@ export default function AccountPasswordRestModal({
account: Account | undefined; account: Account | undefined;
}){ }){
const [ newPassword, setNewPassword ] = useState<string>(""); const [ newPassword, setNewPassword ] = useState<string>("");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const passwordResetMutate = useResetPassword(account?.accountId ?? ""); const passwordResetMutate = useResetPassword(account?.accountId ?? "");
@@ -27,14 +29,18 @@ export default function AccountPasswordRestModal({
passwordResetMutate.mutate(newPassword); passwordResetMutate.mutate(newPassword);
} }
useEffect(() => {
if(passwordResetMutate.isSuccess){ if(passwordResetMutate.isSuccess){
passwordResetMutate.reset(); passwordResetMutate.reset();
addSuccessMessage(`Successfully reset password for ${account?.username}`);
close(); close();
} }
else if(passwordResetMutate.isError){ else if(passwordResetMutate.isError){
//TODO: Add message modal here passwordResetMutate.reset();
addErrorMessage(`Failed to reset password for ${account?.username}: ${passwordResetMutate.error.message}`);
console.log(passwordResetMutate.error); console.log(passwordResetMutate.error);
} }
});
return ( return (

View File

@@ -3,6 +3,8 @@ import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useDeleteAccount } from "@/hooks/AccountHooks"; import { useDeleteAccount } from "@/hooks/AccountHooks";
import { Account } from "@/interface/Account"; import { Account } from "@/interface/Account";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function DeleteAccountModal({ export default function DeleteAccountModal({
@@ -15,20 +17,26 @@ export default function DeleteAccountModal({
account: Account | undefined; account: Account | undefined;
}){ }){
const deleteAccountMutate = useDeleteAccount(account?.accountId ?? ""); const deleteAccountMutate = useDeleteAccount(account?.accountId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const deleteAccount = () => { const deleteAccount = () => {
deleteAccountMutate.mutate(); deleteAccountMutate.mutate();
} }
useEffect(() => {
if(deleteAccountMutate.isSuccess){ if(deleteAccountMutate.isSuccess){
deleteAccountMutate.reset(); deleteAccountMutate.reset();
addSuccessMessage(`Successfully deleted ${account?.username}`);
close(); close();
} }
else if(deleteAccountMutate.isError){ else if(deleteAccountMutate.isError){
//TODO: Add message modal here deleteAccountMutate.reset();
addErrorMessage(`Error deleting ${account?.username}: ${deleteAccountMutate.error.message}`);
console.log(deleteAccountMutate.error); console.log(deleteAccountMutate.error);
} }
});
return ( return (
<RaidBuilderModal <RaidBuilderModal

View File

@@ -3,6 +3,8 @@ import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useForcePasswordReset } from "@/hooks/AccountHooks"; import { useForcePasswordReset } from "@/hooks/AccountHooks";
import { Account } from "@/interface/Account"; import { Account } from "@/interface/Account";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function ForcePasswordResetModal({ export default function ForcePasswordResetModal({
@@ -14,21 +16,26 @@ export default function ForcePasswordResetModal({
close: () => void; close: () => void;
account: Account | undefined; account: Account | undefined;
}){ }){
const accountMutate = useForcePasswordReset(account?.accountId ?? ""); const forcePasswordResetMutate = useForcePasswordReset(account?.accountId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const forcePasswordReset = () => { const forcePasswordReset = () => {
accountMutate.mutate(); forcePasswordResetMutate.mutate();
} }
if(accountMutate.isSuccess){ useEffect(() => {
accountMutate.reset(); if(forcePasswordResetMutate.isSuccess){
forcePasswordResetMutate.reset();
addSuccessMessage(`Successfully forced password reset for ${account?.username}`);
close(); close();
} }
else if(accountMutate.isError){ else if(forcePasswordResetMutate.isError){
//TODO: Add message modal here forcePasswordResetMutate.reset();
console.log(accountMutate.error); addErrorMessage(`Error forcing password reset for ${account?.username}: ${forcePasswordResetMutate.error.message}`);
console.log(forcePasswordResetMutate.error);
} }
});
return ( return (

View File

@@ -3,6 +3,8 @@ import SecondaryButton from "@/components/button/SecondaryButton";
import RaidBuilderModal from "@/components/modal/RaidBuilderModal"; import RaidBuilderModal from "@/components/modal/RaidBuilderModal";
import { useRevokeRefreshToken } from "@/hooks/AccountHooks"; import { useRevokeRefreshToken } from "@/hooks/AccountHooks";
import { Account } from "@/interface/Account"; import { Account } from "@/interface/Account";
import { useTimedModal } from "@/providers/TimedModalProvider";
import { useEffect } from "react";
export default function RevokeRefreshTokenModal({ export default function RevokeRefreshTokenModal({
@@ -15,20 +17,25 @@ export default function RevokeRefreshTokenModal({
account: Account | undefined; account: Account | undefined;
}){ }){
const revokeRefreshTokenMutate = useRevokeRefreshToken(account?.accountId ?? ""); const revokeRefreshTokenMutate = useRevokeRefreshToken(account?.accountId ?? "");
const { addSuccessMessage, addErrorMessage } = useTimedModal();
const revokeRefreshToken = () => { const revokeRefreshToken = () => {
revokeRefreshTokenMutate.mutate(); revokeRefreshTokenMutate.mutate();
} }
useEffect(() => {
if(revokeRefreshTokenMutate.isSuccess){ if(revokeRefreshTokenMutate.isSuccess){
revokeRefreshTokenMutate.reset(); revokeRefreshTokenMutate.reset();
addSuccessMessage(`Refresh token for ${account?.username} was successfully revoked`);
close(); close();
} }
else if(revokeRefreshTokenMutate.isError){ else if(revokeRefreshTokenMutate.isError){
//TODO: Add message modal here revokeRefreshTokenMutate.reset();
addErrorMessage(`Error revoking refresh token for ${account?.username}: ${revokeRefreshTokenMutate.error.message}`);
console.log(revokeRefreshTokenMutate.error); console.log(revokeRefreshTokenMutate.error);
} }
});
return ( return (

1
src/util/SkeletonUtil.ts Normal file
View File

@@ -0,0 +1 @@
export const elementBg = "rounded-lg bg-neutral-700 dark:bg-neutral-200 animate-pulse";