@anantagnanam/lms 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +19 -2
  2. package/astro/assignment-player.ts +231 -0
  3. package/astro/client.ts +24 -1
  4. package/astro/components/AssignmentFeedback.astro +32 -0
  5. package/astro/components/AssignmentRubric.astro +27 -0
  6. package/astro/components/LearnerDashboard.astro +3 -0
  7. package/astro/components/LearningShell.astro +6 -0
  8. package/astro/components/LessonContent.astro +16 -2
  9. package/astro/components/LessonPlayer.astro +126 -38
  10. package/astro/draft-queue.ts +95 -0
  11. package/astro/media-player.ts +26 -1
  12. package/astro/pages/author-preview.astro +2 -0
  13. package/astro/pages/study.astro +180 -0
  14. package/astro/quiz-drafts.ts +12 -76
  15. package/astro/study-tools.ts +386 -0
  16. package/astro/styles.css +18 -0
  17. package/astro/transcript-search.ts +56 -0
  18. package/dist/admin.js +172 -12
  19. package/dist/admin.js.map +1 -1
  20. package/dist/assignments-CsuAjxGO.js +75 -0
  21. package/dist/assignments-CsuAjxGO.js.map +1 -0
  22. package/dist/astro.js +1 -0
  23. package/dist/astro.js.map +1 -1
  24. package/dist/{constants-Cnx7k82Z.js → constants-DyQbwNbP.js} +2 -2
  25. package/dist/{constants-Cnx7k82Z.js.map → constants-DyQbwNbP.js.map} +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/runtime.d.ts +26 -5
  29. package/dist/runtime.js +307 -16
  30. package/dist/runtime.js.map +1 -1
  31. package/dist/schemas.d.ts +69 -3
  32. package/dist/schemas.js +47 -6
  33. package/dist/schemas.js.map +1 -1
  34. package/dist/server.d.ts +9 -2
  35. package/dist/server.js +6 -3
  36. package/dist/server.js.map +1 -1
  37. package/dist/types.d.ts +67 -2
  38. package/package.json +1 -1
  39. package/dist/errors-ChzJS9al.js +0 -16
  40. package/dist/errors-ChzJS9al.js.map +0 -1
package/dist/admin.js CHANGED
@@ -1,4 +1,4 @@
1
- import { r as PLUGIN_ID, t as API_BASE } from "./constants-Cnx7k82Z.js";
1
+ import { r as PLUGIN_ID, t as API_BASE } from "./constants-DyQbwNbP.js";
2
2
  import { assignmentSchema, courseDataSchema, lessonSchema, moduleSchema, questionSchema, quizSchema, settingsSchema } from "./schemas.js";
3
3
  import { n as enrollmentCsvLines, t as enrollmentCsvHeader } from "./csv-DKq_4OWU.js";
4
4
  import { useEffect, useState } from "react";
@@ -1117,6 +1117,83 @@ function ContentPicker({ value, onChange }) {
1117
1117
  })
1118
1118
  ] });
1119
1119
  }
