diff --git a/src/Explorer/ContainerCopy/ContainerCopyMessages.ts b/src/Explorer/ContainerCopy/ContainerCopyMessages.ts index a72f9abcc..3791e87de 100644 --- a/src/Explorer/ContainerCopy/ContainerCopyMessages.ts +++ b/src/Explorer/ContainerCopy/ContainerCopyMessages.ts @@ -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", + } } \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CopyJobUtils.ts b/src/Explorer/ContainerCopy/CopyJobUtils.ts new file mode 100644 index 000000000..9fb9f52b3 --- /dev/null +++ b/src/Explorer/ContainerCopy/CopyJobUtils.ts @@ -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}`; +} \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddManagedIdentity.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddManagedIdentity.tsx new file mode 100644 index 000000000..e87d960a1 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddManagedIdentity.tsx @@ -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 ( + + + {ContainerCopyMessages.addManagedIdentity.toggleLabel}  + + } + checked={systemAssigned} + onText={ContainerCopyMessages.toggleBtn.onText} + offText={ContainerCopyMessages.toggleBtn.offText} + onChange={onToggle} + /> + + {ContainerCopyMessages.addManagedIdentity.userAssignedIdentityLabel}  + +
+ + {ContainerCopyMessages.addManagedIdentity.createUserAssignedIdentityLink} + +
+ onToggle(null, false)} + onPrimary={() => console.log('Primary action taken')} + > + {ContainerCopyMessages.addManagedIdentity.enablementDescription(copyJobState.target?.account?.name)} + +
+ ); +}; + +export default AddManagedIdentity; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx new file mode 100644 index 000000000..cec472a53 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/AddReadPermissionToDefaultIdentity.tsx @@ -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 = { root: { display: 'inline-block' } }; + +const AddReadPermissionToDefaultIdentity: React.FC = () => { + const [readPermissionAssigned, onToggle] = useToggle(false); + + return ( + +
+ {ContainerCopyMessages.readPermissionAssigned.description}   +
+ + onToggle(null, false)} + onPrimary={() => console.log('Primary action taken')} + > + {ContainerCopyMessages.readPermissionAssigned.popoverDescription} + +
+ ); +}; + +export default AddReadPermissionToDefaultIdentity; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/DefaultManagedIdentity.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/DefaultManagedIdentity.tsx new file mode 100644 index 000000000..50de01ae2 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/DefaultManagedIdentity.tsx @@ -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 ( + +
+ {ContainerCopyMessages.defaultManagedIdentity.description}   +
+ + onToggle(null, false)} + onPrimary={() => console.log('Primary action taken')} + > + {ContainerCopyMessages.defaultManagedIdentity.popoverDescription} + +
+ ); +}; + +export default DefaultManagedIdentity; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/OnlineCopyEnabled.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/OnlineCopyEnabled.tsx new file mode 100644 index 000000000..b8d041db1 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/OnlineCopyEnabled.tsx @@ -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 ( + +
+ {ContainerCopyMessages.onlineCopyEnabled.description} +
+ +
+ ); +}; + +export default OnlineCopyEnabled; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/PointInTimeRestore.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/PointInTimeRestore.tsx new file mode 100644 index 000000000..3ef23d0c3 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/PointInTimeRestore.tsx @@ -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 ( + +
+ {ContainerCopyMessages.pointInTimeRestore.description} +
+ +
+ ); +}; + +export default PointInTimeRestore; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/usePermissionsSection.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/usePermissionsSection.tsx new file mode 100644 index 000000000..b60edeaf0 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/usePermissionsSection.tsx @@ -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; \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useToggle.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useToggle.tsx new file mode 100644 index 000000000..3c995eafc --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useToggle.tsx @@ -0,0 +1,11 @@ +import { useCallback, useState } from "react"; + +const useToggle = (initialState = false) => { + const [state, setState] = useState(initialState); + const onToggle = useCallback((_, checked?: boolean) => { + setState(!!checked); + }, []); + return [state, onToggle] as const; +}; + +export default useToggle; \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useWindowOpenMonitor.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useWindowOpenMonitor.tsx new file mode 100644 index 000000000..ce4daab34 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/hooks/useWindowOpenMonitor.tsx @@ -0,0 +1,32 @@ +import { useEffect, useRef } from "react"; + +const useWindowOpenMonitor = (url: string, onClose?: () => void, intervalMs = 500) => { + const intervalRef = useRef(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; \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/index.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/index.tsx new file mode 100644 index 000000000..ee7934795 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/AssignPermissions/index.tsx @@ -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 = ({ id, title, Component }) => ( + + + {title} + Warning icon + + + + + +); + +const AssignPermissions = () => { + const { copyJobState } = useCopyJobContext(); + const permissionSections = usePermissionSections(copyJobState); + return ( + + + {ContainerCopyMessages.assignPermissions.description} + + + { + permissionSections.map(section => ( + + )) + } + + + ); +}; + +export default AssignPermissions; \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/InfoTooltip.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/InfoTooltip.tsx new file mode 100644 index 000000000..1d6f9808f --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/InfoTooltip.tsx @@ -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 = { root: { display: 'inline-block' } }; + return ( + + Information + + ); +}; + +export default React.memo(InfoTooltip); \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/PopoverContainer.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/PopoverContainer.tsx new file mode 100644 index 000000000..dc8424233 --- /dev/null +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/Components/PopoverContainer.tsx @@ -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 = React.memo(({ title, children, onPrimary, onCancel }) => { + return ( + + {title} + {children} + + + + + + ); +}); + +interface PopoverMessageProps { + visible: boolean; + title: string; + onCancel: () => void; + onPrimary: () => void; + children: React.ReactNode; +} + +const PopoverMessage: React.FC = ({ visible, title, onCancel, onPrimary, children }) => { + if (!visible) return null; + return ( + + {children} + + ); +}; + +export default PopoverMessage; diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx index 2a79285ea..46c778d08 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/Utils/selectAccountUtils.tsx @@ -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, checked?: boolean) => { setCopyJobState((prevState: CopyJobContextState) => ({ ...prevState, - migrationType: checked ? "offline" : "online", + migrationType: checked ? CopyJobMigrationType.Offline : CopyJobMigrationType.Online, })); }, [setCopyJobState] diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/index.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/index.tsx index 241a59536..bf070e413 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/index.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Screens/SelectAccount/index.tsx @@ -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 ( diff --git a/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx b/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx index 1d6108ce7..9ea950d22 100644 --- a/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx +++ b/src/Explorer/ContainerCopy/CreateCopyJob/Utils/useCreateCopyJobScreensList.tsx @@ -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: , 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: , 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: , + validations: [ + { + validate: (state: CopyJobContextState) => !!state?.jobName, + message: "Please enter a job name to proceed", + }, + ], + }, + { + key: SCREEN_KEYS.AssignPermissions, + component: , validations: [], }, ], diff --git a/src/Explorer/ContainerCopy/Enums/index.ts b/src/Explorer/ContainerCopy/Enums/index.ts new file mode 100644 index 000000000..97beac1ec --- /dev/null +++ b/src/Explorer/ContainerCopy/Enums/index.ts @@ -0,0 +1,10 @@ +export enum CopyJobMigrationType { + Offline = "offline", + Online = "online", +} + +export enum CopyJobMigrationStatus { + Pause = "Pause", + Resume = "Resume", + Cancel = "Cancel", +} \ No newline at end of file diff --git a/src/Explorer/ContainerCopy/Types/index.ts b/src/Explorer/ContainerCopy/Types/index.ts index 5d66e8d01..d97eece8e 100644 --- a/src/Explorer/ContainerCopy/Types/index.ts +++ b/src/Explorer/ContainerCopy/Types/index.ts @@ -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; diff --git a/src/Explorer/ContainerCopy/containerCopyStyles.less b/src/Explorer/ContainerCopy/containerCopyStyles.less index 0c3f41b8e..84ba37b53 100644 --- a/src/Explorer/ContainerCopy/containerCopyStyles.less +++ b/src/Explorer/ContainerCopy/containerCopyStyles.less @@ -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; + } }