import { useAuthStore } from "../../stores/authStore"; import { useEffect, useState, useMemo } from "react"; import { api } from "../../utils/api"; import { type Lesson, type LessonMetadata } from "../../types/lesson"; import { LessonModal } from "../../components/LessonModal"; import { BookOpen, Calculator, Video, ChevronRight, Search, X, Play, } from "lucide-react"; import { truncate } from "../../lib/utils"; import { EBRW_LESSONS, MATH_LESSONS } from "../../utils/constants"; import { renderLessonIcon } from "../../components/RenderLessonIcon"; // ─── Types ──────────────────────────────────────────────────────────────────── type VideoSubTab = "rw" | "math"; // ─── Decorative dots ───────────────────────────────────────────────────────── const DOTS = [ { size: 10, color: "#f97316", top: "6%", left: "4%", delay: "0s" }, { size: 7, color: "#a855f7", top: "25%", left: "2%", delay: "1.2s" }, { size: 9, color: "#22c55e", top: "60%", left: "3%", delay: "0.6s" }, { size: 12, color: "#3b82f6", top: "10%", right: "4%", delay: "1.8s" }, { size: 7, color: "#f43f5e", top: "45%", right: "2%", delay: "0.9s" }, { size: 9, color: "#eab308", top: "75%", right: "5%", delay: "0.4s" }, ]; // ─── Styles ─────────────────────────────────────────────────────────────────── const STYLES = ` @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap'); :root { --content-max: 1100px; } .ls-screen { min-height: 100vh; background: #fffbf4; font-family: 'Nunito', sans-serif; position: relative; overflow-x: hidden; } .ls-blob { position:fixed; pointer-events:none; z-index:0; filter:blur(48px); opacity:0.35; } .ls-blob-1 { width:240px;height:240px;background:#fde68a;top:-80px;left:-80px;border-radius:60% 40% 70% 30%/50% 60% 40% 50%;animation:lsWobble1 14s ease-in-out infinite; } .ls-blob-2 { width:190px;height:190px;background:#a5f3c0;bottom:-50px;left:6%;border-radius:40% 60% 30% 70%/60% 40% 60% 40%;animation:lsWobble2 16s ease-in-out infinite; } .ls-blob-3 { width:210px;height:210px;background:#fbcfe8;top:15%;right:-60px;border-radius:70% 30% 50% 50%/40% 60% 40% 60%;animation:lsWobble1 18s ease-in-out infinite reverse; } .ls-blob-4 { width:150px;height:150px;background:#bfdbfe;bottom:12%;right:2%;border-radius:50% 50% 30% 70%/60% 40% 60% 40%;animation:lsWobble2 12s ease-in-out infinite; } @keyframes lsWobble1 { 0%,100%{ border-radius:60% 40% 70% 30%/50% 60% 40% 50%; transform:translate(0,0) rotate(0deg); } 50% { border-radius:40% 60% 30% 70%/60% 40% 60% 40%; transform:translate(12px,16px) rotate(8deg); } } @keyframes lsWobble2 { 0%,100%{ border-radius:40% 60% 30% 70%/60% 40% 60% 40%; transform:translate(0,0) rotate(0deg); } 50% { border-radius:60% 40% 70% 30%/40% 60% 40% 60%; transform:translate(-10px,12px) rotate(-6deg); } } .ls-dot { position:fixed; border-radius:50%; pointer-events:none; z-index:0; opacity:0.3; animation:lsFloat 7s ease-in-out infinite; } @keyframes lsFloat { 0%,100%{ transform:translateY(0) rotate(0deg); } 50% { transform:translateY(-12px) rotate(180deg); } } .ls-inner { position:relative; z-index:1; max-width:680px; margin:0 auto; padding:2rem 1.25rem 6rem; display:flex; flex-direction:column; gap:1.5rem; } /* Desktop: wider centered layout matching rewards page */ @media (min-width: 900px) { .ls-inner { max-width: var(--content-max); padding: 2rem 2rem 6rem; } } @keyframes lsPopIn { from { opacity:0; transform:scale(0.92) translateY(12px); } to { opacity:1; transform:scale(1) translateY(0); } } .ls-anim { animation:lsPopIn 0.4s cubic-bezier(0.34,1.56,0.64,1) both; } .ls-anim-1 { animation-delay:0.05s; } .ls-anim-2 { animation-delay:0.1s; } .ls-anim-3 { animation-delay:0.15s; } .ls-header { animation:lsPopIn 0.4s cubic-bezier(0.34,1.56,0.64,1) both; } .ls-title { font-size:1.8rem; font-weight:900; color:#1e1b4b; letter-spacing:-0.02em; } .ls-sub { font-family:'Nunito Sans',sans-serif; font-size:0.85rem; font-weight:600; color:#9ca3af; margin-top:0.25rem; line-height:1.5; max-width:420px; } /* ── Search ── */ .ls-search-wrap { position: relative; animation: lsPopIn 0.4s cubic-bezier(0.34,1.56,0.64,1) 0.08s both; } .ls-search-input { width: 100%; padding: 0.75rem 1rem 0.75rem 2.75rem; border-radius: 16px; border: 2.5px solid #f3f4f6; background: white; font-family: 'Nunito', sans-serif; font-size: 0.875rem; font-weight: 700; color: #1e1b4b; outline: none; box-shadow: 0 4px 14px rgba(0,0,0,0.04); transition: border-color 0.2s ease, box-shadow 0.2s ease; box-sizing: border-box; } .ls-search-input::placeholder { color: #c4b5fd; font-weight: 600; } .ls-search-input:focus { border-color: #a855f7; box-shadow: 0 4px 20px rgba(168,85,247,0.15); } .ls-search-icon { position: absolute; left: 0.875rem; top: 50%; transform: translateY(-50%); color: #c4b5fd; pointer-events: none; transition: color 0.2s ease; } .ls-search-wrap:focus-within .ls-search-icon { color: #a855f7; } .ls-search-clear { position: absolute; right: 0.875rem; top: 50%; transform: translateY(-50%); background: #f3f4f6; border: none; border-radius: 50%; cursor: pointer; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; color: #9ca3af; transition: background 0.15s ease, color 0.15s ease; padding: 0; } .ls-search-clear:hover { background: #f3e8ff; color: #a855f7; } .ls-search-results-hint { font-family: 'Nunito Sans', sans-serif; font-size: 0.72rem; font-weight: 700; color: #9ca3af; padding: 0 0.25rem; display: flex; align-items: center; gap: 0.4rem; } .ls-search-results-hint strong { color: #a855f7; } /* ── Tabs ── */ .ls-tabs-list { display:flex; gap:0.5rem; } .ls-tab-btn { display:flex; align-items:center; gap:0.5rem; padding:0.55rem 1.1rem; border-radius:100px; cursor:pointer; border:none; font-family:'Nunito',sans-serif; font-size:0.82rem; font-weight:800; transition:all 0.2s ease; background:white; border:2.5px solid #f3f4f6; color:#9ca3af; box-shadow:0 2px 8px rgba(0,0,0,0.04); } .ls-tab-btn.active { background:#1e1b4b; border-color:#1e1b4b; color:white; box-shadow:0 4px 0 #1e1b4b66; } .ls-tab-btn:not(.active):hover { border-color:#c4b5fd; color:#7c3aed; } /* ── Video sub-tabs ── */ .ls-video-subtabs { display: flex; gap: 0.4rem; padding: 0.5rem; background: white; border: 2px solid #f3f4f6; border-radius: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.04); animation: lsPopIn 0.35s cubic-bezier(0.34,1.56,0.64,1) both; } .ls-video-subtab-btn { flex: 1; display: flex; align-items: center; justify-content: center; gap: 0.45rem; padding: 0.5rem 1rem; border-radius: 10px; cursor: pointer; border: none; font-family: 'Nunito', sans-serif; font-size: 0.78rem; font-weight: 800; transition: all 0.2s ease; color: #9ca3af; background: transparent; } .ls-video-subtab-btn.active { background: #fff7ed; color: #f97316; box-shadow: 0 2px 8px rgba(249,115,22,0.15); } .ls-video-subtab-btn:not(.active):hover { background: #fafafa; color: #6b7280; } .ls-video-subtab-dot { width: 7px; height: 7px; border-radius: 50%; } .ls-video-subtab-btn.active .ls-video-subtab-dot { background: #f97316; } .ls-video-subtab-btn:not(.active) .ls-video-subtab-dot { background: #d1d5db; } /* ── Section group ── */ .ls-group { display:flex; flex-direction:column; gap:0; } .ls-group-header { display:flex; align-items:center; gap:0.75rem; padding:0.6rem 1rem; margin-bottom:0; } .ls-group-accent { width:3px; height:1.1rem; border-radius:100px; flex-shrink:0; } .ls-group-accent.rw { background:#a855f7; } .ls-group-accent.math { background:#0891b2; } .ls-group-accent.video{ background:#f97316; } .ls-group-name { font-family:'Nunito Sans',sans-serif; font-size:0.68rem; font-weight:700; letter-spacing:0.14em; text-transform:uppercase; color:#9ca3af; flex:1; } .ls-group-count { font-family:'Nunito',sans-serif; font-size:0.68rem; font-weight:800; color:#d1d5db; } /* ── Lesson list card ── */ .ls-list { background:white; border:2px solid #f3f4f6; border-radius:20px; overflow:hidden; box-shadow:0 4px 14px rgba(0,0,0,0.04); animation:lsPopIn 0.4s cubic-bezier(0.34,1.56,0.64,1) both; } .ls-lesson-row { display:flex; align-items:center; gap:1rem; padding:1rem 1.1rem; cursor:pointer; transition:background 0.15s ease; position:relative; } .ls-lesson-row:not(:last-child)::after { content:''; position:absolute; bottom:0; left:1.1rem; right:1.1rem; height:1px; background:#f3f4f6; } .ls-lesson-row:hover { background:#fafafa; } .ls-lesson-row:active { background:#f5f3ff; } .ls-row-num { font-family:'Nunito',sans-serif; font-size:0.7rem; font-weight:900; color:#d1d5db; width:1.4rem; text-align:center; flex-shrink:0; letter-spacing:0.04em; } .ls-row-icon { width:36px; height:36px; border-radius:10px; flex-shrink:0; display:flex; align-items:center; justify-content:center; } .ls-row-icon.rw { background:#f3e8ff; color:#a855f7; } .ls-row-icon.math { background:#e0f2fe; color:#0891b2; } .ls-row-icon.video { background:#fff7ed; color:#f97316; } .ls-row-body { flex:1; min-width:0; } .ls-row-title { font-size:0.9rem; font-weight:800; color:#1e1b4b; line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .ls-row-desc { font-family:'Nunito Sans',sans-serif; font-size:0.72rem; font-weight:600; color:#9ca3af; margin-top:0.15rem; line-height:1.4; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } .ls-status { display:flex; align-items:center; gap:0.3rem; padding:0.25rem 0.6rem; border-radius:100px; flex-shrink:0; font-family:'Nunito',sans-serif; font-size:0.65rem; font-weight:800; letter-spacing:0.06em; text-transform:uppercase; white-space:nowrap; } .ls-status.started { background:#fef9c3; color:#ca8a04; border:1.5px solid #fde047; } .ls-status.completed { background:#dcfce7; color:#16a34a; border:1.5px solid #86efac; } .ls-chevron { color:#d1d5db; flex-shrink:0; } .ls-lesson-row:hover .ls-chevron { color:#a855f7; } /* ── Search highlight ── */ .ls-highlight { background: linear-gradient(120deg, #fde68a 0%, #fbbf24 100%); border-radius: 3px; padding: 0 2px; color: #92400e; } /* ── Video Cards Grid ── */ .ls-video-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.875rem; } @media (max-width: 480px) { .ls-video-grid { grid-template-columns: 1fr; } } .ls-video-card { background: white; border: 2px solid #f3f4f6; border-radius: 16px; overflow: hidden; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,0.05); transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; animation: lsPopIn 0.4s cubic-bezier(0.34,1.56,0.64,1) both; } .ls-video-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(249,115,22,0.15); border-color: #fed7aa; } .ls-video-card:active { transform: translateY(-1px); } .ls-video-thumb { position: relative; width: 100%; aspect-ratio: 16 / 9; background: linear-gradient(135deg, #1e1b4b 0%, #312e81 50%, #4c1d95 100%); overflow: hidden; } .ls-video-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.3s ease; } .ls-video-card:hover .ls-video-thumb img { transform: scale(1.04); } .ls-video-thumb-overlay { position: absolute; inset: 0; background: linear-gradient(to top, rgba(30,27,75,0.7) 0%, rgba(30,27,75,0.1) 50%, transparent 100%); transition: opacity 0.2s ease; } .ls-video-card:hover .ls-video-thumb-overlay { opacity: 0.85; } .ls-video-play-btn { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 40px; height: 40px; background: rgba(255,255,255,0.95); border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(0,0,0,0.25); transition: transform 0.2s ease, background 0.2s ease; color: #f97316; } .ls-video-card:hover .ls-video-play-btn { transform: translate(-50%, -50%) scale(1.1); background: white; } .ls-video-thumb-fallback { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #1e1b4b 0%, #312e81 60%, #4c1d95 100%); font-size: 1.6rem; opacity: 0.7; } .ls-video-card-body { padding: 0.75rem 0.875rem 0.875rem; } .ls-video-card-title { font-size: 0.82rem; font-weight: 800; color: #1e1b4b; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .ls-video-card-topic { font-family: 'Nunito Sans', sans-serif; font-size: 0.68rem; font-weight: 600; color: #a855f7; margin-top: 0.25rem; } .ls-video-card-arrow { color: #f97316; opacity: 0.5; margin-top: 0.5rem; display: flex; justify-content: flex-end; transition: opacity 0.15s ease, transform 0.15s ease; } .ls-video-card:hover .ls-video-card-arrow { opacity: 1; transform: translateX(2px); } /* ── Skeleton ── */ .ls-skel-list { background:white; border:2px solid #f3f4f6; border-radius:20px; overflow:hidden; box-shadow:0 4px 14px rgba(0,0,0,0.04); } .ls-skel-row { display:flex; align-items:center; gap:1rem; padding:1rem 1.1rem; position:relative; } .ls-skel-row:not(:last-child)::after { content:''; position:absolute; bottom:0; left:1.1rem; right:1.1rem; height:1px; background:#f3f4f6; } .ls-skel-circle { width:36px; height:36px; border-radius:10px; flex-shrink:0; } .ls-skel-lines { flex:1; display:flex; flex-direction:column; gap:0.4rem; } .ls-skel-line { height:10px; border-radius:100px; } .ls-skel-video-grid { display:grid; grid-template-columns:1fr 1fr; gap:0.875rem; } .ls-skel-video-card { background:white; border:2px solid #f3f4f6; border-radius:16px; overflow:hidden; } .ls-skel-video-thumb { aspect-ratio:16/9; } .ls-skel-video-body { padding:0.75rem; display:flex; flex-direction:column; gap:0.4rem; } @keyframes lsSkelShimmer { 0%{ background-position:200% 0; } 100%{ background-position:-200% 0; } } .ls-skel-circle,.ls-skel-line,.ls-skel-video-thumb,.ls-skel-video-body .ls-skel-line { background:linear-gradient(90deg,#f3f4f6 25%,#e9eaec 50%,#f3f4f6 75%); background-size:200% 100%; animation:lsSkelShimmer 1.4s ease-in-out infinite; } /* ── Empty ── */ .ls-empty { text-align:center; padding:3rem 1rem; background:white; border:2.5px dashed #e5e7eb; border-radius:20px; display:flex; flex-direction:column; align-items:center; gap:0.5rem; } .ls-empty-emoji { font-size:2.5rem; } .ls-empty-text { font-size:0.9rem; font-weight:700; color:#9ca3af; } .ls-empty-sub { font-family:'Nunito Sans',sans-serif; font-size:0.78rem; color:#c4b5fd; font-weight:600; } `; // ─── Helpers ───────────────────────────────────────────────────────────────── function highlightText(text: string, query: string): React.ReactNode { if (!query.trim()) return text; const idx = text.toLowerCase().indexOf(query.toLowerCase()); if (idx === -1) return text; return ( <> {text.slice(0, idx)} {text.slice(idx, idx + query.length)} {text.slice(idx + query.length)} > ); } function filterLessonMetadata( lessons: LessonMetadata[], query: string, ): LessonMetadata[] { if (!query.trim()) return lessons; const q = query.toLowerCase(); return lessons.filter( (l) => l.title.toLowerCase().includes(q) || l.description.toLowerCase().includes(q) || l.category.toLowerCase().includes(q), ); } function filterVideoLessons(lessons: Lesson[], query: string): Lesson[] { if (!query.trim()) return lessons; const q = query.toLowerCase(); return lessons.filter( (l) => l.title.toLowerCase().includes(q) || l.topic?.name?.toLowerCase().includes(q), ); } // ─── Skeletons ──────────────────────────────────────────────────────────────── const SkeletonGroup = () => (
{highlightText(lesson.title, searchQuery)}
{lesson.topic?.name && ({highlightText(lesson.topic.name, searchQuery)}
)}No lessons found for "{searchQuery}"
Try a different search term
{highlightText(lesson.title, searchQuery)}
{lesson.description && ({highlightText(lesson.description, searchQuery)}
)}{searchQuery ? `No videos found for "${searchQuery}"` : "No videos available yet."}
{searchQuery && (Try a different search term
)}Step-by-step lessons from expert Edbridge tutors — pick up tips to tackle similar questions with confidence.