@burdenoff/microfe-buildmyiq 2026.720.5 → 2026.725.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AssessmentsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/assessments/AssessmentsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAoEnD,eAAO,MAAM,eAAe,EAAE,EAuY7B,CAAC"}
1
+ {"version":3,"file":"AssessmentsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/assessments/AssessmentsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAoEnD,eAAO,MAAM,eAAe,EAAE,EAyZ7B,CAAC"}
@@ -2,22 +2,22 @@ import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
2
  import "../../context/index.js";
3
3
  import { PageLayout as t } from "../../components/PageLayout.js";
4
4
  import { MetricsCard as n } from "../../components/MetricsCard.js";
5
- import { formatEnumLabel as ee, formatRelativeTime as te } from "../../utils/formatters.js";
6
- import { StatusBadge as r } from "../../components/StatusBadge.js";
7
- import { EmptyState as i } from "../../components/EmptyState.js";
8
- import { ErrorView as ne, LoadingView as a } from "../../components/StateViews.js";
5
+ import { formatEnumLabel as r, formatRelativeTime as ee } from "../../utils/formatters.js";
6
+ import { StatusBadge as i } from "../../components/StatusBadge.js";
7
+ import { EmptyState as a } from "../../components/EmptyState.js";
8
+ import { ErrorView as te, LoadingView as ne } from "../../components/StateViews.js";
9
9
  import { DataTable as o } from "../../components/DataTable.js";
10
10
  import { FilterTabs as s } from "../../components/FilterTabs.js";
11
11
  import { useBuiAssessments as c, useBuiCourses as l, useBuiCreateAssessment as u } from "../../hooks/useBuildmyiqApi.js";
12
12
  import "../../hooks/index.js";
13
13
  import "../../components/index.js";
14
- import { ASSESSMENT_TYPES as re } from "../../types/index.js";
15
- import { useMemo as d, useState as f } from "react";
16
- import { CheckCircle2 as ie, ClipboardList as p, FileEdit as m, ListChecks as h, Plus as g } from "lucide-react";
17
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
18
- import { Button as y, GlassCard as b, Input as x, Label as S, ResponsiveDialog as C, ResponsiveDialogContent as w, ResponsiveDialogDescription as ae, ResponsiveDialogFooter as oe, ResponsiveDialogHeader as se, ResponsiveDialogTitle as ce, Select as T, SelectContent as E, SelectItem as D, SelectTrigger as O, SelectValue as k, Switch as A, Textarea as j } from "@burdenoff/fe-libs/ui";
14
+ import { ASSESSMENT_TYPES as d } from "../../types/index.js";
15
+ import { useMemo as f, useState as p } from "react";
16
+ import { CheckCircle2 as re, ClipboardList as m, FileEdit as ie, ListChecks as ae, Plus as h } from "lucide-react";
17
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
18
+ import { Button as v, GlassCard as y, Input as b, Label as x, ResponsiveDialog as S, ResponsiveDialogContent as C, ResponsiveDialogDescription as w, ResponsiveDialogFooter as oe, ResponsiveDialogHeader as se, ResponsiveDialogTitle as ce, Select as T, SelectContent as E, SelectItem as D, SelectTrigger as O, SelectValue as k, Switch as A, Textarea as le } from "@burdenoff/fe-libs/ui";
19
19
  //#region src/buildmyiq/pages/assessments/AssessmentsPage.tsx
