@@ -9,11 +9,12 @@ import { toast } from 'vue-sonner'
99import { McpInstallationService } from ' @/services/mcpInstallationService'
1010import { TeamService } from ' @/services/teamService'
1111import { McpCatalogService } from ' @/services/mcpCatalogService'
12+ import { SatelliteService , type TeamSatellite } from ' @/services/satelliteService'
1213import { useEventBus } from ' @/composables/useEventBus'
1314import McpServerSelectionStep from ' ./McpServerSelectionStep.vue'
1415import EnvironmentVariablesStep from ' ./EnvironmentVariablesStep.vue'
1516import OAuthAuthorizationStep from ' ./OAuthAuthorizationStep.vue'
16- import PlatformSelectionStep from ' ./PlatformSelectionStep .vue'
17+ import SatelliteSelectionStep from ' ./SatelliteSelectionStep .vue'
1718
1819// Props
1920interface Props {
@@ -53,6 +54,7 @@ interface InstallationFormData {
5354 }
5455 platform: {
5556 installation_type: string
57+ satellite_id: string
5658 installation_id? : string
5759 platform_config? : any
5860 }
@@ -69,6 +71,10 @@ const environmentValidation = ref({
6971const environmentStepTouched = ref (false )
7072const currentTeamId = ref <string | null >(null )
7173
74+ // Satellite selection state
75+ const satellites = ref <TeamSatellite []>([])
76+ const isFetchingSatellites = ref (false )
77+
7278// Form data with proper initialization
7379const formData = ref <InstallationFormData >({
7480 server: {
@@ -83,7 +89,8 @@ const formData = ref<InstallationFormData>({
8389 user_url_query_params: {}
8490 },
8591 platform: {
86- installation_type: ' global'
92+ installation_type: ' global' ,
93+ satellite_id: ' '
8794 }
8895})
8996
@@ -92,25 +99,35 @@ const requiresOAuth = computed(() => {
9299 return formData .value .server .server_data ?.requires_oauth === true
93100})
94101
102+ // Computed property to determine if satellite step should be shown
103+ const shouldShowSatelliteStep = computed (() => {
104+ return satellites .value .length > 1
105+ })
106+
95107// Progress steps for DsProgressSteps component
96108const progressSteps = computed <ProgressStep []>(() => {
97109 // Skip the first step (server selection) for progress display
98- const wizardSteps = [
99- {
110+ const wizardSteps = []
111+
112+ // Step 1: Satellite Selection (only if multiple satellites)
113+ if (shouldShowSatelliteStep .value ) {
114+ wizardSteps .push ({
100115 id: 1 ,
101- title: t (' mcpInstallations.wizard.steps.selectPlatform' ),
102- description: t (' mcpInstallations.wizard.platform.helpText' )
103- },
104- {
105- id: 2 ,
106- title: requiresOAuth .value
107- ? ' OAuth Authorization'
108- : t (' mcpInstallations.wizard.steps.configureEnvironment' ),
109- description: requiresOAuth .value
110- ? ' Authorize access to your account'
111- : t (' mcpInstallations.wizard.environment.helpText' )
112- }
113- ]
116+ title: t (' mcpInstallations.wizard.satellite.title' ),
117+ description: t (' mcpInstallations.wizard.satellite.description' )
118+ })
119+ }
120+
121+ // Step 2: Environment or OAuth
122+ wizardSteps .push ({
123+ id: shouldShowSatelliteStep .value ? 2 : 1 ,
124+ title: requiresOAuth .value
125+ ? ' OAuth Authorization'
126+ : t (' mcpInstallations.wizard.steps.configureEnvironment' ),
127+ description: requiresOAuth .value
128+ ? ' Authorize access to your account'
129+ : t (' mcpInstallations.wizard.environment.helpText' )
130+ })
114131
115132 return wizardSteps
116133})
@@ -119,8 +136,15 @@ const progressSteps = computed<ProgressStep[]>(() => {
119136const completedSteps = computed (() => {
120137 const completed: number [] = []
121138
122- // Environment step (index 0 in progress, step 1 in wizard)
123- if (currentStep .value > 1 ) {
139+ // If satellite step is shown and we've passed it
140+ if (shouldShowSatelliteStep .value && currentStep .value > 1 ) {
141+ completed .push (0 ) // Satellite step completed
142+ }
143+
144+ // If we're on the final step (environment/oauth)
145+ if (shouldShowSatelliteStep .value && currentStep .value > 2 ) {
146+ completed .push (1 )
147+ } else if (! shouldShowSatelliteStep .value && currentStep .value > 1 ) {
124148 completed .push (0 )
125149 }
126150
@@ -130,13 +154,23 @@ const completedSteps = computed(() => {
130154// Current progress step (adjusted for skipped server selection)
131155const currentProgressStep = computed (() => {
132156 if (currentStep .value === 0 ) return - 1 // Server selection, no progress shown
133- return currentStep .value - 1 // Adjust for 0-based progress index
157+
158+ if (shouldShowSatelliteStep .value ) {
159+ // With satellite step: Step 1 = satellite (progress 0), Step 2 = environment (progress 1)
160+ return currentStep .value - 1
161+ } else {
162+ // Without satellite step: Step 1 = environment (progress 0)
163+ return currentStep .value - 1
164+ }
134165})
135166
136167// Additional computed properties
137- const totalSteps = 3 // Server selection (0), Environment (1), Platform (2)
168+ const totalSteps = computed (() => {
169+ // Server selection (0) + Satellite (1, conditional) + Environment/OAuth (2 or 1)
170+ return shouldShowSatelliteStep .value ? 3 : 2
171+ })
138172const isFirstStep = computed (() => currentStep .value === 0 )
139- const isLastStep = computed (() => currentStep .value === totalSteps - 1 )
173+ const isLastStep = computed (() => currentStep .value === totalSteps . value - 1 )
140174const canGoNext = computed (() => ! isLastStep .value )
141175const canGoPrevious = computed (() => ! isFirstStep .value )
142176
@@ -162,8 +196,11 @@ const canSubmit = computed(() => {
162196})
163197
164198const nextStep = () => {
199+ // Determine which step we're on based on whether satellite step is shown
200+ const environmentStepIndex = shouldShowSatelliteStep .value ? 2 : 1
201+
165202 // Mark environment step as touched when user tries to proceed from it
166- if (currentStep .value === 1 ) {
203+ if (currentStep .value === environmentStepIndex ) {
167204 environmentStepTouched .value = true
168205
169206 // Check validation before proceeding
@@ -195,10 +232,11 @@ const previousStep = () => {
195232 formData .value .environment .team_env = {}
196233 formData .value .environment .user_env = {}
197234 environmentStepTouched .value = false
198- } else if (currentStep .value === 1 ) {
199- // Going back to environment step - clear platform data
200- formData .value .platform .installation_type = ' global'
201- formData .value .platform .platform_config = undefined
235+ } else if (shouldShowSatelliteStep .value && currentStep .value === 1 ) {
236+ // Going back to satellite step - clear satellite selection
237+ formData .value .platform .satellite_id = ' '
238+ } else if (! shouldShowSatelliteStep .value && currentStep .value === 1 ) {
239+ // No satellite step, going back to server selection (step 0) - already handled above
202240 }
203241 }
204242}
@@ -207,6 +245,33 @@ const handleCancel = () => {
207245 emit (' cancel' )
208246}
209247
248+ // Fetch available satellites for the team
249+ const fetchSatellites = async () => {
250+ if (! currentTeamId .value ) {
251+ return
252+ }
253+
254+ try {
255+ isFetchingSatellites .value = true
256+ const response = await SatelliteService .getTeamSatellites (currentTeamId .value )
257+ satellites .value = response .data .satellites
258+
259+ // Auto-select satellite if only one is available
260+ if (satellites .value .length === 1 ) {
261+ formData .value .platform .satellite_id = satellites .value [0 ]! .id
262+ }
263+ } catch (error ) {
264+ const errorMessage = error instanceof Error ? error .message : ' Failed to fetch satellites'
265+ toast .error (t (' mcpInstallations.wizard.satellite.errorFetching' ), {
266+ description: errorMessage
267+ })
268+ // Set empty array on error to allow wizard to continue
269+ satellites .value = []
270+ } finally {
271+ isFetchingSatellites .value = false
272+ }
273+ }
274+
210275// Initialize team context from event bus storage
211276const initializeTeamContext = async () => {
212277 try {
@@ -538,6 +603,9 @@ onMounted(async () => {
538603 // Initialize team context
539604 await initializeTeamContext ()
540605
606+ // Fetch available satellites for the team
607+ await fetchSatellites ()
608+
541609 // Handle query parameters for pre-selection
542610 await handleQueryParameters ()
543611
@@ -550,7 +618,7 @@ onMounted(async () => {
550618 formData .value = {
551619 server: { server_id: ' ' },
552620 environment: { team_args: [], team_env: {}, team_headers: {}, team_url_query_params: {}, user_env: {}, user_url_query_params: {} },
553- platform: { installation_type: ' global' }
621+ platform: { installation_type: ' global' , satellite_id: ' ' }
554622 }
555623 })
556624})
@@ -628,34 +696,93 @@ onUnmounted(() => {
628696 :completed-steps =" completedSteps"
629697 max-width =" max-w-3xl"
630698 >
631- <!-- Step 1 Content: Platform Selection -->
699+ <!-- Step Content 0: Satellite Selection if shown, otherwise Environment/OAuth -->
632700 <template #step-content-0 >
633- <PlatformSelectionStep
634- v-model =" formData.platform.installation_type"
635- />
636-
637- <!-- Navigation Buttons for Platform Step -->
638- <div class =" flex items-center justify-between mt-6" >
639- <Button variant =" outline" @click =" previousStep" >
640- {{ t('navigation.previous') }}
641- </Button >
701+ <!-- Satellite Selection (only if multiple satellites) -->
702+ <div v-if =" shouldShowSatelliteStep" >
703+ <SatelliteSelectionStep
704+ v-model =" formData.platform.satellite_id"
705+ :satellites =" satellites"
706+ :is-loading =" isFetchingSatellites"
707+ />
642708
643- <div class =" flex items-center gap-2" >
644- <Button variant =" ghost" @click =" handleCancel" >
645- {{ t('navigation.cancel') }}
709+ <!-- Navigation Buttons for Satellite Step -->
710+ <div class =" flex items-center justify-between mt-6" >
711+ <Button variant =" outline" @click =" previousStep" >
712+ {{ t('navigation.previous') }}
646713 </Button >
647714
648- <Button
649- @click =" nextStep"
650- :disabled =" !formData.platform.installation_type"
651- >
652- {{ t('navigation.next') }}
715+ <div class =" flex items-center gap-2" >
716+ <Button variant =" ghost" @click =" handleCancel" >
717+ {{ t('navigation.cancel') }}
718+ </Button >
719+
720+ <Button
721+ @click =" nextStep"
722+ :disabled =" !formData.platform.satellite_id"
723+ >
724+ {{ t('navigation.next') }}
725+ </Button >
726+ </div >
727+ </div >
728+ </div >
729+
730+ <!-- Environment/OAuth Step (when satellite step is hidden) -->
731+ <div v-else >
732+ <!-- OAuth Authorization Step (if OAuth required) -->
733+ <OAuthAuthorizationStep
734+ v-if =" requiresOAuth"
735+ :server-data =" formData.server.server_data"
736+ :is-authorizing =" isSubmitting"
737+ @authorize =" handleOAuthAuthorization"
738+ />
739+
740+ <!-- Environment Variables Step (if OAuth NOT required) -->
741+ <EnvironmentVariablesStep
742+ v-else
743+ v-model =" formData.environment"
744+ :server-data =" formData.server.server_data"
745+ @validation-change =" handleValidationChange"
746+ />
747+
748+ <!-- Navigation Buttons -->
749+ <div class =" flex items-center justify-between mt-6" >
750+ <Button variant =" outline" @click =" previousStep" >
751+ {{ t('navigation.previous') }}
653752 </Button >
753+
754+ <div class =" flex items-center gap-2" >
755+ <Button variant =" ghost" @click =" handleCancel" >
756+ {{ t('navigation.cancel') }}
757+ </Button >
758+
759+ <!-- OAuth: "Authorize & Install" button -->
760+ <Button
761+ v-if =" requiresOAuth"
762+ @click =" handleOAuthAuthorization"
763+ :loading =" isSubmitting"
764+ :loading-text =" t('mcpInstallations.wizard.authorizing')"
765+ :disabled =" !formData.platform.installation_type"
766+ >
767+ {{ t('mcpInstallations.wizard.authorizeAndInstall') }}
768+ </Button >
769+
770+ <!-- Non-OAuth: "Install" button -->
771+ <Button
772+ v-else
773+ @click =" submitInstallation"
774+ :disabled =" !canSubmit"
775+ :loading =" isSubmitting"
776+ :loading-text =" t('mcpInstallations.wizard.installing')"
777+ >
778+ {{ t('mcpInstallations.wizard.install') }}
779+ </Button >
780+ </div >
654781 </div >
655782 </div >
656783 </template >
657784
658- <!-- Step 2 Content: Environment Variables OR OAuth Authorization -->
785+ <!-- Environment/OAuth Step when satellite step is shown -->
659786 <template #step-content-1 >
660787 <!-- OAuth Authorization Step (if OAuth required) -->
661788 <OAuthAuthorizationStep
0 commit comments