@burdenoff/microfe-buildmyiq 2026.716.1 → 2026.720.2

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 (85) hide show
  1. package/dist/buildmyiq/BuildmyiqAdminRoot.d.ts.map +1 -1
  2. package/dist/buildmyiq/BuildmyiqAdminRoot.js +149 -135
  3. package/dist/buildmyiq/BuildmyiqAdminRoot.js.map +1 -1
  4. package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
  5. package/dist/buildmyiq/BuildmyiqRoot.js +59 -54
  6. package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
  7. package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
  8. package/dist/buildmyiq/components/Avatar.js.map +1 -1
  9. package/dist/buildmyiq/components/BuiDiscussion.js.map +1 -1
  10. package/dist/buildmyiq/components/BuiPermissionButton.d.ts +25 -0
  11. package/dist/buildmyiq/components/BuiPermissionButton.d.ts.map +1 -0
  12. package/dist/buildmyiq/components/BuiPermissionButton.js +15 -0
  13. package/dist/buildmyiq/components/BuiPermissionButton.js.map +1 -0
  14. package/dist/buildmyiq/components/BuiTags.js.map +1 -1
  15. package/dist/buildmyiq/components/DataTable.js.map +1 -1
  16. package/dist/buildmyiq/components/EmptyState.js.map +1 -1
  17. package/dist/buildmyiq/components/EvidenceUpload.js.map +1 -1
  18. package/dist/buildmyiq/components/FileLink.js.map +1 -1
  19. package/dist/buildmyiq/components/FilterTabs.js.map +1 -1
  20. package/dist/buildmyiq/components/MetricsCard.js.map +1 -1
  21. package/dist/buildmyiq/components/MiniBar.js.map +1 -1
  22. package/dist/buildmyiq/components/PageLayout.js.map +1 -1
  23. package/dist/buildmyiq/components/Pagination.js.map +1 -1
  24. package/dist/buildmyiq/components/StateViews.js.map +1 -1
  25. package/dist/buildmyiq/components/StatusBadge.js.map +1 -1
  26. package/dist/buildmyiq/components/Tabs.js.map +1 -1
  27. package/dist/buildmyiq/components/Viz.js.map +1 -1
  28. package/dist/buildmyiq/components/index.d.ts +1 -0
  29. package/dist/buildmyiq/components/index.d.ts.map +1 -1
  30. package/dist/buildmyiq/components/index.js +1 -0
  31. package/dist/buildmyiq/context/BuildmyiqContext.js.map +1 -1
  32. package/dist/buildmyiq/hooks/index.d.ts +1 -0
  33. package/dist/buildmyiq/hooks/index.d.ts.map +1 -1
  34. package/dist/buildmyiq/hooks/index.js +1 -0
  35. package/dist/buildmyiq/hooks/useBuiComments.js.map +1 -1
  36. package/dist/buildmyiq/hooks/useBuiFiles.js.map +1 -1
  37. package/dist/buildmyiq/hooks/useBuiPermissions.d.ts +54 -0
  38. package/dist/buildmyiq/hooks/useBuiPermissions.d.ts.map +1 -0
  39. package/dist/buildmyiq/hooks/useBuiPermissions.js +2 -0
  40. package/dist/buildmyiq/hooks/useBuiTags.js.map +1 -1
  41. package/dist/buildmyiq/hooks/useBuildmyiqApi.js.map +1 -1
  42. package/dist/buildmyiq/hooks/useCursorPager.js.map +1 -1
  43. package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
  44. package/dist/buildmyiq/pages/_shared/CourseCard.js.map +1 -1
  45. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
  46. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
  47. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
  48. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
  49. package/dist/buildmyiq/pages/authoring/AuthoringPage.js +97 -86
  50. package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
  51. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
  52. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +159 -118
  53. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
  54. package/dist/buildmyiq/pages/authoring/_forms.js.map +1 -1
  55. package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
  56. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
  57. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +75 -69
  58. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  59. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
  60. package/dist/buildmyiq/pages/certificates/CertificatesPage.js +186 -170
  61. package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
  62. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
  63. package/dist/buildmyiq/pages/cohorts/CohortsPage.js +177 -166
  64. package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
  65. package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
  66. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
  67. package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
  68. package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -1
  69. package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
  70. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
  71. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +160 -154
  72. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
  73. package/dist/buildmyiq/shared/hooks/useTr.js.map +1 -1
  74. package/dist/buildmyiq/types/index.js.map +1 -1
  75. package/dist/buildmyiq/utils/cn.js.map +1 -1
  76. package/dist/buildmyiq/utils/formatters.js.map +1 -1
  77. package/dist/buildmyiq/utils/navigation.js.map +1 -1
  78. package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
  79. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
  80. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  81. package/dist/services/createSandboxAssistantTransport.js.map +1 -1
  82. package/dist/utils/pageContext.d.ts.map +1 -1
  83. package/dist/utils/pageContext.js +7 -2
  84. package/dist/utils/pageContext.js.map +1 -1
  85. package/package.json +2 -2
