Skip to content

Commit 61d8d34

Browse files
author
Lasim
committed
refactor: improve structure and styling of environment variable cards in EnvironmentVariableCard component
1 parent e4a68f0 commit 61d8d34

File tree

1 file changed

+144
-109
lines changed

1 file changed

+144
-109
lines changed

services/frontend/src/components/mcp-server/EnvironmentVariableCard.vue

Lines changed: 144 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { Button } from '@/components/ui/button'
77
import { Input } from '@/components/ui/input'
88
import { Textarea } from '@/components/ui/textarea'
99
import { 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
1318
const modelValue = defineModel<Record<string, string>>({ required: true })
@@ -40,7 +45,6 @@ const environmentVariables = computed(() => {
4045
return props.serverData.environment_variables
4146
})
4247
43-
4448
const 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

Comments
 (0)