pixelroast / src /features /FixItButton.jsx
r139-8r
feat: implement security fixes and AI detector features
5dd75cf
Raw History Blame Contribute Delete
7.16 kB
import React, { useState, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Wrench, Check, Copy, X, Eye, EyeOff } from 'lucide-react';
import { Button } from '../components/ui/Button';
export const FixItButton = ({ code = "p-4 bg-red-500 rounded-lg" }) => {
const [isOpen, setIsOpen] = useState(false);
const [copied, setCopied] = useState(false);
const [showPreview, setShowPreview] = useState(false);
const iframeRef = useRef(null);
const handleCopy = () => {
navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
// Extract the fix code string from various fix formats
const getFixCode = () => {
if (typeof code === 'string') return code;
if (typeof code === 'object' && code.fix_code) return code.fix_code;
if (typeof code === 'object' && code.issue) return `${code.issue}: ${code.fix_code || ''}`;
return String(code);
};
const fixCode = getFixCode();
// Generate preview HTML with Tailwind CDN
const getPreviewHTML = () => `
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.tailwindcss.com"></script>
<style>body{margin:0;padding:16px;background:#f9fafb;display:flex;align-items:center;justify-content:center;min-height:calc(100vh - 32px);}</style>
</head>
<body>
<div class="${fixCode}">
<p class="text-sm">Preview: Your fix applied</p>
</div>
</body>
</html>`;
return (
<div className="relative">
<Button
onClick={() => setIsOpen(!isOpen)}
className={`h-8 px-3 text-xs bg-brutalist-black text-white shrink-0 ${isOpen ? 'bg-brutalist-red hover:bg-red-700' : ''}`}
>
<Wrench size={12} />
FIX IT
</Button>
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop to close on click outside */}
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
/>
{/* Popup - opens ABOVE the button to avoid cut-off */}
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.9 }}
className="fixed sm:absolute z-50 left-4 right-4 sm:left-auto sm:right-0 bottom-20 sm:bottom-full sm:mb-2 sm:w-80 p-4 bg-white border-2 border-brutalist-black shadow-hard-lg"
>
{/* Header */}
<div className="flex justify-between items-center mb-3">
<span className="font-bold text-sm uppercase text-brutalist-black flex items-center gap-2">
<Wrench size={14} />
Tailwind Fix
</span>
<button
onClick={() => setIsOpen(false)}
className="p-1 hover:bg-gray-100 rounded"
>
<X size={16} />
</button>
</div>
{/* Code block */}
<div className="bg-gray-900 text-green-400 p-3 font-mono text-xs break-all rounded mb-3 max-h-32 overflow-y-auto">
{fixCode}
</div>
{/* Action buttons */}
<div className="flex gap-2 mb-3">
{/* Copy button */}
<button
onClick={handleCopy}
className={`flex-1 py-2 font-bold text-sm flex items-center justify-center gap-2 border-2 border-brutalist-black transition-colors ${copied
? 'bg-green-500 text-white'
: 'bg-brutalist-yellow hover:bg-brutalist-black hover:text-white'
}`}
>
{copied ? (
<>
<Check size={16} />
COPIED!
</>
) : (
<>
<Copy size={16} />
COPY
</>
)}
</button>
{/* Preview toggle */}
<button
onClick={() => setShowPreview(!showPreview)}
className={`py-2 px-3 font-bold text-sm flex items-center justify-center gap-2 border-2 border-brutalist-black transition-colors ${
showPreview
? 'bg-blue-600 text-white'
: 'bg-white hover:bg-gray-100'
}`}
>
{showPreview ? <EyeOff size={16} /> : <Eye size={16} />}
{showPreview ? 'HIDE' : 'PREVIEW'}
</button>
</div>
{/* Live Preview iframe */}
<AnimatePresence>
{showPreview && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 120, opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
className="overflow-hidden border-2 border-dashed border-gray-300 rounded"
>
<iframe
ref={iframeRef}
srcDoc={getPreviewHTML()}
title="Tailwind Preview"
className="w-full h-[120px] border-0"
sandbox="allow-scripts"
/>
</motion.div>
)}
</AnimatePresence>
</motion.div>
</>
)}
</AnimatePresence>
</div>
);
};