|
import { AnimatePresence, motion } from 'framer-motion'; |
|
import type { ActionAlert } from '~/types/actions'; |
|
import { classNames } from '~/utils/classNames'; |
|
|
|
interface Props { |
|
alert: ActionAlert; |
|
clearAlert: () => void; |
|
postMessage: (message: string) => void; |
|
} |
|
|
|
export default function ChatAlert({ alert, clearAlert, postMessage }: Props) { |
|
const { description, content, source } = alert; |
|
|
|
const isPreview = source === 'preview'; |
|
const title = isPreview ? 'Preview Error' : 'Terminal Error'; |
|
const message = isPreview |
|
? 'We encountered an error while running the preview. Would you like Bolt to analyze and help resolve this issue?' |
|
: 'We encountered an error while running terminal commands. Would you like Bolt to analyze and help resolve this issue?'; |
|
|
|
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={`i-ph:warning-duotone text-xl text-bolt-elements-button-danger-text`}></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>{message}</p> |
|
{description && ( |
|
<div className="text-xs text-bolt-elements-textSecondary p-2 bg-bolt-elements-background-depth-3 rounded mt-4 mb-4"> |
|
Error: {description} |
|
</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')}> |
|
<button |
|
onClick={() => |
|
postMessage( |
|
`*Fix this ${isPreview ? 'preview' : 'terminal'} error* \n\`\`\`${isPreview ? 'js' : 'sh'}\n${content}\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> |
|
); |
|
} |
|
|