@burdenoff/microfe-buildmyiq 2026.705.13 → 2026.706.10

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 (67) hide show
  1. package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
  2. package/dist/buildmyiq/BuildmyiqRoot.js +57 -49
  3. package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
  4. package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -1
  5. package/dist/buildmyiq/BuildmyiqRoutes.js +15 -11
  6. package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
  7. package/dist/buildmyiq/components/Avatar.d.ts +15 -0
  8. package/dist/buildmyiq/components/Avatar.d.ts.map +1 -0
  9. package/dist/buildmyiq/components/Avatar.js +42 -0
  10. package/dist/buildmyiq/components/Avatar.js.map +1 -0
  11. package/dist/buildmyiq/components/Viz.d.ts +25 -0
  12. package/dist/buildmyiq/components/Viz.d.ts.map +1 -0
  13. package/dist/buildmyiq/components/Viz.js +78 -0
  14. package/dist/buildmyiq/components/Viz.js.map +1 -0
  15. package/dist/buildmyiq/components/index.d.ts +2 -0
  16. package/dist/buildmyiq/components/index.d.ts.map +1 -1
  17. package/dist/buildmyiq/components/index.js +2 -0
  18. package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -1
  19. package/dist/buildmyiq/pages/DashboardPage.js +200 -98
  20. package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
  21. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -1
  22. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +262 -241
  23. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
  24. package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -1
  25. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +142 -115
  26. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
  27. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.d.ts.map +1 -1
  28. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js +220 -156
  29. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
  30. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
  31. package/dist/buildmyiq/pages/authoring/AuthoringPage.js +51 -46
  32. package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
  33. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
  34. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +581 -448
  35. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
  36. package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -1
  37. package/dist/buildmyiq/pages/catalog/CatalogPage.js +52 -42
  38. package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
  39. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
  40. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +422 -301
  41. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  42. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
  43. package/dist/buildmyiq/pages/certificates/CertificatesPage.js +237 -151
  44. package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
  45. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
  46. package/dist/buildmyiq/pages/cohorts/CohortsPage.js +219 -138
  47. package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
  48. package/dist/buildmyiq/pages/insights/InsightsPage.d.ts.map +1 -1
  49. package/dist/buildmyiq/pages/insights/InsightsPage.js +276 -152
  50. package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
  51. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
  52. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +237 -174
  53. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
  54. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
  55. package/dist/buildmyiq/pages/learning/MyLearningPage.js +307 -169
  56. package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
  57. package/dist/buildmyiq/pages/paths/PathsPage.d.ts +4 -0
  58. package/dist/buildmyiq/pages/paths/PathsPage.d.ts.map +1 -0
  59. package/dist/buildmyiq/pages/paths/PathsPage.js +187 -0
  60. package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -0
  61. package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -1
  62. package/dist/buildmyiq/pages/skills/SkillsPage.js +233 -140
  63. package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
  64. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
  65. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +379 -275
  66. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
  67. package/package.json +1 -1
@@ -1,230 +1,323 @@
1
1
  import { PageLayout as e } from "../../components/PageLayout.js";
2
- import { MetricsCard as t } from "../../components/MetricsCard.js";
3
- import { formatEnumLabel as n, formatRelativeTime as r } from "../../utils/formatters.js";
4
- import { StatusBadge as i } from "../../components/StatusBadge.js";
5
- import { EmptyState as a } from "../../components/EmptyState.js";
6
- import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
7
- import { FilterTabs as c } from "../../components/FilterTabs.js";
8
- import { useBuiCreateSkill as l, useBuiSkills as u } from "../../hooks/useBuildmyiqApi.js";
2
+ import { formatEnumLabel as t, formatRelativeTime as n } from "../../utils/formatters.js";
3
+ import { EmptyState as r } from "../../components/EmptyState.js";
4
+ import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
5
+ import { FilterTabs as o } from "../../components/FilterTabs.js";
6
+ import { ProgressRing as s } from "../../components/Viz.js";
7
+ import { useBuiCreateSkill as c, useBuiSkills as l } from "../../hooks/useBuildmyiqApi.js";
9
8
  import "../../hooks/index.js";
10
9
  import "../../components/index.js";
11
- import { SKILL_CATEGORIES as d } from "../../types/index.js";
12
- import { useMemo as f, useState as p } from "react";
13
- import { CheckCircle2 as m, CircleSlash as h, Plus as g, Sparkles as _ } from "lucide-react";
14
- import { jsx as v, jsxs as y } from "react/jsx-runtime";
15
- import { Button as b, GlassCard as x, Input as S, Label as C, ResponsiveDialog as w, ResponsiveDialogContent as T, ResponsiveDialogDescription as E, ResponsiveDialogFooter as D, ResponsiveDialogHeader as O, ResponsiveDialogTitle as k, Select as A, SelectContent as j, SelectItem as M, SelectTrigger as N, SelectValue as P, Textarea as F } from "@burdenoff/fe-libs/ui";
10
+ import { SKILL_CATEGORIES as u } from "../../types/index.js";
11
+ import { useMemo as d, useState as f } from "react";
12
+ import { BarChart3 as p, Brain as m, Briefcase as h, Code2 as g, Languages as _, Palette as v, Plus as y, Sparkles as b, Users as x } from "lucide-react";
13
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
14
+ import { Button as w, GlassCard as T, Input as E, Label as D, ResponsiveDialog as O, ResponsiveDialogContent as k, ResponsiveDialogDescription as ee, ResponsiveDialogFooter as A, ResponsiveDialogHeader as j, ResponsiveDialogTitle as M, Select as N, SelectContent as P, SelectItem as F, SelectTrigger as I, SelectValue as L, Textarea as R } from "@burdenoff/fe-libs/ui";
16
15
  //#region src/buildmyiq/pages/skills/SkillsPage.tsx
