1→'use client'; 2→ 3→import { useState, useCallback, useRef, useEffect } from 'react'; 4→import { motion, AnimatePresence } from 'framer-motion'; 5→import { 6→ Upload, Image as ImageIcon, Sparkles, Download, ArrowRight, 7→ Palette, Camera, Layers, SlidersHorizontal, Loader2, Check, 8→ Trash2, RotateCcw, Eye, Wand2, Sun, Paintbrush, Film, 9→ Pencil, History, ChevronLeft, X, GripVertical, WandSparkles, 10→ Square, RectangleVertical, Monitor, Settings, Megaphone, Code 11→} from 'lucide-react'; 12→import { Button } from '@/components/ui/button'; 13→import { Textarea } from '@/components/ui/textarea'; 14→import { Progress } from '@/components/ui/progress'; 15→import { Card, CardContent } from '@/components/ui/card'; 16→import { 17→ Dialog, 18→ DialogContent, 19→ DialogHeader, 20→ DialogTitle, 21→ DialogTrigger, 22→} from '@/components/ui/dialog'; 23→import { Input } from '@/components/ui/input'; 24→import { Switch } from '@/components/ui/switch'; 25→import { Label } from '@/components/ui/label'; 26→import { useAppStore, type PhotoProject } from '@/store/photo-store'; 27→ 28→/* ─── Types ─── */ 29→interface BannerData { 30→ id: string; 31→ adType: string; 32→ imageUrl: string; 33→ adCode: string; 34→ linkUrl: string; 35→ text: string; 36→ active: boolean; 37→ position: string; 38→} 39→ 40→/* ─── Banner Display Component ─── */ 41→function BannerDisplay({ position }: { position: 'top' | 'above-upload' | 'below-features' }) { 42→ const [banner, setBanner] = useState(null); 43→ const [dismissed, setDismissed] = useState(false); 44→ 45→ useEffect(() => { 46→ fetch('/api/banner') 47→ .then((r) => r.json()) 48→ .then((data) => { 49→ if (data.banner && data.banner.position === position) { 50→ setBanner(data.banner); 51→ } 52→ }) 53→ .catch(() => {}); 54→ }, [position]); 55→ 56→ if (!banner || !banner.active || dismissed) return null; 57→ 58→ // External ad code (Google AdSense, etc.) 59→ if (banner.adType === 'external' && banner.adCode) { 60→ return ( 61→ 66→
67→
71→
72→ 79→ 80→ ); 81→ } 82→ 83→ // Image banner 84→ if (!banner.imageUrl) return null; 85→ 86→ const Wrapper = banner.linkUrl ? 'a' : 'div'; 87→ const wrapperProps = banner.linkUrl 88→ ? { href: banner.linkUrl, target: '_blank' as const, rel: 'noopener noreferrer' } 89→ : {}; 90→ 91→ return ( 92→ 97→ 101→ {banner.text 106→ {banner.text && ( 107→
108→

{banner.text}