@@ -9,17 +9,18 @@ import { Pagination as s } from "../../components/Pagination.js";
9
9
  import { useBuiCourses as c, useBuiCreateCourse as l, useBuiSkills as u } from "../../hooks/useBuildmyiqApi.js";
10
10
  import { useCursorPager as d } from "../../hooks/useCursorPager.js";
11
11
  import "../../hooks/index.js";
12
+ import { BuiPermissionButton as f } from "../../components/BuiPermissionButton.js";
12
13
  import "../../components/index.js";
13
- import { CourseCard as f } from "../_shared/CourseCard.js";
14
+ import { CourseCard as p } from "../_shared/CourseCard.js";
14
15
  import "../_shared/index.js";
15
- import { COURSE_LEVELS as p } from "../../types/index.js";
16
- import { Field as m, Modal as h, NO_SKILL as g, SelectField as _, slugify as v } from "./_forms.js";
17
- import { useMemo as y, useState as b } from "react";
18
- import { Plus as x } from "lucide-react";
19
- import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
20
- import { Button as T, GlassCard as E, Input as D, Switch as O, Textarea as k } from "@burdenoff/fe-libs/ui";
16
+ import { COURSE_LEVELS as m } from "../../types/index.js";
17
+ import { Field as h, Modal as g, NO_SKILL as _, SelectField as v, slugify as y } from "./_forms.js";
18
+ import { useMemo as b, useState as x } from "react";
19
+ import { Plus as S } from "lucide-react";
20
+ import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
21
+ import { Button as E, GlassCard as D, Input as O, Switch as k, Textarea as A } from "@burdenoff/fe-libs/ui";
21
22
  //#region src/buildmyiq/pages/authoring/AuthoringPage.tsx