17
- var I = [{
16
+ var z = [{
18
17
  value: "ALL",
19
18
  label: "All"
20
- }, ...d.map((e) => ({
19
+ }, ...u.map((e) => ({
21
20
  value: e,
22
- label: n(e)
23
- }))], L = {
21
+ label: t(e)
22
+ }))], B = {
23
+ TECHNICAL: {
24
+ icon: g,
25
+ color: "var(--color-accent-teal)"
26
+ },
27
+ BUSINESS: {
28
+ icon: h,
29
+ color: "var(--color-accent-blue)"
30
+ },
31
+ CREATIVE: {
32
+ icon: v,
33
+ color: "var(--color-status-warning-text)"
34
+ },
35
+ COGNITIVE: {
36
+ icon: m,
37
+ color: "var(--color-accent-purple)"
38
+ },
39
+ SOCIAL: {
40
+ icon: x,
41
+ color: "var(--color-status-success-text)"
42
+ },
43
+ LANGUAGE: {
44
+ icon: _,
45
+ color: "var(--color-accent-indigo)"
46
+ }
47
+ }, V = {
24
48
  name: "",
25
49
  category: "TECHNICAL",
26
50
  description: ""
27
- }, R = ({ skill: e }) => /* @__PURE__ */ y(x, {
28
- treatment: "plain",
29
- className: "flex flex-col gap-3 p-5",
30
- children: [
31
- /* @__PURE__ */ y("div", {
32
- className: "flex items-start justify-between gap-3",
33
- children: [/* @__PURE__ */ v("h3", {
34
- className: "min-w-0 break-words font-semibold text-text-primary",
35
- children: e.name
36
- }), /* @__PURE__ */ v(i, {
37
- status: e.category,
38
- className: "shrink-0"
39
- })]
40
- }),
41
- e.description ? /* @__PURE__ */ v("p", {
42
- className: "line-clamp-3 text-sm text-text-secondary",
43
- children: e.description
44
- }) : /* @__PURE__ */ v("p", {
45
- className: "text-sm italic text-text-secondary/70",
46
- children: "No description provided."
47
- }),
48
- /* @__PURE__ */ y("div", {
49
- className: "mt-auto flex items-center justify-between gap-3 pt-2",
50
- children: [/* @__PURE__ */ y("span", {
51
- className: e.isActive ? "inline-flex items-center gap-1.5 text-xs font-medium text-status-success-text" : "inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary",
52
- children: [/* @__PURE__ */ v("span", {
51
+ }, H = ({ skill: e }) => {
52
+ let r = B[e.category], i = r.icon;
53
+ return /* @__PURE__ */ C(T, {
54
+ treatment: "plain",
55
+ className: "flex flex-col gap-3 p-5",
56
+ children: [
57
+ /* @__PURE__ */ C("div", {
58
+ className: "flex items-start gap-3",
59
+ children: [/* @__PURE__ */ S("span", {
53
60
  "aria-hidden": "true",
54
- className: e.isActive ? "size-1.5 rounded-full bg-status-success-text" : "size-1.5 rounded-full bg-text-secondary"
55
- }), e.isActive ? "Active" : "Inactive"]
56
- }), /* @__PURE__ */ y("span", {
57
- className: "text-xs text-text-secondary",
58
- children: ["Added ", r(e.createdAt)]
59
- })]
60
- })
61
- ]
62
- }), z = () => {
63
- let [r, i] = p("ALL"), [x, z] = p(!1), [B, V] = p(L), [H, U] = p(null), { data: W, isLoading: G, isError: K, refetch: q } = u(r === "ALL" ? void 0 : r), J = l(), Y = f(() => W ?? [], [W]), X = f(() => {
64
- let e = Y.length, t = Y.filter((e) => e.isActive).length;
61
+ className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl",
62
+ style: {
63
+ backgroundColor: `color-mix(in srgb, ${r.color} 15%, transparent)`,
64
+ color: r.color
65
+ },
66
+ children: /* @__PURE__ */ S(i, { className: "h-5 w-5" })
67
+ }), /* @__PURE__ */ C("div", {
68
+ className: "flex min-w-0 flex-1 items-start justify-between gap-2",
69
+ children: [/* @__PURE__ */ S("h3", {
70
+ className: "min-w-0 break-words font-semibold leading-snug text-text-primary",
71
+ children: e.name
72
+ }), /* @__PURE__ */ S("span", {
73
+ className: "inline-flex shrink-0 items-center rounded-full border px-2.5 py-0.5 text-xs font-medium",
74
+ style: {
75
+ color: r.color,
76
+ borderColor: `color-mix(in srgb, ${r.color} 40%, transparent)`
77
+ },
78
+ children: t(e.category)
79
+ })]
80
+ })]
81
+ }),
82
+ e.description ? /* @__PURE__ */ S("p", {
83
+ className: "line-clamp-3 text-sm text-text-secondary",
84
+ children: e.description
85
+ }) : /* @__PURE__ */ S("p", {
86
+ className: "text-sm italic text-text-secondary/70",
87
+ children: "No description provided."
88
+ }),
89
+ /* @__PURE__ */ C("div", {
90
+ className: "mt-auto flex items-center justify-between gap-3 border-t border-border-seam/60 pt-3",
91
+ children: [/* @__PURE__ */ C("span", {
92
+ className: e.isActive ? "inline-flex items-center gap-1.5 text-xs font-medium text-status-success-text" : "inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary",
93
+ children: [/* @__PURE__ */ S("span", {
94
+ "aria-hidden": "true",
95
+ className: e.isActive ? "size-1.5 rounded-full bg-status-success-text" : "size-1.5 rounded-full bg-text-secondary"
96
+ }), e.isActive ? "Active" : "Inactive"]
97
+ }), /* @__PURE__ */ C("span", {
98
+ className: "text-xs text-text-tertiary",
99
+ children: ["Added ", n(e.createdAt)]
100
+ })]
101
+ })
102
+ ]
103
+ });
104
+ }, U = () => {
105
+ let [n, m] = f("ALL"), [h, g] = f(!1), [_, v] = f(V), [x, B] = f(null), { data: U, isLoading: W, isError: G, refetch: K } = l(n === "ALL" ? void 0 : n), q = c(), J = d(() => U ?? [], [U]), Y = d(() => {
106
+ let e = J.length, t = J.filter((e) => e.isActive).length, n = e > 0 ? Math.round(t / e * 100) : 0;
65
107
  return {
66
108
  total: e,
67
109
  active: t,
68
- inactive: e - t
110
+ inactive: e - t,
111
+ activeRatio: n
69
112
  };
70
- }, [Y]), Z = r === "ALL" ? "" : ` · ${n(r)}`, Q = () => {
71
- V(L), U(null);
113
+ }, [J]), X = n === "ALL" ? "Skills" : `${t(n)} skills`, Z = !W && !G && Y.total > 0, Q = () => {
114
+ v(V), B(null);
72
115
  }, $ = () => {
73
- z(!1), Q();
116
+ g(!1), Q();
74
117
  };
75
- return /* @__PURE__ */ y(e, {
118
+ return /* @__PURE__ */ C(e, {
76
119
  title: "Skills",
77
120
  description: "The competency taxonomy your courses map to — organised by category.",
78
121
  titleGradient: !0,
79
122
  aurora: !0,
80
- actions: /* @__PURE__ */ y(b, {
81
- onClick: () => z(!0),
82
- children: [/* @__PURE__ */ v(g, { className: "mr-1.5 h-4 w-4" }), " New skill"]
123
+ actions: /* @__PURE__ */ C(w, {
124
+ onClick: () => g(!0),
125
+ children: [/* @__PURE__ */ S(y, { className: "mr-1.5 h-4 w-4" }), " New skill"]
83
126
  }),
84
- children: [/* @__PURE__ */ y("div", {
127
+ children: [/* @__PURE__ */ C("div", {
85
128
  className: "flex flex-col gap-6",
86
129
  children: [
87
- /* @__PURE__ */ y("div", {
88
- className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
89
- children: [
90
- /* @__PURE__ */ v(t, {
91
- icon: /* @__PURE__ */ v(_, { className: "h-5 w-5" }),
92
- value: X.total,
93
- label: `Skills${Z}`,
94
- featured: !0
95
- }),
96
- /* @__PURE__ */ v(t, {
97
- icon: /* @__PURE__ */ v(m, { className: "h-5 w-5" }),
98
- value: X.active,
99
- label: "Active",
100
- accent: "success"
101
- }),
102
- /* @__PURE__ */ v(t, {
103
- icon: /* @__PURE__ */ v(h, { className: "h-5 w-5" }),
104
- value: X.inactive,
105
- label: "Inactive"
106
- })
107
- ]
130
+ Z && /* @__PURE__ */ C(T, {
131
+ treatment: "glass",
132
+ glow: !0,
133
+ className: "flex flex-col gap-4 p-5 sm:flex-row sm:items-center sm:justify-between",
134
+ children: [/* @__PURE__ */ C("div", {
135
+ className: "flex flex-wrap items-center gap-x-6 gap-y-3",
136
+ children: [
137
+ /* @__PURE__ */ C("div", {
138
+ className: "flex items-center gap-3",
139
+ children: [/* @__PURE__ */ S("span", {
140
+ className: "flex h-11 w-11 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]",
141
+ children: /* @__PURE__ */ S(p, { className: "h-5 w-5" })
142
+ }), /* @__PURE__ */ C("span", {
143
+ className: "flex items-baseline gap-1.5",
144
+ children: [/* @__PURE__ */ S("span", {
145
+ className: "text-2xl font-bold tabular-nums text-text-primary",
146
+ children: Y.total
147
+ }), /* @__PURE__ */ S("span", {
148
+ className: "text-sm text-text-secondary",
149
+ children: X
150
+ })]
151
+ })]
152
+ }),
153
+ /* @__PURE__ */ S("span", {
154
+ "aria-hidden": "true",
155
+ className: "hidden h-8 w-px bg-border-seam sm:block"
156
+ }),
157
+ /* @__PURE__ */ C("span", {
158
+ className: "inline-flex items-center gap-2 text-sm",
159
+ children: [
160
+ /* @__PURE__ */ S("span", { className: "size-2 rounded-full bg-status-success-text" }),
161
+ /* @__PURE__ */ S("span", {
162
+ className: "text-lg font-semibold tabular-nums text-text-primary",
163
+ children: Y.active
164
+ }),
165
+ /* @__PURE__ */ S("span", {
166
+ className: "text-text-secondary",
167
+ children: "Active"
168
+ })
169
+ ]
170
+ }),
171
+ /* @__PURE__ */ C("span", {
172
+ className: "inline-flex items-center gap-2 text-sm",
173
+ children: [
174
+ /* @__PURE__ */ S("span", { className: "size-2 rounded-full bg-text-tertiary" }),
175
+ /* @__PURE__ */ S("span", {
176
+ className: "text-lg font-semibold tabular-nums text-text-primary",
177
+ children: Y.inactive
178
+ }),
179
+ /* @__PURE__ */ S("span", {
180
+ className: "text-text-secondary",
181
+ children: "Inactive"
182
+ })
183
+ ]
184
+ })
185
+ ]
186
+ }), /* @__PURE__ */ C("div", {
187
+ className: "flex items-center gap-3 self-start sm:self-auto",
188
+ children: [/* @__PURE__ */ S(s, {
189
+ value: Y.activeRatio,
190
+ size: 52,
191
+ stroke: 5
192
+ }), /* @__PURE__ */ C("span", {
193
+ className: "text-xs leading-tight text-text-secondary",
194
+ children: [
195
+ "of the taxonomy",
196
+ /* @__PURE__ */ S("br", {}),
197
+ "is active"
198
+ ]
199
+ })]
200
+ })]
108
201
  }),
109
- /* @__PURE__ */ v(c, {
110
- options: I,
111
- value: r,
112
- onChange: i
202
+ /* @__PURE__ */ S(o, {
203
+ options: z,
204
+ value: n,
205
+ onChange: m
113
206
  }),
114
- G ? /* @__PURE__ */ v(s, { label: "Loading skills…" }) : K ? /* @__PURE__ */ v(o, {
207
+ W ? /* @__PURE__ */ S(a, { label: "Loading skills…" }) : G ? /* @__PURE__ */ S(i, {
115
208
  title: "Couldn't load skills",
116
209
  message: "The skills taxonomy failed to load.",
117
- onRetry: () => q()
118
- }) : Y.length === 0 ? /* @__PURE__ */ v(a, {
210
+ onRetry: () => K()
211
+ }) : J.length === 0 ? /* @__PURE__ */ S(r, {
119
212
  illustration: "empty-data",
120
- icon: /* @__PURE__ */ v(_, { className: "h-10 w-10" }),
121
- title: r === "ALL" ? "No skills yet" : "No skills in this category",
122
- description: r === "ALL" ? "Define the competencies your courses build. Skills let you organise the catalog and target learning outcomes." : "No skills are tagged with this category yet. Add one, or switch categories.",
123
- action: /* @__PURE__ */ y(b, {
124
- onClick: () => z(!0),
125
- children: [/* @__PURE__ */ v(g, { className: "mr-1.5 h-4 w-4" }), " New skill"]
213
+ icon: /* @__PURE__ */ S(b, { className: "h-10 w-10" }),
214
+ title: n === "ALL" ? "No skills yet" : "No skills in this category",
215
+ description: n === "ALL" ? "Define the competencies your courses build. Skills let you organise the catalog and target learning outcomes." : "No skills are tagged with this category yet. Add one, or switch categories.",
216
+ action: /* @__PURE__ */ C(w, {
217
+ onClick: () => g(!0),
218
+ children: [/* @__PURE__ */ S(y, { className: "mr-1.5 h-4 w-4" }), " New skill"]
126
219
  })
127
- }) : /* @__PURE__ */ v("div", {
128
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
129
- children: Y.map((e) => /* @__PURE__ */ v(R, { skill: e }, e.id))
220
+ }) : /* @__PURE__ */ S("div", {
221
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
222
+ children: J.map((e) => /* @__PURE__ */ S(H, { skill: e }, e.id))
130
223
  })
131
224
  ]
132
- }), /* @__PURE__ */ v(w, {
133
- open: x,
225
+ }), /* @__PURE__ */ S(O, {
226
+ open: h,
134
227
  onOpenChange: (e) => {
135
- e ? z(!0) : $();
228
+ e ? g(!0) : $();
136
229
  },
137
- children: /* @__PURE__ */ y(T, { children: [
138
- /* @__PURE__ */ y(O, { children: [/* @__PURE__ */ v(k, { children: "New skill" }), /* @__PURE__ */ v(E, { children: "Add a competency to the taxonomy. Courses can then be mapped to it." })] }),
139
- /* @__PURE__ */ y("div", {
230
+ children: /* @__PURE__ */ C(k, { children: [
231
+ /* @__PURE__ */ C(j, { children: [/* @__PURE__ */ S(M, { children: "New skill" }), /* @__PURE__ */ S(ee, { children: "Add a competency to the taxonomy. Courses can then be mapped to it." })] }),
232
+ /* @__PURE__ */ C("div", {
140
233
  className: "grid gap-4 py-2",
141
234
  children: [
142
- /* @__PURE__ */ y("div", {
235
+ /* @__PURE__ */ C("div", {
143
236
  className: "grid gap-1.5",
144
- children: [/* @__PURE__ */ v(C, {
237
+ children: [/* @__PURE__ */ S(D, {
145
238
  htmlFor: "skill-name",
146
239
  children: "Name *"
147
- }), /* @__PURE__ */ v(S, {
240
+ }), /* @__PURE__ */ S(E, {
148
241
  id: "skill-name",
149
- value: B.name,
150
- onChange: (e) => V({
151
- ...B,
242
+ value: _.name,
243
+ onChange: (e) => v({
244
+ ..._,
152
245
  name: e.target.value
153
246
  }),
154
247
  placeholder: "e.g. Data Visualisation"
155
248
  })]
156
249
  }),
157
- /* @__PURE__ */ y("div", {
250
+ /* @__PURE__ */ C("div", {
158
251
  className: "grid gap-1.5",
159
- children: [/* @__PURE__ */ v(C, {
252
+ children: [/* @__PURE__ */ S(D, {
160
253
  htmlFor: "skill-category",
161
254
  children: "Category"
162
- }), /* @__PURE__ */ y(A, {
163
- value: B.category,
164
- onValueChange: (e) => V({
165
- ...B,
255
+ }), /* @__PURE__ */ C(N, {
256
+ value: _.category,
257
+ onValueChange: (e) => v({
258
+ ..._,
166
259
  category: e
167
260
  }),
168
- children: [/* @__PURE__ */ v(N, {
261
+ children: [/* @__PURE__ */ S(I, {
169
262
  id: "skill-category",
170
- children: /* @__PURE__ */ v(P, {})
171
- }), /* @__PURE__ */ v(j, { children: d.map((e) => /* @__PURE__ */ v(M, {
263
+ children: /* @__PURE__ */ S(L, {})
264
+ }), /* @__PURE__ */ S(P, { children: u.map((e) => /* @__PURE__ */ S(F, {
172
265
  value: e,
173
- children: n(e)
266
+ children: t(e)
174
267
  }, e)) })]
175
268
  })]
176
269
  }),
177
- /* @__PURE__ */ y("div", {
270
+ /* @__PURE__ */ C("div", {
178
271
  className: "grid gap-1.5",
179
- children: [/* @__PURE__ */ v(C, {
272
+ children: [/* @__PURE__ */ S(D, {
180
273
  htmlFor: "skill-description",
181
274
  children: "Description"
182
- }), /* @__PURE__ */ v(F, {
275
+ }), /* @__PURE__ */ S(R, {
183
276
  id: "skill-description",
184
- value: B.description,
185
- onChange: (e) => V({
186
- ...B,
277
+ value: _.description,
278
+ onChange: (e) => v({
279
+ ..._,
187
280
  description: e.target.value
188
281
  }),
189
282
  placeholder: "Optional — what this competency covers and how it's assessed.",
190
283
  rows: 3
191
284
  })]
192
285
  }),
193
- H && /* @__PURE__ */ v("p", {
286
+ x && /* @__PURE__ */ S("p", {
194
287
  className: "text-sm text-status-error-text",
195
- children: H
288
+ children: x
196
289
  })
197
290
  ]
198
291
  }),
199
- /* @__PURE__ */ y(D, { children: [/* @__PURE__ */ v(b, {
292
+ /* @__PURE__ */ C(A, { children: [/* @__PURE__ */ S(w, {
200
293
  variant: "outline",
201
294
  onClick: $,
202
295
  children: "Cancel"
203
- }), /* @__PURE__ */ v(b, {
296
+ }), /* @__PURE__ */ S(w, {
204
297
  onClick: async () => {
205
- let e = B.name.trim();
298
+ let e = _.name.trim();
206
299
  if (!e) {
207
- U("A skill name is required.");
300
+ B("A skill name is required.");
208
301
  return;
209
302
  }
210
303
  try {
211
- await J.mutateAsync({
304
+ await q.mutateAsync({
212
305
  name: e,
213
- category: B.category,
214
- description: B.description.trim() || void 0
306
+ category: _.category,
307
+ description: _.description.trim() || void 0
215
308
  }), $();
216
309
  } catch (e) {
217
- U(e instanceof Error ? e.message : "Failed to create skill.");
310
+ B(e instanceof Error ? e.message : "Failed to create skill.");
218
311
  }
219
312
  },
220
- disabled: J.isPending,
221
- children: J.isPending ? "Creating…" : "Create skill"
313
+ disabled: q.isPending,
314
+ children: q.isPending ? "Creating…" : "Create skill"
222
315
  })] })
223
316
  ] })
224
317
  })]
225
318
  });
226
319
  };
227
320
  //#endregion
228
- export { z as SkillsPage };
321
+ export { U as SkillsPage };
229
322
 
230
323
  //# sourceMappingURL=SkillsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SkillsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/skills/SkillsPage.tsx"],"sourcesContent":["/**\n * SkillsPage — the workspace skills taxonomy.\n * @module buildmyiq/pages/skills\n *\n * The competency vocabulary that courses map to (`BuiCourse.skillId → BuiSkill`).\n * Reads the taxonomy via `useBuiSkills(category?)` — the category filter is applied\n * server-side — and writes new competencies via `useBuiCreateSkill`. A small KPI\n * summary (total / active / inactive) is derived from the loaded list, so it is\n * honest for whichever category is currently in view. Every tile renders REAL\n * `bui*` data; the empty state is honest.\n */\n\nimport { useMemo, useState } from 'react';\nimport type { FC } from 'react';\nimport { Plus, Sparkles, CheckCircle2, CircleSlash } from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\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 EmptyState,\n LoadingView,\n ErrorView,\n type FilterOption,\n} from '../../components';\nimport { useBuiSkills, useBuiCreateSkill } from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport { SKILL_CATEGORIES } from '../../types';\nimport type { BuiSkill, BuiSkillCategory } from '../../types';\n\ntype CategoryFilter = BuiSkillCategory | 'ALL';\n\nconst CATEGORY_FILTERS: FilterOption<CategoryFilter>[] = [\n { value: 'ALL', label: 'All' },\n ...SKILL_CATEGORIES.map((c) => ({ value: c, label: formatEnumLabel(c) })),\n];\n\ninterface CreateFormState {\n name: string;\n category: BuiSkillCategory;\n description: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n name: '',\n category: 'TECHNICAL',\n description: '',\n};\n\n/** A single competency tile — name, category badge, description, active state. */\nconst SkillTile: FC<{ skill: BuiSkill }> = ({ skill }) => (\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 p-5\">\n <div className=\"flex items-start justify-between gap-3\">\n <h3 className=\"min-w-0 break-words font-semibold text-text-primary\">{skill.name}</h3>\n <StatusBadge status={skill.category} className=\"shrink-0\" />\n </div>\n\n {skill.description ? (\n <p className=\"line-clamp-3 text-sm text-text-secondary\">{skill.description}</p>\n ) : (\n <p className=\"text-sm italic text-text-secondary/70\">No description provided.</p>\n )}\n\n <div className=\"mt-auto flex items-center justify-between gap-3 pt-2\">\n <span\n className={\n skill.isActive\n ? 'inline-flex items-center gap-1.5 text-xs font-medium text-status-success-text'\n : 'inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary'\n }\n >\n <span\n aria-hidden=\"true\"\n className={\n skill.isActive\n ? 'size-1.5 rounded-full bg-status-success-text'\n : 'size-1.5 rounded-full bg-text-secondary'\n }\n />\n {skill.isActive ? 'Active' : 'Inactive'}\n </span>\n <span className=\"text-xs text-text-secondary\">\n Added {formatRelativeTime(skill.createdAt)}\n </span>\n </div>\n </GlassCard>\n);\n\nexport const SkillsPage: FC = () => {\n const [category, setCategory] = useState<CategoryFilter>('ALL');\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useBuiSkills(\n category === 'ALL' ? undefined : category\n );\n const createSkill = useBuiCreateSkill();\n\n const skills = useMemo<BuiSkill[]>(() => data ?? [], [data]);\n\n // KPIs are derived from the loaded (optionally category-filtered) list.\n const summary = useMemo(() => {\n const total = skills.length;\n const active = skills.filter((s) => s.isActive).length;\n return { total, active, inactive: total - active };\n }, [skills]);\n\n const scopeLabel = category === 'ALL' ? '' : ` · ${formatEnumLabel(category)}`;\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setFormError(null);\n };\n\n const closeDialog = () => {\n setDialogOpen(false);\n resetForm();\n };\n\n const handleCreate = async () => {\n const name = form.name.trim();\n if (!name) {\n setFormError('A skill name is required.');\n return;\n }\n try {\n await createSkill.mutateAsync({\n name,\n category: form.category,\n description: form.description.trim() || undefined,\n });\n closeDialog();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create skill.');\n }\n };\n\n return (\n <PageLayout\n title=\"Skills\"\n description=\"The competency taxonomy your courses map to — organised by category.\"\n titleGradient\n aurora\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New skill\n </Button>\n }\n >\n <div className=\"flex flex-col gap-6\">\n {/* KPI row — scoped to the loaded list. */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <MetricsCard\n icon={<Sparkles className=\"h-5 w-5\" />}\n value={summary.total}\n label={`Skills${scopeLabel}`}\n featured\n />\n <MetricsCard\n icon={<CheckCircle2 className=\"h-5 w-5\" />}\n value={summary.active}\n label=\"Active\"\n accent=\"success\"\n />\n <MetricsCard\n icon={<CircleSlash className=\"h-5 w-5\" />}\n value={summary.inactive}\n label=\"Inactive\"\n />\n </div>\n\n <FilterTabs options={CATEGORY_FILTERS} value={category} onChange={setCategory} />\n\n {isLoading ? (\n <LoadingView label=\"Loading skills…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load skills\"\n message=\"The skills taxonomy failed to load.\"\n onRetry={() => refetch()}\n />\n ) : skills.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Sparkles className=\"h-10 w-10\" />}\n title={category === 'ALL' ? 'No skills yet' : 'No skills in this category'}\n description={\n category === 'ALL'\n ? 'Define the competencies your courses build. Skills let you organise the catalog and target learning outcomes.'\n : 'No skills are tagged with this category yet. Add one, or switch categories.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New skill\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {skills.map((skill) => (\n <SkillTile key={skill.id} skill={skill} />\n ))}\n </div>\n )}\n </div>\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (open) setDialogOpen(true);\n else closeDialog();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New skill</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a competency to the taxonomy. Courses can then be mapped to it.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-name\">Name *</Label>\n <Input\n id=\"skill-name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n placeholder=\"e.g. Data Visualisation\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-category\">Category</Label>\n <Select\n value={form.category}\n onValueChange={(v) => setForm({ ...form, category: v as BuiSkillCategory })}\n >\n <SelectTrigger id=\"skill-category\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SKILL_CATEGORIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-description\">Description</Label>\n <Textarea\n id=\"skill-description\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Optional — what this competency covers and how it's assessed.\"\n rows={3}\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={createSkill.isPending}>\n {createSkill.isPending ? 'Creating…' : 'Create skill'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAkDA,IAAM,IAAmD,CACvD;CAAE,OAAO;CAAO,OAAO;CAAO,EAC9B,GAAG,EAAiB,KAAK,OAAO;CAAE,OAAO;CAAG,OAAO,EAAgB,EAAE;CAAE,EAAE,CAC1E,EAQK,IAA8B;CAClC,MAAM;CACN,UAAU;CACV,aAAa;CACd,EAGK,KAAsC,EAAE,eAC5C,kBAAC,GAAD;CAAW,WAAU;CAAQ,WAAU;WAAvC;EACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAuD,EAAM;IAAU,CAAA,EACrF,kBAAC,GAAD;IAAa,QAAQ,EAAM;IAAU,WAAU;IAAa,CAAA,CACxD;;EAEL,EAAM,cACL,kBAAC,KAAD;GAAG,WAAU;aAA4C,EAAM;GAAgB,CAAA,GAE/E,kBAAC,KAAD;GAAG,WAAU;aAAwC;GAA4B,CAAA;EAGnF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IACE,WACE,EAAM,WACF,kFACA;cAJR,CAOE,kBAAC,QAAD;KACE,eAAY;KACZ,WACE,EAAM,WACF,iDACA;KAEN,CAAA,EACD,EAAM,WAAW,WAAW,WACxB;OACP,kBAAC,QAAD;IAAM,WAAU;cAAhB,CAA8C,UACrC,EAAmB,EAAM,UAAU,CACrC;MACH;;EACI;IAGD,UAAuB;CAClC,IAAM,CAAC,GAAU,KAAe,EAAyB,MAAM,EACzD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAM,KAAW,EAA0B,EAAW,EACvD,CAAC,GAAW,KAAgB,EAAwB,KAAK,EAEzD,EAAE,SAAM,cAAW,YAAS,eAAY,EAC5C,MAAa,QAAQ,KAAA,IAAY,EAClC,EACK,IAAc,GAAmB,EAEjC,IAAS,QAA0B,KAAQ,EAAE,EAAE,CAAC,EAAK,CAAC,EAGtD,IAAU,QAAc;EAC5B,IAAM,IAAQ,EAAO,QACf,IAAS,EAAO,QAAQ,MAAM,EAAE,SAAS,CAAC;AAChD,SAAO;GAAE;GAAO;GAAQ,UAAU,IAAQ;GAAQ;IACjD,CAAC,EAAO,CAAC,EAEN,IAAa,MAAa,QAAQ,KAAK,MAAM,EAAgB,EAAS,IAEtE,UAAkB;AAEtB,EADA,EAAQ,EAAW,EACnB,EAAa,KAAK;IAGd,UAAoB;AAExB,EADA,EAAc,GAAM,EACpB,GAAW;;AAqBb,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,eAAA;EACA,QAAA;EACA,SACE,kBAAC,GAAD;GAAQ,eAAe,EAAc,GAAK;aAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,aAC5B;;YARb,CAWE,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAO,EAAQ;OACf,OAAO,SAAS;OAChB,UAAA;OACA,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;OAC1C,OAAO,EAAQ;OACf,OAAM;OACN,QAAO;OACP,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OACzC,OAAO,EAAQ;OACf,OAAM;OACN,CAAA;MACE;;IAEN,kBAAC,GAAD;KAAY,SAAS;KAAkB,OAAO;KAAU,UAAU;KAAe,CAAA;IAEhF,IACC,kBAAC,GAAD,EAAa,OAAM,mBAAoB,CAAA,GACrC,IACF,kBAAC,GAAD;KACE,OAAM;KACN,SAAQ;KACR,eAAe,GAAS;KACxB,CAAA,GACA,EAAO,WAAW,IACpB,kBAAC,GAAD;KACE,cAAa;KACb,MAAM,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA;KACxC,OAAO,MAAa,QAAQ,kBAAkB;KAC9C,aACE,MAAa,QACT,kHACA;KAEN,QACE,kBAAC,GAAD;MAAQ,eAAe,EAAc,GAAK;gBAA1C,CACE,kBAAC,GAAD,EAAM,WAAU,kBAAmB,CAAA,EAAA,aAC5B;;KAEX,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAAiC,UAAS,EAA1B,EAAM,GAAoB,CAC1C;KACE,CAAA;IAEJ;MAEN,kBAAC,GAAD;GACE,MAAM;GACN,eAAe,MAAS;AACtB,IAAI,IAAM,EAAc,GAAK,GACxB,GAAa;;aAGpB,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAuB,aAAiC,CAAA,EACxD,kBAAC,GAAD,EAAA,UAA6B,uEAEC,CAAA,CACP,EAAA,CAAA;IAEzB,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAa;QAAc,CAAA,EAC1C,kBAAC,GAAD;QACE,IAAG;QACH,OAAO,EAAK;QACZ,WAAW,MAAM,EAAQ;SAAE,GAAG;SAAM,MAAM,EAAE,OAAO;SAAO,CAAC;QAC3D,aAAY;QACZ,CAAA,CACE;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAiB;QAAgB,CAAA,EAChD,kBAAC,GAAD;QACE,OAAO,EAAK;QACZ,gBAAgB,MAAM,EAAQ;SAAE,GAAG;SAAM,UAAU;SAAuB,CAAC;kBAF7E,CAIE,kBAAC,GAAD;SAAe,IAAG;mBAChB,kBAAC,GAAD,EAAe,CAAA;SACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAiB,KAAK,MACrB,kBAAC,GAAD;SAAoB,OAAO;mBACxB,EAAgB,EAAE;SACR,EAFI,EAEJ,CACb,EACY,CAAA,CACT;UACL;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;kBAAoB;QAAmB,CAAA,EACtD,kBAAC,GAAD;QACE,IAAG;QACH,OAAO,EAAK;QACZ,WAAW,MAAM,EAAQ;SAAE,GAAG;SAAM,aAAa,EAAE,OAAO;SAAO,CAAC;QAClE,aAAY;QACZ,MAAM;QACN,CAAA,CACE;;MAEL,KAAa,kBAAC,KAAD;OAAG,WAAU;iBAAkC;OAAc,CAAA;MACvE;;IAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;eAAa;KAEvC,CAAA,EACT,kBAAC,GAAD;KAAQ,SAtJG,YAAY;MAC/B,IAAM,IAAO,EAAK,KAAK,MAAM;AAC7B,UAAI,CAAC,GAAM;AACT,SAAa,4BAA4B;AACzC;;AAEF,UAAI;AAMF,OALA,MAAM,EAAY,YAAY;QAC5B;QACA,UAAU,EAAK;QACf,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;QACzC,CAAC,EACF,GAAa;eACN,GAAK;AACZ,SAAa,aAAe,QAAQ,EAAI,UAAU,0BAA0B;;;KAwIvC,UAAU,EAAY;eAClD,EAAY,YAAY,cAAc;KAChC,CAAA,CACc,EAAA,CAAA;IACD,EAAA,CAAA;GACT,CAAA,CACR"}
1
+ {"version":3,"file":"SkillsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/skills/SkillsPage.tsx"],"sourcesContent":["/**\n * SkillsPage — the workspace skills taxonomy.\n * @module buildmyiq/pages/skills\n *\n * The competency vocabulary that courses map to (`BuiCourse.skillId → BuiSkill`).\n * Reads the taxonomy via `useBuiSkills(category?)` — the category filter is applied\n * server-side — and writes new competencies via `useBuiCreateSkill`. A small KPI\n * summary (total / active / inactive) is derived from the loaded list, so it is\n * honest for whichever category is currently in view. Every tile renders REAL\n * `bui*` data; the empty state is honest.\n */\n\nimport { useMemo, useState } from 'react';\nimport type { FC } from 'react';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n Plus,\n Sparkles,\n BarChart3,\n Code2,\n Briefcase,\n Palette,\n Brain,\n Users,\n Languages,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n Input,\n Label,\n Textarea,\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 ProgressRing,\n FilterTabs,\n EmptyState,\n LoadingView,\n ErrorView,\n type FilterOption,\n} from '../../components';\nimport { useBuiSkills, useBuiCreateSkill } from '../../hooks';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport { SKILL_CATEGORIES } from '../../types';\nimport type { BuiSkill, BuiSkillCategory } from '../../types';\n\ntype CategoryFilter = BuiSkillCategory | 'ALL';\n\nconst CATEGORY_FILTERS: FilterOption<CategoryFilter>[] = [\n { value: 'ALL', label: 'All' },\n ...SKILL_CATEGORIES.map((c) => ({ value: c, label: formatEnumLabel(c) })),\n];\n\n/**\n * Per-category icon + semantic-token colour. Distinct hues drawn ONLY from the\n * Boff UI 2.0 accent/status palette (theme-safe in light + dark, no raw colours).\n */\nconst CATEGORY_VISUAL: Record<BuiSkillCategory, { icon: LucideIcon; color: string }> = {\n TECHNICAL: { icon: Code2, color: 'var(--color-accent-teal)' },\n BUSINESS: { icon: Briefcase, color: 'var(--color-accent-blue)' },\n CREATIVE: { icon: Palette, color: 'var(--color-status-warning-text)' },\n COGNITIVE: { icon: Brain, color: 'var(--color-accent-purple)' },\n SOCIAL: { icon: Users, color: 'var(--color-status-success-text)' },\n LANGUAGE: { icon: Languages, color: 'var(--color-accent-indigo)' },\n};\n\ninterface CreateFormState {\n name: string;\n category: BuiSkillCategory;\n description: string;\n}\n\nconst EMPTY_FORM: CreateFormState = {\n name: '',\n category: 'TECHNICAL',\n description: '',\n};\n\n/** A single competency tile — category-coloured icon, badge, description, state. */\nconst SkillTile: FC<{ skill: BuiSkill }> = ({ skill }) => {\n const visual = CATEGORY_VISUAL[skill.category];\n const Icon = visual.icon;\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 p-5\">\n <div className=\"flex items-start gap-3\">\n <span\n aria-hidden=\"true\"\n className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl\"\n style={{\n backgroundColor: `color-mix(in srgb, ${visual.color} 15%, transparent)`,\n color: visual.color,\n }}\n >\n <Icon className=\"h-5 w-5\" />\n </span>\n <div className=\"flex min-w-0 flex-1 items-start justify-between gap-2\">\n <h3 className=\"min-w-0 break-words font-semibold leading-snug text-text-primary\">\n {skill.name}\n </h3>\n <span\n className=\"inline-flex shrink-0 items-center rounded-full border px-2.5 py-0.5 text-xs font-medium\"\n style={{\n color: visual.color,\n borderColor: `color-mix(in srgb, ${visual.color} 40%, transparent)`,\n }}\n >\n {formatEnumLabel(skill.category)}\n </span>\n </div>\n </div>\n\n {skill.description ? (\n <p className=\"line-clamp-3 text-sm text-text-secondary\">{skill.description}</p>\n ) : (\n <p className=\"text-sm italic text-text-secondary/70\">No description provided.</p>\n )}\n\n <div className=\"mt-auto flex items-center justify-between gap-3 border-t border-border-seam/60 pt-3\">\n <span\n className={\n skill.isActive\n ? 'inline-flex items-center gap-1.5 text-xs font-medium text-status-success-text'\n : 'inline-flex items-center gap-1.5 text-xs font-medium text-text-secondary'\n }\n >\n <span\n aria-hidden=\"true\"\n className={\n skill.isActive\n ? 'size-1.5 rounded-full bg-status-success-text'\n : 'size-1.5 rounded-full bg-text-secondary'\n }\n />\n {skill.isActive ? 'Active' : 'Inactive'}\n </span>\n <span className=\"text-xs text-text-tertiary\">Added {formatRelativeTime(skill.createdAt)}</span>\n </div>\n </GlassCard>\n );\n};\n\nexport const SkillsPage: FC = () => {\n const [category, setCategory] = useState<CategoryFilter>('ALL');\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<CreateFormState>(EMPTY_FORM);\n const [formError, setFormError] = useState<string | null>(null);\n\n const { data, isLoading, isError, refetch } = useBuiSkills(\n category === 'ALL' ? undefined : category\n );\n const createSkill = useBuiCreateSkill();\n\n const skills = useMemo<BuiSkill[]>(() => data ?? [], [data]);\n\n // KPIs are derived from the loaded (optionally category-filtered) list.\n const summary = useMemo(() => {\n const total = skills.length;\n const active = skills.filter((s) => s.isActive).length;\n const activeRatio = total > 0 ? Math.round((active / total) * 100) : 0;\n return { total, active, inactive: total - active, activeRatio };\n }, [skills]);\n\n const scopeLabel = category === 'ALL' ? 'Skills' : `${formatEnumLabel(category)} skills`;\n const showSummary = !isLoading && !isError && summary.total > 0;\n\n const resetForm = () => {\n setForm(EMPTY_FORM);\n setFormError(null);\n };\n\n const closeDialog = () => {\n setDialogOpen(false);\n resetForm();\n };\n\n const handleCreate = async () => {\n const name = form.name.trim();\n if (!name) {\n setFormError('A skill name is required.');\n return;\n }\n try {\n await createSkill.mutateAsync({\n name,\n category: form.category,\n description: form.description.trim() || undefined,\n });\n closeDialog();\n } catch (err) {\n setFormError(err instanceof Error ? err.message : 'Failed to create skill.');\n }\n };\n\n return (\n <PageLayout\n title=\"Skills\"\n description=\"The competency taxonomy your courses map to — organised by category.\"\n titleGradient\n aurora\n actions={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New skill\n </Button>\n }\n >\n <div className=\"flex flex-col gap-6\">\n {/* Summary strip — scoped to the loaded list. */}\n {showSummary && (\n <GlassCard\n treatment=\"glass\"\n glow\n className=\"flex flex-col gap-4 p-5 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex flex-wrap items-center gap-x-6 gap-y-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-11 w-11 items-center justify-center rounded-xl bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]\">\n <BarChart3 className=\"h-5 w-5\" />\n </span>\n <span className=\"flex items-baseline gap-1.5\">\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {summary.total}\n </span>\n <span className=\"text-sm text-text-secondary\">{scopeLabel}</span>\n </span>\n </div>\n <span aria-hidden=\"true\" className=\"hidden h-8 w-px bg-border-seam sm:block\" />\n <span className=\"inline-flex items-center gap-2 text-sm\">\n <span className=\"size-2 rounded-full bg-status-success-text\" />\n <span className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {summary.active}\n </span>\n <span className=\"text-text-secondary\">Active</span>\n </span>\n <span className=\"inline-flex items-center gap-2 text-sm\">\n <span className=\"size-2 rounded-full bg-text-tertiary\" />\n <span className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {summary.inactive}\n </span>\n <span className=\"text-text-secondary\">Inactive</span>\n </span>\n </div>\n <div className=\"flex items-center gap-3 self-start sm:self-auto\">\n <ProgressRing value={summary.activeRatio} size={52} stroke={5} />\n <span className=\"text-xs leading-tight text-text-secondary\">\n of the taxonomy\n <br />\n is active\n </span>\n </div>\n </GlassCard>\n )}\n\n <FilterTabs options={CATEGORY_FILTERS} value={category} onChange={setCategory} />\n\n {isLoading ? (\n <LoadingView label=\"Loading skills…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load skills\"\n message=\"The skills taxonomy failed to load.\"\n onRetry={() => refetch()}\n />\n ) : skills.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Sparkles className=\"h-10 w-10\" />}\n title={category === 'ALL' ? 'No skills yet' : 'No skills in this category'}\n description={\n category === 'ALL'\n ? 'Define the competencies your courses build. Skills let you organise the catalog and target learning outcomes.'\n : 'No skills are tagged with this category yet. Add one, or switch categories.'\n }\n action={\n <Button onClick={() => setDialogOpen(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> New skill\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {skills.map((skill) => (\n <SkillTile key={skill.id} skill={skill} />\n ))}\n </div>\n )}\n </div>\n\n <ResponsiveDialog\n open={dialogOpen}\n onOpenChange={(open) => {\n if (open) setDialogOpen(true);\n else closeDialog();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New skill</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Add a competency to the taxonomy. Courses can then be mapped to it.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"grid gap-4 py-2\">\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-name\">Name *</Label>\n <Input\n id=\"skill-name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n placeholder=\"e.g. Data Visualisation\"\n />\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-category\">Category</Label>\n <Select\n value={form.category}\n onValueChange={(v) => setForm({ ...form, category: v as BuiSkillCategory })}\n >\n <SelectTrigger id=\"skill-category\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SKILL_CATEGORIES.map((c) => (\n <SelectItem key={c} value={c}>\n {formatEnumLabel(c)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"grid gap-1.5\">\n <Label htmlFor=\"skill-description\">Description</Label>\n <Textarea\n id=\"skill-description\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Optional — what this competency covers and how it's assessed.\"\n rows={3}\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={createSkill.isPending}>\n {createSkill.isPending ? 'Creating…' : 'Create skill'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SubmissionsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/submissions/SubmissionsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAmG3C,eAAO,MAAM,eAAe,EAAE,EAyX7B,CAAC"}
1
+ {"version":3,"file":"SubmissionsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/submissions/SubmissionsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AA4H3C,eAAO,MAAM,eAAe,EAAE,EAgc7B,CAAC"}