diff --git a/src/Common/DatabaseAccountUtility.ts b/src/Common/DatabaseAccountUtility.ts index 41f8c4b0a..7bcf26480 100644 --- a/src/Common/DatabaseAccountUtility.ts +++ b/src/Common/DatabaseAccountUtility.ts @@ -44,8 +44,8 @@ export const getDatabaseEndpoint = (apiType: ApiType): string => { return "gremlinDatabases"; case "Tables": return "tables"; - default: case "SQL": + default: return "sqlDatabases"; } }; @@ -58,8 +58,8 @@ export const getCollectionEndpoint = (apiType: ApiType): string => { return "tables"; case "Gremlin": return "graphs"; - default: case "SQL": + default: return "containers"; } }; diff --git a/src/Explorer/ContainerCopy/Actions/CopyJobActions.tsx b/src/Explorer/ContainerCopy/Actions/CopyJobActions.tsx index 14198b700..20fa8bf92 100644 --- a/src/Explorer/ContainerCopy/Actions/CopyJobActions.tsx +++ b/src/Explorer/ContainerCopy/Actions/CopyJobActions.tsx @@ -74,7 +74,6 @@ export const getCopyJobs = async (): Promise => { } copyJobsAbortController = null; - /* added a lower bound to "0" and upper bound to "100" */ const calculateCompletionPercentage = (processed: number, total: number): number => { if ( typeof processed !== "number" || diff --git a/src/Explorer/ContainerCopy/CopyJobUtils.ts b/src/Explorer/ContainerCopy/CopyJobUtils.ts index 1a9e46aad..e2d358a60 100644 --- a/src/Explorer/ContainerCopy/CopyJobUtils.ts +++ b/src/Explorer/ContainerCopy/CopyJobUtils.ts @@ -1,5 +1,5 @@ import { DatabaseAccount } from "Contracts/DataModels"; -import { CopyJobErrorType } from "./Types/CopyJobTypes"; +import { CopyJobErrorType, CopyJobType } from "./Types/CopyJobTypes"; const azurePortalMpacEndpoint = "https://ms.portal.azure.com/"; @@ -124,3 +124,15 @@ export function isIntraAccountCopy(sourceAccountId: string | undefined, targetAc sourceAccountDetails?.accountName === targetAccountDetails?.accountName ); } +export function isEqual(prevJobs: CopyJobType[], newJobs: CopyJobType[]): boolean { + if (prevJobs.length !== newJobs.length) { + return false; + } + return prevJobs.every((prevJob: CopyJobType) => { + const newJob = newJobs.find((job) => job.Name === prevJob.Name); + if (!newJob) { + return false; + } + return prevJob.Status === newJob.Status; + }); +} diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx index 160c6d973..075257644 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx @@ -1,5 +1,5 @@ import { Link, Stack, Text, Toggle } from "@fluentui/react"; -import React, { useCallback } from "react"; +import React from "react"; import { logError } from "../../../../../Common/Logger"; import { assignRole } from "../../../../../Utils/arm/RbacUtils"; import ContainerCopyMessages from "../../../ContainerCopyMessages"; @@ -25,7 +25,7 @@ const AddReadPermissionToDefaultIdentity: React.FC { + const handleAddReadPermission = async () => { const { source, target } = copyJobState; const selectedSourceAccount = source?.account; try { @@ -56,7 +56,7 @@ const AddReadPermissionToDefaultIdentity: React.FC diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useManagedIdentity.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useManagedIdentity.tsx index 9ac826e8c..559789191 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useManagedIdentity.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useManagedIdentity.tsx @@ -47,7 +47,7 @@ const useManagedIdentity = ( } finally { setLoading(false); } - }, [copyJobState, updateIdentityFn, setCopyJobState]); + }, [updateIdentityFn]); return { loading, handleAddSystemIdentity }; }; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/AccountDropdown.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/AccountDropdown.tsx index 423920b43..33ca7ec24 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/AccountDropdown.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/AccountDropdown.tsx @@ -26,5 +26,5 @@ export const AccountDropdown: React.FC = React.memo( onChange={onChange} /> - ), + ), (prev, next) => prev.options.length === next.options.length ); diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/SubscriptionDropdown.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/SubscriptionDropdown.tsx index 67b8e4f87..911938891 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/SubscriptionDropdown.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Components/SubscriptionDropdown.tsx @@ -24,5 +24,5 @@ export const SubscriptionDropdown: React.FC = React.m onChange={onChange} /> - ), + ), (prev, next) => prev.options.length === next.options.length ); diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx index adb36b3a1..e03672206 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx @@ -11,25 +11,17 @@ export function useDropdownOptions( subscriptionOptions: DropdownOptionType[]; accountOptions: DropdownOptionType[]; } { - const subscriptionOptions = React.useMemo( - () => - subscriptions?.map((sub) => ({ - key: sub.subscriptionId, - text: sub.displayName, - data: sub, - })) || [], - [subscriptions], - ); + const subscriptionOptions = subscriptions?.map((sub) => ({ + key: sub.subscriptionId, + text: sub.displayName, + data: sub, + })) || []; - const accountOptions = React.useMemo( - () => - accounts?.map((account) => ({ - key: account.id, - text: account.name, - data: account, - })) || [], - [accounts], - ); + const accountOptions = accounts?.map((account) => ({ + key: account.id, + text: account.name, + data: account, + })) || []; return { subscriptionOptions, accountOptions }; } @@ -38,8 +30,7 @@ type setCopyJobStateType = CopyJobContextProviderType["setCopyJobState"]; export function useEventHandlers(setCopyJobState: setCopyJobStateType) { const { setValidationCache } = useCopyJobPrerequisitesCache(); - const handleSelectSourceAccount = React.useCallback( - (type: "subscription" | "account", data: (Subscription & DatabaseAccount) | undefined) => { + const handleSelectSourceAccount = (type: "subscription" | "account", data: (Subscription & DatabaseAccount) | undefined) => { setCopyJobState((prevState: CopyJobContextState) => { if (type === "subscription") { return { @@ -63,9 +54,7 @@ export function useEventHandlers(setCopyJobState: setCopyJobStateType) { return prevState; }); setValidationCache(new Map()); - }, - [setCopyJobState, setValidationCache], - ); + }; const handleMigrationTypeChange = React.useCallback( (_ev?: React.FormEvent, checked?: boolean) => { @@ -75,7 +64,7 @@ export function useEventHandlers(setCopyJobState: setCopyJobStateType) { })); setValidationCache(new Map()); }, - [setCopyJobState, setValidationCache], + [], ); return { handleSelectSourceAccount, handleMigrationTypeChange }; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers.tsx index fb42c3a69..f3517c567 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers.tsx @@ -7,36 +7,40 @@ import ContainerCopyMessages from "../../../ContainerCopyMessages"; import { useCopyJobContext } from "../../../Context/CopyJobContext"; import { DatabaseContainerSection } from "./components/DatabaseContainerSection"; import { dropDownChangeHandler } from "./Events/DropDownChangeHandler"; -import { useMemoizedSourceAndTargetData } from "./memoizedData"; +import { useSourceAndTargetData } from "./memoizedData"; const SelectSourceAndTargetContainers = () => { const { copyJobState, setCopyJobState } = useCopyJobContext(); const { source, target, sourceDbParams, sourceContainerParams, targetDbParams, targetContainerParams } = - useMemoizedSourceAndTargetData(copyJobState); + useSourceAndTargetData(copyJobState); - const sourceDatabases = useDatabases(...sourceDbParams) || []; - const sourceContainers = useDataContainers(...sourceContainerParams) || []; - const targetDatabases = useDatabases(...targetDbParams) || []; - const targetContainers = useDataContainers(...targetContainerParams) || []; + if(!source) { + return null; + } + + const sourceDatabases = useDatabases(...sourceDbParams); + const sourceContainers = useDataContainers(...sourceContainerParams); + const targetDatabases = useDatabases(...targetDbParams); + const targetContainers = useDataContainers(...targetContainerParams); const sourceDatabaseOptions = React.useMemo( - () => sourceDatabases.map((db: DatabaseModel) => ({ key: db.name, text: db.name, data: db })), + () => sourceDatabases?.map((db: DatabaseModel) => ({ key: db.name, text: db.name, data: db })) || [], [sourceDatabases], ); const sourceContainerOptions = React.useMemo( - () => sourceContainers.map((c: DatabaseModel) => ({ key: c.name, text: c.name, data: c })), + () => sourceContainers?.map((c: DatabaseModel) => ({ key: c.name, text: c.name, data: c })) || [], [sourceContainers], ); const targetDatabaseOptions = React.useMemo( - () => targetDatabases.map((db: DatabaseModel) => ({ key: db.name, text: db.name, data: db })), + () => targetDatabases?.map((db: DatabaseModel) => ({ key: db.name, text: db.name, data: db })) || [], [targetDatabases], ); const targetContainerOptions = React.useMemo( - () => targetContainers.map((c: DatabaseModel) => ({ key: c.name, text: c.name, data: c })), + () => targetContainers?.map((c: DatabaseModel) => ({ key: c.name, text: c.name, data: c })) || [], [targetContainers], ); - const onDropdownChange = React.useCallback(dropDownChangeHandler(setCopyJobState), [setCopyJobState]); + const onDropdownChange = dropDownChangeHandler(setCopyJobState); return ( diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/memoizedData.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/memoizedData.tsx index 627bc8812..7a100aadb 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/memoizedData.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectSourceAndTargetContainers/memoizedData.tsx @@ -1,8 +1,7 @@ -import React from "react"; import { getAccountDetailsFromResourceId } from "../../../CopyJobUtils"; import { CopyJobContextState, DatabaseParams, DataContainerParams } from "../../../Types/CopyJobTypes"; -export function useMemoizedSourceAndTargetData(copyJobState: CopyJobContextState) { +export function useSourceAndTargetData(copyJobState: CopyJobContextState) { const { source, target } = copyJobState ?? {}; const selectedSourceAccount = source?.account; const selectedTargetAccount = target?.account; @@ -17,27 +16,10 @@ export function useMemoizedSourceAndTargetData(copyJobState: CopyJobContextState accountName: targetAccountName, } = getAccountDetailsFromResourceId(selectedTargetAccount?.id); - const sourceDbParams = React.useMemo( - () => [sourceSubscriptionId, sourceResourceGroup, sourceAccountName, "SQL"] as DatabaseParams, - [sourceSubscriptionId, sourceResourceGroup, sourceAccountName], - ); - - const sourceContainerParams = React.useMemo( - () => - [sourceSubscriptionId, sourceResourceGroup, sourceAccountName, source?.databaseId, "SQL"] as DataContainerParams, - [sourceSubscriptionId, sourceResourceGroup, sourceAccountName, source?.databaseId], - ); - - const targetDbParams = React.useMemo( - () => [targetSubscriptionId, targetResourceGroup, targetAccountName, "SQL"] as DatabaseParams, - [targetSubscriptionId, targetResourceGroup, targetAccountName], - ); - - const targetContainerParams = React.useMemo( - () => - [targetSubscriptionId, targetResourceGroup, targetAccountName, target?.databaseId, "SQL"] as DataContainerParams, - [targetSubscriptionId, targetResourceGroup, targetAccountName, target?.databaseId], - ); + const sourceDbParams = [sourceSubscriptionId, sourceResourceGroup, sourceAccountName, "SQL"] as DatabaseParams; + const sourceContainerParams = [sourceSubscriptionId, sourceResourceGroup, sourceAccountName, source?.databaseId, "SQL"] as DataContainerParams; + const targetDbParams = [targetSubscriptionId, targetResourceGroup, targetAccountName, "SQL"] as DatabaseParams; + const targetContainerParams = [targetSubscriptionId, targetResourceGroup, targetAccountName, target?.databaseId, "SQL"] as DataContainerParams; return { source, target, sourceDbParams, sourceContainerParams, targetDbParams, targetContainerParams }; } diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx index 1b0c74f05..e90f28bf8 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx @@ -85,3 +85,4 @@ function useCreateCopyJobScreensList() { } export { SCREEN_KEYS, useCreateCopyJobScreensList }; + diff --git a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobStatusWithIcon.tsx b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobStatusWithIcon.tsx index b10d69087..ff0e67d56 100644 --- a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobStatusWithIcon.tsx +++ b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobStatusWithIcon.tsx @@ -34,7 +34,7 @@ const iconMap: Partial> = { [CopyJobStatusType.Completed]: "CompletedSolid", }; -const CopyJobStatusWithIcon: React.FC<{ status: CopyJobStatusType }> = ({ status }) => { +const CopyJobStatusWithIcon: React.FC<{ status: CopyJobStatusType }> = React.memo(({ status }) => { const statusText = ContainerCopyMessages.MonitorJobs.Status[status] || "Unknown"; const isSpinnerStatus = [ @@ -57,6 +57,6 @@ const CopyJobStatusWithIcon: React.FC<{ status: CopyJobStatusType }> = ({ status {statusText} ); -}; +}); export default CopyJobStatusWithIcon; diff --git a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobs.NotFound.tsx b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobs.NotFound.tsx index d7f32d3e8..15c67b5ae 100644 --- a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobs.NotFound.tsx +++ b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobs.NotFound.tsx @@ -1,5 +1,5 @@ import { ActionButton, Image } from "@fluentui/react"; -import React, { useCallback } from "react"; +import React, { memo } from "react"; import CopyJobIcon from "../../../../../images/ContainerCopy/copy-jobs.svg"; import * as Actions from "../../Actions/CopyJobActions"; import ContainerCopyMessages from "../../ContainerCopyMessages"; @@ -7,16 +7,15 @@ import ContainerCopyMessages from "../../ContainerCopyMessages"; interface CopyJobsNotFoundProps {} const CopyJobsNotFound: React.FC = () => { - const handleCreateCopyJob = useCallback(Actions.openCreateCopyJobPanel, []); return (
{ContainerCopyMessages.noCopyJobsTitle}

{ContainerCopyMessages.noCopyJobsTitle}

- + {ContainerCopyMessages.createCopyJobButtonText}
); }; -export default CopyJobsNotFound; +export default memo(CopyJobsNotFound); diff --git a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobsList.tsx b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobsList.tsx index 6fdf915d6..e3b29ae17 100644 --- a/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobsList.tsx +++ b/src/Explorer/ContainerCopy/MonitorCopyJobs/Components/CopyJobsList.tsx @@ -58,22 +58,19 @@ const CopyJobsList: React.FC = ({ jobs, handleActionClick, pa setStartIndex(0); }; - const columns: IColumn[] = React.useMemo( - () => getColumns(handleSort, handleActionClick, sortedColumnKey, isSortedDescending), - [handleSort, handleActionClick, sortedColumnKey, isSortedDescending], - ); + const columns: IColumn[] = getColumns(handleSort, handleActionClick, sortedColumnKey, isSortedDescending); - const _handleRowClick = React.useCallback((job: CopyJobType) => { + const _handleRowClick = (job: CopyJobType) => { openCopyJobDetailsPanel(job); - }, []); + }; - const _onRenderRow = React.useCallback((props: any) => { + const _onRenderRow = (props: any) => { return (
); - }, []); + }; return (
diff --git a/src/Explorer/ContainerCopy/MonitorCopyJobs/MonitorCopyJobs.tsx b/src/Explorer/ContainerCopy/MonitorCopyJobs/MonitorCopyJobs.tsx index cb4d0fea8..f9ad92dff 100644 --- a/src/Explorer/ContainerCopy/MonitorCopyJobs/MonitorCopyJobs.tsx +++ b/src/Explorer/ContainerCopy/MonitorCopyJobs/MonitorCopyJobs.tsx @@ -3,13 +3,14 @@ import { MessageBar, MessageBarType, Stack } from "@fluentui/react"; import ShimmerTree, { IndentLevel } from "Common/ShimmerTree/ShimmerTree"; import React, { forwardRef, useEffect, useImperativeHandle } from "react"; import { getCopyJobs, updateCopyJobStatus } from "../Actions/CopyJobActions"; -import { convertToCamelCase } from "../CopyJobUtils"; +import { convertToCamelCase, isEqual } from "../CopyJobUtils"; import { CopyJobStatusType } from "../Enums/CopyJobEnums"; import CopyJobsNotFound from "../MonitorCopyJobs/Components/CopyJobs.NotFound"; import { CopyJobType, JobActionUpdatorType } from "../Types/CopyJobTypes"; import CopyJobsList from "./Components/CopyJobsList"; const FETCH_INTERVAL_MS = 30 * 1000; +const SHIMMER_INDENT_LEVELS: IndentLevel[] = Array(7).fill({ level: 0, width: "100%" }); interface MonitorCopyJobsProps {} @@ -24,8 +25,6 @@ const MonitorCopyJobs = forwardRef((_p const isUpdatingRef = React.useRef(false); const isFirstFetchRef = React.useRef(true); - const indentLevels = React.useMemo(() => Array(7).fill({ level: 0, width: "100%" }), []); - const fetchJobs = React.useCallback(async () => { if (isUpdatingRef.current) { return; @@ -38,8 +37,7 @@ const MonitorCopyJobs = forwardRef((_p const response = await getCopyJobs(); setJobs((prevJobs) => { - const isSame = JSON.stringify(prevJobs) === JSON.stringify(response); - return isSame ? prevJobs : response; + return isEqual(prevJobs, response) ? prevJobs : response; }); } catch (error) { setError(error.message || "Failed to load copy jobs. Please try again later."); @@ -96,7 +94,7 @@ const MonitorCopyJobs = forwardRef((_p [], ); - const memoizedJobsList = React.useMemo(() => { + const renderJobsList = () => { if (loading) { return null; } @@ -104,17 +102,17 @@ const MonitorCopyJobs = forwardRef((_p return ; } return ; - }, [jobs, loading, handleActionClick]); + }; return ( - {loading && } + {loading && } {error && ( setError(null)}> {error} )} - {memoizedJobsList} + {renderJobsList()} ); });