AdminModal.tsx 10.5 KB
import React, { useState, useEffect, useRef } from 'react';
import { ImageItem } from '../types';
import { X, Upload, Save, Trash2, ThumbsUp } from 'lucide-react';

interface AdminModalProps {
  isOpen: boolean;
  onClose: () => void;
  onSave: (image: ImageItem) => void;
  onDelete?: (id: string) => void;
  initialData?: ImageItem | null;
}

const AdminModal: React.FC<AdminModalProps> = ({ isOpen, onClose, onSave, onDelete, initialData }) => {
  const fileInputRef = useRef<HTMLInputElement>(null);
  
  const [formData, setFormData] = useState<Partial<ImageItem>>({
    prompt: '',
    width: 1024,
    height: 1024,
    num_inference_steps: 20,
    guidance_scale: 7.5,
    seed: 12345,
    url: '',
    likes: 0,
  });

  useEffect(() => {
    if (isOpen) {
      if (initialData) {
        setFormData({ ...initialData });
      } else {
        // Reset form for new entry
        setFormData({
          prompt: '',
          width: 1024,
          height: 1024,
          num_inference_steps: 20,
          guidance_scale: 7.5,
          seed: Math.floor(Math.random() * 1000000),
          url: '',
          likes: 0,
        });
      }
    }
  }, [isOpen, initialData]);

  if (!isOpen) return null;

  const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setFormData(prev => ({ ...prev, url: reader.result as string }));
      };
      reader.readAsDataURL(file);
    }
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!formData.url) {
      alert("Please upload an image or provide a URL");
      return;
    }

    const imageItem: ImageItem = {
      id: initialData?.id || Date.now().toString(),
      createdAt: initialData?.createdAt || Date.now(),
      prompt: formData.prompt || 'Untitled',
      width: Number(formData.width),
      height: Number(formData.height),
      num_inference_steps: Number(formData.num_inference_steps),
      guidance_scale: Number(formData.guidance_scale),
      seed: Number(formData.seed),
      url: formData.url,
      isMock: true, 
      likes: Number(formData.likes || 0), // Admin Override Likes
      authorId: initialData?.authorId || 'ADMIN',
      isLikedByCurrentUser: initialData?.isLikedByCurrentUser,
    };

    onSave(imageItem);
    onClose();
  };

  return (
    <div className="fixed inset-0 z-[110] flex items-center justify-center p-4">
      <div className="absolute inset-0 bg-black/80 backdrop-blur-sm" onClick={onClose} />
      
      <div className="relative bg-white dark:bg-gray-900 w-full max-w-2xl rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-[90vh]">
        <div className="flex justify-between items-center p-6 border-b border-gray-100 dark:border-gray-800">
          <h2 className="text-xl font-bold text-gray-800 dark:text-white">
            {initialData ? 'Edit Image Details' : 'Upload New Image'}
          </h2>
          <button onClick={onClose} className="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full">
            <X size={20} />
          </button>
        </div>

        <div className="overflow-y-auto p-6">
          <form id="admin-form" onSubmit={handleSubmit} className="space-y-6">
            {/* Image Upload/URL Section */}
            <div className="space-y-3">
               <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">Image Source</label>
               
               {/* Preview */}
               <div className="flex flex-col items-center justify-center">
                <div 
                  className="relative w-full h-64 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-xl flex flex-col items-center justify-center bg-gray-50 dark:bg-gray-800 cursor-pointer hover:bg-gray-100 transition-colors overflow-hidden group"
                  onClick={() => fileInputRef.current?.click()}
                >
                  {formData.url ? (
                    <img src={formData.url} alt="Preview" className="w-full h-full object-contain" />
                  ) : (
                    <div className="flex flex-col items-center text-gray-400">
                      <Upload size={32} className="mb-2" />
                      <p className="text-sm">Click to upload or paste URL below</p>
                    </div>
                  )}
                  <div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-opacity">
                     <p className="text-white font-medium">Change Image</p>
                  </div>
                </div>
                <input 
                  type="file" 
                  ref={fileInputRef} 
                  onChange={handleImageUpload} 
                  accept="image/*" 
                  className="hidden" 
                />
              </div>

              {/* URL Input */}
              <input 
                type="text"
                value={formData.url}
                onChange={(e) => setFormData({...formData, url: e.target.value})}
                placeholder="Or paste image URL here..."
                className="w-full p-2 text-sm bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg"
              />
            </div>

            {/* Prompt */}
            <div>
              <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Prompt</label>
              <textarea 
                value={formData.prompt}
                onChange={(e) => setFormData({...formData, prompt: e.target.value})}
                className="w-full p-3 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-black dark:focus:ring-white outline-none min-h-[100px]"
                placeholder="Enter the prompt used for this image..."
                required
              />
            </div>

            {/* Parameters Grid */}
            <div className="grid grid-cols-2 gap-4">
              <div>
                <label className="block text-xs font-semibold text-gray-500 uppercase mb-1">Width</label>
                <input 
                  type="number" 
                  value={formData.width}
                  onChange={(e) => setFormData({...formData, width: Number(e.target.value)})}
                  className="w-full p-2 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
                />
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-500 uppercase mb-1">Height</label>
                <input 
                  type="number" 
                  value={formData.height}
                  onChange={(e) => setFormData({...formData, height: Number(e.target.value)})}
                  className="w-full p-2 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
                />
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-500 uppercase mb-1">Steps</label>
                <input 
                  type="number" 
                  value={formData.num_inference_steps}
                  onChange={(e) => setFormData({...formData, num_inference_steps: Number(e.target.value)})}
                  className="w-full p-2 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
                />
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-500 uppercase mb-1">Guidance Scale</label>
                <input 
                  type="number" 
                  step="0.1"
                  value={formData.guidance_scale}
                  onChange={(e) => setFormData({...formData, guidance_scale: Number(e.target.value)})}
                  className="w-full p-2 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
                />
              </div>
              <div>
                <label className="block text-xs font-semibold text-gray-500 uppercase mb-1">Seed</label>
                <input 
                  type="number" 
                  value={formData.seed}
                  onChange={(e) => setFormData({...formData, seed: Number(e.target.value)})}
                  className="w-full p-2 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
                />
              </div>
              
              {/* ADMIN ONLY: Likes Editor */}
              <div>
                <label className="block text-xs font-bold text-purple-600 uppercase mb-1 flex items-center gap-1">
                   <ThumbsUp size={12} /> Likes (Admin Override)
                </label>
                <input 
                  type="number" 
                  value={formData.likes}
                  onChange={(e) => setFormData({...formData, likes: Number(e.target.value)})}
                  className="w-full p-2 bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded font-bold text-purple-700 dark:text-purple-300"
                />
              </div>
            </div>
          </form>
        </div>

        <div className="p-6 border-t border-gray-100 dark:border-gray-800 flex justify-between bg-gray-50 dark:bg-gray-900">
           {initialData && onDelete ? (
             <button 
               type="button"
               onClick={() => {
                 if(confirm('Are you sure you want to delete this image?')) {
                   onDelete(initialData.id);
                   onClose();
                 }
               }}
               className="flex items-center gap-2 px-4 py-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
             >
               <Trash2 size={18} />
               <span>Delete</span>
             </button>
           ) : <div />}
           
           <div className="flex gap-3">
             <button 
               type="button" 
               onClick={onClose}
               className="px-6 py-2 rounded-lg text-gray-600 hover:bg-gray-200 transition-colors"
             >
               Cancel
             </button>
             <button 
               type="submit" 
               form="admin-form"
               className="flex items-center gap-2 px-6 py-2 bg-black dark:bg-white text-white dark:text-black rounded-lg hover:opacity-90 transition-opacity font-medium"
             >
               <Save size={18} />
               <span>Save Image</span>
             </button>
           </div>
        </div>
      </div>
    </div>
  );
};

export default AdminModal;