| 'use client'; |
|
|
| import React, { useEffect, useState } from 'react'; |
| import { Deployment } from '@/lib/vfs/types'; |
| import { Server, Loader2 } from 'lucide-react'; |
| import { |
| Select, |
| SelectContent, |
| SelectItem, |
| SelectTrigger, |
| SelectValue, |
| } from '@/components/ui/select'; |
| import { cn } from '@/lib/utils'; |
|
|
| interface DeploymentSelectorProps { |
| projectId: string; |
| selectedDeploymentId: string | null; |
| onDeploymentChange: (deploymentId: string | null, deploymentName: string | null) => void; |
| className?: string; |
| workspaceId?: string; |
| } |
|
|
| export function DeploymentSelector({ |
| projectId, |
| selectedDeploymentId, |
| onDeploymentChange, |
| className, |
| workspaceId, |
| }: DeploymentSelectorProps) { |
| const apiBase = workspaceId ? `/api/w/${workspaceId}` : '/api'; |
| const [deployments, setDeployments] = useState<Deployment[]>([]); |
| const [loading, setLoading] = useState(true); |
| const [error, setError] = useState<string | null>(null); |
|
|
| |
| useEffect(() => { |
| const fetchDeployments = async () => { |
| |
| if (process.env.NEXT_PUBLIC_SERVER_MODE !== 'true') { |
| setLoading(false); |
| return; |
| } |
|
|
| try { |
| setLoading(true); |
| setError(null); |
|
|
| const response = await fetch(`${apiBase}/projects/${projectId}/deployments`); |
| if (!response.ok) { |
| throw new Error('Failed to fetch deployments'); |
| } |
|
|
| const data = await response.json(); |
| setDeployments(data.deployments || []); |
|
|
| |
| if (data.deployments?.length === 1 && !selectedDeploymentId) { |
| const deployment = data.deployments[0]; |
| if (deployment.databaseEnabled) { |
| onDeploymentChange(deployment.id, deployment.name); |
| } |
| } |
| } catch { |
| |
| |
| } finally { |
| setLoading(false); |
| } |
| }; |
|
|
| fetchDeployments(); |
| }, [projectId]); |
|
|
| |
| if (process.env.NEXT_PUBLIC_SERVER_MODE !== 'true') { |
| return null; |
| } |
|
|
| |
| const databaseEnabledDeployments = deployments.filter(s => s.databaseEnabled); |
| if (!loading && databaseEnabledDeployments.length === 0) { |
| return null; |
| } |
|
|
| if (loading) { |
| return ( |
| <div className={cn('flex items-center gap-2 text-sm text-muted-foreground', className)}> |
| <Loader2 className="h-4 w-4 animate-spin" /> |
| <span>Loading deployments...</span> |
| </div> |
| ); |
| } |
|
|
| if (error) { |
| return ( |
| <div className={cn('flex items-center gap-2 text-sm text-destructive', className)}> |
| <Server className="h-4 w-4" /> |
| <span>{error}</span> |
| </div> |
| ); |
| } |
|
|
| return ( |
| <div className={cn('flex items-center gap-2', className)}> |
| <Select |
| value={selectedDeploymentId || 'none'} |
| onValueChange={(value) => { |
| if (value === 'none') { |
| onDeploymentChange(null, null); |
| } else { |
| const deployment = deployments.find(s => s.id === value); |
| onDeploymentChange(value, deployment?.name || null); |
| } |
| }} |
| > |
| <SelectTrigger size="sm" className="w-[180px] h-8"> |
| <SelectValue placeholder="No deployment" /> |
| </SelectTrigger> |
| <SelectContent> |
| <SelectItem value="none"> |
| <span className="text-muted-foreground">No deployment</span> |
| </SelectItem> |
| {databaseEnabledDeployments.map((deployment) => ( |
| <SelectItem key={deployment.id} value={deployment.id}> |
| <div className="flex items-center gap-2"> |
| <Server className="h-3.5 w-3.5" /> |
| <span>{deployment.name}</span> |
| </div> |
| </SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| ); |
| } |
|
|