diff --git a/src/components/ApplicationDetails/ApplicationHeader.tsx b/src/components/ApplicationDetails/ApplicationHeader.tsx index d150f9635..35840bc28 100644 --- a/src/components/ApplicationDetails/ApplicationHeader.tsx +++ b/src/components/ApplicationDetails/ApplicationHeader.tsx @@ -12,13 +12,9 @@ export const ApplicationHeader: React.FC< - - - - {application?.spec?.displayName || ''} - - - + + {application?.spec?.displayName || ''} + ); diff --git a/src/components/ApplicationDetails/ApplicationThumbnail.tsx b/src/components/ApplicationDetails/ApplicationThumbnail.tsx index ee0df24b7..3704166cd 100644 --- a/src/components/ApplicationDetails/ApplicationThumbnail.tsx +++ b/src/components/ApplicationDetails/ApplicationThumbnail.tsx @@ -36,9 +36,12 @@ const getThumbnailFromApplication = (application: ApplicationKind) => { }; export const ApplicationThumbnail: React.FC< - React.PropsWithChildren<{ application: ApplicationKind }> -> = ({ application }) => { - const icon = getThumbnailFromApplication(application); + React.PropsWithChildren<{ application?: ApplicationKind; annotationValue?: number }> +> = ({ application, annotationValue }) => { + const icon = + annotationValue !== undefined + ? ICONS[annotationValue] + : getThumbnailFromApplication(application); return ( Application thumbnail ); diff --git a/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx b/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx new file mode 100644 index 000000000..811401e7a --- /dev/null +++ b/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { Flex, FlexItem, FormSection } from '@patternfly/react-core'; +import { useFormikContext } from 'formik'; +import { ApplicationThumbnail } from '../../ApplicationDetails/ApplicationThumbnail'; +import { InputField } from '../components/InputField'; +import { ImportFormValues } from '../type'; + +const ApplicationSection: React.FunctionComponent> = () => { + const { + values: { inAppContext }, + } = useFormikContext(); + + return ( + + + + + + + + + + + ); +}; + +export default ApplicationSection; diff --git a/src/components/ImportForm_new/ApplicationSection/__tests__/ApplicationSection.spec.tsx b/src/components/ImportForm_new/ApplicationSection/__tests__/ApplicationSection.spec.tsx new file mode 100644 index 000000000..7590470bd --- /dev/null +++ b/src/components/ImportForm_new/ApplicationSection/__tests__/ApplicationSection.spec.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { useField, useFormikContext } from 'formik'; +import ApplicationSection from '../ApplicationSection'; +import '@testing-library/jest-dom'; + +jest.mock('formik', () => ({ + useFormikContext: jest.fn(), + useField: jest.fn(), +})); + +const useFormikContextMock = useFormikContext as jest.Mock; +const useFieldMock = useField as jest.Mock; + +describe('ApplicationSection', () => { + it('should disable input field if in app context', () => { + const setValue = jest.fn(); + const setTouched = jest.fn(); + useFieldMock.mockReturnValue([{}, { value: '', touched: false }, { setValue, setTouched }]); + + useFormikContextMock.mockReturnValue({ values: { inAppContext: true } }); + render(); + screen.getByText('Name'); + expect(screen.getByPlaceholderText('Enter name')).toBeDisabled(); + }); +}); diff --git a/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx b/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx new file mode 100644 index 000000000..47d19eff9 --- /dev/null +++ b/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { Text, TextContent, TextVariants } from '@patternfly/react-core'; +import { InputField } from '../components/InputField'; +import { SourceSection } from './SourceSection'; + +export const ComponentSection = () => { + return ( + <> + + Component details + + A component is an image built from source code repository. + + + + + + ); +}; diff --git a/src/components/ImportForm_new/ComponentSection/GitOptions.tsx b/src/components/ImportForm_new/ComponentSection/GitOptions.tsx new file mode 100644 index 000000000..7296dcabe --- /dev/null +++ b/src/components/ImportForm_new/ComponentSection/GitOptions.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { ExpandableSection } from '@patternfly/react-core'; +import HelpPopover from '../../HelpPopover'; +import { InputField } from '../components/InputField'; + +const GitOptions: React.FC> = () => { + return ( + + + + + } + /> + + ); +}; + +export default GitOptions; diff --git a/src/components/ImportForm_new/ComponentSection/SourceSection.tsx b/src/components/ImportForm_new/ComponentSection/SourceSection.tsx new file mode 100644 index 000000000..3942c9eb6 --- /dev/null +++ b/src/components/ImportForm_new/ComponentSection/SourceSection.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { FormSection } from '@patternfly/react-core'; +import { InputField } from '../components/InputField'; +import GitOptions from './GitOptions'; + +export const SourceSection = () => { + return ( + + + + + + ); +}; diff --git a/src/components/ImportForm_new/GitImportActions.scss b/src/components/ImportForm_new/GitImportActions.scss new file mode 100644 index 000000000..08fec9596 --- /dev/null +++ b/src/components/ImportForm_new/GitImportActions.scss @@ -0,0 +1,8 @@ +.git-import-actions { + &__sticky { + position: fixed; + bottom: 0; + width: 100%; + padding-top: var(--pf-v5-global--spacer--lg); + } +} diff --git a/src/components/ImportForm_new/GitImportActions.tsx b/src/components/ImportForm_new/GitImportActions.tsx new file mode 100644 index 000000000..f7432f329 --- /dev/null +++ b/src/components/ImportForm_new/GitImportActions.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { + ActionList, + ActionListItem, + Button, + PageSection, + PageSectionVariants, +} from '@patternfly/react-core'; +import classNames from 'classnames'; +import { useFormikContext } from 'formik'; +import type { ImportFormValues } from './type'; + +import './GitImportActions.scss'; + +const GitImportActions: React.FunctionComponent = () => { + const { + values: { inAppContext, showComponent }, + isValid, + dirty, + isSubmitting, + setFieldValue, + } = useFormikContext(); + + const handleComponent = React.useCallback(() => { + setFieldValue('showComponent', true); + }, [setFieldValue]); + + return ( + + + + + + {!showComponent ? ( + + + + ) : null} + + + + + + ); +}; + +export default GitImportActions; diff --git a/src/components/ImportForm_new/GitImportForm.tsx b/src/components/ImportForm_new/GitImportForm.tsx new file mode 100644 index 000000000..231c799e0 --- /dev/null +++ b/src/components/ImportForm_new/GitImportForm.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import { Form, PageSection } from '@patternfly/react-core'; +import { Formik } from 'formik'; +import ApplicationSection from './ApplicationSection/ApplicationSection'; +import { ComponentSection } from './ComponentSection/ComponentSection'; +import GitImportActions from './GitImportActions'; +import { PipelineSection } from './PipelineSection/PipelineSection'; +import { ImportFormValues } from './type'; + +export const GitImportForm: React.FC<{ applicationName: string }> = ({ applicationName }) => { + const initialValues: ImportFormValues = { + application: applicationName || '', + inAppContext: !!applicationName, + showComponent: !!applicationName, + }; + + const handleSubmit = React.useCallback(() => {}, []); + const handleReset = React.useCallback(() => {}, []); + + return ( + + initialValues={initialValues} + onSubmit={handleSubmit} + onReset={handleReset} + > + {(formikProps) => { + return ( +
+ + + + + + + + ); + }} + + ); +}; diff --git a/src/components/ImportForm_new/ImportForm.tsx b/src/components/ImportForm_new/ImportForm.tsx new file mode 100644 index 000000000..62fbd8f0b --- /dev/null +++ b/src/components/ImportForm_new/ImportForm.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { PageSection, PageSectionVariants } from '@patternfly/react-core'; +import { useApplicationBreadcrumbs } from '../../utils/breadcrumb-utils'; +import PageLayout from '../PageLayout/PageLayout'; +import { GitImportForm } from './GitImportForm'; + +export const ImportForm: React.FC<{ applicationName: string }> = ({ applicationName }) => { + const applicationBreadcrumbs = useApplicationBreadcrumbs(applicationName); + return ( + + + + + + ); +}; diff --git a/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx b/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx new file mode 100644 index 000000000..822c95edb --- /dev/null +++ b/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { useFormikContext } from 'formik'; +import { DropdownField } from '../../../shared'; +// import { usePipelineTemplates } from './usePipelineTemplate'; + +export const PipelineSection: React.FunctionComponent = () => { + // const [pipelines, loaded] = usePipelineTemplates(); + const { setFieldValue, setFieldTouched } = useFormikContext(); + + const setValues = React.useCallback( + (type) => { + setFieldValue('pipeline', type); + setFieldTouched('pipeline', true); + }, + [setFieldValue, setFieldTouched], + ); + + // console.log('#################', pipelines, loaded); + + return ( + + ); +}; diff --git a/src/components/ImportForm_new/PipelineSection/usePipelineTemplate.ts b/src/components/ImportForm_new/PipelineSection/usePipelineTemplate.ts new file mode 100644 index 000000000..19e9d9f44 --- /dev/null +++ b/src/components/ImportForm_new/PipelineSection/usePipelineTemplate.ts @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { K8sResourceCommon, k8sGetResource } from '@openshift/dynamic-plugin-sdk-utils'; +import { ConfigMapModel } from '../../../models'; + +export const usePipelineTemplates = () => { + const [data, setdata] = React.useState(); + const [loaded, setLoaded] = React.useState(false); + + React.useEffect(() => { + let isMounted = true; + const fetchData = async () => { + try { + const res = await k8sGetResource({ + model: ConfigMapModel, + queryOptions: { ns: 'build-service', name: 'build-pipeline-config' }, + }); + if (isMounted) { + setdata(res); + setLoaded(true); + } + } catch (e) { + // eslint-disable-next-line no-console + console.warn('Error while fetching ConfigMap', e); + if (isMounted) { + setdata(undefined); + setLoaded(false); + } + } + }; + fetchData(); + return () => { + isMounted = false; + }; + }, []); + + return [data, loaded]; +}; diff --git a/src/components/ImportForm_new/components/InputField.scss b/src/components/ImportForm_new/components/InputField.scss new file mode 100644 index 000000000..829e8387e --- /dev/null +++ b/src/components/ImportForm_new/components/InputField.scss @@ -0,0 +1,5 @@ +.import-flow { + &__input { + max-width: 750px; + } +} \ No newline at end of file diff --git a/src/components/ImportForm_new/components/InputField.tsx b/src/components/ImportForm_new/components/InputField.tsx new file mode 100644 index 000000000..546887c5e --- /dev/null +++ b/src/components/ImportForm_new/components/InputField.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; +// use Input field from 'formik-pf' +import { InputField as FpfInput } from '../../../shared'; + +export const InputField: React.FunctionComponent> = ( + props, +) => ; diff --git a/src/components/ImportForm_new/type.ts b/src/components/ImportForm_new/type.ts new file mode 100644 index 000000000..63bf42bcf --- /dev/null +++ b/src/components/ImportForm_new/type.ts @@ -0,0 +1,5 @@ +export type ImportFormValues = { + application: string; + inAppContext: boolean; + showComponent: boolean; +}; diff --git a/src/components/ImportForm_new/validation.utils.ts b/src/components/ImportForm_new/validation.utils.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/models/config-map.ts b/src/models/config-map.ts new file mode 100644 index 000000000..4131c37bb --- /dev/null +++ b/src/models/config-map.ts @@ -0,0 +1,14 @@ +import { K8sModelCommon } from '@openshift/dynamic-plugin-sdk-utils'; +import { K8sGroupVersionKind } from './../dynamic-plugin-sdk'; + +export const ConfigMapModel: K8sModelCommon = { + apiVersion: 'v1', + kind: 'ConfigMap', + plural: 'configmaps', + namespaced: true, +}; + +export const ConfigMapGroupVersionKind: K8sGroupVersionKind = { + version: ConfigMapModel.apiVersion, + kind: ConfigMapModel.kind, +}; diff --git a/src/models/index.ts b/src/models/index.ts index 050a11647..86af184af 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -22,3 +22,4 @@ export * from './limit-range'; export * from './remotesecret'; export * from './workspace'; export * from './space-binding-request'; +export * from './config-map'; diff --git a/src/pages/ImportPage.tsx b/src/pages/ImportPage.tsx index 02559b03d..aa5926315 100644 --- a/src/pages/ImportPage.tsx +++ b/src/pages/ImportPage.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import ImportForm from '../components/ImportForm/ImportForm'; +import { ImportForm } from '../components/ImportForm_new/ImportForm'; import NamespacedPage from '../components/NamespacedPage/NamespacedPage'; import PageAccessCheck from '../components/PageAccess/PageAccessCheck'; import { useQuickstartCloseOnUnmount } from '../hooks/useQuickstartCloseOnUnmount'; -import { ApplicationModel, ComponentDetectionQueryModel, ComponentModel } from '../models'; +import { ApplicationModel, ComponentModel } from '../models'; import { getQueryArgument } from '../shared/utils'; import { AccessReviewResources } from '../types'; @@ -13,14 +13,10 @@ const ImportPage: React.FunctionComponent> = () const applicationName = getQueryArgument('application'); const accessReviewResources: AccessReviewResources = applicationName - ? [ - { model: ComponentModel, verb: 'create' }, - { model: ComponentDetectionQueryModel, verb: 'create' }, - ] + ? [{ model: ComponentModel, verb: 'create' }] : [ { model: ApplicationModel, verb: 'create' }, { model: ComponentModel, verb: 'create' }, - { model: ComponentDetectionQueryModel, verb: 'create' }, ]; return (