|
2 | 2 | import { ref, onMounted, onUnmounted, computed } from 'vue' |
3 | 3 | import { useI18n } from 'vue-i18n' |
4 | 4 | import { Button } from '@/components/ui/button' |
5 | | -import { |
6 | | - Breadcrumb, |
7 | | - BreadcrumbItem, |
8 | | - BreadcrumbLink, |
9 | | - BreadcrumbList, |
10 | | - BreadcrumbPage, |
11 | | - BreadcrumbSeparator, |
12 | | -} from '@/components/ui/breadcrumb' |
13 | 5 | import { Alert, AlertDescription } from '@/components/ui/alert' |
| 6 | +import { ProgressBars } from '@/components/ui/progress-bars' |
14 | 7 | import { FileText, Github, Settings, Loader2 } from 'lucide-vue-next' |
15 | 8 | import { McpCatalogService } from '@/services/mcpCatalogService' |
16 | 9 | import { useEventBus } from '@/composables/useEventBus' |
@@ -89,6 +82,67 @@ const steps = [ |
89 | 82 | } |
90 | 83 | ] |
91 | 84 |
|
| 85 | +// Progress steps for ProgressBars component |
| 86 | +const progressSteps = computed(() => { |
| 87 | + return steps.map((step, index) => { |
| 88 | + let status: 'completed' | 'current' | 'pending' | 'error' = 'pending' |
| 89 | + |
| 90 | + if (index < currentStep.value) { |
| 91 | + status = 'completed' |
| 92 | + } else if (index === currentStep.value) { |
| 93 | + status = 'current' |
| 94 | + } |
| 95 | + |
| 96 | + // Check for errors |
| 97 | + if (index === 0 && githubFetchError.value) { |
| 98 | + status = 'error' |
| 99 | + } |
| 100 | + |
| 101 | + return { |
| 102 | + id: step.key, |
| 103 | + label: step.label, |
| 104 | + status, |
| 105 | + clickable: index < currentStep.value // Only completed steps are clickable |
| 106 | + } |
| 107 | + }) |
| 108 | +}) |
| 109 | +
|
| 110 | +// Calculate progress percentage |
| 111 | +const progressPercentage = computed(() => { |
| 112 | + // Progress should match visual step positions: |
| 113 | + // Step 1 (index 0): 0% progress |
| 114 | + // Step 2 (index 1): 50% progress (middle) |
| 115 | + // Step 3 (index 2): 100% progress (right) |
| 116 | + if (currentStep.value === 0) return 0 |
| 117 | + if (currentStep.value === 1) return 50 |
| 118 | + if (currentStep.value === 2) return 100 |
| 119 | + return 0 |
| 120 | +}) |
| 121 | +
|
| 122 | +// Progress title based on current step |
| 123 | +const progressTitle = computed(() => { |
| 124 | + if (isSubmitting.value) { |
| 125 | + return t('mcpCatalog.form.navigation.creating') |
| 126 | + } |
| 127 | + if (isFetchingGitHub.value) { |
| 128 | + return t('mcpCatalog.form.navigation.fetching') |
| 129 | + } |
| 130 | + if (githubFetchError.value) { |
| 131 | + return t('mcpCatalog.form.errors.githubFetch') |
| 132 | + } |
| 133 | + |
| 134 | + const currentStepData = steps[currentStep.value] |
| 135 | + return `${currentStepData.label} - ${t('mcpCatalog.form.steps.configuring')}` |
| 136 | +}) |
| 137 | +
|
| 138 | +// Progress variant based on state |
| 139 | +const progressVariant = computed(() => { |
| 140 | + if (githubFetchError.value || submitError.value) return 'destructive' |
| 141 | + if (isSubmitting.value) return 'default' // Keep default while submitting |
| 142 | + // Only show success after actual completion (would need to be handled by parent component) |
| 143 | + return 'default' |
| 144 | +}) |
| 145 | +
|
92 | 146 | // State |
93 | 147 | const currentStep = ref(0) |
94 | 148 | const isSubmitting = ref(false) |
@@ -201,6 +255,13 @@ const goToStep = (stepIndex: number) => { |
201 | 255 | } |
202 | 256 | } |
203 | 257 |
|
| 258 | +// Handle step click from ProgressBars |
| 259 | +const handleStepClick = (step: any, index: number) => { |
| 260 | + if (step.clickable) { |
| 261 | + goToStep(index) |
| 262 | + } |
| 263 | +} |
| 264 | +
|
204 | 265 | const nextStep = () => { |
205 | 266 | if (canGoNext.value) { |
206 | 267 | const oldStep = currentStep.value |
@@ -335,39 +396,17 @@ onUnmounted(() => { |
335 | 396 |
|
336 | 397 | <template> |
337 | 398 | <div class="space-y-6"> |
338 | | - <!-- Breadcrumb Navigation --> |
339 | | - <Breadcrumb> |
340 | | - <BreadcrumbList> |
341 | | - <template v-for="(step, index) in steps" :key="index"> |
342 | | - <BreadcrumbItem> |
343 | | - <!-- Current Step --> |
344 | | - <BreadcrumbPage v-if="index === currentStep" class="flex items-center gap-2"> |
345 | | - <component :is="step.icon" class="h-4 w-4" /> |
346 | | - <span>{{ step.label }}</span> |
347 | | - </BreadcrumbPage> |
348 | | - |
349 | | - <!-- Completed Steps (clickable) --> |
350 | | - <BreadcrumbLink |
351 | | - v-else-if="index < currentStep" |
352 | | - @click="goToStep(index)" |
353 | | - class="flex items-center gap-2 cursor-pointer hover:text-foreground" |
354 | | - > |
355 | | - <component :is="step.icon" class="h-4 w-4" /> |
356 | | - <span>{{ step.label }}</span> |
357 | | - </BreadcrumbLink> |
358 | | - |
359 | | - <!-- Future Steps (disabled) --> |
360 | | - <span v-else class="flex items-center gap-2 text-muted-foreground"> |
361 | | - <component :is="step.icon" class="h-4 w-4" /> |
362 | | - <span>{{ step.label }}</span> |
363 | | - </span> |
364 | | - </BreadcrumbItem> |
365 | | - |
366 | | - <!-- Separator --> |
367 | | - <BreadcrumbSeparator v-if="index < steps.length - 1" /> |
368 | | - </template> |
369 | | - </BreadcrumbList> |
370 | | - </Breadcrumb> |
| 399 | + <!-- Progress Bar Navigation --> |
| 400 | + <ProgressBars |
| 401 | + :steps="progressSteps" |
| 402 | + :progress="progressPercentage" |
| 403 | + :title="progressTitle" |
| 404 | + :variant="progressVariant" |
| 405 | + size="md" |
| 406 | + interactive |
| 407 | + styled |
| 408 | + @step-click="handleStepClick" |
| 409 | + /> |
371 | 410 |
|
372 | 411 | <!-- Error Message --> |
373 | 412 | <Alert v-if="submitError" variant="destructive"> |
|
0 commit comments