bookwiz.io / components / EditBookDialog.tsx
EditBookDialog.tsx
Raw
'use client'

import { useState, useEffect, useMemo, useCallback } from 'react'
import { XMarkIcon, BookOpenIcon, TagIcon, PencilIcon, UserIcon, PhotoIcon, ArrowUpTrayIcon, LinkIcon, ChartBarIcon, CheckCircleIcon, ArrowTopRightOnSquareIcon, SparklesIcon, ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/24/outline'
import { useAuth } from '@/components/AuthProvider'
import Image from 'next/image'
import { supabase } from '@/lib/supabase'
import { useImageGeneration, type GeneratedImage } from '@/lib/hooks/useImageGeneration'

interface Book {
  id: string
  title: string
  description: string | null
  author?: string | null
  cover_image_url?: string | null
  genre?: string | null
  target_word_count?: number | null
  status: string
}

interface EditBookDialogProps {
  isOpen: boolean
  onClose: () => void
  onSubmit: (bookData: { title: string; description?: string; author?: string; cover_image_url?: string; genre?: string; target_word_count?: number; status?: string }) => void
  loading: boolean
  book: Book | null
}

interface ImageOptionProps {
  image: GeneratedImage
  isSelected: boolean
  onSelect: () => void
}

function ImageOption({ image, isSelected, onSelect }: ImageOptionProps) {
  const [isLoading, setIsLoading] = useState(true)
  
  return (
    <button
      type="button"
      onClick={onSelect}
      className={`relative aspect-square rounded-lg overflow-hidden border-2 transition-all duration-200 ${
        isSelected 
          ? 'border-teal-500 ring-2 ring-teal-500/50' 
          : 'border-slate-600 hover:border-slate-500'
      }`}
    >
      {isLoading && (
        <div className="absolute inset-0 bg-slate-700/50 animate-pulse flex items-center justify-center">
          <div className="w-6 h-6 bg-white/20 rounded animate-pulse"></div>
        </div>
      )}
      <Image
        src={image.image_url}
        alt={image.prompt}
        fill
        className="object-cover"
        onLoad={() => setIsLoading(false)}
        onError={() => setIsLoading(false)}
      />
      {isSelected && (
        <div className="absolute inset-0 bg-teal-500/20 flex items-center justify-center">
          <div className="w-6 h-6 rounded-full bg-teal-500 flex items-center justify-center">
            <svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
              <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
            </svg>
          </div>
        </div>
      )}
    </button>
  )
}

export default function EditBookDialog({ isOpen, onClose, onSubmit, loading, book }: EditBookDialogProps) {
  const { user } = useAuth()
  const [title, setTitle] = useState('')
  const [description, setDescription] = useState('')
  const [author, setAuthor] = useState('')
  const [coverImageUrl, setCoverImageUrl] = useState<string | null>(null)
  const [selectedGenres, setSelectedGenres] = useState<string[]>([])
  const [targetWordCount, setTargetWordCount] = useState<number | null>(null)
  const [status, setStatus] = useState('')
  const [showUrlInput, setShowUrlInput] = useState(false)
  const [urlInput, setUrlInput] = useState('')
  const [isUploading, setIsUploading] = useState(false)
  const [showImageSelector, setShowImageSelector] = useState(false)
  const [prompt, setPrompt] = useState('')
  const [isCoverExpanded, setIsCoverExpanded] = useState(false)
  const [isGenerateExpanded, setIsGenerateExpanded] = useState(false)

  const { images, isGenerating, generateImage } = useImageGeneration()

  const availableGenres = useMemo(() => [
    'Fantasy', 'Science Fiction', 'Mystery', 'Romance', 'Thriller', 'Horror',
    'Historical Fiction', 'Contemporary Fiction', 'Young Adult', "Children's",
    'Biography', 'Memoir', 'Self-Help', 'Business', 'Health', 'Travel',
    'Poetry', 'Drama', 'Comedy', 'Adventure', 'Crime', 'Western',
    'Dystopian', 'Paranormal', 'Urban Fantasy', 'Epic Fantasy'
  ], [])

  const sortedGenres = useMemo(() => {
    return [
      ...selectedGenres,
      ...availableGenres.filter(genre => !selectedGenres.includes(genre))
    ]
  }, [selectedGenres, availableGenres])

  const wordCountOptions = [
    { value: 1000, label: '๐Ÿ“– Flash Fiction', description: '~1,000 words' },
    { value: 7500, label: '๐Ÿ“š Short Story', description: '~7,500 words' },
    { value: 17500, label: '๐Ÿ“‘ Novelette', description: '~17,500 words' },
    { value: 40000, label: '๐Ÿ“˜ Novella', description: '~40,000 words' },
    { value: 80000, label: '๐Ÿ“• Novel', description: '~80,000 words' },
    { value: 120000, label: '๐Ÿ“— Epic Novel', description: '~120,000 words' },
    { value: null, label: 'โœจ Custom Target', description: 'Set your own goal' }
  ]

  const statusOptions = [
    { value: 'Planning', label: '๐ŸŽฏ Planning', description: 'Outlining and brainstorming' },
    { value: 'draft', label: 'โœ๏ธ First Draft', description: 'Getting words on paper' },
    { value: 'in_progress', label: '๐Ÿ“ In Progress', description: 'Actively writing' },
    { value: 'completed', label: 'โœ… Completed', description: 'Ready for review' },
    { value: 'published', label: '๐Ÿš€ Published', description: 'Live for the world to see' }
  ]

  // Reset form when book changes or dialog opens
  useEffect(() => {
    if (book && isOpen) {
      setTitle(book.title || '')
      setDescription(book.description || '')
      setAuthor(book.author || '')
      setCoverImageUrl(book.cover_image_url || null)
      
      // Parse genres from string to array
      if (book.genre) {
        const genresArray = book.genre.split(',').map(g => g.trim()).filter(g => g.length > 0)
        setSelectedGenres(genresArray)
      } else {
        setSelectedGenres([])
      }
      
      setTargetWordCount(book.target_word_count || null)
      setStatus(book.status || 'draft')
    }
  }, [book, isOpen])

  // Body scroll lock effect
  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = 'hidden'
    } else {
      document.body.style.overflow = 'unset'
    }
    
    return () => {
      document.body.style.overflow = 'unset'
    }
  }, [isOpen])

  const handleGenreClick = useCallback((genre: string) => {
    setSelectedGenres(prev => 
      prev.includes(genre) 
        ? prev.filter(g => g !== genre)
        : [...prev, genre]
    )
  }, [])

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    
    if (!title.trim()) return

    const bookData = {
      title: title.trim(),
      description: description.trim() || undefined,
      author: author.trim() || undefined,
      cover_image_url: coverImageUrl || undefined,
      genre: selectedGenres.length > 0 ? selectedGenres.join(', ') : undefined,
      target_word_count: targetWordCount || undefined,
      status: status || undefined
    }

    onSubmit(bookData)
  }

  const resetForm = () => {
    setTitle('')
    setDescription('')
    setAuthor('')
    setCoverImageUrl(null)
    setSelectedGenres([])
    setTargetWordCount(null)
    setStatus('')
    setShowUrlInput(false)
    setUrlInput('')
    setShowImageSelector(false)
    setPrompt('')
    setIsCoverExpanded(false)
    setIsGenerateExpanded(false)
  }

  const handleClose = () => {
    if (!loading) {
      resetForm()
      onClose()
    }
  }

  const handleUrlSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (urlInput.trim()) {
      setCoverImageUrl(urlInput.trim())
      setUrlInput('')
      setShowUrlInput(false)
      setShowImageSelector(false)
    }
  }

  const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]
    if (!file) return

    if (!file.type.startsWith('image/')) {
      alert('Please select an image file')
      return
    }

    if (file.size > 5 * 1024 * 1024) {
      alert('Image size must be less than 5MB')
      return
    }

    try {
      setIsUploading(true)

      const fileExt = file.name.split('.').pop()
      const fileName = `${Date.now()}-${Math.random().toString(36).substring(2)}.${fileExt}`
      const filePath = user?.id ? `book-covers/${user.id}/${fileName}` : `book-covers/${fileName}`

      const { data, error } = await supabase.storage
        .from('book-covers')
        .upload(filePath, file, {
          cacheControl: '3600',
          upsert: false
        })

      if (error) {
        console.error('Upload error:', error)
        alert('Failed to upload image. Please try again.')
        return
      }

      const { data: { publicUrl } } = supabase.storage
        .from('book-covers')
        .getPublicUrl(data.path)

      setCoverImageUrl(publicUrl)
      setShowImageSelector(false)

    } catch (error) {
      console.error('Upload error:', error)
      alert('Failed to upload image. Please try again.')
    } finally {
      setIsUploading(false)
      if (event.target) {
        event.target.value = ''
      }
    }
  }

  const handleGenerateImage = async (e: React.FormEvent) => {
    e.preventDefault()
    if (!prompt.trim() || isGenerating) return
    
    const generatedImage = await generateImage(prompt, book?.id)
    if (generatedImage) {
      setCoverImageUrl(generatedImage.image_url)
      setPrompt('')
      setShowImageSelector(false)
    }
  }

  const openImagesPage = () => {
    window.open('/dashboard/images', '_blank')
  }

  if (!isOpen) return null

  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
      <div className="bg-slate-900 rounded-2xl shadow-2xl w-full max-w-2xl border border-slate-700/50 overflow-hidden flex flex-col"
           style={{ 
             maxHeight: '85vh'
           }}>
        
        {/* Header */}
        <div className="relative bg-slate-800/50 border-b border-slate-700/50 flex-shrink-0">
          <div className="flex items-center justify-between p-4">
            <div className="flex items-center space-x-3">
              <div className="p-1.5 bg-gradient-to-r from-teal-500 to-purple-500 rounded-lg">
                <BookOpenIcon className="h-5 w-5 text-white" />
              </div>
              <h2 className="text-xl font-semibold text-white">Edit Book</h2>
            </div>
            <button
              onClick={handleClose}
              className="p-1.5 text-slate-400 hover:text-white hover:bg-slate-700/50 rounded-lg transition-colors duration-150"
              disabled={loading}
            >
              <XMarkIcon className="h-5 w-5" />
            </button>
          </div>
        </div>

        {/* Scrollable Content */}
        <div className="flex-1 overflow-y-auto">
          <div className="p-6">
            <form onSubmit={handleSubmit} className="space-y-6">
              {/* Cover Image Section - First */}
              <div className="space-y-4">
                {/* Current Image Display - Centered */}
                <div className="flex justify-center">
                  <div className={`relative w-32 h-40 rounded-lg border-2 border-dashed overflow-hidden transition-all duration-200 ${
                    coverImageUrl 
                      ? 'border-slate-600 bg-slate-800/50' 
                      : 'border-slate-600 bg-slate-800/30'
                  }`}>
                    {coverImageUrl ? (
                      <>
                        <Image
                          src={coverImageUrl}
                          alt="Book cover"
                          fill
                          className="object-cover"
                        />
                        <button
                          type="button"
                          onClick={() => setCoverImageUrl(null)}
                          className="absolute -top-1 -right-1 w-5 h-5 bg-red-500 hover:bg-red-600 rounded-full flex items-center justify-center transition-colors"
                          title="Remove cover image"
                        >
                          <XMarkIcon className="w-3 h-3 text-white" />
                        </button>
                      </>
                    ) : (
                      <div className="w-full h-full flex items-center justify-center">
                        <PhotoIcon className="w-12 h-12 text-slate-500" />
                      </div>
                    )}
                  </div>
                </div>

                {/* Action Buttons - Side by Side */}
                <div className="flex space-x-3">
                  <button
                    type="button"
                    onClick={() => setIsCoverExpanded(!isCoverExpanded)}
                    className="flex-1 flex items-center justify-center space-x-2 px-4 py-3 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white rounded-xl transition-all duration-200 font-medium shadow-lg hover:shadow-xl"
                  >
                    <PhotoIcon className="w-4 h-4" />
                    <span>Change Cover</span>
                  </button>
                  
                  <button
                    type="button"
                    onClick={openImagesPage}
                    className="flex-1 flex items-center justify-center space-x-2 px-4 py-3 bg-slate-800/50 border border-slate-600 hover:bg-slate-700/50 hover:border-slate-500 text-slate-300 hover:text-white rounded-xl transition-all duration-200 font-medium"
                  >
                    <SparklesIcon className="w-4 h-4" />
                    <span>AI Images</span>
                    <ArrowTopRightOnSquareIcon className="w-3 h-3" />
                  </button>
                </div>

                {/* Collapsible Cover Options */}
                {isCoverExpanded && (
                  <div className="bg-slate-800/30 border border-slate-700/50 rounded-xl p-4 space-y-4">
                    {/* Generated Images Grid */}
                    <div className="space-y-3">
                      <div className="flex items-center justify-between">
                        <h4 className="text-sm font-semibold text-white">Your Generated Images</h4>
                        <button
                          type="button"
                          onClick={() => setIsGenerateExpanded(!isGenerateExpanded)}
                          className="text-xs text-slate-400 hover:text-white transition-colors"
                        >
                          {isGenerateExpanded ? 'Hide Generator' : 'Generate New'}
                        </button>
                      </div>
                      
                      {images.length > 0 ? (
                        <div className="grid grid-cols-3 gap-2">
                          {images.slice(0, 6).map((image) => (
                            <ImageOption
                              key={image.id}
                              image={image}
                              isSelected={coverImageUrl === image.image_url}
                              onSelect={() => {
                                setCoverImageUrl(image.image_url)
                                setIsCoverExpanded(false)
                              }}
                            />
                          ))}
                        </div>
                      ) : (
                        <div className="text-center py-4">
                          <SparklesIcon className="w-8 h-8 text-slate-500 mx-auto mb-2" />
                          <p className="text-sm text-slate-400">No generated images yet</p>
                        </div>
                      )}
                    </div>

                    {/* Generate New Image */}
                    {isGenerateExpanded && (
                      <div className="space-y-3 pt-3 border-t border-slate-700/50">
                        <h4 className="text-sm font-semibold text-white">Generate New Image</h4>
                        <form onSubmit={handleGenerateImage} className="space-y-3">
                          <textarea
                            value={prompt}
                            onChange={(e) => setPrompt(e.target.value)}
                            placeholder="Describe your book cover... (e.g., 'Fantasy novel cover with dragons and mountains')"
                            rows={2}
                            className="w-full px-3 py-2 bg-slate-800/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 resize-none focus:outline-none focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 text-sm"
                            disabled={isGenerating || loading}
                          />
                          <button
                            type="submit"
                            disabled={!prompt.trim() || isGenerating || loading}
                            className="w-full flex items-center justify-center space-x-2 px-4 py-2 bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white rounded-lg transition-all duration-200 font-medium disabled:opacity-50 disabled:cursor-not-allowed"
                          >
                            {isGenerating ? (
                              <>
                                <div className="animate-spin rounded-full h-4 w-4 border-2 border-white border-t-transparent"></div>
                                <span>Generating...</span>
                              </>
                            ) : (
                              <>
                                <SparklesIcon className="w-4 h-4" />
                                <span>Generate Image</span>
                              </>
                            )}
                          </button>
                        </form>
                      </div>
                    )}

                    {/* Upload Options */}
                    <div className="space-y-3 pt-3 border-t border-slate-700/50">
                      <h4 className="text-sm font-semibold text-white">Upload Your Own</h4>
                      <div className="flex space-x-2">
                        <button
                          type="button"
                          onClick={() => document.getElementById('file-upload')?.click()}
                          disabled={isUploading || loading}
                          className="flex-1 flex items-center justify-center space-x-2 px-3 py-2 bg-slate-800/50 border border-slate-600 rounded-lg text-slate-300 hover:bg-slate-700/50 hover:border-slate-500 transition-colors disabled:opacity-50 text-sm"
                        >
                          <ArrowUpTrayIcon className="w-4 h-4" />
                          <span>{isUploading ? 'Uploading...' : 'Upload File'}</span>
                        </button>
                        
                        <button
                          type="button"
                          onClick={() => setShowUrlInput(!showUrlInput)}
                          className="flex-1 flex items-center justify-center space-x-2 px-3 py-2 bg-slate-800/50 border border-slate-600 rounded-lg text-slate-300 hover:bg-slate-700/50 hover:border-slate-500 transition-colors text-sm"
                        >
                          <LinkIcon className="w-4 h-4" />
                          <span>Add URL</span>
                        </button>
                      </div>

                      {showUrlInput && (
                        <form onSubmit={handleUrlSubmit} className="flex space-x-2">
                          <input
                            type="url"
                            value={urlInput}
                            onChange={(e) => setUrlInput(e.target.value)}
                            placeholder="https://example.com/image.jpg"
                            className="flex-1 px-3 py-2 bg-slate-800/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500"
                          />
                          <button
                            type="submit"
                            className="px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors text-sm"
                          >
                            Add
                          </button>
                        </form>
                      )}
                    </div>
                  </div>
                )}

                <input
                  id="file-upload"
                  type="file"
                  accept="image/*"
                  onChange={handleFileUpload}
                  className="hidden"
                />
              </div>

              {/* Title Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <PencilIcon className="h-4 w-4 text-teal-400" />
                  <label className="text-base font-semibold text-white">Book Title</label>
                </div>
                <input
                  type="text"
                  value={title}
                  onChange={(e) => setTitle(e.target.value)}
                  className="w-full px-4 py-3 bg-slate-800/50 border border-slate-600 rounded-xl text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-teal-500/50 focus:border-teal-500 transition-colors duration-150"
                  placeholder="Enter your book title..."
                  required
                  disabled={loading}
                />
              </div>

              {/* Description Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <BookOpenIcon className="h-4 w-4 text-purple-400" />
                  <label className="text-base font-semibold text-white">Description</label>
                  <span className="text-sm text-slate-400">(optional)</span>
                </div>
                <textarea
                  value={description}
                  onChange={(e) => setDescription(e.target.value)}
                  rows={3}
                  className="w-full px-4 py-3 bg-slate-800/50 border border-slate-600 rounded-xl text-white placeholder-slate-400 resize-none focus:outline-none focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 transition-colors duration-150"
                  placeholder="What's your book about? A brief description helps organize your thoughts..."
                  disabled={loading}
                />
              </div>

              {/* Author Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <UserIcon className="h-4 w-4 text-amber-400" />
                  <label className="text-base font-semibold text-white">Author</label>
                  <span className="text-sm text-slate-400">(optional)</span>
                </div>
                <input
                  type="text"
                  value={author}
                  onChange={(e) => setAuthor(e.target.value)}
                  className="w-full px-4 py-3 bg-slate-800/50 border border-slate-600 rounded-xl text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-amber-500/50 focus:border-amber-500 transition-colors duration-150"
                  placeholder="Pen name for this book..."
                  disabled={loading}
                />
              </div>

              {/* Genres Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <TagIcon className="h-4 w-4 text-cyan-400" />
                  <label className="text-base font-semibold text-white">Genres</label>
                  <span className="text-sm text-slate-400">(optional)</span>
                  {selectedGenres.length > 0 && (
                    <span className="px-2 py-1 bg-gradient-to-r from-cyan-500/20 to-teal-500/20 text-cyan-400 text-xs rounded-full border border-cyan-500/30">
                      {selectedGenres.length} selected
                    </span>
                  )}
                </div>
                <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2">
                  {sortedGenres.map((genre) => {
                    const isSelected = selectedGenres.includes(genre)
                    return (
                      <button
                        key={genre}
                        type="button"
                        onClick={() => handleGenreClick(genre)}
                        disabled={loading}
                        className={`px-3 py-2 text-sm rounded-lg border transition-colors duration-150 text-left font-medium ${
                          isSelected
                            ? 'bg-gradient-to-r from-teal-600 to-cyan-600 border-teal-500 text-white'
                            : 'bg-slate-800/50 border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500 hover:text-white'
                        }`}
                      >
                        {genre}
                      </button>
                    )
                  })}
                </div>
              </div>

              {/* Target Word Count Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <ChartBarIcon className="h-4 w-4 text-emerald-400" />
                  <label className="text-base font-semibold text-white">Writing Goal</label>
                  <span className="text-sm text-slate-400">(optional)</span>
                </div>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                  {wordCountOptions.map((option, index) => {
                    const isSelected = targetWordCount === option.value
                    const isCustom = option.value === null
                    return (
                      <button
                        key={index}
                        type="button"
                        onClick={() => setTargetWordCount(option.value)}
                        disabled={loading}
                        className={`p-3 rounded-lg border transition-colors duration-150 text-left ${
                          isSelected
                            ? 'bg-gradient-to-r from-emerald-600 to-teal-600 border-emerald-500 text-white'
                            : 'bg-slate-800/50 border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500 hover:text-white'
                        }`}
                      >
                        <div className="font-medium text-sm">{option.label}</div>
                        <div className="text-xs opacity-75">{option.description}</div>
                      </button>
                    )
                  })}
                </div>
                
                {targetWordCount === null && (
                  <input
                    type="number"
                    value={targetWordCount || ''}
                    onChange={(e) => setTargetWordCount(e.target.value ? parseInt(e.target.value) : null)}
                    className="w-full px-4 py-3 bg-slate-800/50 border border-slate-600 rounded-xl text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-500/50 focus:border-emerald-500 transition-colors duration-150"
                    placeholder="Enter custom word count target..."
                    min="1"
                    disabled={loading}
                  />
                )}
              </div>

              {/* Status Section */}
              <div className="space-y-3">
                <div className="flex items-center space-x-2">
                  <CheckCircleIcon className="h-4 w-4 text-blue-400" />
                  <label className="text-base font-semibold text-white">Writing Status</label>
                </div>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
                  {statusOptions.map((option) => {
                    const isSelected = status === option.value
                    return (
                      <button
                        key={option.value}
                        type="button"
                        onClick={() => setStatus(option.value)}
                        disabled={loading}
                        className={`p-3 rounded-lg border transition-colors duration-150 text-left ${
                          isSelected
                            ? 'bg-gradient-to-r from-blue-600 to-purple-600 border-blue-500 text-white'
                            : 'bg-slate-800/50 border-slate-600 text-slate-300 hover:bg-slate-700/50 hover:border-slate-500 hover:text-white'
                        }`}
                      >
                        <div className="font-medium text-sm">{option.label}</div>
                        <div className="text-xs opacity-75">{option.description}</div>
                      </button>
                    )
                  })}
                </div>
              </div>
            </form>
          </div>
        </div>

        {/* Sticky Footer */}
        <div className="border-t border-slate-700/50 p-4 bg-slate-900/90 backdrop-blur-sm flex-shrink-0">
          <div className="flex justify-between items-center">
            <button
              type="button"
              onClick={handleClose}
              className="px-4 py-2 text-slate-400 hover:text-white hover:bg-slate-700/50 rounded-lg transition-colors duration-150 font-medium"
              disabled={loading}
            >
              Cancel
            </button>
            
            <button
              type="submit"
              onClick={handleSubmit}
              className="px-6 py-2 bg-gradient-to-r from-teal-600 to-cyan-600 hover:from-teal-700 hover:to-cyan-700 text-white rounded-lg transition-colors duration-150 font-semibold shadow-lg shadow-teal-500/25 disabled:opacity-50 disabled:cursor-not-allowed disabled:shadow-none flex items-center space-x-2"
              disabled={!title.trim() || loading}
            >
              {loading && (
                <div className="animate-spin rounded-full h-4 w-4 border-2 border-white border-t-transparent"></div>
              )}
              <span>{loading ? 'Updating...' : 'Update Book'}</span>
            </button>
          </div>
        </div>
      </div>
    </div>
  )
}