Skip to content

Commit

Permalink
Merge pull request sinamics#474 from sinamics/performance
Browse files Browse the repository at this point in the history
Improved overall application performance
  • Loading branch information
sinamics authored Aug 4, 2024
2 parents e3b5db5 + 82fcb2d commit 9e15f58
Show file tree
Hide file tree
Showing 29 changed files with 163 additions and 314 deletions.
274 changes: 22 additions & 252 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@
"next-intl": "3.1.4",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.9",
"p-limit": "^6.1.0",
"pug": "^3.0.3",
"qrcode.react": "^3.1.0",
"react": "18.2.0",
"react-copy-to-clipboard": "^5.1.0",
Expand Down
92 changes: 82 additions & 10 deletions src/__tests__/components/modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,54 @@ import enTranslation from "~/locales/en/common.json";
import { NextIntlClientProvider } from "next-intl";
import Modal from "~/components/shared/modal";

jest.mock("../../utils/store", () => ({
jest.mock("~/utils/store", () => ({
useModalStore: jest.fn(),
}));

jest.mock("usehooks-ts", () => ({
useOnClickOutside: jest.fn(),
}));

// Get the mocked type
const mockedUseModalStore = useModalStore as jest.MockedFunction<typeof useModalStore>;

describe("Modal", () => {
const closeModal = jest.fn();
const toggleModal = jest.fn();
const yesAction = jest.fn();
const callModal = jest.fn();

beforeEach(() => {
(useModalStore as unknown as jest.Mock).mockImplementation(() => ({
isOpen: true,
description: "Test description",
title: "Test title",
yesAction,
toggleModal,
closeModal,
}));
mockedUseModalStore.mockImplementation((selector) =>
selector({
isOpen: true,
description: "Test description",
content: <div>Test content</div>,
title: "Test title",
rootStyle: "",
showButtons: true,
yesAction,
disableClickOutside: false,
toggleModal,
closeModal,
callModal,
}),
);
});

afterEach(() => {
jest.clearAllMocks();
});

test("renders modal with title and description", () => {
test("renders modal with title, description, and content", () => {
render(
<NextIntlClientProvider locale="en" messages={enTranslation}>
<Modal />
</NextIntlClientProvider>,
);
expect(screen.getByText("Test title")).toBeInTheDocument();
expect(screen.getByText("Test description")).toBeInTheDocument();
expect(screen.getByText("Test content")).toBeInTheDocument();
});

test("handles yes and cancel actions", () => {
Expand All @@ -51,4 +67,60 @@ describe("Modal", () => {
fireEvent.click(screen.getByText("Cancel"));
expect(closeModal).toHaveBeenCalledTimes(1);
});

test("renders close button when yesAction is not provided", () => {
mockedUseModalStore.mockImplementation((selector) =>
selector({
isOpen: true,
description: "Test description",
content: <div>Test content</div>,
title: "Test title",
rootStyle: "",
showButtons: true,
yesAction: null,
disableClickOutside: false,
toggleModal,
closeModal,
callModal,
}),
);

render(
<NextIntlClientProvider locale="en" messages={enTranslation}>
<Modal />
</NextIntlClientProvider>,
);

expect(screen.getByText("Close")).toBeInTheDocument();
fireEvent.click(screen.getByText("Close"));
expect(closeModal).toHaveBeenCalledTimes(1);
});

test("does not render buttons when showButtons is false", () => {
mockedUseModalStore.mockImplementation((selector) =>
selector({
isOpen: true,
description: "Test description",
content: <div>Test content</div>,
title: "Test title",
rootStyle: "",
showButtons: false,
yesAction,
disableClickOutside: false,
toggleModal,
closeModal,
callModal,
}),
);

render(
<NextIntlClientProvider locale="en" messages={enTranslation}>
<Modal />
</NextIntlClientProvider>,
);

expect(screen.queryByText("Yes")).not.toBeInTheDocument();
expect(screen.queryByText("Cancel")).not.toBeInTheDocument();
expect(screen.queryByText("Close")).not.toBeInTheDocument();
});
});
2 changes: 1 addition & 1 deletion src/components/adminPage/controller/privateRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CreatePlanet from "./createPlanet";
const PrivateRoot = () => {
const t = useTranslations("admin");
const [open, setOpen] = useState(false);
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const { data: getPlanet, refetch: refetchPlanet } = api.admin.getPlanet.useQuery();

const closeForm = () => setOpen(false);
Expand Down
2 changes: 1 addition & 1 deletion src/components/adminPage/controller/rootForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const RootNodesArray: React.FC<RootNodesArrayProps> = ({

const RootForm: React.FC<{ onClose: () => void }> = ({ onClose }) => {
const t = useTranslations("admin");
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const { data: getPlanet, refetch: refetchPlanet } = api.admin.getPlanet.useQuery();

const { data: getIdentity } = api.admin.getIdentity.useQuery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const UnlinkedNetwork = () => {
});
const initialValue = getValue();
// eslint-disable-next-line react-hooks/rules-of-hooks
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

const dropDownHandler = (e: React.ChangeEvent<HTMLSelectElement>, nwid: string) => {
const userId = e.target.value;
Expand Down
2 changes: 1 addition & 1 deletion src/components/adminPage/users/table/accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const Accounts = () => {
const t = useTranslations("admin");

const [globalFilter, setGlobalFilter] = useState("");
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const [sorting, setSorting] = useState<SortingState>(initialSortingState);
const { data: users, isLoading: loadingUsers } = api.admin.getUsers.useQuery({
isAdmin: false,
Expand Down
2 changes: 1 addition & 1 deletion src/components/adminPage/users/userGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const GroupLabel = ({ groups }: GroupLabelProps) => {
const handleApiSuccess = useTrpcApiSuccessHandler();

const { refetch: refetchGroups } = api.admin.getUserGroups.useQuery();
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

const { mutate: updateGroup } = api.admin.addUserGroup.useMutation({
onError: handleApiError,
Expand Down
4 changes: 3 additions & 1 deletion src/components/adminPage/users/userInvitation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ const InvitationLink = () => {
const handleApiError = useTrpcApiErrorHandler();
const handleApiSuccess = useTrpcApiSuccessHandler();

const { callModal, closeModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const closeModal = useModalStore((state) => state.closeModal);

const { data: invitationData, refetch: refetchInvitations } =
api.admin.getInvitationLink.useQuery();

Expand Down
2 changes: 1 addition & 1 deletion src/components/adminPage/users/userOptionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const UserOptionsModal = ({ userId }: Iprops) => {
const handleApiError = useTrpcApiErrorHandler();
const handleApiSuccess = useTrpcApiSuccessHandler();

const { closeModal } = useModalStore((state) => state);
const closeModal = useModalStore((state) => state.closeModal);

const { data: user, isLoading: loadingUser } = api.admin.getUser.useQuery({
userId: userId,
Expand Down
2 changes: 1 addition & 1 deletion src/components/networkByIdPage/ipv4Assignment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface IProp {

export const Ipv4Assignment = ({ central = false, organizationId }: IProp) => {
const t = useTranslations("networkById");
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

const handleApiError = useTrpcApiErrorHandler();
const handleApiSuccess = useTrpcApiSuccessHandler();
Expand Down
2 changes: 1 addition & 1 deletion src/components/networkByIdPage/memberOptionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const MemberOptionsModal: React.FC<ModalContentProps> = ({
const handleApiError = useTrpcApiErrorHandler();
const handleApiSuccess = useTrpcApiSuccessHandler();

// const { closeModal } = useModalStore((state) => state);
// const closeModal = useModalStore((state) => state.closeModal);
const [state, setState] = useState(initialIpState);
const [ipAssignments, seIpAssignments] = useState<string[]>([]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ListOrganizations = () => {
const t = useTranslations();
const b = useTranslations("commonButtons");
const { data: userOrgs } = api.org.getAllOrg.useQuery();
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

return (
<div className="space-y-10">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const DeletedNetworkMembersTable = ({ nwid, organizationId }: IProps) =>
desc: true,
},
]);
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const { data: networkById, refetch: refetchNetworkById } =
api.network.getNetworkById.useQuery(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const MemberHeaderColumns = ({ nwid, central = false, organizationId }: I
const handleApiError = useTrpcApiErrorHandler();
const handleApiSuccess = useTrpcApiSuccessHandler();

const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const { data: me } = api.auth.me.useQuery();
const { data: networkById, refetch: refetchNetworkById } =
api.network.getNetworkById.useQuery(
Expand Down
13 changes: 10 additions & 3 deletions src/components/networkByIdPage/table/networkMembersTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import {
useReactTable,
getCoreRowModel,
Expand Down Expand Up @@ -58,14 +58,21 @@ export const NetworkMembersTable = ({ nwid, central = false, organizationId }: I
);

const { data: me } = api.auth.me.useQuery();

// memorize the members for better performance.
const memoizedMembers = useMemo(
() => networkById?.members ?? [],
[networkById?.members],
);

// Save to localStorage whenever sorting changes
useEffect(() => {
setLocalStorageItem(LOCAL_STORAGE_KEY, sorting);
}, [sorting]);

useEffect(() => {
setData(networkById?.members ?? []);
}, [networkById?.members]);
setData(memoizedMembers);
}, [memoizedMembers]);

// const [data, setData] = useState(() => makeNetworkMemberData(11));
const [data, setData] = useState(networkById?.members ?? []);
Expand Down
2 changes: 1 addition & 1 deletion src/components/networkPage/networkOptionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const NetworkOptionsModal = ({ networkId }: Iprops) => {

const [action, setAction] = useState({ deleteNetwork: false, moveNetwork: false });
const [input, setInput] = useState({ organizationId: null });
const { closeModal } = useModalStore((state) => state);
const closeModal = useModalStore((state) => state.closeModal);

const { refetch: refetchNetwork } = api.network.getUserNetworks.useQuery({
central: false,
Expand Down
2 changes: 1 addition & 1 deletion src/components/networkPage/networkTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const NetworkTable = ({ tableData = [] }) => {
const router = useRouter();
const t = useTranslations();

const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

// Load initial state from localStorage or set to default
const initialSortingState = getLocalStorageItem(LOCAL_STORAGE_KEY, [
Expand Down
2 changes: 1 addition & 1 deletion src/components/organization/deleteOrganizationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const DeleteOrganizationModal = ({ org }) => {

const [input, setInput] = useState({ orgNameDelete: "" });

const { closeModal } = useModalStore((state) => state);
const closeModal = useModalStore((state) => state.closeModal);

const { refetch: refetchOrg } = api.org.getAllOrg.useQuery();

Expand Down
2 changes: 1 addition & 1 deletion src/components/organization/editUserModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const EditOrganizationUserModal = ({ user, organizationId }: Iprops) => {

const [deleted, setDelete] = useState(false);
const [input, setInput] = useState({ name: "" });
const { closeModal } = useModalStore((state) => state);
const closeModal = useModalStore((state) => state.closeModal);

const { refetch: refecthOrgUsers } = api.org.getOrgUsers.useQuery({
organizationId,
Expand Down
2 changes: 1 addition & 1 deletion src/components/organization/userTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const OrganizationUserTable = ({ organizationId }: Iprops) => {
organizationId,
});
const [data, setData] = useState(tableData || []);
const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);

// Load initial state from localStorage or set to default
const initialSortingState = getLocalStorageItem(LOCAL_STORAGE_KEY, [
Expand Down
31 changes: 17 additions & 14 deletions src/components/shared/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import cn from "classnames";
import { useRef } from "react";
import { useCallback, useRef } from "react";
import { useOnClickOutside } from "usehooks-ts";
import { useModalStore } from "~/utils/store";
import { useTranslations } from "next-intl";

const Modal = () => {
const t = useTranslations("commonButtons");
const ref = useRef(null);
const {
isOpen,
description,
content,
title,
rootStyle,
showButtons = true,
yesAction,
toggleModal,
disableClickOutside,
closeModal,
} = useModalStore((state) => state);
// Select only the necessary state
const isOpen = useModalStore((state) => state.isOpen);
const description = useModalStore((state) => state.description);
const content = useModalStore((state) => state.content);
const title = useModalStore((state) => state.title);
const rootStyle = useModalStore((state) => state.rootStyle);
const showButtons = useModalStore((state) => state.showButtons);
const yesAction = useModalStore((state) => state.yesAction);
const disableClickOutside = useModalStore((state) => state.disableClickOutside);

// Use separate selectors for actions to prevent unnecessary re-renders
const toggleModal = useModalStore(useCallback((state) => state.toggleModal, []));
const closeModal = useModalStore(useCallback((state) => state.closeModal, []));

// eslint-disable-next-line @typescript-eslint/no-unsafe-call
useOnClickOutside(ref, () => {
if (!disableClickOutside) {
closeModal();
}
});

const actionHandler = () => {
yesAction();
yesAction?.();
toggleModal();
};

const modalClass = cn({
"modal transition-none z-20": true,
"modal-open": isOpen,
Expand Down
2 changes: 1 addition & 1 deletion src/components/userSettings/apiToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ApiLables = ({ tokens }) => {

const { refetch } = api.auth.getApiToken.useQuery();

const { callModal } = useModalStore((state) => state);
const callModal = useModalStore((state) => state.callModal);
const { mutate: deleteToken } = api.auth.deleteApiToken.useMutation({
onError: (error) => {
if ((error.data as ErrorData)?.zodError) {
Expand Down
Loading

0 comments on commit 9e15f58

Please sign in to comment.