20
- var M = [
20
+ var j = [
21
21
  {
22
22
  value: "ALL",
23
23
  label: "All"
@@ -34,7 +34,7 @@ var M = [
34
34
  value: "ARCHIVED",
35
35
  label: "Archived"
36
36
  }
37
- ], N = () => ({
37
+ ], M = () => ({
38
38
  title: "",
39
39
  description: "",
40
40
  type: "QUIZ",
@@ -42,8 +42,8 @@ var M = [
42
42
  maxAttempts: "1",
43
43
  timeLimitMin: "0",
44
44
  isGraded: !0
45
- }), P = () => {
46
- let { navigateTo: P } = e(), [F, I] = f(""), [L, R] = f("ALL"), [z, B] = f(!1), [V, H] = f(N), [U, W] = f(null), G = l({ limit: 100 }), K = c(F || void 0), q = u(), J = G.data?.items ?? [], Y = d(() => K.data ?? [], [K.data]), X = d(() => {
45
+ }), N = () => {
46
+ let { navigateTo: N } = e(), [P, F] = p(""), [I, L] = p("ALL"), [R, z] = p(!1), [B, V] = p(M), [H, U] = p(null), W = l({ limit: 100 }), G = c(P || void 0), K = c(P || void 0, "ARCHIVED"), q = u(), J = W.data?.items ?? [], Y = f(() => [...G.data ?? [], ...K.data ?? []], [G.data, K.data]), X = f(() => {
47
47
  let e = {
48
48
  ALL: Y.length,
49
49
  DRAFT: 0,
@@ -52,20 +52,22 @@ var M = [
52
52
  };
53
53
  for (let t of Y) e[t.status] += 1;
54
54
  return e;
55
- }, [Y]), Z = d(() => L === "ALL" ? Y : Y.filter((e) => e.status === L), [Y, L]), le = d(() => Y.reduce((e, t) => e + (t.questions?.length ?? 0), 0), [Y]), ue = M.map((e) => ({
55
+ }, [Y]), Z = f(() => I === "ALL" ? Y : Y.filter((e) => e.status === I), [Y, I]), ue = f(() => Y.reduce((e, t) => e + (t.questions?.length ?? 0), 0), [Y]), de = j.map((e) => ({
56
56
  value: e.value,
57
57
  label: e.label,
58
58
  count: X[e.value]
59
- })), de = [
59
+ })), fe = G.isLoading || K.isLoading, pe = G.isError || K.isError, me = G.isSuccess && K.isSuccess, he = () => {
60
+ G.refetch(), K.refetch();
61
+ }, ge = [
60
62
  {
61
63
  key: "title",
62
64
  header: "Assessment",
63
- render: (e) => /* @__PURE__ */ v("div", {
65
+ render: (e) => /* @__PURE__ */ _("div", {
64
66
  className: "min-w-0",
65
- children: [/* @__PURE__ */ _("p", {
67
+ children: [/* @__PURE__ */ g("p", {
66
68
  className: "font-semibold text-text-primary",
67
69
  children: e.title
68
- }), e.description && /* @__PURE__ */ _("p", {
70
+ }), e.description && /* @__PURE__ */ g("p", {
69
71
  className: "mt-0.5 max-w-md truncate text-xs text-text-secondary",
70
72
  title: e.description,
71
73
  children: e.description
@@ -75,13 +77,13 @@ var M = [
75
77
  {
76
78
  key: "type",
77
79
  header: "Type",
78
- render: (e) => /* @__PURE__ */ _(r, { status: e.type })
80
+ render: (e) => /* @__PURE__ */ g(i, { status: e.type })
79
81
  },
80
82
  {
81
83
  key: "status",
82
84
  header: "Status",
83
85
  sortValue: (e) => e.status,
84
- render: (e) => /* @__PURE__ */ _(r, { status: e.status })
86
+ render: (e) => /* @__PURE__ */ g(i, { status: e.status })
85
87
  },
86
88
  {
87
89
  key: "passingScore",
@@ -89,7 +91,7 @@ var M = [
89
91
  align: "right",
90
92
  hideOnMobile: !0,
91
93
  sortValue: (e) => e.passingScore,
92
- render: (e) => /* @__PURE__ */ v("span", {
94
+ render: (e) => /* @__PURE__ */ _("span", {
93
95
  className: "tabular-nums text-text-secondary",
94
96
  children: [e.passingScore, "%"]
95
97
  })
@@ -99,7 +101,7 @@ var M = [
99
101
  header: "Questions",
100
102
  align: "right",
101
103
  hideOnMobile: !0,
102
- render: (e) => /* @__PURE__ */ _("span", {
104
+ render: (e) => /* @__PURE__ */ g("span", {
103
105
  className: "tabular-nums text-text-secondary",
104
106
  children: e.questions?.length ?? "—"
105
107
  })
@@ -109,298 +111,298 @@ var M = [
109
111
  header: "Updated",
110
112
  hideOnMobile: !0,
111
113
  sortValue: (e) => e.updatedAt,
112
- render: (e) => /* @__PURE__ */ _("span", {
114
+ render: (e) => /* @__PURE__ */ g("span", {
113
115
  className: "whitespace-nowrap text-text-secondary",
114
- children: te(e.updatedAt)
116
+ children: ee(e.updatedAt)
115
117
  })
116
118
  }
117
119
  ], Q = () => {
118
- H(N()), W(null), B(!0);
120
+ V(M()), U(null), z(!0);
119
121
  }, $ = () => {
120
- B(!1), H(N()), W(null);
122
+ z(!1), V(M()), U(null);
121
123
  };
122
- return /* @__PURE__ */ v(t, {
124
+ return /* @__PURE__ */ _(t, {
123
125
  title: "Assessments",
124
126
  description: "Quizzes, exams and graded assignments for every course in your workspace.",
125
127
  titleGradient: !0,
126
128
  aurora: !0,
127
- actions: /* @__PURE__ */ v(y, {
129
+ actions: /* @__PURE__ */ _(v, {
128
130
  onClick: Q,
129
- disabled: !F,
130
- children: [/* @__PURE__ */ _(g, { className: "mr-1.5 h-4 w-4" }), " New assessment"]
131
+ disabled: !P,
132
+ children: [/* @__PURE__ */ g(h, { className: "mr-1.5 h-4 w-4" }), " New assessment"]
131
133
  }),
132
134
  children: [
133
- /* @__PURE__ */ _(b, {
135
+ /* @__PURE__ */ g(y, {
134
136
  treatment: "plain",
135
137
  className: "mb-5 p-4",
136
- children: /* @__PURE__ */ v("div", {
138
+ children: /* @__PURE__ */ _("div", {
137
139
  className: "flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end",
138
- children: [/* @__PURE__ */ v("div", {
140
+ children: [/* @__PURE__ */ _("div", {
139
141
  className: "grid gap-1.5",
140
142
  children: [
141
- /* @__PURE__ */ _("span", {
143
+ /* @__PURE__ */ g("span", {
142
144
  className: "text-xs font-medium uppercase tracking-wide text-text-secondary",
143
145
  children: "Course"
144
146
  }),
145
- /* @__PURE__ */ v(T, {
146
- value: F,
147
+ /* @__PURE__ */ _(T, {
148
+ value: P,
147
149
  onValueChange: (e) => {
148
- I(e), R("ALL");
150
+ F(e), L("ALL");
149
151
  },
150
- children: [/* @__PURE__ */ _(O, {
152
+ children: [/* @__PURE__ */ g(O, {
151
153
  className: "w-full sm:w-72",
152
- children: /* @__PURE__ */ _(k, { placeholder: "Select a course" })
153
- }), /* @__PURE__ */ _(E, { children: J.map((e) => /* @__PURE__ */ _(D, {
154
+ children: /* @__PURE__ */ g(k, { placeholder: "Select a course" })
155
+ }), /* @__PURE__ */ g(E, { children: J.map((e) => /* @__PURE__ */ g(D, {
154
156
  value: e.id,
155
157
  children: e.title
156
158
  }, e.id)) })]
157
159
  }),
158
- G.isSuccess && J.length === 0 && /* @__PURE__ */ _("p", {
160
+ W.isSuccess && J.length === 0 && /* @__PURE__ */ g("p", {
159
161
  className: "text-xs text-text-secondary",
160
162
  children: "No courses yet — create one in Authoring first."
161
163
  })
162
164
  ]
163
- }), F && /* @__PURE__ */ v("div", {
165
+ }), P && /* @__PURE__ */ _("div", {
164
166
  className: "grid gap-1.5",
165
- children: [/* @__PURE__ */ _("span", {
167
+ children: [/* @__PURE__ */ g("span", {
166
168
  className: "text-xs font-medium uppercase tracking-wide text-text-secondary",
167
169
  children: "Status"
168
- }), /* @__PURE__ */ _(s, {
169
- options: ue,
170
- value: L,
171
- onChange: R
170
+ }), /* @__PURE__ */ g(s, {
171
+ options: de,
172
+ value: I,
173
+ onChange: L
172
174
  })]
173
175
  })]
174
176
  })
175
177
  }),
176
- F && K.isSuccess && Y.length > 0 && /* @__PURE__ */ v("div", {
178
+ P && me && Y.length > 0 && /* @__PURE__ */ _("div", {
177
179
  className: "mb-5 grid grid-cols-2 gap-4 lg:grid-cols-4",
178
180
  children: [
179
- /* @__PURE__ */ _(n, {
180
- icon: /* @__PURE__ */ _(p, { className: "h-5 w-5" }),
181
+ /* @__PURE__ */ g(n, {
182
+ icon: /* @__PURE__ */ g(m, { className: "h-5 w-5" }),
181
183
  value: X.ALL,
182
184
  label: "Assessments"
183
185
  }),
184
- /* @__PURE__ */ _(n, {
185
- icon: /* @__PURE__ */ _(ie, { className: "h-5 w-5" }),
186
+ /* @__PURE__ */ g(n, {
187
+ icon: /* @__PURE__ */ g(re, { className: "h-5 w-5" }),
186
188
  value: X.PUBLISHED,
187
189
  label: "Published",
188
190
  accent: "success"
189
191
  }),
190
- /* @__PURE__ */ _(n, {
191
- icon: /* @__PURE__ */ _(m, { className: "h-5 w-5" }),
192
+ /* @__PURE__ */ g(n, {
193
+ icon: /* @__PURE__ */ g(ie, { className: "h-5 w-5" }),
192
194
  value: X.DRAFT,
193
195
  label: "Drafts"
194
196
  }),
195
- /* @__PURE__ */ _(n, {
196
- icon: /* @__PURE__ */ _(h, { className: "h-5 w-5" }),
197
- value: le,
197
+ /* @__PURE__ */ g(n, {
198
+ icon: /* @__PURE__ */ g(ae, { className: "h-5 w-5" }),
199
+ value: ue,
198
200
  label: "Questions"
199
201
  })
200
202
  ]
201
203
  }),
202
- F ? K.isLoading ? /* @__PURE__ */ _(a, { label: "Loading assessments…" }) : K.isError ? /* @__PURE__ */ _(ne, {
204
+ P ? fe ? /* @__PURE__ */ g(ne, { label: "Loading assessments…" }) : pe ? /* @__PURE__ */ g(te, {
203
205
  message: "Couldn't load assessments for this course.",
204
- onRetry: () => K.refetch()
205
- }) : Z.length === 0 ? /* @__PURE__ */ _(i, {
206
+ onRetry: he
207
+ }) : Z.length === 0 ? /* @__PURE__ */ g(a, {
206
208
  illustration: "empty-data",
207
- icon: /* @__PURE__ */ _(p, { className: "h-10 w-10" }),
208
- title: L === "ALL" ? "No assessments yet" : "No assessments match this filter",
209
- description: L === "ALL" ? "Create the first quiz, exam or assignment for this course." : "Try a different status filter, or create a new assessment.",
210
- action: /* @__PURE__ */ v(y, {
209
+ icon: /* @__PURE__ */ g(m, { className: "h-10 w-10" }),
210
+ title: I === "ALL" ? "No assessments yet" : "No assessments match this filter",
211
+ description: I === "ALL" ? "Create the first quiz, exam or assignment for this course." : "Try a different status filter, or create a new assessment.",
212
+ action: /* @__PURE__ */ _(v, {
211
213
  onClick: Q,
212
- children: [/* @__PURE__ */ _(g, { className: "mr-1.5 h-4 w-4" }), " New assessment"]
214
+ children: [/* @__PURE__ */ g(h, { className: "mr-1.5 h-4 w-4" }), " New assessment"]
213
215
  })
214
- }) : /* @__PURE__ */ _(o, {
215
- columns: de,
216
+ }) : /* @__PURE__ */ g(o, {
217
+ columns: ge,
216
218
  rows: Z,
217
219
  rowKey: (e) => e.id,
218
- onRowClick: (e) => P(`assessments/${e.id}`),
220
+ onRowClick: (e) => N(`assessments/${e.id}`),
219
221
  defaultSort: {
220
222
  key: "updatedAt",
221
223
  dir: "desc"
222
224
  }
223
- }) : /* @__PURE__ */ _(i, {
225
+ }) : /* @__PURE__ */ g(a, {
224
226
  illustration: "onboarding",
225
- icon: /* @__PURE__ */ _(p, { className: "h-10 w-10" }),
227
+ icon: /* @__PURE__ */ g(m, { className: "h-10 w-10" }),
226
228
  title: "Pick a course to see its assessments",
227
229
  description: "Assessments are scoped to a course. Choose a course above to view, filter and create its quizzes and exams."
228
230
  }),
229
- /* @__PURE__ */ _(C, {
230
- open: z,
231
+ /* @__PURE__ */ g(S, {
232
+ open: R,
231
233
  onOpenChange: (e) => {
232
- e ? B(!0) : $();
234
+ e ? z(!0) : $();
233
235
  },
234
- children: /* @__PURE__ */ v(w, { children: [
235
- /* @__PURE__ */ v(se, { children: [/* @__PURE__ */ _(ce, { children: "New assessment" }), /* @__PURE__ */ _(ae, { children: "Add a quiz, exam or assignment to this course. You can add questions after it's created." })] }),
236
- /* @__PURE__ */ v("div", {
236
+ children: /* @__PURE__ */ _(C, { children: [
237
+ /* @__PURE__ */ _(se, { children: [/* @__PURE__ */ g(ce, { children: "New assessment" }), /* @__PURE__ */ g(w, { children: "Add a quiz, exam or assignment to this course. You can add questions after it's created." })] }),
238
+ /* @__PURE__ */ _("div", {
237
239
  className: "grid gap-4 py-2",
238
240
  children: [
239
- /* @__PURE__ */ v("div", {
241
+ /* @__PURE__ */ _("div", {
240
242
  className: "grid gap-1.5",
241
- children: [/* @__PURE__ */ _(S, {
243
+ children: [/* @__PURE__ */ g(x, {
242
244
  htmlFor: "assessmentTitle",
243
245
  children: "Title *"
244
- }), /* @__PURE__ */ _(x, {
246
+ }), /* @__PURE__ */ g(b, {
245
247
  id: "assessmentTitle",
246
- value: V.title,
247
- onChange: (e) => H({
248
- ...V,
248
+ value: B.title,
249
+ onChange: (e) => V({
250
+ ...B,
249
251
  title: e.target.value
250
252
  }),
251
253
  placeholder: "e.g. Module 1 knowledge check"
252
254
  })]
253
255
  }),
254
- /* @__PURE__ */ v("div", {
256
+ /* @__PURE__ */ _("div", {
255
257
  className: "grid gap-1.5",
256
- children: [/* @__PURE__ */ _(S, {
258
+ children: [/* @__PURE__ */ g(x, {
257
259
  htmlFor: "assessmentDescription",
258
260
  children: "Description"
259
- }), /* @__PURE__ */ _(j, {
261
+ }), /* @__PURE__ */ g(le, {
260
262
  id: "assessmentDescription",
261
- value: V.description,
262
- onChange: (e) => H({
263
- ...V,
263
+ value: B.description,
264
+ onChange: (e) => V({
265
+ ...B,
264
266
  description: e.target.value
265
267
  }),
266
268
  placeholder: "What this assessment covers and how it's graded.",
267
269
  rows: 2
268
270
  })]
269
271
  }),
270
- /* @__PURE__ */ v("div", {
272
+ /* @__PURE__ */ _("div", {
271
273
  className: "grid gap-2 sm:grid-cols-2",
272
- children: [/* @__PURE__ */ v("div", {
274
+ children: [/* @__PURE__ */ _("div", {
273
275
  className: "grid gap-1.5",
274
- children: [/* @__PURE__ */ _(S, {
276
+ children: [/* @__PURE__ */ g(x, {
275
277
  htmlFor: "assessmentType",
276
278
  children: "Type"
277
- }), /* @__PURE__ */ v(T, {
278
- value: V.type,
279
- onValueChange: (e) => H({
280
- ...V,
279
+ }), /* @__PURE__ */ _(T, {
280
+ value: B.type,
281
+ onValueChange: (e) => V({
282
+ ...B,
281
283
  type: e
282
284
  }),
283
- children: [/* @__PURE__ */ _(O, {
285
+ children: [/* @__PURE__ */ g(O, {
284
286
  id: "assessmentType",
285
- children: /* @__PURE__ */ _(k, {})
286
- }), /* @__PURE__ */ _(E, { children: re.map((e) => /* @__PURE__ */ _(D, {
287
+ children: /* @__PURE__ */ g(k, {})
288
+ }), /* @__PURE__ */ g(E, { children: d.map((e) => /* @__PURE__ */ g(D, {
287
289
  value: e,
288
- children: ee(e)
290
+ children: r(e)
289
291
  }, e)) })]
290
292
  })]
291
- }), /* @__PURE__ */ v("div", {
293
+ }), /* @__PURE__ */ _("div", {
292
294
  className: "grid gap-1.5",
293
- children: [/* @__PURE__ */ _(S, {
295
+ children: [/* @__PURE__ */ g(x, {
294
296
  htmlFor: "passingScore",
295
297
  children: "Passing score (%)"
296
- }), /* @__PURE__ */ _(x, {
298
+ }), /* @__PURE__ */ g(b, {
297
299
  id: "passingScore",
298
300
  type: "number",
299
301
  min: "0",
300
302
  max: "100",
301
303
  step: "1",
302
- value: V.passingScore,
303
- onChange: (e) => H({
304
- ...V,
304
+ value: B.passingScore,
305
+ onChange: (e) => V({
306
+ ...B,
305
307
  passingScore: e.target.value
306
308
  }),
307
309
  placeholder: "70"
308
310
  })]
309
311
  })]
310
312
  }),
311
- /* @__PURE__ */ v("div", {
313
+ /* @__PURE__ */ _("div", {
312
314
  className: "grid gap-2 sm:grid-cols-2",
313
- children: [/* @__PURE__ */ v("div", {
315
+ children: [/* @__PURE__ */ _("div", {
314
316
  className: "grid gap-1.5",
315
- children: [/* @__PURE__ */ _(S, {
317
+ children: [/* @__PURE__ */ g(x, {
316
318
  htmlFor: "maxAttempts",
317
319
  children: "Max attempts"
318
- }), /* @__PURE__ */ _(x, {
320
+ }), /* @__PURE__ */ g(b, {
319
321
  id: "maxAttempts",
320
322
  type: "number",
321
323
  min: "1",
322
324
  step: "1",
323
- value: V.maxAttempts,
324
- onChange: (e) => H({
325
- ...V,
325
+ value: B.maxAttempts,
326
+ onChange: (e) => V({
327
+ ...B,
326
328
  maxAttempts: e.target.value
327
329
  }),
328
330
  placeholder: "1"
329
331
  })]
330
- }), /* @__PURE__ */ v("div", {
332
+ }), /* @__PURE__ */ _("div", {
331
333
  className: "grid gap-1.5",
332
- children: [/* @__PURE__ */ _(S, {
334
+ children: [/* @__PURE__ */ g(x, {
333
335
  htmlFor: "timeLimitMin",
334
336
  children: "Time limit (minutes)"
335
- }), /* @__PURE__ */ _(x, {
337
+ }), /* @__PURE__ */ g(b, {
336
338
  id: "timeLimitMin",
337
339
  type: "number",
338
340
  min: "0",
339
341
  step: "1",
340
- value: V.timeLimitMin,
341
- onChange: (e) => H({
342
- ...V,
342
+ value: B.timeLimitMin,
343
+ onChange: (e) => V({
344
+ ...B,
343
345
  timeLimitMin: e.target.value
344
346
  }),
345
347
  placeholder: "0 = no limit"
346
348
  })]
347
349
  })]
348
350
  }),
349
- /* @__PURE__ */ v("div", {
351
+ /* @__PURE__ */ _("div", {
350
352
  className: "flex items-center justify-between rounded-lg border border-border-seam bg-bg-surface px-3 py-2.5",
351
- children: [/* @__PURE__ */ v("div", {
353
+ children: [/* @__PURE__ */ _("div", {
352
354
  className: "min-w-0",
353
- children: [/* @__PURE__ */ _(S, {
355
+ children: [/* @__PURE__ */ g(x, {
354
356
  htmlFor: "isGraded",
355
357
  className: "cursor-pointer",
356
358
  children: "Graded"
357
- }), /* @__PURE__ */ _("p", {
359
+ }), /* @__PURE__ */ g("p", {
358
360
  className: "text-xs text-text-secondary",
359
361
  children: "Contributes to the learner's score and pass/fail outcome."
360
362
  })]
361
- }), /* @__PURE__ */ _(A, {
363
+ }), /* @__PURE__ */ g(A, {
362
364
  id: "isGraded",
363
- checked: V.isGraded,
364
- onCheckedChange: (e) => H({
365
- ...V,
365
+ checked: B.isGraded,
366
+ onCheckedChange: (e) => V({
367
+ ...B,
366
368
  isGraded: e
367
369
  })
368
370
  })]
369
371
  }),
370
- U && /* @__PURE__ */ _("p", {
372
+ H && /* @__PURE__ */ g("p", {
371
373
  className: "text-sm text-status-error-text",
372
- children: U
374
+ children: H
373
375
  })
374
376
  ]
375
377
  }),
376
- /* @__PURE__ */ v(oe, { children: [/* @__PURE__ */ _(y, {
378
+ /* @__PURE__ */ _(oe, { children: [/* @__PURE__ */ g(v, {
377
379
  variant: "outline",
378
380
  onClick: $,
379
381
  children: "Cancel"
380
- }), /* @__PURE__ */ _(y, {
382
+ }), /* @__PURE__ */ g(v, {
381
383
  onClick: async () => {
382
- if (!F) {
383
- W("Pick a course first.");
384
+ if (!P) {
385
+ U("Pick a course first.");
384
386
  return;
385
387
  }
386
- if (!V.title.trim()) {
387
- W("Title is required.");
388
+ if (!B.title.trim()) {
389
+ U("Title is required.");
388
390
  return;
389
391
  }
390
392
  try {
391
393
  let e = await q.mutateAsync({
392
- courseId: F,
393
- title: V.title.trim(),
394
- description: V.description.trim() || void 0,
395
- type: V.type,
396
- passingScore: V.passingScore ? Number(V.passingScore) : void 0,
397
- maxAttempts: V.maxAttempts ? Number(V.maxAttempts) : void 0,
398
- timeLimitMin: V.timeLimitMin ? Number(V.timeLimitMin) : void 0,
399
- isGraded: V.isGraded
394
+ courseId: P,
395
+ title: B.title.trim(),
396
+ description: B.description.trim() || void 0,
397
+ type: B.type,
398
+ passingScore: B.passingScore ? Number(B.passingScore) : void 0,
399
+ maxAttempts: B.maxAttempts ? Number(B.maxAttempts) : void 0,
400
+ timeLimitMin: B.timeLimitMin ? Number(B.timeLimitMin) : void 0,
401
+ isGraded: B.isGraded
400
402
  });
401
- $(), P(`assessments/${e.id}`);
403
+ $(), N(`assessments/${e.id}`);
402
404
  } catch (e) {
403
- W(e instanceof Error ? e.message : "Failed to create assessment.");
405
+ U(e instanceof Error ? e.message : "Failed to create assessment.");
404
406
  }
405
407
  },
406
408
  disabled: q.isPending,
@@ -412,6 +414,6 @@ var M = [
412
414
  });
413
415
  };
414
416
  //#endregion
415
- export { P as AssessmentsPage };
417
+ export { N as AssessmentsPage };
416
418
 
417
419
  //# sourceMappingURL=AssessmentsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AssessmentsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/assessments/AssessmentsPage.tsx"],"sourcesContent":["/**\n * AssessmentsPage — the assessment authoring surface (primary page of the section).\n * @module buildmyiq/pages/assessments\n *\n * `buiListAssessments` REQUIRES a courseId, so this page is course-scoped: the\n * educator first picks a course, then sees its assessments filtered by status.\n * Everything is real `bui*` data — no fabrication. Create flow wires\n * `useBuiCreateAssessment`.\n */\n\nimport { useMemo, useState, type FC } from 'react';\nimport { Plus, ClipboardList, CheckCircle2, FileEdit, ListChecks } from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Switch,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n FilterTabs,\n DataTable,\n EmptyState,\n LoadingView,\n ErrorView,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useBuiAssessments, useBuiCreateAssessment } from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport { ASSESSMENT_TYPES } from '../../types';\nimport type { BuiAssessment, BuiAssessmentStatus, BuiAssessmentType } from '../../types';\n\ntype StatusFilter = 'ALL' | BuiAssessmentStatus;\n\nconst STATUS_TABS: { value: StatusFilter; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\ninterface AssessmentFormState {\n title: string;\n description: string;\n type: BuiAssessmentType;\n passingScore: string;\n maxAttempts: string;\n timeLimitMin: string;\n isGraded: boolean;\n}\n\nconst makeEmptyForm = (): AssessmentFormState => ({\n title: '',\n description: '',\n type: 'QUIZ',\n passingScore: '70',\n maxAttempts: '1',\n timeLimitMin: '0',\n isGraded: true,\n});\n\nexport const AssessmentsPage: FC = () => {\n const { navigateTo } = useBuildmyiq();\n\n const [courseId, setCourseId] = useState<string>('');\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<AssessmentFormState>(makeEmptyForm);\n const [formError, setFormError] = useState<string | null>(null);\n\n const coursesQuery = useBuiCourses({ limit: 100 });\n // Fetch the full (unfiltered) list so the tab counts stay accurate; filter client-side.\n const assessmentsQuery = useBuiAssessments(courseId || undefined);\n const createAssessment = useBuiCreateAssessment();\n\n const courses = coursesQuery.data?.items ?? [];\n const assessments = useMemo(() => assessmentsQuery.data ?? [], [assessmentsQuery.data]);\n\n const statusCounts = useMemo(() => {\n const counts: Record<StatusFilter, number> = {\n ALL: assessments.length,\n DRAFT: 0,\n PUBLISHED: 0,\n ARCHIVED: 0,\n };\n for (const a of assessments) counts[a.status] += 1;\n return counts;\n }, [assessments]);\n\n const rows = useMemo(\n () => (status === 'ALL' ? assessments : assessments.filter((a) => a.status === status)),\n [assessments, status]\n );\n\n const totalQuestions = useMemo(\n () => assessments.reduce((sum, a) => sum + (a.questions?.length ?? 0), 0),\n [assessments]\n );\n\n const tabOptions: FilterOption<StatusFilter>[] = STATUS_TABS.map((t) => ({\n value: t.value,\n label: t.label,\n count: statusCounts[t.value],\n }));\n\n const columns: Column<BuiAssessment>[] = [\n {\n key: 'title',\n header: 'Assessment',\n render: (a) => (\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-text-primary\">{a.title}</p>\n {a.description && (\n <p\n className=\"mt-0.5 max-w-md truncate text-xs text-text-secondary\"\n title={a.description}\n >\n {a.description}\n </p>\n )}\n </div>\n ),\n },\n {\n key: 'type',\n header: 'Type',\n render: (a) => <StatusBadge status={a.type} />,\n },\n {\n key: 'status',\n header: 'Status',\n sortValue: (a) => a.status,\n render: (a) => <StatusBadge status={a.status} />,\n },\n {\n key: 'passingScore',\n header: 'Pass',\n align: 'right',\n hideOnMobile: true,\n sortValue: (a) => a.passingScore,\n render: (a) => <span className=\"tabular-nums text-text-secondary\">{a.passingScore}%</span>,\n },\n {\n key: 'questions',\n header: 'Questions',\n align: 'right',\n hideOnMobile: true,\n render: (a) => (\n <span className=\"tabular-nums text-text-secondary\">{a.questions?.length ?? '—'}</span>\n ),\n },\n {\n key: 'updatedAt',\n header: 'Updated',\n hideOnMobile: true,\n sortValue: (a) => a.updatedAt,\n render: (a) => (\n <span className=\"whitespace-nowrap text-text-secondary\">\n {formatRelativeTime(a.updatedAt)}\n </span>\n ),\n },\n ];\n\n const openDialog = () => {\n setForm(makeEmptyForm());\n setFormError(null);\n setDialogOpen(true);\n };\n\n const closeDialog = () => {\n setDialogOpen(false);\n setForm(makeEmptyForm());\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!courseId) {\n setFormError('Pick a course first.');\n return;\n }\n if (!form.title.trim()) {\n setFormError('Title is required.');\n return;\n }\n try {\n const created = await createAssessment.mutateAsync({\n courseId,\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n type: form.type,\n passingScore: form.passingScore ? Number(form.passingScore) : undefined,\n maxAttempts: form.maxAttempts ? Number(form.maxAttempts) : undefined,\n timeLimitMin: form.timeLimitMin ? Number(form.timeLimitMin) : undefined,\n isGraded: form.isGraded,\n });\n closeDialog();\n navigateTo(`assessments/${created.id}`);\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create assessment.');\n }\n };\n\n return (\n <PageLayout\n title=\"Assessments\"\n description=\"Quizzes, exams and graded assignments for every course in your workspace.\"\n titleGradient\n aurora\n actions={\n <Button onClick={openDialog} disabled={!courseId}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New assessment\n </Button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"mb-5 p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end\">\n <div className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Course\n </span>\n <Select\n value={courseId}\n onValueChange={(v) => {\n setCourseId(v);\n setStatus('ALL');\n }}\n >\n <SelectTrigger className=\"w-full sm:w-72\">\n <SelectValue placeholder=\"Select a course\" />\n </SelectTrigger>\n <SelectContent>\n {courses.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {coursesQuery.isSuccess && courses.length === 0 && (\n <p className=\"text-xs text-text-secondary\">\n No courses yet — create one in Authoring first.\n </p>\n )}\n </div>\n {courseId && (\n <div className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Status\n </span>\n <FilterTabs options={tabOptions} value={status} onChange={setStatus} />\n </div>\n )}\n </div>\n </GlassCard>\n\n {courseId && assessmentsQuery.isSuccess && assessments.length > 0 && (\n <div className=\"mb-5 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<ClipboardList className=\"h-5 w-5\" />}\n value={statusCounts.ALL}\n label=\"Assessments\"\n />\n <MetricsCard\n icon={<CheckCircle2 className=\"h-5 w-5\" />}\n value={statusCounts.PUBLISHED}\n label=\"Published\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<FileEdit className=\"h-5 w-5\" />}\n value={statusCounts.DRAFT}\n label=\"Drafts\"\n />\n <MetricsCard\n icon={<ListChecks className=\"h-5 w-5\" />}\n value={totalQuestions}\n label=\"Questions\"\n />\n </div>\n )}\n\n {!courseId ? (\n <EmptyState\n illustration=\"onboarding\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title=\"Pick a course to see its assessments\"\n description=\"Assessments are scoped to a course. Choose a course above to view, filter and create its quizzes and exams.\"\n />\n ) : assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n message=\"Couldn't load assessments for this course.\"\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title={status === 'ALL' ? 'No assessments yet' : 'No assessments match this filter'}\n description={\n status === 'ALL'\n ? 'Create the first quiz, exam or assignment for this course.'\n : 'Try a different status filter, or create a new assessment.'\n }\n action={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New assessment\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(a) => a.id}\n onRowClick={(a) => navigateTo(`assessments/${a.id}`)}\n defaultSort={{ key: 'updatedAt', dir: 'desc' }}\n />\n )}\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (!open) closeDialog();\n else setDialogOpen(true);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New assessment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a quiz, exam or assignment to this course. You can add questions after it&apos;s\n created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentTitle\">Title *</Label>\n <Input\n id=\"assessmentTitle\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n placeholder=\"e.g. Module 1 knowledge check\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentDescription\">Description</Label>\n <Textarea\n id=\"assessmentDescription\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"What this assessment covers and how it's graded.\"\n rows={2}\n />\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentType\">Type</Label>\n <Select\n value={form.type}\n onValueChange={(v) => setForm({ ...form, type: v as BuiAssessmentType })}\n >\n <SelectTrigger id=\"assessmentType\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSESSMENT_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {formatEnumLabel(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"passingScore\">Passing score (%)</Label>\n <Input\n id=\"passingScore\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.passingScore}\n onChange={(e) => setForm({ ...form, passingScore: e.target.value })}\n placeholder=\"70\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"maxAttempts\">Max attempts</Label>\n <Input\n id=\"maxAttempts\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={form.maxAttempts}\n onChange={(e) => setForm({ ...form, maxAttempts: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"timeLimitMin\">Time limit (minutes)</Label>\n <Input\n id=\"timeLimitMin\"\n type=\"number\"\n min=\"0\"\n step=\"1\"\n value={form.timeLimitMin}\n onChange={(e) => setForm({ ...form, timeLimitMin: e.target.value })}\n placeholder=\"0 = no limit\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between rounded-lg border border-border-seam bg-bg-surface px-3 py-2.5\">\n <div className=\"min-w-0\">\n <Label htmlFor=\"isGraded\" className=\"cursor-pointer\">\n Graded\n </Label>\n <p className=\"text-xs text-text-secondary\">\n Contributes to the learner&apos;s score and pass/fail outcome.\n </p>\n </div>\n <Switch\n id=\"isGraded\"\n checked={form.isGraded}\n onCheckedChange={(checked) => setForm({ ...form, isGraded: checked })}\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={closeDialog}>\n Cancel\n </Button>\n <Button onClick={handleCreate} disabled={createAssessment.isPending}>\n {createAssessment.isPending ? 'Creating…' : 'Create assessment'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssessmentsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/assessments/AssessmentsPage.tsx"],"sourcesContent":["/**\n * AssessmentsPage — the assessment authoring surface (primary page of the section).\n * @module buildmyiq/pages/assessments\n *\n * `buiListAssessments` REQUIRES a courseId, so this page is course-scoped: the\n * educator first picks a course, then sees its assessments filtered by status.\n * Everything is real `bui*` data — no fabrication. Create flow wires\n * `useBuiCreateAssessment`.\n */\n\nimport { useMemo, useState, type FC } from 'react';\nimport { Plus, ClipboardList, CheckCircle2, FileEdit, ListChecks } from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\n Switch,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n StatusBadge,\n FilterTabs,\n DataTable,\n EmptyState,\n LoadingView,\n ErrorView,\n type Column,\n type FilterOption,\n} from '../../components';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useBuiAssessments, useBuiCreateAssessment } from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport { ASSESSMENT_TYPES } from '../../types';\nimport type { BuiAssessment, BuiAssessmentStatus, BuiAssessmentType } from '../../types';\n\ntype StatusFilter = 'ALL' | BuiAssessmentStatus;\n\nconst STATUS_TABS: { value: StatusFilter; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\ninterface AssessmentFormState {\n title: string;\n description: string;\n type: BuiAssessmentType;\n passingScore: string;\n maxAttempts: string;\n timeLimitMin: string;\n isGraded: boolean;\n}\n\nconst makeEmptyForm = (): AssessmentFormState => ({\n title: '',\n description: '',\n type: 'QUIZ',\n passingScore: '70',\n maxAttempts: '1',\n timeLimitMin: '0',\n isGraded: true,\n});\n\nexport const AssessmentsPage: FC = () => {\n const { navigateTo } = useBuildmyiq();\n\n const [courseId, setCourseId] = useState<string>('');\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<AssessmentFormState>(makeEmptyForm);\n const [formError, setFormError] = useState<string | null>(null);\n\n const coursesQuery = useBuiCourses({ limit: 100 });\n // Fetch the unfiltered list for DRAFT/PUBLISHED/ALL (so those tab counts stay\n // accurate and switching between them is instant) plus a dedicated\n // `status: ARCHIVED` query. The two are merged below because\n // `buiListAssessments` scopes its soft-delete filter to the requested status\n // (BOFF-4780): an unfiltered/undefined-status call always excludes archived\n // (soft-deleted) rows, so they can never surface via client-side filtering\n // alone — they must be fetched explicitly.\n const assessmentsQuery = useBuiAssessments(courseId || undefined);\n const archivedAssessmentsQuery = useBuiAssessments(courseId || undefined, 'ARCHIVED');\n const createAssessment = useBuiCreateAssessment();\n\n const courses = coursesQuery.data?.items ?? [];\n const assessments = useMemo(\n () => [...(assessmentsQuery.data ?? []), ...(archivedAssessmentsQuery.data ?? [])],\n [assessmentsQuery.data, archivedAssessmentsQuery.data]\n );\n\n const statusCounts = useMemo(() => {\n const counts: Record<StatusFilter, number> = {\n ALL: assessments.length,\n DRAFT: 0,\n PUBLISHED: 0,\n ARCHIVED: 0,\n };\n for (const a of assessments) counts[a.status] += 1;\n return counts;\n }, [assessments]);\n\n const rows = useMemo(\n () => (status === 'ALL' ? assessments : assessments.filter((a) => a.status === status)),\n [assessments, status]\n );\n\n const totalQuestions = useMemo(\n () => assessments.reduce((sum, a) => sum + (a.questions?.length ?? 0), 0),\n [assessments]\n );\n\n const tabOptions: FilterOption<StatusFilter>[] = STATUS_TABS.map((t) => ({\n value: t.value,\n label: t.label,\n count: statusCounts[t.value],\n }));\n\n const assessmentsIsLoading = assessmentsQuery.isLoading || archivedAssessmentsQuery.isLoading;\n const assessmentsIsError = assessmentsQuery.isError || archivedAssessmentsQuery.isError;\n const assessmentsIsSuccess = assessmentsQuery.isSuccess && archivedAssessmentsQuery.isSuccess;\n const refetchAssessments = () => {\n void assessmentsQuery.refetch();\n void archivedAssessmentsQuery.refetch();\n };\n\n const columns: Column<BuiAssessment>[] = [\n {\n key: 'title',\n header: 'Assessment',\n render: (a) => (\n <div className=\"min-w-0\">\n <p className=\"font-semibold text-text-primary\">{a.title}</p>\n {a.description && (\n <p\n className=\"mt-0.5 max-w-md truncate text-xs text-text-secondary\"\n title={a.description}\n >\n {a.description}\n </p>\n )}\n </div>\n ),\n },\n {\n key: 'type',\n header: 'Type',\n render: (a) => <StatusBadge status={a.type} />,\n },\n {\n key: 'status',\n header: 'Status',\n sortValue: (a) => a.status,\n render: (a) => <StatusBadge status={a.status} />,\n },\n {\n key: 'passingScore',\n header: 'Pass',\n align: 'right',\n hideOnMobile: true,\n sortValue: (a) => a.passingScore,\n render: (a) => <span className=\"tabular-nums text-text-secondary\">{a.passingScore}%</span>,\n },\n {\n key: 'questions',\n header: 'Questions',\n align: 'right',\n hideOnMobile: true,\n render: (a) => (\n <span className=\"tabular-nums text-text-secondary\">{a.questions?.length ?? '—'}</span>\n ),\n },\n {\n key: 'updatedAt',\n header: 'Updated',\n hideOnMobile: true,\n sortValue: (a) => a.updatedAt,\n render: (a) => (\n <span className=\"whitespace-nowrap text-text-secondary\">\n {formatRelativeTime(a.updatedAt)}\n </span>\n ),\n },\n ];\n\n const openDialog = () => {\n setForm(makeEmptyForm());\n setFormError(null);\n setDialogOpen(true);\n };\n\n const closeDialog = () => {\n setDialogOpen(false);\n setForm(makeEmptyForm());\n setFormError(null);\n };\n\n const handleCreate = async () => {\n if (!courseId) {\n setFormError('Pick a course first.');\n return;\n }\n if (!form.title.trim()) {\n setFormError('Title is required.');\n return;\n }\n try {\n const created = await createAssessment.mutateAsync({\n courseId,\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n type: form.type,\n passingScore: form.passingScore ? Number(form.passingScore) : undefined,\n maxAttempts: form.maxAttempts ? Number(form.maxAttempts) : undefined,\n timeLimitMin: form.timeLimitMin ? Number(form.timeLimitMin) : undefined,\n isGraded: form.isGraded,\n });\n closeDialog();\n navigateTo(`assessments/${created.id}`);\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create assessment.');\n }\n };\n\n return (\n <PageLayout\n title=\"Assessments\"\n description=\"Quizzes, exams and graded assignments for every course in your workspace.\"\n titleGradient\n aurora\n actions={\n <Button onClick={openDialog} disabled={!courseId}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New assessment\n </Button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"mb-5 p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-end\">\n <div className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Course\n </span>\n <Select\n value={courseId}\n onValueChange={(v) => {\n setCourseId(v);\n setStatus('ALL');\n }}\n >\n <SelectTrigger className=\"w-full sm:w-72\">\n <SelectValue placeholder=\"Select a course\" />\n </SelectTrigger>\n <SelectContent>\n {courses.map((c) => (\n <SelectItem key={c.id} value={c.id}>\n {c.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {coursesQuery.isSuccess && courses.length === 0 && (\n <p className=\"text-xs text-text-secondary\">\n No courses yet — create one in Authoring first.\n </p>\n )}\n </div>\n {courseId && (\n <div className=\"grid gap-1.5\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Status\n </span>\n <FilterTabs options={tabOptions} value={status} onChange={setStatus} />\n </div>\n )}\n </div>\n </GlassCard>\n\n {courseId && assessmentsIsSuccess && assessments.length > 0 && (\n <div className=\"mb-5 grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <MetricsCard\n icon={<ClipboardList className=\"h-5 w-5\" />}\n value={statusCounts.ALL}\n label=\"Assessments\"\n />\n <MetricsCard\n icon={<CheckCircle2 className=\"h-5 w-5\" />}\n value={statusCounts.PUBLISHED}\n label=\"Published\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<FileEdit className=\"h-5 w-5\" />}\n value={statusCounts.DRAFT}\n label=\"Drafts\"\n />\n <MetricsCard\n icon={<ListChecks className=\"h-5 w-5\" />}\n value={totalQuestions}\n label=\"Questions\"\n />\n </div>\n )}\n\n {!courseId ? (\n <EmptyState\n illustration=\"onboarding\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title=\"Pick a course to see its assessments\"\n description=\"Assessments are scoped to a course. Choose a course above to view, filter and create its quizzes and exams.\"\n />\n ) : assessmentsIsLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsIsError ? (\n <ErrorView\n message=\"Couldn't load assessments for this course.\"\n onRetry={refetchAssessments}\n />\n ) : rows.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardList className=\"h-10 w-10\" />}\n title={status === 'ALL' ? 'No assessments yet' : 'No assessments match this filter'}\n description={\n status === 'ALL'\n ? 'Create the first quiz, exam or assignment for this course.'\n : 'Try a different status filter, or create a new assessment.'\n }\n action={\n <Button onClick={openDialog}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New assessment\n </Button>\n }\n />\n ) : (\n <DataTable\n columns={columns}\n rows={rows}\n rowKey={(a) => a.id}\n onRowClick={(a) => navigateTo(`assessments/${a.id}`)}\n defaultSort={{ key: 'updatedAt', dir: 'desc' }}\n />\n )}\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (!open) closeDialog();\n else setDialogOpen(true);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New assessment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a quiz, exam or assignment to this course. You can add questions after it&apos;s\n created.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentTitle\">Title *</Label>\n <Input\n id=\"assessmentTitle\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n placeholder=\"e.g. Module 1 knowledge check\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentDescription\">Description</Label>\n <Textarea\n id=\"assessmentDescription\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"What this assessment covers and how it's graded.\"\n rows={2}\n />\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"assessmentType\">Type</Label>\n <Select\n value={form.type}\n onValueChange={(v) => setForm({ ...form, type: v as BuiAssessmentType })}\n >\n <SelectTrigger id=\"assessmentType\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {ASSESSMENT_TYPES.map((t) => (\n <SelectItem key={t} value={t}>\n {formatEnumLabel(t)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"passingScore\">Passing score (%)</Label>\n <Input\n id=\"passingScore\"\n type=\"number\"\n min=\"0\"\n max=\"100\"\n step=\"1\"\n value={form.passingScore}\n onChange={(e) => setForm({ ...form, passingScore: e.target.value })}\n placeholder=\"70\"\n />\n </div>\n </div>\n\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"maxAttempts\">Max attempts</Label>\n <Input\n id=\"maxAttempts\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={form.maxAttempts}\n onChange={(e) => setForm({ ...form, maxAttempts: e.target.value })}\n placeholder=\"1\"\n />\n </div>\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"timeLimitMin\">Time limit (minutes)</Label>\n <Input\n id=\"timeLimitMin\"\n type=\"number\"\n min=\"0\"\n step=\"1\"\n value={form.timeLimitMin}\n onChange={(e) => setForm({ ...form, timeLimitMin: e.target.value })}\n placeholder=\"0 = no limit\"\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between rounded-lg border border-border-seam bg-bg-surface px-3 py-2.5\">\n <div className=\"min-w-0\">\n <Label htmlFor=\"isGraded\" className=\"cursor-pointer\">\n Graded\n </Label>\n <p className=\"text-xs text-text-secondary\">\n Contributes to the learner&apos;s score and pass/fail outcome.\n </p>\n </div>\n <Switch\n id=\"isGraded\"\n checked={form.isGraded}\n onCheckedChange={(checked) => setForm({ ...form, isGraded: checked })}\n />\n </div>\n\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={closeDialog}>\n Cancel\n </Button>\n <Button onClick={handleCreate} disabled={createAssessment.isPending}>\n {createAssessment.isPending ? 'Creating…' : 'Create assessment'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-buildmyiq",
3
- "version": "2026.720.5",
3
+ "version": "2026.725.1",
4
4
  "description": "Burdenoff BuildMyIQ Microfrontend — education, learning & development",
5
5
  "type": "module",
6
6
  "files": [