Menu

MEUS LINKS

dudurocha 4 dias ago 3
import React, { useState, useRef } from 'react';
import { UserProfile, LinkItem, SocialLink, SocialPlatform } from '../types';
import { Icon } from './Icons';
import { enhanceBio, generateLinkDescription } from '../services/gemini';

interface EditorModalProps {
  isOpen: boolean;
  onClose: () => void;
  profile: UserProfile;
  onSave: (profile: UserProfile) => void;
  initialTab?: 'content' | 'appearance';
}

const BG_PRESETS = [
  { name: 'Deep Space', url: 'https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=2072&auto=format&fit=crop' },
  { name: 'Neon City', url: 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?q=80&w=2070&auto=format&fit=crop' },
  { name: 'Cyber Mist', url: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=2564&auto=format&fit=crop' },
  { name: 'Dark Grid', url: 'https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=2070&auto=format&fit=crop' },
  { name: 'Minimal', url: '' }, // Empty string for black background
];

const AVAILABLE_PLATFORMS: { id: SocialPlatform; label: string }[] = [
  { id: 'whatsapp', label: 'WhatsApp' },
  { id: 'telegram', label: 'Telegram' },
  { id: 'instagram', label: 'Instagram' },
  { id: 'tiktok', label: 'TikTok' },
  { id: 'youtube', label: 'YouTube' },
  { id: 'facebook', label: 'Facebook' },
  { id: 'kwai', label: 'Kwai' },
  { id: 'linkedin', label: 'LinkedIn' },
  { id: 'twitter', label: 'Twitter' },
  { id: 'github', label: 'GitHub' },
  { id: 'mail', label: 'Email' },
];

export const EditorModal: React.FC<EditorModalProps> = ({ isOpen, onClose, profile, onSave, initialTab = 'content' }) => {
  const [editedProfile, setEditedProfile] = useState<UserProfile>(profile);
  const [activeTab, setActiveTab] = useState<'content' | 'appearance'>(initialTab);
  const [loadingAi, setLoadingAi] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const avatarInputRef = useRef<HTMLInputElement>(null);

  // Update internal state if initialTab changes when opening
  React.useEffect(() => {
    if (isOpen) {
      setActiveTab(initialTab);
      setEditedProfile(profile);
    }
  }, [isOpen, initialTab, profile]);

  if (!isOpen) return null;

  const handleLinkChange = (id: string, field: keyof LinkItem, value: any) => {
    const updatedLinks = editedProfile.links.map(link => 
      link.id === id ? { ...link, [field]: value } : link
    );
    setEditedProfile({ ...editedProfile, links: updatedLinks });
  };

  const addNewLink = () => {
    const newLink: LinkItem = {
      id: Math.random().toString(36).substr(2, 9),
      title: 'New Project',
      url: 'https://',
      description: 'Project description',
      icon: 'code'
    };
    setEditedProfile({ ...editedProfile, links: [...editedProfile.links, newLink] });
  };

  const removeLink = (id: string) => {
    setEditedProfile({ ...editedProfile, links: editedProfile.links.filter(l => l.id !== id) });
  };

  const handleAiBio = async () => {
    setLoadingAi('bio');
    const newBio = await enhanceBio(editedProfile.bio);
    setEditedProfile({ ...editedProfile, bio: newBio });
    setLoadingAi(null);
  };

  const handleAiDesc = async (id: string, title: string, url: string) => {
    setLoadingAi(id);
    const newDesc = await generateLinkDescription(title, url);
    handleLinkChange(id, 'description', newDesc);
    setLoadingAi(null);
  };

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

  const handleAvatarUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setEditedProfile({ ...editedProfile, avatarUrl: reader.result as string });
      };
      reader.readAsDataURL(file);
    }
  };

  const getSocialUrl = (platform: SocialPlatform) => {
    return editedProfile.socials.find(s => s.platform === platform)?.url || '';
  };

  const handleSocialChange = (platform: SocialPlatform, url: string) => {
    let newSocials = [...editedProfile.socials];
    // Remove existing entry for this platform
    newSocials = newSocials.filter(s => s.platform !== platform);
    
    // Add new entry if URL is not empty
    if (url.trim()) {
      newSocials.push({ platform, url: url.trim() });
    }
    
    // Sort slightly to keep order if needed, but not strictly required
    setEditedProfile({ ...editedProfile, socials: newSocials });
  };

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-sm">
      <div className="bg-slate-900 border border-slate-700 w-full max-w-2xl max-h-[90vh] overflow-hidden rounded-2xl shadow-2xl flex flex-col">
        {/* Header */}
        <div className="p-4 border-b border-slate-800 flex justify-between items-center bg-slate-900">
          <div className="flex space-x-4">
             <button 
               onClick={() => setActiveTab('content')}
               className={`text-sm font-semibold px-4 py-2 rounded-lg transition-colors ${activeTab === 'content' ? 'bg-cyan-500/10 text-cyan-400' : 'text-slate-400 hover:text-white'}`}
             >
               Content
             </button>
             <button 
               onClick={() => setActiveTab('appearance')}
               className={`text-sm font-semibold px-4 py-2 rounded-lg transition-colors ${activeTab === 'appearance' ? 'bg-cyan-500/10 text-cyan-400' : 'text-slate-400 hover:text-white'}`}
             >
               Appearance
             </button>
          </div>
          <button onClick={onClose} className="text-slate-400 hover:text-white">
            <Icon name="x" className="w-6 h-6" />
          </button>
        </div>

        <div className="flex-1 overflow-y-auto p-6 space-y-8">
          
          {activeTab === 'content' ? (
            <>
              {/* Profile Section */}
              <section className="space-y-6">
                <h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider">Identity</h3>
                
                <div className="flex flex-col md:flex-row gap-6 items-start">
                    {/* Avatar Uploader */}
                    <div className="shrink-0 flex flex-col items-center gap-2">
                         <div 
                            className="relative w-24 h-24 rounded-full overflow-hidden border-2 border-slate-700 group cursor-pointer hover:border-cyan-500 transition-all shadow-lg"
                            onClick={() => avatarInputRef.current?.click()}
                            title="Click to upload photo"
                         >
                            <img 
                              src={editedProfile.avatarUrl} 
                              alt="Profile" 
                              className="w-full h-full object-cover" 
                            />
                            <div className="absolute inset-0 bg-black/60 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity backdrop-blur-[2px]">
                               <Icon name="upload" className="w-6 h-6 text-cyan-400" />
                               <span className="text-[10px] text-white font-medium mt-1">Change</span>
                            </div>
                         </div>
                         <input 
                            type="file" 
                            ref={avatarInputRef} 
                            className="hidden" 
                            accept="image/*"
                            onChange={handleAvatarUpload}
                         />
                         
                         {/* Fallback URL input for avatar */}
                         <div className="w-24">
                           <input 
                              type="text" 
                              value={editedProfile.avatarUrl.startsWith('data:') ? '' : editedProfile.avatarUrl} 
                              onChange={(e) => setEditedProfile({...editedProfile, avatarUrl: e.target.value})}
                              className="w-full bg-slate-800 border-b border-slate-700 text-[10px] text-center text-slate-400 focus:border-cyan-500 outline-none px-1 py-1"
                              placeholder="Or paste URL"
                            />
                         </div>
                    </div>

                    {/* Text Fields */}
                    <div className="flex-1 space-y-4 w-full">
                        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                          <div>
                            <label className="block text-xs text-slate-500 mb-1">Name</label>
                            <input 
                              type="text" 
                              value={editedProfile.name} 
                              onChange={(e) => setEditedProfile({...editedProfile, name: e.target.value})}
                              className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-white focus:ring-2 focus:ring-cyan-500 outline-none"
                            />
                          </div>
                          <div>
                            <label className="block text-xs text-slate-500 mb-1">Role</label>
                            <input 
                              type="text" 
                              value={editedProfile.role} 
                              onChange={(e) => setEditedProfile({...editedProfile, role: e.target.value})}
                              className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-white focus:ring-2 focus:ring-cyan-500 outline-none"
                            />
                          </div>
                        </div>
                        
                        <div>
                          <label className="block text-xs text-slate-500 mb-1">Bio</label>
                          <div className="flex gap-2">
                            <textarea 
                              value={editedProfile.bio} 
                              onChange={(e) => setEditedProfile({...editedProfile, bio: e.target.value})}
                              className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-white focus:ring-2 focus:ring-cyan-500 outline-none min-h-[80px]"
                            />
                            <button 
                              onClick={handleAiBio}
                              disabled={!!loadingAi}
                              className="flex-shrink-0 bg-gradient-to-br from-purple-600 to-blue-600 text-white p-3 rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50"
                              title="AI Enhance Bio"
                            >
                              {loadingAi === 'bio' ? <div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" /> : <Icon name="sparkles" className="w-5 h-5" />}
                            </button>
                          </div>
                        </div>
                    </div>
                </div>
              </section>

               {/* Social Media Section */}
               <section className="space-y-4">
                 <h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider">Social Media</h3>
                 <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
                   {AVAILABLE_PLATFORMS.map((platform) => (
                     <div key={platform.id} className="flex items-center gap-3 bg-slate-800/50 p-2 pr-4 rounded-lg border border-slate-700/50">
                        <div className="w-8 h-8 flex items-center justify-center bg-slate-700 rounded text-slate-300">
                          <Icon name={platform.id} className="w-4 h-4" />
                        </div>
                        <input 
                          type="text" 
                          placeholder={`${platform.label} URL`}
                          value={getSocialUrl(platform.id)}
                          onChange={(e) => handleSocialChange(platform.id, e.target.value)}
                          className="flex-1 bg-transparent border-none text-sm text-white placeholder-slate-600 focus:ring-0 outline-none"
                        />
                     </div>
                   ))}
                 </div>
              </section>

              {/* Links Section */}
              <section className="space-y-4 pt-4 border-t border-slate-800/50">
                 <div className="flex justify-between items-center">
                    <h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider">Links</h3>
                    <button 
                      onClick={addNewLink}
                      className="text-xs flex items-center gap-1 bg-slate-800 hover:bg-slate-700 text-cyan-400 px-3 py-1 rounded-full transition-colors"
                    >
                      <Icon name="plus" className="w-3 h-3" /> Add Link
                    </button>
                 </div>
                 
                 <div className="space-y-4">
                   {editedProfile.links.map((link) => (
                     <div key={link.id} className="bg-slate-800/50 p-4 rounded-xl border border-slate-700/50 relative group">
                        <button 
                          onClick={() => removeLink(link.id)}
                          className="absolute top-2 right-2 text-slate-600 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-opacity"
                        >
                          <Icon name="trash" className="w-4 h-4" />
                        </button>

                        <div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-3">
                          <input 
                            type="text" 
                            value={link.title} 
                            onChange={(e) => handleLinkChange(link.id, 'title', e.target.value)}
                            placeholder="Title"
                            className="bg-slate-900 border border-slate-700 rounded px-3 py-2 text-sm text-white focus:border-cyan-500 outline-none"
                          />
                           <input 
                            type="text" 
                            value={link.url} 
                            onChange={(e) => handleLinkChange(link.id, 'url', e.target.value)}
                            placeholder="URL"
                            className="bg-slate-900 border border-slate-700 rounded px-3 py-2 text-sm text-slate-300 focus:border-cyan-500 outline-none"
                          />
                        </div>
                        
                        <div className="flex gap-2">
                          <input 
                            type="text" 
                            value={link.description || ''} 
                            onChange={(e) => handleLinkChange(link.id, 'description', e.target.value)}
                            placeholder="Description"
                            className="flex-1 bg-slate-900 border border-slate-700 rounded px-3 py-2 text-sm text-slate-400 focus:border-cyan-500 outline-none"
                          />
                          <button 
                            onClick={() => handleAiDesc(link.id, link.title, link.url)}
                            disabled={!!loadingAi}
                            className="p-2 bg-slate-700 hover:bg-slate-600 rounded text-purple-400 transition-colors disabled:opacity-50"
                            title="Generate Description"
                          >
                             {loadingAi === link.id ? <div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> : <Icon name="sparkles" className="w-4 h-4" />}
                          </button>
                        </div>
                        
                        <div className="mt-3 flex gap-4 items-center">
                           <label className="flex items-center gap-2 text-xs text-slate-400 cursor-pointer">
                             <input 
                               type="checkbox" 
                               checked={link.highlight} 
                               onChange={(e) => handleLinkChange(link.id, 'highlight', e.target.checked)}
                               className="w-4 h-4 rounded bg-slate-900 border-slate-700 text-cyan-500 focus:ring-offset-0 focus:ring-0" 
                             />
                             Highlight
                           </label>
                        </div>
                     </div>
                   ))}
                 </div>
              </section>
            </>
          ) : (
             /* Appearance Section */
             <section className="space-y-6">
                <h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider">Background</h3>
                
                <div className="grid grid-cols-1 gap-4">
                    {/* File Upload Area */}
                    <div 
                      className="border-2 border-dashed border-slate-700 rounded-xl p-8 flex flex-col items-center justify-center text-center hover:border-cyan-500/50 hover:bg-slate-800/50 transition-all cursor-pointer group"
                      onClick={() => fileInputRef.current?.click()}
                    >
                        <input 
                            type="file" 
                            ref={fileInputRef} 
                            className="hidden" 
                            accept="image/*"
                            onChange={handleImageUpload}
                        />
                        <div className="w-14 h-14 rounded-full bg-slate-800 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
                             <Icon name="upload" className="text-cyan-400 w-6 h-6" />
                        </div>
                        <p className="text-sm font-medium text-slate-300">Upload from Device</p>
                        <p className="text-xs text-slate-500 mt-1">Supports JPG, PNG, GIF</p>
                    </div>

                    {/* URL Input */}
                    <div>
                        <label className="block text-xs text-slate-500 mb-2">Or paste URL</label>
                        <input 
                            type="text" 
                            value={editedProfile.backgroundImageUrl || ''} 
                            onChange={(e) => setEditedProfile({...editedProfile, backgroundImageUrl: e.target.value})}
                            className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-white focus:ring-2 focus:ring-cyan-500 outline-none text-sm"
                            placeholder="https://..."
                        />
                    </div>
                </div>

                <div>
                   <label className="block text-xs text-slate-500 mb-3">Futuristic Presets</label>
                   <div className="grid grid-cols-2 md:grid-cols-3 gap-3">
                      {BG_PRESETS.map((preset) => (
                        <button
                          key={preset.name}
                          onClick={() => setEditedProfile({...editedProfile, backgroundImageUrl: preset.url})}
                          className={`
                            relative h-24 rounded-lg overflow-hidden border-2 transition-all group
                            ${editedProfile.backgroundImageUrl === preset.url ? 'border-cyan-500 ring-2 ring-cyan-500/20' : 'border-slate-700 hover:border-slate-500'}
                          `}
                        >
                          {preset.url ? (
                            <img src={preset.url} alt={preset.name} className="w-full h-full object-cover" />
                          ) : (
                            <div className="w-full h-full bg-black flex items-center justify-center text-xs text-slate-500">None</div>
                          )}
                          <div className="absolute inset-0 bg-black/40 flex items-end p-2 opacity-0 group-hover:opacity-100 transition-opacity">
                            <span className="text-xs font-bold text-white shadow-black drop-shadow-md">{preset.name}</span>
                          </div>
                          {editedProfile.backgroundImageUrl === preset.url && (
                             <div className="absolute top-1 right-1 bg-cyan-500 rounded-full p-0.5">
                               <Icon name="check" className="w-3 h-3 text-black" />
                             </div>
                          )}
                        </button>
                      ))}
                   </div>
                </div>
             </section>
          )}

        </div>

        <div className="p-6 border-t border-slate-800 bg-slate-900 flex justify-end gap-3 shrink-0">
          <button 
            onClick={onClose}
            className="px-6 py-2 rounded-lg text-slate-400 hover:text-white transition-colors"
          >
            Cancel
          </button>
          <button 
            onClick={() => {
              onSave(editedProfile);
              onClose();
            }}
            className="px-6 py-2 rounded-lg bg-cyan-500 hover:bg-cyan-400 text-black font-bold transition-all shadow-[0_0_20px_rgba(6,182,212,0.3)] hover:shadow-[0_0_30px_rgba(6,182,212,0.5)]"
          >
            Save Changes
          </button>
        </div>
      </div>
    </div>
  );
};
Written By