@@ -18,11 +18,14 @@ import {
1818 useK8sModel ,
1919 k8sCreate ,
2020 useActiveNamespace ,
21+ YAMLEditor ,
2122} from "@openshift-console/dynamic-plugin-sdk" ;
2223import { useNavigate } from "react-router-dom-v5-compat" ;
2324import { useCronTabTranslation } from "@crontab-utils/hooks/useCronTabTranslation" ;
2425import { cronTabGroupVersionKind } from "src/utils/utils" ;
2526import { CronTabKind } from "@crontab-model/types" ;
27+ import yaml from "js-yaml" ;
28+ import { defaultCronTabYamlTemplate } from "src/templates/crontab-yaml" ;
2629
2730export const CronTabForm : React . FC = ( ) => {
2831 const [ model ] = useK8sModel ( cronTabGroupVersionKind ) ;
@@ -32,6 +35,10 @@ export const CronTabForm: React.FC = () => {
3235 const [ replicas , setReplicas ] = useState < number | "" > ( 0 ) ;
3336 const [ loading , setLoading ] = useState ( false ) ;
3437 const [ error , setError ] = useState ( "" ) ;
38+ const [ showYaml , setShowYaml ] = useState ( false ) ;
39+ const [ yamlContent , setYamlContent ] = useState < string > (
40+ defaultCronTabYamlTemplate . trim ( )
41+ ) ;
3542 const navigate = useNavigate ( ) ;
3643 const { t } = useCronTabTranslation ( ) ;
3744 const [ namespace ] = useActiveNamespace ( ) ;
@@ -76,109 +83,212 @@ export const CronTabForm: React.FC = () => {
7683 setError ( t ( "Error creating CronTab: {{err}}" , { err : err . toString ( ) } ) ) ;
7784 } ) ;
7885 } ;
86+
87+ const handleYamlSubmit = ( ) => {
88+ setLoading ( true ) ;
89+ setError ( "" ) ;
90+ try {
91+ const loaded = yaml . load ( yamlContent ) as Partial < CronTabKind > | undefined ;
92+ if (
93+ ! loaded ||
94+ ! loaded . metadata ||
95+ ! ( loaded . metadata . name || loaded . metadata . generateName )
96+ ) {
97+ throw new Error (
98+ t ( "YAML must include metadata.name or metadata.generateName" )
99+ ) ;
100+ }
101+ if ( ! loaded . spec ) {
102+ throw new Error ( t ( "YAML must include spec" ) ) ;
103+ }
104+
105+ const data : CronTabKind = {
106+ apiVersion : ( loaded . apiVersion as string ) || CRONTAB_APIGROUP_VERSION ,
107+ kind : ( loaded . kind as string ) || CRONTAB_KIND ,
108+ metadata : {
109+ ...loaded . metadata ,
110+ namespace,
111+ } ,
112+ spec : {
113+ cronSpec,
114+ image,
115+ replicas : replicas ? replicas : 0 ,
116+ } ,
117+ } ;
118+
119+ k8sCreate ( { model, data } )
120+ . then ( ( ) => {
121+ setLoading ( false ) ;
122+ navigate (
123+ `/k8s/ns/${ namespace } /${ cronTabGroupVersionKind . group } ~${ cronTabGroupVersionKind . version } ~${ cronTabGroupVersionKind . kind } `
124+ ) ;
125+ } )
126+ . catch ( ( err ) => {
127+ setLoading ( false ) ;
128+ setError (
129+ t ( "Error creating CronTab: {{err}}" , { err : err . toString ( ) } )
130+ ) ;
131+ } ) ;
132+ } catch ( err ) {
133+ setLoading ( false ) ;
134+ setError ( t ( "Invalid YAML: {{err}}" , { err : ( err as Error ) . message } ) ) ;
135+ }
136+ } ;
137+
79138 return (
80139 < PageSection >
81140 < Title headingLevel = "h1" data-test = "page-heading" >
82141 { t ( "Create CronTab" ) }
83142 </ Title >
84- < Form >
85- < FormGroup label = { t ( "Name" ) } fieldId = "crontab-name" isRequired >
86- < TextInput
87- id = "crontab-name"
88- data-test = "crontab-name"
89- name = "name"
90- onChange = { ( _e , value ) => setName ( value ) }
91- value = { name }
92- required
93- />
94- < FormHelperText >
95- < HelperText >
96- < HelperTextItem >
97- { t ( "A unique identifier for this CronTab within the project." ) }
98- </ HelperTextItem >
99- </ HelperText >
100- </ FormHelperText >
101- </ FormGroup >
102- < FormGroup label = { t ( "CronSpec" ) } fieldId = "crontab-cronSpec" isRequired >
103- < TextInput
104- id = "crontab-cronSpec"
105- data-test = "crontab-cronSpec"
106- value = { cronSpec || "" }
107- onChange = { ( _e , value ) => setCronSpec ( value ) }
108- required
109- />
110- < FormHelperText >
111- < HelperText >
112- < HelperTextItem >
113- { t (
114- "Defines the schedule on which the job will run (e.g., */5 * * * *)."
115- ) }
116- </ HelperTextItem >
117- </ HelperText >
118- </ FormHelperText >
119- </ FormGroup >
120- < FormGroup label = { t ( "Image" ) } fieldId = "crontab-image" isRequired >
121- < TextInput
122- id = "crontab-image"
123- data-test = "crontab-image"
124- value = { image || "" }
125- onChange = { ( _e , value ) => setImage ( value ) }
126- required
127- />
128- < FormHelperText >
129- < HelperText >
130- < HelperTextItem >
131- { t (
132- "Specifies the container image to be executed by the CronTab."
133- ) }
134- </ HelperTextItem >
135- </ HelperText >
136- </ FormHelperText >
137- </ FormGroup >
138- < FormGroup label = { t ( "Replicas" ) } fieldId = "crontab-replicas" isRequired >
139- < NumberInput
140- id = "crontab-replicas"
141- data-test = "crontab-replicas"
142- value = { replicas }
143- onChange = { onChangeReplicas }
144- onMinus = { onReplicasMinus }
145- onPlus = { onReplicasPlus }
146- inputName = { t ( "replicas" ) }
147- inputAriaLabel = { t ( "Number of replicas" ) }
148- minusBtnAriaLabel = { t ( "Decrease replicas" ) }
149- plusBtnAriaLabel = { t ( "Increase replicas" ) }
150- required
151- min = { 0 }
143+ < Button variant = "link" onClick = { ( ) => setShowYaml ( ! showYaml ) } >
144+ { showYaml ? t ( "Form View" ) : t ( "YAML Editor" ) }
145+ </ Button >
146+
147+ { showYaml ? (
148+ < >
149+ < YAMLEditor
150+ value = { yamlContent }
151+ language = "yaml"
152+ onChange = { ( _e , val ) => setYamlContent ( val as string ) }
153+ minHeight = "0"
154+ height = "500px"
155+ options = { {
156+ wordWrap : "on" ,
157+ formatOnPaste : true ,
158+ } }
152159 />
153- < FormHelperText >
154- < HelperText >
155- < HelperTextItem >
156- { t ( "The desired number of instances of this CronTab to run." ) }
157- </ HelperTextItem >
158- </ HelperText >
159- </ FormHelperText >
160- </ FormGroup >
161- { error && < Alert variant = "danger" title = { error } /> }
162- < ActionGroup >
163- < Button
164- type = "button"
165- variant = "primary"
166- isDisabled = { loading || ! name || ! cronSpec || ! image }
167- onClick = { handleSubmit }
168- isLoading = { loading }
169- data-test = "save-changes"
160+ { error && < Alert variant = "danger" title = { error } /> }
161+ < ActionGroup >
162+ < Button
163+ type = "button"
164+ style = { { marginTop : "2rem" , marginRight : "0.5rem" } }
165+ variant = "primary"
166+ isDisabled = { loading }
167+ onClick = { handleYamlSubmit }
168+ isLoading = { loading }
169+ >
170+ { t ( "Create" ) }
171+ </ Button >
172+ < Button
173+ variant = "secondary"
174+ style = { { marginTop : "2rem" , marginLeft : "0.5rem" } }
175+ onClick = { ( ) => navigate ( - 1 ) }
176+ isDisabled = { loading }
177+ >
178+ { t ( "Cancel" ) }
179+ </ Button >
180+ </ ActionGroup >
181+ </ >
182+ ) : (
183+ < Form >
184+ < FormGroup label = { t ( "Name" ) } fieldId = "crontab-name" isRequired >
185+ < TextInput
186+ id = "crontab-name"
187+ data-test = "crontab-name"
188+ name = "name"
189+ onChange = { ( _e , value ) => setName ( value ) }
190+ value = { name }
191+ required
192+ />
193+ < FormHelperText >
194+ < HelperText >
195+ < HelperTextItem >
196+ { t (
197+ "A unique identifier for this CronTab within the project."
198+ ) }
199+ </ HelperTextItem >
200+ </ HelperText >
201+ </ FormHelperText >
202+ </ FormGroup >
203+ < FormGroup
204+ label = { t ( "CronSpec" ) }
205+ fieldId = "crontab-cronSpec"
206+ isRequired
170207 >
171- { t ( "Create" ) }
172- </ Button >
173- < Button
174- variant = "secondary"
175- isDisabled = { loading }
176- onClick = { ( ) => navigate ( - 1 ) }
208+ < TextInput
209+ id = "crontab-cronSpec"
210+ data-test = "crontab-cronSpec"
211+ value = { cronSpec || "" }
212+ onChange = { ( _e , value ) => setCronSpec ( value ) }
213+ required
214+ />
215+ < FormHelperText >
216+ < HelperText >
217+ < HelperTextItem >
218+ { t (
219+ "Defines the schedule on which the job will run (e.g., */5 * * * *)."
220+ ) }
221+ </ HelperTextItem >
222+ </ HelperText >
223+ </ FormHelperText >
224+ </ FormGroup >
225+ < FormGroup label = { t ( "Image" ) } fieldId = "crontab-image" isRequired >
226+ < TextInput
227+ id = "crontab-image"
228+ data-test = "crontab-image"
229+ value = { image || "" }
230+ onChange = { ( _e , value ) => setImage ( value ) }
231+ required
232+ />
233+ < FormHelperText >
234+ < HelperText >
235+ < HelperTextItem >
236+ { t (
237+ "Specifies the container image to be executed by the CronTab."
238+ ) }
239+ </ HelperTextItem >
240+ </ HelperText >
241+ </ FormHelperText >
242+ </ FormGroup >
243+ < FormGroup
244+ label = { t ( "Replicas" ) }
245+ fieldId = "crontab-replicas"
246+ isRequired
177247 >
178- { t ( "Cancel" ) }
179- </ Button >
180- </ ActionGroup >
181- </ Form >
248+ < NumberInput
249+ id = "crontab-replicas"
250+ data-test = "crontab-replicas"
251+ value = { replicas }
252+ onChange = { onChangeReplicas }
253+ onMinus = { onReplicasMinus }
254+ onPlus = { onReplicasPlus }
255+ inputName = { t ( "replicas" ) }
256+ inputAriaLabel = { t ( "Number of replicas" ) }
257+ minusBtnAriaLabel = { t ( "Decrease replicas" ) }
258+ plusBtnAriaLabel = { t ( "Increase replicas" ) }
259+ required
260+ min = { 0 }
261+ />
262+ < FormHelperText >
263+ < HelperText >
264+ < HelperTextItem >
265+ { t ( "The desired number of instances of this CronTab to run." ) }
266+ </ HelperTextItem >
267+ </ HelperText >
268+ </ FormHelperText >
269+ </ FormGroup >
270+ { error && < Alert variant = "danger" title = { error } /> }
271+ < ActionGroup >
272+ < Button
273+ type = "button"
274+ variant = "primary"
275+ isDisabled = { loading || ! name || ! cronSpec || ! image }
276+ onClick = { handleSubmit }
277+ isLoading = { loading }
278+ data-test = "save-changes"
279+ >
280+ { t ( "Create" ) }
281+ </ Button >
282+ < Button
283+ variant = "secondary"
284+ isDisabled = { loading }
285+ onClick = { ( ) => navigate ( - 1 ) }
286+ >
287+ { t ( "Cancel" ) }
288+ </ Button >
289+ </ ActionGroup >
290+ </ Form >
291+ ) }
182292 </ PageSection >
183293 ) ;
184294} ;
0 commit comments