Integrate container creation screen to copy job flow (#2265)

This commit is contained in:
BChoudhury-ms
2025-11-27 13:19:50 +05:30
committed by GitHub
parent bb0bbd8a6e
commit 63cddeb4b8
20 changed files with 193 additions and 50 deletions

View File

@@ -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",
); );
}; };

View File

@@ -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 (

View File

@@ -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);
} }

View File

@@ -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",

View File

@@ -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>
); );
}; };

View File

@@ -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>;

View File

@@ -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;

View File

@@ -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

View File

@@ -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>
); );

View File

@@ -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>
); );

View File

@@ -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,6 +30,7 @@ export const DatabaseContainerSection = ({
/> />
</FieldRow> </FieldRow>
<FieldRow label={ContainerCopyMessages.containerDropdownLabel}> <FieldRow label={ContainerCopyMessages.containerDropdownLabel}>
<Stack>
<Dropdown <Dropdown
placeholder={ContainerCopyMessages.containerDropdownPlaceholder} placeholder={ContainerCopyMessages.containerDropdownPlaceholder}
ariaLabel={ContainerCopyMessages.containerDropdownLabel} ariaLabel={ContainerCopyMessages.containerDropdownLabel}
@@ -38,6 +40,12 @@ export const DatabaseContainerSection = ({
selectedKey={selectedContainer} selectedKey={selectedContainer}
onChange={containerOnChange} onChange={containerOnChange}
/> />
{handleOnDemandCreateContainer && (
<ActionButton className="create-container-link-btn" onClick={() => handleOnDemandCreateContainer()}>
{ContainerCopyMessages.createContainerButtonLabel}
</ActionButton>
)}
</Stack>
</FieldRow> </FieldRow>
</Stack> </Stack>
); );

View File

@@ -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,
}; };
} }

View File

@@ -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],
); );
} }

View File

@@ -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>

View File

@@ -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>
); );
}); });

View File

@@ -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 = {

View File

@@ -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 {

View File

@@ -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>
{!this.props.isCopyJobFlow && (
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={this.state.isThroughputCapExceeded} /> <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 });
if (this.props.isCopyJobFlow && this.props.onSubmitSuccess) {
this.props.onSubmitSuccess({ databaseId, collectionId });
} else {
TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey); TelemetryProcessor.traceSuccess(Action.CreateCollection, telemetryData, startKey);
useSidePanel.getState().closeSidePanel(); 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 });

View File

@@ -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} />
)} )}

View File

@@ -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 })),