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>
);
};