import { useState, useEffect, useCallback } from "react";
/* ══════ DATE HELPERS ══════ */
const DAYS_AR = ["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"];
const DAYS_S = ["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"];
const MONTHS_AR = ["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"];
const HIJRI_M = ["محرم","صفر","ربيع الأول","ربيع الثاني","جمادى الأولى","جمادى الآخرة","رجب","شعبان","رمضان","شوال","ذو القعدة","ذو الحجة"];
function toHijri(d) {
let gy=d.getFullYear(), gm=d.getMonth()+1, gd=d.getDate(), y=gy, m=gm;
if (m<=2){y--;m+=12;}
const A=Math.floor(y/100), B=2-A+Math.floor(A/4);
const JD=Math.floor(365.25*(y+4716))+Math.floor(30.6001*(m+1))+gd+B-1524;
let l=JD-1948440+10632;
const n=Math.floor((l-1)/10631); l=l-10631*n+354;
const j=Math.floor((10985-l)/5316)*Math.floor(50*l/17719)+Math.floor(l/5670)*Math.floor(43*l/15238);
l=l-Math.floor((30-j)/15)*Math.floor(17719*j/50)-Math.floor(j/16)*Math.floor(15238*j/43)+29;
const hm=Math.floor(24*l/709), hd=l-Math.floor(709*hm/24), hy=30*n+j-30;
return { y:hy, m:hm-1, d:hd };
}
function useClock() {
const [now, setNow] = useState(new Date());
useEffect(() => { const t = setInterval(()=>setNow(new Date()), 1000); return ()=>clearInterval(t); }, []);
const h=now.getHours(), mn=now.getMinutes(), s=now.getSeconds();
const pad = n => String(n).padStart(2,"0");
const hj = toHijri(now);
return {
day: DAYS_AR[now.getDay()],
gregorian:`${now.getDate()} ${MONTHS_AR[now.getMonth()]} ${now.getFullYear()}م`,
hijri: `${hj.d} ${HIJRI_M[hj.m]} ${hj.y}هـ`,
time: `${pad(h%12||12)}:${pad(mn)}:${pad(s)} ${h>=12?"م":"ص"}`,
};
}
/* ══════ LOCATIONS ══════ */
const GRP_ORDER = ["تبوك الساحلية","المدينة الساحلية","جدة وضواحيها","مكة الساحلية","جازان الساحلية","الخليج الشمالي","الجبيل والقطيف","الدمام والخبر"];
const LOCS = [
{id:"haql", name:"حقل", grp:"تبوك الساحلية", reg:"البحر الأحمر", lat:29.28,lng:34.93,emoji:"🏝️",type:"ساحل صخري"},
{id:"sharma", name:"شرما", grp:"تبوك الساحلية", reg:"البحر الأحمر", lat:28.03,lng:35.23,emoji:"🌊",type:"خليج عميق"},
{id:"muwaylih", name:"المويلح", grp:"تبوك الساحلية", reg:"البحر الأحمر", lat:27.68,lng:35.47,emoji:"🏖️",type:"ساحل رملي"},
{id:"duba", name:"ضباء", grp:"تبوك الساحلية", reg:"البحر الأحمر", lat:27.35,lng:35.70,emoji:"⚓", type:"ميناء"},
{id:"wajh", name:"الوجه", grp:"المدينة الساحلية", reg:"البحر الأحمر", lat:26.25,lng:36.47,emoji:"🐡",type:"شعاب مرجانية"},
{id:"umluj", name:"أملج", grp:"المدينة الساحلية", reg:"البحر الأحمر", lat:25.05,lng:37.27,emoji:"🐠",type:"شعاب مرجانية"},
{id:"yanbu", name:"ينبع", grp:"المدينة الساحلية", reg:"البحر الأحمر", lat:24.09,lng:38.05,emoji:"⚓", type:"ساحل صخري"},
{id:"rayis", name:"الرايس", grp:"المدينة الساحلية", reg:"البحر الأحمر", lat:23.52,lng:38.76,emoji:"🏖️",type:"ساحل رملي"},
{id:"rabigh", name:"رابغ", grp:"جدة وضواحيها", reg:"البحر الأحمر", lat:22.80,lng:39.03,emoji:"🌴",type:"ساحل رملي"},
{id:"mastorah", name:"مستورة", grp:"جدة وضواحيها", reg:"البحر الأحمر", lat:22.30,lng:39.07,emoji:"🏖️",type:"شاطئ"},
{id:"thuwal", name:"ثول", grp:"جدة وضواحيها", reg:"البحر الأحمر", lat:22.27,lng:39.08,emoji:"🔬",type:"ساحل صخري"},
{id:"dhabban", name:"ذهبان", grp:"جدة وضواحيها", reg:"البحر الأحمر", lat:21.77,lng:39.11,emoji:"🌅",type:"شاطئ"},
{id:"jeddah", name:"جدة", grp:"جدة وضواحيها", reg:"البحر الأحمر", lat:21.49,lng:39.19,emoji:"🏙️",type:"ميناء"},
{id:"lith", name:"الليث", grp:"مكة الساحلية", reg:"البحر الأحمر", lat:20.15,lng:40.27,emoji:"🌊",type:"ساحل رملي"},
{id:"qunfudhah",name:"القنفذة", grp:"مكة الساحلية", reg:"البحر الأحمر", lat:19.13,lng:41.08,emoji:"🐟",type:"ميناء"},
{id:"qahmah", name:"القحمة", grp:"مكة الساحلية", reg:"البحر الأحمر", lat:18.32,lng:41.54,emoji:"🏖️",type:"ساحل رملي"},
{id:"samta", name:"صامطة الساحل",grp:"جازان الساحلية", reg:"البحر الأحمر", lat:17.46,lng:42.07,emoji:"🌿",type:"ساحل"},
{id:"jizan", name:"جازان", grp:"جازان الساحلية", reg:"البحر الأحمر", lat:16.88,lng:42.55,emoji:"🌴",type:"شعاب مرجانية"},
{id:"farasan", name:"جزر فرسان", grp:"جازان الساحلية", reg:"البحر الأحمر", lat:16.72,lng:41.98,emoji:"🏝️",type:"جزر مرجانية"},
{id:"khafji", name:"الخفجي", grp:"الخليج الشمالي", reg:"الخليج العربي", lat:28.42,lng:48.50,emoji:"🛢️",type:"ساحل رملي"},
{id:"nairiyah", name:"العريق", grp:"الخليج الشمالي", reg:"الخليج العربي", lat:27.82,lng:48.88,emoji:"🌊",type:"ساحل"},
{id:"jubail", name:"الجبيل", grp:"الجبيل والقطيف", reg:"الخليج العربي", lat:27.00,lng:49.66,emoji:"🏭",type:"ميناء"},
{id:"rastanura",name:"رأس تنورة", grp:"الجبيل والقطيف", reg:"الخليج العربي", lat:26.70,lng:50.16,emoji:"🛢️",type:"ميناء"},
{id:"safwa", name:"صفوى", grp:"الجبيل والقطيف", reg:"الخليج العربي", lat:26.67,lng:49.93,emoji:"🌅",type:"ساحل"},
{id:"qatif", name:"القطيف", grp:"الجبيل والقطيف", reg:"الخليج العربي", lat:26.57,lng:50.01,emoji:"🌊",type:"ساحل"},
{id:"saihat", name:"سيهات", grp:"الجبيل والقطيف", reg:"الخليج العربي", lat:26.47,lng:50.04,emoji:"🏖️",type:"ساحل رملي"},
{id:"dammam", name:"الدمام", grp:"الدمام والخبر", reg:"الخليج العربي", lat:26.43,lng:50.10,emoji:"🌅",type:"ساحل رملي"},
{id:"khobar", name:"الخبر", grp:"الدمام والخبر", reg:"الخليج العربي", lat:26.28,lng:50.20,emoji:"🌆",type:"كورنيش"},
{id:"uqair", name:"العقير", grp:"الدمام والخبر", reg:"الخليج العربي", lat:25.64,lng:50.21,emoji:"🏺",type:"ساحل تاريخي"},
];
/* ══════ WEATHER ══════ */
function rng(seed){ let s=seed; return()=>{ s=(s*1664525+1013904223)&0xffffffff; return(s>>>0)/0xffffffff; }; }
const season = m => [12,1,2].includes(m)?"winter":[3,4,5].includes(m)?"spring":[6,7,8].includes(m)?"summer":"fall";
const SEASON = {winter:"❄️ الشتاء",spring:"🌸 الربيع",summer:"☀️ الصيف",fall:"🍂 الخريف"};
const sCol = s => s>=70?"#10b981":s>=40?"#f59e0b":"#ef4444";
const sLbl = s => s>=70?"ممتاز":s>=40?"مقبول":"صعب";
function mkWx(loc, seed, mode) {
const r=rng(seed), g=loc.reg==="الخليج العربي", bt=g?27+(loc.lat-25)*.4:29-(loc.lat-17)*.35;
return {
temp:(bt+r()*6-1).toFixed(1), wtemp:(bt-2+r()*5).toFixed(1),
wind:(3+r()*(mode==="boat"?30:24)).toFixed(1),
dir:["شمال","شمال شرق","شرق","جنوب شرق","جنوب","جنوب غرب","غرب","شمال غرب"][Math.floor(r()*8)],
wave:(0.1+r()*(mode==="boat"?4:2.8)).toFixed(1),
vis:(5+r()*20).toFixed(0), hum:(45+r()*45).toFixed(0),
tide:r()>.5?"مد":"جزر", pressure:(1005+r()*20).toFixed(0), uv:Math.floor(r()*10)+1,
};
}
function shoreScore(w){let s=100;const wi=+w.wind,wa=+w.wave,vi=+w.vis;if(wi>22)s-=35;else if(wi>15)s-=18;else if(wi>10)s-=8;if(wa>2)s-=28;else if(wa>1.5)s-=16;else if(wa>1)s-=7;if(vi<6)s-=14;else if(vi<10)s-=5;if(w.tide==="مد")s+=5;return Math.max(10,Math.min(100,Math.round(s)));}
function boatScore(w){let s=100;const wi=+w.wind,wa=+w.wave,vi=+w.vis;if(wi>25)s-=45;else if(wi>18)s-=28;else if(wi>12)s-=12;if(wa>2.5)s-=45;else if(wa>2)s-=28;else if(wa>1.5)s-=14;else if(wa>1)s-=6;if(vi<6)s-=20;else if(vi<10)s-=8;if(+w.pressure<1008)s-=8;return Math.max(5,Math.min(100,Math.round(s)));}
function mk14(loc, mode) {
const today = new Date();
return Array.from({length:14},(_,i)=>{
const d=new Date(today); d.setDate(today.getDate()+i);
const seed=loc.id.charCodeAt(0)*7777+(loc.id.charCodeAt(1)||1)*333+i*1234+d.getMonth()*99+(mode==="boat"?5555:0);
const wx=mkWx(loc,seed,mode);
return {date:d,dayS:DAYS_S[d.getDay()],dayF:DAYS_AR[d.getDay()],dateStr:`${d.getDate()} ${MONTHS_AR[d.getMonth()]}`,isToday:i===0,wx,score:mode==="boat"?boatScore(wx):shoreScore(wx)};
});
}
/* ══════ AI ══════ */
async function callAI(prompt) {
const r = await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"content-type":"application/json","anthropic-version":"2023-06-01","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:"claude-sonnet-4-20250514",max_tokens:900,messages:[{role:"user",content:prompt}]})});
const d = await r.json();
if(!r.ok) throw new Error(d?.error?.message||`HTTP ${r.status}`);
return d.content[0].text;
}
const mkPrompt = (type,loc,wx,label,sn) => {
const b=`الموقع: ${loc.name} — ${loc.reg} (${loc.type}) | ${label} | ${SEASON[sn]}\n🌡️${wx.temp}° | 💧${wx.wtemp}° | 💨${wx.wind}عق(${wx.dir}) | 🌊${wx.wave}م | 👁️${wx.vis}كم | ${wx.tide==="مد"?"🔼مد":"🔽جزر"} | 🔵${wx.pressure}هب | ☀️UV${wx.uv}`;
return type==="shore"?`خبير صيد ساحلي. ${b}\nحلّل: 1.**الحكم** 2.**الأمواج والمد** 3.**أفضل نقطة** 4.**الطعم والعمق** 5.**التوقيت والتحذير**. موجز مع رموز.`
:type==="boat" ?`خبير صيد بحري. ${b}\nحلّل: 1.**أمان الإبحار** 2.**الأمواج والرياح** 3.**المسافة والعمق** 4.**أفضل تقنية** 5.**تحذيرات السلامة**. موجز مع رموز.`
:`خبير صيد. ${b}\nتقرير: 1.**الحكم** 2.**عوامل الطقس** 3.**أفضل أسلوب** 4.**التوقيت** 5.**التحذير**. موجز مع رموز.`;
};
const fmtAI = t => t.replace(/\*\*(.*?)\*\*/g,"$1").replace(/\*(.*?)\*/g,"$1");
/* ══════ STYLES ══════ */
const CSS=`
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@400;700;900&display=swap');
*{box-sizing:border-box;margin:0;padding:0;}body{font-family:Tajawal,sans-serif;}
@keyframes fu{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}
.tap{transition:all .18s;cursor:pointer;}.tap:hover{opacity:.8;transform:translateY(-1px);}
.ait{white-space:pre-wrap;line-height:1.85;font-size:13px;color:rgba(220,240,255,.85);}
.ait strong{color:#38bdf8;font-weight:800;}
.sh{background:linear-gradient(90deg,rgba(56,189,248,.05) 25%,rgba(56,189,248,.18) 50%,rgba(56,189,248,.05) 75%);background-size:200% 100%;animation:shimmer 1.4s infinite;}
input::placeholder{color:rgba(148,210,255,.3);}
button,input{font-family:Tajawal,sans-serif;}
::-webkit-scrollbar{width:4px;} ::-webkit-scrollbar-track{background:transparent;} ::-webkit-scrollbar-thumb{background:rgba(56,189,248,.3);border-radius:2px;}
`;
const WRAP={width:"100%",maxWidth:430,minHeight:"100vh",margin:"0 auto",background:"linear-gradient(160deg,#020e1f,#061424)",fontFamily:"Tajawal,sans-serif",direction:"rtl",color:"#e8f4ff"};
const HDR=96;
/* ══════ HAWAT LOGO SVG ══════ */
function HawatLogo({ size = 44 }) {
return (
);
}
/* ══════ HEADER ══════ */
function Header({ sel }) {
const clk = useClock();
const rc = sel ? (sel.reg==="البحر الأحمر"?"#f87171":"#38bdf8") : "#38bdf8";
return (
{/* Row 1 */}
رادار الحوات
{sel && (
{sel.emoji}
{sel.name}
)}
{clk.time}
{/* Row 2 — dates */}
📅 {clk.day} · {clk.gregorian}
{clk.hijri}
{/* Row 3 — location */}
{sel ? (
<>
📍
{sel.grp}
· {sel.reg}
{sel.type}
>
) : (
<>
📍
اختر موقعاً من الصفحة الرئيسية
>
)}
);
}
/* ══════ NAV BAR ══════ */
function Nav({ scr, setScr, sel }) {
const tabs = [{i:"🏠",l:"الرئيسية",s:"home"},{i:"🏖️",l:"الشاطئ",s:"shore"},{i:"⛵",l:"القارب",s:"boat"},{i:"🗺️",l:"الخريطة",s:"map"},{i:"🎣",l:"النتائج",s:"res"}];
return (
{tabs.map(t => {
const off = t.s!=="home" && !sel;
return (
);
})}
);
}
/* ══════ SECRET SPOTS ══════ */
function SecretSpots() {
const [spots, setSpots] = useState([]);
const [adding, setAdding] = useState(false);
const [name, setName] = useState("");
const [locating, setLocating] = useState(false);
const [coords, setCoords] = useState(null);
const [locErr, setLocErr] = useState("");
const [delId, setDelId] = useState(null);
const [loaded, setLoaded] = useState(false);
// load spots on mount
useEffect(() => {
async function load() {
try {
const res = await window.storage.get("hawat_spots");
if (res?.value) setSpots(JSON.parse(res.value));
} catch(_) {}
setLoaded(true);
}
load();
}, []);
async function save(updated) {
setSpots(updated);
try { await window.storage.set("hawat_spots", JSON.stringify(updated)); } catch(_) {}
}
function getLocation() {
setLocating(true); setLocErr(""); setCoords(null);
if (!navigator.geolocation) { setLocErr("المتصفح لا يدعم تحديد الموقع"); setLocating(false); return; }
navigator.geolocation.getCurrentPosition(
pos => { setCoords({ lat: pos.coords.latitude, lng: pos.coords.longitude }); setLocating(false); },
() => { setLocErr("تعذّر تحديد الموقع — تحقق من صلاحيات GPS"); setLocating(false); }
);
}
function addSpot() {
if (!name.trim() || !coords) return;
const spot = { id: Date.now(), name: name.trim(), lat: coords.lat, lng: coords.lng, date: new Date().toLocaleDateString("ar-SA") };
save([spot, ...spots]);
setName(""); setCoords(null); setAdding(false);
}
function deleteSpot(id) { save(spots.filter(s => s.id !== id)); setDelId(null); }
const mapsUrl = (lat, lng) => `https://www.google.com/maps?q=${lat},${lng}`;
if (!loaded) return null;
return (
{/* Section header */}
🔐
مواقعي الخاصة
محفوظة على جهازك فقط · لا يراها أحد
{/* Add form */}
{adding && (
{/* name input */}
📝 اسم الموقع
setName(e.target.value)} placeholder="مثال: شعبة الهامور 🐟"
style={{width:"100%",padding:"9px 13px",borderRadius:10,background:"rgba(255,255,255,.06)",border:"1px solid rgba(245,158,11,.3)",color:"#e8f4ff",fontSize:13,outline:"none",direction:"rtl"}}/>
{/* GPS button */}
{locErr &&
⚠️ {locErr}
}
{/* save btn */}
)}
{/* Spots list */}
{spots.length === 0 ? (
📍
لا توجد مواقع محفوظة بعد
أضف أول موقع سري لك!
) : (
{spots.map(sp => (
{delId === sp.id ? (
/* confirm delete */
حذف "{sp.name}"؟
) : (
📍
{sp.name}
{sp.lat.toFixed(5)}, {sp.lng.toFixed(5)} · {sp.date}
🗺️ خريطة
)}
))}
{spots.length > 0 && (
🔒 {spots.length} موقع محفوظ على جهازك فقط
)}
)}
);
}
/* ══════ NO-LOCATION PLACEHOLDER ══════ */
function NoLoc({ go }) {
return (
📍
لم يتم اختيار موقع
اختر موقع الصيد من الصفحة الرئيسية،
سيُطبَّق على جميع صفحات التطبيق
);
}
/* ══════ SCORE RING ══════ */
function Ring({ score, size=88 }) {
const R=size*.38, C=2*Math.PI*R, c=sCol(score);
return (
);
}
/* ══════ WX GRID ══════ */
function WxGrid({ wx, small }) {
const fs = small ? {i:14,v:11,l:8,p:"7px 4px",r:9} : {i:18,v:13,l:9,p:"10px 7px",r:12};
const items = [
{i:"🌡️",l:"الهواء", v:`${wx.temp}°`, c:"#f87171"},
{i:"💧", l:"الماء", v:`${wx.wtemp}°`, c:"#38bdf8"},
{i:"🌊", l:"الأمواج", v:`${wx.wave}م`, c:+wx.wave>1.5?"#ef4444":"#10b981"},
{i:"💨", l:"الرياح", v:`${wx.wind}عق`, c:"#fbbf24"},
{i:"🧭", l:"الاتجاه", v:wx.dir, c:"#a78bfa"},
{i:"🌊", l:"المد", v:wx.tide==="مد"?"🔼مد":"🔽جزر",c:"#10b981"},
{i:"🔵", l:"الضغط", v:wx.pressure, c:"#60a5fa"},
{i:"👁️",l:"الرؤية", v:`${wx.vis}كم`, c:"#22d3ee"},
{i:"☀️", l:"UV", v:wx.uv, c:"#fb923c"},
];
return (
);
}
/* ══════ AI BLOCK ══════ */
function AIBlock({ prompt, ac, ad, ab }) {
const [st, setSt] = useState("idle");
const [tx, setTx] = useState("");
const [er, setEr] = useState("");
const run = async () => {
setSt("loading"); setTx(""); setEr("");
try { setTx(await callAI(prompt)); setSt("done"); }
catch(e) { setEr(e.message||"خطأ"); setSt("error"); }
};
if (st==="idle") return ;
if (st==="loading") return (
{[88,70,82,60,76].map((w,k)=>
);
if (st==="error") return (
⚠️ تعذّر جلب التحليل
{er}
);
return (
🤖 تحليل الذكاء الاصطناعي
);
}
/* ══════ LOCATION PICKER (home only) ══════ */
function Picker({ onPick, current }) {
const [q, setQ] = useState("");
const [reg, setReg] = useState("all");
const list = LOCS.filter(l => {
const rOk = reg==="all"||(reg==="red"&&l.reg==="البحر الأحمر")||(reg==="gulf"&&l.reg==="الخليج العربي");
const sOk = !q || l.name.includes(q) || l.grp.includes(q);
return rOk && sOk;
});
const groups = [...new Set(list.map(l=>l.grp))].sort((a,b)=>GRP_ORDER.indexOf(a)-GRP_ORDER.indexOf(b));
return (
{/* banner */}
📍
اختر موقع الصيد
يُطبَّق على جميع صفحات التطبيق
{current &&
✓ {current.name}}
{/* search */}
🔍
setQ(e.target.value)} placeholder="ابحث عن موقع..."
style={{width:"100%",padding:"9px 34px 9px 12px",borderRadius:11,background:"rgba(255,255,255,.06)",border:"1px solid rgba(255,255,255,.1)",color:"#e8f4ff",fontSize:13,outline:"none",direction:"rtl"}}/>
{/* region tabs */}
{[["all","🌍 الكل"],["red","🔴 الأحمر"],["gulf","🔵 الخليج"]].map(([v,lb])=>(
))}
{/* list */}
{groups.length===0
?
لا توجد نتائج
: groups.map(grp=>{
const items = list.filter(l=>l.grp===grp);
const gc = items[0]?.reg==="البحر الأحمر"?"#f87171":"#38bdf8";
return (
{grp} ({items.length})
{items.map(loc => {
const active = current?.id===loc.id;
return (
onPick(loc)}
style={{background:active?"rgba(16,185,129,.1)":"rgba(255,255,255,.04)",borderRadius:12,padding:"10px 13px",border:`1px solid ${active?"rgba(16,185,129,.35)":"rgba(255,255,255,.07)"}`,display:"flex",alignItems:"center",justifyContent:"space-between"}}>
{loc.emoji}
{loc.name}
{loc.type} · {loc.reg}
{active
?
✓ محدد
:
‹}
);
})}
);
})
}
);
}
/* ══════ FORECAST PAGE (shore / boat) ══════ */
function ForecastPage({ type, sel, setScr, scr }) {
const [days, setDays] = useState([]);
const [open, setOpen] = useState(null);
const sn = season(new Date().getMonth()+1);
const isS = type==="shore";
const ac = isS?"#f59e0b":"#38bdf8";
const ad = isS?"rgba(245,158,11,.12)":"rgba(56,189,248,.12)";
const ab = isS?"rgba(245,158,11,.28)":"rgba(56,189,248,.25)";
const bg = isS?"linear-gradient(160deg,#0e0800,#1c1100)":"linear-gradient(160deg,#020e1f,#061424)";
useEffect(()=>{
if(sel){ setDays(mk14(sel,type)); setOpen(null); }
},[sel,type]);
return (
{!sel ?
: (
<>
{/* page banner */}
{isS?"🏖️ الصيد من الشاطئ":"⛵ الصيد بالقارب"}
📅 توقعات ١٤ يوم · {SEASON[sn]}
{sel.emoji} {sel.name}
{sel.reg}
{/* 14 days */}
{days.map((d,i)=>{
const isOpen = open===i;
const prompt = mkPrompt(type,sel,d.wx,`${d.dayF} ${d.dateStr}`,sn);
return (
setOpen(isOpen?null:i)}
style={{background:isOpen?ad:"rgba(255,255,255,.03)",padding:"10px 13px",display:"flex",alignItems:"center",gap:9}}>
{d.dayS}
{d.isToday?"اليوم":d.date.getDate()}
{MONTHS_AR[d.date.getMonth()].slice(0,3)}
🌊 {d.wx.wave}م
💨 {d.wx.wind}عق
🌡️ {d.wx.temp}°
{d.wx.tide==="مد"?"🔼مد":"🔽جزر"}
‹
{isOpen && (
📅 {d.dayF}، {d.dateStr} {d.date.getFullYear()}
)}
);
})}
>
)}
);
}
/* ══════════════════════════════════════
MAIN APP
══════════════════════════════════════ */
export default function App() {
const [scr, setScr] = useState("home");
const [sel, setSel] = useState(null); // ← single global location
const [wx, setWx] = useState(null);
const [wxLoading, setWxLoading] = useState(false);
const [tab, setTab] = useState("wx");
const sn = season(new Date().getMonth()+1);
const clk = useClock();
// ── load saved location on mount ──
useEffect(() => {
async function loadSaved() {
try {
const res = await window.storage.get("hawat_location");
if (res?.value) {
const saved = LOCS.find(l => l.id === res.value);
if (saved) {
setSel(saved);
setWxLoading(true);
setTimeout(() => {
const seed = saved.id.charCodeAt(0)*999 + new Date().getHours()*37;
setWx(mkWx(saved, seed, "today"));
setWxLoading(false);
}, 600);
}
}
} catch(_) {}
}
loadSaved();
}, []);
const pickLocation = useCallback(async loc => {
setSel(loc);
setWxLoading(true);
setWx(null);
// save to persistent storage
try { await window.storage.set("hawat_location", loc.id); } catch(_) {}
setTimeout(()=>{
const seed = loc.id.charCodeAt(0)*999 + new Date().getHours()*37;
setWx(mkWx(loc, seed, "today"));
setWxLoading(false);
}, 600);
}, []);
/* ── route: shore ── */
if (scr==="shore") return
;
/* ── route: boat ── */
if (scr==="boat") return ;
/* ── route: map ── */
if (scr==="map") {
const W=340,H=210, px=(la,ln)=>[((ln-34)/(52-34))*W, H-((la-16)/(30-16))*H];
return (
{!sel ?
: (
<>
🗺️ الخريطة الساحلية
٢٩ موقعاً على سواحل المملكة
{sel.emoji} {sel.name}
🔴 البحر الأحمر🔵 الخليج العربي🟢 موقعك
>
)}
);
}
/* ── route: res ── */
if (scr==="res") {
const sc = wx ? shoreScore(wx) : 0;
const col = sCol(sc);
const verdict = sc>=70?"ممتاز للصيد! 🎣":sc>=40?"مقبول مع حذر ⚠️":"غير مناسب اليوم 🚫";
const prompt = (sel && wx) ? mkPrompt("today",sel,wx,`${clk.day} ${clk.gregorian}`,sn) : "";
return (
{!sel ?
: wxLoading ? (
) : wx && (
<>
{/* score card */}
تقييم الصيد اليوم
{verdict}
{SEASON[sn]}
{/* tabs */}
{[{id:"wx",l:"🌊 الطقس"},{id:"ai",l:"🤖 تحليل AI"},{id:"times",l:"⏰ الأوقات"}].map(t=>(
))}
{tab==="wx" && }
{tab==="ai" && (
🤖
تحليل الصيد بالذكاء الاصطناعي
بناءً على جميع عوامل الطقس الحالية
)}
{tab==="times" && (
{[{ic:"🌅",l:"⭐ الأفضل",t:"5:15 ص – 7:45 ص",c:"#10b981"},{ic:"🌇",l:"الوقت الثاني",t:"5:00 م – 7:30 م",c:"#38bdf8"}].map(x=>(
))}
📌 نصائح السلامة
{["ابدأ التحضير قبل ساعة","ارتدِ سترة النجاة دائماً","راقب تغيرات الطقس","احترم أنظمة الصيد"].map(t=>(
✓{t}
))}
)}
>
)}
);
}
/* ══════ HOME ══════ */
return (
);
}