PageSourceSearch

https://skillforge.com/assets/instructor-B8sJKUi6.js

js skillforge.com collected 2026-10-03 22:12:55 UTC 67,621 bytes, 1,074 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, aK as RefreshCw, L as LoaderCircle, _ as Star, ba as toast, aH as format, bb as MessageSquare, a as ChevronDown, aJ as ChevronRight, bc as ChartColumn, ak as Users, ah as Search, X, aB as BookOpen, bd as ShieldCheck, be as Copy, b9 as ExternalLink, E as useNavigate, $ as Navigate, Q as Helmet, V as motion, bf as PenLine, aI as ChevronLeft, W as Monitor, bg as Building2, bh as CheckCheck, b8 as Key, bi as Printer, ai as Calendar, aD as MapPin, C as CircleUser, aL as Phone, aM as Mail, J as FileText, av as Video } from "./vendor-Visk9v06.js";
2import { B as Button, i as cn, I as Input, f as Badge, u as useSession, g as getUserWithRole, C as Card, a as CardHeader, b as CardTitle, c as CardDescription, S as Select, j as SelectTrigger, k as SelectValue, l as SelectContent, n as SelectItem, d as CardContent, D as Dialog, o as DialogContent, p as DialogHeader, q as DialogTitle, r as DialogDescription, h as formatSessionDate } from "./index-CXY7xJdM.js";
3import { T as Tabs, a as TabsList, b as TabsTrigger, c as TabsContent } from "./tabs-Cr7Q0jtH.js";
4import { S as Separator, C as CoursewareSection } from "./separator-D9zpTTcM.js";
5import { P as ProfileMenu } from "./ProfileMenu-BH0_dJt6.js";
6import "./preload-DNrUkv91.js";
7const SURVEY_CATEGORIES = [{
8  title: "Overall Experience",
9  questions: [{
10    key: "q1",
11    label: "Overall Satisfaction"
12  }, {
13    key: "q2",
14    label: "Class Length Appropriate"
15  }, {
16    key: "q3",
17    label: "Setup Organized"
18  }, {
19    key: "q4",
20    label: "Will Apply Learning"
21  }]
22}, {
23  title: "Instructor",
24  questions: [{
25    key: "q5",
26    label: "Knowledgeable"
27  }, {
28    key: "q6",
29    label: "Answered Questions Clearly"
30  }, {
31    key: "q7",
32    label: "Explained Concepts Clearly"
33  }, {
34    key: "q8",
35    label: "Real-World Examples"
36  }, {
37    key: "q9",
38    label: "Time Management"
39  }]
40}, {
41  title: "Course Materials",
42  questions: [{
43    key: "q10",
44    label: "Free of Errors"
45  }, {
46    key: "q11",
47    label: "Exercises Easy to Follow"
48  }]
49}, {
50  title: "Technology",
51  questions: [{
52    key: "q12",
53    label: "Platform Easy to Use"
54  }, {
55    key: "q13",
56    label: "No Connectivity Issues"
57  }, {
58    key: "q14",
59    label: "Audio/Video Quality"
60  }]
61}];
62const ALL_Q_KEYS = ["q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13", "q14"];
63function avg(vals) {
64  const nums = vals.filter((v) => v !== null);
65  if (nums.length === 0) return null;
66  return nums.reduce((a, b) => a + b, 0) / nums.length;
67}
68function ScoreBar({
69  value,
70  max = 5
71}) {
72  if (value === null) return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-xs", children: "—" });
73  const pct = value / max * 100;
74  const color = value >= 4.5 ? "bg-green-500" : value >= 3.5 ? "bg-blue-500" : value >= 2.5 ? "bg-amber-500" : "bg-red-500";
75  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-1", children: [
76    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 bg-muted rounded-full h-2 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("h-full rounded-full transition-all", color), style: {
77      width: `${pct}%`
78    } }) }),
79    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-semibold text-foreground w-8 text-right", children: value.toFixed(1) })
80  ] });
81}
82function StudentResponseRow({
83  survey
84}
84) {
85  var _a, _b;
86  const [expanded, setExpanded] = reactExports.useState(false);
87  const studentAvg = avg(ALL_Q_KEYS.map((k) => survey[k]));
88  const submittedDate = survey.submittedAt ? format(new Date(survey.submittedAt), "MMM d") : null;
89  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-border last:border-0", children: [
90    /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-5 py-2.5 text-left hover:bg-muted/20 transition-colors", children: [
91      expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
92      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-sm text-muted-foreground flex-1", children: [
93        "Response ",
94        submittedDate ? `· ${submittedDate}` : ""
95      ] }),
96      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
97        studentAvg !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn("inline-flex items-center gap-0.5 px-2 py-0.5 rounded-full text-xs font-semibold", studentAvg >= 4.5 ? "bg-green-100 text-green-800" : studentAvg >= 3.5 ? "bg-blue-100 text-blue-800" : studentAvg >= 2.5 ? "bg-amber-100 text-amber-800" : "bg-red-100 text-red-800"), children: [
98          /* @__PURE__ */ jsxRuntimeExports.jsx(Star, { className: "h-3 w-3" }),
99          studentAvg.toFixed(1)
100        ] }),
101        ((_a = survey.comments) == null ? void 0 : _a.trim()) && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: "Has comment", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-3.5 w-3.5 text-blue-500 shrink-0" }) })
102      ] })
103    ] }),
104    expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-5 pb-3 space-y-3", children: [
105      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: SURVEY_CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
106        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wide mb-1.5", children: cat.title }),
107        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-1.5", children: cat.questions.map(({
108          key,
109          label
110        }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex items-center justify-between gap-1.5 rounded-md px-2.5 py-1.5 text-xs", (survey[key] ?? 0) >= 4 ? "bg-green-50 text-green-800" : (survey[key] ?? 0) >= 3 ? "bg-amber-50 text-amber-800" : survey[key] !== null ? "bg-red-50 text-red-800" : "bg-muted/40 text-muted-foreground"), children: [
111          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: label }),
112          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold shrink-0", children: survey[key] ?? "—" })
113        ] }, key)) })
114      ] }, cat.title)) }),
115      ((_b = survey.comments) == null ? void 0 : _b.trim()) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2 bg-blue-50 border border-blue-200 rounded-lg px-3 py-2.5", children: [
116        /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-3.5 w-3.5 text-blue-500 mt-0.5 shrink-0" }),
117        /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-blue-900 italic leading-relaxed", children: [
118          '"',
119          survey.comments,
120          '"'
121        ] })
122      ] })
123    ] })
124  ] });
125}
126function SessionCard({
127  group
128}) {
129  const [expanded, setExpanded] = reactExports.useState(false);
130  const avgScores = Object.fromEntries(ALL_Q_KEYS.map((k) => [k, avg(group.surveys.map((s) => s[k]))]));
131  const overallAvg = avg(Object.values(avgScores));
132  const sessionDate = group.sessionStartDate ? format(new Date(group.sessionStartDate), "MMM d, yyyy") : "—";
133  const commentsWithText = group.surveys.filter((s) => {
134    var _a;
135    return (_a = s.comments) == null ? void 0 : _a.trim();
136  });
137  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-border bg-card overflow-hidden", children: [
138    /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-4 px-5 py-4 hover:bg-muted/30 transition-colors text-left", children: [
139      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
140        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
141          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-foreground text-sm", children: group.courseTitle }),
142          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-xs", children: sessionDate })
143        ] }),
144        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-1", children: [
145          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-muted-foreground", children: [
146            group.surveys.length,
147            " response",
148            group.surveys.length !== 1 ? "s" : ""
149          ] }),
150          overallAvg !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-semibold text-primary", children: [
151            /* @__P
151URE__ */ jsxRuntimeExports.jsx(Star, { className: "h-3 w-3" }),
152            overallAvg.toFixed(1),
153            " / 5.0 overall"
154          ] }),
155          commentsWithText.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-medium text-blue-700 bg-blue-50 border border-blue-200 rounded-full px-2 py-0.5", children: [
156            /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "h-3 w-3" }),
157            commentsWithText.length,
158            " comment",
159            commentsWithText.length !== 1 ? "s" : ""
160          ] })
161        ] })
162      ] }),
163      expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground shrink-0" })
164    ] }),
165    expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-border", children: [
166      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-5 py-5 bg-muted/20 border-b border-border", children: [
167        /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide mb-4 flex items-center gap-1.5", children: [
168          /* @__PURE__ */ jsxRuntimeExports.jsx(ChartColumn, { className: "h-3.5 w-3.5" }),
169          "Score Breakdown"
170        ] }),
171        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-5", children: SURVEY_CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
172          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] font-semibold text-muted-foreground uppercase tracking-wide mb-2", children: cat.title }),
173          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: cat.questions.map(({
174            key,
175            label
176          }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
177            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-foreground w-48 shrink-0", children: label }),
178            /* @__PURE__ */ jsxRuntimeExports.jsx(ScoreBar, { value: avgScores[key] })
179          ] }, key)) })
180        ] }, cat.title)) })
181      ] }),
182      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-border", children: [
183        /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide px-5 pt-3 pb-1 flex items-center gap-1.5", children: [
184          /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { className: "h-3.5 w-3.5" }),
185          "Individual Responses (",
186          group.surveys.length,
187          ")"
188        ] }),
189        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "divide-y divide-border", children: group.surveys.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(StudentResponseRow, { survey: s }, s.id)) })
190      ] })
191    ] })
192  ] });
193}
194function InstructorSurveysTab() {
195  const [surveys, setSurveys] = reactExports.useState([]);
196  const [loading, setLoading] = reactExports.useState(true);
197  async function loadSurveys() {
198    setLoading(true);
199    try {
200      const res = await fetch("/api/instructor/surveys");
201      const data = await res.json();
202      setSurveys(Array.isArray(data) ? data : []);
203    } catch {
204      toast.error("Failed to load surveys");
205    } finally {
206      setLoading(false);
207    }
208  }
209  reactExports.useEffect(() => {
210    loadSurveys();
211  }, []);
212  const sessionGroups = reactExports.useMemo(() => {
213    const map = /* @__PURE__ */ new Map();
214    for (const s of surveys) {
215      if (!map.has(s.sessionId)) {
216        map.set(s.sessionId, {
217          sessionId: s.sessionId,
218          courseTitle: s.courseTitle,
219          sessionStartDate: s.sessionStartDate,
220          surveys: []
221        });
222      }
223      map.get(s.sessionId).surveys.push(s);
224    }
225    return Array.from(map.values()).sort((a, b) => new Date(b.sessionStartDate).getTime() - new Date(a.sessionStartDate).getTime());
226  }, [surveys]);
227  const overallAvg = avg(surveys.flatMap((s) => [s.q1, s.q2, s.q3, s.q4, s.q5, s.q6]));
228  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [
229    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between flex-wrap gap-3", children: [
230      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
231        /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-bold text-foreground", children: "My Survey Results" }),
232        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground mt-0.5", children: "Anonymised feedback from students in your sessions" })
233      ] }),
234      /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", onClick: loadSurveys, disabled: loading, className: "gap-1.5", children: [
235        /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: cn("h-3.5 w-3.5", loading && "animate-spin") }),
236        "Refresh"
237      ] })
238    ] }),
239    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
240      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-border bg-card p-4 text-center", children: [
241        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-foreground", children: sessionGroups.length }),
242        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "Sessions with Feedback" })
243      ] }),
244      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-border bg-card p-4 text-center", children: [
245        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-foreground", children: surveys.length }),
246        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "Total Responses" })
247      ] }),
248      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-xl border border-border bg-card p-4 text-center", children: overallAvg !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
249        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-primary", children: overallAvg.toFixed(1) }),
250        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "Overall Avg / 5.0" })
251      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
252        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-2xl font-bold text-muted-foreground", children: "—" }),
253        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: "Overall Avg / 5.0" })
254      ] }) })
255    ] }),
256    loading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-16 text-center", children: [
257      /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-8 w-8 animate-spin mx-auto text-muted-foreground" }),
258      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground mt-3 text-sm", children: "Loading feedback…" })
259    ] }) : sessionGroups.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-16 text-center rounded-xl border border-dashed border-border", children: [
260      /* @__PURE__ */ jsxRuntimeExports.jsx(Star, { className: "h-10 w-10 text-muted-foreground mx-auto mb-3 opacity-40" }),
261      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground font-medium", children: "No feedback yet" }),
262      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Survey results will appear here after students complete their post-class feedback." })
263    ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: sessionGroups.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionCard, { group }, group.sessionId)) })
264  ] });
265}
266function InstructorManualsTab() {
267  const [manuals, setManuals] = reactExports.useState([]);
268  const [loading, setLoading] = reactExports.useState(true);
269  const [search, setSearch] = reactExports.useState("");
270  reactExports.useEffect(() => {
271    fetch("/api/admin/manuals").then((r) => r.json()).then((data) => setManuals(Array.isArray(data) ? data : [])).catch(() => toast.error("Failed to load manuals")).finally(() => setLoading(false));
272  }, []);
273  const filtered = manuals.filter((m) => !search.trim() || m.courseTitle.toLowerCase().includes(search.toLowerCase()) || m.label.toLowerCase().includes(search.toLowerCase()) || m.originalName.toLowerCase().includes(search.toLowerCase()));
274  const copyToClipboard = (text, label) => {
275    navigator.clipboard.writeText(text).then(() => toast.success(`${label} copied`));
276  };
277  const formatSize = (bytes) => {
278    if (!bytes) return "—";
279    if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;
280    return `${(bytes / 1024).toFixed(0)} KB`;
281  };
282  const fullUrl = (fileUrl) => `${window.location.origin}${fileUrl}`;
283  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
284    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
285      /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
286      /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { className: "pl-9 pr-8", placeholder: "Search by course or manual name…", value: search, onChange: (e) => setSearch(e.target.value) }),
287      search && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground", onClick: () => setSearch(""), children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "h-4 w-4" }) })
288    ] }),
289    loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-12 text-center text-muted-foreground text-sm", children: "Loading manuals…" }) : filtered.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-12 text-center text-muted-foreground text-sm", children: search ? "No manuals match your search." : "No course manuals have been uploaded yet." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: filtered.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border bg-card p-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
290      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3 min-w-0", children: [
291        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 shrink-0 rounded-md bg-primary/10 p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(BookOpen, { className: "h-4 w-4 text-primary" }) }),
292        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
293          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
294            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-sm truncate", children: m.courseTitle }),
295            /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "text-xs shrink-0", children: [
296              "v",
297              m.version
298            ] })
299          ] }),
300          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: m.label }),
301          /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground truncate", children: [
302            m.originalName,
303            " · ",
304            formatSize(m.fileSize)
305          ] }),
306          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex items-center gap-1.5", children: [
307            /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
308            /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-xs font-mono bg-muted px-1.5 py-0.5 rounded select-all", children: m.accessCode }),
309            /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "text-muted-foreground hover:text-foreground transition-colors", title: "Copy access code", onClick: () => copyToClipboard(m.accessCode, "Access code"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "h-3.5 w-3.5" }) })
310          ] })
311        ] })
312      ] }),
313      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
314        /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "hidden lg:block text-xs font-mono text-muted-foreground bg-muted px-2 py-1 rounded max-w-[220px] truncate", children: m.fileUrl }),
315        /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5 text-xs", onClick: () => window.open(fullUrl(m.fileUrl), "_blank"), children: [
316          /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
317          "Open"
318        ] }),
319        /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "ghost", size: "sm", className: "gap-1.5 text-xs", onClick: () => copyToClipboard(fullUrl(m.fileUrl), "URL"), children: [
320          /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "h-3.5 w-3.5" }),
321          "Copy URL"
322        ] })
323      ] })
324    ] }, m.id)) })
325  ] });
326}
327function parseLocalDate(dateStr) {
328  if (!dateStr) return /* @__PURE__ */ new Date();
329  if (dateStr.endsWith("Z") || /[+-]\d{2}:\d{2}$/.test(dateStr)) return new Date(dateStr);
330  return new Date(dateStr.replace(" ", "T"));
331}
332function formatTime(t) {
333  if (!t) return "—";
334  if (t.includes("AM") || t.includes("PM")) return t.trim();
335  const [h, m] = t.split(":");
336  const hour = parseInt(h);
337  const ampm = hour >= 12 ? "PM" : "AM";
338  const display = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
339  return `${display}:${m} ${ampm}`;
340}
341function printSignInSheet(sessionData, students) {
342  const sorted = [...students].sort((a, b) => {
343    var _a, _b;
344    const nameA = (a.enrollment.studentName || ((_a = a.student) == null ? void 0 : _a.name) || "").toLowerCase();
345    const nameB = (b.enrollment.studentName || ((_b = b.student) == null ? void 0 : _b.name) || "").toLowerCase();
346    return nameA.localeCompare(nameB);
347  });
348  students = sorted;
349  const dateStr = formatSessionDate(sessionData.session.startDate, {
350    weekday: "long",
351    year: "numeric",
352    month: "long",
353    day: "numeric"
354  });
355  const timeStr = `${formatTime(sessionData.session.startTime)} – ${formatTime(sessionData.session.endTime)}${sessionData.session.timezone ? " " + sessionData.session.timezone : ""}`;
356  const isOnline = sessionData.session.deliveryMode === "online";
357  const location = isOnline ? "Live Online (Virtual)" : sessionData.session.location || "Location TBD";
358  const address = !isOnline && sessionData.session.address ? sessionData.session.address : "";
359  const contactName = !isOnline && sessionData.session.contactName ? sessionData.session.contactName : "";
360  const contactEmail = !isOnline && sessionData.session.contactEmail ? sessionData.session.contactEmail : "";
361  const contactPhone = !isOnline && sessionData.session.contactPhone ? sessionData.session.contactPhone : "";
362  const hasContact = contactName || contactEmail || contactPhone;
363  const rows = students.map((s, idx) => {
364    var _a, _b;
365    const name = s.enrollment.studentName || ((_a = s.student) == null ? void 0 : _a.name) || "";
366    const company = s.enrollment.studentCompany || "";
367    const email = s.enrollment.studentEmail || ((_b = s.student) == null ? void 0 : _b.email) || "";
368    const keys = [s.enrollment.accessKey1, s.enrollment.accessKey2, s.enrollment.accessKey3, s.enrollment.accessKey4].filter(Boolean);
369    const keyHtml = keys.length > 0 ? keys.map((k, i) => `<div class="key-row"><span class="key-label">Key ${i + 1}</span><span class="key-val">${k}</span></div>`).join("") : '<span class="empty">—</span>';
370    const rowBg = idx % 2 === 0 ? "#ffffff" : "#f8fafc";
371    return `
372      <tr style="background:${rowBg}">
373        <td class="num">${idx + 1}</td>
374        <td class="cell-name">
375          <div class="student-name">${name}</div>
376          <div class="student-email">${email}</div>
377        </td>
378        <td class="cell">${company}</td>
379        <td class="cell-keys">${keyHtml}</td>
380        <td class="cell-sig"></td>
381      </tr>`;
382  }).join("");
383  const html = `<!DOCTYPE html>
384<html>
385<head>
386  <meta charset="utf-8" />
387  <title>Sign-In Sheet — ${sessionData.course.title}</title>
388  <style>
389    @page { size: portrait; margin: 1cm 1.2cm; }
390    * { box-sizing: border-box; margin: 0; padding: 0; }
391    body { font-family: 'Helvetica Neue', Arial, sans-serif; color: #111; font-size: 9.5px; }
392
393    /* Header */
394    .header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; padding-bottom: 8px; border-bottom: 2.5px solid #FF7A00; }
395    .header-left {}
396    .brand { font-size: 9px; font-weight: 700; color: #FF7A00; letter-spacing: .08em; text-transform: uppercase; margin-bottom: 3px; }
397    .course-title { font-size: 15px; font-weight: 700; color: #0f172a; line-height: 1.2; margin-bottom: 2px; }
398    .course-sub { font-size: 9px; color: #64748b; }
399    .header-right { text-align: right; }
400    .sheet-label { font-size: 8.5px; font-weight: 600; color: #94a3b8; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 4px; }
401    .meta-grid { display: flex; flex-direction: column; gap: 2px; }
402    .meta-row { display: flex; gap: 5px; justify-content: flex-end; font-size: 9px; color: #334155; }
403    .meta-label { font-weight: 600; color: #64748b; }
404
405    /* Table */
406    table { width: 100%; border-collapse: collapse; }
407    thead tr { background: #0f172a; }
408    thead th {
409      color: #fff; font-size: 8px; font-weight: 600;
410      text-transform: uppercase; letter-spacing: .07em;
411      padding: 5px 7px; text-align: left;
412    }
413    tbody tr { border-bottom: 1px solid #e2e8f0; }
414    tbody tr:last-child { border-bottom: 2px solid #cbd5e1; }
415
416    td { padding: 5px 7px; vertical-align: top; }
417    .num { color: #94a3b8; font-size: 8.5px; width: 22px; text-align: center; padding-top: 6px; }
418    .cell-name { width: 24%; }
419    .student-name { font-weight: 600; font-size: 9.5px; color: #0f172a; margin-bottom: 1px; }
420    .student-email { font-size: 8px; color: #64748b; }
421    .cell { width: 18%; font-size: 9px; color: #334155; }
422    .cell-keys { width: 26%; }
423    .cell-sig { width: 24%; border-left: 1px dashed #cbd5e1; }
424
425    .key-row { display: flex; gap: 4px; align-items: baseline; margin-bottom: 1px; }
426    .key-label { font-size: 7.5px; font-weight: 600; color: #94a3b8; text-transform: uppercase; letter-spacing: .05em; white-space: nowrap; }
427    .key-val { font-family: 'Courier New', monospace; font-size: 8.5px; color: #0f172a; background: #f1f5f9; padding: 1px 4px; border-radius: 2px; }
428    .empty { color: #cbd5e1; }
429
430    /* Footer */
431    .footer { margin-top: 8px; display: flex; justify-content: space-between; align-items: center; padding-top: 5px; border-top: 1px solid #e2e8f0; }
432    .footer-left { font-size: 8px; color: #94a3b8; }
433    .footer-right { font-size: 8px; color: #94a3b8; }
434    .count-badge { display: inline-block; background: #FF7A00; color: #fff; font-size: 7.5px; font-weight: 700; padding: 1px 6px; border-radius: 10px; margin-left: 5px; }
435
436    /* Signature line hint */
437    .sig-hint { font-size: 7.5px; color: #cbd5e1; margin-top: 14px; border-top: 1px solid #e2e8f0; padding-top: 2px; }
438  </style>
439</head>
440<body>
441  <div class="header">
442    <div class="header-left">
443      <div class="brand">
443SkillForge Training</div>
444      <div class="course-title">${sessionData.course.title}</div>
445      <div class="course-sub">${sessionData.course.category}</div>
446    </div>
447    <div class="header-right">
448      <div class="sheet-label">Class Sign-In Sheet</div>
449      <div class="meta-grid">
450        <div class="meta-row"><span class="meta-label">Date:</span> ${dateStr}</div>
451        <div class="meta-row"><span class="meta-label">Time:</span> ${timeStr}</div>
452        <div class="meta-row"><span class="meta-label">Location:</span> ${location}</div>
453        ${address ? `<div class="meta-row"><span class="meta-label">Address:</span> ${address}</div>` : ""}
454        ${hasContact ? `<div class="meta-row"><span class="meta-label">Contact:</span> ${[contactName, contactPhone, contactEmail].filter(Boolean).join(" &nbsp;·&nbsp; ")}</div>` : ""}
455      </div>
456    </div>
457  </div>
458
459  <table>
460    <thead>
461      <tr>
462        <th style="width:28px">#</th>
463        <th style="width:22%">Student Name &amp; Email</th>
464        <th style="width:18%">Company</th>
465        <th style="width:28%">Access Key(s)</th>
466        <th style="width:22%">Signature</th>
467      </tr>
468    </thead>
469    <tbody>${rows}</tbody>
470  </table>
471
472  <div class="footer">
473    <div class="footer-left">
474      Printed ${(/* @__PURE__ */ new Date()).toLocaleString("en-US", {
475    dateStyle: "medium",
476    timeStyle: "short"
477  })}
478    </div>
479    <div class="footer-right">
480      Total Students <span class="count-badge">${students.length}</span>
481    </div>
482  </div>
483</body>
484</html>`;
485  const blob = new Blob([html], {
486    type: "text/html"
487  });
488  const url = URL.createObjectURL(blob);
489  const win = window.open(url, "_blank");
490  if (!win) return;
491  win.focus();
492  win.onload = () => {
493    win.print();
494    URL.revokeObjectURL(url);
495  };
496  setTimeout(() => URL.revokeObjectURL(url), 15e3);
497}
498function InstructorSessionRecordings({
499  session
500}) {
501  const [recs, setRecs] = reactExports.useState([{
502    url: session.recording1Url || "",
503    key: session.recording1Key || ""
504  }, {
505    url: session.recording2Url || "",
506    key: session.recording2Key || ""
507  }, {
508    url: session.recording3Url || "",
509    key: session.recording3Key || ""
510  }]);
511  const [saving, setSaving] = reactExports.useState(false);
512  const [dirty, setDirty] = reactExports.useState(false);
513  const [visibleCount, setVisibleCount] = reactExports.useState(() => {
514    if (session.recording3Url || session.recording3Key) return 3;
515    if (session.recording2Url || session.recording2Key) return 2;
516    return 1;
517  });
518  function updateRec(i, field, val) {
519    setRecs((prev) => {
520      const next = [...prev];
521      next[i] = {
522        ...next[i],
523        [field]: val
524      };
525      return next;
526    });
527    setDirty(true);
528  }
529  async function save() {
530    setSaving(true);
531    try {
532      const res = await fetch(`/api/sessions/${session.id}/recordings`, {
533        method: "PATCH",
534        headers: {
535          "Content-Type": "application/json"
536        },
537        body: JSON.stringify({
538          recording1Url: recs[0].url,
539          recording1Key: recs[0].key,
540          recording2Url: recs[1].url,
541          recording2Key: recs[1].key,
542          recording3Url: recs[2].url,
543          recording3Key: recs[2].key
544        })
545      });
546      if (!res.ok) throw new Error();
547      toast.success("Recordings saved");
548      setDirty(false);
549    } catch {
550      toast.error("Failed to save recordings");
551    } finally {
552      setSaving(false);
553    }
554  }
555  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border rounded-md p-3 space-y-2 bg-muted/30", children: [
556    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-medium text-muted-foreground flex items-center gap-1.5", children: [
557      /* @__PURE__ */ jsxRuntimeExports.jsx(Video, { className: "h-3 w-3" }),
558      "Session Recordings"
559    ] }),
560    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: Array.from({
561      length: visibleCount
562    }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
563      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground pl-1", children: [
564        "Recording ",
565        i + 1
566      ] }),
567      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
568        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground w-7 shrink-0 text-right text-xs", children: "URL" }),
569        /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { className: "h-6 text-xs flex-1", placeholder: "https://…", value: recs[i].url, onChange: (e) => updateRec(i, "url", e.target.value) })
570      ] }),
571      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
572        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground w-14 shrink-0 text-right text-xs", children: "Passcode" }),
573        /* @__PURE__ */ jsxRuntimeExports.jsx(Input, { className: "h-6 text-xs font-mono flex-1", placeholder: "Passcode…", value: recs[i].key, onChange: (e) => updateRec(i, "key", e.target.value) })
574      ] })
575    ] }, i)) }),
576    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 pt-1", children: [
577      visibleCount < 3 && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "text-xs text-primary hover:underline", onClick: () => setVisibleCount((v) => Math.min(v + 1, 3)), children: [
578        "+ Add Recording ",
579        visibleCount + 1
580      ] }),
581      /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { size: "sm", variant: "outline", className: "h-6 px-2 text-xs ml-auto", disabled: saving || !dirty, onClick: save, children: saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }) : "Save" })
582    ] })
583  ] });
584}
585function InstructorDashboard() {
586  const {
587    session,
588    isPending
589  } = useSession();
590  const navigate = useNavigate();
591  const [assignedSessions, setAssignedSessions] = reactExports.useState([]);
592  const [loading, setLoading] = reactExports.useState(true);
593  const [canWriteBlog, setCanWriteBlog] = reactExports.useState(false);
594  const [calendarDate, setCalendarDate] = reactExports.useState(() => {
595    const d = /* @__PURE__ */ new Date();
596    return {
597      year: d.getFullYear(),
598      month: d.getMonth()
599    };
600  });
601  const [rosterOpen, setRosterOpen] = reactExports.useState(false);
602  const [activeSession, setActiveSession] = reactExports.useState(null);
603  const [students, setStudents] = reactExports.useState([]);
604  const [loadingStudents, setLoadingStudents] = reactExports.useState(false);
605  const [bulkStatus, setBulkStatus] = reactExports.useState("attended");
606  const [bulkApplying, setBulkApplying] = reactExports.useState(false);
607  const [sessionSort, setSessionSort] = reactExports.useState("newest");
608  reactExports.useEffect(() => {
609    if (session == null ? void 0 : session.user) {
610      fetch(`/api/sessions?instructorId=${session.user.id}`).then((res) => res.json()).then((data) => {
611        setAssignedSessions(Array.isArray(data) ? data : []);
612        setLoading(false);
613      }).catch(() => setLoading(false));
614      fetch("/api/users/me").then((r) => r.ok ? r.json() : null).then((u) => {
615        if ((u == null ? void 0 : u.canWriteBlog) || (u == null ? void 0 : u.role) === "admin") setCanWriteBlog(true);
616      }).catch(() => {
617      });
618    }
619  }, [session]);
620  const openRoster = async (sessionData) => {
621    setActiveSession(sessionData);
622    setStudents([]);
623    setRosterOpen(true);
624    setLoadingStudents(true);
625    try {
626      const res = await fetch(`/api/enrollments/session/${sessionData.session.id}`);
627      const data = await res.json();
628      setStudents(Array.isArray(data) ? data : []);
629    } catch {
630      toast.error("Failed to load students");
631    } finally {
632      setLoadingStudents(false);
633    }
634  };
635  const handleUpdateStatus = reactExports.useCallback(async (enrollmentId, status) => {
636    try {
637      const res = await fetch(`/api/enrollments/${enrollmentId}/status`, {
638        method: "PATCH",
639        headers: {
640          "Content-Type": "application/json"
641        },
642        body: JSON.stringify({
643          status
644        })
645      });
646      if (res.ok) {
647        setStudents((prev) => prev.map((s) => s.enrollment.id === enrollmentId ? {
648          ...s,
649          enrollment: {
650            ...s.enrollment,
651            status
652          }
653        } : s));
654      } else {
655        toast.error("Failed to update status");
656      }
657    } catch {
658      toast.error("Failed to update status");
659    }
660  }, []);
661  const handleBulkUpdate = reactExports.useCallback(async () => {
662    if (!students.length) return;
663    setBulkApplying(true);
664    try {
665      await Promise.all(students.map((s) => fetch(`/api/enrollments/${s.enrollment.id}/status`, {
666        method: "PATCH",
667        headers: {
668          "Content-Type": "application/json"
669        },
670        body: JSON.stringify({
671          status: bulkStatus
672        })
673      })));
674      setStudents((prev) => prev.map((s) => ({
675        ...s,
676        enrollment: {
677          ...s.enrollment,
678          status: bulkStatus
679        }
680      })));
681      toast.success(`Marked all ${students.length} student${students.length !== 1 ? "s" : ""} as ${bulkStatus}`);
682    } catch {
683      toast.error("Some updates failed — please try again");
684    } finally {
685      setBulkApplying(false);
686    }
687  }, [students, bulkStatus]);
688  if (isPending || loading) {
689    return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-h-screen bg-background flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "Loading dashboard..." }) });
690  }
691  if (!(session == null ? void 0 : session.user)) return /* @__PURE__ */ jsxRuntimeExports.jsx(Navigate, { to: "/auth?mode=sign-in", replace: true });
692  const user = getUserWithRole(session.user);
693  if (user.role !== "instructor") {
694    if (user.role === "admin") return /* @__PURE__ */ jsxRuntimeExports.jsx(Navigate, { to: "/admin", replace: true });
695    if (user.role === "student") return /* @__PURE__ */ jsxRuntimeExports.jsx(Navigate, { to: "/student", replace: true });
696  }
697  const now = /* @__PURE__ */ new Date();
698  const upcomingSessions = assignedSessions.filter((s) => {
699    const end = parseLocalDate(s.session.endDate);
700    end.setDate(end.getDate() + 1);
701    return end > now;
702  });
703  const pastSessions = assignedSessions.filter((s) => {
704    const end = parseLocalDate(s.session.endDate);
705    end.setDate(end.getDate() + 1);
706    return end <= now;
707  });
708  const sortSessions = (list) => [...list].sort((a, b) => {
709    const aDate = new Date(a.session.startDate).getTime();
710    const bDate = new Date(b.session.startDate).getTime();
711    return sessionSort === "newest" ? bDate - aDate : aDate - bDate;
712  });
713  const sortedUpcoming = sortSessions(upcomingSessions);
714  const sortedPast = sortSessions(pastSessions);
715  const SessionRow = ({
716    sessionData,
717    past = false
718  }) => {
719    const isOnline = sessionData.session.deliveryMode === "online";
720    const startTime = formatTime(sessionData.session.startTime);
721    const endTime = formatTime(sessionData.session.endTime);
722    const tz = sessionData.session.timezone || "";
723    const hasContact = !isOnline && (sessionData.session.contactName || sessionData.session.contactEmail || sessionData.session.contactPhone);
724    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-start gap-4 px-4 py-3 rounded-lg border bg-card hover:bg-muted/40 transition-colors${past ? " opacity-60" : ""}`, children: [
725      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-shrink-0 w-14 text-center pt-0.5", children: [
726        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: formatSessionDate(sessionData.session.startDate, {
727          month: "short"
728        }) }),
729        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-2xl font-bold text-foreground leading-none", children: new Date(sessionData.session.startDate).getUTCDate() }),
730        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: formatSessionDate(sessionData.session.startDate, {
731          weekday: "short"
732        }) })
733      ] }),
734      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px self-stretch bg-border" }),
735      /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: sessionData.course.imageUrl, alt: sessionData.course.title, className: `h-10 w-16 rounded object-cover flex-shrink-0 mt-0.5${past ? " grayscale" : ""}` }),
736      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
737        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-0.5", children: [
738          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-sm text-foreground truncate", children: sessionData.course.title }),
739          /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: past ? "outline" : "secondary", className: "text-xs shrink-0", children: past ? "Completed" : sessionData.course.category })
740        ] }),
741        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
742          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
743            isOnline ? /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "h-3 w-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Building2, { className: "h-3 w-3" }),
744            isOnline ? "Live Online" : sessionData.session.location || "Location TBD"
745          ] }),
746          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
747            /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { className: "h-3 w-3" }),
748            "Max ",
749            sessionData.session.maxStudents
750          ] }),
751          /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
752            /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { className: "h-3 w-3" }),
753            startTime,
754            " – ",
755            endTime,
756            tz ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60", children: [
757              " ",
758              tz
759            ] }) : null
760          ] })
761        ] }),
762        !isOnline && sessionData.session.address && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-muted-foreground mt-1 flex items-start gap-1", children: [
763          /* @__PURE__ */ jsxRuntimeExports.jsx(MapPin, { className: "h-3 w-3 shrink-0 mt-0.5" }),
764          /* @__P
764URE__ */ jsxRuntimeExports.jsx("span", { children: sessionData.session.address })
765        ] }),
766        hasContact && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1.5 flex flex-wrap gap-x-4 gap-y-0.5 text-xs", children: [
767          sessionData.session.contactName && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground", children: [
768            /* @__PURE__ */ jsxRuntimeExports.jsx(CircleUser, { className: "h-3 w-3 shrink-0" }),
769            sessionData.session.contactName
770          ] }),
771          sessionData.session.contactPhone && /* @__PURE__ */ jsxRuntimeExports.jsxs("a", { href: `tel:${sessionData.session.contactPhone}`, className: "flex items-center gap-1 text-primary hover:underline", children: [
772            /* @__PURE__ */ jsxRuntimeExports.jsx(Phone, { className: "h-3 w-3 shrink-0" }),
773            sessionData.session.contactPhone
774          ] }),
775          sessionData.session.contactEmail && /* @__PURE__ */ jsxRuntimeExports.jsxs("a", { href: `mailto:${sessionData.session.contactEmail}`, className: "flex items-center gap-1 text-primary hover:underline", children: [
776            /* @__PURE__ */ jsxRuntimeExports.jsx(Mail, { className: "h-3 w-3 shrink-0" }),
777            sessionData.session.contactEmail
778          ] })
779        ] }),
780        sessionData.session.notes && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 flex items-start gap-2", children: [
781          /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-3.5 w-3.5 shrink-0 mt-0.5 text-amber-600" }),
782          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
783            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-semibold text-amber-800 mb-0.5", children: "Session Notes" }),
784            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-amber-900 whitespace-pre-line", children: sessionData.session.notes })
785          ] })
786        ] }),
787        /* @__PURE__ */ jsxRuntimeExports.jsx(CoursewareSection, { courseId: sessionData.course.id })
788      ] }),
789      /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", variant: "outline", className: "shrink-0 mt-0.5", onClick: () => openRoster(sessionData), children: [
790        /* @__PURE__ */ jsxRuntimeExports.jsx(Users, { className: "h-3.5 w-3.5 mr-1.5" }),
791        "Roster"
792      ] })
793    ] });
794  };
795  const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
796  const calendarCells = (() => {
797    const {
798      year,
799      month
800    } = calendarDate;
801    const firstDay = new Date(year, month, 1).getDay();
802    const daysInMonth = new Date(year, month + 1, 0).getDate();
803    const cells = [];
804    for (let i = 0; i < firstDay; i++) cells.push(null);
805    for (let d = 1; d <= daysInMonth; d++) cells.push(d);
806    while (cells.length % 7 !== 0) cells.push(null);
807    return cells;
808  })();
809  const sessionsByDay = assignedSessions.reduce((acc, s) => {
810    const d = new Date(s.session.startDate);
811    if (d.getUTCFullYear() === calendarDate.year && d.getUTCMonth() === calendarDate.month) {
812      const day = d.getUTCDate();
813      if (!acc[day]) acc[day] = [];
814      acc[day].push(s);
815    }
816    return acc;
817  }, {});
818  const today = /* @__PURE__ */ new Date();
819  const isToday = (day) => today.getFullYear() === calendarDate.year && today.getMonth() === calendarDate.month && today.getDate() === day;
820  const monthLabel = new Date(calendarDate.year, calendarDate.month, 1).toLocaleDateString("en-US", {
821    month: "long",
822    year: "numeric"
823  });
824  const prevMonth = () => setCalendarDate(({
825    year,
826    month
827  }) => month === 0 ? {
828    year: year - 1,
829    month: 11
830  } : {
831    year,
832    month: month - 1
833  });
834  const nextMonth = () => setCalendarDate(({
835    year,
836    month
837  }) => month === 11 ? {
838    year: year + 1,
839    month: 0
840  } : {
841    year,
842    month: month + 1
843  });
844  const goToday = () => setCalendarDate({
845    year: today.getFullYear(),
846    month: today.getMonth()
847  });
848  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-h-screen bg-background py-12", children: [
849    /* @__PURE__ */ jsxRuntimeExports.jsxs(Helmet, { children: [
850      /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: "Instructor Portal - SkillForge" }),
851      /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "description", content: "SkillForge instructor portal." }),
852      /* @__PURE__ */ jsxRuntimeExports.jsx("link", { rel: "canonical", href: "https://skillforge.com/instructor" }),
853      /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "robots", content: "noindex, nofollow" })
854    ] }),
855    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: [
856      /* @__PURE__ */ jsxRuntimeExports.jsxs(motion.div, { initial: {
857        opacity: 0,
858        y: 20
859      }, animate: {
860        opacity: 1,
861        y: 0
862      }, transition: {
863        duration: 0.5
864      }, className: "mb-8 flex items-center justify-between", children: [
865        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
866          /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-4xl font-bold text-foreground", children: "Instructor Portal" }),
867          /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "mt-2 text-lg text-muted-foreground", children: [
868            "Welcome back, ",
869            session.user.name,
870            "!"
871          ] })
872        ] }),
873        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
874          canWriteBlog && /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", onClick: () => navigate("/instructor/blog"), children: [
875            /* @__PURE__ */ jsxRuntimeExports.jsx(PenLine, { className: "h-4 w-4 mr-2" }),
876            " Blog Posts"
877          ] }),
878          /* @__PURE__ */ jsxRuntimeExports.jsx(ProfileMenu, {})
879        ] })
880      ] }),
881      /* @__PURE__ */ jsxRuntimeExports.jsxs(motion.div, { initial: {
882        opacity: 0,
883        y: 20
884      }, animate: {
885        opacity: 1,
886        y: 0
887      }, transition: {
888        duration: 0.5,
889        delay: 0.1
890      }, className: "grid grid-cols-1 md:grid-cols-3 gap-6 mb-8", children: [
891        /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
892          /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-2xl", children: upcomingSessions.length }),
893          /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: "Upcoming Sessions" })
894        ] }) }),
895        /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
896          /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-2xl", children: pastSessions.length }),
897          /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: "Completed Sessions" })
898        ] }) }),
899        /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, { children: [
900          /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-2xl", children: assignedSessions.length }),
901          /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: "Total Assignments" })
902        ] }) })
903      ] }),
904      /* @__PURE__ */ jsxRuntimeExports.jsx(motion.div, { initial: {
905        opacity: 0,
906        y: 20
907      }, animate: {
908        opacity: 1,
909        y: 0
910      }, transition: {
911        duration: 0.5,
912        delay: 0.2
913      }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "upcoming", className: "w-full", children: [
914        /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { className: "grid w-full grid-cols-5 mb-8", children: [
915          /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, { value: "upcoming", children: [
916            "Upcoming (",
917            upcomingSessions.length,
918            ")"
919          ] }),
920          /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "calendar", children: "Calendar" }),
921          /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, { value: "past", children: [
922            "Past (",
923            pastSessions.length,
924            ")"
925          ] }),
926          /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "surveys", children: "Surveys" }),
927          /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "manuals", children: "Manuals" })
928        ] }),
929        /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "upcoming", children: upcomingSessions.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
930          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end mb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: sessionSort, onValueChange: (v) => setSessionSort(v), children: [
931            /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-36 h-8 text-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, {}) }),
932            /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
933              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "newest", children: "Newest first" }),
934              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "oldest", children: "Oldest first" })
935            ] })
936          ] }) }),
937          sortedUpcoming.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionRow, { sessionData: s }, s.session.id))
938        ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "py-12 text-center", children: [
939          /* @__P
939URE__ */ jsxRuntimeExports.jsx(BookOpen, { className: "h-12 w-12 text-muted-foreground mx-auto mb-4" }),
940          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "No upcoming sessions assigned" })
941        ] }) }) }),
942        /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "calendar", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
943          /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
944            /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "icon", onClick: prevMonth, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "h-4 w-4" }) }),
945            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
946              /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold", children: monthLabel }),
947              /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "outline", size: "sm", className: "text-xs h-7 px-2", onClick: goToday, children: "Today" })
948            ] }),
949            /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "icon", onClick: nextMonth, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "h-4 w-4" }) })
950          ] }) }),
951          /* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, { className: "p-0 pb-4", children: [
952            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-7 border-b", children: WEEKDAYS.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-2 text-center text-xs font-semibold text-muted-foreground uppercase tracking-wide", children: d }, d)) }),
953            /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-7", children: calendarCells.map((day, idx) => {
954              const sessions = day ? sessionsByDay[day] ?? [] : [];
955              const today2 = isToday(day ?? -1);
956              return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `min-h-[90px] p-1.5 border-b border-r relative
957                            ${idx % 7 === 0 ? "border-l" : ""}
958                            ${!day ? "bg-muted/20" : "bg-background"}
959                          `, children: day && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
960                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-flex items-center justify-center w-6 h-6 text-xs font-medium rounded-full mb-1
961                                ${today2 ? "bg-primary text-primary-foreground" : "text-foreground"}`, children: day }),
962                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0.5", children: sessions.map((s) => {
963                  const isOnline = s.session.deliveryMode === "online";
964                  return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { onClick: () => openRoster(s), className: `w-full text-left rounded px-1.5 py-0.5 text-[10px] leading-tight font-medium truncate transition-opacity hover:opacity-80
965                                        ${isOnline ? "bg-[#4A7A8A]/15 text-[#4A7A8A] border border-[#4A7A8A]/30" : "bg-[#FF7A00]/15 text-[#FF7A00] border border-[#FF7A00]/30"}`, title: `${s.course.title} — ${formatTime(s.session.startTime)} ${s.session.timezone}`, children: [
966                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate", children: s.course.title }),
967                    /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "block text-[9px] opacity-70 font-normal", children: [
968                      isOnline ? /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "inline h-2 w-2 mr-0.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Building2, { className: "inline h-2 w-2 mr-0.5" }),
969                      formatTime(s.session.startTime)
970                    ] })
971                  ] }, s.session.id);
972                }) })
973              ] }) }, idx);
974            }) }),
975            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 px-4 pt-3 text-xs text-muted-foreground", children: [
976              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
977                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block w-3 h-3 rounded-sm bg-[#4A7A8A]/20 border border-[#4A7A8A]/40" }),
978                "Live Online"
979              ] }),
980              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [
981                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block w-3 h-3 rounded-sm bg-[#FF7A00]/20 border border-[#FF7A00]/40" }),
982                "Onsite"
983              ] }),
984              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1.5 ml-auto", children: "Click any session to view roster" })
985            ] })
986          ] })
987        ] }) }),
988        /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "past", children: pastSessions.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
989          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end mb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: sessionSort, onValueChange: (v) => setSessionSort(v), children: [
990            /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-36 h-8 text-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, {}) }),
991            /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
992              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "newest", children: "Newest first" }),
993              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "oldest", children: "Oldest first" })
994            ] })
995          ] }) }),
996          sortedPast.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionRow, { sessionData: s, past: true }, s.session.id))
997        ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Card, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "py-12 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "No completed sessions yet" }) }) }) }),
998        /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "surveys", children: /* @__PURE__ */ jsxRuntimeExports.jsx(InstructorSurveysTab, {}) }),
999        /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "manuals", children: /* @__PURE__ */ jsxRuntimeExports.jsx(InstructorManualsTab, {}) })
1000      ] }) })
1001    ] }),
1002    /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: rosterOpen, onOpenChange: setRosterOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] overflow-y-auto", children: [
1003      /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { children: [
1004        /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Student Roster" }),
1005        /* @__PURE__ */ jsxRuntimeExports.jsx(DialogDescription, { children: activeSession == null ? void 0 : activeSession.course.title })
1006      ] }),
1007      loadingStudents ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-8 text-center", children: [
1008        /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-6 w-6 animate-spin mx-auto text-muted-foreground" }),
1009        /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground mt-2", children: "Loading students..." })
1010      ] }) : students.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1011        activeSession && /* @__PURE__ */ jsxRuntimeExports.jsx(InstructorSessionRecordings, { session: activeSession.session }),
1012        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 p-3 bg-muted/50 rounded-lg border", children: [
1013          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-muted-foreground whitespace-nowrap", children: "Bulk update:" }),
1014          /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: bulkStatus, onValueChange: setBulkStatus, children: [
1015            /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "h-8 flex-1 text-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, {}) }),
1016            /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
1017              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "enrolled", children: "Enrolled" }),
1018              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "attended", children: "Attended" }),
1019              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "no-show", children: "No Show" }),
1020              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "cancelled", children: "Cancelled" }),
1021              /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "rescheduled", children: "Rescheduled" })
1022            ] })
1023          ] }),
1024          /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { size: "sm", className: "whitespace-nowrap", disabled: bulkApplying, onClick: handleBulkUpdate, children: [
1025            bulkApplying ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CheckCheck, { className: "h-3.5 w-3.5 mr-1.5" }),
1026            "Apply to All"
1027          ] })
1028        ] }),
1029        /* @__PURE__ */ jsxRuntimeExports.jsx(Separator, {}),
1030        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: students.map((studentData) => {
1031          var _a, _b;
1032          const keys = [studentData.enrollment.accessKey1, studentData.enrollment.accessKey2, studentData.enrollment.accessKey3, studentData.enrollment.accessKey4].filter(Boolean);
1033          return /* @__PURE__ */ jsxRuntimeExports.jsxs(Card, { children: [
1034            /* @__PURE__ */ jsxRuntimeExports.jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
1035              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
1036                /* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-base", children: studentData.enrollment.studentName || ((_a = studentData.student) == null ? void 0 : _a.name) }),
1037                /* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { className: "break-words text-xs", children: studentData.enrollment.studentEmail || ((_b = studentData.student) == null ? void 0 : _b.email) }),
1038                studentData.enrollment.studentCompany && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: studentData.enrollment.studentCompany })
1039              ] }),
1040              /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: studentData.enrollment.status || "enrolled", onValueChange: (val) => handleUpdateStatus(studentData.enrollment.id, val), children: [
1041                /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "h-7 w-28 text-xs shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, {}) }),
1042                /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
1043                  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "enrolled", children: "Enrolled" }),
1044                  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "attended", children: "Attended" }),
1045                  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "no-show", children: "No Show" }),
1046                  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "cancelled", children: "Cancelled" }),
1047                  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "rescheduled", children: "Rescheduled" })
1048                ] })
1049              ] })
1050            ] }) }),
1051            /* @__PURE__ */ jsxRuntimeExports.jsx(CardContent, { className: "pt-0 space-y-2", children: keys.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
1052              /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { className: "h-3 w-3 text-muted-foreground shrink-0" }),
1053              keys.map((k, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-xs bg-muted rounded px-2 py-0.5 font-mono", children: [
1054                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground not-italic", children: [
1055                  "Key ",
1056                  i + 1,
1057                  ":"
1058                ] }),
1059                k
1060              ] }, i))
1061            ] }) })
1062          ] }, studentData.enrollment.id);
1063        }) }),
1064        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pt-2 border-t", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", className: "w-full", onClick: () => activeSession && printSignInSheet(activeSession, students), children: [
1065          /* @__PURE__ */ jsxRuntimeExports.jsx(Printer, { className: "h-4 w-4 mr-2" }),
1066          "Print Sign-In Sheet"
1067        ] }) })
1068      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-8 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground", children: "No students enrolled yet" }) })
1069    ] }) })
1070  ] });
1071}
1072export {
1073  InstructorDashboard as default
1074};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.