@contractspec/example.learning-journey-ui-gamified 1.56.1 → 1.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +59 -58
  2. package/.turbo/turbo-prebuild.log +1 -0
  3. package/CHANGELOG.md +40 -0
  4. package/dist/GamifiedMiniApp.d.ts +4 -14
  5. package/dist/GamifiedMiniApp.d.ts.map +1 -1
  6. package/dist/GamifiedMiniApp.js +995 -59
  7. package/dist/browser/GamifiedMiniApp.js +998 -0
  8. package/dist/browser/components/DayCalendar.js +42 -0
  9. package/dist/browser/components/FlashCard.js +102 -0
  10. package/dist/browser/components/MasteryRing.js +75 -0
  11. package/dist/browser/components/index.js +217 -0
  12. package/dist/browser/docs/index.js +22 -0
  13. package/dist/browser/docs/learning-journey-ui-gamified.docblock.js +22 -0
  14. package/dist/browser/example.js +32 -0
  15. package/dist/browser/index.js +1057 -0
  16. package/dist/browser/views/Overview.js +236 -0
  17. package/dist/browser/views/Progress.js +294 -0
  18. package/dist/browser/views/Steps.js +157 -0
  19. package/dist/browser/views/Timeline.js +235 -0
  20. package/dist/browser/views/index.js +919 -0
  21. package/dist/components/DayCalendar.d.ts +5 -13
  22. package/dist/components/DayCalendar.d.ts.map +1 -1
  23. package/dist/components/DayCalendar.js +41 -31
  24. package/dist/components/FlashCard.d.ts +7 -16
  25. package/dist/components/FlashCard.d.ts.map +1 -1
  26. package/dist/components/FlashCard.js +99 -76
  27. package/dist/components/MasteryRing.d.ts +6 -15
  28. package/dist/components/MasteryRing.d.ts.map +1 -1
  29. package/dist/components/MasteryRing.js +72 -78
  30. package/dist/components/index.d.ts +4 -4
  31. package/dist/components/index.d.ts.map +1 -0
  32. package/dist/components/index.js +217 -4
  33. package/dist/docs/index.d.ts +2 -1
  34. package/dist/docs/index.d.ts.map +1 -0
  35. package/dist/docs/index.js +23 -1
  36. package/dist/docs/learning-journey-ui-gamified.docblock.d.ts +2 -1
  37. package/dist/docs/learning-journey-ui-gamified.docblock.d.ts.map +1 -0
  38. package/dist/docs/learning-journey-ui-gamified.docblock.js +21 -18
  39. package/dist/example.d.ts +2 -6
  40. package/dist/example.d.ts.map +1 -1
  41. package/dist/example.js +30 -39
  42. package/dist/index.d.ts +6 -12
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +1058 -14
  45. package/dist/node/GamifiedMiniApp.js +998 -0
  46. package/dist/node/components/DayCalendar.js +42 -0
  47. package/dist/node/components/FlashCard.js +102 -0
  48. package/dist/node/components/MasteryRing.js +75 -0
  49. package/dist/node/components/index.js +217 -0
  50. package/dist/node/docs/index.js +22 -0
  51. package/dist/node/docs/learning-journey-ui-gamified.docblock.js +22 -0
  52. package/dist/node/example.js +32 -0
  53. package/dist/node/index.js +1057 -0
  54. package/dist/node/views/Overview.js +236 -0
  55. package/dist/node/views/Progress.js +294 -0
  56. package/dist/node/views/Steps.js +157 -0
  57. package/dist/node/views/Timeline.js +235 -0
  58. package/dist/node/views/index.js +919 -0
  59. package/dist/views/Overview.d.ts +4 -12
  60. package/dist/views/Overview.d.ts.map +1 -1
  61. package/dist/views/Overview.js +234 -158
  62. package/dist/views/Progress.d.ts +2 -10
  63. package/dist/views/Progress.d.ts.map +1 -1
  64. package/dist/views/Progress.js +292 -140
  65. package/dist/views/Steps.d.ts +2 -11
  66. package/dist/views/Steps.d.ts.map +1 -1
  67. package/dist/views/Steps.js +155 -53
  68. package/dist/views/Timeline.d.ts +2 -10
  69. package/dist/views/Timeline.d.ts.map +1 -1
  70. package/dist/views/Timeline.js +233 -130
  71. package/dist/views/index.d.ts +5 -5
  72. package/dist/views/index.d.ts.map +1 -0
  73. package/dist/views/index.js +919 -5
  74. package/package.json +155 -39
  75. package/tsdown.config.js +1 -2
  76. package/.turbo/turbo-build$colon$bundle.log +0 -57
  77. package/dist/GamifiedMiniApp.js.map +0 -1
  78. package/dist/components/DayCalendar.js.map +0 -1
  79. package/dist/components/FlashCard.js.map +0 -1
  80. package/dist/components/MasteryRing.js.map +0 -1
  81. package/dist/docs/learning-journey-ui-gamified.docblock.js.map +0 -1
  82. package/dist/example.js.map +0 -1
  83. package/dist/views/Overview.js.map +0 -1
  84. package/dist/views/Progress.js.map +0 -1
  85. package/dist/views/Steps.js.map +0 -1
  86. package/dist/views/Timeline.js.map +0 -1
  87. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,919 @@