22
- var A = [
23
+ var j = [
23
24
  {
24
25
  value: "ALL",
25
26
  label: "All"
@@ -40,30 +41,30 @@ var A = [
40
41
  value: "ARCHIVED",
41
42
  label: "Archived"
42
43
  }
43
- ], j = 24, M = ({ onClose: t, onCreated: r }) => {
44
- let { currentUser: i } = e(), a = u(), o = l(), [s, c] = b(""), [d, f] = b(""), [y, x] = b(!1), [E, O] = b(""), [A, j] = b("BEGINNER"), [M, N] = b("en"), [P, F] = b(""), [I, L] = b(g), [R, z] = b(null), B = a.data ?? [], V = (e) => {
45
- c(e), y || f(v(e));
44
+ ], M = 24, N = ({ onClose: t, onCreated: r }) => {
45
+ let { currentUser: i } = e(), a = u(), o = l(), [s, c] = x(""), [d, f] = x(""), [p, b] = x(!1), [S, D] = x(""), [k, j] = x("BEGINNER"), [M, N] = x("en"), [P, F] = x(""), [I, L] = x(_), [R, z] = x(null), B = a.data ?? [], V = (e) => {
46
+ c(e), p || f(y(e));
46
47
  }, H = s.trim().length > 0 && d.trim().length > 0 && !o.isPending;
47
- return /* @__PURE__ */ C(h, {
48
+ return /* @__PURE__ */ w(g, {
48
49
  open: !0,
49
50
  onClose: t,
50
51
  title: "New course",
51
52
  description: "Create a draft course — you can add modules, lessons and assessments next.",
52
53
  className: "sm:max-w-xl",
53
- footer: /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ C(T, {
54
+ footer: /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ w(E, {
54
55
  variant: "ghost",
55
56
  onClick: t,
56
57
  disabled: o.isPending,
57
58
  children: "Cancel"
58
- }), /* @__PURE__ */ C(T, {
59
+ }), /* @__PURE__ */ w(E, {
59
60
  onClick: async () => {
60
61
  if (!H) return;
61
62
  z(null);
62
63
  let e = {
63
64
  title: s.trim(),
64
65
  slug: d.trim(),
65
- summary: E.trim() || void 0,
66
- level: A,
66
+ summary: S.trim() || void 0,
67
+ level: k,
67
68
  language: M.trim() || "en",
68
69
  estimatedHours: P.trim() ? Number(P) : void 0,
69
70
  skillId: I === "__none__" ? void 0 : I,
@@ -78,73 +79,73 @@ var A = [
78
79
  disabled: !H,
79
80
  children: o.isPending ? "Creating…" : "Create course"
80
81
  })] }),
81
- children: /* @__PURE__ */ w("div", {
82
+ children: /* @__PURE__ */ T("div", {
82
83
  className: "grid gap-4",
83
84
  children: [
84
- /* @__PURE__ */ C(m, {
85
+ /* @__PURE__ */ w(h, {
85
86
  label: "Title",
86
87
  htmlFor: "new-course-title",
87
88
  required: !0,
88
- children: /* @__PURE__ */ C(D, {
89
+ children: /* @__PURE__ */ w(O, {
89
90
  id: "new-course-title",
90
91
  value: s,
91
92
  onChange: (e) => V(e.target.value),
92
93
  placeholder: "e.g. Foundations of Data Literacy"
93
94
  })
94
95
  }),
95
- /* @__PURE__ */ C(m, {
96
+ /* @__PURE__ */ w(h, {
96
97
  label: "Slug",
97
98
  htmlFor: "new-course-slug",
98
99
  required: !0,
99
100
  hint: "Used in the course URL. Auto-generated from the title — edit if you like.",
100
- children: /* @__PURE__ */ C(D, {
101
+ children: /* @__PURE__ */ w(O, {
101
102
  id: "new-course-slug",
102
103
  value: d,
103
104
  onChange: (e) => {
104
- f(e.target.value), x(!0);
105
+ f(e.target.value), b(!0);
105
106
  },
106
107
  placeholder: "foundations-of-data-literacy"
107
108
  })
108
109
  }),
109
- /* @__PURE__ */ C(m, {
110
+ /* @__PURE__ */ w(h, {
110
111
  label: "Summary",
111
112
  htmlFor: "new-course-summary",
112
- children: /* @__PURE__ */ C(k, {
113
+ children: /* @__PURE__ */ w(A, {
113
114
  id: "new-course-summary",
114
115
  rows: 3,
115
- value: E,
116
- onChange: (e) => O(e.target.value),
116
+ value: S,
117
+ onChange: (e) => D(e.target.value),
117
118
  placeholder: "A short description shown on the catalog card."
118
119
  })
119
120
  }),
120
- /* @__PURE__ */ w("div", {
121
+ /* @__PURE__ */ T("div", {
121
122
  className: "grid gap-4 sm:grid-cols-2",
122
123
  children: [
123
- /* @__PURE__ */ C(m, {
124
+ /* @__PURE__ */ w(h, {
124
125
  label: "Level",
125
- children: /* @__PURE__ */ C(_, {
126
- value: A,
126
+ children: /* @__PURE__ */ w(v, {
127
+ value: k,
127
128
  onChange: (e) => j(e),
128
- options: p.map((e) => ({
129
+ options: m.map((e) => ({
129
130
  value: e,
130
131
  label: n(e)
131
132
  }))
132
133
  })
133
134
  }),
134
- /* @__PURE__ */ C(m, {
135
+ /* @__PURE__ */ w(h, {
135
136
  label: "Language",
136
137
  htmlFor: "new-course-language",
137
- children: /* @__PURE__ */ C(D, {
138
+ children: /* @__PURE__ */ w(O, {
138
139
  id: "new-course-language",
139
140
  value: M,
140
141
  onChange: (e) => N(e.target.value),
141
142
  placeholder: "en"
142
143
  })
143
144
  }),
144
- /* @__PURE__ */ C(m, {
145
+ /* @__PURE__ */ w(h, {
145
146
  label: "Estimated hours",
146
147
  htmlFor: "new-course-hours",
147
- children: /* @__PURE__ */ C(D, {
148
+ children: /* @__PURE__ */ w(O, {
148
149
  id: "new-course-hours",
149
150
  type: "number",
150
151
  min: 0,
@@ -153,13 +154,13 @@ var A = [
153
154
  placeholder: "0"
154
155
  })
155
156
  }),
156
- /* @__PURE__ */ C(m, {
157
+ /* @__PURE__ */ w(h, {
157
158
  label: "Skill",
158
- children: /* @__PURE__ */ C(_, {
159
+ children: /* @__PURE__ */ w(v, {
159
160
  value: I,
160
161
  onChange: L,
161
162
  options: [{
162
- value: g,
163
+ value: _,
163
164
  label: a.isLoading ? "Loading…" : "No skill"
164
165
  }, ...B.map((e) => ({
165
166
  value: e.id,
@@ -169,89 +170,99 @@ var A = [
169
170
  })
170
171
  ]
171
172
  }),
172
- R && /* @__PURE__ */ C("p", {
173
+ R && /* @__PURE__ */ w("p", {
173
174
  className: "text-sm text-status-error-text",
174
175
  children: R
175
176
  })
176
177
  ]
177
178
  })
178
179
  });
179
- }, N = () => {
180
- let { currentUser: l, navigateTo: u } = e(), p = l?.id, [m, h] = b("ALL"), [g, _] = b(!1), [v, D] = b(!1), k = d(), N = c({
181
- status: m === "ALL" ? void 0 : m,
182
- cursor: k.cursor,
183
- limit: j
184
- }), P = y(() => N.data?.items ?? [], [N.data]), F = y(() => g && p ? P.filter((e) => e.instructorId === p) : P, [
185
- P,
186
- g,
187
- p
188
- ]), I = (e) => {
189
- h(e), k.reset();
190
- }, L = g ? "No courses you author yet" : m === "ALL" ? "No courses yet" : `No ${n(m).toLowerCase()} courses`;
191
- return /* @__PURE__ */ w(t, {
180
+ }, P = () => {
181
+ let { currentUser: l, navigateTo: u } = e(), m = l?.id, [h, g] = x("ALL"), [_, v] = x(!1), [y, O] = x(!1), A = d(), P = c({
182
+ status: h === "ALL" ? void 0 : h,
183
+ cursor: A.cursor,
184
+ limit: M
185
+ }), F = b(() => P.data?.items ?? [], [P.data]), I = b(() => _ && m ? F.filter((e) => e.instructorId === m) : F, [
186
+ F,
187
+ _,
188
+ m
189
+ ]), L = (e) => {
190
+ g(e), A.reset();
191
+ }, R = _ ? "No courses you author yet" : h === "ALL" ? "No courses yet" : `No ${n(h).toLowerCase()} courses`;
192
+ return /* @__PURE__ */ T(t, {
192
193
  title: "Authoring",
193
194
  description: "Create, curate and publish your workspace courses.",
194
195
  titleGradient: !0,
195
196
  aurora: !0,
196
- actions: /* @__PURE__ */ w(T, {
197
- onClick: () => D(!0),
198
- children: [/* @__PURE__ */ C(x, { className: "mr-1.5 h-4 w-4" }), " New course"]
197
+ actions: /* @__PURE__ */ w(f, {
198
+ resource: "buildmyiq-course",
199
+ action: "create",
200
+ deniedBehavior: "hide",
201
+ children: /* @__PURE__ */ T(E, {
202
+ onClick: () => O(!0),
203
+ children: [/* @__PURE__ */ w(S, { className: "mr-1.5 h-4 w-4" }), " New course"]
204
+ })
199
205
  }),
200
- children: [/* @__PURE__ */ w("div", {
206
+ children: [/* @__PURE__ */ T("div", {
201
207
  className: "flex flex-col gap-5",
202
- children: [/* @__PURE__ */ w(E, {
208
+ children: [/* @__PURE__ */ T(D, {
203
209
  treatment: "plain",
204
210
  className: "flex flex-col gap-3 p-3 sm:flex-row sm:items-center sm:justify-between",
205
- children: [/* @__PURE__ */ C("div", {
211
+ children: [/* @__PURE__ */ w("div", {
206
212
  className: "-mx-1 overflow-x-auto px-1",
207
- children: /* @__PURE__ */ C(o, {
208
- options: A,
209
- value: m,
210
- onChange: I,
213
+ children: /* @__PURE__ */ w(o, {
214
+ options: j,
215
+ value: h,
216
+ onChange: L,
211
217
  className: "border-0 bg-transparent p-0"
212
218
  })
213
- }), /* @__PURE__ */ w("label", {
219
+ }), /* @__PURE__ */ T("label", {
214
220
  className: "inline-flex shrink-0 cursor-pointer items-center gap-2 text-sm text-text-secondary",
215
- children: [/* @__PURE__ */ C(O, {
216
- checked: g,
217
- onCheckedChange: _,
221
+ children: [/* @__PURE__ */ w(k, {
222
+ checked: _,
223
+ onCheckedChange: v,
218
224
  disabled: !l?.id
219
225
  }), "Mine only"]
220
226
  })]
221
- }), N.isLoading ? /* @__PURE__ */ C(a, { label: "Loading courses…" }) : N.isError ? /* @__PURE__ */ C(i, {
227
+ }), P.isLoading ? /* @__PURE__ */ w(a, { label: "Loading courses…" }) : P.isError ? /* @__PURE__ */ w(i, {
222
228
  message: "We couldn't load your courses.",
223
- onRetry: () => N.refetch()
224
- }) : /* @__PURE__ */ w(S, { children: [F.length === 0 ? /* @__PURE__ */ C(r, {
229
+ onRetry: () => P.refetch()
230
+ }) : /* @__PURE__ */ T(C, { children: [I.length === 0 ? /* @__PURE__ */ w(r, {
225
231
  illustration: "empty-data",
226
- title: L,
227
- description: g ? "None of the courses on this page are authored by you — clear \"Mine only\" or page through to find them." : "Create your first course to start building curriculum, assessments and cohorts.",
228
- action: /* @__PURE__ */ w(T, {
229
- onClick: () => D(!0),
230
- children: [/* @__PURE__ */ C(x, { className: "mr-1.5 h-4 w-4" }), " New course"]
232
+ title: R,
233
+ description: _ ? "None of the courses on this page are authored by you — clear \"Mine only\" or page through to find them." : "Create your first course to start building curriculum, assessments and cohorts.",
234
+ action: /* @__PURE__ */ w(f, {
235
+ resource: "buildmyiq-course",
236
+ action: "create",
237
+ deniedBehavior: "hide",
238
+ children: /* @__PURE__ */ T(E, {
239
+ onClick: () => O(!0),
240
+ children: [/* @__PURE__ */ w(S, { className: "mr-1.5 h-4 w-4" }), " New course"]
241
+ })
231
242
  })
232
- }) : /* @__PURE__ */ C("div", {
243
+ }) : /* @__PURE__ */ w("div", {
233
244
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4",
234
- children: F.map((e) => /* @__PURE__ */ C(f, {
245
+ children: I.map((e) => /* @__PURE__ */ w(p, {
235
246
  course: e,
236
247
  showStatus: !0,
237
248
  onClick: () => u(`authoring/${e.id}`)
238
249
  }, e.id))
239
- }), /* @__PURE__ */ C(s, {
240
- page: k.page,
241
- pageSize: j,
242
- itemCount: F.length,
243
- hasMore: N.data?.pageInfo.hasMore ?? !1,
244
- onPageChange: (e) => k.onPageChange(e, N.data?.pageInfo.cursor)
250
+ }), /* @__PURE__ */ w(s, {
251
+ page: A.page,
252
+ pageSize: M,
253
+ itemCount: I.length,
254
+ hasMore: P.data?.pageInfo.hasMore ?? !1,
255
+ onPageChange: (e) => A.onPageChange(e, P.data?.pageInfo.cursor)
245
256
  })] })]
246
- }), v && /* @__PURE__ */ C(M, {
247
- onClose: () => D(!1),
257
+ }), y && /* @__PURE__ */ w(N, {
258
+ onClose: () => O(!1),
248
259
  onCreated: (e) => {
249
- D(!1), u(`authoring/${e.id}`);
260
+ O(!1), u(`authoring/${e.id}`);
250
261
  }
251
262
  })]
252
263
  });
253
264
  };
254
265
  //#endregion
255
- export { N as AuthoringPage };
266
+ export { P as AuthoringPage };
256
267
 
257
268
  //# sourceMappingURL=AuthoringPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthoringPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/authoring/AuthoringPage.tsx"],"sourcesContent":["/**\n * AuthoringPage — the instructor course-management surface.\n * @module buildmyiq/pages/authoring\n *\n * Lists EVERY workspace course (including DRAFT / IN_REVIEW) via `useBuiCourses`,\n * filterable by workflow status and optionally narrowed to the caller's own\n * courses. A \"New course\" modal creates a draft (`useBuiCreateCourse`) and jumps\n * straight into the CourseBuilder. All data is live `bui*` data — no fabrication.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Plus } from 'lucide-react';\nimport { Button, GlassCard, Input, Switch, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n FilterTabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n} from '../../components';\nimport { CourseCard } from '../_shared';\nimport { Modal, Field, SelectField, NO_SKILL, slugify } from './_forms';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useBuiCreateCourse, useBuiSkills, useCursorPager } from '../../hooks';\nimport { COURSE_LEVELS } from '../../types';\nimport type { BuiCourse, BuiCourseLevel, BuiCourseStatus, BuiCreateCourseInput } from '../../types';\nimport { formatEnumLabel } from '../../utils/formatters';\n\ntype StatusFilter = 'ALL' | BuiCourseStatus;\n\nconst STATUS_TABS: { value: StatusFilter; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IN_REVIEW', label: 'In review' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst PAGE_SIZE = 24;\n\n/**\n * Create-course modal. Mounted only while open so its form state resets each\n * time it is opened.\n */\nconst NewCourseModal: FC<{ onClose: () => void; onCreated: (course: BuiCourse) => void }> = ({\n onClose,\n onCreated,\n}) => {\n const { currentUser } = useBuildmyiq();\n const skillsQuery = useBuiSkills();\n const createCourse = useBuiCreateCourse();\n\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugEdited, setSlugEdited] = useState(false);\n const [summary, setSummary] = useState('');\n const [level, setLevel] = useState<BuiCourseLevel>('BEGINNER');\n const [language, setLanguage] = useState('en');\n const [estimatedHours, setEstimatedHours] = useState('');\n const [skillId, setSkillId] = useState<string>(NO_SKILL);\n const [error, setError] = useState<string | null>(null);\n\n const skills = skillsQuery.data ?? [];\n\n const onTitleChange = (value: string) => {\n setTitle(value);\n if (!slugEdited) setSlug(slugify(value));\n };\n\n const canSubmit = title.trim().length > 0 && slug.trim().length > 0 && !createCourse.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateCourseInput = {\n title: title.trim(),\n slug: slug.trim(),\n summary: summary.trim() || undefined,\n level,\n language: language.trim() || 'en',\n estimatedHours: estimatedHours.trim() ? Number(estimatedHours) : undefined,\n skillId: skillId !== NO_SKILL ? skillId : undefined,\n instructorId: currentUser?.id,\n };\n try {\n const course = await createCourse.mutateAsync(input);\n onCreated(course);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to create course.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"New course\"\n description=\"Create a draft course — you can add modules, lessons and assessments next.\"\n className=\"sm:max-w-xl\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createCourse.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createCourse.isPending ? 'Creating…' : 'Create course'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Title\" htmlFor=\"new-course-title\" required>\n <Input\n id=\"new-course-title\"\n value={title}\n onChange={(e) => onTitleChange(e.target.value)}\n placeholder=\"e.g. Foundations of Data Literacy\"\n />\n </Field>\n <Field\n label=\"Slug\"\n htmlFor=\"new-course-slug\"\n required\n hint=\"Used in the course URL. Auto-generated from the title — edit if you like.\"\n >\n <Input\n id=\"new-course-slug\"\n value={slug}\n onChange={(e) => {\n setSlug(e.target.value);\n setSlugEdited(true);\n }}\n placeholder=\"foundations-of-data-literacy\"\n />\n </Field>\n <Field label=\"Summary\" htmlFor=\"new-course-summary\">\n <Textarea\n id=\"new-course-summary\"\n rows={3}\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"A short description shown on the catalog card.\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Level\">\n <SelectField\n value={level}\n onChange={(v) => setLevel(v as BuiCourseLevel)}\n options={COURSE_LEVELS.map((l) => ({ value: l, label: formatEnumLabel(l) }))}\n />\n </Field>\n <Field label=\"Language\" htmlFor=\"new-course-language\">\n <Input\n id=\"new-course-language\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n />\n </Field>\n <Field label=\"Estimated hours\" htmlFor=\"new-course-hours\">\n <Input\n id=\"new-course-hours\"\n type=\"number\"\n min={0}\n value={estimatedHours}\n onChange={(e) => setEstimatedHours(e.target.value)}\n placeholder=\"0\"\n />\n </Field>\n <Field label=\"Skill\">\n <SelectField\n value={skillId}\n onChange={setSkillId}\n options={[\n { value: NO_SKILL, label: skillsQuery.isLoading ? 'Loading…' : 'No skill' },\n ...skills.map((s) => ({ value: s.id, label: s.name })),\n ]}\n />\n </Field>\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nexport const AuthoringPage: FC = () => {\n const { currentUser, navigateTo } = useBuildmyiq();\n const myId = currentUser?.id;\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [mineOnly, setMineOnly] = useState(false);\n const [showCreate, setShowCreate] = useState(false);\n const pager = useCursorPager();\n\n const coursesQuery = useBuiCourses({\n status: status === 'ALL' ? undefined : status,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n\n const items: BuiCourse[] = useMemo(() => coursesQuery.data?.items ?? [], [coursesQuery.data]);\n const visible = useMemo(\n () => (mineOnly && myId ? items.filter((c) => c.instructorId === myId) : items),\n [items, mineOnly, myId]\n );\n\n const handleStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n const emptyTitle = mineOnly\n ? 'No courses you author yet'\n : status === 'ALL'\n ? 'No courses yet'\n : `No ${formatEnumLabel(status).toLowerCase()} courses`;\n\n return (\n <PageLayout\n title=\"Authoring\"\n description=\"Create, curate and publish your workspace courses.\"\n titleGradient\n aurora\n actions={\n <Button onClick={() => setShowCreate(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New course\n </Button>\n }\n >\n <div className=\"flex flex-col gap-5\">\n <GlassCard\n treatment=\"plain\"\n className=\"flex flex-col gap-3 p-3 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"-mx-1 overflow-x-auto px-1\">\n <FilterTabs\n options={STATUS_TABS}\n value={status}\n onChange={handleStatus}\n className=\"border-0 bg-transparent p-0\"\n />\n </div>\n <label className=\"inline-flex shrink-0 cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={mineOnly} onCheckedChange={setMineOnly} disabled={!currentUser?.id} />\n Mine only\n </label>\n </GlassCard>\n\n {coursesQuery.isLoading ? (\n <LoadingView label=\"Loading courses…\" />\n ) : coursesQuery.isError ? (\n <ErrorView\n message=\"We couldn't load your courses.\"\n onRetry={() => coursesQuery.refetch()}\n />\n ) : (\n <>\n {visible.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={emptyTitle}\n description={\n mineOnly\n ? 'None of the courses on this page are authored by you — clear \"Mine only\" or page through to find them.'\n : 'Create your first course to start building curriculum, assessments and cohorts.'\n }\n action={\n <Button onClick={() => setShowCreate(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New course\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4\">\n {visible.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n showStatus\n onClick={() => navigateTo(`authoring/${course.id}`)}\n />\n ))}\n </div>\n )}\n {/* Pager stays visible even when mineOnly trims the page empty, so the\n user can advance to later pages to find their courses. */}\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={visible.length}\n hasMore={coursesQuery.data?.pageInfo.hasMore ?? false}\n onPageChange={(p) => pager.onPageChange(p, coursesQuery.data?.pageInfo.cursor)}\n />\n </>\n )}\n </div>\n\n {showCreate && (\n <NewCourseModal\n onClose={() => setShowCreate(false)}\n onCreated={(course) => {\n setShowCreate(false);\n navigateTo(`authoring/${course.id}`);\n }}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AuthoringPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/authoring/AuthoringPage.tsx"],"sourcesContent":["/**\n * AuthoringPage — the instructor course-management surface.\n * @module buildmyiq/pages/authoring\n *\n * Lists EVERY workspace course (including DRAFT / IN_REVIEW) via `useBuiCourses`,\n * filterable by workflow status and optionally narrowed to the caller's own\n * courses. A \"New course\" modal creates a draft (`useBuiCreateCourse`) and jumps\n * straight into the CourseBuilder. All data is live `bui*` data — no fabrication.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Plus } from 'lucide-react';\nimport { Button, GlassCard, Input, Switch, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n FilterTabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n} from '../../components';\nimport { CourseCard } from '../_shared';\nimport { Modal, Field, SelectField, NO_SKILL, slugify } from './_forms';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useBuiCreateCourse, useBuiSkills, useCursorPager } from '../../hooks';\nimport { COURSE_LEVELS } from '../../types';\nimport type { BuiCourse, BuiCourseLevel, BuiCourseStatus, BuiCreateCourseInput } from '../../types';\nimport { formatEnumLabel } from '../../utils/formatters';\nimport { BuiPermissionButton } from '../../components/BuiPermissionButton';\n\ntype StatusFilter = 'ALL' | BuiCourseStatus;\n\nconst STATUS_TABS: { value: StatusFilter; label: string }[] = [\n { value: 'ALL', label: 'All' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'IN_REVIEW', label: 'In review' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst PAGE_SIZE = 24;\n\n/**\n * Create-course modal. Mounted only while open so its form state resets each\n * time it is opened.\n */\nconst NewCourseModal: FC<{ onClose: () => void; onCreated: (course: BuiCourse) => void }> = ({\n onClose,\n onCreated,\n}) => {\n const { currentUser } = useBuildmyiq();\n const skillsQuery = useBuiSkills();\n const createCourse = useBuiCreateCourse();\n\n const [title, setTitle] = useState('');\n const [slug, setSlug] = useState('');\n const [slugEdited, setSlugEdited] = useState(false);\n const [summary, setSummary] = useState('');\n const [level, setLevel] = useState<BuiCourseLevel>('BEGINNER');\n const [language, setLanguage] = useState('en');\n const [estimatedHours, setEstimatedHours] = useState('');\n const [skillId, setSkillId] = useState<string>(NO_SKILL);\n const [error, setError] = useState<string | null>(null);\n\n const skills = skillsQuery.data ?? [];\n\n const onTitleChange = (value: string) => {\n setTitle(value);\n if (!slugEdited) setSlug(slugify(value));\n };\n\n const canSubmit = title.trim().length > 0 && slug.trim().length > 0 && !createCourse.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateCourseInput = {\n title: title.trim(),\n slug: slug.trim(),\n summary: summary.trim() || undefined,\n level,\n language: language.trim() || 'en',\n estimatedHours: estimatedHours.trim() ? Number(estimatedHours) : undefined,\n skillId: skillId !== NO_SKILL ? skillId : undefined,\n instructorId: currentUser?.id,\n };\n try {\n const course = await createCourse.mutateAsync(input);\n onCreated(course);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to create course.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"New course\"\n description=\"Create a draft course — you can add modules, lessons and assessments next.\"\n className=\"sm:max-w-xl\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createCourse.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createCourse.isPending ? 'Creating…' : 'Create course'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Title\" htmlFor=\"new-course-title\" required>\n <Input\n id=\"new-course-title\"\n value={title}\n onChange={(e) => onTitleChange(e.target.value)}\n placeholder=\"e.g. Foundations of Data Literacy\"\n />\n </Field>\n <Field\n label=\"Slug\"\n htmlFor=\"new-course-slug\"\n required\n hint=\"Used in the course URL. Auto-generated from the title — edit if you like.\"\n >\n <Input\n id=\"new-course-slug\"\n value={slug}\n onChange={(e) => {\n setSlug(e.target.value);\n setSlugEdited(true);\n }}\n placeholder=\"foundations-of-data-literacy\"\n />\n </Field>\n <Field label=\"Summary\" htmlFor=\"new-course-summary\">\n <Textarea\n id=\"new-course-summary\"\n rows={3}\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"A short description shown on the catalog card.\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Level\">\n <SelectField\n value={level}\n onChange={(v) => setLevel(v as BuiCourseLevel)}\n options={COURSE_LEVELS.map((l) => ({ value: l, label: formatEnumLabel(l) }))}\n />\n </Field>\n <Field label=\"Language\" htmlFor=\"new-course-language\">\n <Input\n id=\"new-course-language\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n />\n </Field>\n <Field label=\"Estimated hours\" htmlFor=\"new-course-hours\">\n <Input\n id=\"new-course-hours\"\n type=\"number\"\n min={0}\n value={estimatedHours}\n onChange={(e) => setEstimatedHours(e.target.value)}\n placeholder=\"0\"\n />\n </Field>\n <Field label=\"Skill\">\n <SelectField\n value={skillId}\n onChange={setSkillId}\n options={[\n { value: NO_SKILL, label: skillsQuery.isLoading ? 'Loading…' : 'No skill' },\n ...skills.map((s) => ({ value: s.id, label: s.name })),\n ]}\n />\n </Field>\n </div>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nexport const AuthoringPage: FC = () => {\n const { currentUser, navigateTo } = useBuildmyiq();\n const myId = currentUser?.id;\n const [status, setStatus] = useState<StatusFilter>('ALL');\n const [mineOnly, setMineOnly] = useState(false);\n const [showCreate, setShowCreate] = useState(false);\n const pager = useCursorPager();\n\n const coursesQuery = useBuiCourses({\n status: status === 'ALL' ? undefined : status,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n\n const items: BuiCourse[] = useMemo(() => coursesQuery.data?.items ?? [], [coursesQuery.data]);\n const visible = useMemo(\n () => (mineOnly && myId ? items.filter((c) => c.instructorId === myId) : items),\n [items, mineOnly, myId]\n );\n\n const handleStatus = (next: StatusFilter) => {\n setStatus(next);\n pager.reset();\n };\n\n const emptyTitle = mineOnly\n ? 'No courses you author yet'\n : status === 'ALL'\n ? 'No courses yet'\n : `No ${formatEnumLabel(status).toLowerCase()} courses`;\n\n return (\n <PageLayout\n title=\"Authoring\"\n description=\"Create, curate and publish your workspace courses.\"\n titleGradient\n aurora\n actions={\n <BuiPermissionButton resource=\"buildmyiq-course\" action=\"create\" deniedBehavior=\"hide\">\n <Button onClick={() => setShowCreate(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New course\n </Button>\n </BuiPermissionButton>\n }\n >\n <div className=\"flex flex-col gap-5\">\n <GlassCard\n treatment=\"plain\"\n className=\"flex flex-col gap-3 p-3 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"-mx-1 overflow-x-auto px-1\">\n <FilterTabs\n options={STATUS_TABS}\n value={status}\n onChange={handleStatus}\n className=\"border-0 bg-transparent p-0\"\n />\n </div>\n <label className=\"inline-flex shrink-0 cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={mineOnly} onCheckedChange={setMineOnly} disabled={!currentUser?.id} />\n Mine only\n </label>\n </GlassCard>\n\n {coursesQuery.isLoading ? (\n <LoadingView label=\"Loading courses…\" />\n ) : coursesQuery.isError ? (\n <ErrorView\n message=\"We couldn't load your courses.\"\n onRetry={() => coursesQuery.refetch()}\n />\n ) : (\n <>\n {visible.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={emptyTitle}\n description={\n mineOnly\n ? 'None of the courses on this page are authored by you — clear \"Mine only\" or page through to find them.'\n : 'Create your first course to start building curriculum, assessments and cohorts.'\n }\n action={\n <BuiPermissionButton\n resource=\"buildmyiq-course\"\n action=\"create\"\n deniedBehavior=\"hide\"\n >\n <Button onClick={() => setShowCreate(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New course\n </Button>\n </BuiPermissionButton>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4\">\n {visible.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n showStatus\n onClick={() => navigateTo(`authoring/${course.id}`)}\n />\n ))}\n </div>\n )}\n {/* Pager stays visible even when mineOnly trims the page empty, so the\n user can advance to later pages to find their courses. */}\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={visible.length}\n hasMore={coursesQuery.data?.pageInfo.hasMore ?? false}\n onPageChange={(p) => pager.onPageChange(p, coursesQuery.data?.pageInfo.cursor)}\n />\n </>\n )}\n </div>\n\n {showCreate && (\n <NewCourseModal\n onClose={() => setShowCreate(false)}\n onCreated={(course) => {\n setShowCreate(false);\n navigateTo(`authoring/${course.id}`);\n }}\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CourseBuilderPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/authoring/CourseBuilderPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AA6sB3C,eAAO,MAAM,iBAAiB,EAAE,EA2J/B,CAAC"}
1
+ {"version":3,"file":"CourseBuilderPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/authoring/CourseBuilderPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAkuB3C,eAAO,MAAM,iBAAiB,EAAE,EA+J/B,CAAC"}