"use client"import React, { useRef, useState } from "react";import styled from "styled-components";import { motion, AnimatePresence } from "framer-motion";import { v4 as uuidv4 } from "uuid";import { nanoid } from "nanoid";import { db } from "../lib/firebase";import { collection, addDoc, serverTimestamp } from "firebase/firestore";import { uploadFile } from "../lib/vercel-blob";import { QRCodeCanvas } from "qrcode.react";import bcrypt from "bcryptjs";const Container = styled.main` min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 1rem; position: relative; overflow: hidden; &::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Ccircle cx='30' cy='30' r='2'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); pointer-events: none; }`;const Card = styled(motion.div)` background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); border-radius: 2rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); padding: 3rem 2rem; width: 100%; max-width: 500px; display: flex; flex-direction: column; align-items: center; gap: 2rem; border: 1px solid rgba(255, 255, 255, 0.2); position: relative; z-index: 1;`;const Title = styled(motion.h1)` font-size: 3rem; font-weight: 800; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 0.5rem; text-align: center;`;const Subtitle = styled(motion.p)` color: #64748b; font-size: 1.1rem; margin-bottom: 1rem; text-align: center; line-height: 1.6;`;const UploadArea = styled(motion.div)` width: 100%; border: 3px dashed #e2e8f0; border-radius: 1.5rem; padding: 3rem 2rem; text-align: center; cursor: pointer; transition: all 0.3s ease; background: rgba(248, 250, 252, 0.5); position: relative; overflow: hidden; &:hover { border-color: #667eea; background: rgba(102, 126, 234, 0.05); transform: translateY(-2px); } &.drag-over { border-color: #667eea; background: rgba(102, 126, 234, 0.1); transform: scale(1.02); }`;const UploadIcon = styled.div` font-size: 3rem; color: #667eea; margin-bottom: 1rem;`;const FileInput = styled.input` display: none;`;const FileLabel = styled.label` display: inline-block; padding: 1rem 2rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); &:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); }`;const FileInfo = styled(motion.div)` background: rgba(102, 126, 234, 0.1); border-radius: 1rem; padding: 1.5rem; width: 100%; text-align: center; border: 1px solid rgba(102, 126, 234, 0.2);`;const FileName = styled.div` color: #1e293b; font-size: 1.1rem; font-weight: 600; margin-bottom: 0.5rem;`;const FileSize = styled.div` color: #64748b; font-size: 0.9rem;`;const Input = styled(motion.input)` width: 100%; padding: 1rem 1.5rem; border: 2px solid #e2e8f0; border-radius: 1rem; font-size: 1rem; background: rgba(255, 255, 255, 0.8); color: #334155; transition: all 0.3s ease; &:focus { outline: none; border-color: #667eea; background: white; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } &::placeholder { color: #94a3b8; }`;const UploadButton = styled(motion.button)` padding: 1rem 2.5rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 1rem; font-weight: 700; font-size: 1.1rem; cursor: pointer; box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); transition: all 0.3s ease; position: relative; overflow: hidden; &:hover { transform: translateY(-2px); box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4); } &:disabled { background: #94a3b8; cursor: not-allowed; transform: none; box-shadow: none; } &::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); transition: left 0.5s; } &:hover::before { left: 100%; }`;const SuccessCard = styled(motion.div)` background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; border-radius: 1.5rem; padding: 2rem; width: 100%; text-align: center; box-shadow: 0 10px 30px rgba(16, 185, 129, 0.3);`;const DownloadLink = styled(motion.a)` color: #1e40af; font-weight: 600; margin: 1rem 0; word-break: break-all; text-decoration: none; padding: 1rem; background: rgba(255, 255, 255, 0.9); border-radius: 1rem; border: 2px solid #e2e8f0; transition: all 0.3s ease; &:hover { background: white; border-color: #667eea; transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); }`;const CopyButton = styled(motion.button)` padding: 0.8rem 2rem; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white; border: none; border-radius: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(245, 158, 11, 0.3); &:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(245, 158, 11, 0.4); }`;const ShareCode = styled(motion.div)` background: linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%); color: white; font-size: 1.3rem; font-weight: 800; padding: 1rem 2rem; border-radius: 1rem; letter-spacing: 0.2em; box-shadow: 0 8px 25px rgba(14, 165, 233, 0.3);`;const QRWrapper = styled(motion.div)` background: white; padding: 2rem; border-radius: 1.5rem; display: flex; flex-direction: column; align-items: center; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); border: 2px solid #e2e8f0;`;const ErrorMsg = styled(motion.div)` color: #dc2626; font-weight: 600; padding: 1rem 2rem; background: rgba(220, 38, 38, 0.1); border-radius: 1rem; border: 2px solid rgba(220, 38, 38, 0.2); text-align: center;`;const LoadingSpinner = styled(motion.div)` width: 20px; height: 20px; border: 2px solid rgba(255, 255, 255, 0.3); border-top: 2px solid white; border-radius: 50%; animation: spin 1s linear infinite; margin-right: 0.5rem; @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }`;export default function Home() { const fileInputRef = useRef(); const [file, setFile] = useState(null); const [fileName, setFileName] = useState(""); const [fileSize, setFileSize] = useState(""); const [uploading, setUploading] = useState(false); const [success, setSuccess] = useState(""); const [downloadUrl, setDownloadUrl] = useState(""); const [error, setError] = useState(""); const [shareCode, setShareCode] = useState(""); const [copied, setCopied] = useState(false); const [password, setPassword] = useState(""); const [dragOver, setDragOver] = useState(false); const formatFileSize = (bytes) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const handleFileChange = (e) => { const f = e.target.files[0]; if (f) { setFile(f); setFileName(f.name); setFileSize(formatFileSize(f.size)); setSuccess(""); setDownloadUrl(""); setError(""); setShareCode(""); setCopied(false); } }; const handleDragOver = (e) => { e.preventDefault(); setDragOver(true); }; const handleDragLeave = (e) => { e.preventDefault(); setDragOver(false); }; const handleDrop = (e) => { e.preventDefault(); setDragOver(false); const files = e.dataTransfer.files; if (files.length > 0) { const f = files[0]; setFile(f); setFileName(f.name); setFileSize(formatFileSize(f.size)); setSuccess(""); setDownloadUrl(""); setError(""); setShareCode(""); setCopied(false); } }; const handleUpload = async () => { if (!file) return; setUploading(true); setSuccess(""); setDownloadUrl(""); setError(""); setShareCode(""); setCopied(false); try { const uniqueName = `${uuidv4()}-${file.name}`; const url = await uploadFile(file, uniqueName); setSuccess("File uploaded successfully!"); setDownloadUrl(url); const code = nanoid(6); setShareCode(code); // Firestore save const expiresAt = Date.now() + 7 * 24 * 60 * 60 * 1000; let passwordHash = undefined; if (password) { passwordHash = await bcrypt.hash(password, 8); } await addDoc(collection(db, "files"), { code, url, filename: file.name, createdAt: serverTimestamp(), expiresAt, ...(passwordHash ? { passwordHash } : {}), }); } catch (err) { setError("Upload failed. Please try again."); } finally { setUploading(false); } }; const handleCopy = () => { if (downloadUrl) { navigator.clipboard.writeText(downloadUrl); setCopied(true); setTimeout(() => setCopied(false), 1500); } }; return ( <Container> <Card initial={{ opacity: 0, y: 50 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, ease: "easeOut" }} > <Title initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.2, duration: 0.6 }} > DropLink </Title> <Subtitle initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.4, duration: 0.6 }} > Upload a file to generate a download link, QR code, and access code. </Subtitle> <UploadArea className={dragOver ? 'drag-over' : ''} onDragOver={handleDragOver} onDragLeave={handleDragLeave} onDrop={handleDrop} onClick={() => fileInputRef.current?.click()} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.6, duration: 0.6 }} whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} > <UploadIcon>📁</UploadIcon> <FileLabel htmlFor="file-upload"> {fileName ? "Change File" : "Choose a file or drag & drop"} </FileLabel> <FileInput id="file-upload" type="file" ref={fileInputRef} onChange={handleFileChange} /> </UploadArea> <AnimatePresence> {fileName && ( <FileInfo initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: "auto" }} exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.3 }} > <FileName>{fileName}</FileName> <FileSize>{fileSize}</FileSize> </FileInfo> )} </AnimatePresence> <Input type="password" placeholder="Optional password protection" value={password} onChange={e => setPassword(e.target.value)} autoComplete="new-password" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.8, duration: 0.6 }} /> <UploadButton onClick={handleUpload} disabled={!file || uploading} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 1, duration: 0.6 }} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {uploading ? ( <> <LoadingSpinner /> Uploading... </> ) : ( "Upload File" )} </UploadButton> <AnimatePresence> {success && ( <SuccessCard initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.4 }} > <motion.div initial={{ rotate: -180, scale: 0 }} animate={{ rotate: 0, scale: 1 }} transition={{ delay: 0.2, duration: 0.6 }} style={{ fontSize: '3rem', marginBottom: '1rem' }} > ✅ </motion.div> {success} </SuccessCard> )} </AnimatePresence> <AnimatePresence> {downloadUrl && ( <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.4 }} style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem' }} > <DownloadLink href={downloadUrl} target="_blank" rel="noopener noreferrer" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} > {downloadUrl} </DownloadLink> <CopyButton onClick={handleCopy} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {copied ? "Copied! 🎉" : "Copy Link"} </CopyButton> {shareCode && ( <ShareCode initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.2, duration: 0.4 }} > Share Code: {shareCode} </ShareCode> )} <QRWrapper initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.4, duration: 0.4 }} whileHover={{ scale: 1.05 }} > <QRCodeCanvas value={downloadUrl} size={180} /> </QRWrapper> </motion.div> )} </AnimatePresence> <AnimatePresence> {error && ( <ErrorMsg initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.4 }} > {error} </ErrorMsg> )} </AnimatePresence> </Card> </Container> );} improve the design and make it more professional
I'll help you create a more professional and modern design for your DropLink file upload service. Let me convert this to use Tailwind CSS with shadcn/ui components and implement a cleaner, more professional design system.
I've completely redesigned your DropLink application with a more professional and modern approach. Here are the key improvements:
🎨 Visual Design:
🔧 Technical Improvements:
✨ User Experience:
🎯 Professional Features:
The new design maintains all your original functionality while presenting it in a much more professional and user-friendly way. The interface now feels like a modern SaaS application that users would trust with their files.