1- import { FC , useState } from 'react' ;
1+ import { FC , FormEvent , useState } from 'react' ;
22import { Plus , Trash2 } from 'lucide-react' ;
33import { cn } from '@/lib/utils' ;
4+ import { usePost } from '@/core/react-query' ;
5+ import { API } from '@/enums/api.enums' ;
6+ import {
7+ HelmTemplateBody ,
8+ HelmTemplateResponse ,
9+ helmTemplateValidationError ,
10+ } from './helm-template.types' ;
11+ import { toast } from 'sonner' ;
12+ import { isAxiosError } from 'axios' ;
13+ import Select , { SingleValue } from 'react-select' ;
14+ import { accessModesOptions , sizeOptions } from './data/select-options' ;
15+ import { selectStyle } from './styles/helm-template.style' ;
16+ import { OptionType } from '@/types/select.types' ;
17+ import { useDownload } from '@/hooks' ;
418
519const HelmTemplate : FC = ( ) => {
20+ const { mutateAsync : helmTemplateMutate , isPending : helmTemplatePending } =
21+ usePost < HelmTemplateResponse , HelmTemplateBody > (
22+ API . HelmTemplate ,
23+ 'helm-template' ,
24+ ) ;
25+ const { download, isPending : downloadPending } = useDownload ( {
26+ downloadFileName : 'helm-template' ,
27+ source : 'helm' ,
28+ folderName : 'MyHelm' ,
29+ } ) ;
30+
31+ const [ version , setVersion ] = useState ( '' ) ;
32+ const [ name , setName ] = useState ( '' ) ;
33+ const [ image , setImage ] = useState ( '' ) ;
34+ const [ targetPort , setTargetPort ] = useState ( '' ) ;
35+ const [ replicas , setReplicas ] = useState ( '' ) ;
36+ const [ sizeValue , setSizeValue ] = useState ( '' ) ;
37+ const [ sizeType , setSizeType ] = useState < SingleValue < OptionType > > ( ) ;
38+ const [ accessModes , setAccessModes ] = useState < SingleValue < OptionType > > ( ) ;
639 const [ environments , setEnvironments ] = useState ( [
740 {
841 name : '' ,
@@ -13,6 +46,7 @@ const HelmTemplate: FC = () => {
1346 ] ) ;
1447 const [ stateless , setStateless ] = useState ( false ) ;
1548 const [ ingress , setIngress ] = useState ( false ) ;
49+ const [ ingressHost , setIngressHost ] = useState ( '' ) ;
1650
1751 const handleAddEnvironment = ( ) => {
1852 setEnvironments ( ( prev ) => [
@@ -30,101 +64,184 @@ const HelmTemplate: FC = () => {
3064 setEnvironments ( ( prev ) => prev . filter ( ( _ , i ) => i !== index ) ) ;
3165 } ;
3266
67+ const handleEnvironmentChange = (
68+ index : number ,
69+ field : string ,
70+ value : string ,
71+ ) => {
72+ setEnvironments ( ( prev ) =>
73+ prev . map ( ( env , i ) => ( i === index ? { ...env , [ field ] : value } : env ) ) ,
74+ ) ;
75+ } ;
76+
77+ const handleForm = async ( e : FormEvent ) => {
78+ e . preventDefault ( ) ;
79+
80+ try {
81+ const body : HelmTemplateBody = {
82+ api_version : parseInt ( version ) ,
83+ pods : [
84+ {
85+ name,
86+ image,
87+ target_port : parseInt ( targetPort ) ,
88+ replicas : parseInt ( replicas ) ,
89+ persistance : {
90+ size : `${ sizeValue } ${ sizeType ?. value as string } ` ,
91+ accessModes : accessModes ?. value as string ,
92+ } ,
93+ environment : environments . map ( ( env ) => ( {
94+ name : env . name ,
95+ value : env . value ,
96+ } ) ) ,
97+ stateless,
98+ ingress : {
99+ enabled : ingress ,
100+ host : ingressHost ,
101+ } ,
102+ } ,
103+ ] ,
104+ } ;
105+
106+ await helmTemplateMutate ( body ) ;
107+ await download ( ) ;
108+ } catch ( error ) {
109+ console . log ( error ) ;
110+ if ( isAxiosError < helmTemplateValidationError > ( error ) ) {
111+ toast . error (
112+ `${ error . response ?. data . detail [ 0 ] . loc [ error . response ?. data . detail [ 0 ] . loc . length - 1 ] } ${ error . response ?. data . detail [ 0 ] . msg } ` ,
113+ ) ;
114+ } else {
115+ toast . error ( 'Something went wrong' ) ;
116+ }
117+ }
118+ } ;
119+
33120 return (
34- < div className = "scrollbar-thin flex h-[calc(100%-56px)] w-full items-center justify-center overflow-y-auto" >
35- < form className = "flex h-full w-full max-w-[768px] flex-col justify-center " >
36- < div className = "flex flex-col w-full mb-4 " >
121+ < div className = "flex h-[calc(100%-56px)] w-full justify-center overflow-y-auto scrollbar-thin " >
122+ < form onSubmit = { handleForm } className = "h-full w-full max-w-[768px]" >
123+ < div className = "mb-4 flex w-full flex-col " >
37124 < label htmlFor = "api_version" className = "mb-1" >
38125 Api Version
39126 </ label >
40127 < input
41128 id = "api_version"
42129 placeholder = "2"
43- className = "w-full px-3 py-2 rounded-md outline-none"
130+ value = { version }
131+ onChange = { ( e ) => setVersion ( e . target . value ) }
132+ className = "w-full rounded-md px-3 py-2 outline-none"
44133 />
45134 </ div >
46135 < h1 className = "mb-4 text-2xl font-bold" > Pods</ h1 >
47- < div className = "flex flex-col mb-4 " >
136+ < div className = "mb-4 flex flex-col" >
48137 < label htmlFor = "pods_name" className = "mb-1" >
49138 Name
50139 </ label >
51140 < input
52141 id = "pods_name"
53- placeholder = "2"
54- className = "w-full px-3 py-2 rounded-md outline-none"
142+ placeholder = "web"
143+ value = { name }
144+ onChange = { ( e ) => setName ( e . target . value ) }
145+ className = "w-full rounded-md px-3 py-2 outline-none"
55146 />
56147 </ div >
57- < div className = "flex flex-col mb-4 " >
148+ < div className = "mb-4 flex flex-col" >
58149 < label htmlFor = "pods_image" className = "mb-1" >
59150 Image
60151 </ label >
61152 < input
62153 id = "pods_image"
63154 placeholder = "nginx"
64- className = "w-full px-3 py-2 rounded-md outline-none"
155+ value = { image }
156+ onChange = { ( e ) => setImage ( e . target . value ) }
157+ className = "w-full rounded-md px-3 py-2 outline-none"
65158 />
66159 </ div >
67- < div className = "flex flex-col mb-4 " >
160+ < div className = "mb-4 flex flex-col" >
68161 < label htmlFor = "pods_target_port" className = "mb-1" >
69162 Target Port
70163 </ label >
71164 < input
72165 id = "pods_target_port"
73- placeholder = "nginx"
74- className = "w-full px-3 py-2 rounded-md outline-none"
166+ placeholder = "80"
167+ value = { targetPort }
168+ onChange = { ( e ) => setTargetPort ( e . target . value ) }
169+ className = "w-full rounded-md px-3 py-2 outline-none"
75170 />
76171 </ div >
77- < div className = "flex flex-col mb-2 " >
172+ < div className = "mb-2 flex flex-col" >
78173 < label htmlFor = "pods_replicas" className = "mb-1" >
79174 Replicas
80175 </ label >
81176 < input
82177 id = "pods_replicas"
83178 placeholder = "1"
84- className = "w-full px-3 py-2 rounded-md outline-none"
179+ value = { replicas }
180+ onChange = { ( e ) => setReplicas ( e . target . value ) }
181+ className = "w-full rounded-md px-3 py-2 outline-none"
85182 />
86183 </ div >
87184 < h2 className = "mb-2 text-lg font-bold" > Persistence</ h2 >
88- < div className = "flex flex-col mb-7" >
89- < label htmlFor = "pods_persistence_size" className = "mb-1" >
90- Size
91- </ label >
92- < input
93- id = "pods_persistence_size"
94- placeholder = "iG1"
95- className = "w-full px-3 py-2 rounded-md outline-none"
96- />
185+ < div className = "mb-7 flex flex-col" >
186+ < p className = "mb-1" > Size</ p >
187+ < div className = "flex items-center gap-3" >
188+ < input
189+ placeholder = "Value"
190+ type = "number"
191+ value = { sizeValue }
192+ onChange = { ( e ) => setSizeValue ( e . target . value ) }
193+ className = "w-full gap-2 rounded-md px-3 py-2 outline-none"
194+ />
195+ < Select
196+ placeholder = "Select..."
197+ options = { sizeOptions }
198+ value = { sizeType }
199+ onChange = { ( e ) => setSizeType ( e ) }
200+ className = "h-10 w-full"
201+ styles = { selectStyle }
202+ />
203+ </ div >
97204 </ div >
98- < div className = "flex flex-col mb-2 " >
99- < label htmlFor = "pods_accessModes" className = "mb-1" >
100- Access Modes
101- </ label >
102- < input
103- id = "pods_accessModes"
104- placeholder = "ReadWriteOnce"
105- className = "w-full px-3 py-2 rounded-md outline-none"
205+ < div className = "mb-2 flex flex-col" >
206+ < label className = "mb-1" > Access Modes </ label >
207+ < Select
208+ placeholder = "Select..."
209+ options = { accessModesOptions }
210+ value = { accessModes }
211+ onChange = { ( e ) => setAccessModes ( e ) }
212+ styles = { selectStyle }
106213 />
107214 </ div >
108- < div className = "flex items-center mt-5 mb-2 " >
215+ < div className = "mb-2 mt-5 flex items-center " >
109216 < h3 className = "text-lg font-bold" > Environments</ h3 >
110- < button className = "ml-4 btn btn-xs" onClick = { handleAddEnvironment } >
217+ < button
218+ type = "button"
219+ className = "btn btn-xs ml-4"
220+ onClick = { handleAddEnvironment }
221+ >
111222 Add < Plus className = "size-3" />
112223 </ button >
113224 </ div >
114225 < div className = "grid grid-cols-2 gap-4" >
115226 { environments . map ( ( env , index ) => (
116227 < div
117- className = "flex items-center border border-gray-500 divide-x divide-gray-500 rounded-md"
228+ className = "flex items-center divide-x divide-gray-500 rounded-md border border-gray-500 "
118229 key = { index }
119230 >
120231 < input
121- value = { env . name }
122232 placeholder = { env . namePlaceholder }
123- className = "w-full h-12 px-2 outline-none rounded-s-md"
233+ value = { env . name }
234+ onChange = { ( e ) =>
235+ handleEnvironmentChange ( index , 'name' , e . target . value )
236+ }
237+ className = "h-12 w-full rounded-s-md px-2 outline-none"
124238 />
125239 < input
126- value = { env . value }
127240 placeholder = { env . valuePlaceholder }
241+ value = { env . value }
242+ onChange = { ( e ) =>
243+ handleEnvironmentChange ( index , 'value' , e . target . value )
244+ }
128245 className = { cn ( 'h-12 w-full px-2 outline-none' , {
129246 'rounded-e-md' : index === 0 ,
130247 } ) }
@@ -140,7 +257,7 @@ const HelmTemplate: FC = () => {
140257 </ div >
141258 ) ) }
142259 </ div >
143- < div className = "flex justify-between mb-2 mt-7" >
260+ < div className = "mb-2 mt-7 flex justify-between " >
144261 < label htmlFor = "pods_stateless" className = "mb-1" >
145262 Stateless
146263 </ label >
@@ -154,7 +271,7 @@ const HelmTemplate: FC = () => {
154271 />
155272 </ div >
156273 < h4 className = "mt-5 text-lg font-bold" > Ingress</ h4 >
157- < div className = "flex justify-between mt-3 mb-2 " >
274+ < div className = "mb-2 mt-3 flex justify-between " >
158275 < label htmlFor = "pods_ingress_enabled" className = "mb-1" >
159276 Enabled
160277 </ label >
@@ -167,18 +284,28 @@ const HelmTemplate: FC = () => {
167284 onChange = { ( ) => setIngress ( ! ingress ) }
168285 />
169286 </ div >
170- < div className = "flex flex-col mt-3 mb-2 " >
287+ < div className = "mb-2 mt-3 flex flex-col " >
171288 < label htmlFor = "pods_ingress_host" className = "mb-1" >
172289 Host
173290 </ label >
174291 < input
175292 id = "pods_ingress_host"
176293 placeholder = "www.example.com"
177- className = "w-full px-3 py-2 rounded-md outline-none"
294+ value = { ingressHost }
295+ onChange = { ( e ) => setIngressHost ( e . target . value ) }
296+ className = "w-full rounded-md px-3 py-2 outline-none"
178297 />
179298 </ div >
180- < button className = "w-full mt-3 text-white btn bg-orange-base hover:bg-orange-base/70" >
181- Submit
299+ < button
300+ type = "submit"
301+ disabled = { helmTemplatePending }
302+ className = "btn mt-3 w-full bg-orange-base text-white hover:bg-orange-base/70 disabled:bg-orange-base/50 disabled:text-white/70"
303+ >
304+ { helmTemplatePending
305+ ? 'Generating...'
306+ : downloadPending
307+ ? 'Downloading...'
308+ : 'Generate' }
182309 </ button >
183310 </ form >
184311 </ div >
0 commit comments