@@ -13,9 +13,15 @@ type ScriptVariant = {
1313 code : string ;
1414} ;
1515
16+ type CodeBlock = {
17+ label : string ;
18+ code : string ;
19+ } ;
20+
1621type Installation = {
1722 label : string ;
1823 code : string | ScriptVariant [ ] ;
24+ codeBlocks ?: CodeBlock [ ] ;
1925 language : string ;
2026 description : string ;
2127} ;
@@ -70,8 +76,19 @@ curl -sSL https://raw.githubusercontent.com/RostislavDugin/postgresus/refs/heads
7076 label : "Helm (Kubernetes)" ,
7177 language : "bash" ,
7278 description :
73- "For Kubernetes deployments, use the official Helm chart. This will create a StatefulSet with persistent storage and optional ingress." ,
74- code : `helm install postgresus ./deploy/postgresus -n postgresus --create-namespace` ,
79+ "For Kubernetes deployments, clone the repository and use the official Helm chart. This will create a StatefulSet with persistent storage and optional ingress." ,
80+ code : "" ,
81+ codeBlocks : [
82+ {
83+ label : "Clone the repository" ,
84+ code : `git clone https://github.com/RostislavDugin/postgresus.git
85+ cd postgresus` ,
86+ } ,
87+ {
88+ label : "Install the Helm chart" ,
89+ code : `helm install postgresus ./deploy/postgresus -n postgresus --create-namespace` ,
90+ } ,
91+ ] ,
7592 } ,
7693} ;
7794
@@ -87,14 +104,20 @@ export default function InstallationComponent() {
87104 useState < InstallMethod > ( "Automated Script" ) ;
88105 const [ selectedVariant , setSelectedVariant ] = useState ( 0 ) ;
89106 const [ copied , setCopied ] = useState ( false ) ;
107+ const [ copiedBlockIndex , setCopiedBlockIndex ] = useState < number | null > ( null ) ;
90108
91109 const currentInstallation = installationMethods [ selectedMethod ] ;
92- const hasVariants = Array . isArray ( currentInstallation . code ) ;
110+ const hasVariants =
111+ Array . isArray ( currentInstallation . code ) &&
112+ currentInstallation . code . length > 0 ;
113+ const hasCodeBlocks =
114+ currentInstallation . codeBlocks && currentInstallation . codeBlocks . length > 0 ;
93115
94116 const handleMethodChange = ( method : InstallMethod ) => {
95117 setSelectedMethod ( method ) ;
96118 setSelectedVariant ( 0 ) ;
97119 setCopied ( false ) ;
120+ setCopiedBlockIndex ( null ) ;
98121 } ;
99122
100123 const handleVariantChange = ( index : number ) => {
@@ -120,6 +143,16 @@ export default function InstallationComponent() {
120143 }
121144 } ;
122145
146+ const handleCopyBlock = async ( code : string , index : number ) => {
147+ try {
148+ await navigator . clipboard . writeText ( code ) ;
149+ setCopiedBlockIndex ( index ) ;
150+ setTimeout ( ( ) => setCopiedBlockIndex ( null ) , 2000 ) ;
151+ } catch ( err ) {
152+ console . error ( "Failed to copy:" , err ) ;
153+ }
154+ } ;
155+
123156 return (
124157 < div className = "mx-auto w-full" >
125158 { /* Installation methods tabs */ }
@@ -165,23 +198,53 @@ export default function InstallationComponent() {
165198 { currentInstallation . description }
166199 </ div >
167200
168- { /* Code block with copy button */ }
169- < div className = "relative max-w-[600px]" >
170- < pre className = "rounded-lg bg-gray-100 p-4 pr-16 text-sm" >
171- < code className = "block whitespace-pre-wrap wrap-break-word" >
172- { getCurrentCode ( ) }
173- </ code >
174- </ pre >
175-
176- < button
177- onClick = { handleCopy }
178- className = { `absolute right-2 top-2 rounded px-2 py-1 text-xs text-white transition-colors ${
179- copied ? "bg-green-500" : "bg-blue-600 hover:bg-blue-700"
180- } `}
181- >
182- { copied ? "Copied!" : "Copy" }
183- </ button >
184- </ div >
201+ { /* Multiple code blocks (for Helm) */ }
202+ { hasCodeBlocks ? (
203+ < div className = "space-y-4 max-w-[600px]" >
204+ { currentInstallation . codeBlocks ! . map ( ( block , index ) => (
205+ < div key = { index } >
206+ < p className = "mb-2 text-sm font-medium text-gray-700" >
207+ { block . label }
208+ </ p >
209+ < div className = "relative" >
210+ < pre className = "rounded-lg bg-gray-100 p-4 pr-16 text-sm" >
211+ < code className = "block whitespace-pre-wrap wrap-break-word" >
212+ { block . code }
213+ </ code >
214+ </ pre >
215+ < button
216+ onClick = { ( ) => handleCopyBlock ( block . code , index ) }
217+ className = { `absolute right-2 top-2 rounded px-2 py-1 text-xs text-white transition-colors ${
218+ copiedBlockIndex === index
219+ ? "bg-green-500"
220+ : "bg-blue-600 hover:bg-blue-700"
221+ } `}
222+ >
223+ { copiedBlockIndex === index ? "Copied!" : "Copy" }
224+ </ button >
225+ </ div >
226+ </ div >
227+ ) ) }
228+ </ div >
229+ ) : (
230+ /* Single code block with copy button */
231+ < div className = "relative max-w-[600px]" >
232+ < pre className = "rounded-lg bg-gray-100 p-4 pr-16 text-sm" >
233+ < code className = "block whitespace-pre-wrap wrap-break-word" >
234+ { getCurrentCode ( ) }
235+ </ code >
236+ </ pre >
237+
238+ < button
239+ onClick = { handleCopy }
240+ className = { `absolute right-2 top-2 rounded px-2 py-1 text-xs text-white transition-colors ${
241+ copied ? "bg-green-500" : "bg-blue-600 hover:bg-blue-700"
242+ } `}
243+ >
244+ { copied ? "Copied!" : "Copy" }
245+ </ button >
246+ </ div >
247+ ) }
185248 </ div >
186249 ) ;
187250}
0 commit comments