278 lines
11 KiB
TypeScript
278 lines
11 KiB
TypeScript
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
|
import { useStore } from '@nanostores/react';
|
|
import { netlifyConnection } from '~/lib/stores/netlify';
|
|
import { vercelConnection } from '~/lib/stores/vercel';
|
|
import { isGitLabConnected } from '~/lib/stores/gitlabConnection';
|
|
import { workbenchStore } from '~/lib/stores/workbench';
|
|
import { streamingState } from '~/lib/stores/streaming';
|
|
import { classNames } from '~/utils/classNames';
|
|
import { useState } from 'react';
|
|
import { NetlifyDeploymentLink } from '~/components/chat/NetlifyDeploymentLink.client';
|
|
import { VercelDeploymentLink } from '~/components/chat/VercelDeploymentLink.client';
|
|
import { useVercelDeploy } from '~/components/deploy/VercelDeploy.client';
|
|
import { useNetlifyDeploy } from '~/components/deploy/NetlifyDeploy.client';
|
|
import { useGitHubDeploy } from '~/components/deploy/GitHubDeploy.client';
|
|
import { useGitLabDeploy } from '~/components/deploy/GitLabDeploy.client';
|
|
import { GitHubDeploymentDialog } from '~/components/deploy/GitHubDeploymentDialog';
|
|
import { GitLabDeploymentDialog } from '~/components/deploy/GitLabDeploymentDialog';
|
|
|
|
interface DeployButtonProps {
|
|
onVercelDeploy?: () => Promise<void>;
|
|
onNetlifyDeploy?: () => Promise<void>;
|
|
onGitHubDeploy?: () => Promise<void>;
|
|
onGitLabDeploy?: () => Promise<void>;
|
|
}
|
|
|
|
export const DeployButton = ({
|
|
onVercelDeploy,
|
|
onNetlifyDeploy,
|
|
onGitHubDeploy,
|
|
onGitLabDeploy,
|
|
}: DeployButtonProps) => {
|
|
const netlifyConn = useStore(netlifyConnection);
|
|
const vercelConn = useStore(vercelConnection);
|
|
const gitlabIsConnected = useStore(isGitLabConnected);
|
|
const [activePreviewIndex] = useState(0);
|
|
const previews = useStore(workbenchStore.previews);
|
|
const activePreview = previews[activePreviewIndex];
|
|
const [isDeploying, setIsDeploying] = useState(false);
|
|
const [deployingTo, setDeployingTo] = useState<'netlify' | 'vercel' | 'github' | 'gitlab' | null>(null);
|
|
const isStreaming = useStore(streamingState);
|
|
const { handleVercelDeploy } = useVercelDeploy();
|
|
const { handleNetlifyDeploy } = useNetlifyDeploy();
|
|
const { handleGitHubDeploy } = useGitHubDeploy();
|
|
const { handleGitLabDeploy } = useGitLabDeploy();
|
|
const [showGitHubDeploymentDialog, setShowGitHubDeploymentDialog] = useState(false);
|
|
const [showGitLabDeploymentDialog, setShowGitLabDeploymentDialog] = useState(false);
|
|
const [githubDeploymentFiles, setGithubDeploymentFiles] = useState<Record<string, string> | null>(null);
|
|
const [gitlabDeploymentFiles, setGitlabDeploymentFiles] = useState<Record<string, string> | null>(null);
|
|
const [githubProjectName, setGithubProjectName] = useState('');
|
|
const [gitlabProjectName, setGitlabProjectName] = useState('');
|
|
|
|
const handleVercelDeployClick = async () => {
|
|
setIsDeploying(true);
|
|
setDeployingTo('vercel');
|
|
|
|
try {
|
|
if (onVercelDeploy) {
|
|
await onVercelDeploy();
|
|
} else {
|
|
await handleVercelDeploy();
|
|
}
|
|
} finally {
|
|
setIsDeploying(false);
|
|
setDeployingTo(null);
|
|
}
|
|
};
|
|
|
|
const handleNetlifyDeployClick = async () => {
|
|
setIsDeploying(true);
|
|
setDeployingTo('netlify');
|
|
|
|
try {
|
|
if (onNetlifyDeploy) {
|
|
await onNetlifyDeploy();
|
|
} else {
|
|
await handleNetlifyDeploy();
|
|
}
|
|
} finally {
|
|
setIsDeploying(false);
|
|
setDeployingTo(null);
|
|
}
|
|
};
|
|
|
|
const handleGitHubDeployClick = async () => {
|
|
setIsDeploying(true);
|
|
setDeployingTo('github');
|
|
|
|
try {
|
|
if (onGitHubDeploy) {
|
|
await onGitHubDeploy();
|
|
} else {
|
|
const result = await handleGitHubDeploy();
|
|
|
|
if (result && result.success && result.files) {
|
|
setGithubDeploymentFiles(result.files);
|
|
setGithubProjectName(result.projectName);
|
|
setShowGitHubDeploymentDialog(true);
|
|
}
|
|
}
|
|
} finally {
|
|
setIsDeploying(false);
|
|
setDeployingTo(null);
|
|
}
|
|
};
|
|
|
|
const handleGitLabDeployClick = async () => {
|
|
setIsDeploying(true);
|
|
setDeployingTo('gitlab');
|
|
|
|
try {
|
|
if (onGitLabDeploy) {
|
|
await onGitLabDeploy();
|
|
} else {
|
|
const result = await handleGitLabDeploy();
|
|
|
|
if (result && result.success && result.files) {
|
|
setGitlabDeploymentFiles(result.files);
|
|
setGitlabProjectName(result.projectName);
|
|
setShowGitLabDeploymentDialog(true);
|
|
}
|
|
}
|
|
} finally {
|
|
setIsDeploying(false);
|
|
setDeployingTo(null);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden text-sm">
|
|
<DropdownMenu.Root>
|
|
<DropdownMenu.Trigger
|
|
disabled={isDeploying || !activePreview || isStreaming}
|
|
className="rounded-md items-center justify-center [&:is(:disabled,.disabled)]:cursor-not-allowed [&:is(:disabled,.disabled)]:opacity-60 px-3 py-1.5 text-xs bg-accent-500 text-white hover:text-bolt-elements-item-contentAccent [&:not(:disabled,.disabled)]:hover:bg-bolt-elements-button-primary-backgroundHover outline-accent-500 flex gap-1.7"
|
|
>
|
|
{isDeploying ? `Deploying to ${deployingTo}...` : 'Deploy'}
|
|
<span className={classNames('i-ph:caret-down transition-transform')} />
|
|
</DropdownMenu.Trigger>
|
|
<DropdownMenu.Content
|
|
className={classNames(
|
|
'z-[250]',
|
|
'bg-bolt-elements-background-depth-2',
|
|
'rounded-lg shadow-lg',
|
|
'border border-bolt-elements-borderColor',
|
|
'animate-in fade-in-0 zoom-in-95',
|
|
'py-1',
|
|
)}
|
|
sideOffset={5}
|
|
align="end"
|
|
>
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
|
{
|
|
'opacity-60 cursor-not-allowed': isDeploying || !activePreview || !netlifyConn.user,
|
|
},
|
|
)}
|
|
disabled={isDeploying || !activePreview || !netlifyConn.user}
|
|
onClick={handleNetlifyDeployClick}
|
|
>
|
|
<img
|
|
className="w-5 h-5"
|
|
height="24"
|
|
width="24"
|
|
crossOrigin="anonymous"
|
|
src="https://cdn.simpleicons.org/netlify"
|
|
/>
|
|
<span className="mx-auto">
|
|
{!netlifyConn.user ? 'No Netlify Account Connected' : 'Deploy to Netlify'}
|
|
</span>
|
|
{netlifyConn.user && <NetlifyDeploymentLink />}
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
|
{
|
|
'opacity-60 cursor-not-allowed': isDeploying || !activePreview || !vercelConn.user,
|
|
},
|
|
)}
|
|
disabled={isDeploying || !activePreview || !vercelConn.user}
|
|
onClick={handleVercelDeployClick}
|
|
>
|
|
<img
|
|
className="w-5 h-5 bg-black p-1 rounded"
|
|
height="24"
|
|
width="24"
|
|
crossOrigin="anonymous"
|
|
src="https://cdn.simpleicons.org/vercel/white"
|
|
alt="vercel"
|
|
/>
|
|
<span className="mx-auto">{!vercelConn.user ? 'No Vercel Account Connected' : 'Deploy to Vercel'}</span>
|
|
{vercelConn.user && <VercelDeploymentLink />}
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
|
{
|
|
'opacity-60 cursor-not-allowed': isDeploying || !activePreview,
|
|
},
|
|
)}
|
|
disabled={isDeploying || !activePreview}
|
|
onClick={handleGitHubDeployClick}
|
|
>
|
|
<img
|
|
className="w-5 h-5"
|
|
height="24"
|
|
width="24"
|
|
crossOrigin="anonymous"
|
|
src="https://cdn.simpleicons.org/github"
|
|
alt="github"
|
|
/>
|
|
<span className="mx-auto">Deploy to GitHub</span>
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Item
|
|
className={classNames(
|
|
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
|
|
{
|
|
'opacity-60 cursor-not-allowed': isDeploying || !activePreview || !gitlabIsConnected,
|
|
},
|
|
)}
|
|
disabled={isDeploying || !activePreview || !gitlabIsConnected}
|
|
onClick={handleGitLabDeployClick}
|
|
>
|
|
<img
|
|
className="w-5 h-5"
|
|
height="24"
|
|
width="24"
|
|
crossOrigin="anonymous"
|
|
src="https://cdn.simpleicons.org/gitlab"
|
|
alt="gitlab"
|
|
/>
|
|
<span className="mx-auto">{!gitlabIsConnected ? 'No GitLab Account Connected' : 'Deploy to GitLab'}</span>
|
|
</DropdownMenu.Item>
|
|
|
|
<DropdownMenu.Item
|
|
disabled
|
|
className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2 opacity-60 cursor-not-allowed"
|
|
>
|
|
<img
|
|
className="w-5 h-5"
|
|
height="24"
|
|
width="24"
|
|
crossOrigin="anonymous"
|
|
src="https://cdn.simpleicons.org/cloudflare"
|
|
alt="cloudflare"
|
|
/>
|
|
<span className="mx-auto">Deploy to Cloudflare (Coming Soon)</span>
|
|
</DropdownMenu.Item>
|
|
</DropdownMenu.Content>
|
|
</DropdownMenu.Root>
|
|
</div>
|
|
|
|
{/* GitHub Deployment Dialog */}
|
|
{showGitHubDeploymentDialog && githubDeploymentFiles && (
|
|
<GitHubDeploymentDialog
|
|
isOpen={showGitHubDeploymentDialog}
|
|
onClose={() => setShowGitHubDeploymentDialog(false)}
|
|
projectName={githubProjectName}
|
|
files={githubDeploymentFiles}
|
|
/>
|
|
)}
|
|
|
|
{/* GitLab Deployment Dialog */}
|
|
{showGitLabDeploymentDialog && gitlabDeploymentFiles && (
|
|
<GitLabDeploymentDialog
|
|
isOpen={showGitLabDeploymentDialog}
|
|
onClose={() => setShowGitLabDeploymentDialog(false)}
|
|
projectName={gitlabProjectName}
|
|
files={gitlabDeploymentFiles}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|