/* global React, window, Icon, Mascot, useT, useEyad, PORTAL_USER, PORTAL_SESSIONS, PORTAL_PROGRESS, PORTAL_BADGES, PORTAL_CERTS, PORTAL_LEADERS, PORTAL_REPORTS, PORTAL_INSIGHTS_AR, PORTAL_INSIGHTS_EN, SUBJECTS, PageHead, pickName, subjectOf, subjectAccent, formatHourLabel, daysFromNowLabel */ const { useState, useMemo } = React; // ───────────────────────────────────────────────────────────── // TIMETABLE — weekly grid // ───────────────────────────────────────────────────────────── function TimetableView({ onOpenSession }) { const t = useT(); const { lang } = useEyad(); // Show days 0..4 (today + next 4 days) const days = [0, 1, 2, 3, 4]; const dayNames = lang === "ar" ? PORTAL_PROGRESS.weeklyDayLabels_ar : PORTAL_PROGRESS.weeklyDayLabels_en; // Group sessions by day const byDay = days.map(d => ({ offset: d, sessions: PORTAL_SESSIONS.filter(s => s.dayOffset === d).sort((a, b) => a.startHour - b.startHour), })); // Hour rows: 9..21 const hours = []; for (let h = 9; h <= 21; h++) hours.push(h); return ( <>
{t.portal.timetable.today}
{byDay.map((d, i) => (
{dayNames[(i) % dayNames.length]}
{d.offset === 0 ? t.portal.timetable.today : daysFromNowLabel(d.offset, lang)}
))} {hours.map((h, hi) => (
{formatHourLabel(h, lang)}
{byDay.map((d, ci) => { const sess = d.sessions.find(s => s.startHour === h); return (
{sess && ( )}
); })}
))}
); } // ───────────────────────────────────────────────────────────── // PROGRESS // ───────────────────────────────────────────────────────────── function ProgressView() { const t = useT(); const { lang } = useEyad(); const totalH = PORTAL_PROGRESS.weeklyHours.reduce((a, b) => a + b, 0); const avgH = (totalH / PORTAL_PROGRESS.weeklyHours.length).toFixed(1); const maxH = Math.max(...PORTAL_PROGRESS.weeklyHours); return ( <>
{PORTAL_PROGRESS.subjects.map((p) => { const subj = subjectOf(p.id); const pct = Math.round((p.completed / p.total) * 100); return (

{t.subjects.items[p.id]?.name}

{subj.en}
10 ? "hot" : ""}`}> ↑ {p.trend}%
{t.portal.progress.completed}{p.completed} / {p.total} {t.portal.progress.lessons}
{t.portal.progress.hoursThisMonth}{p.hours} {lang === "ar" ? "س" : "h"}
); })}

{t.portal.progress.weekly.h}

{totalH.toFixed(1)}{t.portal.progress.weekly.total} {lang === "ar" ? "س" : "h"}
{avgH}{t.portal.progress.weekly.avg} {lang === "ar" ? "س" : "h"}
{PORTAL_PROGRESS.weeklyHours.map((h, i) => { const dayNames = lang === "ar" ? PORTAL_PROGRESS.weeklyDayLabels_ar : PORTAL_PROGRESS.weeklyDayLabels_en; return (
{h.toFixed(1)}
{dayNames[i]}
); })}

{t.portal.progress.breakdown.h}

({ value: s.hours, color: s.accent, label: t.subjects.items[s.id]?.name }))}/>
); } function ProgressRing({ pct, color }) { const r = 38, c = 2 * Math.PI * r; const off = c - (c * pct / 100); return (
{pct}%
); } function DonutChart({ data }) { const total = data.reduce((a, b) => a + b.value, 0); const r = 60, sw = 24; const c = 2 * Math.PI * r; let offset = 0; return (
{data.map((d, i) => { const len = (d.value / total) * c; const seg = ( ); offset += len; return seg; })} {total} HOURS
    {data.map((d, i) => { const pct = Math.round((d.value / total) * 100); return (
  • {d.label} {pct}%
  • ); })}
); } // ───────────────────────────────────────────────────────────── // CERTIFICATES // ───────────────────────────────────────────────────────────── function CertificatesView() { const t = useT(); const { lang } = useEyad(); return ( <>
{PORTAL_CERTS.map((c) => { const subj = subjectOf(c.subject); const accent = subjectAccent(c.subject); return (
{c.earned ? (
) : (
)}
{t.subjects.items[c.subject]?.name}

{lang === "ar" ? c.title_ar : c.title_en}

{c.earned ? ( <>
{t.portal.certificates.earned} {lang === "ar" ? c.date_ar : c.date_en} · {lang === "ar" ? c.grade_ar : c.grade_en}
) : ( <>
{t.portal.certificates.progressLabel} {c.progress}%
{t.portal.certificates.unlock}
)}
); })}
); } // ───────────────────────────────────────────────────────────── // GAMIFICATION // ───────────────────────────────────────────────────────────── function GamificationView() { const t = useT(); const { lang } = useEyad(); const earnedCount = PORTAL_BADGES.filter(b => b.earned).length; const totalBadges = PORTAL_BADGES.length; const [badgeTab, setBadgeTab] = useState("all"); // all | earned | locked const filteredBadges = badgeTab === "all" ? PORTAL_BADGES : PORTAL_BADGES.filter(b => badgeTab === "earned" ? b.earned : !b.earned); return ( <>
{t.portal.gamification.xpCard.kicker}
{t.portal.gamification.xpCard.levelLabel} {PORTAL_USER.level} {PORTAL_USER.xp.toLocaleString()}/{PORTAL_USER.xpNext.toLocaleString()}
{(PORTAL_USER.xpNext - PORTAL_USER.xp).toLocaleString()} {t.portal.gamification.xpCard.toNext}
{t.portal.gamification.streakCard.kicker}
{PORTAL_USER.streak}
{t.portal.gamification.streakCard.days}
{t.portal.gamification.streakCard.best}: {PORTAL_USER.streak}
{t.portal.gamification.rankCard.kicker}
#{PORTAL_USER.rank}
{t.portal.gamification.rankCard.among}

{t.portal.gamification.badgesGrid.h}

{earnedCount}/{totalBadges}
{filteredBadges.map((b) => (
{!b.earned &&
}
{lang === "ar" ? b.name_ar : b.name_en}
{lang === "ar" ? b.desc_ar : b.desc_en}
{b.earned &&
{b.date}
}
))}

{t.portal.gamification.leaderboard.h}

    {PORTAL_LEADERS.map((l) => (
  • {l.rank}
    {l.init}
    {lang === "ar" ? l.name_ar : l.name_en} {l.you && · {t.portal.gamification.leaderboard.you}}
    {l.xp.toLocaleString()} {t.portal.gamification.leaderboard.xp} · {l.streak} {lang === "ar" ? "يوم" : "d"} {t.portal.gamification.leaderboard.streak}
  • ))}
); } // ───────────────────────────────────────────────────────────── // REPORTS // ───────────────────────────────────────────────────────────── function ReportsView() { const t = useT(); const { lang } = useEyad(); const insights = lang === "ar" ? PORTAL_INSIGHTS_AR : PORTAL_INSIGHTS_EN; return ( <>
{PORTAL_REPORTS.length === 0 &&
{t.portal.reports.empty}
} {PORTAL_REPORTS.map((r, i) => (
{i === 0 &&
{lang === "ar" ? "الأحدث" : "Latest"}
}

{lang === "ar" ? r.week_ar : r.week_en}

{t.portal.reports.attended}
{r.attended}/{r.total}
{t.portal.reports.avg}
{lang === "ar" ? r.avg_ar : r.avg_en}
✦ {t.portal.reports.highlights}

{lang === "ar" ? r.highlights_ar : r.highlights_en}

{(lang === "ar" ? r.flags_ar : r.flags_en) !== "—" && (
⚠ {t.portal.reports.flags}

{lang === "ar" ? r.flags_ar : r.flags_en}

)}
))}
); } // ───────────────────────────────────────────────────────────── // SETTINGS // ───────────────────────────────────────────────────────────── function SettingsView() { const t = useT(); const { lang, theme, setLang, setTheme } = useEyad(); const [form, setForm] = useState({ name: pickName(PORTAL_USER.name, lang), grade: pickName(PORTAL_USER.grade, lang), parent: pickName(PORTAL_USER.parent, lang), parentEmail: PORTAL_USER.parentEmail, phone: "+966 5XX XXX XXXX", notifEmail: true, notifSms: true, notifWeekly: true, reminders: "15", }); const [saved, setSaved] = useState(false); const setF = (k, v) => setForm(p => ({ ...p, [k]: v })); const save = () => { setSaved(true); setTimeout(() => setSaved(false), 2400); }; const labels = t.portal.settings.labels; return ( <>
{/* Profile */}

{t.portal.settings.groups.profile}

{PORTAL_USER.avatar}
{t.portal.settings.photos}
setF("name", e.target.value)}/>
setF("grade", e.target.value)}/>
{/* Family */}

{t.portal.settings.groups.parents}

setF("parent", e.target.value)}/>
setF("parentEmail", e.target.value)}/>
setF("phone", e.target.value)}/>
{/* Preferences */}

{t.portal.settings.groups.prefs}

{labels.lang}
{labels.theme}
{labels.reminders}
{labels.notifEmail}
setF("notifEmail", v)}/>
{labels.notifSms}
setF("notifSms", v)}/>
{labels.notifWeekly}
setF("notifWeekly", v)}/>
{/* Account */}

{t.portal.settings.groups.account}

{/* Danger zone */}

{t.portal.settings.groups.danger}

{t.portal.settings.deleteHint}

); } function Switch({ on, onChange }) { return ( ); } // ───────────────────────────────────────────────────────────── // Icons used here // ───────────────────────────────────────────────────────────── function LockIcon() { return (); } function DownloadIcon2() { return (); } function ShareIcon() { return (); } function TrophyIcon() { return (); } function FlameIconSm() { return ( ); } Object.assign(window, { TimetableView, ProgressView, CertificatesView, GamificationView, ReportsView, SettingsView });