mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-06 13:14:35 +00:00
Introduce a new `DeployAlert` interface and related components to provide visual feedback on build and deployment stages. This includes status updates for Vercel and Netlify deployments, with progress visualization and error handling. The changes enhance user experience by offering real-time updates during the deployment process.
198 lines
8.2 KiB
TypeScript
198 lines
8.2 KiB
TypeScript
import { AnimatePresence, motion } from 'framer-motion';
|
|
import { classNames } from '~/utils/classNames';
|
|
import type { DeployAlert } from '~/types/actions';
|
|
|
|
interface DeployAlertProps {
|
|
alert: DeployAlert;
|
|
clearAlert: () => void;
|
|
postMessage: (message: string) => void;
|
|
}
|
|
|
|
export default function DeployChatAlert({ alert, clearAlert, postMessage }: DeployAlertProps) {
|
|
const { type, title, description, content, url, stage, buildStatus, deployStatus } = alert;
|
|
|
|
// Determine if we should show the deployment progress
|
|
const showProgress = stage && (buildStatus || deployStatus);
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -20 }}
|
|
transition={{ duration: 0.3 }}
|
|
className={`rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-background-depth-2 p-4 mb-2`}
|
|
>
|
|
<div className="flex items-start">
|
|
{/* Icon */}
|
|
<motion.div
|
|
className="flex-shrink-0"
|
|
initial={{ scale: 0 }}
|
|
animate={{ scale: 1 }}
|
|
transition={{ delay: 0.2 }}
|
|
>
|
|
<div
|
|
className={classNames(
|
|
'text-xl',
|
|
type === 'success'
|
|
? 'i-ph:check-circle-duotone text-bolt-elements-icon-success'
|
|
: type === 'error'
|
|
? 'i-ph:warning-duotone text-bolt-elements-button-danger-text'
|
|
: 'i-ph:info-duotone text-bolt-elements-loader-progress',
|
|
)}
|
|
></div>
|
|
</motion.div>
|
|
{/* Content */}
|
|
<div className="ml-3 flex-1">
|
|
<motion.h3
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: 0.1 }}
|
|
className={`text-sm font-medium text-bolt-elements-textPrimary`}
|
|
>
|
|
{title}
|
|
</motion.h3>
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: 0.2 }}
|
|
className={`mt-2 text-sm text-bolt-elements-textSecondary`}
|
|
>
|
|
<p>{description}</p>
|
|
|
|
{/* Deployment Progress Visualization */}
|
|
{showProgress && (
|
|
<div className="mt-4 mb-2">
|
|
<div className="flex items-center space-x-2 mb-3">
|
|
{/* Build Step */}
|
|
<div className="flex items-center">
|
|
<div
|
|
className={classNames(
|
|
'w-6 h-6 rounded-full flex items-center justify-center',
|
|
buildStatus === 'running'
|
|
? 'bg-bolt-elements-loader-progress'
|
|
: buildStatus === 'complete'
|
|
? 'bg-bolt-elements-icon-success'
|
|
: buildStatus === 'failed'
|
|
? 'bg-bolt-elements-button-danger-background'
|
|
: 'bg-bolt-elements-textTertiary',
|
|
)}
|
|
>
|
|
{buildStatus === 'running' ? (
|
|
<div className="i-svg-spinners:90-ring-with-bg text-white text-xs"></div>
|
|
) : buildStatus === 'complete' ? (
|
|
<div className="i-ph:check text-white text-xs"></div>
|
|
) : buildStatus === 'failed' ? (
|
|
<div className="i-ph:x text-white text-xs"></div>
|
|
) : (
|
|
<span className="text-white text-xs">1</span>
|
|
)}
|
|
</div>
|
|
<span className="ml-2">Build</span>
|
|
</div>
|
|
|
|
{/* Connector Line */}
|
|
<div
|
|
className={classNames(
|
|
'h-0.5 w-8',
|
|
buildStatus === 'complete' ? 'bg-bolt-elements-icon-success' : 'bg-bolt-elements-textTertiary',
|
|
)}
|
|
></div>
|
|
|
|
{/* Deploy Step */}
|
|
<div className="flex items-center">
|
|
<div
|
|
className={classNames(
|
|
'w-6 h-6 rounded-full flex items-center justify-center',
|
|
deployStatus === 'running'
|
|
? 'bg-bolt-elements-loader-progress'
|
|
: deployStatus === 'complete'
|
|
? 'bg-bolt-elements-icon-success'
|
|
: deployStatus === 'failed'
|
|
? 'bg-bolt-elements-button-danger-background'
|
|
: 'bg-bolt-elements-textTertiary',
|
|
)}
|
|
>
|
|
{deployStatus === 'running' ? (
|
|
<div className="i-svg-spinners:90-ring-with-bg text-white text-xs"></div>
|
|
) : deployStatus === 'complete' ? (
|
|
<div className="i-ph:check text-white text-xs"></div>
|
|
) : deployStatus === 'failed' ? (
|
|
<div className="i-ph:x text-white text-xs"></div>
|
|
) : (
|
|
<span className="text-white text-xs">2</span>
|
|
)}
|
|
</div>
|
|
<span className="ml-2">Deploy</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{content && (
|
|
<div className="text-xs text-bolt-elements-textSecondary p-2 bg-bolt-elements-background-depth-3 rounded mt-4 mb-4">
|
|
{content}
|
|
</div>
|
|
)}
|
|
{url && type === 'success' && (
|
|
<div className="mt-2">
|
|
<a
|
|
href={url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-bolt-elements-item-contentAccent hover:underline flex items-center"
|
|
>
|
|
<span className="mr-1">View deployed site</span>
|
|
<div className="i-ph:arrow-square-out"></div>
|
|
</a>
|
|
</div>
|
|
)}
|
|
</motion.div>
|
|
|
|
{/* Actions */}
|
|
<motion.div
|
|
className="mt-4"
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: 0.3 }}
|
|
>
|
|
<div className={classNames('flex gap-2')}>
|
|
{type === 'error' && (
|
|
<button
|
|
onClick={() =>
|
|
postMessage(`*Fix this deployment error*\n\`\`\`\n${content || description}\n\`\`\`\n`)
|
|
}
|
|
className={classNames(
|
|
`px-2 py-1.5 rounded-md text-sm font-medium`,
|
|
'bg-bolt-elements-button-primary-background',
|
|
'hover:bg-bolt-elements-button-primary-backgroundHover',
|
|
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-danger-background',
|
|
'text-bolt-elements-button-primary-text',
|
|
'flex items-center gap-1.5',
|
|
)}
|
|
>
|
|
<div className="i-ph:chat-circle-duotone"></div>
|
|
Ask Bolt
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={clearAlert}
|
|
className={classNames(
|
|
`px-2 py-1.5 rounded-md text-sm font-medium`,
|
|
'bg-bolt-elements-button-secondary-background',
|
|
'hover:bg-bolt-elements-button-secondary-backgroundHover',
|
|
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-secondary-background',
|
|
'text-bolt-elements-button-secondary-text',
|
|
)}
|
|
>
|
|
Dismiss
|
|
</button>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
);
|
|
}
|