@@ -7,7 +7,12 @@ import { Button } from '@/components/ui/button'
77import { Input } from ' @/components/ui/input'
88import { Textarea } from ' @/components/ui/textarea'
99import { Label } from ' @/components/ui/label'
10- import { Card , CardDescription , CardHeader , CardTitle } from ' @/components/ui/card'
10+ import {
11+ Card ,
12+ CardDescription ,
13+ CardHeader ,
14+ CardTitle ,
15+ } from ' @/components/ui/card'
1116
1217// Props and model
1318const modelValue = defineModel <Record <string , string >>({ required: true })
@@ -40,7 +45,6 @@ const environmentVariables = computed(() => {
4045 return props .serverData .environment_variables
4146})
4247
43-
4448const hasRequiredVariables = computed (() => {
4549 return environmentVariables .value .some ((env : any ) => env .required )
4650})
@@ -155,12 +159,8 @@ watch(() => environmentVariables.value, (newVariables) => {
155159 if (modelValue .value [env .name ] !== undefined ) {
156160 newValues [env .name ] = modelValue .value [env .name ]
157161 } else {
158- // Set default/placeholder values
159- if (env .placeholder && env .placeholder !== ` <insert-your-${env .name .toLowerCase ()}-here> ` ) {
160- newValues [env .name ] = env .placeholder
161- } else {
162- newValues [env .name ] = ' '
163- }
162+ // Initialize with empty string - let placeholder show naturally
163+ newValues [env .name ] = ' '
164164 }
165165 })
166166 modelValue .value = newValues
@@ -190,7 +190,7 @@ watch(() => environmentVariables.value, (newVariables) => {
190190 <div v-if =" environmentVariables.length" >
191191 <div class =" mx-auto max-w-7xl sm:px-2 lg:px-8" >
192192 <div class =" mx-auto max-w-2xl space-y-8 sm:px-4 lg:max-w-4xl lg:px-0" >
193- <!-- Server Info -->
193+ <!-- Server Info Card -->
194194 <Card v-if =" serverData" class =" border-t border-b border-gray-200 bg-white shadow-xs sm:rounded-lg sm:border" >
195195 <CardHeader >
196196 <CardTitle class =" flex items-center gap-2" >
@@ -203,131 +203,166 @@ watch(() => environmentVariables.value, (newVariables) => {
203203 </CardHeader >
204204 </Card >
205205
206- <!-- Required Variables -->
207- <div v-if =" hasRequiredVariables" class =" border-t border-b border-gray-200 bg-white shadow-xs sm:rounded-lg sm:border" >
206+ <!-- Required Variables Card -->
207+ <Card v-if =" hasRequiredVariables" class =" border-t border-b border-gray-200 bg-white shadow-xs sm:rounded-lg sm:border" >
208208 <div class =" p-4 sm:p-6" >
209209 <h3 class =" text-lg font-medium text-gray-900 mb-4" >
210210 {{ t('mcpInstallations.wizard.environment.requiredVariables') }}
211211 </h3 >
212212
213213 <div class =" space-y-4" >
214214 <div
215- v-for =" env in requiredVariables"
215+ v-for =" ( env, index) in requiredVariables"
216216 :key =" env.name"
217217 class =" space-y-2"
218218 >
219- <Label :for =" env.name" class =" text-sm font-medium" >
220- {{ env.name }}
221- </Label >
222-
223- <!-- Textarea for long values -->
224- <div v-if =" isTextarea(env)" class =" relative" >
225- <Textarea
226- :id =" env.name"
227- :value =" modelValue[env.name] || ''"
228- @input =" updateValue(env.name, ($event.target as HTMLTextAreaElement).value)"
229- :placeholder =" env.placeholder || `Enter ${env.name}`"
230- class =" min-h-[100px] bg-white"
231- :class =" { 'border-red-500': env.required && isPlaceholderValue(modelValue[env.name] || '', env) }"
232- />
233- </div >
234-
235- <!-- Regular input -->
236- <div v-else class =" relative" >
237- <Input
238- :id =" env.name"
239- :type =" getInputType(env)"
240- :value =" modelValue[env.name] || ''"
241- @input =" updateValue(env.name, ($event.target as HTMLInputElement).value)"
242- :placeholder =" env.placeholder || `Enter ${env.name}`"
243- class =" bg-white"
244- :class =" { 'border-red-500': env.required && isPlaceholderValue(modelValue[env.name] || '', env) }"
245- />
246-
247- <!-- Password toggle -->
248- <Button
249- v-if =" env.type === 'password'"
250- type =" button"
251- variant =" ghost"
252- size =" sm"
253- class =" absolute right-2 top-1/2 transform -translate-y-1/2 h-6 w-6 p-0"
254- @click =" togglePasswordVisibility(env.name)"
255- >
256- <Eye v-if =" !showPasswords[env.name]" class =" h-4 w-4" />
257- <EyeOff v-else class =" h-4 w-4" />
258- </Button >
259- </div >
260-
261- <!-- Validation message -->
262- <p v-if =" env.validation" class =" text-xs text-gray-600" >
263- {{ env.validation }}
264- </p >
265- </div >
219+ <!-- Horizontal line separator -->
220+ <div
221+ v-if =" index > 0 && requiredVariables.length > 1"
222+ class =" border-t border-gray-200 my-8"
223+ ></div >
224+ <Label :for =" env.name" class =" text-sm font-medium" >
225+ {{ env.name }}
226+ <span class =" text-destructive ml-1" >*</span >
227+ </Label >
228+
229+ <!-- Description -->
230+ <p v-if =" env.description" class =" text-sm text-muted-foreground" >
231+ {{ env.description }}
232+ </p >
233+
234+ <!-- Textarea for long values -->
235+ <div v-if =" isTextarea(env)" class =" relative" >
236+ <Textarea
237+ :id =" env.name"
238+ :value =" modelValue[env.name] || ''"
239+ @input =" updateValue(env.name, ($event.target as HTMLTextAreaElement).value)"
240+ :placeholder =" env.placeholder || `Enter ${env.name}`"
241+ class =" min-h-[100px] bg-white"
242+ :class =" { 'border-red-500': env.required && isPlaceholderValue(modelValue[env.name] || '', env) }"
243+ required
244+ />
245+ </div >
246+
247+ <!-- Regular input -->
248+ <div v-else class =" relative" >
249+ <Input
250+ :id =" env.name"
251+ :type =" getInputType(env)"
252+ :value =" modelValue[env.name] || ''"
253+ @input =" updateValue(env.name, ($event.target as HTMLInputElement).value)"
254+ :placeholder =" env.placeholder || `Enter ${env.name}`"
255+ class =" bg-white"
256+ :class =" { 'border-red-500': env.required && isPlaceholderValue(modelValue[env.name] || '', env) }"
257+ required
258+ />
259+
260+ <!-- Password toggle -->
261+ <Button
262+ v-if =" env.type === 'password'"
263+ type =" button"
264+ variant =" ghost"
265+ size =" sm"
266+ class =" absolute right-2 top-1/2 transform -translate-y-1/2 h-6 w-6 p-0"
267+ @click =" togglePasswordVisibility(env.name)"
268+ >
269+ <span class =" sr-only" >
270+ {{ showPasswords[env.name] ? 'Hide password' : 'Show password' }}
271+ </span >
272+ <Eye v-if =" !showPasswords[env.name]" class =" h-4 w-4" />
273+ <EyeOff v-else class =" h-4 w-4" />
274+ </Button >
275+ </div >
276+
277+ <!-- Validation message -->
278+ <div v-if =" env.required && isPlaceholderValue(modelValue[env.name] || '', env)" class =" text-sm text-red-600" >
279+ This field is required
280+ </div >
281+
282+ <!-- Validation message -->
283+ <p v-if =" env.validation" class =" text-xs text-gray-600" >
284+ {{ env.validation }}
285+ </p >
286+ </div >
266287 </div >
267288 </div >
268- </div >
289+ </Card >
269290
270- <!-- Optional Variables -->
271- <div v-if =" hasOptionalVariables" class =" border-t border-b border-gray-200 bg-white shadow-xs sm:rounded-lg sm:border" >
291+ <!-- Optional Variables Card -->
292+ <Card v-if =" hasOptionalVariables" class =" border-t border-b border-gray-200 bg-white shadow-xs sm:rounded-lg sm:border" >
272293 <div class =" p-4 sm:p-6" >
273294 <h3 class =" text-lg font-medium text-gray-900 mb-4" >
274295 {{ t('mcpInstallations.wizard.environment.optionalVariables') }}
275296 </h3 >
276297
277298 <div class =" space-y-4" >
278299 <div
279- v-for =" env in optionalVariables"
300+ v-for =" ( env, index) in optionalVariables"
280301 :key =" env.name"
281302 class =" space-y-2"
282303 >
283- <Label :for =" env.name" class =" text-sm font-medium" >
284- {{ env.name }}
285- </Label >
286-
287- <!-- Textarea for long values -->
288- <div v-if =" isTextarea(env)" class =" relative" >
289- <Textarea
290- :id =" env.name"
291- :value =" modelValue[env.name] || ''"
292- @input =" updateValue(env.name, ($event.target as HTMLTextAreaElement).value)"
293- :placeholder =" env.placeholder || `Enter ${env.name} (optional)`"
294- class =" min-h-[100px] bg-white"
295- />
296- </div >
297-
298- <!-- Regular input -->
299- <div v-else class =" relative" >
300- <Input
301- :id =" env.name"
302- :type =" getInputType(env)"
303- :value =" modelValue[env.name] || ''"
304- @input =" updateValue(env.name, ($event.target as HTMLInputElement).value)"
305- :placeholder =" env.placeholder || `Enter ${env.name} (optional)`"
306- class =" bg-white"
307- />
308-
309- <!-- Password toggle -->
310- <Button
311- v-if =" env.type === 'password'"
312- type =" button"
313- variant =" ghost"
314- size =" sm"
315- class =" absolute right-2 top-1/2 transform -translate-y-1/2 h-6 w-6 p-0"
316- @click =" togglePasswordVisibility(env.name)"
317- >
318- <Eye v-if =" !showPasswords[env.name]" class =" h-4 w-4" />
319- <EyeOff v-else class =" h-4 w-4" />
320- </Button >
321- </div >
322-
323- <!-- Validation message -->
324- <p v-if =" env.validation" class =" text-xs text-gray-600" >
325- {{ env.validation }}
326- </p >
327- </div >
304+ <!-- Horizontal line separator -->
305+ <div
306+ v-if =" index > 0 && optionalVariables.length > 1"
307+ class =" border-t border-gray-200 my-8"
308+ ></div >
309+ <Label :for =" env.name" class =" text-sm font-medium" >
310+ {{ env.name }}
311+ <span class =" text-muted-foreground text-xs ml-1" >(optional)</span >
312+ </Label >
313+
314+ <!-- Description -->
315+ <p v-if =" env.description" class =" text-sm text-muted-foreground" >
316+ {{ env.description }}
317+ </p >
318+
319+ <!-- Textarea for long values -->
320+ <div v-if =" isTextarea(env)" class =" relative" >
321+ <Textarea
322+ :id =" env.name"
323+ :value =" modelValue[env.name] || ''"
324+ @input =" updateValue(env.name, ($event.target as HTMLTextAreaElement).value)"
325+ :placeholder =" env.placeholder || `Enter ${env.name} (optional)`"
326+ class =" min-h-[100px]"
327+ />
328+ </div >
329+
330+ <!-- Regular input -->
331+ <div v-else class =" relative" >
332+ <Input
333+ :id =" env.name"
334+ :type =" getInputType(env)"
335+ :value =" modelValue[env.name] || ''"
336+ @input =" updateValue(env.name, ($event.target as HTMLInputElement).value)"
337+ :placeholder =" env.placeholder || `Enter ${env.name} (optional)`"
338+ class =" bg-white"
339+ />
340+
341+ <!-- Password toggle -->
342+ <Button
343+ v-if =" env.type === 'password'"
344+ type =" button"
345+ variant =" ghost"
346+ size =" sm"
347+ class =" absolute right-2 top-1/2 transform -translate-y-1/2 h-6 w-6 p-0"
348+ @click =" togglePasswordVisibility(env.name)"
349+ >
350+ <span class =" sr-only" >
351+ {{ showPasswords[env.name] ? 'Hide password' : 'Show password' }}
352+ </span >
353+ <Eye v-if =" !showPasswords[env.name]" class =" h-4 w-4" />
354+ <EyeOff v-else class =" h-4 w-4" />
355+ </Button >
356+ </div >
357+
358+ <!-- Validation message -->
359+ <p v-if =" env.validation" class =" text-xs text-gray-600" >
360+ {{ env.validation }}
361+ </p >
362+ </div >
328363 </div >
329364 </div >
330- </div >
365+ </Card >
331366 </div >
332367 </div >
333368 </div >
0 commit comments