mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-29 22:02:01 +00:00
Added Copy Job prerequisites screen
This commit is contained in:
@@ -39,4 +39,48 @@ export default {
|
||||
sourceContainerLabel: "Source container",
|
||||
targetDatabaseLabel: "Destination database",
|
||||
targetContainerLabel: "Destination container",
|
||||
|
||||
// Assign Permissions Screen
|
||||
assignPermissions: {
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
|
||||
},
|
||||
toggleBtn: {
|
||||
onText: "On",
|
||||
offText: "Off"
|
||||
},
|
||||
addManagedIdentity: {
|
||||
title: "System assigned managed identity enabled",
|
||||
description: "Enable a system assigned managed identity for the destination account to allow the copy job to access it.",
|
||||
toggleLabel: "System assigned managed identity",
|
||||
managedIdentityTooltip: "A system assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. You can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don't have to store any credentials in code.",
|
||||
userAssignedIdentityTooltip: "You can select an existing user assigned identity or create a new one.",
|
||||
userAssignedIdentityLabel: "You may also select a user assigned managed identity.",
|
||||
createUserAssignedIdentityLink: "Create User Assigned Managed Identity",
|
||||
enablementTitle: "Enable system assigned managed identity",
|
||||
enablementDescription: (identityName: string) => identityName ? `'${identityName}' will be registered with Microsoft Entra ID. Once it is registered, '${identityName}' can be granted permissions to access resources protected by Microsoft Entra ID. Do you want to enable the system assigned managed identity for '${identityName}'?` : "",
|
||||
},
|
||||
defaultManagedIdentity: {
|
||||
title: "System assigned managed identity enabled as default",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
tooltip: "A system assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. You can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don't have to store any credentials in code.",
|
||||
popoverTitle: "System assigned managed identity set as default",
|
||||
popoverDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.",
|
||||
},
|
||||
readPermissionAssigned: {
|
||||
title: "Read permission assigned to default identity",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
tooltip: "A system assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. You can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don't have to store any credentials in code.",
|
||||
popoverTitle: "Read permission assigned to default identity",
|
||||
popoverDescription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco.",
|
||||
},
|
||||
pointInTimeRestore: {
|
||||
title: "Point In Time Restore enabled",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
buttonText: "Enable Point In Time Restore",
|
||||
},
|
||||
onlineCopyEnabled: {
|
||||
title: "Online copy enabled",
|
||||
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
buttonText: "Enable Online Copy",
|
||||
}
|
||||
}
|
||||
7
src/Explorer/ContainerCopy/CopyJobUtils.ts
Normal file
7
src/Explorer/ContainerCopy/CopyJobUtils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { DatabaseAccount } from "Contracts/DataModels";
|
||||
|
||||
export const buildResourceLink = (resource: DatabaseAccount): string => {
|
||||
const resourceId = resource.id;
|
||||
// TODO: update "ms.portal.azure.com" based on environment (e.g. for PROD or Fairfax)
|
||||
return `https://ms.portal.azure.com/#resource${resourceId}`;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Link, Stack, Text, Toggle } from "@fluentui/react";
|
||||
import React, { useMemo } from "react";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||
import { buildResourceLink } from "../../../CopyJobUtils";
|
||||
import InfoTooltip from "../Components/InfoTooltip";
|
||||
import PopoverMessage from "../Components/PopoverContainer";
|
||||
import useToggle from "./hooks/useToggle";
|
||||
|
||||
const managedIdentityTooltip = ContainerCopyMessages.addManagedIdentity.managedIdentityTooltip;
|
||||
const userAssignedTooltip = ContainerCopyMessages.addManagedIdentity.userAssignedIdentityTooltip;
|
||||
|
||||
const textStyle = { display: "flex", alignItems: "center" };
|
||||
|
||||
const AddManagedIdentity: React.FC = () => {
|
||||
const { copyJobState } = useCopyJobContext();
|
||||
const [systemAssigned, onToggle] = useToggle(false);
|
||||
|
||||
const manageIdentityLink = useMemo(() => {
|
||||
const { target } = copyJobState;
|
||||
const resourceUri = buildResourceLink(target.account);
|
||||
return target?.account?.id ? `${resourceUri}/ManagedIdentitiesBlade` : "#";
|
||||
}, [copyJobState]);
|
||||
|
||||
return (
|
||||
<Stack className="addManagedIdentityContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<Toggle
|
||||
label={
|
||||
<Text className="toggle-label" style={textStyle}>
|
||||
{ContainerCopyMessages.addManagedIdentity.toggleLabel} <InfoTooltip content={managedIdentityTooltip} />
|
||||
</Text>
|
||||
}
|
||||
checked={systemAssigned}
|
||||
onText={ContainerCopyMessages.toggleBtn.onText}
|
||||
offText={ContainerCopyMessages.toggleBtn.offText}
|
||||
onChange={onToggle}
|
||||
/>
|
||||
<Text className="user-assigned-label" style={textStyle}>
|
||||
{ContainerCopyMessages.addManagedIdentity.userAssignedIdentityLabel} <InfoTooltip content={userAssignedTooltip} />
|
||||
</Text>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Link href={manageIdentityLink} target="_blank" rel="noopener noreferrer">
|
||||
{ContainerCopyMessages.addManagedIdentity.createUserAssignedIdentityLink}
|
||||
</Link>
|
||||
</div>
|
||||
<PopoverMessage
|
||||
visible={systemAssigned}
|
||||
title={ContainerCopyMessages.addManagedIdentity.enablementTitle}
|
||||
onCancel={() => onToggle(null, false)}
|
||||
onPrimary={() => console.log('Primary action taken')}
|
||||
>
|
||||
{ContainerCopyMessages.addManagedIdentity.enablementDescription(copyJobState.target?.account?.name)}
|
||||
</PopoverMessage>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddManagedIdentity;
|
||||
@@ -0,0 +1,42 @@
|
||||
import { ITooltipHostStyles, Stack, Toggle } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import InfoTooltip from "../Components/InfoTooltip";
|
||||
import PopoverMessage from "../Components/PopoverContainer";
|
||||
import useToggle from "./hooks/useToggle";
|
||||
|
||||
const TooltipContent = ContainerCopyMessages.readPermissionAssigned.tooltip;
|
||||
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: 'inline-block' } };
|
||||
|
||||
const AddReadPermissionToDefaultIdentity: React.FC = () => {
|
||||
const [readPermissionAssigned, onToggle] = useToggle(false);
|
||||
|
||||
return (
|
||||
<Stack className="defaultManagedIdentityContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<div className="toggle-label">
|
||||
{ContainerCopyMessages.readPermissionAssigned.description} <InfoTooltip content={TooltipContent} />
|
||||
</div>
|
||||
<Toggle
|
||||
checked={readPermissionAssigned}
|
||||
onText={ContainerCopyMessages.toggleBtn.onText}
|
||||
offText={ContainerCopyMessages.toggleBtn.offText}
|
||||
onChange={onToggle}
|
||||
inlineLabel
|
||||
styles={{
|
||||
root: { marginTop: 8, marginBottom: 12 },
|
||||
label: { display: "none" },
|
||||
}}
|
||||
/>
|
||||
<PopoverMessage
|
||||
visible={readPermissionAssigned}
|
||||
title={ContainerCopyMessages.readPermissionAssigned.popoverTitle}
|
||||
onCancel={() => onToggle(null, false)}
|
||||
onPrimary={() => console.log('Primary action taken')}
|
||||
>
|
||||
{ContainerCopyMessages.readPermissionAssigned.popoverDescription}
|
||||
</PopoverMessage>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddReadPermissionToDefaultIdentity;
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Stack, Toggle } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import InfoTooltip from "../Components/InfoTooltip";
|
||||
import PopoverMessage from "../Components/PopoverContainer";
|
||||
import useToggle from "./hooks/useToggle";
|
||||
|
||||
const managedIdentityTooltip = ContainerCopyMessages.defaultManagedIdentity.tooltip;
|
||||
|
||||
const DefaultManagedIdentity: React.FC = () => {
|
||||
const [defaultSystemAssigned, onToggle] = useToggle(false);
|
||||
|
||||
return (
|
||||
<Stack className="defaultManagedIdentityContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<div className="toggle-label">
|
||||
{ContainerCopyMessages.defaultManagedIdentity.description} <InfoTooltip content={managedIdentityTooltip} />
|
||||
</div>
|
||||
<Toggle
|
||||
checked={defaultSystemAssigned}
|
||||
onText={ContainerCopyMessages.toggleBtn.onText}
|
||||
offText={ContainerCopyMessages.toggleBtn.offText}
|
||||
onChange={onToggle}
|
||||
inlineLabel
|
||||
styles={{
|
||||
root: { marginTop: 8, marginBottom: 12 },
|
||||
label: { display: "none" },
|
||||
}}
|
||||
/>
|
||||
<PopoverMessage
|
||||
visible={defaultSystemAssigned}
|
||||
title={ContainerCopyMessages.defaultManagedIdentity.popoverTitle}
|
||||
onCancel={() => onToggle(null, false)}
|
||||
onPrimary={() => console.log('Primary action taken')}
|
||||
>
|
||||
{ContainerCopyMessages.defaultManagedIdentity.popoverDescription}
|
||||
</PopoverMessage>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default DefaultManagedIdentity;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { PrimaryButton, Stack } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||
import { buildResourceLink } from "../../../CopyJobUtils";
|
||||
import useWindowOpenMonitor from "./hooks/useWindowOpenMonitor";
|
||||
|
||||
const OnlineCopyEnabled: React.FC = () => {
|
||||
const { copyJobState: { source } = {} } = useCopyJobContext();
|
||||
const sourceAccountLink = buildResourceLink(source?.account);
|
||||
const onlineCopyUrl = `${sourceAccountLink}/Features`;
|
||||
const onWindowClosed = () => {
|
||||
console.log('Online copy window closed');
|
||||
};
|
||||
const openWindowAndMonitor = useWindowOpenMonitor(onlineCopyUrl, onWindowClosed);
|
||||
|
||||
return (
|
||||
<Stack className="onlineCopyContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<div className="toggle-label">
|
||||
{ContainerCopyMessages.onlineCopyEnabled.description}
|
||||
</div>
|
||||
<PrimaryButton
|
||||
text={ContainerCopyMessages.onlineCopyEnabled.buttonText}
|
||||
onClick={openWindowAndMonitor}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnlineCopyEnabled;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { PrimaryButton, Stack } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||
import { buildResourceLink } from "../../../CopyJobUtils";
|
||||
import useWindowOpenMonitor from "./hooks/useWindowOpenMonitor";
|
||||
|
||||
const PointInTimeRestore: React.FC = () => {
|
||||
const { copyJobState: { source } = {} } = useCopyJobContext();
|
||||
const sourceAccountLink = buildResourceLink(source?.account);
|
||||
const pitrUrl = `${sourceAccountLink}/backupRestore`;
|
||||
|
||||
const onWindowClosed = () => {
|
||||
console.log('Point-in-time restore window closed');
|
||||
};
|
||||
const openWindowAndMonitor = useWindowOpenMonitor(pitrUrl, onWindowClosed);
|
||||
|
||||
return (
|
||||
<Stack className="pointInTimeRestoreContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<div className="toggle-label">
|
||||
{ContainerCopyMessages.pointInTimeRestore.description}
|
||||
</div>
|
||||
<PrimaryButton
|
||||
text={ContainerCopyMessages.pointInTimeRestore.buttonText}
|
||||
onClick={openWindowAndMonitor}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default PointInTimeRestore;
|
||||
@@ -0,0 +1,57 @@
|
||||
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
|
||||
import { CopyJobMigrationType } from "../../../../Enums";
|
||||
import { CopyJobContextState } from "../../../../Types";
|
||||
import AddManagedIdentity from "../AddManagedIdentity";
|
||||
import AddReadPermissionToDefaultIdentity from "../AddReadPermissionToDefaultIdentity";
|
||||
import DefaultManagedIdentity from "../DefaultManagedIdentity";
|
||||
import OnlineCopyEnabled from "../OnlineCopyEnabled";
|
||||
import PointInTimeRestore from "../PointInTimeRestore";
|
||||
|
||||
// Define a typed config for permission sections
|
||||
export interface PermissionSectionConfig {
|
||||
id: string;
|
||||
title: string;
|
||||
Component: React.FC;
|
||||
shouldShow?: (state: CopyJobContextState) => boolean; // optional conditional rendering
|
||||
}
|
||||
|
||||
// Base permission sections with dynamic visibility logic
|
||||
const PERMISSION_SECTIONS_CONFIG: PermissionSectionConfig[] = [
|
||||
{
|
||||
id: "addManagedIdentity",
|
||||
title: ContainerCopyMessages.addManagedIdentity.title,
|
||||
Component: AddManagedIdentity,
|
||||
},
|
||||
{
|
||||
id: "defaultManagedIdentity",
|
||||
title: ContainerCopyMessages.defaultManagedIdentity.title,
|
||||
Component: DefaultManagedIdentity,
|
||||
},
|
||||
{
|
||||
id: "readPermissionAssigned",
|
||||
title: ContainerCopyMessages.readPermissionAssigned.title,
|
||||
Component: AddReadPermissionToDefaultIdentity,
|
||||
}
|
||||
];
|
||||
|
||||
const PERMISSION_SECTIONS_FOR_ONLINE_JOBS: PermissionSectionConfig[] = [
|
||||
{
|
||||
id: "pointInTimeRestore",
|
||||
title: ContainerCopyMessages.pointInTimeRestore.title,
|
||||
Component: PointInTimeRestore,
|
||||
},
|
||||
{
|
||||
id: "onlineCopyEnabled",
|
||||
title: ContainerCopyMessages.onlineCopyEnabled.title,
|
||||
Component: OnlineCopyEnabled,
|
||||
}
|
||||
];
|
||||
|
||||
const usePermissionSections = (state: CopyJobContextState): PermissionSectionConfig[] => {
|
||||
return [
|
||||
...PERMISSION_SECTIONS_CONFIG,
|
||||
...(state.migrationType !== CopyJobMigrationType.Offline ? PERMISSION_SECTIONS_FOR_ONLINE_JOBS : []),
|
||||
];
|
||||
};
|
||||
|
||||
export default usePermissionSections;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
const useToggle = (initialState = false) => {
|
||||
const [state, setState] = useState<boolean>(initialState);
|
||||
const onToggle = useCallback((_, checked?: boolean) => {
|
||||
setState(!!checked);
|
||||
}, []);
|
||||
return [state, onToggle] as const;
|
||||
};
|
||||
|
||||
export default useToggle;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
const useWindowOpenMonitor = (url: string, onClose?: () => void, intervalMs = 500) => {
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const openWindowAndMonitor = () => {
|
||||
const newWindow = window.open(url, '_blank');
|
||||
intervalRef.current = setInterval(() => {
|
||||
if (newWindow?.closed) {
|
||||
clearInterval(intervalRef.current!);
|
||||
intervalRef.current = null;
|
||||
console.log('New window has been closed!');
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
}, intervalMs);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return openWindowAndMonitor;
|
||||
};
|
||||
|
||||
export default useWindowOpenMonitor;
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Image, Stack, Text } from "@fluentui/react";
|
||||
import { Accordion, AccordionHeader, AccordionItem, AccordionPanel } from "@fluentui/react-components";
|
||||
import React from "react";
|
||||
import WarningIcon from "../../../../../../images/warning.svg";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||
import usePermissionSections, { PermissionSectionConfig } from "./hooks/usePermissionsSection";
|
||||
|
||||
const PermissionSection: React.FC<PermissionSectionConfig> = ({ id, title, Component }) => (
|
||||
<AccordionItem key={id} value={id}>
|
||||
<AccordionHeader className="accordionHeader">
|
||||
<Text className="accordionHeaderText" variant="medium">{title}</Text>
|
||||
<Image className="statusIcon" src={WarningIcon} alt="Warning icon" width={24} height={24} />
|
||||
</AccordionHeader>
|
||||
<AccordionPanel>
|
||||
<Component />
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
|
||||
const AssignPermissions = () => {
|
||||
const { copyJobState } = useCopyJobContext();
|
||||
const permissionSections = usePermissionSections(copyJobState);
|
||||
return (
|
||||
<Stack className="assignPermissionsContainer" tokens={{ childrenGap: 15 }}>
|
||||
<span>
|
||||
{ContainerCopyMessages.assignPermissions.description}
|
||||
</span>
|
||||
<Accordion className="permissionsAccordion" collapsible>
|
||||
{
|
||||
permissionSections.map(section => (
|
||||
<PermissionSection key={section.id} {...section} />
|
||||
))
|
||||
}
|
||||
</Accordion>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssignPermissions;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Image, ITooltipHostStyles, TooltipHost } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import InfoIcon from "../../../../../../images/Info.svg";
|
||||
|
||||
const InfoTooltip: React.FC<{ content?: string }> = ({ content }) => {
|
||||
if (!content) return null;
|
||||
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: 'inline-block' } };
|
||||
return (
|
||||
<TooltipHost content={content} calloutProps={{ gapSpace: 0 }} styles={hostStyles}>
|
||||
<Image src={InfoIcon} alt="Information" width={14} height={14} />
|
||||
</TooltipHost>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(InfoTooltip);
|
||||
@@ -0,0 +1,41 @@
|
||||
import { DefaultButton, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import React from "react";
|
||||
|
||||
interface PopoverContainerProps {
|
||||
title?: string;
|
||||
children?: React.ReactNode;
|
||||
onPrimary: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const PopoverContainer: React.FC<PopoverContainerProps> = React.memo(({ title, children, onPrimary, onCancel }) => {
|
||||
return (
|
||||
<Stack className="foreground" tokens={{ childrenGap: 20 }} style={{ maxWidth: 450 }}>
|
||||
<Text variant="mediumPlus" style={{ fontWeight: 600 }}>{title}</Text>
|
||||
<Text>{children}</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<PrimaryButton text="Yes" onClick={onPrimary} />
|
||||
<DefaultButton text="No" onClick={onCancel} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
|
||||
interface PopoverMessageProps {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
onCancel: () => void;
|
||||
onPrimary: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const PopoverMessage: React.FC<PopoverMessageProps> = ({ visible, title, onCancel, onPrimary, children }) => {
|
||||
if (!visible) return null;
|
||||
return (
|
||||
<PopoverContainer title={title} onCancel={onCancel} onPrimary={onPrimary}>
|
||||
{children}
|
||||
</PopoverContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopoverMessage;
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { DatabaseAccount, Subscription } from "../../../../../../Contracts/DataModels";
|
||||
import { CopyJobMigrationType } from "../../../../Enums";
|
||||
import { CopyJobContextProviderType, CopyJobContextState, DropdownOptionType } from "../../../../Types";
|
||||
|
||||
export function useDropdownOptions(
|
||||
@@ -67,7 +68,7 @@ export function useEventHandlers(setCopyJobState: setCopyJobStateType) {
|
||||
(_ev?: React.FormEvent<HTMLElement>, checked?: boolean) => {
|
||||
setCopyJobState((prevState: CopyJobContextState) => ({
|
||||
...prevState,
|
||||
migrationType: checked ? "offline" : "online",
|
||||
migrationType: checked ? CopyJobMigrationType.Offline : CopyJobMigrationType.Online,
|
||||
}));
|
||||
},
|
||||
[setCopyJobState]
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useDatabaseAccounts } from "../../../../../hooks/useDatabaseAccounts";
|
||||
import { useSubscriptions } from "../../../../../hooks/useSubscriptions";
|
||||
import ContainerCopyMessages from "../../../ContainerCopyMessages";
|
||||
import { useCopyJobContext } from "../../../Context/CopyJobContext";
|
||||
import { CopyJobMigrationType } from "../../../Enums";
|
||||
import { AccountDropdown } from "./Components/AccountDropdown";
|
||||
import { MigrationTypeCheckbox } from "./Components/MigrationTypeCheckbox";
|
||||
import { SubscriptionDropdown } from "./Components/SubscriptionDropdown";
|
||||
@@ -18,12 +19,13 @@ const SelectAccount = React.memo(
|
||||
const selectedSubscriptionId = copyJobState?.source?.subscription?.subscriptionId;
|
||||
|
||||
const subscriptions: Subscription[] = useSubscriptions(armToken);
|
||||
const accounts: DatabaseAccount[] = useDatabaseAccounts(selectedSubscriptionId, armToken);
|
||||
const allAccounts: DatabaseAccount[] = useDatabaseAccounts(selectedSubscriptionId, armToken);
|
||||
const sqlApiOnlyAccounts: DatabaseAccount[] = allAccounts?.filter(account => account.type === "SQL" || account.kind === "GlobalDocumentDB");
|
||||
|
||||
const { subscriptionOptions, accountOptions } = useDropdownOptions(subscriptions, accounts);
|
||||
const { subscriptionOptions, accountOptions } = useDropdownOptions(subscriptions, sqlApiOnlyAccounts);
|
||||
const { handleSelectSourceAccount, handleMigrationTypeChange } = useEventHandlers(setCopyJobState);
|
||||
|
||||
const migrationTypeChecked = copyJobState?.migrationType === "offline";
|
||||
const migrationTypeChecked = copyJobState?.migrationType === CopyJobMigrationType.Offline;
|
||||
|
||||
return (
|
||||
<Stack className="selectAccountContainer" tokens={{ childrenGap: 15 }}>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { CopyJobContextState } from "../../Types";
|
||||
import AssignPermissions from "../Screens/AssignPermissions";
|
||||
import PreviewCopyJob from "../Screens/PreviewCopyJob";
|
||||
import SelectAccount from "../Screens/SelectAccount";
|
||||
import SelectSourceAndTargetContainers from "../Screens/SelectSourceAndTargetContainers";
|
||||
@@ -8,6 +9,7 @@ const SCREEN_KEYS = {
|
||||
SelectAccount: "SelectAccount",
|
||||
SelectSourceAndTargetContainers: "SelectSourceAndTargetContainers",
|
||||
PreviewCopyJob: "PreviewCopyJob",
|
||||
AssignPermissions: "AssignPermissions",
|
||||
};
|
||||
|
||||
type Validation = {
|
||||
@@ -29,7 +31,7 @@ function useCreateCopyJobScreensList() {
|
||||
component: <SelectAccount />,
|
||||
validations: [
|
||||
{
|
||||
validate: (state) => !!state?.source?.subscription && !!state?.source?.account,
|
||||
validate: (state: CopyJobContextState) => !!state?.source?.subscription && !!state?.source?.account,
|
||||
message: "Please select a subscription and account to proceed",
|
||||
},
|
||||
],
|
||||
@@ -39,7 +41,7 @@ function useCreateCopyJobScreensList() {
|
||||
component: <SelectSourceAndTargetContainers />,
|
||||
validations: [
|
||||
{
|
||||
validate: (state) => (
|
||||
validate: (state: CopyJobContextState) => (
|
||||
!!state?.source?.databaseId && !!state?.source?.containerId && !!state?.target?.databaseId && !!state?.target?.containerId
|
||||
),
|
||||
message: "Please select source and target containers to proceed",
|
||||
@@ -49,6 +51,16 @@ function useCreateCopyJobScreensList() {
|
||||
{
|
||||
key: SCREEN_KEYS.PreviewCopyJob,
|
||||
component: <PreviewCopyJob />,
|
||||
validations: [
|
||||
{
|
||||
validate: (state: CopyJobContextState) => !!state?.jobName,
|
||||
message: "Please enter a job name to proceed",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: SCREEN_KEYS.AssignPermissions,
|
||||
component: <AssignPermissions />,
|
||||
validations: [],
|
||||
},
|
||||
],
|
||||
|
||||
10
src/Explorer/ContainerCopy/Enums/index.ts
Normal file
10
src/Explorer/ContainerCopy/Enums/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum CopyJobMigrationType {
|
||||
Offline = "offline",
|
||||
Online = "online",
|
||||
}
|
||||
|
||||
export enum CopyJobMigrationStatus {
|
||||
Pause = "Pause",
|
||||
Resume = "Resume",
|
||||
Cancel = "Cancel",
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { DatabaseAccount, Subscription } from "Contracts/DataModels";
|
||||
import React from "react";
|
||||
import { ApiType } from "UserContext";
|
||||
import Explorer from "../../Explorer";
|
||||
import { CopyJobMigrationType } from "../Enums";
|
||||
|
||||
export interface ContainerCopyProps {
|
||||
container: Explorer;
|
||||
@@ -68,7 +69,7 @@ export interface DatabaseContainerSectionProps {
|
||||
|
||||
export interface CopyJobContextState {
|
||||
jobName: string;
|
||||
migrationType: "online" | "offline";
|
||||
migrationType: CopyJobMigrationType;
|
||||
// source details
|
||||
source: {
|
||||
subscription: Subscription;
|
||||
|
||||
@@ -46,4 +46,27 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.accordionHeader {
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.accordionHeaderText {
|
||||
margin-left: 5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.statusIcon {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.foreground {
|
||||
z-index: 10;
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
// transform: translate(0%, -5%);
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user