Skip to content

Commit 2dc06ac

Browse files
authored
Merge pull request #2458 from devtron-labs/feature/application-template
feat: add support for application templates in devtron apps
2 parents aab9e4a + 894b9a7 commit 2dc06ac

File tree

143 files changed

+4039
-1852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+4039
-1852
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ FEATURE_DEFAULT_MERGE_STRATEGY=
6464
FEATURE_CLUSTER_MAP_ENABLE=true
6565
FEATURE_DEFAULT_LANDING_RB_ENABLE=false
6666
FEATURE_ACTION_AUDIOS_ENABLE=true
67+
FEATURE_APPLICATION_TEMPLATES_ENABLE=true
6768
FEATURE_CODE_MIRROR_ENABLE=false
6869
FEATURE_EXPERIMENTAL_THEMING_ENABLE=true
6970
FEATURE_DEFAULT_AUTHENTICATED_VIEW_ENABLE=false

.eslintignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ vite.config.mts
88
# The following files have eslint errors/warnings
99
src/App.tsx
1010
src/Pages/GlobalConfigurations/Authorization/APITokens/__tests__/ApiTokens.test.tsx
11-
src/components/AppSelector/AppSelector.tsx
12-
src/components/AppSelector/AppSelectorUtil.tsx
1311
src/components/AppSelector/ChartSelector.tsx
1412
src/components/ApplicationGroup/AppGroup.service.ts
1513
src/components/ApplicationGroup/AppGroup.types.ts

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.9.7",
7+
"@devtron-labs/devtron-fe-common-lib": "1.9.8",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
AppSelectorNoOptionsMessage as appSelectorNoOptionsMessage,
3+
ComponentSizeType,
4+
InfoBlock,
5+
SelectPicker,
6+
SelectPickerProps,
7+
} from '@devtron-labs/devtron-fe-common-lib'
8+
import { appListOptions } from '@Components/AppSelector/AppSelectorUtil'
9+
import { useState } from 'react'
10+
import { AppToCloneSelectorProps } from './types'
11+
12+
const AppToCloneSelector = ({ isJobView, error, handleCloneIdChange }: AppToCloneSelectorProps) => {
13+
const [inputValue, setInputValue] = useState('')
14+
const [areOptionsLoading, setAreOptionsLoading] = useState(false)
15+
const [options, setOptions] = useState([])
16+
17+
const onInputChange: SelectPickerProps['onInputChange'] = async (val) => {
18+
setInputValue(val)
19+
setAreOptionsLoading(true)
20+
const fetchedOptions = await appListOptions(val, isJobView)
21+
setAreOptionsLoading(false)
22+
setOptions(fetchedOptions)
23+
}
24+
25+
const onChange = (selectedClonedApp) => {
26+
handleCloneIdChange(selectedClonedApp.value)
27+
}
28+
29+
const noOptionsMessage: SelectPickerProps['noOptionsMessage'] = () =>
30+
appSelectorNoOptionsMessage({
31+
inputValue,
32+
})
33+
34+
return (
35+
<>
36+
<SelectPicker
37+
label={`Select an ${isJobView ? 'job' : 'app'} to clone`}
38+
inputId={`${isJobView ? 'job' : 'app'}-name-for-clone`}
39+
options={options}
40+
onChange={onChange}
41+
placeholder={`Select ${isJobView ? 'job' : 'app'}`}
42+
inputValue={inputValue}
43+
onInputChange={onInputChange}
44+
isLoading={areOptionsLoading}
45+
noOptionsMessage={noOptionsMessage}
46+
error={error}
47+
size={ComponentSizeType.large}
48+
/>
49+
<InfoBlock
50+
heading="Important:"
51+
description={
52+
isJobView
53+
? 'Do not forget to modify git repositories and corresponding branches to be used for each Job Pipeline if required.'
54+
: 'Do not forget to modify git repositories, corresponding branches and container registries to be used for each CI Pipeline if required.'
55+
}
56+
/>
57+
</>
58+
)
59+
}
60+
61+
export default AppToCloneSelector
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { CustomInput, TagsContainer, Textarea } from '@devtron-labs/devtron-fe-common-lib'
2+
import { ReactComponent as ICDevtronApp } from '@Icons/ic-devtron-app.svg'
3+
import { ReactComponent as ICCaretLeftSmall } from '@Icons/ic-caret-left-small.svg'
4+
import { ChangeEvent } from 'react'
5+
import { importComponentFromFELibrary } from '@Components/common'
6+
import { APP_TYPE } from '@Config/constants'
7+
import ProjectSelector from './ProjectSelector'
8+
import {
9+
ApplicationInfoFormProps,
10+
CreateAppFormStateActionType,
11+
CreateAppFormStateType,
12+
CreationMethodType,
13+
HandleFormStateChangeParamsType,
14+
ProjectSelectorProps,
15+
} from './types'
16+
import AppToCloneSelector from './AppToCloneSelector'
17+
18+
const MandatoryTagsContainer = importComponentFromFELibrary('MandatoryTagsContainer', null, 'function')
19+
20+
const ApplicationInfoForm = ({
21+
formState,
22+
handleFormStateChange,
23+
isJobView,
24+
formErrorState,
25+
handleTagErrorChange,
26+
selectedCreationMethod,
27+
isTagsAccordionExpanded,
28+
toggleIsTagsAccordionExpanded,
29+
}: ApplicationInfoFormProps) => {
30+
const handleInputChange =
31+
(
32+
action: Extract<
33+
HandleFormStateChangeParamsType['action'],
34+
CreateAppFormStateActionType.updateName | CreateAppFormStateActionType.updateDescription
35+
>,
36+
) =>
37+
(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
38+
handleFormStateChange({ action, value: event.target.value })
39+
}
40+
41+
const handleProjectIdChange: ProjectSelectorProps['handleProjectIdChange'] = (projectId) => {
42+
handleFormStateChange({
43+
action: CreateAppFormStateActionType.updateProjectId,
44+
value: projectId,
45+
})
46+
}
47+
48+
const handleTagsUpdate = (tags: CreateAppFormStateType['tags']) => {
49+
handleFormStateChange({
50+
action: CreateAppFormStateActionType.updateTags,
51+
value: tags,
52+
})
53+
}
54+
55+
const handleCloneIdChange = (cloneId) => {
56+
handleFormStateChange({
57+
action: CreateAppFormStateActionType.updateCloneAppId,
58+
value: cloneId,
59+
})
60+
}
61+
62+
return (
63+
// key is required for ensuring autoFocus on name on creation method change
64+
<div className="flexbox-col dc__gap-16 p-20 br-8 border__secondary bg__primary" key={selectedCreationMethod}>
65+
<ICDevtronApp className="icon-dim-48 dc__no-shrink" />
66+
<div className="flexbox dc__gap-8">
67+
<ProjectSelector
68+
selectedProjectId={formState.projectId}
69+
handleProjectIdChange={handleProjectIdChange}
70+
error={formErrorState.projectId}
71+
/>
72+
<span className="pt-26 fs-20 lh-36 cn-7 dc__no-shrink">/</span>
73+
<CustomInput
74+
label={isJobView ? 'Job name' : 'Application name'}
75+
required
76+
name="name"
77+
onChange={handleInputChange(CreateAppFormStateActionType.updateName)}
78+
value={formState.name}
79+
placeholder="Enter name"
80+
error={formErrorState.name}
81+
helperText={
82+
!isJobView && 'Apps are NOT env specific and can be used to deploy to multiple environments.'
83+
}
84+
fullWidth
85+
autoFocus
86+
/>
87+
</div>
88+
<Textarea
89+
label="Description"
90+
name="description"
91+
value={formState.description}
92+
onChange={handleInputChange(CreateAppFormStateActionType.updateDescription)}
93+
placeholder={isJobView ? 'Describe this job' : 'Write a description for this application'}
94+
fullWidth
95+
error={formErrorState.description}
96+
/>
97+
<div className="flexbox-col dc__gap-16">
98+
<button
99+
className="dc__transparent p-0 flex left dc__gap-8 dc__w-fit-content"
100+
type="button"
101+
onClick={toggleIsTagsAccordionExpanded}
102+
>
103+
<ICCaretLeftSmall
104+
className={`scn-7 dc__no-shrink dc__transition--transform ${isTagsAccordionExpanded ? 'dc__flip-270' : 'dc__flip-180'}`}
105+
/>
106+
<span className="fs-13 fw-6 lh-20 cn-9">Add tags to {isJobView ? 'job' : 'application'}</span>
107+
</button>
108+
<div className={!isTagsAccordionExpanded ? 'dc__hide-section' : ''}>
109+
{MandatoryTagsContainer ? (
110+
<MandatoryTagsContainer
111+
isCreateApp
112+
appType={isJobView ? APP_TYPE.JOB : APP_TYPE.DEVTRON_APPS}
113+
projectId={formState.projectId}
114+
tags={formState.tags}
115+
setTags={handleTagsUpdate}
116+
tagsError={formErrorState.tags}
117+
setTagErrors={handleTagErrorChange}
118+
/>
119+
) : (
120+
<TagsContainer
121+
appType={isJobView ? APP_TYPE.JOB : APP_TYPE.DEVTRON_APPS}
122+
isCreateApp
123+
rows={formState.tags}
124+
setRows={handleTagsUpdate}
125+
tagsError={formErrorState.tags}
126+
setTagErrors={handleTagErrorChange}
127+
/>
128+
)}
129+
</div>
130+
</div>
131+
{selectedCreationMethod === CreationMethodType.clone && (
132+
<AppToCloneSelector
133+
error={formErrorState.cloneAppId}
134+
isJobView={isJobView}
135+
handleCloneIdChange={handleCloneIdChange}
136+
/>
137+
)}
138+
</div>
139+
)
140+
}
141+
142+
export default ApplicationInfoForm

0 commit comments

Comments
 (0)