mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 00:41:31 +00:00
Integrate container creation screen to copy job flow (#2265)
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { logError } from "../../../Common/Logger";
|
import { logError } from "../../../Common/Logger";
|
||||||
@@ -29,12 +30,12 @@ import CopyJobDetails from "../MonitorCopyJobs/Components/CopyJobDetails";
|
|||||||
import { MonitorCopyJobsRefState } from "../MonitorCopyJobs/MonitorCopyJobRefState";
|
import { MonitorCopyJobsRefState } from "../MonitorCopyJobs/MonitorCopyJobRefState";
|
||||||
import { CopyJobContextState, CopyJobError, CopyJobErrorType, CopyJobType } from "../Types/CopyJobTypes";
|
import { CopyJobContextState, CopyJobError, CopyJobErrorType, CopyJobType } from "../Types/CopyJobTypes";
|
||||||
|
|
||||||
export const openCreateCopyJobPanel = () => {
|
export const openCreateCopyJobPanel = (explorer: Explorer) => {
|
||||||
const sidePanelState = useSidePanel.getState();
|
const sidePanelState = useSidePanel.getState();
|
||||||
sidePanelState.setPanelHasConsole(false);
|
sidePanelState.setPanelHasConsole(false);
|
||||||
sidePanelState.openSidePanel(
|
sidePanelState.openSidePanel(
|
||||||
ContainerCopyMessages.createCopyJobPanelTitle,
|
ContainerCopyMessages.createCopyJobPanelTitle,
|
||||||
<CreateCopyJobScreensProvider />,
|
<CreateCopyJobScreensProvider explorer={explorer} />,
|
||||||
"650px",
|
"650px",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ const rootStyle = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const CopyJobCommandBar: React.FC<ContainerCopyProps> = ({ container }) => {
|
const CopyJobCommandBar: React.FC<ContainerCopyProps> = ({ explorer }) => {
|
||||||
const commandBarItems: CommandButtonComponentProps[] = getCommandBarButtons(container);
|
const commandBarItems: CommandButtonComponentProps[] = getCommandBarButtons(explorer);
|
||||||
const controlButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(commandBarItems, backgroundColor);
|
const controlButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(commandBarItems, backgroundColor);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import ContainerCopyMessages from "../ContainerCopyMessages";
|
|||||||
import { MonitorCopyJobsRefState } from "../MonitorCopyJobs/MonitorCopyJobRefState";
|
import { MonitorCopyJobsRefState } from "../MonitorCopyJobs/MonitorCopyJobRefState";
|
||||||
import { CopyJobCommandBarBtnType } from "../Types/CopyJobTypes";
|
import { CopyJobCommandBarBtnType } from "../Types/CopyJobTypes";
|
||||||
|
|
||||||
function getCopyJobBtns(container: Explorer): CopyJobCommandBarBtnType[] {
|
function getCopyJobBtns(explorer: Explorer): CopyJobCommandBarBtnType[] {
|
||||||
const monitorCopyJobsRef = MonitorCopyJobsRefState((state) => state.ref);
|
const monitorCopyJobsRef = MonitorCopyJobsRefState((state) => state.ref);
|
||||||
const buttons: CopyJobCommandBarBtnType[] = [
|
const buttons: CopyJobCommandBarBtnType[] = [
|
||||||
{
|
{
|
||||||
@@ -17,7 +17,7 @@ function getCopyJobBtns(container: Explorer): CopyJobCommandBarBtnType[] {
|
|||||||
iconSrc: AddIcon,
|
iconSrc: AddIcon,
|
||||||
label: ContainerCopyMessages.createCopyJobButtonLabel,
|
label: ContainerCopyMessages.createCopyJobButtonLabel,
|
||||||
ariaLabel: ContainerCopyMessages.createCopyJobButtonAriaLabel,
|
ariaLabel: ContainerCopyMessages.createCopyJobButtonAriaLabel,
|
||||||
onClick: Actions.openCreateCopyJobPanel,
|
onClick: Actions.openCreateCopyJobPanel.bind(null, explorer),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "refresh",
|
key: "refresh",
|
||||||
@@ -34,7 +34,7 @@ function getCopyJobBtns(container: Explorer): CopyJobCommandBarBtnType[] {
|
|||||||
label: ContainerCopyMessages.feedbackButtonLabel,
|
label: ContainerCopyMessages.feedbackButtonLabel,
|
||||||
ariaLabel: ContainerCopyMessages.feedbackButtonAriaLabel,
|
ariaLabel: ContainerCopyMessages.feedbackButtonAriaLabel,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
container.openContainerCopyFeedbackBlade();
|
explorer.openContainerCopyFeedbackBlade();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,6 @@ function btnMapper(config: CopyJobCommandBarBtnType): CommandButtonComponentProp
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function getCommandBarButtons(explorer: Explorer): CommandButtonComponentProps[] {
|
||||||
return getCopyJobBtns(container).map(btnMapper);
|
return getCopyJobBtns(explorer).map(btnMapper);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ export default {
|
|||||||
databaseDropdownPlaceholder: "Select a database",
|
databaseDropdownPlaceholder: "Select a database",
|
||||||
containerDropdownLabel: "Container",
|
containerDropdownLabel: "Container",
|
||||||
containerDropdownPlaceholder: "Select a container",
|
containerDropdownPlaceholder: "Select a container",
|
||||||
|
createNewContainerSubHeading: "Select the properties for your container.",
|
||||||
|
createContainerButtonLabel: "Create a new container",
|
||||||
|
createContainerHeading: "Create new container",
|
||||||
|
|
||||||
// Preview and Create Screen
|
// Preview and Create Screen
|
||||||
jobNameLabel: "Job name",
|
jobNameLabel: "Job name",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { MonitorCopyJobsRefState } from "./MonitorCopyJobs/MonitorCopyJobRefStat
|
|||||||
import MonitorCopyJobs, { MonitorCopyJobsRef } from "./MonitorCopyJobs/MonitorCopyJobs";
|
import MonitorCopyJobs, { MonitorCopyJobsRef } from "./MonitorCopyJobs/MonitorCopyJobs";
|
||||||
import { ContainerCopyProps } from "./Types/CopyJobTypes";
|
import { ContainerCopyProps } from "./Types/CopyJobTypes";
|
||||||
|
|
||||||
const ContainerCopyPanel: React.FC<ContainerCopyProps> = ({ container }) => {
|
const ContainerCopyPanel: React.FC<ContainerCopyProps> = ({ explorer }) => {
|
||||||
const monitorCopyJobsRef = React.useRef<MonitorCopyJobsRef>();
|
const monitorCopyJobsRef = React.useRef<MonitorCopyJobsRef>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (monitorCopyJobsRef.current) {
|
if (monitorCopyJobsRef.current) {
|
||||||
@@ -14,8 +14,8 @@ const ContainerCopyPanel: React.FC<ContainerCopyProps> = ({ container }) => {
|
|||||||
}, [monitorCopyJobsRef.current]);
|
}, [monitorCopyJobsRef.current]);
|
||||||
return (
|
return (
|
||||||
<div id="containerCopyWrapper" className="flexContainer hideOverflows">
|
<div id="containerCopyWrapper" className="flexContainer hideOverflows">
|
||||||
<CopyJobCommandBar container={container} />
|
<CopyJobCommandBar explorer={explorer} />
|
||||||
<MonitorCopyJobs ref={monitorCopyJobsRef} />
|
<MonitorCopyJobs ref={monitorCopyJobsRef} explorer={explorer} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import { Subscription } from "Contracts/DataModels";
|
import { Subscription } from "Contracts/DataModels";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
@@ -15,6 +16,7 @@ export const useCopyJobContext = (): CopyJobContextProviderType => {
|
|||||||
|
|
||||||
interface CopyJobContextProviderProps {
|
interface CopyJobContextProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInitialCopyJobState = (): CopyJobContextState => {
|
const getInitialCopyJobState = (): CopyJobContextState => {
|
||||||
@@ -56,6 +58,7 @@ const CopyJobContextProvider: React.FC<CopyJobContextProviderProps> = (props) =>
|
|||||||
flow,
|
flow,
|
||||||
setFlow,
|
setFlow,
|
||||||
resetCopyJobState,
|
resetCopyJobState,
|
||||||
|
explorer: props.explorer,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <CopyJobContext.Provider value={contextValue}>{props.children}</CopyJobContext.Provider>;
|
return <CopyJobContext.Provider value={contextValue}>{props.children}</CopyJobContext.Provider>;
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Stack, Text } from "@fluentui/react";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
|
import { produce } from "immer";
|
||||||
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
import { AddCollectionPanel } from "../../../../Panes/AddCollectionPanel/AddCollectionPanel";
|
||||||
|
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||||
|
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||||
|
|
||||||
|
type AddCollectionPanelWrapperProps = {
|
||||||
|
explorer?: Explorer;
|
||||||
|
goBack?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddCollectionPanelWrapper: React.FunctionComponent<AddCollectionPanelWrapperProps> = ({ explorer, goBack }) => {
|
||||||
|
const { setCopyJobState } = useCopyJobContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sidePanelStore = useSidePanel.getState();
|
||||||
|
if (sidePanelStore.headerText !== ContainerCopyMessages.createContainerHeading) {
|
||||||
|
sidePanelStore.setHeaderText(ContainerCopyMessages.createContainerHeading);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
sidePanelStore.setHeaderText(ContainerCopyMessages.createCopyJobPanelTitle);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddCollectionSuccess = useCallback(
|
||||||
|
(collectionData: { databaseId: string; collectionId: string }) => {
|
||||||
|
setCopyJobState(
|
||||||
|
produce((state) => {
|
||||||
|
state.target.databaseId = collectionData.databaseId;
|
||||||
|
state.target.containerId = collectionData.collectionId;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
goBack?.();
|
||||||
|
},
|
||||||
|
[goBack],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack className="addCollectionPanelWrapper">
|
||||||
|
<Stack.Item className="addCollectionPanelHeader">
|
||||||
|
<Text>{ContainerCopyMessages.createNewContainerSubHeading}</Text>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item className="addCollectionPanelBody">
|
||||||
|
<AddCollectionPanel explorer={explorer} isCopyJobFlow={true} onSubmitSuccess={handleAddCollectionSuccess} />
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddCollectionPanelWrapper;
|
||||||
@@ -13,6 +13,7 @@ const CreateCopyJobScreens: React.FC = () => {
|
|||||||
handlePrevious,
|
handlePrevious,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
primaryBtnText,
|
primaryBtnText,
|
||||||
|
showAddCollectionPanel,
|
||||||
} = useCopyJobNavigation();
|
} = useCopyJobNavigation();
|
||||||
const { contextError, setContextError } = useCopyJobContext();
|
const { contextError, setContextError } = useCopyJobContext();
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ const CreateCopyJobScreens: React.FC = () => {
|
|||||||
{contextError}
|
{contextError}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
{currentScreen?.component}
|
{React.cloneElement(currentScreen?.component as React.ReactElement, { showAddCollectionPanel })}
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item className="createCopyJobScreensFooter">
|
<Stack.Item className="createCopyJobScreensFooter">
|
||||||
<NavigationControls
|
<NavigationControls
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import CopyJobContextProvider from "../../Context/CopyJobContext";
|
import CopyJobContextProvider from "../../Context/CopyJobContext";
|
||||||
import CreateCopyJobScreens from "./CreateCopyJobScreens";
|
import CreateCopyJobScreens from "./CreateCopyJobScreens";
|
||||||
|
|
||||||
const CreateCopyJobScreensProvider = () => {
|
const CreateCopyJobScreensProvider = ({ explorer }: { explorer: Explorer }) => {
|
||||||
return (
|
return (
|
||||||
<CopyJobContextProvider>
|
<CopyJobContextProvider explorer={explorer}>
|
||||||
<CreateCopyJobScreens />
|
<CreateCopyJobScreens />
|
||||||
</CopyJobContextProvider>
|
</CopyJobContextProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import { DatabaseContainerSection } from "./components/DatabaseContainerSection"
|
|||||||
import { dropDownChangeHandler } from "./Events/DropDownChangeHandler";
|
import { dropDownChangeHandler } from "./Events/DropDownChangeHandler";
|
||||||
import { useMemoizedSourceAndTargetData } from "./memoizedData";
|
import { useMemoizedSourceAndTargetData } from "./memoizedData";
|
||||||
|
|
||||||
const SelectSourceAndTargetContainers = () => {
|
type SelectSourceAndTargetContainers = {
|
||||||
|
showAddCollectionPanel?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SelectSourceAndTargetContainers = ({ showAddCollectionPanel }: SelectSourceAndTargetContainers) => {
|
||||||
const { copyJobState, setCopyJobState } = useCopyJobContext();
|
const { copyJobState, setCopyJobState } = useCopyJobContext();
|
||||||
const { source, target, sourceDbParams, sourceContainerParams, targetDbParams, targetContainerParams } =
|
const { source, target, sourceDbParams, sourceContainerParams, targetDbParams, targetContainerParams } =
|
||||||
useMemoizedSourceAndTargetData(copyJobState);
|
useMemoizedSourceAndTargetData(copyJobState);
|
||||||
@@ -62,6 +66,7 @@ const SelectSourceAndTargetContainers = () => {
|
|||||||
selectedContainer={target?.containerId}
|
selectedContainer={target?.containerId}
|
||||||
containerDisabled={!target?.databaseId}
|
containerDisabled={!target?.databaseId}
|
||||||
containerOnChange={onDropdownChange("targetContainer")}
|
containerOnChange={onDropdownChange("targetContainer")}
|
||||||
|
handleOnDemandCreateContainer={showAddCollectionPanel}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Dropdown, Stack } from "@fluentui/react";
|
import { ActionButton, Dropdown, Stack } from "@fluentui/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
|
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
|
||||||
import { DatabaseContainerSectionProps } from "../../../../Types/CopyJobTypes";
|
import { DatabaseContainerSectionProps } from "../../../../Types/CopyJobTypes";
|
||||||
@@ -14,6 +14,7 @@ export const DatabaseContainerSection = ({
|
|||||||
selectedContainer,
|
selectedContainer,
|
||||||
containerDisabled,
|
containerDisabled,
|
||||||
containerOnChange,
|
containerOnChange,
|
||||||
|
handleOnDemandCreateContainer,
|
||||||
}: DatabaseContainerSectionProps) => (
|
}: DatabaseContainerSectionProps) => (
|
||||||
<Stack tokens={{ childrenGap: 15 }} className="databaseContainerSection">
|
<Stack tokens={{ childrenGap: 15 }} className="databaseContainerSection">
|
||||||
<label className="subHeading">{heading}</label>
|
<label className="subHeading">{heading}</label>
|
||||||
@@ -29,15 +30,22 @@ export const DatabaseContainerSection = ({
|
|||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
<FieldRow label={ContainerCopyMessages.containerDropdownLabel}>
|
<FieldRow label={ContainerCopyMessages.containerDropdownLabel}>
|
||||||
<Dropdown
|
<Stack>
|
||||||
placeholder={ContainerCopyMessages.containerDropdownPlaceholder}
|
<Dropdown
|
||||||
ariaLabel={ContainerCopyMessages.containerDropdownLabel}
|
placeholder={ContainerCopyMessages.containerDropdownPlaceholder}
|
||||||
options={containerOptions}
|
ariaLabel={ContainerCopyMessages.containerDropdownLabel}
|
||||||
required
|
options={containerOptions}
|
||||||
disabled={!!containerDisabled}
|
required
|
||||||
selectedKey={selectedContainer}
|
disabled={!!containerDisabled}
|
||||||
onChange={containerOnChange}
|
selectedKey={selectedContainer}
|
||||||
/>
|
onChange={containerOnChange}
|
||||||
|
/>
|
||||||
|
{handleOnDemandCreateContainer && (
|
||||||
|
<ActionButton className="create-container-link-btn" onClick={() => handleOnDemandCreateContainer()}>
|
||||||
|
{ContainerCopyMessages.createContainerButtonLabel}
|
||||||
|
</ActionButton>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -35,10 +35,14 @@ function navigationReducer(state: NavigationState, action: Action): NavigationSt
|
|||||||
export function useCopyJobNavigation() {
|
export function useCopyJobNavigation() {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { copyJobState, resetCopyJobState, setContextError } = useCopyJobContext();
|
const { copyJobState, resetCopyJobState, setContextError } = useCopyJobContext();
|
||||||
const screens = useCreateCopyJobScreensList();
|
|
||||||
const { validationCache: cache } = useCopyJobPrerequisitesCache();
|
const { validationCache: cache } = useCopyJobPrerequisitesCache();
|
||||||
const [state, dispatch] = useReducer(navigationReducer, { screenHistory: [SCREEN_KEYS.SelectAccount] });
|
const [state, dispatch] = useReducer(navigationReducer, { screenHistory: [SCREEN_KEYS.SelectAccount] });
|
||||||
|
|
||||||
|
const handlePrevious = useCallback(() => {
|
||||||
|
dispatch({ type: "PREVIOUS" });
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const screens = useCreateCopyJobScreensList(handlePrevious);
|
||||||
const currentScreenKey = state.screenHistory[state.screenHistory.length - 1];
|
const currentScreenKey = state.screenHistory[state.screenHistory.length - 1];
|
||||||
const currentScreen = screens.find((screen) => screen.key === currentScreenKey);
|
const currentScreen = screens.find((screen) => screen.key === currentScreenKey);
|
||||||
|
|
||||||
@@ -51,7 +55,9 @@ export function useCopyJobNavigation() {
|
|||||||
}, [currentScreen.key, copyJobState, cache, isLoading]);
|
}, [currentScreen.key, copyJobState, cache, isLoading]);
|
||||||
|
|
||||||
const primaryBtnText = useMemo(() => {
|
const primaryBtnText = useMemo(() => {
|
||||||
if (currentScreenKey === SCREEN_KEYS.PreviewCopyJob) {
|
if (currentScreenKey === SCREEN_KEYS.CreateCollection) {
|
||||||
|
return "Create";
|
||||||
|
} else if (currentScreenKey === SCREEN_KEYS.PreviewCopyJob) {
|
||||||
return "Copy";
|
return "Copy";
|
||||||
}
|
}
|
||||||
return "Next";
|
return "Next";
|
||||||
@@ -107,7 +113,26 @@ export function useCopyJobNavigation() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddCollectionPanelSubmit = () => {
|
||||||
|
const form = document.getElementById("panelContainer") as HTMLFormElement;
|
||||||
|
if (form) {
|
||||||
|
const submitEvent = new Event("submit", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
form.dispatchEvent(submitEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAddCollectionPanel = useCallback(() => {
|
||||||
|
dispatch({ type: "NEXT", nextScreen: SCREEN_KEYS.CreateCollection });
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
const handlePrimary = useCallback(() => {
|
const handlePrimary = useCallback(() => {
|
||||||
|
if (currentScreenKey === SCREEN_KEYS.CreateCollection) {
|
||||||
|
handleAddCollectionPanelSubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (currentScreenKey === SCREEN_KEYS.SelectSourceAndTargetContainers && areContainersIdentical()) {
|
if (currentScreenKey === SCREEN_KEYS.SelectSourceAndTargetContainers && areContainersIdentical()) {
|
||||||
setContextError(
|
setContextError(
|
||||||
"Source and destination containers cannot be the same. Please select different containers to proceed.",
|
"Source and destination containers cannot be the same. Please select different containers to proceed.",
|
||||||
@@ -132,10 +157,6 @@ export function useCopyJobNavigation() {
|
|||||||
}
|
}
|
||||||
}, [currentScreenKey, copyJobState, areContainersIdentical, handleCopyJobSubmission]);
|
}, [currentScreenKey, copyJobState, areContainersIdentical, handleCopyJobSubmission]);
|
||||||
|
|
||||||
const handlePrevious = useCallback(() => {
|
|
||||||
dispatch({ type: "PREVIOUS" });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentScreen,
|
currentScreen,
|
||||||
isPrimaryDisabled,
|
isPrimaryDisabled,
|
||||||
@@ -143,6 +164,7 @@ export function useCopyJobNavigation() {
|
|||||||
handlePrimary,
|
handlePrimary,
|
||||||
handlePrevious,
|
handlePrevious,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
|
showAddCollectionPanel,
|
||||||
primaryBtnText,
|
primaryBtnText,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useCopyJobContext } from "../../Context/CopyJobContext";
|
||||||
import { CopyJobContextState } from "../../Types/CopyJobTypes";
|
import { CopyJobContextState } from "../../Types/CopyJobTypes";
|
||||||
import AssignPermissions from "../Screens/AssignPermissions/AssignPermissions";
|
import AssignPermissions from "../Screens/AssignPermissions/AssignPermissions";
|
||||||
|
import AddCollectionPanelWrapper from "../Screens/CreateContainer/AddCollectionPanelWrapper";
|
||||||
import PreviewCopyJob from "../Screens/PreviewCopyJob/PreviewCopyJob";
|
import PreviewCopyJob from "../Screens/PreviewCopyJob/PreviewCopyJob";
|
||||||
import SelectAccount from "../Screens/SelectAccount/SelectAccount";
|
import SelectAccount from "../Screens/SelectAccount/SelectAccount";
|
||||||
import SelectSourceAndTargetContainers from "../Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers";
|
import SelectSourceAndTargetContainers from "../Screens/SelectSourceAndTargetContainers/SelectSourceAndTargetContainers";
|
||||||
|
|
||||||
const SCREEN_KEYS = {
|
const SCREEN_KEYS = {
|
||||||
|
CreateCollection: "CreateCollection",
|
||||||
SelectAccount: "SelectAccount",
|
SelectAccount: "SelectAccount",
|
||||||
SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers",
|
SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers",
|
||||||
PreviewCopyJob: "PreviewCopyJob",
|
PreviewCopyJob: "PreviewCopyJob",
|
||||||
@@ -23,7 +26,9 @@ type Screen = {
|
|||||||
validations: Validation[];
|
validations: Validation[];
|
||||||
};
|
};
|
||||||
|
|
||||||
function useCreateCopyJobScreensList() {
|
function useCreateCopyJobScreensList(goBack: () => void): Screen[] {
|
||||||
|
const { explorer } = useCopyJobContext();
|
||||||
|
|
||||||
return React.useMemo<Screen[]>(
|
return React.useMemo<Screen[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -50,6 +55,11 @@ function useCreateCopyJobScreensList() {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SCREEN_KEYS.CreateCollection,
|
||||||
|
component: <AddCollectionPanelWrapper explorer={explorer} goBack={goBack} />,
|
||||||
|
validations: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: SCREEN_KEYS.PreviewCopyJob,
|
key: SCREEN_KEYS.PreviewCopyJob,
|
||||||
component: <PreviewCopyJob />,
|
component: <PreviewCopyJob />,
|
||||||
@@ -80,7 +90,7 @@ function useCreateCopyJobScreensList() {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[explorer],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
import { ActionButton, Image } from "@fluentui/react";
|
import { ActionButton, Image } from "@fluentui/react";
|
||||||
import React, { useCallback } from "react";
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import React from "react";
|
||||||
import CopyJobIcon from "../../../../../images/ContainerCopy/copy-jobs.svg";
|
import CopyJobIcon from "../../../../../images/ContainerCopy/copy-jobs.svg";
|
||||||
import * as Actions from "../../Actions/CopyJobActions";
|
import * as Actions from "../../Actions/CopyJobActions";
|
||||||
import ContainerCopyMessages from "../../ContainerCopyMessages";
|
import ContainerCopyMessages from "../../ContainerCopyMessages";
|
||||||
|
|
||||||
interface CopyJobsNotFoundProps {}
|
interface CopyJobsNotFoundProps {
|
||||||
|
explorer: Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
const CopyJobsNotFound: React.FC<CopyJobsNotFoundProps> = () => {
|
const CopyJobsNotFound: React.FC<CopyJobsNotFoundProps> = ({ explorer }) => {
|
||||||
const handleCreateCopyJob = useCallback(Actions.openCreateCopyJobPanel, []);
|
|
||||||
return (
|
return (
|
||||||
<div className="notFoundContainer flexContainer centerContent">
|
<div className="notFoundContainer flexContainer centerContent">
|
||||||
<Image src={CopyJobIcon} alt={ContainerCopyMessages.noCopyJobsTitle} width={100} height={100} />
|
<Image src={CopyJobIcon} alt={ContainerCopyMessages.noCopyJobsTitle} width={100} height={100} />
|
||||||
<h4 className="noCopyJobsMessage">{ContainerCopyMessages.noCopyJobsTitle}</h4>
|
<h4 className="noCopyJobsMessage">{ContainerCopyMessages.noCopyJobsTitle}</h4>
|
||||||
<ActionButton allowDisabledFocus className="createCopyJobButton" onClick={handleCreateCopyJob}>
|
<ActionButton
|
||||||
|
allowDisabledFocus
|
||||||
|
className="createCopyJobButton"
|
||||||
|
onClick={Actions.openCreateCopyJobPanel.bind(null, explorer)}
|
||||||
|
>
|
||||||
{ContainerCopyMessages.createCopyJobButtonText}
|
{ContainerCopyMessages.createCopyJobButtonText}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { MessageBar, MessageBarType, Stack } from "@fluentui/react";
|
import { MessageBar, MessageBarType, Stack } from "@fluentui/react";
|
||||||
import ShimmerTree, { IndentLevel } from "Common/ShimmerTree/ShimmerTree";
|
import ShimmerTree, { IndentLevel } from "Common/ShimmerTree/ShimmerTree";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
import React, { forwardRef, useEffect, useImperativeHandle } from "react";
|
import React, { forwardRef, useEffect, useImperativeHandle } from "react";
|
||||||
import { getCopyJobs, updateCopyJobStatus } from "../Actions/CopyJobActions";
|
import { getCopyJobs, updateCopyJobStatus } from "../Actions/CopyJobActions";
|
||||||
import { convertToCamelCase } from "../CopyJobUtils";
|
import { convertToCamelCase } from "../CopyJobUtils";
|
||||||
@@ -11,13 +12,15 @@ import CopyJobsList from "./Components/CopyJobsList";
|
|||||||
|
|
||||||
const FETCH_INTERVAL_MS = 30 * 1000;
|
const FETCH_INTERVAL_MS = 30 * 1000;
|
||||||
|
|
||||||
interface MonitorCopyJobsProps {}
|
interface MonitorCopyJobsProps {
|
||||||
|
explorer: Explorer;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MonitorCopyJobsRef {
|
export interface MonitorCopyJobsRef {
|
||||||
refreshJobList: () => void;
|
refreshJobList: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MonitorCopyJobs = forwardRef<MonitorCopyJobsRef, MonitorCopyJobsProps>((_props, ref) => {
|
const MonitorCopyJobs = forwardRef<MonitorCopyJobsRef, MonitorCopyJobsProps>(({ explorer }, ref) => {
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
const [jobs, setJobs] = React.useState<CopyJobType[]>([]);
|
const [jobs, setJobs] = React.useState<CopyJobType[]>([]);
|
||||||
@@ -96,15 +99,15 @@ const MonitorCopyJobs = forwardRef<MonitorCopyJobsRef, MonitorCopyJobsProps>((_p
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const memoizedJobsList = React.useMemo(() => {
|
const renderJobsList = () => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (jobs.length > 0) {
|
if (jobs.length > 0) {
|
||||||
return <CopyJobsList jobs={jobs} handleActionClick={handleActionClick} />;
|
return <CopyJobsList jobs={jobs} handleActionClick={handleActionClick} />;
|
||||||
}
|
}
|
||||||
return <CopyJobsNotFound />;
|
return <CopyJobsNotFound explorer={explorer} />;
|
||||||
}, [jobs, loading, handleActionClick]);
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="monitorCopyJobs flexContainer">
|
<Stack className="monitorCopyJobs flexContainer">
|
||||||
@@ -114,7 +117,7 @@ const MonitorCopyJobs = forwardRef<MonitorCopyJobsRef, MonitorCopyJobsProps>((_p
|
|||||||
{error}
|
{error}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
{memoizedJobsList}
|
{renderJobsList()}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Explorer from "../../Explorer";
|
|||||||
import { CopyJobMigrationType, CopyJobStatusType } from "../Enums/CopyJobEnums";
|
import { CopyJobMigrationType, CopyJobStatusType } from "../Enums/CopyJobEnums";
|
||||||
|
|
||||||
export interface ContainerCopyProps {
|
export interface ContainerCopyProps {
|
||||||
container: Explorer;
|
explorer: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CopyJobCommandBarBtnType = {
|
export type CopyJobCommandBarBtnType = {
|
||||||
@@ -48,6 +48,7 @@ export interface DatabaseContainerSectionProps {
|
|||||||
selectedContainer: string;
|
selectedContainer: string;
|
||||||
containerDisabled?: boolean;
|
containerDisabled?: boolean;
|
||||||
containerOnChange: (ev: React.FormEvent<HTMLDivElement>, option: DropdownOptionType) => void;
|
containerOnChange: (ev: React.FormEvent<HTMLDivElement>, option: DropdownOptionType) => void;
|
||||||
|
handleOnDemandCreateContainer?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CopyJobContextState {
|
export interface CopyJobContextState {
|
||||||
@@ -80,6 +81,7 @@ export interface CopyJobContextProviderType {
|
|||||||
copyJobState: CopyJobContextState | null;
|
copyJobState: CopyJobContextState | null;
|
||||||
setCopyJobState: React.Dispatch<React.SetStateAction<CopyJobContextState>>;
|
setCopyJobState: React.Dispatch<React.SetStateAction<CopyJobContextState>>;
|
||||||
resetCopyJobState: () => void;
|
resetCopyJobState: () => void;
|
||||||
|
explorer?: Explorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CopyJobType = {
|
export type CopyJobType = {
|
||||||
|
|||||||
@@ -75,6 +75,20 @@
|
|||||||
.createCopyJobErrorMessageBar {
|
.createCopyJobErrorMessageBar {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
.create-container-link-btn {
|
||||||
|
padding: 0;
|
||||||
|
height: 25px;
|
||||||
|
color: @LinkColor;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create collection panel */
|
||||||
|
.panelFormWrapper .panelMainContent {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitorCopyJobs {
|
.monitorCopyJobs {
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ export interface AddCollectionPanelProps {
|
|||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
databaseId?: string;
|
databaseId?: string;
|
||||||
isQuickstart?: boolean;
|
isQuickstart?: boolean;
|
||||||
|
isCopyJobFlow?: boolean;
|
||||||
|
onSubmitSuccess?: (collectionData: { databaseId: string; collectionId: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
|
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
|
||||||
@@ -975,7 +977,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={this.state.isThroughputCapExceeded} />
|
{!this.props.isCopyJobFlow && (
|
||||||
|
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={this.state.isThroughputCapExceeded} />
|
||||||
|
)}
|
||||||
|
|
||||||
{this.state.isExecuting && (
|
{this.state.isExecuting && (
|
||||||
<div>
|
<div>
|
||||||
@@ -1415,8 +1419,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({ isExecuting: false });
|
this.setState({ isExecuting: false });
|
||||||
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
|
|
||||||
useSidePanel.getState().closeSidePanel();
|
if (this.props.isCopyJobFlow && this.props.onSubmitSuccess) {
|
||||||
|
this.props.onSubmitSuccess({ databaseId, collectionId });
|
||||||
|
} else {
|
||||||
|
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
|
||||||
|
useSidePanel.getState().closeSidePanel();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage: string = getErrorMessage(error);
|
const errorMessage: string = getErrorMessage(error);
|
||||||
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });
|
this.setState({ isExecuting: false, errorMessage, showErrorDetails: true });
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
<KeyboardShortcutRoot>
|
<KeyboardShortcutRoot>
|
||||||
<div className="flexContainer" aria-hidden="false" data-test="DataExplorerRoot">
|
<div className="flexContainer" aria-hidden="false" data-test="DataExplorerRoot">
|
||||||
{userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? (
|
{userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? (
|
||||||
<ContainerCopyPanel container={explorer} />
|
<ContainerCopyPanel explorer={explorer} />
|
||||||
) : (
|
) : (
|
||||||
<DivExplorer explorer={explorer} />
|
<DivExplorer explorer={explorer} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface SidePanelState {
|
|||||||
hasConsole: boolean;
|
hasConsole: boolean;
|
||||||
panelContent?: JSX.Element;
|
panelContent?: JSX.Element;
|
||||||
headerText?: string;
|
headerText?: string;
|
||||||
|
setHeaderText: (headerText: string) => void;
|
||||||
openSidePanel: (headerText: string, panelContent: JSX.Element, panelWidth?: string, onClose?: () => void) => void;
|
openSidePanel: (headerText: string, panelContent: JSX.Element, panelWidth?: string, onClose?: () => void) => void;
|
||||||
closeSidePanel: () => void;
|
closeSidePanel: () => void;
|
||||||
setPanelHasConsole: (hasConsole: boolean) => void;
|
setPanelHasConsole: (hasConsole: boolean) => void;
|
||||||
@@ -15,6 +16,7 @@ export const useSidePanel: UseStore<SidePanelState> = create((set) => ({
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
panelWidth: "440px",
|
panelWidth: "440px",
|
||||||
hasConsole: true,
|
hasConsole: true,
|
||||||
|
setHeaderText: (headerText: string) => set((state) => ({ ...state, headerText })),
|
||||||
setPanelHasConsole: (hasConsole: boolean) => set((state) => ({ ...state, hasConsole })),
|
setPanelHasConsole: (hasConsole: boolean) => set((state) => ({ ...state, hasConsole })),
|
||||||
openSidePanel: (headerText, panelContent, panelWidth = "440px") =>
|
openSidePanel: (headerText, panelContent, panelWidth = "440px") =>
|
||||||
set((state) => ({ ...state, headerText, panelContent, panelWidth, isOpen: true })),
|
set((state) => ({ ...state, headerText, panelContent, panelWidth, isOpen: true })),
|
||||||
|
|||||||
Reference in New Issue
Block a user