Skip to content

Commit af20218

Browse files
author
Lasim
committed
refactor: simplify platform selection component and enhance UI for better user experience
1 parent 8a7a908 commit af20218

File tree

1 file changed

+79
-104
lines changed

1 file changed

+79
-104
lines changed
Lines changed: 79 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
<script setup lang="ts">
2-
import { } from 'vue'
2+
import { onMounted } from 'vue'
33
import { useI18n } from 'vue-i18n'
4-
import { Cloud, Monitor, Check } from 'lucide-vue-next'
5-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
4+
import { Cloud, Monitor } from 'lucide-vue-next'
65
import { Badge } from '@/components/ui/badge'
7-
import { cn } from '@/lib/utils'
86
97
// Props and model
108
const modelValue = defineModel<string>({ required: true })
@@ -19,54 +17,32 @@ const platforms = [
1917
description: t('mcpInstallations.wizard.platform.local.description'),
2018
icon: Monitor,
2119
available: true,
22-
recommended: false,
23-
features: [
20+
recommended: true,
21+
details: [
2422
t('mcpInstallations.wizard.platform.local.features.direct'),
25-
t('mcpInstallations.wizard.platform.local.features.fast'),
26-
t('mcpInstallations.wizard.platform.local.features.secure')
23+
t('mcpInstallations.wizard.platform.local.features.fast')
2724
]
2825
},
29-
// {
30-
// id: 'docker',
31-
// name: t('mcpInstallations.wizard.platform.docker.name'),
32-
// description: t('mcpInstallations.wizard.platform.docker.description'),
33-
// icon: Server,
34-
// available: false,
35-
// recommended: false,
36-
// features: [
37-
// t('mcpInstallations.wizard.platform.docker.features.isolated'),
38-
// t('mcpInstallations.wizard.platform.docker.features.portable'),
39-
// t('mcpInstallations.wizard.platform.docker.features.scalable')
40-
// ]
41-
// },
4226
{
4327
id: 'cloud',
4428
name: t('mcpInstallations.wizard.platform.cloud.name'),
4529
description: t('mcpInstallations.wizard.platform.cloud.description'),
4630
icon: Cloud,
4731
available: false,
4832
recommended: false,
49-
features: [
33+
details: [
5034
t('mcpInstallations.wizard.platform.cloud.features.managed'),
51-
t('mcpInstallations.wizard.platform.cloud.features.scalable'),
52-
t('mcpInstallations.wizard.platform.cloud.features.redundant')
35+
t('mcpInstallations.wizard.platform.cloud.features.scalable')
5336
]
5437
}
5538
]
5639
57-
58-
// Methods
59-
const selectPlatform = (platformId: string) => {
60-
const platform = platforms.find(p => p.id === platformId)
61-
if (platform?.available) {
62-
modelValue.value = platformId
40+
// Set default to local when component mounts
41+
onMounted(() => {
42+
if (!modelValue.value) {
43+
modelValue.value = 'local'
6344
}
64-
}
65-
66-
// Set default to local if not set
67-
if (!modelValue.value) {
68-
modelValue.value = 'local'
69-
}
45+
})
7046
</script>
7147

7248
<template>
@@ -81,82 +57,81 @@ if (!modelValue.value) {
8157
</p>
8258
</div>
8359

84-
<!-- Platform Options -->
85-
<div class="grid gap-4">
86-
<Card
87-
v-for="platform in platforms"
88-
:key="platform.id"
89-
:class="cn(
90-
'cursor-pointer transition-all duration-200 hover:shadow-md',
91-
{
92-
'ring-2 ring-blue-500 bg-blue-50': modelValue === platform.id && platform.available,
93-
'opacity-50 cursor-not-allowed': !platform.available,
94-
'hover:border-blue-300': platform.available && modelValue !== platform.id
95-
}
96-
)"
97-
@click="selectPlatform(platform.id)"
98-
>
99-
<CardHeader>
100-
<div class="flex items-start justify-between">
101-
<div class="flex items-center gap-3">
102-
<div
103-
:class="cn(
104-
'p-2 rounded-lg',
105-
{
106-
'bg-blue-100 text-blue-600': modelValue === platform.id && platform.available,
107-
'bg-gray-100 text-gray-400': !platform.available,
108-
'bg-gray-100 text-gray-600': platform.available && modelValue !== platform.id
109-
}
110-
)"
111-
>
112-
<component :is="platform.icon" class="h-6 w-6" />
60+
<!-- Platform Selection -->
61+
<fieldset :aria-label="t('mcpInstallations.wizard.platform.title')">
62+
<div class="space-y-4">
63+
<div
64+
v-for="platform in platforms"
65+
:key="platform.id"
66+
:class="[
67+
'group relative block rounded-lg border-1 px-6 py-4 cursor-pointer transition-all duration-200',
68+
'focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2',
69+
'sm:flex sm:justify-between',
70+
// Use the same gray background pattern as ProgressBars
71+
platform.available
72+
? modelValue === platform.id
73+
? '!border-primary bg-muted/50'
74+
: 'border-gray-300 bg-white hover:border-gray-400'
75+
: '!border-gray-50 !bg-gray-50 opacity-60 cursor-not-allowed'
76+
]"
77+
@click="() => platform.available && (modelValue = platform.id)"
78+
>
79+
<input
80+
type="radio"
81+
name="platform"
82+
:value="platform.id"
83+
v-model="modelValue"
84+
:disabled="!platform.available"
85+
class="sr-only"
86+
/>
87+
88+
<span class="flex items-center relative z-10">
89+
<span class="flex items-center gap-3 mr-4">
90+
<!-- Platform Icon -->
91+
<div :class="[
92+
'p-2 rounded-lg transition-colors',
93+
modelValue === platform.id && platform.available
94+
? 'bg-primary/10 text-primary'
95+
: platform.available
96+
? 'bg-gray-100 text-gray-600'
97+
: 'bg-gray-100 text-gray-400'
98+
]">
99+
<component :is="platform.icon" class="h-5 w-5" />
113100
</div>
114-
<div>
115-
<CardTitle class="flex items-center gap-2">
116-
{{ platform.name }}
101+
102+
<!-- Platform Info -->
103+
<span class="flex flex-col text-sm">
104+
<span class="flex items-center gap-2">
105+
<span class="font-medium text-base text-gray-900">
106+
{{ platform.name }}
107+
</span>
117108
<Badge v-if="platform.recommended && platform.available" variant="default" class="text-xs">
118109
{{ t('labels.recommended') }}
119110
</Badge>
120111
<Badge v-if="!platform.available" variant="secondary" class="text-xs">
121112
{{ t('labels.comingSoon') }}
122113
</Badge>
123-
</CardTitle>
124-
<CardDescription class="mt-1">
125-
{{ platform.description }}
126-
</CardDescription>
127-
</div>
128-
</div>
129-
130-
<!-- Selection indicator -->
131-
<div
132-
v-if="modelValue === platform.id && platform.available"
133-
class="flex items-center justify-center w-6 h-6 bg-blue-500 rounded-full"
134-
>
135-
<Check class="h-4 w-4 text-white" />
136-
</div>
137-
</div>
138-
</CardHeader>
114+
</span>
115+
<span class="text-sm mt-1 text-gray-600">
116+
<span class="block sm:inline">{{ platform.details[0] }}</span>
117+
<span v-if="platform.details[1]" class="hidden sm:mx-1 sm:inline" aria-hidden="true">&middot;</span>
118+
<span v-if="platform.details[1]" class="block sm:inline">{{ platform.details[1] }}</span>
119+
</span>
120+
</span>
121+
</span>
122+
</span>
139123

140-
<CardContent>
141-
<!-- Features -->
142-
<div class="space-y-2">
143-
<h4 class="text-sm font-medium text-gray-700">
144-
{{ t('mcpInstallations.wizard.platform.features') }}:
145-
</h4>
146-
<ul class="space-y-1">
147-
<li
148-
v-for="feature in platform.features"
149-
:key="feature"
150-
class="flex items-center gap-2 text-sm text-gray-600"
151-
>
152-
<div class="w-1.5 h-1.5 bg-gray-400 rounded-full"></div>
153-
{{ feature }}
154-
</li>
155-
</ul>
124+
<!-- Selection Indicator -->
125+
<div
126+
v-if="modelValue === platform.id && platform.available"
127+
class="absolute top-4 right-4 w-6 h-6 bg-primary rounded-full flex items-center justify-center"
128+
>
129+
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
130+
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
131+
</svg>
156132
</div>
157-
</CardContent>
158-
</Card>
159-
</div>
160-
133+
</div>
134+
</div>
135+
</fieldset>
161136
</div>
162137
</template>

0 commit comments

Comments
 (0)