Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create app revamp and support for templates #2451

Merged
merged 30 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d833c25
feat: add form state for create app modal
eshankvaish Feb 4, 2025
fc50923
fix: overflow issue
eshankvaish Feb 4, 2025
0b01f25
feat: add support for creation method
eshankvaish Feb 4, 2025
4d3908f
feat: add tags and form validation
eshankvaish Feb 5, 2025
c027f1f
refactor: code clean up and component breakdown
eshankvaish Feb 5, 2025
04a37f3
refactor: remove old create app modal file
eshankvaish Feb 5, 2025
e046391
refactor: common out the method for validation
eshankvaish Feb 5, 2025
d9664ec
feat: add clone app
eshankvaish Feb 5, 2025
5d92eaf
fix: checks for job
eshankvaish Feb 5, 2025
02c7c07
fix: width for project selector
eshankvaish Feb 5, 2025
d2e84de
fix: check for isJobView
eshankvaish Feb 5, 2025
bca23a2
fix: styling issues
eshankvaish Feb 5, 2025
c0aafee
feat: add feature flag for application templates
eshankvaish Feb 5, 2025
5b9da07
feat: add section for code source and build config
eshankvaish Feb 5, 2025
4a33084
fix: description margin
eshankvaish Feb 5, 2025
94c6f94
feat: add material list
eshankvaish Feb 6, 2025
5ce51de
feat: add build configuration
eshankvaish Feb 6, 2025
7a5aad2
feat: integration for build config state
eshankvaish Feb 6, 2025
34ee6c7
feat: hide templates based on flag
eshankvaish Feb 6, 2025
c453548
feat: sync state for git materials
eshankvaish Feb 6, 2025
005ad20
fix: race condition for form state
eshankvaish Feb 6, 2025
cf41038
feat: add icons for create app modal
eshankvaish Feb 6, 2025
1ad78da
feat: add handler for workflow config change
eshankvaish Feb 6, 2025
79007d9
feat: add payload for creation method
eshankvaish Feb 6, 2025
e3f6129
fix: typing for MaterialList component
eshankvaish Feb 6, 2025
165e644
refactor: code clean up
eshankvaish Feb 6, 2025
32604fd
refactor: migrate the CreateAppModal to Pages/App
eshankvaish Feb 6, 2025
56305f0
refactor: remove unused code
eshankvaish Feb 7, 2025
764a3d3
Merge branch 'feature/application-template' of github.com:devtron-lab…
eshankvaish Feb 10, 2025
5a5c3b8
feat: api integration
eshankvaish Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ FEATURE_CLUSTER_MAP_ENABLE=true
FEATURE_DEFAULT_LANDING_RB_ENABLE=false
FEATURE_ACTION_AUDIOS_ENABLE=true
FEATURE_EXPERIMENTAL_THEMING_ENABLE=false
FEATURE_APPLICATION_TEMPLATES_ENABLE=false
78 changes: 78 additions & 0 deletions src/Pages/App/CreateAppModal/AppToCloneSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import AsyncSelect from 'react-select/async'
import { multiSelectStyles } from '@devtron-labs/devtron-fe-common-lib'
import { appListOptions, noOptionsMessage } from '@Components/AppSelector/AppSelectorUtil'
import { Option } from '@Components/v2/common/ReactSelect.utils'
import { ReactComponent as ICError } from '@Icons/ic-warning.svg'
import { ReactComponent as ICInfoFilled } from '@Icons/ic-info-filled.svg'
import { AppToCloneSelectorProps } from './types'

const _multiSelectStyles = {
...multiSelectStyles,
control: (base, state) => ({
...multiSelectStyles.control(base, state),
cursor: 'pointer',
}),
menu: (base, state) => ({
...multiSelectStyles.menu(base, state),
marginTop: 'auto',
}),
menuList: (base) => ({
...base,
position: 'relative',
paddingBottom: '0px',
maxHeight: '180px',
}),
}

const AppToCloneSelector = ({ isJobView, error, handleCloneIdChange }: AppToCloneSelectorProps) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is moved from CreateApp

const loadAppListOptions = (inputValue: string) => appListOptions(inputValue, isJobView)

const onChange = (selectedClonedApp) => {
handleCloneIdChange(selectedClonedApp.value)
}

return (
<>
<div>
<span
className="form__label dc__required-field"
data-testid={`Clone-${isJobView ? 'job' : 'app'}-option`}
>
Select an {isJobView ? 'job' : 'app'} to clone
</span>
<AsyncSelect
classNamePrefix={`${isJobView ? 'job' : 'app'}-name-for-clone`}
loadOptions={loadAppListOptions}
noOptionsMessage={noOptionsMessage}
onChange={onChange}
styles={_multiSelectStyles}
components={{
IndicatorSeparator: null,
LoadingIndicator: null,
Option,
}}
placeholder={`Select ${isJobView ? 'job' : 'app'}`}
/>
{error && (
<span className="form__error">
<ICError className="form__icon form__icon--error" />
{error}
</span>
)}
</div>
<div className="dc__info-container info-container--create-app eb-2 mb-16">
<ICInfoFilled />
<div className="flex column left">
<div>
<div className="dc__info-title">Important: </div>
{isJobView
? 'Do not forget to modify git repositories and corresponding branches to be used for each Job Pipeline if required.'
: 'Do not forget to modify git repositories, corresponding branches and container registries to be used for each CI Pipeline if required.'}
</div>
</div>
</div>
</>
)
}