1
+ // src/views/Overview.tsx
2
+ import { Button } from "@contractspec/lib.design-system";
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardHeader,
7
+ CardTitle
8
+ } from "@contractspec/lib.ui-kit-web/ui/card";
9
+ import {
10
+ XpBar,
11
+ StreakCounter,
12
+ BadgeDisplay
13
+ } from "@contractspec/example.learning-journey-ui-shared";
14
+ import { jsxDEV } from "react/jsx-dev-runtime";
15
+ "use client";
16
+ function Overview({ track, progress, onStart }) {
17
+ const totalXp = track.totalXp ?? track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) + (track.completionRewards?.xpBonus ?? 0);
18
+ const completedSteps = progress.completedStepIds.length;
19
+ const totalSteps = track.steps.length;
20
+ const isComplete = completedSteps === totalSteps;
21
+ return /* @__PURE__ */ jsxDEV("div", {
22
+ className: "space-y-6",
23
+ children: [
24
+ /* @__PURE__ */ jsxDEV(Card, {
25
+ className: "overflow-hidden bg-gradient-to-br from-violet-500/10 via-purple-500/10 to-fuchsia-500/10",
26
+ children: /* @__PURE__ */ jsxDEV(CardContent, {
27
+ className: "p-6",
28
+ children: /* @__PURE__ */ jsxDEV("div", {
29
+ className: "flex flex-col items-center gap-4 text-center md:flex-row md:text-left",
30
+ children: [
31
+ /* @__PURE__ */ jsxDEV("div", {
32
+ className: "flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 text-4xl shadow-lg",
33
+ children: isComplete ? "\uD83C\uDFC6" : "\uD83C\uDFAF"
34
+ }, undefined, false, undefined, this),
35
+ /* @__PURE__ */ jsxDEV("div", {
36
+ className: "flex-1",
37
+ children: [
38
+ /* @__PURE__ */ jsxDEV("h1", {
39
+ className: "text-2xl font-bold",
40
+ children: track.name
41
+ }, undefined, false, undefined, this),
42
+ /* @__PURE__ */ jsxDEV("p", {
43
+ className: "text-muted-foreground mt-1",
44
+ children: track.description
45
+ }, undefined, false, undefined, this)
46
+ ]
47
+ }, undefined, true, undefined, this),
48
+ /* @__PURE__ */ jsxDEV("div", {
49
+ className: "flex items-center gap-3",
50
+ children: /* @__PURE__ */ jsxDEV(StreakCounter, {
51
+ days: progress.streakDays,
52
+ size: "lg"
53
+ }, undefined, false, undefined, this)
54
+ }, undefined, false, undefined, this)
55
+ ]
56
+ }, undefined, true, undefined, this)
57
+ }, undefined, false, undefined, this)
58
+ }, undefined, false, undefined, this),
59
+ /* @__PURE__ */ jsxDEV("div", {
60
+ className: "grid gap-4 md:grid-cols-3",
61
+ children: [
62
+ /* @__PURE__ */ jsxDEV(Card, {
63
+ children: [
64
+ /* @__PURE__ */ jsxDEV(CardHeader, {
65
+ className: "pb-2",
66
+ children: /* @__PURE__ */ jsxDEV(CardTitle, {
67
+ className: "text-muted-foreground text-sm font-medium",
68
+ children: "XP Progress"
69
+ }, undefined, false, undefined, this)
70
+ }, undefined, false, undefined, this),
71
+ /* @__PURE__ */ jsxDEV(CardContent, {
72
+ children: [
73
+ /* @__PURE__ */ jsxDEV("div", {
74
+ className: "text-3xl font-bold text-violet-500",
75
+ children: progress.xpEarned.toLocaleString()
76
+ }, undefined, false, undefined, this),
77
+ /* @__PURE__ */ jsxDEV(XpBar, {
78
+ current: progress.xpEarned,
79
+ max: totalXp,
80
+ showLabel: false,
81
+ size: "sm"
82
+ }, undefined, false, undefined, this)
83
+ ]
84
+ }, undefined, true, undefined, this)
85
+ ]
86
+ }, undefined, true, undefined, this),
87
+ /* @__PURE__ */ jsxDEV(Card, {
88
+ children: [
89
+ /* @__PURE__ */ jsxDEV(CardHeader, {
90
+ className: "pb-2",
91
+ children: /* @__PURE__ */ jsxDEV(CardTitle, {
92
+ className: "text-muted-foreground text-sm font-medium",
93
+ children: "Steps Completed"
94
+ }, undefined, false, undefined, this)
95
+ }, undefined, false, undefined, this),
96
+ /* @__PURE__ */ jsxDEV(CardContent, {
97
+ children: [
98
+ /* @__PURE__ */ jsxDEV("div", {
99
+ className: "text-3xl font-bold",
100
+ children: [
101
+ completedSteps,
102
+ " ",
103
+ /* @__PURE__ */ jsxDEV("span", {
104
+ className: "text-muted-foreground text-lg",
105
+ children: [
106
+ "/ ",
107
+ totalSteps
108
+ ]
109
+ }, undefined, true, undefined, this)
110
+ ]
111
+ }, undefined, true, undefined, this),
112
+ /* @__PURE__ */ jsxDEV("div", {
113
+ className: "bg-muted mt-2 h-2 w-full overflow-hidden rounded-full",
114
+ children: /* @__PURE__ */ jsxDEV("div", {
115
+ className: "h-full bg-green-500 transition-all duration-500",
116
+ style: { width: `${completedSteps / totalSteps * 100}%` }
117
+ }, undefined, false, undefined, this)
118
+ }, undefined, false, undefined, this)
119
+ ]
120
+ }, undefined, true, undefined, this)
121
+ ]
122
+ }, undefined, true, undefined, this),
123
+ /* @__PURE__ */ jsxDEV(Card, {
124
+ children: [
125
+ /* @__PURE__ */ jsxDEV(CardHeader, {
126
+ className: "pb-2",
127
+ children: /* @__PURE__ */ jsxDEV(CardTitle, {
128
+ className: "text-muted-foreground text-sm font-medium",
129
+ children: "Badges Earned"
130
+ }, undefined, false, undefined, this)
131
+ }, undefined, false, undefined, this),
132
+ /* @__PURE__ */ jsxDEV(CardContent, {
133
+ children: /* @__PURE__ */ jsxDEV(BadgeDisplay, {
134
+ badges: progress.badges,
135
+ size: "lg"
136
+ }, undefined, false, undefined, this)
137
+ }, undefined, false, undefined, this)
138
+ ]
139
+ }, undefined, true, undefined, this)
140
+ ]
141
+ }, undefined, true, undefined, this),
142
+ !isComplete && /* @__PURE__ */ jsxDEV(Card, {
143
+ children: [
144
+ /* @__PURE__ */ jsxDEV(CardHeader, {
145
+ children: /* @__PURE__ */ jsxDEV(CardTitle, {
146
+ className: "flex items-center gap-2",
147
+ children: [
148
+ /* @__PURE__ */ jsxDEV("span", {
149
+ children: "\uD83C\uDFAF"
150
+ }, undefined, false, undefined, this),
151
+ /* @__PURE__ */ jsxDEV("span", {
152
+ children: "Next Challenge"
153
+ }, undefined, false, undefined, this)
154
+ ]
155
+ }, undefined, true, undefined, this)
156
+ }, undefined, false, undefined, this),
157
+ /* @__PURE__ */ jsxDEV(CardContent, {
158
+ children: (() => {
159
+ const nextStep = track.steps.find((s) => !progress.completedStepIds.includes(s.id));
160
+ if (!nextStep)
161
+ return null;
162
+ return /* @__PURE__ */ jsxDEV("div", {
163
+ className: "flex items-center justify-between gap-4",
164
+ children: [
165
+ /* @__PURE__ */ jsxDEV("div", {
166
+ children: [
167
+ /* @__PURE__ */ jsxDEV("h3", {
168
+ className: "font-semibold",
169
+ children: nextStep.title
170
+ }, undefined, false, undefined, this),
171
+ /* @__PURE__ */ jsxDEV("p", {
172
+ className: "text-muted-foreground text-sm",
173
+ children: nextStep.description
174
+ }, undefined, false, undefined, this)
175
+ ]
176
+ }, undefined, true, undefined, this),
177
+ /* @__PURE__ */ jsxDEV("div", {
178
+ className: "flex items-center gap-3",
179
+ children: [
180
+ nextStep.xpReward && /* @__PURE__ */ jsxDEV("span", {
181
+ className: "rounded-full bg-green-500/10 px-3 py-1 text-sm font-semibold text-green-500",
182
+ children: [
183
+ "+",
184
+ nextStep.xpReward,
185
+ " XP"
186
+ ]
187
+ }, undefined, true, undefined, this),
188
+ /* @__PURE__ */ jsxDEV(Button, {
189
+ onClick: onStart,
190
+ children: "Start"
191
+ }, undefined, false, undefined, this)
192
+ ]
193
+ }, undefined, true, undefined, this)
194
+ ]
195
+ }, undefined, true, undefined, this);
196
+ })()
197
+ }, undefined, false, undefined, this)
198
+ ]
199
+ }, undefined, true, undefined, this),
200
+ isComplete && /* @__PURE__ */ jsxDEV(Card, {
201
+ className: "border-green-500/50 bg-green-500/5",
202
+ children: /* @__PURE__ */ jsxDEV(CardContent, {
203
+ className: "flex items-center gap-4 p-6",
204
+ children: [
205
+ /* @__PURE__ */ jsxDEV("div", {
206
+ className: "text-4xl",
207
+ children: "\uD83C\uDF89"
208
+ }, undefined, false, undefined, this),
209
+ /* @__PURE__ */ jsxDEV("div", {
210
+ children: [
211
+ /* @__PURE__ */ jsxDEV("h3", {
212
+ className: "text-lg font-semibold text-green-500",
213
+ children: "Track Complete!"
214
+ }, undefined, false, undefined, this),
215
+ /* @__PURE__ */ jsxDEV("p", {
216
+ className: "text-muted-foreground",
217
+ children: [
218
+ "You've mastered all ",
219
+ totalSteps,
220
+ " challenges and earned",
221
+ " ",
222
+ progress.xpEarned,
223
+ " XP."
224
+ ]
225
+ }, undefined, true, undefined, this)
226
+ ]
227
+ }, undefined, true, undefined, this)
228
+ ]
229
+ }, undefined, true, undefined, this)
230
+ }, undefined, false, undefined, this)
231
+ ]
232
+ }, undefined, true, undefined, this);
233
+ }
234
+
235
+ // src/components/FlashCard.tsx
236
+ import { useState } from "react";
237
+ import { Button as Button2 } from "@contractspec/lib.design-system";
238
+ import { Card as Card2, CardContent as CardContent2 } from "@contractspec/lib.ui-kit-web/ui/card";
239
+ import { cn } from "@contractspec/lib.ui-kit-web/ui/utils";
240
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
241
+ "use client";
242
+ function FlashCard({
243
+ step,
244
+ isCompleted,
245
+ isCurrent,
246
+ onComplete
247
+ }) {
248
+ const [isFlipped, setIsFlipped] = useState(false);
249
+ return /* @__PURE__ */ jsxDEV2(Card2, {
250
+ className: cn("relative cursor-pointer overflow-hidden transition-all duration-300", isCurrent && "ring-primary ring-2", isCompleted && "opacity-60"),
251
+ onClick: () => !isCompleted && setIsFlipped(!isFlipped),
252
+ children: /* @__PURE__ */ jsxDEV2(CardContent2, {
253
+ className: "p-6",
254
+ children: [
255
+ /* @__PURE__ */ jsxDEV2("div", {
256
+ className: cn("space-y-4 transition-opacity duration-200", isFlipped ? "opacity-0" : "opacity-100"),
257
+ children: [
258
+ /* @__PURE__ */ jsxDEV2("div", {
259
+ className: "flex items-start justify-between",
260
+ children: [
261
+ /* @__PURE__ */ jsxDEV2("div", {
262
+ className: "flex-1",
263
+ children: [
264
+ /* @__PURE__ */ jsxDEV2("h3", {
265
+ className: "text-lg font-semibold",
266
+ children: step.title
267
+ }, undefined, false, undefined, this),
268
+ step.description && /* @__PURE__ */ jsxDEV2("p", {
269
+ className: "text-muted-foreground mt-1 text-sm",
270
+ children: step.description
271
+ }, undefined, false, undefined, this)
272
+ ]
273
+ }, undefined, true, undefined, this),
274
+ step.xpReward && /* @__PURE__ */ jsxDEV2("span", {
275
+ className: "rounded-full bg-green-500/10 px-2 py-1 text-xs font-semibold text-green-500",
276
+ children: [
277
+ "+",
278
+ step.xpReward,
279
+ " XP"
280
+ ]
281
+ }, undefined, true, undefined, this)
282
+ ]
283
+ }, undefined, true, undefined, this),
284
+ isCompleted && /* @__PURE__ */ jsxDEV2("div", {
285
+ className: "flex items-center gap-2 text-green-500",
286
+ children: [
287
+ /* @__PURE__ */ jsxDEV2("span", {
288
+ children: "✓"
289
+ }, undefined, false, undefined, this),
290
+ /* @__PURE__ */ jsxDEV2("span", {
291
+ className: "text-sm font-medium",
292
+ children: "Completed"
293
+ }, undefined, false, undefined, this)
294
+ ]
295
+ }, undefined, true, undefined, this),
296
+ isCurrent && !isCompleted && /* @__PURE__ */ jsxDEV2("p", {
297
+ className: "text-muted-foreground text-xs",
298
+ children: "Tap to reveal action"
299
+ }, undefined, false, undefined, this)
300
+ ]
301
+ }, undefined, true, undefined, this),
302
+ isFlipped && !isCompleted && /* @__PURE__ */ jsxDEV2("div", {
303
+ className: "absolute inset-0 flex flex-col items-center justify-center gap-4 bg-gradient-to-br from-violet-500/10 to-violet-600/10 p-6",
304
+ children: [
305
+ /* @__PURE__ */ jsxDEV2("p", {
306
+ className: "text-center text-sm",
307
+ children: step.instructions ?? "Complete this step to earn XP"
308
+ }, undefined, false, undefined, this),
309
+ /* @__PURE__ */ jsxDEV2("div", {
310
+ className: "flex gap-2",
311
+ children: [
312
+ /* @__PURE__ */ jsxDEV2(Button2, {
313
+ variant: "outline",
314
+ size: "sm",
315
+ onClick: () => setIsFlipped(false),
316
+ children: "Back"
317
+ }, undefined, false, undefined, this),
318
+ /* @__PURE__ */ jsxDEV2(Button2, {
319
+ size: "sm",
320
+ onClick: (e) => {
321
+ e.stopPropagation();
322
+ onComplete?.();
323
+ },
324
+ children: "Mark Complete"
325
+ }, undefined, false, undefined, this)
326
+ ]
327
+ }, undefined, true, undefined, this)
328
+ ]
329
+ }, undefined, true, undefined, this)
330
+ ]
331
+ }, undefined, true, undefined, this)
332
+ }, undefined, false, undefined, this);
333
+ }
334
+
335
+ // src/views/Steps.tsx
336
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
337
+ "use client";
338
+ function Steps({ track, progress, onStepComplete }) {
339
+ const currentStepIndex = track.steps.findIndex((s) => !progress.completedStepIds.includes(s.id));
340
+ return /* @__PURE__ */ jsxDEV3("div", {
341
+ className: "space-y-6",
342
+ children: [
343
+ /* @__PURE__ */ jsxDEV3("div", {
344
+ className: "text-center",
345
+ children: [
346
+ /* @__PURE__ */ jsxDEV3("h2", {
347
+ className: "text-xl font-bold",
348
+ children: "Complete Your Challenges"
349
+ }, undefined, false, undefined, this),
350
+ /* @__PURE__ */ jsxDEV3("p", {
351
+ className: "text-muted-foreground",
352
+ children: "Tap each card to reveal the action, then mark as complete"
353
+ }, undefined, false, undefined, this)
354
+ ]
355
+ }, undefined, true, undefined, this),
356
+ /* @__PURE__ */ jsxDEV3("div", {
357
+ className: "grid gap-4 md:grid-cols-2",
358
+ children: track.steps.map((step, index) => {
359
+ const isCompleted = progress.completedStepIds.includes(step.id);
360
+ const isCurrent = index === currentStepIndex;
361
+ return /* @__PURE__ */ jsxDEV3(FlashCard, {
362
+ step,
363
+ isCompleted,
364
+ isCurrent,
365
+ onComplete: () => onStepComplete?.(step.id)
366
+ }, step.id, false, undefined, this);
367
+ })
368
+ }, undefined, false, undefined, this),
369
+ /* @__PURE__ */ jsxDEV3("div", {
370
+ className: "text-muted-foreground text-center text-sm",
371
+ children: [
372
+ progress.completedStepIds.length,
373
+ " of ",
374
+ track.steps.length,
375
+ " completed",
376
+ track.completionRewards?.xpBonus && /* @__PURE__ */ jsxDEV3("span", {
377
+ className: "ml-2 text-green-500",
378
+ children: [
379
+ "(+",
380
+ track.completionRewards.xpBonus,
381
+ " XP bonus on completion)"
382
+ ]
383
+ }, undefined, true, undefined, this)
384
+ ]
385
+ }, undefined, true, undefined, this)
386
+ ]
387
+ }, undefined, true, undefined, this);
388
+ }
389
+
390
+ // src/components/MasteryRing.tsx
391
+ import { cn as cn2 } from "@contractspec/lib.ui-kit-web/ui/utils";
392
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
393
+ "use client";
394
+ var sizeStyles = {
395
+ sm: { container: "h-16 w-16", text: "text-xs", ring: 48, stroke: 4 },
396
+ md: { container: "h-24 w-24", text: "text-sm", ring: 72, stroke: 6 },
397
+ lg: { container: "h-32 w-32", text: "text-base", ring: 96, stroke: 8 }
398
+ };
399
+ var colorStyles = {
400
+ green: "stroke-green-500",
401
+ blue: "stroke-blue-500",
402
+ violet: "stroke-violet-500",
403
+ orange: "stroke-orange-500"
404
+ };
405
+ function MasteryRing({
406
+ label,
407
+ percentage,
408
+ size = "md",
409
+ color = "violet"
410
+ }) {
411
+ const styles = sizeStyles[size];
412
+ const radius = (styles.ring - styles.stroke) / 2;
413
+ const circumference = 2 * Math.PI * radius;
414
+ const strokeDashoffset = circumference - percentage / 100 * circumference;
415
+ return /* @__PURE__ */ jsxDEV4("div", {
416
+ className: cn2("relative flex flex-col items-center gap-1", styles.container),
417
+ children: [
418
+ /* @__PURE__ */ jsxDEV4("svg", {
419
+ className: "absolute -rotate-90",
420
+ width: styles.ring,
421
+ height: styles.ring,
422
+ viewBox: `0 0 ${styles.ring} ${styles.ring}`,
423
+ children: [
424
+ /* @__PURE__ */ jsxDEV4("circle", {
425
+ cx: styles.ring / 2,
426
+ cy: styles.ring / 2,
427
+ r: radius,
428
+ fill: "none",
429
+ strokeWidth: styles.stroke,
430
+ className: "stroke-muted"
431
+ }, undefined, false, undefined, this),
432
+ /* @__PURE__ */ jsxDEV4("circle", {
433
+ cx: styles.ring / 2,
434
+ cy: styles.ring / 2,
435
+ r: radius,
436
+ fill: "none",
437
+ strokeWidth: styles.stroke,
438
+ strokeLinecap: "round",
439
+ strokeDasharray: circumference,
440
+ strokeDashoffset,
441
+ className: cn2("transition-all duration-500", colorStyles[color])
442
+ }, undefined, false, undefined, this)
443
+ ]
444
+ }, undefined, true, undefined, this),
445
+ /* @__PURE__ */ jsxDEV4("div", {
446
+ className: "flex h-full flex-col items-center justify-center",
447
+ children: /* @__PURE__ */ jsxDEV4("span", {
448
+ className: cn2("font-bold", styles.text),
449
+ children: [
450
+ Math.round(percentage),
451
+ "%"
452
+ ]
453
+ }, undefined, true, undefined, this)
454
+ }, undefined, false, undefined, this),
455
+ /* @__PURE__ */ jsxDEV4("span", {
456
+ className: cn2("text-muted-foreground mt-1 truncate", styles.text),
457
+ children: label
458
+ }, undefined, false, undefined, this)
459
+ ]
460
+ }, undefined, true, undefined, this);
461
+ }
462
+
463
+ // src/views/Progress.tsx
464
+ import {
465
+ Card as Card3,
466
+ CardContent as CardContent3,
467
+ CardHeader as CardHeader2,
468
+ CardTitle as CardTitle2
469
+ } from "@contractspec/lib.ui-kit-web/ui/card";
470
+ import {
471
+ XpBar as XpBar2,
472
+ BadgeDisplay as BadgeDisplay2
473
+ } from "@contractspec/example.learning-journey-ui-shared";
474
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
475
+ "use client";
476
+ function Progress({ track, progress }) {
477
+ const totalXp = track.totalXp ?? track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) + (track.completionRewards?.xpBonus ?? 0);
478
+ const completedSteps = progress.completedStepIds.length;
479
+ const totalSteps = track.steps.length;
480
+ const percentComplete = totalSteps > 0 ? completedSteps / totalSteps * 100 : 0;
481
+ const surfaces = new Map;
482
+ track.steps.forEach((step) => {
483
+ const surface = step.metadata?.surface ?? "general";
484
+ const current = surfaces.get(surface) ?? { total: 0, completed: 0 };
485
+ surfaces.set(surface, {
486
+ total: current.total + 1,
487
+ completed: current.completed + (progress.completedStepIds.includes(step.id) ? 1 : 0)
488
+ });
489
+ });
490
+ const surfaceColors = [
491
+ "green",
492
+ "blue",
493
+ "violet",
494
+ "orange"
495
+ ];
496
+ return /* @__PURE__ */ jsxDEV5("div", {
497
+ className: "space-y-6",
498
+ children: [
499
+ /* @__PURE__ */ jsxDEV5(Card3, {
500
+ children: [
501
+ /* @__PURE__ */ jsxDEV5(CardHeader2, {
502
+ children: /* @__PURE__ */ jsxDEV5(CardTitle2, {
503
+ className: "flex items-center gap-2",
504
+ children: [
505
+ /* @__PURE__ */ jsxDEV5("span", {
506
+ children: "⚡"
507
+ }, undefined, false, undefined, this),
508
+ /* @__PURE__ */ jsxDEV5("span", {
509
+ children: "Experience Points"
510
+ }, undefined, false, undefined, this)
511
+ ]
512
+ }, undefined, true, undefined, this)
513
+ }, undefined, false, undefined, this),
514
+ /* @__PURE__ */ jsxDEV5(CardContent3, {
515
+ className: "space-y-4",
516
+ children: [
517
+ /* @__PURE__ */ jsxDEV5("div", {
518
+ className: "flex items-baseline gap-2",
519
+ children: [
520
+ /* @__PURE__ */ jsxDEV5("span", {
521
+ className: "text-4xl font-bold text-violet-500",
522
+ children: progress.xpEarned.toLocaleString()
523
+ }, undefined, false, undefined, this),
524
+ /* @__PURE__ */ jsxDEV5("span", {
525
+ className: "text-muted-foreground",
526
+ children: [
527
+ "/ ",
528
+ totalXp.toLocaleString(),
529
+ " XP"
530
+ ]
531
+ }, undefined, true, undefined, this)
532
+ ]
533
+ }, undefined, true, undefined, this),
534
+ /* @__PURE__ */ jsxDEV5(XpBar2, {
535
+ current: progress.xpEarned,
536
+ max: totalXp,
537
+ showLabel: false,
538
+ size: "lg"
539
+ }, undefined, false, undefined, this),
540
+ track.completionRewards?.xpBonus && percentComplete < 100 && /* @__PURE__ */ jsxDEV5("p", {
541
+ className: "text-muted-foreground text-sm",
542
+ children: [
543
+ "\uD83C\uDF81 Complete all steps for a",
544
+ " ",
545
+ /* @__PURE__ */ jsxDEV5("span", {
546
+ className: "font-semibold text-green-500",
547
+ children: [
548
+ "+",
549
+ track.completionRewards.xpBonus,
550
+ " XP"
551
+ ]
552
+ }, undefined, true, undefined, this),
553
+ " ",
554
+ "bonus!"
555
+ ]
556
+ }, undefined, true, undefined, this)
557
+ ]
558
+ }, undefined, true, undefined, this)
559
+ ]
560
+ }, undefined, true, undefined, this),
561
+ /* @__PURE__ */ jsxDEV5(Card3, {
562
+ children: [
563
+ /* @__PURE__ */ jsxDEV5(CardHeader2, {
564
+ children: /* @__PURE__ */ jsxDEV5(CardTitle2, {
565
+ className: "flex items-center gap-2",
566
+ children: [
567
+ /* @__PURE__ */ jsxDEV5("span", {
568
+ children: "\uD83C\uDFAF"
569
+ }, undefined, false, undefined, this),
570
+ /* @__PURE__ */ jsxDEV5("span", {
571
+ children: "Skill Mastery"
572
+ }, undefined, false, undefined, this)
573
+ ]
574
+ }, undefined, true, undefined, this)
575
+ }, undefined, false, undefined, this),
576
+ /* @__PURE__ */ jsxDEV5(CardContent3, {
577
+ children: /* @__PURE__ */ jsxDEV5("div", {
578
+ className: "flex flex-wrap justify-center gap-6",
579
+ children: [
580
+ Array.from(surfaces.entries()).map(([surface, data], index) => /* @__PURE__ */ jsxDEV5(MasteryRing, {
581
+ label: surface.charAt(0).toUpperCase() + surface.slice(1),
582
+ percentage: data.completed / data.total * 100,
583
+ color: surfaceColors[index % surfaceColors.length],
584
+ size: "lg"
585
+ }, surface, false, undefined, this)),
586
+ /* @__PURE__ */ jsxDEV5(MasteryRing, {
587
+ label: "Overall",
588
+ percentage: percentComplete,
589
+ color: "violet",
590
+ size: "lg"
591
+ }, undefined, false, undefined, this)
592
+ ]
593
+ }, undefined, true, undefined, this)
594
+ }, undefined, false, undefined, this)
595
+ ]
596
+ }, undefined, true, undefined, this),
597
+ /* @__PURE__ */ jsxDEV5(Card3, {
598
+ children: [
599
+ /* @__PURE__ */ jsxDEV5(CardHeader2, {
600
+ children: /* @__PURE__ */ jsxDEV5(CardTitle2, {
601
+ className: "flex items-center gap-2",
602
+ children: [
603
+ /* @__PURE__ */ jsxDEV5("span", {
604
+ children: "\uD83C\uDFC5"
605
+ }, undefined, false, undefined, this),
606
+ /* @__PURE__ */ jsxDEV5("span", {
607
+ children: "Badges Earned"
608
+ }, undefined, false, undefined, this)
609
+ ]
610
+ }, undefined, true, undefined, this)
611
+ }, undefined, false, undefined, this),
612
+ /* @__PURE__ */ jsxDEV5(CardContent3, {
613
+ children: [
614
+ /* @__PURE__ */ jsxDEV5(BadgeDisplay2, {
615
+ badges: progress.badges,
616
+ size: "lg",
617
+ maxVisible: 10
618
+ }, undefined, false, undefined, this),
619
+ progress.badges.length === 0 && /* @__PURE__ */ jsxDEV5("p", {
620
+ className: "text-muted-foreground text-sm",
621
+ children: "Complete the track to earn your first badge!"
622
+ }, undefined, false, undefined, this)
623
+ ]
624
+ }, undefined, true, undefined, this)
625
+ ]
626
+ }, undefined, true, undefined, this),
627
+ /* @__PURE__ */ jsxDEV5(Card3, {
628
+ children: [
629
+ /* @__PURE__ */ jsxDEV5(CardHeader2, {
630
+ children: /* @__PURE__ */ jsxDEV5(CardTitle2, {
631
+ className: "flex items-center gap-2",
632
+ children: [
633
+ /* @__PURE__ */ jsxDEV5("span", {
634
+ children: "\uD83D\uDCCA"
635
+ }, undefined, false, undefined, this),
636
+ /* @__PURE__ */ jsxDEV5("span", {
637
+ children: "Step Breakdown"
638
+ }, undefined, false, undefined, this)
639
+ ]
640
+ }, undefined, true, undefined, this)
641
+ }, undefined, false, undefined, this),
642
+ /* @__PURE__ */ jsxDEV5(CardContent3, {
643
+ children: /* @__PURE__ */ jsxDEV5("div", {
644
+ className: "space-y-2",
645
+ children: track.steps.map((step) => {
646
+ const isCompleted = progress.completedStepIds.includes(step.id);
647
+ return /* @__PURE__ */ jsxDEV5("div", {
648
+ className: "flex items-center justify-between rounded-lg border p-3",
649
+ children: [
650
+ /* @__PURE__ */ jsxDEV5("div", {
651
+ className: "flex items-center gap-3",
652
+ children: [
653
+ /* @__PURE__ */ jsxDEV5("span", {
654
+ className: isCompleted ? "text-green-500" : "text-muted-foreground",
655
+ children: isCompleted ? "✓" : "○"
656
+ }, undefined, false, undefined, this),
657
+ /* @__PURE__ */ jsxDEV5("span", {
658
+ className: isCompleted ? "text-foreground" : "text-muted-foreground",
659
+ children: step.title
660
+ }, undefined, false, undefined, this)
661
+ ]
662
+ }, undefined, true, undefined, this),
663
+ step.xpReward && /* @__PURE__ */ jsxDEV5("span", {
664
+ className: `text-sm font-medium ${isCompleted ? "text-green-500" : "text-muted-foreground"}`,
665
+ children: [
666
+ isCompleted ? "+" : "",
667
+ step.xpReward,
668
+ " XP"
669
+ ]
670
+ }, undefined, true, undefined, this)
671
+ ]
672
+ }, step.id, true, undefined, this);
673
+ })
674
+ }, undefined, false, undefined, this)
675
+ }, undefined, false, undefined, this)
676
+ ]
677
+ }, undefined, true, undefined, this)
678
+ ]
679
+ }, undefined, true, undefined, this);
680
+ }
681
+
682
+ // src/components/DayCalendar.tsx
683
+ import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
684
+ import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
685
+ "use client";
686
+ function DayCalendar({
687
+ totalDays,
688
+ currentDay,
689
+ completedDays
690
+ }) {
691
+ const days = Array.from({ length: totalDays }, (_, i) => i + 1);
692
+ return /* @__PURE__ */ jsxDEV6("div", {
693
+ className: "grid grid-cols-7 gap-2",
694
+ children: days.map((day) => {
695
+ const isCompleted = completedDays.includes(day);
696
+ const isCurrent = day === currentDay;
697
+ const isLocked = day > currentDay;
698
+ return /* @__PURE__ */ jsxDEV6("div", {
699
+ className: cn3("flex h-12 w-12 flex-col items-center justify-center rounded-lg border text-sm font-medium transition-all", isCompleted && "border-green-500 bg-green-500/10 text-green-500", isCurrent && !isCompleted && "border-violet-500 bg-violet-500/10 text-violet-500 ring-2 ring-violet-500/50", isLocked && "border-muted bg-muted/50 text-muted-foreground", !isCompleted && !isCurrent && !isLocked && "border-border bg-card"),
700
+ children: isCompleted ? /* @__PURE__ */ jsxDEV6("span", {
701
+ className: "text-lg",
702
+ children: "✓"
703
+ }, undefined, false, undefined, this) : isLocked ? /* @__PURE__ */ jsxDEV6("span", {
704
+ className: "text-lg",
705
+ children: "\uD83D\uDD12"
706
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6(Fragment, {
707
+ children: [
708
+ /* @__PURE__ */ jsxDEV6("span", {
709
+ className: "text-muted-foreground text-xs",
710
+ children: "Day"
711
+ }, undefined, false, undefined, this),
712
+ /* @__PURE__ */ jsxDEV6("span", {
713
+ children: day
714
+ }, undefined, false, undefined, this)
715
+ ]
716
+ }, undefined, true, undefined, this)
717
+ }, day, false, undefined, this);
718
+ })
719
+ }, undefined, false, undefined, this);
720
+ }
721
+
722
+ // src/views/Timeline.tsx
723
+ import {
724
+ Card as Card4,
725
+ CardContent as CardContent4,
726
+ CardHeader as CardHeader3,
727
+ CardTitle as CardTitle3
728
+ } from "@contractspec/lib.ui-kit-web/ui/card";
729
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
730
+ "use client";
731
+ function Timeline({ track, progress }) {
732
+ const hasQuestDays = track.steps.some((s) => s.availability?.unlockOnDay !== undefined);
733
+ if (hasQuestDays) {
734
+ const totalDays = Math.max(...track.steps.map((s) => s.availability?.unlockOnDay ?? 1), 7);
735
+ const completedDays = track.steps.filter((s) => progress.completedStepIds.includes(s.id)).map((s) => s.availability?.unlockOnDay ?? 1);
736
+ const currentDay = track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.availability?.unlockOnDay ?? 1;
737
+ return /* @__PURE__ */ jsxDEV7("div", {
738
+ className: "space-y-6",
739
+ children: [
740
+ /* @__PURE__ */ jsxDEV7("div", {
741
+ className: "text-center",
742
+ children: [
743
+ /* @__PURE__ */ jsxDEV7("h2", {
744
+ className: "text-xl font-bold",
745
+ children: track.name
746
+ }, undefined, false, undefined, this),
747
+ /* @__PURE__ */ jsxDEV7("p", {
748
+ className: "text-muted-foreground",
749
+ children: "Complete each day's challenge to progress"
750
+ }, undefined, false, undefined, this)
751
+ ]
752
+ }, undefined, true, undefined, this),
753
+ /* @__PURE__ */ jsxDEV7(Card4, {
754
+ children: [
755
+ /* @__PURE__ */ jsxDEV7(CardHeader3, {
756
+ children: /* @__PURE__ */ jsxDEV7(CardTitle3, {
757
+ className: "flex items-center gap-2",
758
+ children: [
759
+ /* @__PURE__ */ jsxDEV7("span", {
760
+ children: "\uD83D\uDCC5"
761
+ }, undefined, false, undefined, this),
762
+ /* @__PURE__ */ jsxDEV7("span", {
763
+ children: "Your Journey"
764
+ }, undefined, false, undefined, this)
765
+ ]
766
+ }, undefined, true, undefined, this)
767
+ }, undefined, false, undefined, this),
768
+ /* @__PURE__ */ jsxDEV7(CardContent4, {
769
+ className: "flex justify-center",
770
+ children: /* @__PURE__ */ jsxDEV7(DayCalendar, {
771
+ totalDays,
772
+ currentDay,
773
+ completedDays
774
+ }, undefined, false, undefined, this)
775
+ }, undefined, false, undefined, this)
776
+ ]
777
+ }, undefined, true, undefined, this),
778
+ /* @__PURE__ */ jsxDEV7(Card4, {
779
+ children: [
780
+ /* @__PURE__ */ jsxDEV7(CardHeader3, {
781
+ children: /* @__PURE__ */ jsxDEV7(CardTitle3, {
782
+ className: "flex items-center gap-2",
783
+ children: [
784
+ /* @__PURE__ */ jsxDEV7("span", {
785
+ children: "\uD83D\uDCDD"
786
+ }, undefined, false, undefined, this),
787
+ /* @__PURE__ */ jsxDEV7("span", {
788
+ children: "Daily Challenges"
789
+ }, undefined, false, undefined, this)
790
+ ]
791
+ }, undefined, true, undefined, this)
792
+ }, undefined, false, undefined, this),
793
+ /* @__PURE__ */ jsxDEV7(CardContent4, {
794
+ children: /* @__PURE__ */ jsxDEV7("div", {
795
+ className: "space-y-3",
796
+ children: track.steps.map((step) => {
797
+ const day = step.availability?.unlockOnDay ?? 1;
798
+ const isCompleted = progress.completedStepIds.includes(step.id);
799
+ const isLocked = day > currentDay;
800
+ return /* @__PURE__ */ jsxDEV7("div", {
801
+ className: `flex items-start gap-4 rounded-lg border p-4 ${isLocked ? "opacity-50" : ""}`,
802
+ children: [
803
+ /* @__PURE__ */ jsxDEV7("div", {
804
+ className: "bg-muted flex h-10 w-10 shrink-0 items-center justify-center rounded-lg font-semibold",
805
+ children: isCompleted ? "✓" : isLocked ? "\uD83D\uDD12" : day
806
+ }, undefined, false, undefined, this),
807
+ /* @__PURE__ */ jsxDEV7("div", {
808
+ className: "flex-1",
809
+ children: [
810
+ /* @__PURE__ */ jsxDEV7("h4", {
811
+ className: "font-semibold",
812
+ children: step.title
813
+ }, undefined, false, undefined, this),
814
+ /* @__PURE__ */ jsxDEV7("p", {
815
+ className: "text-muted-foreground text-sm",
816
+ children: step.description
817
+ }, undefined, false, undefined, this)
818
+ ]
819
+ }, undefined, true, undefined, this),
820
+ step.xpReward && /* @__PURE__ */ jsxDEV7("span", {
821
+ className: `text-sm font-medium ${isCompleted ? "text-green-500" : "text-muted-foreground"}`,
822
+ children: [
823
+ "+",
824
+ step.xpReward,
825
+ " XP"
826
+ ]
827
+ }, undefined, true, undefined, this)
828
+ ]
829
+ }, step.id, true, undefined, this);
830
+ })
831
+ }, undefined, false, undefined, this)
832
+ }, undefined, false, undefined, this)
833
+ ]
834
+ }, undefined, true, undefined, this)
835
+ ]
836
+ }, undefined, true, undefined, this);
837
+ }
838
+ return /* @__PURE__ */ jsxDEV7("div", {
839
+ className: "space-y-6",
840
+ children: [
841
+ /* @__PURE__ */ jsxDEV7("div", {
842
+ className: "text-center",
843
+ children: [
844
+ /* @__PURE__ */ jsxDEV7("h2", {
845
+ className: "text-xl font-bold",
846
+ children: "Learning Path"
847
+ }, undefined, false, undefined, this),
848
+ /* @__PURE__ */ jsxDEV7("p", {
849
+ className: "text-muted-foreground",
850
+ children: "Follow the steps to master this skill"
851
+ }, undefined, false, undefined, this)
852
+ ]
853
+ }, undefined, true, undefined, this),
854
+ /* @__PURE__ */ jsxDEV7(Card4, {
855
+ children: /* @__PURE__ */ jsxDEV7(CardContent4, {
856
+ className: "p-6",
857
+ children: /* @__PURE__ */ jsxDEV7("div", {
858
+ className: "relative",
859
+ children: [
860
+ /* @__PURE__ */ jsxDEV7("div", {
861
+ className: "bg-border absolute top-0 left-5 h-full w-0.5"
862
+ }, undefined, false, undefined, this),
863
+ /* @__PURE__ */ jsxDEV7("div", {
864
+ className: "space-y-6",
865
+ children: track.steps.map((step, index) => {
866
+ const isCompleted = progress.completedStepIds.includes(step.id);
867
+ const isCurrent = !isCompleted && track.steps.slice(0, index).every((s) => progress.completedStepIds.includes(s.id));
868
+ return /* @__PURE__ */ jsxDEV7("div", {
869
+ className: "relative flex gap-4 pl-2",
870
+ children: [
871
+ /* @__PURE__ */ jsxDEV7("div", {
872
+ className: `relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2 ${isCompleted ? "border-green-500 bg-green-500 text-white" : isCurrent ? "border-violet-500 bg-violet-500 text-white" : "border-border bg-background"}`,
873
+ children: isCompleted ? "✓" : index + 1
874
+ }, undefined, false, undefined, this),
875
+ /* @__PURE__ */ jsxDEV7("div", {
876
+ className: "flex-1 pb-4",
877
+ children: /* @__PURE__ */ jsxDEV7("div", {
878
+ className: "flex items-start justify-between gap-2",
879
+ children: [
880
+ /* @__PURE__ */ jsxDEV7("div", {
881
+ children: [
882
+ /* @__PURE__ */ jsxDEV7("h4", {
883
+ className: `font-semibold ${isCompleted ? "text-foreground" : isCurrent ? "text-violet-500" : "text-muted-foreground"}`,
884
+ children: step.title
885
+ }, undefined, false, undefined, this),
886
+ /* @__PURE__ */ jsxDEV7("p", {
887
+ className: "text-muted-foreground mt-1 text-sm",
888
+ children: step.description
889
+ }, undefined, false, undefined, this)
890
+ ]
891
+ }, undefined, true, undefined, this),
892
+ step.xpReward && /* @__PURE__ */ jsxDEV7("span", {
893
+ className: `shrink-0 rounded-full px-2 py-1 text-xs font-semibold ${isCompleted ? "bg-green-500/10 text-green-500" : "bg-muted text-muted-foreground"}`,
894
+ children: [
895
+ "+",
896
+ step.xpReward,
897
+ " XP"
898
+ ]
899
+ }, undefined, true, undefined, this)
900
+ ]
901
+ }, undefined, true, undefined, this)
902
+ }, undefined, false, undefined, this)
903
+ ]
904
+ }, step.id, true, undefined, this);
905
+ })
906
+ }, undefined, false, undefined, this)
907
+ ]
908
+ }, undefined, true, undefined, this)
909
+ }, undefined, false, undefined, this)
910
+ }, undefined, false, undefined, this)
911
+ ]
912
+ }, undefined, true, undefined, this);
913
+ }
914
+ export {
915
+ Timeline,
916
+ Steps,
917
+ Progress,
918
+ Overview
919
+ };