diff --git a/app/jenkins-for-jira-ui/src/components/JenkinsSetup/JenkinsSetup.tsx b/app/jenkins-for-jira-ui/src/components/JenkinsSetup/JenkinsSetup.tsx index e94ffc2e..a45f0e51 100644 --- a/app/jenkins-for-jira-ui/src/components/JenkinsSetup/JenkinsSetup.tsx +++ b/app/jenkins-for-jira-ui/src/components/JenkinsSetup/JenkinsSetup.tsx @@ -11,6 +11,7 @@ import Button, { ButtonGroup } from '@atlaskit/button'; import InfoIcon from '@atlaskit/icon/glyph/info'; import CopyIcon from '@atlaskit/icon/glyph/copy'; import OpenIcon from '@atlaskit/icon/glyph/open'; +import CheckIcon from '@atlaskit/icon/glyph/check'; import Tooltip from '@atlaskit/tooltip'; import { useHistory, useParams } from 'react-router'; import Spinner from '@atlaskit/spinner'; @@ -34,7 +35,6 @@ import { } from './JenkinsSetup.styles'; import { getJenkinsServerWithSecret } from '../../api/getJenkinsServerWithSecret'; import { serverNameFormOuterContainer } from '../ServerNameForm/ServerNameForm.styles'; -import { CopiedToClipboard } from '../CopiedToClipboard/CopiedToClipboard'; import { ConnectionFlowHeader } from '../ConnectionWizard/ConnectionFlowHeader'; import { SecretTokenContent, WebhookGuideContent } from '../CopiedToClipboard/CopyToClipboardContent'; import { getWebhookUrl } from '../../common/util/jenkinsConnectionsUtils'; @@ -47,26 +47,34 @@ import { } from '../../common/analytics/analytics-events'; import { AnalyticsClient } from '../../common/analytics/analytics-client'; import { ParamTypes } from '../../common/types'; +import { CopiedToClipboard } from '../CopiedToClipboard/CopiedToClipboard'; const analyticsClient = new AnalyticsClient(); type CopyProps = { handleCopyToClipboard: (copyRef: React.RefObject, elementName?: string) => Promise | void; + handleCopyButton: (copyButton: CopyButtonNameEnum) => void; + primaryButtonName: CopyButtonNameEnum; + copyButtonName: CopyButtonNameEnum; testId?: string }; const CopyButton = ({ handleCopyToClipboard, copyRef, + handleCopyButton, + primaryButtonName, + copyButtonName, testId }: CopyProps & { copyRef: React.RefObject }): JSX.Element => { const [isCopied, setIsCopied] = useState(false); + const [showIsCopiedToClipboardTooltip, setShowIsCopiedToClipboardTooltip] = useState(false); useEffect(() => { let timeoutId: NodeJS.Timeout; - if (isCopied) { + if (showIsCopiedToClipboardTooltip) { timeoutId = setTimeout(() => { - setIsCopied(false); + setShowIsCopiedToClipboardTooltip(false); }, 2000); } return () => { @@ -74,38 +82,45 @@ const CopyButton = ({ clearTimeout(timeoutId); } }; - }, [isCopied]); + }, [showIsCopiedToClipboardTooltip]); return (
- - {isCopied && } + {showIsCopiedToClipboardTooltip && }
); }; type MyJenkinsAdminProps = { + handleCopyToClipboard: (copyRef: React.RefObject, elementName?: string) => Promise | void; + handleCopyButton: (copyButton: CopyButtonNameEnum) => void, webhookGuideRef: RefObject, - secretTokenRef: RefObject + secretTokenRef: RefObject, + primaryButtonName: CopyButtonNameEnum, }; const MyJenkinsAdmin = ({ handleCopyToClipboard, + handleCopyButton, webhookGuideRef, - secretTokenRef -}: CopyProps & MyJenkinsAdminProps): JSX.Element => { + secretTokenRef, + primaryButtonName +}: MyJenkinsAdminProps): JSX.Element => { const tooltipContent = 'Send this token separately to the webhook URL and step-by-step guide. It\'s best practice to use a secure channel like a password management tool.'; @@ -118,6 +133,9 @@ const MyJenkinsAdmin = ({ Webhook URL and step-by-step guide @@ -126,7 +144,12 @@ const MyJenkinsAdmin = ({ - + @@ -134,17 +157,22 @@ const MyJenkinsAdmin = ({ }; type IAmTheJenkinsAdminProps = { + handleCopyToClipboard: (copyRef: React.RefObject, elementName?: string) => Promise | void; + handleCopyButton: (copyButton: CopyButtonNameEnum) => void, siteNameRef: RefObject, webhookUrlRef: RefObject, secretRef: RefObject, + primaryButtonName: CopyButtonNameEnum, }; const IAmTheJenkinsAdmin = ({ handleCopyToClipboard, + handleCopyButton, siteNameRef, webhookUrlRef, - secretRef -}: CopyProps & IAmTheJenkinsAdminProps): JSX.Element => { + secretRef, + primaryButtonName +}: IAmTheJenkinsAdminProps): JSX.Element => { const handleFollowLink = async (e: React.MouseEvent): Promise => { e.preventDefault(); @@ -183,17 +211,28 @@ const IAmTheJenkinsAdmin = ({ Site name
  • Webhook URL
  • Secret - +
  • @@ -202,7 +241,25 @@ const IAmTheJenkinsAdmin = ({ ); }; +type CopyButtonNameEnum = 'adminSiteName' | 'adminWebhook' | 'adminSecret' | 'nonAdminWebhook' | 'nonAdminSecret'; + +interface CopyButtonMetadata { + name: CopyButtonNameEnum; + isClicked: boolean; +} + const JenkinsSetup = (): JSX.Element => { + const initialAdminButtonStates: CopyButtonMetadata[] = [ + { name: 'adminSiteName', isClicked: false }, + { name: 'adminWebhook', isClicked: false }, + { name: 'adminSecret', isClicked: false } + ]; + + const initialNonAdminButtonStates: CopyButtonMetadata[] = [ + { name: 'nonAdminWebhook', isClicked: false }, + { name: 'nonAdminSecret', isClicked: false } + ]; + const history = useHistory(); const { path } = useParams(); const webhookGuideRef = useRef(null); @@ -214,12 +271,37 @@ const JenkinsSetup = (): JSX.Element => { const [serverName, setServerName] = useState(''); const [showMyJenkinsAdmin, setShowMyJenkinsAdmin] = useState(false); const [showIAmTheJenkinsAdmin, setShowIAmTheJenkinsAdmin] = useState(false); + const [copyAdminButtonStates, setCopyAdminButtonStates] = useState(initialAdminButtonStates); + const [copyNonAdminButtonStates, setCopyNonAdminButtonStates] = + useState(initialNonAdminButtonStates); const [webhookUrl, setWebhookUrl] = useState(''); const [secret, setSecret] = useState(''); const [siteName, setSiteName] = useState(''); const [globalPageUrl, setGlobalPageUrl] = useState(''); + const [primaryCopyButtonName, setPrimaryCopyButtonName] = + useState('nonAdminWebhook'); const connectionSettings = settings === 'connection-settings'; + const updateAdminCopyButtonState = (key: CopyButtonNameEnum, isClicked: boolean) => { + const existingItemIndex = copyAdminButtonStates.findIndex((item) => item.name === key); + + if (existingItemIndex !== -1) { + const updatedButtons = [...copyAdminButtonStates]; + updatedButtons[existingItemIndex].isClicked = isClicked; + setCopyAdminButtonStates(updatedButtons); + } + }; + + const updateNonAdminCopyButtonState = (key: CopyButtonNameEnum, isClicked: boolean) => { + const existingItemIndex = copyNonAdminButtonStates.findIndex((item) => item.name === key); + + if (existingItemIndex !== -1) { + const updatedButtons = [...copyNonAdminButtonStates]; + updatedButtons[existingItemIndex].isClicked = isClicked; + setCopyNonAdminButtonStates(updatedButtons); + } + }; + const getServer = useCallback(async () => { try { const { name, secret: retrievedSecret } = await getJenkinsServerWithSecret(uuid); @@ -252,6 +334,31 @@ const JenkinsSetup = (): JSX.Element => { fetchData(); }, [uuid, getServer]); + const findNextPrimaryButtonIndex = (buttonStates: CopyButtonMetadata[]) => { + return buttonStates.findIndex((item) => item.isClicked === false); + }; + + const handleAdminCopyButton = (copyButtonName: CopyButtonNameEnum) => { + updateAdminCopyButtonState(copyButtonName, true); + const nextPrimaryButtonIndex = findNextPrimaryButtonIndex(copyAdminButtonStates); + if (nextPrimaryButtonIndex !== -1) { + setPrimaryCopyButtonName(copyAdminButtonStates[nextPrimaryButtonIndex].name); + } + }; + + const handleNonAdminCopyButton = (copyButtonName: CopyButtonNameEnum) => { + updateNonAdminCopyButtonState(copyButtonName, true); + const nextPrimaryButtonIndex = findNextPrimaryButtonIndex(copyNonAdminButtonStates); + if (nextPrimaryButtonIndex !== -1) { + setPrimaryCopyButtonName(copyNonAdminButtonStates[nextPrimaryButtonIndex].name); + } + }; + + const clearCopiedButtonStates = () => { + setCopyAdminButtonStates(initialAdminButtonStates); + setCopyNonAdminButtonStates(initialNonAdminButtonStates); + }; + const handleCopyToClipboard = async (copyRef: React.RefObject, elementName?: string) => { if (copyRef.current) { @@ -290,6 +397,10 @@ const JenkinsSetup = (): JSX.Element => { const handleMyJenkinsAdminClick = async (e: React.MouseEvent) => { e.preventDefault(); + if (!showMyJenkinsAdmin) { + clearCopiedButtonStates(); + setPrimaryCopyButtonName('nonAdminWebhook'); + } setShowMyJenkinsAdmin(true); setShowIAmTheJenkinsAdmin(false); @@ -306,6 +417,10 @@ const JenkinsSetup = (): JSX.Element => { const handleIAmTheJenkinsAdminClick = async (e: React.MouseEvent) => { e.preventDefault(); + if (!showIAmTheJenkinsAdmin) { + clearCopiedButtonStates(); + setPrimaryCopyButtonName('adminSiteName'); + } setShowIAmTheJenkinsAdmin(true); setShowMyJenkinsAdmin(false); @@ -343,6 +458,9 @@ const JenkinsSetup = (): JSX.Element => { const isFetchingData = !serverName || !webhookUrl || !secret; + const isAllRequiredButtonsClicked = copyAdminButtonStates.every((button) => button.isClicked) || + (copyNonAdminButtonStates.every((button) => button.isClicked)); + return (
    @@ -391,6 +509,8 @@ const JenkinsSetup = (): JSX.Element => { {showMyJenkinsAdmin ? ( @@ -399,6 +519,8 @@ const JenkinsSetup = (): JSX.Element => { {showIAmTheJenkinsAdmin ? ( {