     1→     1→'use client';
     2→     2→
     3→     3→import { useState, useCallback, useRef, useEffect } from 'react';
     4→     4→import { motion, AnimatePresence } from 'framer-motion';
     5→     5→import {
     6→     6→  Upload, Image as ImageIcon, Sparkles, Download, ArrowRight,
     7→     7→  Palette, Camera, Layers, SlidersHorizontal, Loader2, Check,
     8→     8→  Trash2, RotateCcw, Eye, Wand2, Sun, Paintbrush, Film,
     9→     9→  Pencil, History, ChevronLeft, X, GripVertical, WandSparkles,
    10→    10→  Square, RectangleVertical, Monitor, Settings, Megaphone, Code,
    11→    11→  LogIn, LogOut, Shield
    12→    12→} from 'lucide-react';
    13→    13→import { Button } from '@/components/ui/button';
    14→    14→import { Textarea } from '@/components/ui/textarea';
    15→    15→import { Progress } from '@/components/ui/progress';
    16→    16→import { Card, CardContent } from '@/components/ui/card';
    17→    17→import {
    18→    18→  Dialog,
    19→    19→  DialogContent,
    20→    20→  DialogHeader,
    21→    21→  DialogTitle,
    22→    22→  DialogTrigger,
    23→    23→} from '@/components/ui/dialog';
    24→    24→import { Input } from '@/components/ui/input';
    25→    25→import { Switch } from '@/components/ui/switch';
    26→    26→import { Label } from '@/components/ui/label';
    27→    27→import { useAppStore, type PhotoProject } from '@/store/photo-store';
    28→    28→import { signIn, signOut } from 'next-auth/react';
    29→    29→
    30→    30→/* ─── Session Types ─── */
    31→    31→interface SessionUser {
    32→    32→  id: string;
    33→    33→  name?: string | null;
    34→    34→  email?: string | null;
    35→    35→  image?: string | null;
    36→    36→  role?: string;
    37→    37→}
    38→    38→
    39→    39→/* ─── useAdminSession Hook ─── */
    40→    40→function useAdminSession() {
    41→    41→  const [isAdmin, setIsAdmin] = useState(false);
    42→    42→  const [isLoading, setIsLoading] = useState(true);
    43→    43→  const [userName, setUserName] = useState<string | null>(null);
    44→    44→
    45→    45→  const checkSession = useCallback(async () => {
    46→    46→    try {
    47→    47→      const res = await fetch('/api/auth/session');
    48→    48→      if (!res.ok) { setIsAdmin(false); setIsLoading(false); return; }
    49→    49→      const session = await res.json();
    50→    50→      const role = (session?.user as SessionUser | undefined)?.role;
    51→    51→      setIsAdmin(role === 'admin');
    52→    52→      setUserName(session?.user?.name || null);
    53→    53→    } catch {
    54→    54→      setIsAdmin(false);
    55→    55→    } finally {
    56→    56→      setIsLoading(false);
    57→    57→    }
    58→    58→  }, []);
    59→    59→
    60→    60→  useEffect(() => { checkSession(); }, [checkSession]);
    61→    61→
    62→    62→  return { isAdmin, isLoading, userName, refresh: checkSession };
    63→    63→}
    64→    64→
    65→    65→/* ─── Types ─── */
    66→    66→interface BannerData {
    67→    67→  id: string;
    68→    68→  adType: string;
    69→    69→  imageUrl: string;
    70→    70→  adCode: string;
    71→    71→  linkUrl: string;
    72→    72→  text: string;
    73→    73→  active: boolean;
    74→    74→  position: string;
    75→    75→}
    76→    76→
    77→    77→/* ─── Banner Display Component ─── */
    78→    78→function BannerDisplay({ position }: { position: 'top' | 'above-upload' | 'below-features' }) {
    79→    79→  const [banner, setBanner] = useState<BannerData | null>(null);
    80→    80→  const [dismissed, setDismissed] = useState(false);
    81→    81→
    82→    82→  useEffect(() => {
    83→    83→    fetch('/api/banner')
    84→    84→      .then((r) => r.json())
    85→    85→      .then((data) => {
    86→    86→        if (data.banner && data.banner.position === position) {
    87→    87→          setBanner(data.banner);
    88→    88→        }
    89→    89→      })
    90→    90→      .catch(() => {});
    91→    91→  }, [position]);
    92→    92→
    93→    93→  if (!banner || !banner.active || dismissed) return null;
    94→    94→
    95→    95→  // External ad code (Google AdSense, etc.)
    96→    96→  if (banner.adType === 'external' && banner.adCode) {
    97→    97→    return (
    98→    98→      <motion.div
    99→    99→        initial={{ opacity: 0, y: -10 }}
   100→   100→        animate={{ opacity: 1, y: 0 }}
   101→   101→        className="relative w-full max-w-4xl mx-auto"
   102→   102→      >
   103→   103→        <div className="rounded-xl overflow-hidden bg-card/50 border border-border/30 p-1">
   104→   104→          <div
   105→   105→            className="w-full min-h-[60px] flex items-center justify-center"
   106→   106→            dangerouslySetInnerHTML={{ __html: banner.adCode }}
   107→   107→          />
   108→   108→        </div>
   109→   109→        <button
   110→   110→          onClick={() => setDismissed(true)}
   111→   111→          className="absolute -top-2 -left-2 w-6 h-6 rounded-full bg-muted text-muted-foreground flex items-center justify-center hover:bg-muted-foreground hover:text-background transition-colors border border-border/50"
   112→   112→          aria-label="إغلاق الإعلان"
   113→   113→        >
   114→   114→          <X className="w-3 h-3" />
   115→   115→        </button>
   116→   116→      </motion.div>
   117→   117→    );
   118→   118→  }
   119→   119→
   120→   120→  // Image banner
   121→   121→  if (!banner.imageUrl) return null;
   122→   122→
   123→   123→  const Wrapper = banner.linkUrl ? 'a' : 'div';
   124→   124→  const wrapperProps = banner.linkUrl
   125→   125→    ? { href: banner.linkUrl, target: '_blank' as const, rel: 'noopener noreferrer' }
   126→   126→    : {};
   127→   127→
   128→   128→  return (
   129→   129→    <motion.div
   130→   130→      initial={{ opacity: 0, y: -10 }}
   131→   131→      animate={{ opacity: 1, y: 0 }}
   132→   132→      className="relative w-full max-w-4xl mx-auto"
   133→   133→    >
   134→   134→      <Wrapper
   135→   135→        {...wrapperProps}
   136→   136→        className="block relative rounded-xl overflow-hidden"
   137→   137→      >
   138→   138→        <img
   139→   139→          src={banner.imageUrl}
   140→   140→          alt={banner.text || 'إعلان'}
   141→   141→          className="w-full h-auto object-cover max-h-48 rounded-xl"
   142→   142→        />
   143→   143→        {banner.text && (
   144→   144→          <div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent flex items-end p-4">
   145→   145→            <p className="text-white text-sm sm:text-base font-medium">{banner.text}</p>
   146→   146→          </div>
   147→   147→        )}
   148→   148→      </Wrapper>
   149→   149→      <button
   150→   150→        onClick={() => setDismissed(true)}
   151→   151→        className="absolute top-2 left-2 w-6 h-6 rounded-full bg-black/50 text-white flex items-center justify-center hover:bg-black/70 transition-colors"
   152→   152→        aria-label="إغلاق الإعلان"
   153→   153→      >
   154→   154→        <X className="w-3 h-3" />
   155→   155→      </button>
   156→   156→    </motion.div>
   157→   157→  );
   158→   158→}
   159→   159→
   160→   160→/* ─── Login Dialog ─── */
   161→   161→function LoginDialog({ open, onOpenChange, onLoginSuccess }: { open: boolean; onOpenChange: (v: boolean) => void; onLoginSuccess: () => void }) {
   162→   162→  const [username, setUsername] = useState('');
   163→   163→  const [password, setPassword] = useState('');
   164→   164→  const [loading, setLoading] = useState(false);
   165→   165→  const [error, setError] = useState<string | null>(null);
   166→   166→
   167→   167→  const handleSubmit = useCallback(async (e: React.FormEvent) => {
   168→   168→    e.preventDefault();
   169→   169→    if (!username.trim() || !password.trim()) return;
   170→   170→    setLoading(true);
   171→   171→    setError(null);
   172→   172→    try {
   173→   173→      const res = await signIn('credentials', {
   174→   174→        username,
   175→   175→        password,
   176→   176→        redirect: false,
   177→   177→      });
   178→   178→      if (res?.error) {
   179→   179→        setError('اسم المستخدم أو كلمة المرور غير صحيحة');
   180→   180→      } else {
   181→   181→        setUsername('');
   182→   182→        setPassword('');
   183→   183→        onOpenChange(false);
   184→   184→        onLoginSuccess();
   185→   185→      }
   186→   186→    } catch {
   187→   187→      setError('حدث خطأ أثناء تسجيل الدخول');
   188→   188→    } finally {
   189→   189→      setLoading(false);
   190→   190→    }
   191→   191→  }, [username, password, onOpenChange, onLoginSuccess]);
   192→   192→
   193→   193→  return (
   194→   194→    <Dialog open={open} onOpenChange={(v) => { if (!v) setError(null); onOpenChange(v); }}>
   195→   195→      <DialogContent className="max-w-sm" dir="rtl">
   196→   196→        <DialogHeader>
   197→   197→          <DialogTitle className="flex items-center gap-2">
   198→   198→            <Shield className="w-5 h-5 text-primary" />
   199→   199→            تسجيل دخول المدير
   200→   200→          </DialogTitle>
   201→   201→        </DialogHeader>
   202→   202→        <form onSubmit={handleSubmit} className="space-y-4 mt-2">
   203→   203→          <div className="space-y-2">
   204→   204→            <Label htmlFor="admin-user">اسم المستخدم</Label>
   205→   205→            <Input
   206→   206→              id="admin-user"
   207→   207→              value={username}
   208→   208→              onChange={(e) => setUsername(e.target.value)}
   209→   209→              placeholder="admin"
   210→   210→              autoComplete="username"
   211→   211→              autoFocus
   212→   212→            />
   213→   213→          </div>
   214→   214→          <div className="space-y-2">
   215→   215→            <Label htmlFor="admin-pass">كلمة المرور</Label>
   216→   216→            <Input
   217→   217→              id="admin-pass"
   218→   218→              type="password"
   219→   219→              value={password}
   220→   220→              onChange={(e) => setPassword(e.target.value)}
   221→   221→              placeholder="••••••••"
   222→   222→              autoComplete="current-password"
   223→   223→            />
   224→   224→          </div>
   225→   225→          {error && (
   226→   226→            <p className="text-sm text-red-500 bg-red-500/10 p-2 rounded-lg text-center">{error}</p>
   227→   227→          )}
   228→   228→          <Button type="submit" disabled={loading || !username.trim() || !password.trim()} className="w-full gap-2">
   229→   229→            {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <LogIn className="w-4 h-4" />}
   230→   230→            {loading ? 'جارٍ التحقق...' : 'تسجيل الدخول'}
   231→   231→          </Button>
   232→   232→        </form>
   233→   233→      </DialogContent>
   234→   234→    </Dialog>
   235→   235→  );
   236→   236→}
   237→   237→
   238→   238→/* ─── Banner Admin Panel ─── */
   239→   239→function BannerAdmin({ open, onOpenChange }: { open: boolean; onOpenChange: (v: boolean) => void }) {
   240→   240→  const [adType, setAdType] = useState<'image' | 'external'>('image');
   241→   241→  const [imageUrl, setImageUrl] = useState('');
   242→   242→  const [adCode, setAdCode] = useState('');
   243→   243→  const [linkUrl, setLinkUrl] = useState('');
   244→   244→  const [text, setText] = useState('');
   245→   245→  const [active, setActive] = useState(false);
   246→   246→  const [position, setPosition] = useState('top');
   247→   247→  const [saving, setSaving] = useState(false);
   248→   248→  const [uploading, setUploading] = useState(false);
   249→   249→  const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
   250→   250→  const fileInputRef = useRef<HTMLInputElement>(null);
   251→   251→
   252→   252→  // Load existing banner on open
   253→   253→  useEffect(() => {
   254→   254→    if (!open) return;
   255→   255→    fetch('/api/banner')
   256→   256→      .then((r) => r.json())
   257→   257→      .then((data) => {
   258→   258→        if (data.banner) {
   259→   259→          setAdType((data.banner.adType as 'image' | 'external') || 'image');
   260→   260→          setImageUrl(data.banner.imageUrl || '');
   261→   261→          setAdCode(data.banner.adCode || '');
   262→   262→          setLinkUrl(data.banner.linkUrl);
   263→   263→          setText(data.banner.text);
   264→   264→          setActive(data.banner.active);
   265→   265→          setPosition(data.banner.position);
   266→   266→        } else {
   267→   267→          setAdType('image');
   268→   268→          setImageUrl('');
   269→   269→          setAdCode('');
   270→   270→          setLinkUrl('');
   271→   271→          setText('');
   272→   272→          setActive(false);
   273→   273→          setPosition('top');
   274→   274→        }
   275→   275→        setMessage(null);
   276→   276→      })
   277→   277→      .catch(() => {});
   278→   278→  }, [open]);
   279→   279→
   280→   280→  const handleUpload = useCallback(async (file: File) => {
   281→   281→    if (!file.type.startsWith('image/')) return;
   282→   282→    setUploading(true);
   283→   283→    setMessage(null);
   284→   284→    try {
   285→   285→      const formData = new FormData();
   286→   286→      formData.append('image', file);
   287→   287→      const res = await fetch('/api/banner', { method: 'POST', body: formData });
   288→   288→      const data = await res.json();
   289→   289→      if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة');
   290→   290→      setImageUrl(data.imageUrl);
   291→   291→    } catch (err: unknown) {
   292→   292→      setMessage({ type: 'error', text: err instanceof Error ? err.message : 'خطأ' });
   293→   293→    } finally {
   294→   294→      setUploading(false);
   295→   295→    }
   296→   296→  }, []);
   297→   297→
   298→   298→  const handleSave = useCallback(async () => {
   299→   299→    if (adType === 'image' && !imageUrl) {
   300→   300→      setMessage({ type: 'error', text: 'يرجى رفع صورة البانر أولاً' });
   301→   301→      return;
   302→   302→    }
   303→   303→    if (adType === 'external' && !adCode.trim()) {
   304→   304→      setMessage({ type: 'error', text: 'يرجى لصق كود الإعلان الخارجي' });
   305→   305→      return;
   306→   306→    }
   307→   307→    setSaving(true);
   308→   308→    setMessage(null);
   309→   309→    try {
   310→   310→      const res = await fetch('/api/banner', {
   311→   311→        method: 'PUT',
   312→   312→        headers: { 'Content-Type': 'application/json' },
   313→   313→        body: JSON.stringify({ adType, imageUrl, adCode, linkUrl, text, active, position }),
   314→   314→      });
   315→   315→      const data = await res.json();
   316→   316→      if (!res.ok) throw new Error(data.error || 'فشل الحفظ');
   317→   317→      setMessage({ type: 'success', text: 'تم حفظ البانر بنجاح' });
   318→   318→    } catch (err: unknown) {
   319→   319→      setMessage({ type: 'error', text: err instanceof Error ? err.message : 'خطأ' });
   320→   320→    } finally {
   321→   321→      setSaving(false);
   322→   322→    }
   323→   323→  }, [adType, imageUrl, adCode, linkUrl, text, active, position]);
   324→   324→
   325→   325→  const POSITION_OPTIONS = [
   326→   326→    { value: 'top', label: 'أعلى الصفحة' },
   327→   327→    { value: 'above-upload', label: 'فوق منطقة الرفع' },
   328→   328→    { value: 'below-features', label: 'أسفل المميزات' },
   329→   329→  ];
   330→   330→
   331→   331→  return (
   332→   332→    <Dialog open={open} onOpenChange={onOpenChange}>
   333→   333→      <DialogContent className="max-w-md max-h-[90vh] overflow-y-auto" dir="rtl">
   334→   334→        <DialogHeader>
   335→   335→          <DialogTitle className="flex items-center gap-2">
   336→   336→            <Megaphone className="w-5 h-5" />
   337→   337→            إدارة البانر الإعلاني
   338→   338→          </DialogTitle>
   339→   339→        </DialogHeader>
   340→   340→
   341→   341→        <div className="space-y-4 mt-2">
   342→   342→          {/* Ad Type Selector */}
   343→   343→          <div className="space-y-2">
   344→   344→            <Label>نوع الإعلان</Label>
   345→   345→            <div className="grid grid-cols-2 gap-2">
   346→   346→              <button
   347→   347→                onClick={() => setAdType('image')}
   348→   348→                className={`p-3 rounded-lg border text-sm font-medium transition-all flex flex-col items-center gap-1.5 ${
   349→   349→                  adType === 'image'
   350→   350→                    ? 'border-primary bg-primary/10 text-primary'
   351→   351→                    : 'border-border/50 hover:border-primary/30 text-muted-foreground'
   352→   352→                }`}
   353→   353→              >
   354→   354→                <ImageIcon className="w-5 h-5" />
   355→   355→                صورة مرفوعة
   356→   356→              </button>
   357→   357→              <button
   358→   358→                onClick={() => setAdType('external')}
   359→   359→                className={`p-3 rounded-lg border text-sm font-medium transition-all flex flex-col items-center gap-1.5 ${
   360→   360→                  adType === 'external'
   361→   361→                    ? 'border-primary bg-primary/10 text-primary'
   362→   362→                    : 'border-border/50 hover:border-primary/30 text-muted-foreground'
   363→   363→                }`}
   364→   364→              >
   365→   365→                <Code className="w-5 h-5" />
   366→   366→                كود خارجي
   367→   367→              </button>
   368→   368→            </div>
   369→   369→          </div>
   370→   370→
   371→   371→          {/* Image Upload (only for image type) */}
   372→   372→          {adType === 'image' && (
   373→   373→            <div className="space-y-2">
   374→   374→              <Label>صورة البانر</Label>
   375→   375→              <div
   376→   376→                onClick={() => !uploading && fileInputRef.current?.click()}
   377→   377→                className={`relative rounded-lg border-2 border-dashed p-4 text-center cursor-pointer transition-all
   378→   378→                  ${imageUrl ? 'border-primary/30' : 'border-muted-foreground/30 hover:border-primary/50'}`}
   379→   379→              >
   380→   380→                <input
   381→   381→                  ref={fileInputRef}
   382→   382→                  type="file"
   383→   383→                  accept="image/*"
   384→   384→                  className="hidden"
   385→   385→                  onChange={(e) => {
   386→   386→                    const f = e.target.files?.[0];
   387→   387→                    if (f) handleUpload(f);
   388→   388→                    e.target.value = '';
   389→   389→                  }}
   390→   390→                />
   391→   391→                {uploading ? (
   392→   392→                  <div className="flex items-center justify-center gap-2 py-2">
   393→   393→                    <Loader2 className="w-5 h-5 animate-spin text-primary" />
   394→   394→                    <span className="text-sm text-muted-foreground">جارٍ الرفع...</span>
   395→   395→                  </div>
   396→   396→                ) : imageUrl ? (
   397→   397→                  <div className="space-y-2">
   398→   398→                    <img src={imageUrl} alt="بانر" className="w-full h-24 object-cover rounded-md" />
   399→   399→                    <p className="text-xs text-muted-foreground">انقر لتغيير الصورة</p>
   400→   400→                  </div>
   401→   401→                ) : (
   402→   402→                  <div className="py-2">
   403→   403→                    <Upload className="w-8 h-8 mx-auto text-muted-foreground/50 mb-2" />
   404→   404→                    <p className="text-sm text-muted-foreground">انقر لاختيار صورة البانر</p>
   405→   405→                    <p className="text-xs text-muted-foreground mt-1">PNG, JPG, WEBP — حتى 2 ميجا</p>
   406→   406→                  </div>
   407→   407→                )}
   408→   408→              </div>
   409→   409→            </div>
   410→   410→          )}
   411→   411→
   412→   412→          {/* External Ad Code (only for external type) */}
   413→   413→          {adType === 'external' && (
   414→   414→            <div className="space-y-2">
   415→   415→              <Label>كود الإعلان الخارجي</Label>
   416→   416→              <p className="text-xs text-muted-foreground">
   417→   417→                الصق كود الإعلان من جوجل أدسنس أو أي شبكة إعلانية أخرى
   418→   418→              </p>
   419→   419→              <Textarea
   420→   420→                value={adCode}
   421→   421→                onChange={(e) => setAdCode(e.target.value)}
   422→   422→                placeholder={'<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXX"></script>\n<ins class="adsbygoogle" ...></ins>'}
   423→   423→                className="min-h-[120px] font-mono text-xs"
   424→   424→                dir="ltr"
   425→   425→              />
   426→   426→            </div>
   427→   427→          )}
   428→   428→
   429→   429→          {/* Link URL (only for image type) */}
   430→   430→          {adType === 'image' && (
   431→   431→            <div className="space-y-2">
   432→   432→              <Label>رابط الإعلان (اختياري)</Label>
   433→   433→              <Input
   434→   434→                value={linkUrl}
   435→   435→                onChange={(e) => setLinkUrl(e.target.value)}
   436→   436→                placeholder="https://example.com"
   437→   437→                dir="ltr"
   438→   438→              />
   439→   439→            </div>
   440→   440→          )}
   441→   441→
   442→   442→          {/* Text Overlay (only for image type) */}
   443→   443→          {adType === 'image' && (
   444→   444→            <div className="space-y-2">
   445→   445→              <Label>نص على البانر (اختياري)</Label>
   446→   446→              <Input
   447→   447→                value={text}
   448→   448→                onChange={(e) => setText(e.target.value)}
   449→   449→                placeholder="عنوان الإعلان..."
   450→   450→              />
   451→   451→            </div>
   452→   452→          )}
   453→   453→
   454→   454→          {/* Position */}
   455→   455→          <div className="space-y-2">
   456→   456→            <Label>موضع البانر</Label>
   457→   457→            <div className="grid grid-cols-3 gap-2">
   458→   458→              {POSITION_OPTIONS.map((opt) => (
   459→   459→                <button
   460→   460→                  key={opt.value}
   461→   461→                  onClick={() => setPosition(opt.value)}
   462→   462→                  className={`p-2 rounded-lg border text-xs font-medium transition-all
   463→   463→                    ${position === opt.value
   464→   464→                      ? 'border-primary bg-primary/10 text-primary'
   465→   465→                      : 'border-border/50 hover:border-primary/30 text-muted-foreground'}`}
   466→   466→                >
   467→   467→                  {opt.label}
   468→   468→                </button>
   469→   469→              ))}
   470→   470→            </div>
   471→   471→          </div>
   472→   472→
   473→   473→          {/* Active Toggle */}
   474→   474→          <div className="flex items-center justify-between p-3 rounded-lg border border-border/50">
   475→   475→            <Label className="cursor-pointer">عرض البانر</Label>
   476→   476→            <Switch checked={active} onCheckedChange={setActive} />
   477→   477→          </div>
   478→   478→
   479→   479→          {/* Message */}
   480→   480→          <AnimatePresence>
   481→   481→            {message && (
   482→   482→              <motion.div
   483→   483→                initial={{ opacity: 0, y: -5 }}
   484→   484→                animate={{ opacity: 1, y: 0 }}
   485→   485→                exit={{ opacity: 0 }}
   486→   486→                className={`text-sm p-3 rounded-lg ${message.type === 'success' ? 'bg-emerald-500/10 text-emerald-600' : 'bg-red-500/10 text-red-600'}`}
   487→   487→              >
   488→   488→                {message.text}
   489→   489→              </motion.div>
   490→   490→            )}
   491→   491→          </AnimatePresence>
   492→   492→
   493→   493→          {/* Save Button */}
   494→   494→          <Button
   495→   495→            onClick={handleSave}
   496→   496→            disabled={saving || (adType === 'image' ? !imageUrl : !adCode.trim())}
   497→   497→            className="w-full gap-2"
   498→   498→          >
   499→   499→            {saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
   500→   500→            {saving ? 'جارٍ الحفظ...' : 'حفظ البانر'}
   501→   501→          </Button>
   502→   502→        </div>
   503→   503→      </DialogContent>
   504→   504→    </Dialog>
   505→   505→  );
   506→   506→}
   507→   507→
   508→   508→/* ─── Edit Tool Definitions ─── */
   509→   509→const EDIT_TOOLS = [
   510→   510→  { id: 'oil-painting', label: 'لوحة زيتية', icon: Paintbrush, prompt: 'Transform this image into an oil painting style, with visible brushstrokes and rich, vibrant colors, maintain the composition and subject', color: 'from-amber-500/20 to-orange-600/20' },
   511→   511→  { id: 'cartoon', label: 'رسم كرتوني', icon: Pencil, prompt: 'Transform this image into a cartoon illustration style, with bold outlines, flat colors, and simplified shapes, keep the main subject recognizable', color: 'from-pink-500/20 to-rose-600/20' },
   512→   512→  { id: 'bw', label: 'أبيض وأسود', icon: Camera, prompt: 'Convert this image to a stunning black and white photograph with high contrast and dramatic lighting, professional photography style', color: 'from-gray-500/20 to-gray-700/20' },
   513→   513→  { id: 'golden-hour', label: 'إضاءة ذهبية', icon: Sun, prompt: 'Apply warm golden hour lighting to this image, with beautiful warm tones, soft shadows, and a magical sunset glow, maintain all subjects', color: 'from-yellow-500/20 to-amber-600/20' },
   514→   514→  { id: 'enhance', label: 'تحسين الجودة', icon: Sparkles, prompt: 'Enhance the quality and sharpness of this image, improve colors, add more detail and clarity, make it look professional and high-resolution', color: 'from-emerald-500/20 to-teal-600/20' },
   515→   515→  { id: 'bg-remove', label: 'خلفية احترافية', icon: Layers, prompt: 'Enhance this photo with a clean neutral studio background while preserving the main subject naturally', color: 'from-cyan-500/20 to-blue-600/20' },
   516→   516→  { id: 'cinematic', label: 'تأثير سينمائي', icon: Film, prompt: 'Apply a cinematic color grading to this image, with deep shadows, teal and orange tones, letterbox style, dramatic and moody atmosphere', color: 'from-violet-500/20 to-purple-600/20' },
   517→   517→  { id: 'illustration', label: 'أسلوب رسومي', icon: Palette, prompt: 'Transform this image into a digital illustration style, clean lines, artistic rendering, vibrant but harmonious colors, maintain the scene composition', color: 'from-fuchsia-500/20 to-pink-600/20' },
   518→   518→];
   519→   519→
   520→   520→/* ─── Header Component ─── */
   521→   521→function Header({
   522→   522→  isAdmin,
   523→   523→  isLoading,
   524→   524→  onOpenBannerAdmin,
   525→   525→  onOpenLogin,
   526→   526→  onLogout,
   527→   527→}: {
   528→   528→  isAdmin: boolean;
   529→   529→  isLoading: boolean;
   530→   530→  onOpenBannerAdmin: () => void;
   531→   531→  onOpenLogin: () => void;
   532→   532→  onLogout: () => void;
   533→   533→}) {
   534→   534→  const { currentView, setView } = useAppStore();
   535→   535→  return (
   536→   536→    <header className="glass sticky top-0 z-50">
   537→   537→      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
   538→   538→        <button
   539→   539→          onClick={() => setView('home')}
   540→   540→          className="flex items-center gap-3 group"
   541→   541→        >
   542→   542→          <div className="w-9 h-9 rounded-lg bg-primary/20 flex items-center justify-center">
   543→   543→            <Wand2 className="w-5 h-5 text-primary" />
   544→   544→          </div>
   545→   545→          <span className="text-lg font-bold gradient-text">محرر الصور Fouad AI</span>
   546→   546→        </button>
   547→   547→        <nav className="flex items-center gap-1">
   548→   548→          <Button
   549→   549→            variant={currentView === 'home' ? 'default' : 'ghost'}
   550→   550→            size="sm"
   551→   551→            onClick={() => setView('home')}
   552→   552→            className="gap-2"
   553→   553→          >
   554→   554→            <ImageIcon className="w-4 h-4" />
   555→   555→            <span className="hidden sm:inline">الرئيسية</span>
   556→   556→          </Button>
   557→   557→          <Button
   558→   558→            variant={currentView === 'gallery' ? 'default' : 'ghost'}
   559→   559→            size="sm"
   560→   560→            onClick={() => setView('gallery')}
   561→   561→            className="gap-2"
   562→   562→          >
   563→   563→            <History className="w-4 h-4" />
   564→   564→            <span className="hidden sm:inline">المعرض</span>
   565→   565→          </Button>
   566→   566→          <Button
   567→   567→            variant={currentView === 'generate' ? 'default' : 'ghost'}
   568→   568→            size="sm"
   569→   569→            onClick={() => setView('generate')}
   570→   570→            className="gap-2"
   571→   571→          >
   572→   572→            <WandSparkles className="w-4 h-4" />
   573→   573→            <span className="hidden sm:inline">توليد الصور</span>
   574→   574→          </Button>
   575→   575→          {!isLoading && (
   576→   576→            isAdmin ? (
   577→   577→              <>
   578→   578→                <Button
   579→   579→                  variant="ghost"
   580→   580→                  size="icon"
   581→   581→                  className="mr-1"
   582→   582→                  onClick={onOpenBannerAdmin}
   583→   583→                  title="إدارة البانر"
   584→   584→                >
   585→   585→                  <Settings className="w-4 h-4" />
   586→   586→                </Button>
   587→   587→                <Button
   588→   588→                  variant="ghost"
   589→   589→                  size="icon"
   590→   590→                  onClick={onLogout}
   591→   591→                  title="تسجيل الخروج"
   592→   592→                  className="text-red-400 hover:text-red-300 hover:bg-red-500/10"
   593→   593→                >
   594→   594→                  <LogOut className="w-4 h-4" />
   595→   595→                </Button>
   596→   596→              </>
   597→   597→            ) : (
   598→   598→              <Button
   599→   599→                variant="ghost"
   600→   600→                size="icon"
   601→   601→                className="mr-1"
   602→   602→                onClick={onOpenLogin}
   603→   603→                title="دخول المدير"
   604→   604→              >
   605→   605→                <LogIn className="w-4 h-4" />
   606→   606→              </Button>
   607→   607→            )
   608→   608→          )}
   609→   609→        </nav>
   610→   610→      </div>
   611→   611→    </header>
   612→   612→  );
   613→   613→}
   614→   614→
   615→   615→/* ─── Footer Component ─── */
   616→   616→function Footer() {
   617→   617→  return (
   618→   618→    <footer className="glass mt-auto">
   619→   619→      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex flex-col sm:flex-row items-center justify-between gap-2 text-sm text-muted-foreground">
   620→   620→        <p>محرر الصور Fouad AI — قوّة الذكاء الاصطناعي بين يديك</p>
   621→   621→        <p>© {new Date().getFullYear()} جميع الحقوق محفوظة</p>
   622→   622→      </div>
   623→   623→    </footer>
   624→   624→  );
   625→   625→}
   626→   626→
   627→   627→/* ─── Upload Area Component ─── */
   628→   628→function UploadArea() {
   629→   629→  const { setOriginalImage, setOriginalFileName, setView, setAnalysis, setEditedImage, setEditError } = useAppStore();
   630→   630→  const [isDragOver, setIsDragOver] = useState(false);
   631→   631→  const [isUploading, setIsUploading] = useState(false);
   632→   632→  const fileInputRef = useRef<HTMLInputElement>(null);
   633→   633→
   634→   634→  const handleFile = useCallback(async (file: File) => {
   635→   635→    if (!file.type.startsWith('image/')) return;
   636→   636→    setIsUploading(true);
   637→   637→    setEditError(null);
   638→   638→    setEditedImage(null);
   639→   639→    setAnalysis(null);
   640→   640→    try {
   641→   641→      const formData = new FormData();
   642→   642→      formData.append('image', file);
   643→   643→
   644→   644→      const res = await fetch('/api/upload', { method: 'POST', body: formData });
   645→   645→      const data = await res.json();
   646→   646→      if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة');
   647→   647→
   648→   648→      setOriginalImage(data.imageUrl);
   649→   649→      setOriginalFileName(file.name);
   650→   650→      setView('editor');
   651→   651→    } catch (err: unknown) {
   652→   652→      const message = err instanceof Error ? err.message : 'حدث خطأ';
   653→   653→      setEditError(message);
   654→   654→    } finally {
   655→   655→      setIsUploading(false);
   656→   656→    }
   657→   657→  }, [setOriginalImage, setOriginalFileName, setView, setEditedImage, setAnalysis, setEditError]);
   658→   658→
   659→   659→  const onDrop = useCallback((e: React.DragEvent) => {
   660→   660→    e.preventDefault();
   661→   661→    setIsDragOver(false);
   662→   662→    const file = e.dataTransfer.files[0];
   663→   663→    if (file) handleFile(file);
   664→   664→  }, [handleFile]);
   665→   665→
   666→   666→  const onDragOver = useCallback((e: React.DragEvent) => {
   667→   667→    e.preventDefault();
   668→   668→    setIsDragOver(true);
   669→   669→  }, []);
   670→   670→
   671→   671→  const onDragLeave = useCallback(() => setIsDragOver(false), []);
   672→   672→
   673→   673→  return (
   674→   674→    <motion.div
   675→   675→      initial={{ opacity: 0, y: 30 }}
   676→   676→      animate={{ opacity: 1, y: 0 }}
   677→   677→      transition={{ duration: 0.6 }}
   678→   678→      className="w-full max-w-2xl mx-auto"
   679→   679→    >
   680→   680→      <div
   681→   681→        onDrop={onDrop}
   682→   682→        onDragOver={onDragOver}
   683→   683→        onDragLeave={onDragLeave}
   684→   684→        onClick={() => fileInputRef.current?.click()}
   685→   685→        className={`
   686→   686→          relative cursor-pointer rounded-2xl border-2 border-dashed p-12 sm:p-16
   687→   687→          transition-all duration-300 text-center
   688→   688→          ${isDragOver
   689→   689→            ? 'upload-area-active border-primary'
   690→   690→            : 'border-muted-foreground/30 hover:border-primary/50 hover:bg-primary/5'
   691→   691→          }
   692→   692→        `}
   693→   693→      >
   694→   694→        <input
   695→   695→          ref={fileInputRef}
   696→   696→          type="file"
   697→   697→          accept="image/*"
   698→   698→          className="hidden"
   699→   699→          onChange={(e) => {
   700→   700→            const file = e.target.files?.[0];
   701→   701→            if (file) handleFile(file);
   702→   702→            e.target.value = '';
   703→   703→          }}
   704→   704→        />
   705→   705→        {isUploading ? (
   706→   706→          <div className="flex flex-col items-center gap-4">
   707→   707→            <Loader2 className="w-12 h-12 text-primary animate-spin" />
   708→   708→            <p className="text-lg text-muted-foreground">جارٍ رفع الصورة...</p>
   709→   709→          </div>
   710→   710→        ) : (
   711→   711→          <div className="flex flex-col items-center gap-4">
   712→   712→            <div className="w-20 h-20 rounded-full bg-primary/10 flex items-center justify-center">
   713→   713→              <Upload className="w-10 h-10 text-primary" />
   714→   714→            </div>
   715→   715→            <div>
   716→   716→              <p className="text-xl font-semibold mb-2">اسحب الصورة هنا أو انقر للاختيار</p>
   717→   717→              <p className="text-muted-foreground text-sm">يدعم: PNG, JPG, WEBP — حتى 10 ميجا</p>
   718→   718→            </div>
   719→   719→          </div>
   720→   720→        )}
   721→   721→      </div>
   722→   722→    </motion.div>
   723→   723→  );
   724→   724→}
   725→   725→
   726→   726→/* ─── Feature Cards ─── */
   727→   727→function FeatureCards() {
   728→   728→  const features = [
   729→   729→    { icon: Sparkles, title: 'تحرير بالذكاء', desc: 'أدوات تعديل ذكية تفهم صورتك وتحسّنها' },
   730→   730→    { icon: Palette, title: 'أنماط فنية', desc: 'حوّل صورك إلى لوحات زيتية أو رسوم كرتونية' },
   731→   731→    { icon: SlidersHorizontal, title: 'مقارنة فورية', desc: 'قارن بين الأصل والنسخة المحرّرة بسهولة' },
   732→   732→    { icon: Download, title: 'تحميل مباشر', desc: 'حمّل الصور المحرّرة بجودة عالية' },
   733→   733→  ];
   734→   734→  return (
   735→   735→    <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 max-w-5xl mx-auto mt-16">
   736→   736→      {features.map((f, i) => (
   737→   737→        <motion.div
   738→   738→          key={f.title}
   739→   739→          initial={{ opacity: 0, y: 20 }}
   740→   740→          animate={{ opacity: 1, y: 0 }}
   741→   741→          transition={{ duration: 0.5, delay: 0.2 + i * 0.1 }}
   742→   742→        >
   743→   743→          <Card className="glass h-full text-center p-4 sm:p-6">
   744→   744→            <CardContent className="p-0 flex flex-col items-center gap-3">
   745→   745→              <div className="w-12 h-12 rounded-xl bg-primary/15 flex items-center justify-center">
   746→   746→                <f.icon className="w-6 h-6 text-primary" />
   747→   747→              </div>
   748→   748→              <h3 className="font-semibold text-sm sm:text-base">{f.title}</h3>
   749→   749→              <p className="text-xs sm:text-sm text-muted-foreground">{f.desc}</p>
   750→   750→            </CardContent>
   751→   751→          </Card>
   752→   752→        </motion.div>
   753→   753→      ))}
   754→   754→    </div>
   755→   755→  );
   756→   756→}
   757→   757→
   758→   758→/* ─── Home View ─── */
   759→   759→function HomeView() {
   760→   760→  return (
   761→   761→    <main className="flex-1 flex flex-col items-center px-4 py-12">
   762→   762→      <div className="my-6">
   763→   763→        <BannerDisplay position="above-upload" />
   764→   764→      </div>
   765→   765→      <motion.div
   766→   766→        initial={{ opacity: 0, y: -20 }}
   767→   767→        animate={{ opacity: 1, y: 0 }}
   768→   768→        transition={{ duration: 0.5 }}
   769→   769→        className="text-center mb-10"
   770→   770→      >
   771→   771→        <h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold mb-4">
   772→   772→          <span className="gradient-text">محرر الصور Fouad AI</span>
   773→   773→        </h1>
   774→   774→        <p className="text-lg sm:text-xl text-muted-foreground max-w-xl mx-auto">
   775→   775→          ارفع صورتك واختر التأثير المطلوب — وشاهد الذكاء الاصطناعي يُبدع
   776→   776→        </p>
   777→   777→      </motion.div>
   778→   778→      <UploadArea />
   779→   779→      <FeatureCards />
   780→   780→      <div className="my-6">
   781→   781→        <BannerDisplay position="below-features" />
   782→   782→      </div>
   783→   783→    </main>
   784→   784→  );
   785→   785→}
   786→   786→
   787→   787→/* ─── Before/After Comparison Slider ─── */
   788→   788→function ComparisonSlider({ original, edited }: { original: string; edited: string }) {
   789→   789→  const containerRef = useRef<HTMLDivElement>(null);
   790→   790→  const [position, setPosition] = useState(50);
   791→   791→  const [containerWidth, setContainerWidth] = useState(0);
   792→   792→  const isDragging = useRef(false);
   793→   793→
   794→   794→  useEffect(() => {
   795→   795→    const el = containerRef.current;
   796→   796→    if (!el) return;
   797→   797→    const observer = new ResizeObserver((entries) => {
   798→   798→      for (const entry of entries) {
   799→   799→        setContainerWidth(entry.contentRect.width);
   800→   800→      }
   801→   801→    });
   802→   802→    observer.observe(el);
   803→   803→    return () => observer.disconnect();
   804→   804→  }, []);
   805→   805→
   806→   806→  const updatePosition = useCallback((clientX: number) => {
   807→   807→    if (!containerRef.current) return;
   808→   808→    const rect = containerRef.current.getBoundingClientRect();
   809→   809→    // RTL: position is measured from the right edge
   810→   810→    const x = clientX - rect.left;
   811→   811→    const pct = Math.min(100, Math.max(0, (x / rect.width) * 100));
   812→   812→    setPosition(pct);
   813→   813→  }, []);
   814→   814→
   815→   815→  const handlePointerDown = useCallback((e: React.PointerEvent) => {
   816→   816→    isDragging.current = true;
   817→   817→    (e.target as HTMLElement).setPointerCapture(e.pointerId);
   818→   818→    updatePosition(e.clientX);
   819→   819→  }, [updatePosition]);
   820→   820→
   821→   821→  const handlePointerMove = useCallback((e: React.PointerEvent) => {
   822→   822→    if (!isDragging.current) return;
   823→   823→    updatePosition(e.clientX);
   824→   824→  }, [updatePosition]);
   825→   825→
   826→   826→  const handlePointerUp = useCallback(() => {
   827→   827→    isDragging.current = false;
   828→   828→  }, []);
   829→   829→
   830→   830→  return (
   831→   831→    <div
   832→   832→      ref={containerRef}
   833→   833→      className="comparison-container rounded-xl overflow-hidden bg-black/50 relative w-full"
   834→   834→      style={{ aspectRatio: 'auto' }}
   835→   835→      onPointerDown={handlePointerDown}
   836→   836→      onPointerMove={handlePointerMove}
   837→   837→      onPointerUp={handlePointerUp}
   838→   838→    >
   839→   839→      {/* Edited image (full width, behind) */}
   840→   840→      <img
   841→   841→        src={edited}
   842→   842→        alt="After"
   843→   843→        className="w-full h-auto block"
   844→   844→        draggable={false}
   845→   845→      />
   846→   846→
   847→   847→      {/* Original image (clipped from right in RTL) */}
   848→   848→      <div
   849→   849→        className="absolute inset-0 overflow-hidden"
   850→   850→        style={{ width: `${position}%` }}
   851→   851→      >
   852→   852→        <img
   853→   853→          src={original}
   854→   854→          alt="Before"
   855→   855→          className="w-full h-auto block"
   856→   856→          style={{ width: containerWidth > 0 ? `${containerWidth}px` : '100%' }}
   857→   857→          draggable={false}
   858→   858→        />
   859→   859→      </div>
   860→   860→
   861→   861→      {/* Slider line */}
   862→   862→      <div
   863→   863→        className="comparison-slider-line"
   864→   864→        style={{ left: `${position}%` }}
   865→   865→      >
   866→   866→        <div className="comparison-slider-handle">
   867→   867→          <GripVertical className="w-5 h-5 text-gray-700" />
   868→   868→        </div>
   869→   869→      </div>
   870→   870→
   871→   871→      {/* Labels */}
   872→   872→      <div className="absolute top-3 right-3 bg-black/60 text-white text-xs px-2 py-1 rounded-md">الأصلي</div>
   873→   873→      <div className="absolute top-3 left-3 bg-primary/80 text-white text-xs px-2 py-1 rounded-md">المحرّر</div>
   874→   874→    </div>
   875→   875→  );
   876→   876→}
   877→   877→
   878→   878→/* ─── Editor View ─── */
   879→   879→function EditorView() {
   880→   880→  const {
   881→   881→    originalImage, editedImage, isEditing, editProgress, editError,
   882→   882→    analysis, customPrompt, setCustomPrompt, setEditedImage,
   883→   883→    setEditProgress, setIsEditing, setEditError, setAnalysis,
   884→   884→    setView, resetEditor, projects, setProjects, originalFileName,
   885→   885→  } = useAppStore();
   886→   886→  const [selectedTool, setSelectedTool] = useState<string | null>(null);
   887→   887→  const [lastEditArgs, setLastEditArgs] = useState<{ prompt: string; label: string; toolType: string } | null>(null);
   888→   888→  const [editCount, setEditCount] = useState(0);
   889→   889→  const [isAnalyzing, setIsAnalyzing] = useState(false);
   890→   890→  const fileInputRef = useRef<HTMLInputElement>(null);
   891→   891→
   892→   892→  const handleAnalyze = useCallback(async () => {
   893→   893→    if (!originalImage) return;
   894→   894→    setIsAnalyzing(true);
   895→   895→    try {
   896→   896→      const res = await fetch('/api/analyze', {
   897→   897→        method: 'POST',
   898→   898→        headers: { 'Content-Type': 'application/json' },
   899→   899→        body: JSON.stringify({ imageUrl: originalImage }),
   900→   900→      });
   901→   901→      const data = await res.json();
   902→   902→      if (!res.ok) throw new Error(data.error || 'فشل التحليل');
   903→   903→      setAnalysis(data.analysis);
   904→   904→    } catch (err: unknown) {
   905→   905→      const message = err instanceof Error ? err.message : 'حدث خطأ';
   906→   906→      setEditError(message);
   907→   907→    } finally {
   908→   908→      setIsAnalyzing(false);
   909→   909→    }
   910→   910→  }, [originalImage, setAnalysis, setEditError]);
   911→   911→
   912→   912→  const handleEdit = useCallback(async (prompt: string, label: string, toolType: string) => {
   913→   913→    // Use the last edited image as base for stacking edits
   914→   914→    const baseImage = editedImage || originalImage;
   915→   915→    if (!baseImage) return;
   916→   916→    setLastEditArgs({ prompt, label, toolType });
   917→   917→    setIsEditing(true);
   918→   918→    setEditError(null);
   919→   919→    setEditedImage(null);
   920→   920→    setEditProgress(0);
   921→   921→
   922→   922→    try {
   923→   923→      const res = await fetch('/api/edit', {
   924→   924→        method: 'POST',
   925→   925→        headers: { 'Content-Type': 'application/json' },
   926→   926→        body: JSON.stringify({
   927→   927→          imageUrl: baseImage,
   928→   928→          prompt,
   929→   929→          label,
   930→   930→          toolType,
   931→   931→        }),
   932→   932→      });
   933→   933→
   934→   934→      if (!res.ok) {
   935→   935→        const data = await res.json();
   936→   936→        throw new Error(data.error || 'فشل التعديل');
   937→   937→      }
   938→   938→
   939→   939→      const reader = res.body?.getReader();
   940→   940→      if (!reader) throw new Error('لا يمكن قراءة الاستجابة');
   941→   941→
   942→   942→      const decoder = new TextDecoder();
   943→   943→      let buffer = '';
   944→   944→
   945→   945→      while (true) {
   946→   946→        const { done, value } = await reader.read();
   947→   947→        if (done) break;
   948→   948→
   949→   949→        buffer += decoder.decode(value, { stream: true });
   950→   950→        const lines = buffer.split('\n');
   951→   951→        buffer = lines.pop() || '';
   952→   952→
   953→   953→        for (const line of lines) {
   954→   954→          if (line.startsWith('data: ')) {
   955→   955→            const data = line.slice(6);
   956→   956→            let parsed: Record<string, unknown>;
   957→   957→            try {
   958→   958→              parsed = JSON.parse(data);
   959→   959→            } catch {
   960→   960→              continue; // Skip malformed JSON lines
   961→   961→            }
   962→   962→
   963→   963→            if (parsed.type === 'progress') {
   964→   964→              setEditProgress(parsed.value as number);
   965→   965→            } else if (parsed.type === 'done') {
   966→   966→              setEditedImage(parsed.editedImageUrl as string);
   967→   967→              setEditProgress(100);
   968→   968→              setEditCount((c) => c + 1);
   969→   969→              // Refresh gallery
   970→   970→              try {
   971→   971→                const projectsRes = await fetch('/api/projects');
   972→   972→                if (projectsRes.ok) {
   973→   973→                  const projectsData = await projectsRes.json();
   974→   974→                  setProjects(projectsData);
   975→   975→                }
   976→   976→              } catch { /* ignore gallery refresh failure */ }
   977→   977→            } else if (parsed.type === 'error') {
   978→   978→              throw new Error(parsed.message as string);
   979→   979→            }
   980→   980→          }
   981→   981→        }
   982→   982→      }
   983→   983→    } catch (err: unknown) {
   984→   984→      const message = err instanceof Error ? err.message : 'حدث خطأ أثناء التعديل';
   985→   985→      setEditError(message);
   986→   986→    } finally {
   987→   987→      setIsEditing(false);
   988→   988→      setSelectedTool(null);
   989→   989→    }
   990→   990→  }, [originalImage, editedImage, setIsEditing, setEditError, setEditedImage, setEditProgress, setProjects]);
   991→   991→
   992→   992→  const handleCustomEdit = useCallback(() => {
   993→   993→    if (!customPrompt.trim()) return;
   994→   994→    handleEdit(customPrompt.trim(), 'تعديل مخصص', 'custom');
   995→   995→  }, [customPrompt, handleEdit]);
   996→   996→
   997→   997→  const handleNewImage = useCallback(() => {
   998→   998→    resetEditor();
   999→   999→    setView('home');
  1000→  1000→  }, [resetEditor, setView]);
  1001→  1001→
  1002→  1002→  const handleResetToOriginal = useCallback(() => {
  1003→  1003→    setEditedImage(null);
  1004→  1004→    setEditCount(0);
  1005→  1005→    setEditError(null);
  1006→  1006→  }, [setEditedImage, setEditError]);
  1007→  1007→
  1008→  1008→  const handleReupload = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
  1009→  1009→    const file = e.target.files?.[0];
  1010→  1010→    if (!file || !file.type.startsWith('image/')) return;
  1011→  1011→    const formData = new FormData();
  1012→  1012→    formData.append('image', file);
  1013→  1013→    try {
  1014→  1014→      const res = await fetch('/api/upload', { method: 'POST', body: formData });
  1015→  1015→      const data = await res.json();
  1016→  1016→      if (!res.ok) throw new Error(data.error);
  1017→  1017→      useAppStore.getState().setOriginalImage(data.imageUrl);
  1018→  1018→      useAppStore.getState().setOriginalFileName(file.name);
  1019→  1019→      useAppStore.getState().setEditedImage(null);
  1020→  1020→      useAppStore.getState().setAnalysis(null);
  1021→  1021→    } catch {}
  1022→  1022→    e.target.value = '';
  1023→  1023→  }, []);
  1024→  1024→
  1025→  1025→  const handleDownload = useCallback(() => {
  1026→  1026→    if (!editedImage) return;
  1027→  1027→    const a = document.createElement('a');
  1028→  1028→    a.href = editedImage;
  1029→  1029→    const ext = originalFileName?.split('.').pop() || 'png';
  1030→  1030→    const baseName = originalFileName ? originalFileName.replace(/\.[^.]+$/, '') : 'image';
  1031→  1031→    a.download = `Fouad-AI-${baseName}.${ext}`;
  1032→  1032→    a.click();
  1033→  1033→  }, [editedImage, originalFileName]);
  1034→  1034→
  1035→  1035→  return (
  1036→  1036→    <main className="flex-1 px-4 py-6 max-w-7xl mx-auto w-full">
  1037→  1037→      {/* Top bar */}
  1038→  1038→      <div className="flex items-center justify-between mb-6">
  1039→  1039→        <div className="flex items-center gap-2">
  1040→  1040→          <Button variant="ghost" onClick={handleNewImage} className="gap-2">
  1041→  1041→            <ChevronLeft className="w-4 h-4" />
  1042→  1042→            صورة جديدة
  1043→  1043→          </Button>
  1044→  1044→          {editedImage && (
  1045→  1045→            <Button variant="outline" size="sm" onClick={handleResetToOriginal} className="gap-1.5 text-xs">
  1046→  1046→              <RotateCcw className="w-3.5 h-3.5" />
  1047→  1047→              العودة للأصل
  1048→  1048→            </Button>
  1049→  1049→          )}
  1050→  1050→          {editCount > 0 && (
  1051→  1051→            <span className="text-xs text-muted-foreground bg-muted/50 px-2 py-1 rounded-full">
  1052→  1052→              {editCount} تعديل{editCount > 1 ? 'ات' : ''}
  1053→  1053→            </span>
  1054→  1054→          )}
  1055→  1055→        </div>
  1056→  1056→        {editedImage && (
  1057→  1057→          <Button onClick={handleDownload} className="gap-2">
  1058→  1058→            <Download className="w-4 h-4" />
  1059→  1059→            تحميل الصورة
  1060→  1060→          </Button>
  1061→  1061→        )}
  1062→  1062→      </div>
  1063→  1063→
  1064→  1064→      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
  1065→  1065→        {/* Image Preview (2/3 width on desktop) */}
  1066→  1066→        <div className="lg:col-span-2">
  1067→  1067→          <Card className="glass overflow-hidden">
  1068→  1068→            <CardContent className="p-0 relative">
  1069→  1069→              {/* Image display */}
  1070→  1070→              <div className="relative min-h-[300px] sm:min-h-[400px] flex items-center justify-center bg-black/30">
  1071→  1071→                {isEditing && (
  1072→  1072→                  <div className="absolute inset-0 z-20 flex flex-col items-center justify-center bg-black/50 backdrop-blur-sm">
  1073→  1073→                    <Loader2 className="w-12 h-12 text-primary animate-spin mb-4" />
  1074→  1074→                    <p className="text-lg font-semibold mb-2">جارٍ التعديل بالذكاء الاصطناعي...</p>
  1075→  1075→                    <Progress value={editProgress} className="w-48 h-2" />
  1076→  1076→                    <p className="text-sm text-muted-foreground mt-2">{editProgress}%</p>
  1077→  1077→                  </div>
  1078→  1078→                )}
  1079→  1079→
  1080→  1080→                {editedImage ? (
  1081→  1081→                  <ComparisonSlider original={originalImage!} edited={editedImage} />
  1082→  1082→                ) : (
  1083→  1083→                  <img
  1084→  1084→                    src={originalImage!}
  1085→  1085→                    alt="Original"
  1086→  1086→                    className="max-w-full max-h-[70vh] object-contain mx-auto"
  1087→  1087→                  />
  1088→  1088→                )}
  1089→  1089→              </div>
  1090→  1090→
  1091→  1091→              {/* Image info bar */}
  1092→  1092→              <div className="p-3 border-t border-border/50 flex items-center justify-between text-sm text-muted-foreground">
  1093→  1093→                <span className="truncate max-w-[60%]">{originalFileName || 'صورة'}</span>
  1094→  1094→                <Button variant="ghost" size="sm" className="gap-1 text-xs h-8" onClick={() => fileInputRef.current?.click()}>
  1095→  1095→                  <RotateCcw className="w-3 h-3" />
  1096→  1096→                  تغيير الصورة
  1097→  1097→                </Button>
  1098→  1098→                <input ref={fileInputRef} type="file" accept="image/*" className="hidden" onChange={handleReupload} />
  1099→  1099→              </div>
  1100→  1100→            </CardContent>
  1101→  1101→          </Card>
  1102→  1102→
  1103→  1103→          {/* Analysis section */}
  1104→  1104→          <div className="mt-4">
  1105→  1105→            <Button
  1106→  1106→              variant="outline"
  1107→  1107→              onClick={handleAnalyze}
  1108→  1108→              disabled={isAnalyzing || !originalImage}
  1109→  1109→              className="w-full gap-2 mb-3"
  1110→  1110→            >
  1111→  1111→              {isAnalyzing ? (
  1112→  1112→                <Loader2 className="w-4 h-4 animate-spin" />
  1113→  1113→              ) : (
  1114→  1114→                <Eye className="w-4 h-4" />
  1115→  1115→              )}
  1116→  1116→              تحليل الصورة بالذكاء الاصطناعي
  1117→  1117→            </Button>
  1118→  1118→            <AnimatePresence>
  1119→  1119→              {analysis && (
  1120→  1120→                <motion.div
  1121→  1121→                  initial={{ opacity: 0, height: 0 }}
  1122→  1122→                  animate={{ opacity: 1, height: 'auto' }}
  1123→  1123→                  exit={{ opacity: 0, height: 0 }}
  1124→  1124→                >
  1125→  1125→                  <Card className="glass">
  1126→  1126→                    <CardContent className="p-4">
  1127→  1127→                      <h3 className="text-sm font-semibold mb-2 flex items-center gap-2">
  1128→  1128→                        <Eye className="w-4 h-4 text-primary" />
  1129→  1129→                        نتيجة التحليل
  1130→  1130→                      </h3>
  1131→  1131→                      <p className="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap">{analysis}</p>
  1132→  1132→                    </CardContent>
  1133→  1133→                  </Card>
  1134→  1134→                </motion.div>
  1135→  1135→              )}
  1136→  1136→            </AnimatePresence>
  1137→  1137→          </div>
  1138→  1138→        </div>
  1139→  1139→
  1140→  1140→        {/* Tools Panel (1/3 width on desktop) */}
  1141→  1141→        <div className="lg:col-span-1 space-y-4">
  1142→  1142→          {/* Quick Tools */}
  1143→  1143→          <Card className="glass">
  1144→  1144→            <CardContent className="p-4">
  1145→  1145→              <h3 className="font-semibold mb-3 flex items-center gap-2">
  1146→  1146→                <Wand2 className="w-4 h-4 text-primary" />
  1147→  1147→                أدوات التحرير السريعة
  1148→  1148→              </h3>
  1149→  1149→              <div className="grid grid-cols-2 gap-2">
  1150→  1150→                {EDIT_TOOLS.map((tool) => {
  1151→  1151→                  const Icon = tool.icon;
  1152→  1152→                  const isLoading = isEditing && selectedTool === tool.id;
  1153→  1153→                  return (
  1154→  1154→                    <button
  1155→  1155→                      key={tool.id}
  1156→  1156→                      onClick={() => {
  1157→  1157→                        if (isEditing) return;
  1158→  1158→                        setSelectedTool(tool.id);
  1159→  1159→                        handleEdit(tool.prompt, tool.label, 'quick-tool');
  1160→  1160→                      }}
  1161→  1161→                      disabled={isEditing}
  1162→  1162→                      className={`
  1163→  1163→                        tool-card relative flex flex-col items-center gap-2 p-3 rounded-xl
  1164→  1164→                        border border-border/50 text-center transition-all
  1165→  1165→                        ${isLoading ? 'ring-2 ring-primary' : 'hover:border-primary/40'}
  1166→  1166→                        ${isEditing ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
  1167→  1167→                      `}
  1168→  1168→                    >
  1169→  1169→                      <div className={`w-10 h-10 rounded-lg bg-gradient-to-br ${tool.color} flex items-center justify-center`}>
  1170→  1170→                        {isLoading ? (
  1171→  1171→                          <Loader2 className="w-5 h-5 text-primary animate-spin" />
  1172→  1172→                        ) : (
  1173→  1173→                          <Icon className="w-5 h-5 text-foreground" />
  1174→  1174→                        )}
  1175→  1175→                      </div>
  1176→  1176→                      <span className="text-xs font-medium">{tool.label}</span>
  1177→  1177→                    </button>
  1178→  1178→                  );
  1179→  1179→                })}
  1180→  1180→              </div>
  1181→  1181→            </CardContent>
  1182→  1182→          </Card>
  1183→  1183→
  1184→  1184→          {/* Custom Prompt */}
  1185→  1185→          <Card className="glass">
  1186→  1186→            <CardContent className="p-4">
  1187→  1187→              <h3 className="font-semibold mb-3 flex items-center gap-2">
  1188→  1188→                <Pencil className="w-4 h-4 text-primary" />
  1189→  1189→                تعديل مخصص
  1190→  1190→              </h3>
  1191→  1191→              <Textarea
  1192→  1192→                value={customPrompt}
  1193→  1193→                onChange={(e) => setCustomPrompt(e.target.value)}
  1194→  1194→                placeholder="صِف التعديل الذي تريده... مثال: غيّر الخلفية إلى شاطئ عند الغروب"
  1195→  1195→                className="min-h-[100px] resize-none mb-3"
  1196→  1196→                disabled={isEditing}
  1197→  1197→              />
  1198→  1198→              <Button
  1199→  1199→                onClick={handleCustomEdit}
  1200→  1200→                disabled={isEditing || !customPrompt.trim()}
  1201→  1201→                className="w-full gap-2"
  1202→  1202→              >
  1203→  1203→                {isEditing ? (
  1204→  1204→                  <Loader2 className="w-4 h-4 animate-spin" />
  1205→  1205→                ) : (
  1206→  1206→                  <Sparkles className="w-4 h-4" />
  1207→  1207→                )}
  1208→  1208→                تطبيق التعديل
  1209→  1209→              </Button>
  1210→  1210→            </CardContent>
  1211→  1211→          </Card>
  1212→  1212→
  1213→  1213→          {/* Error display */}
  1214→  1214→          <AnimatePresence>
  1215→  1215→            {editError && (
  1216→  1216→              <motion.div
  1217→  1217→                initial={{ opacity: 0, y: 10 }}
  1218→  1218→                animate={{ opacity: 1, y: 0 }}
  1219→  1219→                exit={{ opacity: 0, y: -10 }}
  1220→  1220→              >
  1221→  1221→                <Card className="border-destructive/50 bg-destructive/10">
  1222→  1222→                  <CardContent className="p-4 flex items-start gap-3">
  1223→  1223→                    <X className="w-5 h-5 text-destructive mt-0.5 shrink-0" />
  1224→  1224→                    <div className="flex-1">
  1225→  1225→                      <p className="text-sm font-medium text-destructive">حدث خطأ</p>
  1226→  1226→                      <p className="text-xs text-muted-foreground mt-1">{editError}</p>
  1227→  1227→                    </div>
  1228→  1228→                    {lastEditArgs && (
  1229→  1229→                      <Button
  1230→  1230→                        variant="outline"
  1231→  1231→                        size="sm"
  1232→  1232→                        className="shrink-0 gap-1 text-xs h-8"
  1233→  1233→                        onClick={() => {
  1234→  1234→                          setEditError(null);
  1235→  1235→                          handleEdit(lastEditArgs.prompt, lastEditArgs.label, lastEditArgs.toolType);
  1236→  1236→                        }}
  1237→  1237→                      >
  1238→  1238→                        <RotateCcw className="w-3 h-3" />
  1239→  1239→                        إعادة المحاولة
  1240→  1240→                      </Button>
  1241→  1241→                    )}
  1242→  1242→                  </CardContent>
  1243→  1243→                </Card>
  1244→  1244→              </motion.div>
  1245→  1245→            )}
  1246→  1246→          </AnimatePresence>
  1247→  1247→        </div>
  1248→  1248→      </div>
  1249→  1249→    </main>
  1250→  1250→  );
  1251→  1251→}
  1252→  1252→
  1253→  1253→/* ─── Gallery View ─── */
  1254→  1254→function GalleryView() {
  1255→  1255→  const { projects, setProjects, setView, setOriginalImage, setOriginalFileName, setEditedImage, setAnalysis } = useAppStore();
  1256→  1256→  const [isLoading, setIsLoading] = useState(true);
  1257→  1257→
  1258→  1258→  useEffect(() => {
  1259→  1259→    const loadProjects = async () => {
  1260→  1260→      try {
  1261→  1261→        const res = await fetch('/api/projects');
  1262→  1262→        if (res.ok) {
  1263→  1263→          const data = await res.json();
  1264→  1264→          setProjects(data);
  1265→  1265→        }
  1266→  1266→      } catch {}
  1267→  1267→      setIsLoading(false);
  1268→  1268→    };
  1269→  1269→    loadProjects();
  1270→  1270→  }, [setProjects]);
  1271→  1271→
  1272→  1272→  const handleDelete = useCallback(async (id: string) => {
  1273→  1273→    try {
  1274→  1274→      const res = await fetch(`/api/projects/${id}`, { method: 'DELETE' });
  1275→  1275→      if (res.ok) {
  1276→  1276→        setProjects(projects.filter((p) => p.id !== id));
  1277→  1277→      }
  1278→  1278→    } catch {}
  1279→  1279→  }, [projects, setProjects]);
  1280→  1280→
  1281→  1281→  const handleOpen = useCallback((project: PhotoProject) => {
  1282→  1282→    const lastEdit = project.edits?.[project.edits.length - 1];
  1283→  1283→    if (lastEdit?.editedPath) {
  1284→  1284→      setOriginalImage(lastEdit.originalPath);
  1285→  1285→      setEditedImage(lastEdit.editedPath);
  1286→  1286→      setOriginalFileName(project.title);
  1287→  1287→      setAnalysis(null);
  1288→  1288→      setView('editor');
  1289→  1289→    } else if (lastEdit) {
  1290→  1290→      setOriginalImage(lastEdit.originalPath);
  1291→  1291→      setEditedImage(null);
  1292→  1292→      setOriginalFileName(project.title);
  1293→  1293→      setAnalysis(null);
  1294→  1294→      setView('editor');
  1295→  1295→    }
  1296→  1296→  }, [setOriginalImage, setEditedImage, setOriginalFileName, setAnalysis, setView]);
  1297→  1297→
  1298→  1298→  if (isLoading) {
  1299→  1299→    return (
  1300→  1300→      <main className="flex-1 flex items-center justify-center">
  1301→  1301→        <Loader2 className="w-8 h-8 text-primary animate-spin" />
  1302→  1302→      </main>
  1303→  1303→    );
  1304→  1304→  }
  1305→  1305→
  1306→  1306→  return (
  1307→  1307→    <main className="flex-1 px-4 py-8 max-w-6xl mx-auto w-full">
  1308→  1308→      <motion.div
  1309→  1309→        initial={{ opacity: 0, y: -10 }}
  1310→  1310→        animate={{ opacity: 1, y: 0 }}
  1311→  1311→        className="text-center mb-8"
  1312→  1312→      >
  1313→  1313→        <h2 className="text-3xl font-bold mb-2">
  1314→  1314→          <span className="gradient-text">المعرض</span>
  1315→  1315→        </h2>
  1316→  1316→        <p className="text-muted-foreground">جميع مشاريع تعديل الصور السابقة</p>
  1317→  1317→      </motion.div>
  1318→  1318→
  1319→  1319→      {projects.length === 0 ? (
  1320→  1320→        <motion.div
  1321→  1321→          initial={{ opacity: 0 }}
  1322→  1322→          animate={{ opacity: 1 }}
  1323→  1323→          className="text-center py-20"
  1324→  1324→        >
  1325→  1325→          <div className="w-20 h-20 rounded-full bg-muted/30 flex items-center justify-center mx-auto mb-4">
  1326→  1326→            <ImageIcon className="w-10 h-10 text-muted-foreground" />
  1327→  1327→          </div>
  1328→  1328→          <p className="text-lg text-muted-foreground mb-4">لا توجد مشاريع بعد</p>
  1329→  1329→          <Button onClick={() => setView('home')} className="gap-2">
  1330→  1330→            <Upload className="w-4 h-4" />
  1331→  1331→            ابدأ بتعديل صورة
  1332→  1332→          </Button>
  1333→  1333→        </motion.div>
  1334→  1334→      ) : (
  1335→  1335→        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
  1336→  1336→          {projects.map((project, i) => {
  1337→  1337→            const lastEdit = project.edits?.[project.edits.length - 1];
  1338→  1338→            const thumbnail = lastEdit?.editedPath || lastEdit?.originalPath || '';
  1339→  1339→            return (
  1340→  1340→              <motion.div
  1341→  1341→                key={project.id}
  1342→  1342→                initial={{ opacity: 0, y: 20 }}
  1343→  1343→                animate={{ opacity: 1, y: 0 }}
  1344→  1344→                transition={{ delay: i * 0.05 }}
  1345→  1345→              >
  1346→  1346→                <Card className="glass overflow-hidden group cursor-pointer hover:border-primary/40 transition-all" onClick={() => handleOpen(project)}>
  1347→  1347→                  <div className="aspect-video bg-black/30 relative overflow-hidden">
  1348→  1348→                    {thumbnail ? (
  1349→  1349→                      <img src={thumbnail} alt={project.title} className="w-full h-full object-cover" />
  1350→  1350→                    ) : (
  1351→  1351→                      <div className="w-full h-full flex items-center justify-center">
  1352→  1352→                        <ImageIcon className="w-8 h-8 text-muted-foreground" />
  1353→  1353→                      </div>
  1354→  1354→                    )}
  1355→  1355→                    <div className="absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-colors flex items-center justify-center">
  1356→  1356→                      <span className="opacity-0 group-hover:opacity-100 transition-opacity bg-white/20 backdrop-blur-sm px-3 py-1.5 rounded-lg text-sm font-medium">
  1357→  1357→                        فتح المشروع
  1358→  1358→                      </span>
  1359→  1359→                    </div>
  1360→  1360→                  </div>
  1361→  1361→                  <CardContent className="p-3 flex items-center justify-between">
  1362→  1362→                    <div>
  1363→  1363→                      <p className="font-medium text-sm truncate">{project.title}</p>
  1364→  1364→                      <p className="text-xs text-muted-foreground">
  1365→  1365→                        {new Date(project.createdAt).toLocaleDateString('ar-EG')}
  1366→  1366→                        {project.edits?.length ? ` — ${project.edits.length} تعديل` : ''}
  1367→  1367→                      </p>
  1368→  1368→                    </div>
  1369→  1369→                    <Button
  1370→  1370→                      variant="ghost"
  1371→  1371→                      size="icon"
  1372→  1372→                      className="h-8 w-8 text-muted-foreground hover:text-destructive shrink-0"
  1373→  1373→                      onClick={(e) => {
  1374→  1374→                        e.stopPropagation();
  1375→  1375→                        handleDelete(project.id);
  1376→  1376→                      }}
  1377→  1377→                    >
  1378→  1378→                      <Trash2 className="w-4 h-4" />
  1379→  1379→                    </Button>
  1380→  1380→                  </CardContent>
  1381→  1381→                </Card>
  1382→  1382→              </motion.div>
  1383→  1383→            );
  1384→  1384→          })}
  1385→  1385→        </div>
  1386→  1386→      )}
  1387→  1387→    </main>
  1388→  1388→  );
  1389→  1389→}
  1390→  1390→
  1391→  1391→/* ─── Generate View ─── */
  1392→  1392→const SIZE_OPTIONS = [
  1393→  1393→  { id: 'square', label: 'مربع', icon: Square, desc: '1024×1024', color: 'from-primary/20 to-primary/5' },
  1394→  1394→  { id: 'portrait', label: 'عمودي', icon: RectangleVertical, desc: '864×1152', color: 'from-amber-500/20 to-amber-600/5' },
  1395→  1395→  { id: 'landscape', label: 'أفقي', icon: Monitor, desc: '1344×768', color: 'from-emerald-500/20 to-emerald-600/5' },
  1396→  1396→  { id: 'wide', label: 'عريض', icon: Monitor, desc: '1440×720', color: 'from-violet-500/20 to-violet-600/5' },
  1397→  1397→];
  1398→  1398→
  1399→  1399→function GenerateView() {
  1400→  1400→  const { setOriginalImage, setOriginalFileName, setView, setEditedImage, setAnalysis } = useAppStore();
  1401→  1401→  const [prompt, setPrompt] = useState('');
  1402→  1402→  const [selectedSize, setSelectedSize] = useState('square');
  1403→  1403→  const [isGenerating, setIsGenerating] = useState(false);
  1404→  1404→  const [generatedImage, setGeneratedImage] = useState<string | null>(null);
  1405→  1405→  const [error, setError] = useState<string | null>(null);
  1406→  1406→  const [progress, setProgress] = useState(0);
  1407→  1407→  const [progressMsg, setProgressMsg] = useState('');
  1408→  1408→
  1409→  1409→  const pollIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
  1410→  1410→
  1411→  1411→  const handleGenerate = useCallback(async () => {
  1412→  1412→    if (!prompt.trim() || isGenerating) return;
  1413→  1413→    setIsGenerating(true);
  1414→  1414→    setError(null);
  1415→  1415→    setGeneratedImage(null);
  1416→  1416→    setProgress(0);
  1417→  1417→    setProgressMsg('جارٍ البدء...');
  1418→  1418→
  1419→  1419→    try {
  1420→  1420→      // Step 1: Start the job (returns immediately)
  1421→  1421→      const startRes = await fetch('/api/generate', {
  1422→  1422→        method: 'POST',
  1423→  1423→        headers: { 'Content-Type': 'application/json' },
  1424→  1424→        body: JSON.stringify({ prompt: prompt.trim(), sizeId: selectedSize }),
  1425→  1425→      });
  1426→  1426→
  1427→  1427→      const startData = await startRes.json();
  1428→  1428→
  1429→  1429→      if (!startRes.ok || startData.error) {
  1430→  1430→        throw new Error(startData.error || 'حدث خطأ غير متوقع');
  1431→  1431→      }
  1432→  1432→
  1433→  1433→      const { jobId } = startData;
  1434→  1434→      if (!jobId) {
  1435→  1435→        throw new Error('لم يتم إنشاء العملية');
  1436→  1436→      }
  1437→  1437→
  1438→  1438→      // Step 2: Poll every 3 seconds until done or error
  1439→  1439→      const result = await new Promise<{ imageUrl: string }>((resolve, reject) => {
  1440→  1440→        let attempts = 0;
  1441→  1441→        const maxAttempts = 120; // 6 minutes max
  1442→  1442→
  1443→  1443→        pollIntervalRef.current = setInterval(async () => {
  1444→  1444→          attempts++;
  1445→  1445→
  1446→  1446→          if (attempts > maxAttempts) {
  1447→  1447→            if (pollIntervalRef.current) clearInterval(pollIntervalRef.current);
  1448→  1448→            pollIntervalRef.current = null;
  1449→  1449→            reject(new Error('انتهت المهلة. حاول مرة أخرى.'));
  1450→  1450→            return;
  1451→  1451→          }
  1452→  1452→
  1453→  1453→          try {
  1454→  1454→            const pollRes = await fetch(`/api/generate?jobId=${jobId}`);
  1455→  1455→            const pollData = await pollRes.json();
  1456→  1456→
  1457→  1457→            if (pollData.error && !pollData.status) {
  1458→  1458→              if (pollIntervalRef.current) clearInterval(pollIntervalRef.current);
  1459→  1459→              pollIntervalRef.current = null;
  1460→  1460→              reject(new Error(pollData.error));
  1461→  1461→              return;
  1462→  1462→            }
  1463→  1463→
  1464→  1464→            // Update progress
  1465→  1465→            if (pollData.progress !== undefined) {
  1466→  1466→              setProgress((prev) => Math.max(prev, pollData.progress));
  1467→  1467→            }
  1468→  1468→            if (pollData.message) {
  1469→  1469→              setProgressMsg(pollData.message);
  1470→  1470→            }
  1471→  1471→
  1472→  1472→            // Check terminal states
  1473→  1473→            if (pollData.status === 'done' && pollData.imageUrl) {
  1474→  1474→              if (pollIntervalRef.current) clearInterval(pollIntervalRef.current);
  1475→  1475→              pollIntervalRef.current = null;
  1476→  1476→              resolve({ imageUrl: pollData.imageUrl });
  1477→  1477→            } else if (pollData.status === 'error') {
  1478→  1478→              if (pollIntervalRef.current) clearInterval(pollIntervalRef.current);
  1479→  1479→              pollIntervalRef.current = null;
  1480→  1480→              reject(new Error(pollData.error || 'فشل توليد الصورة'));
  1481→  1481→            }
  1482→  1482→          } catch (pollErr: unknown) {
  1483→  1483→            // Network error on a single poll — don't abort, just retry
  1484→  1484→            console.error('[GEN] Poll error, retrying...', pollErr);
  1485→  1485→          }
  1486→  1486→        }, 3000);
  1487→  1487→      });
  1488→  1488→
  1489→  1489→      setGeneratedImage(result.imageUrl);
  1490→  1490→    } catch (err: unknown) {
  1491→  1491→      setError(err instanceof Error ? err.message : 'حدث خطأ');
  1492→  1492→    } finally {
  1493→  1493→      if (pollIntervalRef.current) {
  1494→  1494→        clearInterval(pollIntervalRef.current);
  1495→  1495→        pollIntervalRef.current = null;
  1496→  1496→      }
  1497→  1497→      setIsGenerating(false);
  1498→  1498→    }
  1499→  1499→  }, [prompt, selectedSize, isGenerating]);
  1500→  1500→
  1501→  1501→  // Cleanup polling on unmount
  1502→  1502→  useEffect(() => {
  1503→  1503→    return () => {
  1504→  1504→      if (pollIntervalRef.current) {
  1505→  1505→        clearInterval(pollIntervalRef.current);
  1506→  1506→        pollIntervalRef.current = null;
  1507→  1507→      }
  1508→  1508→    };
  1509→  1509→  }, []);
  1510→  1510→
  1511→  1511→  const handleSendToEditor = useCallback(() => {
  1512→  1512→    if (!generatedImage) return;
  1513→  1513→    setOriginalImage(generatedImage);
  1514→  1514→    setOriginalFileName('Fouad-AI-generated.png');
  1515→  1515→    setEditedImage(null);
  1516→  1516→    setAnalysis(null);
  1517→  1517→    setView('editor');
  1518→  1518→  }, [generatedImage, setOriginalImage, setOriginalFileName, setEditedImage, setAnalysis, setView]);
  1519→  1519→
  1520→  1520→  const handleDownload = useCallback(() => {
  1521→  1521→    if (!generatedImage) return;
  1522→  1522→    const a = document.createElement('a');
  1523→  1523→    a.href = generatedImage;
  1524→  1524→    a.download = 'Fouad-AI-generated.png';
  1525→  1525→    a.click();
  1526→  1526→  }, [generatedImage]);
  1527→  1527→
  1528→  1528→  return (
  1529→  1529→    <main className="flex-1 px-4 py-6 max-w-5xl mx-auto w-full">
  1530→  1530→      <motion.div
  1531→  1531→        initial={{ opacity: 0, y: -10 }}
  1532→  1532→        animate={{ opacity: 1, y: 0 }}
  1533→  1533→        className="text-center mb-8"
  1534→  1534→      >
  1535→  1535→        <h2 className="text-3xl font-bold mb-2">
  1536→  1536→          <span className="gradient-text">رسم الصور بالذكاء</span>
  1537→  1537→        </h2>
  1538→  1538→        <p className="text-muted-foreground">صِف الصورة التي تريدها وسنرسمها لك</p>
  1539→  1539→      </motion.div>
  1540→  1540→
  1541→  1541→      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
  1542→  1542→        {/* Left: Input */}
  1543→  1543→        <div className="space-y-4">
  1544→  1544→          {/* Prompt */}
  1545→  1545→          <Card className="glass">
  1546→  1546→            <CardContent className="p-4">
  1547→  1547→              <h3 className="font-semibold mb-3 flex items-center gap-2">
  1548→  1548→                <Pencil className="w-4 h-4 text-primary" />
  1549→  1549→                وصف الصورة
  1550→  1550→              </h3>
  1551→  1551→              <Textarea
  1552→  1552→                value={prompt}
  1553→  1553→                onChange={(e) => setPrompt(e.target.value)}
  1554→  1554→                placeholder="مثال: قطة جميلة تجلس في حديقة مليئة بالزهور عند الغروب..."
  1555→  1555→                className="min-h-[120px] resize-none mb-3"
  1556→  1556→                disabled={isGenerating}
  1557→  1557→              />
  1558→  1558→              <p className="text-xs text-muted-foreground mb-3">
  1559→  1559→                يمكنك الكتابة بالعربية أو الإنجليزية
  1560→  1560→              </p>
  1561→  1561→
  1562→  1562→              {/* Size selector */}
  1563→  1563→              <h4 className="text-sm font-medium mb-2">حجم الصورة</h4>
  1564→  1564→              <div className="grid grid-cols-4 gap-2">
  1565→  1565→                {SIZE_OPTIONS.map((s) => {
  1566→  1566→                  const Icon = s.icon;
  1567→  1567→                  return (
  1568→  1568→                    <button
  1569→  1569→                      key={s.id}
  1570→  1570→                      onClick={() => setSelectedSize(s.id)}
  1571→  1571→                      disabled={isGenerating}
  1572→  1572→                      className={`
  1573→  1573→                        flex flex-col items-center gap-1.5 p-2.5 rounded-xl border text-center transition-all
  1574→  1574→                        ${selectedSize === s.id
  1575→  1575→                          ? 'border-primary bg-primary/10 ring-1 ring-primary/30'
  1576→  1576→                          : 'border-border/50 hover:border-primary/30'}
  1577→  1577→                        ${isGenerating ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
  1578→  1578→                      `}
  1579→  1579→                    >
  1580→  1580→                      <Icon className="w-4 h-4 text-foreground" />
  1581→  1581→                      <span className="text-xs font-medium">{s.label}</span>
  1582→  1582→                      <span className="text-[10px] text-muted-foreground">{s.desc}</span>
  1583→  1583→                    </button>
  1584→  1584→                  );
  1585→  1585→                })}
  1586→  1586→              </div>
  1587→  1587→
  1588→  1588→              {/* Generate button */}
  1589→  1589→              <Button
  1590→  1590→                onClick={handleGenerate}
  1591→  1591→                disabled={isGenerating || !prompt.trim()}
  1592→  1592→                className="w-full gap-2 mt-4"
  1593→  1593→                size="lg"
  1594→  1594→              >
  1595→  1595→                {isGenerating ? (
  1596→  1596→                  <Loader2 className="w-5 h-5 animate-spin" />
  1597→  1597→                ) : (
  1598→  1598→                  <WandSparkles className="w-5 h-5" />
  1599→  1599→                )}
  1600→  1600→                {isGenerating ? 'جارٍ الرسم...' : 'ارسم الصورة'}
  1601→  1601→              </Button>
  1602→  1602→            </CardContent>
  1603→  1603→          </Card>
  1604→  1604→
  1605→  1605→          {/* Error */}
  1606→  1606→          <AnimatePresence>
  1607→  1607→            {error && (
  1608→  1608→              <motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -10 }}>
  1609→  1609→                <Card className="border-destructive/50 bg-destructive/10">
  1610→  1610→                  <CardContent className="p-4 flex items-start gap-3">
  1611→  1611→                    <X className="w-5 h-5 text-destructive mt-0.5 shrink-0" />
  1612→  1612→                    <div className="flex-1">
  1613→  1613→                      <p className="text-sm font-medium text-destructive">خطأ</p>
  1614→  1614→                      <p className="text-xs text-muted-foreground mt-1">{error}</p>
  1615→  1615→                    </div>
  1616→  1616→                    <Button
  1617→  1617→                      variant="outline"
  1618→  1618→                      size="sm"
  1619→  1619→                      className="shrink-0 gap-1 text-xs"
  1620→  1620→                      onClick={() => handleGenerate()}
  1621→  1621→                    >
  1622→  1622→                      <RotateCcw className="w-3 h-3" />
  1623→  1623→                      إعادة المحاولة
  1624→  1624→                    </Button>
  1625→  1625→                  </CardContent>
  1626→  1626→                </Card>
  1627→  1627→              </motion.div>
  1628→  1628→            )}
  1629→  1629→          </AnimatePresence>
  1630→  1630→        </div>
  1631→  1631→
  1632→  1632→        {/* Right: Result */}
  1633→  1633→        <div>
  1634→  1634→          <Card className="glass overflow-hidden">
  1635→  1635→            <CardContent className="p-0">
  1636→  1636→              <div className="relative min-h-[400px] flex items-center justify-center bg-black/30">
  1637→  1637→                {isGenerating && (
  1638→  1638→                  <div className="absolute inset-0 z-10 flex flex-col items-center justify-center bg-black/50 backdrop-blur-sm gap-4 px-8">
  1639→  1639→                    <Loader2 className="w-16 h-16 text-primary animate-spin" />
  1640→  1640→                    <p className="text-sm font-medium animate-pulse">{progressMsg || 'جارٍ رسم الصورة بالذكاء الاصطناعي...'}</p>
  1641→  1641→                    <Progress value={progress} className="w-64 h-2" />
  1642→  1642→                    <p className="text-xs text-muted-foreground">{progress}%</p>
  1643→  1643→                  </div>
  1644→  1644→                )}
  1645→  1645→
  1646→  1646→                {generatedImage ? (
  1647→  1647→                  <img
  1648→  1648→                    src={generatedImage}
  1649→  1649→                    alt="Generated"
  1650→  1650→                    className="w-full h-auto block"
  1651→  1651→                  />
  1652→  1652→                ) : !isGenerating ? (
  1653→  1653→                  <div className="flex flex-col items-center gap-4 py-16">
  1654→  1654→                    <div className="w-20 h-20 rounded-full bg-muted/20 flex items-center justify-center">
  1655→  1655→                      <WandSparkles className="w-10 h-10 text-muted-foreground/50" />
  1656→  1656→                    </div>
  1657→  1657→                    <p className="text-muted-foreground text-center px-4">
  1658→  1658→                      الصورة المرسومة ستظهر هنا
  1659→  1659→                    </p>
  1660→  1660→                  </div>
  1661→  1661→                ) : null}
  1662→  1662→              </div>
  1663→  1663→
  1664→  1664→              {/* Action buttons */}
  1665→  1665→              {generatedImage && (
  1666→  1666→                <div className="p-3 border-t border-border/50 flex items-center gap-2">
  1667→  1667→                  <Button onClick={handleSendToEditor} className="flex-1 gap-2" size="sm">
  1668→  1668→                    <ArrowRight className="w-4 h-4" />
  1669→  1669→                    تعديل الصورة
  1670→  1670→                  </Button>
  1671→  1671→                  <Button onClick={handleDownload} variant="outline" className="gap-2" size="sm">
  1672→  1672→                    <Download className="w-4 h-4" />
  1673→  1673→                    تحميل
  1674→  1674→                  </Button>
  1675→  1675→                </div>
  1676→  1676→              )}
  1677→  1677→            </CardContent>
  1678→  1678→          </Card>
  1679→  1679→
  1680→  1680→          {/* Example prompts */}
  1681→  1681→          {!generatedImage && !isGenerating && (
  1682→  1682→            <Card className="glass mt-4">
  1683→  1683→              <CardContent className="p-4">
  1684→  1684→                <h4 className="text-sm font-medium mb-3 flex items-center gap-2">
  1685→  1685→                  <Sparkles className="w-4 h-4 text-primary" />
  1686→  1686→                  أفكار للتجربة
  1687→  1687→                </h4>
  1688→  1688→                <div className="flex flex-wrap gap-2">
  1689→  1689→                  {[
  1690→  1690→                    'غروب على شاطئ بصخور ملساء',
  1691→  1691→                    'مدينة مستقبلية بالليل',
  1692→  1692→                    'طائر فيلمنق زاهي الألوان',
  1693→  1693→                    'قرية جبلية وسط الضباب',
  1694→  1694→                  ].map((example) => (
  1695→  1695→                    <button
  1696→  1696→                      key={example}
  1697→  1697→                      onClick={() => setPrompt(example)}
  1698→  1698→                      className="text-xs px-3 py-1.5 rounded-full border border-border/50 hover:border-primary/40 hover:bg-primary/5 transition-all cursor-pointer"
  1699→  1699→                    >
  1700→  1700→                      {example}
  1701→  1701→                    </button>
  1702→  1702→                  ))}
  1703→  1703→                </div>
  1704→  1704→              </CardContent>
  1705→  1705→            </Card>
  1706→  1706→          )}
  1707→  1707→        </div>
  1708→  1708→      </div>
  1709→  1709→    </main>
  1710→  1710→  );
  1711→  1711→}
  1712→  1712→
  1713→  1713→/* ─── Main Client Component ─── */
  1714→  1714→export default function HomeClient() {
  1715→  1715→  const { currentView, editError, setEditError } = useAppStore();
  1716→  1716→  const [bannerAdminOpen, setBannerAdminOpen] = useState(false);
  1717→  1717→  const [loginOpen, setLoginOpen] = useState(false);
  1718→  1718→  const { isAdmin, isLoading, refresh: refreshSession } = useAdminSession();
  1719→  1719→
  1720→  1720→  const handleLoginSuccess = useCallback(() => {
  1721→  1721→    refreshSession().then(() => {
  1722→  1722→      setBannerAdminOpen(true);
  1723→  1723→    });
  1724→  1724→  }, [refreshSession]);
  1725→  1725→
  1726→  1726→  const handleLogout = useCallback(async () => {
  1727→  1727→    await signOut({ redirect: false });
  1728→  1728→    refreshSession();
  1729→  1729→  }, [refreshSession]);
  1730→  1730→
  1731→  1731→  const handleSettingsClick = useCallback(() => {
  1732→  1732→    if (isAdmin) {
  1733→  1733→      setBannerAdminOpen(true);
  1734→  1734→    } else {
  1735→  1735→      setLoginOpen(true);
  1736→  1736→    }
  1737→  1737→  }, [isAdmin]);
  1738→  1738→
  1739→  1739→  // Dismiss errors on click outside
  1740→  1740→  useEffect(() => {
  1741→  1741→    if (editError) {
  1742→  1742→      const timer = setTimeout(() => setEditError(null), 8000);
  1743→  1743→      return () => clearTimeout(timer);
  1744→  1744→    }
  1745→  1745→  }, [editError, setEditError]);
  1746→  1746→
  1747→  1747→  return (
  1748→  1748→    <div className="min-h-screen flex flex-col">
  1749→  1749→      <Header
  1750→  1750→        isAdmin={isAdmin}
  1751→  1751→        isLoading={isLoading}
  1752→  1752→        onOpenBannerAdmin={handleSettingsClick}
  1753→  1753→        onOpenLogin={() => setLoginOpen(true)}
  1754→  1754→        onLogout={handleLogout}
  1755→  1755→      />
  1756→  1756→      <div className="w-full">
  1757→  1757→        <BannerDisplay position="top" />
  1758→  1758→      </div>
  1759→  1759→      <LoginDialog open={loginOpen} onOpenChange={setLoginOpen} onLoginSuccess={handleLoginSuccess} />
  1760→  1760→      <BannerAdmin open={bannerAdminOpen} onOpenChange={setBannerAdminOpen} />
  1761→  1761→      <AnimatePresence mode="wait">
  1762→  1762→        {currentView === 'home' && (
  1763→  1763→          <motion.div key="home" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex-1">
  1764→  1764→            <HomeView />
  1765→  1765→          </motion.div>
  1766→  1766→        )}
  1767→  1767→        {currentView === 'editor' && (
  1768→  1768→          <motion.div key="editor" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex-1 flex flex-col">
  1769→  1769→            <EditorView />
  1770→  1770→          </motion.div>
  1771→  1771→        )}
  1772→  1772→        {currentView === 'gallery' && (
  1773→  1773→          <motion.div key="gallery" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex-1">
  1774→  1774→            <GalleryView />
  1775→  1775→          </motion.div>
  1776→  1776→        )}
  1777→  1777→        {currentView === 'generate' && (
  1778→  1778→          <motion.div key="generate" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="flex-1 flex flex-col">
  1779→  1779→            <GenerateView />
  1780→  1780→          </motion.div>
  1781→  1781→        )}
  1782→  1782→      </AnimatePresence>
  1783→  1783→      <Footer />
  1784→  1784→    </div>
  1785→  1785→  );
  1786→  1786→}