Leon4gr45/builder
0
1'use client';2 3import React, { useEffect, useState } from 'react';4import { Deployment } from '@/lib/vfs/types';5import { Server, Loader2 } from 'lucide-react';6import {7 Select,8 SelectContent,9 SelectItem,10 SelectTrigger,11 SelectValue,12} from '@/components/ui/select';13import { cn } from '@/lib/utils';14 15interface DeploymentSelectorProps {16 projectId: string;17 selectedDeploymentId: string | null;18 onDeploymentChange: (deploymentId: string | null, deploymentName: string | null) => void;19 className?: string;20 workspaceId?: string;21}22 23export function DeploymentSelector({24 projectId,25 selectedDeploymentId,26 onDeploymentChange,27 className,28 workspaceId,29}: DeploymentSelectorProps) {30 const apiBase = workspaceId ? `/api/w/${workspaceId}` : '/api';31 const [deployments, setDeployments] = useState<Deployment[]>([]);32 const [loading, setLoading] = useState(true);33 const [error, setError] = useState<string | null>(null);34 35 // Fetch deployments for this project36 useEffect(() => {37 const fetchDeployments = async () => {38 // Only fetch in server mode39 if (process.env.NEXT_PUBLIC_SERVER_MODE !== 'true') {40 setLoading(false);41 return;42 }43 44 try {45 setLoading(true);46 setError(null);47 48 const response = await fetch(`${apiBase}/projects/${projectId}/deployments`);49 if (!response.ok) {50 throw new Error('Failed to fetch deployments');51 }52 53 const data = await response.json();54 setDeployments(data.deployments || []);55 56 // Auto-select first deployment if only one exists and nothing is selected57 if (data.deployments?.length === 1 && !selectedDeploymentId) {58 const deployment = data.deployments[0];59 if (deployment.databaseEnabled) {60 onDeploymentChange(deployment.id, deployment.name);61 }62 }63 } catch {64 // Expected in browser mode or when project hasn't been synced to server yet65 // Don't show error — deployments are optional66 } finally {67 setLoading(false);68 }69 };70 71 fetchDeployments();72 }, [projectId]); // Only refetch when project changes73 74 // Don't render in browser mode75 if (process.env.NEXT_PUBLIC_SERVER_MODE !== 'true') {76 return null;77 }78 79 // Don't render if no deployments with database enabled80 const databaseEnabledDeployments = deployments.filter(s => s.databaseEnabled);81 if (!loading && databaseEnabledDeployments.length === 0) {82 return null;83 }84 85 if (loading) {86 return (87 <div className={cn('flex items-center gap-2 text-sm text-muted-foreground', className)}>88 <Loader2 className="h-4 w-4 animate-spin" />89 <span>Loading deployments...</span>90 </div>91 );92 }93 94 if (error) {95 return (96 <div className={cn('flex items-center gap-2 text-sm text-destructive', className)}>97 <Server className="h-4 w-4" />98 <span>{error}</span>99 </div>100 );101 }102 103 return (104 <div className={cn('flex items-center gap-2', className)}>105 <Select106 value={selectedDeploymentId || 'none'}107 onValueChange={(value) => {108 if (value === 'none') {109 onDeploymentChange(null, null);110 } else {111 const deployment = deployments.find(s => s.id === value);112 onDeploymentChange(value, deployment?.name || null);113 }114 }}115 >116 <SelectTrigger size="sm" className="w-[180px] h-8">117 <SelectValue placeholder="No deployment" />118 </SelectTrigger>119 <SelectContent>120 <SelectItem value="none">121 <span className="text-muted-foreground">No deployment</span>122 </SelectItem>123 {databaseEnabledDeployments.map((deployment) => (124 <SelectItem key={deployment.id} value={deployment.id}>125 <div className="flex items-center gap-2">126 <Server className="h-3.5 w-3.5" />127 <span>{deployment.name}</span>128 </div>129 </SelectItem>130 ))}131 </SelectContent>132 </Select>133 </div>134 );135}136 