export default AppToCloneSelector
153 changes: 153 additions & 0 deletions src/Pages/App/CreateAppModal/ApplicationInfoForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { CustomInput, ResizableTextarea, TagsContainer } from '@devtron-labs/devtron-fe-common-lib'
import { ReactComponent as ICDevtronApp } from '@Icons/ic-devtron-app.svg'
import { ReactComponent as ICCaretLeftSmall } from '@Icons/ic-caret-left-small.svg'
import { ChangeEvent, useState } from 'react'
import { importComponentFromFELibrary } from '@Components/common'
import { APP_TYPE } from '@Config/constants'
import { ReactComponent as ICError } from '@Icons/ic-warning.svg'
import ProjectSelector from './ProjectSelector'
import {
ApplicationInfoFormProps,
CreateAppFormStateActionType,
CreateAppFormStateType,
CreationMethodType,
HandleFormStateChangeParamsType,
ProjectSelectorProps,
} from './types'
import AppToCloneSelector from './AppToCloneSelector'

const MandatoryTagsContainer = importComponentFromFELibrary('MandatoryTagsContainer', null, 'function')

const ApplicationInfoForm = ({
formState,
handleFormStateChange,
isJobView,
formErrorState,
handleTagErrorChange,
selectedCreationMethod,
}: ApplicationInfoFormProps) => {
const [isTagsAccordionExpanded, setIsTagsAccordionExpanded] = useState(false)

const toggleIsTagsAccordionExpanded = () => {
setIsTagsAccordionExpanded((prev) => !prev)
}

const handleInputChange =
(
action: Extract<
HandleFormStateChangeParamsType['action'],
CreateAppFormStateActionType.updateName | CreateAppFormStateActionType.updateDescription
>,
) =>
(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
handleFormStateChange({ action, value: event.target.value })
}

const handleProjectIdChange: ProjectSelectorProps['handleProjectIdChange'] = (projectId) => {
handleFormStateChange({
action: CreateAppFormStateActionType.updateProjectId,
value: projectId,
})
}

const handleTagsUpdate = (tags: CreateAppFormStateType['tags']) => {
handleFormStateChange({
action: CreateAppFormStateActionType.updateTags,
value: tags,
})
}

const handleCloneIdChange = (cloneId) => {
handleFormStateChange({
action: CreateAppFormStateActionType.updateCloneAppId,
value: cloneId,
})
}

return (
<div className="flexbox-col dc__gap-16 p-20 br-8 border__secondary bg__primary">
<ICDevtronApp className="icon-dim-48 dc__no-shrink" />
<div className="flexbox dc__gap-8">
<ProjectSelector
selectedProjectId={formState.projectId}
handleProjectIdChange={handleProjectIdChange}
error={formErrorState.projectId}
/>
<span className="pt-26 fs-20 lh-36 cn-7 dc__no-shrink">/</span>
<CustomInput
label={isJobView ? 'Job name' : 'Application name'}
isRequiredField
required
rootClassName="h-36"
name="name"
onChange={handleInputChange(CreateAppFormStateActionType.updateName)}
value={formState.name}
placeholder="Enter name"
inputWrapClassName="w-100"
error={formErrorState.name}
helperText={
!isJobView && 'Apps are NOT env specific and can be used to deploy to multiple environments.'
}
/>
</div>
<div>
<span className="form__label">Description</span>
<ResizableTextarea
name="description"
value={formState.description}
onChange={handleInputChange(CreateAppFormStateActionType.updateDescription)}
placeholder={isJobView ? 'Describe this job' : 'Write a description for this application'}
className="w-100"
/>
{formErrorState.description ? (
<span className="form__error">
<ICError className="form__icon form__icon--error" />
{formErrorState.description} <br />
</span>
) : null}
</div>
<div className="flexbox-col dc__gap-16">
<button
className="dc__transparent p-0 flex left dc__gap-8 dc__w-fit-content"
type="button"
onClick={toggleIsTagsAccordionExpanded}
>
<ICCaretLeftSmall
className={`scn-7 dc__no-shrink dc__transition--transform ${isTagsAccordionExpanded ? 'dc__flip-270' : 'dc__flip-180'}`}
/>
<span className="fs-13 fw-6 lh-20 cn-9">Add tags to {isJobView ? 'job' : 'application'}</span>
</button>
{isTagsAccordionExpanded &&
(MandatoryTagsContainer ? (
<MandatoryTagsContainer
isCreateApp
appType={isJobView ? APP_TYPE.JOB : APP_TYPE.DEVTRON_APPS}
projectId={formState.projectId}
tags={formState.tags}
setTags={handleTagsUpdate}
tagsError={formErrorState.tags}
setTagErrors={handleTagErrorChange}
/>
) : (
<TagsContainer
appType={isJobView ? APP_TYPE.JOB : APP_TYPE.DEVTRON_APPS}
isCreateApp
rows={formState.tags}
setRows={handleTagsUpdate}
tagsError={formErrorState.tags}
setTagErrors={handleTagErrorChange}
/>
))}
</div>
{selectedCreationMethod === CreationMethodType.clone && (
<AppToCloneSelector
error={formErrorState.cloneAppId}
isJobView={isJobView}
handleCloneIdChange={handleCloneIdChange}
/>
)}
</div>
)
}

export default ApplicationInfoForm
Loading
Loading