'use client' import { useState, useRef, useCallback } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { ProjectLogo } from './project-logo' import { Upload, Loader2, Trash2, ImagePlus } from 'lucide-react' import { trpc } from '@/lib/trpc/client' import { toast } from 'sonner' type LogoUploadProps = { project: { id: string title: string logoKey?: string | null } currentLogoUrl?: string | null onUploadComplete?: () => void children?: React.ReactNode } const MAX_SIZE_MB = 5 const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'] export function LogoUpload({ project, currentLogoUrl, onUploadComplete, children, }: LogoUploadProps) { const [open, setOpen] = useState(false) const [preview, setPreview] = useState(null) const [selectedFile, setSelectedFile] = useState(null) const [isUploading, setIsUploading] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const fileInputRef = useRef(null) const utils = trpc.useUtils() const getUploadUrl = trpc.logo.getUploadUrl.useMutation() const confirmUpload = trpc.logo.confirmUpload.useMutation() const deleteLogo = trpc.logo.delete.useMutation() const handleFileSelect = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return // Validate type if (!ALLOWED_TYPES.includes(file.type)) { toast.error('Invalid file type. Please upload a JPEG, PNG, GIF, or WebP image.') return } // Validate size if (file.size > MAX_SIZE_MB * 1024 * 1024) { toast.error(`File too large. Maximum size is ${MAX_SIZE_MB}MB.`) return } setSelectedFile(file) // Create preview const reader = new FileReader() reader.onload = (e) => { setPreview(e.target?.result as string) } reader.readAsDataURL(file) }, []) const handleUpload = async () => { if (!selectedFile) return setIsUploading(true) try { // Get pre-signed upload URL (includes provider type for tracking) const { uploadUrl, key, providerType } = await getUploadUrl.mutateAsync({ projectId: project.id, fileName: selectedFile.name, contentType: selectedFile.type, }) // Upload file directly to storage const uploadResponse = await fetch(uploadUrl, { method: 'PUT', body: selectedFile, headers: { 'Content-Type': selectedFile.type, }, }) if (!uploadResponse.ok) { throw new Error('Failed to upload file') } // Confirm upload with the provider type that was used await confirmUpload.mutateAsync({ projectId: project.id, key, providerType }) // Invalidate logo query utils.logo.getUrl.invalidate({ projectId: project.id }) toast.success('Logo updated successfully') setOpen(false) setPreview(null) setSelectedFile(null) onUploadComplete?.() } catch (error) { console.error('Upload error:', error) toast.error('Failed to upload logo. Please try again.') } finally { setIsUploading(false) } } const handleDelete = async () => { setIsDeleting(true) try { await deleteLogo.mutateAsync({ projectId: project.id }) utils.logo.getUrl.invalidate({ projectId: project.id }) toast.success('Logo removed') setOpen(false) onUploadComplete?.() } catch (error) { console.error('Delete error:', error) toast.error('Failed to remove logo') } finally { setIsDeleting(false) } } const handleCancel = () => { setPreview(null) setSelectedFile(null) setOpen(false) } return ( {children || ( )} Update Project Logo Upload a logo for "{project.title}". Allowed formats: JPEG, PNG, GIF, WebP. Max size: {MAX_SIZE_MB}MB.
{/* Preview */}
{/* File input */}
{currentLogoUrl && !preview && ( )}
) }