import { useState, useEffect } from "react"; import { Badge } from "@/components/ui/badge"; import { Layers, CheckCheck, RefreshCw, SendHorizonal, Lock, Zap, Info, Tag } from "lucide-react"; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { Progress } from "@/components/ui/progress"; import ResponsiveModal from "@/components/generic/ResponsiveModal"; import { Button } from "@/components/ui/button"; import { useNavigate } from "react-router-dom"; import api from "@/utils/api.util"; import { useClientTiers } from "@/contexts/ClientTiersProvider"; import { resolveTierBadge } from "@/utils/tierBadge.util"; function TierBadge({ tier }) { const { tierMap } = useClientTiers(); const { rank, label, cls } = resolveTierBadge(tier ?? 'free', tierMap); return ( {rank > 0 ? : } {label} ); } const ReadUnit = ({ title = "Read Units", units = [], groupId, taskListId, taskId }) => { const navigate = useNavigate(); const [selected, setSelected] = useState(null); const [details, setDetails] = useState({}); const [locked, setLocked] = useState({}); const [lockedInfo, setLockedInfo] = useState({}); const [unavailable, setUnavailable] = useState({}); const [fetching, setFetching] = useState({}); useEffect(() => { units.forEach(async (unit) => { if (!unit.reference_id) return; setFetching((prev) => ({ ...prev, [unit.reference_id]: true })); try { const res = await api.get(`/client/courses/unit/uuid/${unit.reference_id}`); const d = res.data?.data; if (d) setDetails((prev) => ({ ...prev, [unit.reference_id]: d })); } catch (err) { if (err?.response?.status === 403) { setLocked((prev) => ({ ...prev, [unit.reference_id]: true })); const course = err.response?.data?.course; if (course) setLockedInfo((prev) => ({ ...prev, [unit.reference_id]: course })); } else { setUnavailable((prev) => ({ ...prev, [unit.reference_id]: true })); } } finally { setFetching((prev) => ({ ...prev, [unit.reference_id]: false })); } }); }, []); const getProgress = (unit) => unit.completed ? 100 : (unit.progress ?? 0); if (!units.length) { return (

{title}

0
No units available.
); } const hasLocked = Object.values(locked).some(Boolean); return ( <>

{title}

{units.length}
{hasLocked && (

Subscription Required

To complete this activity, subscribe to one of our available tier plans.

)}
{units.map((unit) => { const info = details[unit.reference_id]; const courseInfo = lockedInfo[unit.reference_id]; const progress = getProgress(unit); const isLocked = locked[unit.reference_id]; const isUnavailable = unavailable[unit.reference_id]; const isFetching = fetching[unit.reference_id]; // ── Locked card ─────────────────────────────────── // Skipped when already completed — a completion recorded // while the unit was accessible still counts even if it // re-locks later (reading-progress rows are never revoked // on a tier change), so it must keep showing as Completed. if (isLocked && !unit.completed) { return (
navigate('/subscriptions')} className="bg-card rounded-2xl border border-dashed p-4 flex flex-col gap-3 hover:bg-muted/60 hover:shadow-sm transition-all cursor-pointer group w-96 shrink-0 opacity-80" >
Unit
{courseInfo?.title && (

from {courseInfo.title}

)}

{unit.title}

Subscribe to {courseInfo?.title ?? 'this course'} to unlock this unit.

); } // ── Unavailable card ────────────────────────────── if (isUnavailable) { return (
Unit Unavailable

{unit.title}

This unit is no longer available.

); } // ── Normal card ─────────────────────────────────── return (
!isFetching && setSelected(unit)} className={`bg-card rounded-2xl border p-4 flex flex-col gap-3 transition-all w-96 shrink-0 ${ isFetching ? 'opacity-60 cursor-wait' : 'hover:bg-muted/60 hover:shadow-sm cursor-pointer group' }`} > {/* Card type row */}
Unit
{info?.course?.subscription ? : isFetching && Loading… }
{info?.course?.title && (

from {info.course.title}

)}

{ if (!info) return; e.stopPropagation(); if (info.course?.course_id) { navigate(`/course/${info.course.course_id}/unit`, { state: taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {}, }); return; } // Standalone unit (junction revamp — no parent course) — // the course-scoped route can't resolve, fall back to // the standalone unit reader instead. navigate(`/units/${unit.reference_id}/read`, { state: taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {}, }); }} className="text-base font-semibold leading-snug line-clamp-2 transition-colors text-blue-600 dark:text-blue-400 hover:underline cursor-pointer" > {unit.title}

{info?.description ?? ''}

{progress >= 100 ? ( Completed ) : progress > 0 ? ( In Progress ) : ( Not Started )} {progress > 0 && {progress}%}
= 100 ? "[&>div]:bg-green-500" : ""}`} />
); })}
!v && setSelected(null)} title={selected?.title} description="Unit Info" footer={ <> } > {selected && (() => { const info = details[selected.reference_id]; const progress = getProgress(selected); const done = progress >= 100; return (
{done && (
Automatically Turned-in
)} {info?.course?.title && (

Course: {info.course.title}

)}

About this unit

{info?.description ?? '—'}

); })()}
); }; export default ReadUnit;