1120
+ function RubricEditor({ assignment, update }) {
1121
+ const rubric = assignment.rubric ?? [];
1122
+ const change = (criteria, removed = false) => {
1123
+ const maxPoints = criteria.length ? criteria.reduce((sum, item) => sum + item.maxPoints, 0) : assignment.maxPoints;
1124
+ update({
1125
+ ...assignment,
1126
+ rubric: criteria,
1127
+ maxPoints,
1128
+ passingPoints: removed ? Math.min(assignment.passingPoints, maxPoints) : assignment.passingPoints
1129
+ });
1130
+ };
1131
+ return /* @__PURE__ */ jsxs("section", { children: [
1132
+ /* @__PURE__ */ jsx("h3", { children: "Grading rubric" }),
1133
+ /* @__PURE__ */ jsx("p", { children: "Describe what learners should demonstrate. Criterion points add up to the assignment maximum. Submitted work keeps its original rubric." }),
1134
+ rubric.map((criterion, index) => /* @__PURE__ */ jsxs("fieldset", {
1135
+ className: "lms-resource",
1136
+ children: [
1137
+ /* @__PURE__ */ jsxs("legend", { children: ["Criterion ", index + 1] }),
1138
+ /* @__PURE__ */ jsx(Field, {
1139
+ label: "Criterion title",
1140
+ children: /* @__PURE__ */ jsx("input", {
1141
+ required: true,
1142
+ maxLength: 200,
1143
+ value: criterion.title,
1144
+ onChange: (event) => change(rubric.map((item) => item.id === criterion.id ? {
1145
+ ...item,
1146
+ title: event.target.value
1147
+ } : item))
1148
+ })
1149
+ }),
1150
+ /* @__PURE__ */ jsx(Field, {
1151
+ label: "What earns these points",
1152
+ children: /* @__PURE__ */ jsx("textarea", {
1153
+ rows: 3,
1154
+ maxLength: 2e3,
1155
+ value: criterion.description,
1156
+ onChange: (event) => change(rubric.map((item) => item.id === criterion.id ? {
1157
+ ...item,
1158
+ description: event.target.value
1159
+ } : item))
1160
+ })
1161
+ }),
1162
+ /* @__PURE__ */ jsx(Field, {
1163
+ label: "Maximum criterion points",
1164
+ children: /* @__PURE__ */ jsx("input", {
1165
+ type: "number",
1166
+ required: true,
1167
+ min: 1,
1168
+ max: 1e3 - (assignment.maxPoints - criterion.maxPoints),
1169
+ step: 1,
1170
+ value: criterion.maxPoints,
1171
+ onChange: (event) => change(rubric.map((item) => item.id === criterion.id ? {
1172
+ ...item,
1173
+ maxPoints: Number(event.target.value)
1174
+ } : item))
1175
+ })
1176
+ }),
1177
+ /* @__PURE__ */ jsxs(Button, {
1178
+ danger: true,
1179
+ onClick: () => change(rubric.filter((item) => item.id !== criterion.id), true),
1180
+ children: ["Remove criterion ", index + 1]
1181
+ })
1182
+ ]
1183
+ }, criterion.id)),
1184
+ /* @__PURE__ */ jsx(Button, {
1185
+ disabled: rubric.length >= 20 || rubric.length > 0 && assignment.maxPoints >= 1e3,
1186
+ onClick: () => change([...rubric, {
1187
+ id: crypto.randomUUID(),
1188
+ title: "New criterion",
1189
+ description: "",
1190
+ maxPoints: rubric.length ? Math.min(10, 1e3 - assignment.maxPoints) : assignment.maxPoints
1191
+ }]),
1192
+ children: rubric.length ? "Add criterion" : "Add grading rubric"
1193
+ }),
1194
+ /* @__PURE__ */ jsx("p", { children: rubric.length ? `${rubric.length} ${rubric.length === 1 ? "criterion" : "criteria"} · ${assignment.maxPoints} points total (maximum 1000). Review the passing points when changing scores.` : "Without a rubric, graders enter a single overall score." })
1195
+ ] });
1196
+ }
1120
1197
  function LessonEditor({ lesson, previewUrl, update, remove }) {
1121
1198
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1122
1199
  /* @__PURE__ */ jsxs("div", {
@@ -1377,6 +1454,8 @@ function LessonEditor({ lesson, previewUrl, update, remove }) {
1377
1454
  children: /* @__PURE__ */ jsx("input", {
1378
1455
  type: "number",
1379
1456
  min: 1,
1457
+ max: 1e3,
1458
+ readOnly: Boolean(lesson.assignment.rubric?.length),
1380
1459
  value: lesson.assignment.maxPoints,
1381
1460
  onChange: (event) => update({ assignment: {
1382
1461
  ...lesson.assignment,
@@ -1389,6 +1468,7 @@ function LessonEditor({ lesson, previewUrl, update, remove }) {
1389
1468
  children: /* @__PURE__ */ jsx("input", {
1390
1469
  type: "number",
1391
1470
  min: 1,
1471
+ max: lesson.assignment.maxPoints,
1392
1472
  value: lesson.assignment.passingPoints,
1393
1473
  onChange: (event) => update({ assignment: {
1394
1474
  ...lesson.assignment,
@@ -1419,6 +1499,10 @@ function LessonEditor({ lesson, previewUrl, update, remove }) {
1419
1499
  allowResubmission: event.target.checked
1420
1500
  } })
1421
1501
  }), "Allow another submission after a failing grade"]
1502
+ }),
1503
+ /* @__PURE__ */ jsx(RubricEditor, {
1504
+ assignment: lesson.assignment,
1505
+ update: (assignment) => update({ assignment })
1422
1506
  })
1423
1507
  ]
1424
1508
  }),
@@ -2056,7 +2140,9 @@ function AssignmentsPage() {
2056
2140
  }, !!courseId);
2057
2141
  const [selected, setSelected] = useState();
2058
2142
  const [points, setPoints] = useState(0);
2143
+ const [rubricScores, setRubricScores] = useState([]);
2059
2144
  const [feedback, setFeedback] = useState("");
2145
+ const rubricTotal = rubricScores.reduce((sum, item) => sum + item.points, 0);
2060
2146
  const action = useAction();
2061
2147
  const grade = (event) => {
2062
2148
  event.preventDefault();
@@ -2067,7 +2153,7 @@ function AssignmentsPage() {
2067
2153
  lessonId: selected.lessonId,
2068
2154
  userId: selected.userId,
2069
2155
  submissionId: selected.id,
2070
- points,
2156
+ ...selected.rubric?.length ? { rubricScores } : { points },
2071
2157
  feedback
2072
2158
  }, "POST");
2073
2159
  setSelected(void 0);
@@ -2107,7 +2193,59 @@ function AssignmentsPage() {
2107
2193
  /* @__PURE__ */ jsxs("form", {
2108
2194
  onSubmit: grade,
2109
2195
  children: [
2110
- /* @__PURE__ */ jsx(Field, {
2196
+ selected.rubric?.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
2197
+ /* @__PURE__ */ jsx("p", { children: "Score each criterion using the rubric saved with this submission." }),
2198
+ selected.rubric.map((criterion) => {
2199
+ const score = rubricScores.find((item) => item.criterionId === criterion.id);
2200
+ const updateScore = (values) => setRubricScores((items) => items.map((item) => item.criterionId === criterion.id ? {
2201
+ ...item,
2202
+ ...values
2203
+ } : item));
2204
+ return /* @__PURE__ */ jsxs("fieldset", {
2205
+ className: "lms-resource",
2206
+ children: [
2207
+ /* @__PURE__ */ jsx("legend", { children: criterion.title }),
2208
+ /* @__PURE__ */ jsx("p", {
2209
+ className: "lms-body",
2210
+ children: criterion.description
2211
+ }),
2212
+ /* @__PURE__ */ jsx(Field, {
2213
+ label: `Points for ${criterion.title} (out of ${criterion.maxPoints})`,
2214
+ children: /* @__PURE__ */ jsx("input", {
2215
+ type: "number",
2216
+ required: true,
2217
+ min: 0,
2218
+ max: criterion.maxPoints,
2219
+ step: 1,
2220
+ value: score.points,
2221
+ onChange: (event) => updateScore({ points: Number(event.target.value) })
2222
+ })
2223
+ }),
2224
+ /* @__PURE__ */ jsx(Field, {
2225
+ label: `Feedback for ${criterion.title}`,
2226
+ children: /* @__PURE__ */ jsx("textarea", {
2227
+ rows: 3,
2228
+ maxLength: 2e3,
2229
+ value: score.feedback,
2230
+ onChange: (event) => updateScore({ feedback: event.target.value })
2231
+ })
2232
+ })
2233
+ ]
2234
+ }, criterion.id);
2235
+ }),
2236
+ /* @__PURE__ */ jsxs("p", {
2237
+ role: "status",
2238
+ children: [
2239
+ "Total: ",
2240
+ rubricTotal,
2241
+ "/",
2242
+ selected.maxPoints,
2243
+ " · Passing grade:",
2244
+ " ",
2245
+ selected.passingPoints
2246
+ ]
2247
+ })
2248
+ ] }) : /* @__PURE__ */ jsx(Field, {
2111
2249
  label: `Points out of ${selected.maxPoints}`,
2112
2250
  help: `Passing grade: ${selected.passingPoints}`,
2113
2251
  children: /* @__PURE__ */ jsx("input", {
@@ -2123,6 +2261,7 @@ function AssignmentsPage() {
2123
2261
  label: "Feedback",
2124
2262
  children: /* @__PURE__ */ jsx("textarea", {
2125
2263
  rows: 6,
2264
+ maxLength: 1e4,
2126
2265
  value: feedback,
2127
2266
  onChange: (event) => setFeedback(event.target.value)
2128
2267
  })
@@ -2164,18 +2303,39 @@ function AssignmentsPage() {
2164
2303
  rel: "noopener noreferrer",
2165
2304
  children: "Open attachment ↗"
2166
2305
  }) }),
2167
- item.status === "graded" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("p", { children: [
2168
- item.points,
2169
- "/",
2170
- item.maxPoints,
2171
- " points"
2172
- ] }), /* @__PURE__ */ jsx("p", {
2173
- className: "lms-body",
2174
- children: item.feedback
2175
- })] }) : /* @__PURE__ */ jsx(Button, {
2306
+ item.status === "graded" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2307
+ /* @__PURE__ */ jsxs("p", { children: [
2308
+ item.points,
2309
+ "/",
2310
+ item.maxPoints,
2311
+ " points"
2312
+ ] }),
2313
+ /* @__PURE__ */ jsx("p", {
2314
+ className: "lms-body",
2315
+ children: item.feedback
2316
+ }),
2317
+ (item.rubric ?? []).map((criterion) => {
2318
+ const score = item.rubricScores?.find((entry) => entry.criterionId === criterion.id);
2319
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("strong", { children: [
2320
+ criterion.title,
2321
+ ": ",
2322
+ score?.points ?? "—",
2323
+ "/",
2324
+ criterion.maxPoints
2325
+ ] }), score?.feedback && /* @__PURE__ */ jsx("p", {
2326
+ className: "lms-body",
2327
+ children: score.feedback
2328
+ })] }, criterion.id);
2329
+ })
2330
+ ] }) : /* @__PURE__ */ jsx(Button, {
2176
2331
  onClick: () => {
2177
2332
  setSelected(item);
2178
2333
  setPoints(0);
2334
+ setRubricScores((item.rubric ?? []).map((criterion) => ({
2335
+ criterionId: criterion.id,
2336
+ points: 0,
2337
+ feedback: ""
2338
+ })));
2179
2339
  setFeedback("");
2180
2340
  },
2181
2341
  children: "Review & grade"