diff --git a/src/Pages/Templates/TemplateDetails/TemplateDetails.test.tsx b/src/Pages/Templates/TemplateDetails/TemplateDetails.test.tsx index fdcf4891..f0237644 100644 --- a/src/Pages/Templates/TemplateDetails/TemplateDetails.test.tsx +++ b/src/Pages/Templates/TemplateDetails/TemplateDetails.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import {fireEvent, render} from '@testing-library/react'; import TemplateDetails from './TemplateDetails'; import { defaultTemplateItem } from 'testingHelpers'; @@ -14,6 +14,9 @@ jest.mock('react-router-dom', () => ({ useNavigate: jest.fn(), useParams: () => ({ templateUUID: 'templateUUID' }), Outlet: () => <>, + useLocation: () => ({ + pathname: '/templates/8b9f5062-5256-459a-9833-2b85b735225b/details/content/advisories', + }), })); jest.mock('Hooks/useArchVersion', () => () => ({ @@ -31,3 +34,22 @@ it('expect TemplateDetails to render correctly', () => { expect(queryByText('Rhel9')).toBeInTheDocument(); expect(queryAllByText(defaultTemplateItem.name)).toHaveLength(2); }); + +it('expect UseTemplateModal to render correctly', () => { + const { queryByText, getByLabelText } = render(); + + const useTemplate = getByLabelText('use-template-button') as Element; + expect(useTemplate).toBeInTheDocument(); + fireEvent.click(useTemplate); + expect(queryByText('Assign the template to a system')).toBeInTheDocument() + + const curlTab = getByLabelText('curl-tab') as Element; + expect(curlTab).toBeInTheDocument(); + fireEvent.click(curlTab); + expect(queryByText('Download the repo file')).toBeInTheDocument() + + const ansibleTab = getByLabelText('ansible-tab') as Element; + expect(ansibleTab).toBeInTheDocument(); + fireEvent.click(ansibleTab); + expect(queryByText('Use this ansible playbook to download the repo file')).toBeInTheDocument() +}); diff --git a/src/Pages/Templates/TemplateDetails/TemplateDetails.tsx b/src/Pages/Templates/TemplateDetails/TemplateDetails.tsx index a380de59..97a233ad 100644 --- a/src/Pages/Templates/TemplateDetails/TemplateDetails.tsx +++ b/src/Pages/Templates/TemplateDetails/TemplateDetails.tsx @@ -7,7 +7,7 @@ import { LabelGroup, Stack, StackItem, - Title, + Title, Toolbar, ToolbarContent, ToolbarItem, } from '@patternfly/react-core'; import { Outlet, useNavigate, useParams } from 'react-router-dom'; import { createUseStyles } from 'react-jss'; @@ -17,10 +17,12 @@ import { useFetchTemplate } from 'services/Templates/TemplateQueries'; import useArchVersion from 'Hooks/useArchVersion'; import DetailItem from './components/DetaiItem'; import { formatDateDDMMMYYYY } from 'helpers'; -import TemplateDetailsTabs from './components/TemplateDetailsTabs'; import { global_BackgroundColor_light_100 } from '@patternfly/react-tokens'; import Loader from 'components/Loader'; import TemplateActionDropdown from './components/TemplateActionDropdown'; +import UseTemplateModal from './components/UseTemplate/UseTemplateModal'; +import React from 'react'; +import TemplateDetailsTabs from './components/TemplateDetailsTabs'; const useStyles = createUseStyles({ fullHeight: { @@ -107,7 +109,16 @@ export default function TemplateDetails() { - + + + + + + + + + + +} + +const playbook1 = `--- +- hosts: all + tasks: + - name: Download template.repo + ansible.builtin.get_url: + url: https://console.redhat.com/api/content-sources/v1/templates/` +const playbook2 = `/config.repo + dest: /etc/yum.repos.d/template.repo + mode: '0444' + client_cert: /etc/pki/consumer/cert.pem + client_key: /etc/pki/consumer/cert.key +` + +const AnsibleTab = ({ tabContentRef }: Props) => { + const classes = useStyles(); + const { templateUUID } = useParams(); + const [copied, setCopied] = React.useState(false); + + const clipboardCopyFunc = (event, text) => { + navigator.clipboard.writeText(text.toString()); + }; + + const onClick = (event, text) => { + clipboardCopyFunc(event, text); + setCopied(true); + }; + + const actions = ( + + + onClick(e, playbook1+`${templateUUID}`+playbook2)} + exitDelay={copied ? 1500 : 600} + maxWidth="110px" + variant="plain" + onTooltipHidden={() => setCopied(false)} + > + {copied ? 'Successfully copied to clipboard!' : 'Copy to clipboard'} + + + + ); + + return ( + + ) +} + +export default AnsibleTab; diff --git a/src/Pages/Templates/TemplateDetails/components/UseTemplate/CurlTab.tsx b/src/Pages/Templates/TemplateDetails/components/UseTemplate/CurlTab.tsx new file mode 100644 index 00000000..dedd38ff --- /dev/null +++ b/src/Pages/Templates/TemplateDetails/components/UseTemplate/CurlTab.tsx @@ -0,0 +1,62 @@ +import { + ClipboardCopy, + ClipboardCopyVariant, + TabContent, + Text, TextContent, + TextList, + TextListItem, + TextVariants +} from '@patternfly/react-core'; +import React from 'react'; +import {createUseStyles} from 'react-jss'; +import {useParams} from 'react-router-dom'; + +export const CURL_TAB = 'curl' + +const useStyles = createUseStyles({ + textGroup: { + paddingTop: '8px', + }, +}) + +type Props = { + tabContentRef: React.RefObject +} + +const CurlTab = ({ tabContentRef }: Props) => { + const classes = useStyles(); + const { templateUUID } = useParams(); + + return ( + + ) +} + +export default CurlTab; diff --git a/src/Pages/Templates/TemplateDetails/components/UseTemplate/InsightsTab.tsx b/src/Pages/Templates/TemplateDetails/components/UseTemplate/InsightsTab.tsx new file mode 100644 index 00000000..f77f10ab --- /dev/null +++ b/src/Pages/Templates/TemplateDetails/components/UseTemplate/InsightsTab.tsx @@ -0,0 +1,63 @@ +import { + ClipboardCopy, + ClipboardCopyVariant, + TabContent, + Text, TextContent, + TextList, + TextListItem, + TextVariants +} from '@patternfly/react-core'; +import React from 'react'; +import {ADD_ROUTE, DETAILS_ROUTE, SYSTEMS_ROUTE} from '../../../../../Routes/constants'; +import {ExternalLinkAltIcon} from '@patternfly/react-icons'; +import {createUseStyles} from 'react-jss'; +import {useLocation} from 'react-router-dom'; + +export const INSIGHTS_TAB = 'insights' + +const useStyles = createUseStyles({ + textGroup: { + paddingTop: '8px', + }, +}) + +type Props = { + tabContentRef: React.RefObject +} + +const InsightsTab = ({ tabContentRef }: Props) => { + const classes = useStyles(); + + const { pathname } = useLocation(); + const [mainRoute] = pathname?.split(`${DETAILS_ROUTE}/`) || []; + const addSystemsRoute = mainRoute + `${DETAILS_ROUTE}/`+ `${SYSTEMS_ROUTE}/` + `${ADD_ROUTE}/`; + + return ( + + + Register for subscriptions + + + Register with rhc + + rhc connect + + + + Assign the template to a system + + + + Add template in {' '} + + Systems + + + + + + + ) +} + +export default InsightsTab; diff --git a/src/Pages/Templates/TemplateDetails/components/UseTemplate/UseTemplateModal.tsx b/src/Pages/Templates/TemplateDetails/components/UseTemplate/UseTemplateModal.tsx new file mode 100644 index 00000000..b0724eb9 --- /dev/null +++ b/src/Pages/Templates/TemplateDetails/components/UseTemplate/UseTemplateModal.tsx @@ -0,0 +1,85 @@ +import { + Button, + Modal, + ModalVariant, + Tab, + Tabs, + TabTitleText, +} from '@patternfly/react-core' +import React from 'react'; +import InsightsTab, {INSIGHTS_TAB} from './InsightsTab'; +import CurlTab, {CURL_TAB} from './CurlTab'; +import AnsibleTab, {ANSIBLE_TAB} from './AnsibleTab'; + +const UseTemplateModal = () => { + const [isModalOpen, setIsModalOpen] = React.useState(false); + const [activeTabKey, setActiveTabKey] = React.useState(INSIGHTS_TAB); + + const handleModalToggle = () => { + setIsModalOpen(!isModalOpen); + }; + + const handleTabClick = (_event: React.MouseEvent, tabIndex: string | number) => { + setActiveTabKey(tabIndex); + }; + + const contentRefInsights = React.createRef(); + const contentRefCurl = React.createRef(); + const contentRefAnsible = React.createRef(); + + return ( +
+ + + + Close + + } + > + + Insights (Preferred)} + tabContentId={INSIGHTS_TAB} + tabContentRef={contentRefInsights} + aria-label='insights-tab' + /> + Curl} + tabContentId={CURL_TAB} + tabContentRef={contentRefCurl} + aria-label='curl-tab' + /> + Ansible} + tabContentId={ANSIBLE_TAB} + tabContentRef={contentRefAnsible} + aria-label='ansible-tab' + /> + +
+ + + +
+
+
+ ) +} + +export default UseTemplateModal