109→
110→ )} 111→
112→ 119→
120→ ); 121→} 122→ 123→/* ─── Banner Admin Panel ─── */ 124→function BannerAdmin({ open, onOpenChange }: { open: boolean; onOpenChange: (v: boolean) => void }) { 125→ const [adType, setAdType] = useState<'image' | 'external'>('image'); 126→ const [imageUrl, setImageUrl] = useState(''); 127→ const [adCode, setAdCode] = useState(''); 128→ const [linkUrl, setLinkUrl] = useState(''); 129→ const [text, setText] = useState(''); 130→ const [active, setActive] = useState(false); 131→ const [position, setPosition] = useState('top'); 132→ const [saving, setSaving] = useState(false); 133→ const [uploading, setUploading] = useState(false); 134→ const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); 135→ const fileInputRef = useRef(null); 136→ 137→ // Load existing banner on open 138→ useEffect(() => { 139→ if (!open) return; 140→ fetch('/api/banner') 141→ .then((r) => r.json()) 142→ .then((data) => { 143→ if (data.banner) { 144→ setAdType((data.banner.adType as 'image' | 'external') || 'image'); 145→ setImageUrl(data.banner.imageUrl || ''); 146→ setAdCode(data.banner.adCode || ''); 147→ setLinkUrl(data.banner.linkUrl); 148→ setText(data.banner.text); 149→ setActive(data.banner.active); 150→ setPosition(data.banner.position); 151→ } else { 152→ setAdType('image'); 153→ setImageUrl(''); 154→ setAdCode(''); 155→ setLinkUrl(''); 156→ setText(''); 157→ setActive(false); 158→ setPosition('top'); 159→ } 160→ setMessage(null); 161→ }) 162→ .catch(() => {}); 163→ }, [open]); 164→ 165→ const handleUpload = useCallback(async (file: File) => { 166→ if (!file.type.startsWith('image/')) return; 167→ setUploading(true); 168→ setMessage(null); 169→ try { 170→ const formData = new FormData(); 171→ formData.append('image', file); 172→ const res = await fetch('/api/banner', { method: 'POST', body: formData }); 173→ const data = await res.json(); 174→ if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة'); 175→ setImageUrl(data.imageUrl); 176→ } catch (err: unknown) { 177→ setMessage({ type: 'error', text: err instanceof Error ? err.message : 'خطأ' }); 178→ } finally { 179→ setUploading(false); 180→ } 181→ }, []); 182→ 183→ const handleSave = useCallback(async () => { 184→ if (adType === 'image' && !imageUrl) { 185→ setMessage({ type: 'error', text: 'يرجى رفع صورة البانر أولاً' }); 186→ return; 187→ } 188→ if (adType === 'external' && !adCode.trim()) { 189→ setMessage({ type: 'error', text: 'يرجى لصق كود الإعلان الخارجي' }); 190→ return; 191→ } 192→ setSaving(true); 193→ setMessage(null); 194→ try { 195→ const res = await fetch('/api/banner', { 196→ method: 'PUT', 197→ headers: { 'Content-Type': 'application/json' }, 198→ body: JSON.stringify({ adType, imageUrl, adCode, linkUrl, text, active, position }), 199→ }); 200→ const data = await res.json(); 201→ if (!res.ok) throw new Error(data.error || 'فشل الحفظ'); 202→ setMessage({ type: 'success', text: 'تم حفظ البانر بنجاح' }); 203→ } catch (err: unknown) { 204→ setMessage({ type: 'error', text: err instanceof Error ? err.message : 'خطأ' }); 205→ } finally { 206→ setSaving(false); 207→ } 208→ }, [adType, imageUrl, adCode, linkUrl, text, active, position]); 209→ 210→ const POSITION_OPTIONS = [ 211→ { value: 'top', label: 'أعلى الصفحة' }, 212→ { value: 'above-upload', label: 'فوق منطقة الرفع' }, 213→ { value: 'below-features', label: 'أسفل المميزات' }, 214→ ]; 215→ 216→ return ( 217→ 218→ 219→ 220→ 221→ 222→ إدارة البانر الإعلاني 223→ 224→ 225→ 226→
227→ {/* Ad Type Selector */} 228→
229→ 230→
231→ 242→ 253→
254→
255→ 256→ {/* Image Upload (only for image type) */} 257→ {adType === 'image' && ( 258→
259→ 260→
!uploading && fileInputRef.current?.click()} 262→ className={`relative rounded-lg border-2 border-dashed p-4 text-center cursor-pointer transition-all 263→ ${imageUrl ? 'border-primary/30' : 'border-muted-foreground/30 hover:border-primary/50'}`} 264→ > 265→ { 271→ const f = e.target.files?.[0]; 272→ if (f) handleUpload(f); 273→ e.target.value = ''; 274→ }} 275→ /> 276→ {uploading ? ( 277→
278→ 279→ جارٍ الرفع... 280→
281→ ) : imageUrl ? ( 282→
283→ بانر 284→

انقر لتغيير الصورة

285→
286→ ) : ( 287→
288→ 289→

انقر لاختيار صورة البانر

290→

PNG, JPG, WEBP — حتى 2 ميجا

291→
292→ )} 293→
294→
295→ )} 296→ 297→ {/* External Ad Code (only for external type) */} 298→ {adType === 'external' && ( 299→
300→ 301→

302→ الصق كود الإعلان من جوجل أدسنس أو أي شبكة إعلانية أخرى 303→

304→