@contractspec/example.learning-journey-ui-onboarding 1.44.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 (71) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +57 -0
  2. package/.turbo/turbo-build.log +58 -0
  3. package/CHANGELOG.md +278 -0
  4. package/LICENSE +21 -0
  5. package/README.md +35 -0
  6. package/dist/OnboardingMiniApp.d.ts +17 -0
  7. package/dist/OnboardingMiniApp.d.ts.map +1 -0
  8. package/dist/OnboardingMiniApp.js +63 -0
  9. package/dist/OnboardingMiniApp.js.map +1 -0
  10. package/dist/components/CodeSnippet.d.ts +16 -0
  11. package/dist/components/CodeSnippet.d.ts.map +1 -0
  12. package/dist/components/CodeSnippet.js +50 -0
  13. package/dist/components/CodeSnippet.js.map +1 -0
  14. package/dist/components/JourneyMap.d.ts +17 -0
  15. package/dist/components/JourneyMap.d.ts.map +1 -0
  16. package/dist/components/JourneyMap.js +49 -0
  17. package/dist/components/JourneyMap.js.map +1 -0
  18. package/dist/components/StepChecklist.d.ts +25 -0
  19. package/dist/components/StepChecklist.d.ts.map +1 -0
  20. package/dist/components/StepChecklist.js +80 -0
  21. package/dist/components/StepChecklist.js.map +1 -0
  22. package/dist/components/index.d.ts +4 -0
  23. package/dist/components/index.js +5 -0
  24. package/dist/docs/index.d.ts +1 -0
  25. package/dist/docs/index.js +1 -0
  26. package/dist/docs/learning-journey-ui-onboarding.docblock.d.ts +1 -0
  27. package/dist/docs/learning-journey-ui-onboarding.docblock.js +20 -0
  28. package/dist/docs/learning-journey-ui-onboarding.docblock.js.map +1 -0
  29. package/dist/example.d.ts +33 -0
  30. package/dist/example.d.ts.map +1 -0
  31. package/dist/example.js +35 -0
  32. package/dist/example.js.map +1 -0
  33. package/dist/index.d.ts +12 -0
  34. package/dist/index.js +14 -0
  35. package/dist/views/Overview.d.ts +15 -0
  36. package/dist/views/Overview.d.ts.map +1 -0
  37. package/dist/views/Overview.js +180 -0
  38. package/dist/views/Overview.js.map +1 -0
  39. package/dist/views/Progress.d.ts +11 -0
  40. package/dist/views/Progress.d.ts.map +1 -0
  41. package/dist/views/Progress.js +161 -0
  42. package/dist/views/Progress.js.map +1 -0
  43. package/dist/views/Steps.d.ts +12 -0
  44. package/dist/views/Steps.d.ts.map +1 -0
  45. package/dist/views/Steps.js +92 -0
  46. package/dist/views/Steps.js.map +1 -0
  47. package/dist/views/Timeline.d.ts +11 -0
  48. package/dist/views/Timeline.d.ts.map +1 -0
  49. package/dist/views/Timeline.js +98 -0
  50. package/dist/views/Timeline.js.map +1 -0
  51. package/dist/views/index.d.ts +5 -0
  52. package/dist/views/index.js +6 -0
  53. package/example.ts +1 -0
  54. package/package.json +80 -0
  55. package/src/OnboardingMiniApp.tsx +93 -0
  56. package/src/components/CodeSnippet.tsx +56 -0
  57. package/src/components/JourneyMap.tsx +86 -0
  58. package/src/components/StepChecklist.tsx +136 -0
  59. package/src/components/index.ts +3 -0
  60. package/src/docs/index.ts +1 -0
  61. package/src/docs/learning-journey-ui-onboarding.docblock.ts +18 -0
  62. package/src/example.ts +24 -0
  63. package/src/index.ts +10 -0
  64. package/src/views/Overview.tsx +204 -0
  65. package/src/views/Progress.tsx +186 -0
  66. package/src/views/Steps.tsx +92 -0
  67. package/src/views/Timeline.tsx +141 -0
  68. package/src/views/index.ts +4 -0
  69. package/tsconfig.json +10 -0
  70. package/tsconfig.tsbuildinfo +1 -0
  71. package/tsdown.config.js +17 -0
@@ -0,0 +1,186 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@contractspec/lib.ui-kit-web/ui/card';
9
+ import { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';
10
+ import {
11
+ XpBar,
12
+ BadgeDisplay,
13
+ } from '@contractspec/example.learning-journey-ui-shared';
14
+ import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
15
+
16
+ export function ProgressView({ track, progress }: LearningViewProps) {
17
+ const totalSteps = track.steps.length;
18
+ const completedSteps = progress.completedStepIds.length;
19
+ const percentComplete =
20
+ totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
21
+
22
+ const totalXp =
23
+ track.totalXp ??
24
+ track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) +
25
+ (track.completionRewards?.xpBonus ?? 0);
26
+
27
+ const remainingSteps = totalSteps - completedSteps;
28
+ const estimatedMinutes = remainingSteps * 5;
29
+
30
+ return (
31
+ <div className="space-y-6">
32
+ {/* Main Progress */}
33
+ <Card>
34
+ <CardHeader>
35
+ <CardTitle className="flex items-center gap-2">
36
+ <span>πŸ“ˆ</span>
37
+ <span>Your Progress</span>
38
+ </CardTitle>
39
+ </CardHeader>
40
+ <CardContent className="space-y-6">
41
+ {/* Circular progress indicator */}
42
+ <div className="flex items-center justify-center">
43
+ <div className="relative flex h-40 w-40 items-center justify-center">
44
+ <svg
45
+ className="absolute h-full w-full -rotate-90"
46
+ viewBox="0 0 100 100"
47
+ >
48
+ <circle
49
+ cx="50"
50
+ cy="50"
51
+ r="45"
52
+ fill="none"
53
+ strokeWidth="8"
54
+ className="stroke-muted"
55
+ />
56
+ <circle
57
+ cx="50"
58
+ cy="50"
59
+ r="45"
60
+ fill="none"
61
+ strokeWidth="8"
62
+ strokeLinecap="round"
63
+ strokeDasharray={`${percentComplete * 2.83} 283`}
64
+ className="stroke-blue-500 transition-all duration-500"
65
+ />
66
+ </svg>
67
+ <div className="text-center">
68
+ <div className="text-3xl font-bold">
69
+ {Math.round(percentComplete)}%
70
+ </div>
71
+ <div className="text-muted-foreground text-sm">Complete</div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ {/* Stats row */}
77
+ <div className="grid grid-cols-3 gap-4 text-center">
78
+ <div>
79
+ <div className="text-2xl font-bold text-green-500">
80
+ {completedSteps}
81
+ </div>
82
+ <div className="text-muted-foreground text-sm">Completed</div>
83
+ </div>
84
+ <div>
85
+ <div className="text-2xl font-bold text-orange-500">
86
+ {remainingSteps}
87
+ </div>
88
+ <div className="text-muted-foreground text-sm">Remaining</div>
89
+ </div>
90
+ <div>
91
+ <div className="text-2xl font-bold">{estimatedMinutes}m</div>
92
+ <div className="text-muted-foreground text-sm">Est. Time</div>
93
+ </div>
94
+ </div>
95
+ </CardContent>
96
+ </Card>
97
+
98
+ {/* XP Progress */}
99
+ <Card>
100
+ <CardHeader>
101
+ <CardTitle className="flex items-center gap-2">
102
+ <span>⚑</span>
103
+ <span>Experience Points</span>
104
+ </CardTitle>
105
+ </CardHeader>
106
+ <CardContent className="space-y-4">
107
+ <div className="flex items-baseline gap-2">
108
+ <span className="text-3xl font-bold text-blue-500">
109
+ {progress.xpEarned}
110
+ </span>
111
+ <span className="text-muted-foreground">/ {totalXp} XP</span>
112
+ </div>
113
+ <XpBar
114
+ current={progress.xpEarned}
115
+ max={totalXp}
116
+ showLabel={false}
117
+ size="lg"
118
+ />
119
+ </CardContent>
120
+ </Card>
121
+
122
+ {/* Badges */}
123
+ <Card>
124
+ <CardHeader>
125
+ <CardTitle className="flex items-center gap-2">
126
+ <span>πŸ…</span>
127
+ <span>Achievements</span>
128
+ </CardTitle>
129
+ </CardHeader>
130
+ <CardContent>
131
+ <BadgeDisplay badges={progress.badges} size="lg" />
132
+ {progress.badges.length === 0 &&
133
+ track.completionRewards?.badgeKey && (
134
+ <p className="text-muted-foreground text-sm">
135
+ Complete all steps to earn the "
136
+ {track.completionRewards.badgeKey}" badge!
137
+ </p>
138
+ )}
139
+ </CardContent>
140
+ </Card>
141
+
142
+ {/* Step-by-step breakdown */}
143
+ <Card>
144
+ <CardHeader>
145
+ <CardTitle className="flex items-center gap-2">
146
+ <span>πŸ“‹</span>
147
+ <span>Step Details</span>
148
+ </CardTitle>
149
+ </CardHeader>
150
+ <CardContent>
151
+ <div className="space-y-3">
152
+ {track.steps.map((step, index) => {
153
+ const isCompleted = progress.completedStepIds.includes(step.id);
154
+ const stepProgress = isCompleted ? 100 : 0;
155
+
156
+ return (
157
+ <div key={step.id} className="space-y-1">
158
+ <div className="flex items-center justify-between text-sm">
159
+ <span
160
+ className={
161
+ isCompleted ? 'text-green-500' : 'text-foreground'
162
+ }
163
+ >
164
+ {index + 1}. {step.title}
165
+ </span>
166
+ <span
167
+ className={
168
+ isCompleted ? 'text-green-500' : 'text-muted-foreground'
169
+ }
170
+ >
171
+ {isCompleted ? 'βœ“' : 'Pending'}
172
+ </span>
173
+ </div>
174
+ <Progress value={stepProgress} className="h-1" />
175
+ </div>
176
+ );
177
+ })}
178
+ </div>
179
+ </CardContent>
180
+ </Card>
181
+ </div>
182
+ );
183
+ }
184
+
185
+ // Re-export with correct name
186
+ export { ProgressView as Progress };
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';
5
+ import { StepChecklist } from '../components/StepChecklist';
6
+ import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
7
+
8
+ export function Steps({ track, progress, onStepComplete }: LearningViewProps) {
9
+ const [expandedStepId, setExpandedStepId] = useState<string | null>(() => {
10
+ // Auto-expand first incomplete step
11
+ const firstIncomplete = track.steps.find(
12
+ (s) => !progress.completedStepIds.includes(s.id)
13
+ );
14
+ return firstIncomplete?.id ?? null;
15
+ });
16
+
17
+ const completedSteps = progress.completedStepIds.length;
18
+ const totalSteps = track.steps.length;
19
+ const percentComplete =
20
+ totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
21
+
22
+ const currentStepIndex = track.steps.findIndex(
23
+ (s) => !progress.completedStepIds.includes(s.id)
24
+ );
25
+
26
+ return (
27
+ <div className="space-y-6">
28
+ {/* Progress Header */}
29
+ <div className="space-y-2">
30
+ <div className="flex items-center justify-between">
31
+ <h2 className="text-xl font-bold">Complete Each Step</h2>
32
+ <span className="text-muted-foreground text-sm">
33
+ {completedSteps} / {totalSteps} completed
34
+ </span>
35
+ </div>
36
+ <Progress value={percentComplete} className="h-2" />
37
+ </div>
38
+
39
+ {/* Steps List */}
40
+ <div className="space-y-3">
41
+ {track.steps.map((step, index) => {
42
+ const isCompleted = progress.completedStepIds.includes(step.id);
43
+ const isCurrent = index === currentStepIndex;
44
+
45
+ return (
46
+ <StepChecklist
47
+ key={step.id}
48
+ step={step}
49
+ stepNumber={index + 1}
50
+ isCompleted={isCompleted}
51
+ isCurrent={isCurrent}
52
+ isExpanded={expandedStepId === step.id}
53
+ onToggle={() =>
54
+ setExpandedStepId(expandedStepId === step.id ? null : step.id)
55
+ }
56
+ onComplete={() => {
57
+ onStepComplete?.(step.id);
58
+ // Auto-expand next step
59
+ const nextStep = track.steps[index + 1];
60
+ if (
61
+ nextStep &&
62
+ !progress.completedStepIds.includes(nextStep.id)
63
+ ) {
64
+ setExpandedStepId(nextStep.id);
65
+ }
66
+ }}
67
+ />
68
+ );
69
+ })}
70
+ </div>
71
+
72
+ {/* Completion rewards hint */}
73
+ {track.completionRewards && percentComplete < 100 && (
74
+ <div className="rounded-lg border border-blue-500/30 bg-blue-500/5 p-4">
75
+ <p className="text-sm">
76
+ 🎁 Complete all steps to unlock:
77
+ {track.completionRewards.xpBonus && (
78
+ <span className="ml-2 font-semibold text-blue-500">
79
+ +{track.completionRewards.xpBonus} XP bonus
80
+ </span>
81
+ )}
82
+ {track.completionRewards.badgeKey && (
83
+ <span className="ml-2 font-semibold text-amber-500">
84
+ + "{track.completionRewards.badgeKey}" badge
85
+ </span>
86
+ )}
87
+ </p>
88
+ </div>
89
+ )}
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,141 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardHeader,
7
+ CardTitle,
8
+ } from '@contractspec/lib.ui-kit-web/ui/card';
9
+ import { JourneyMap } from '../components/JourneyMap';
10
+ import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
11
+
12
+ export function Timeline({ track, progress }: LearningViewProps) {
13
+ // Find current step
14
+ const currentStepId =
15
+ track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.id ??
16
+ null;
17
+
18
+ return (
19
+ <div className="space-y-6">
20
+ {/* Header */}
21
+ <div className="text-center">
22
+ <h2 className="text-xl font-bold">Your Learning Journey</h2>
23
+ <p className="text-muted-foreground">
24
+ Follow the path through each surface and feature
25
+ </p>
26
+ </div>
27
+
28
+ {/* Journey Map */}
29
+ <Card>
30
+ <CardHeader>
31
+ <CardTitle className="flex items-center gap-2">
32
+ <span>πŸ—ΊοΈ</span>
33
+ <span>Journey Map</span>
34
+ </CardTitle>
35
+ </CardHeader>
36
+ <CardContent>
37
+ <JourneyMap
38
+ steps={track.steps}
39
+ completedStepIds={progress.completedStepIds}
40
+ currentStepId={currentStepId}
41
+ />
42
+ </CardContent>
43
+ </Card>
44
+
45
+ {/* Detailed Timeline */}
46
+ <Card>
47
+ <CardHeader>
48
+ <CardTitle className="flex items-center gap-2">
49
+ <span>πŸ“</span>
50
+ <span>Step by Step</span>
51
+ </CardTitle>
52
+ </CardHeader>
53
+ <CardContent>
54
+ <div className="relative">
55
+ {/* Vertical line */}
56
+ <div className="bg-border absolute top-0 left-4 h-full w-0.5" />
57
+
58
+ {/* Steps */}
59
+ <div className="space-y-6">
60
+ {track.steps.map((step, index) => {
61
+ const isCompleted = progress.completedStepIds.includes(step.id);
62
+ const isCurrent = step.id === currentStepId;
63
+ const surface = (step.metadata?.surface as string) ?? 'general';
64
+
65
+ return (
66
+ <div key={step.id} className="relative flex gap-4 pl-2">
67
+ {/* Node */}
68
+ <div
69
+ className={`relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2 transition-all ${
70
+ isCompleted
71
+ ? 'border-green-500 bg-green-500 text-white'
72
+ : isCurrent
73
+ ? 'border-blue-500 bg-blue-500 text-white ring-4 ring-blue-500/20'
74
+ : 'border-border bg-background text-muted-foreground'
75
+ }`}
76
+ >
77
+ {isCompleted ? 'βœ“' : index + 1}
78
+ </div>
79
+
80
+ {/* Content */}
81
+ <div className="flex-1 pb-2">
82
+ <div className="rounded-lg border p-4">
83
+ <div className="flex items-start justify-between gap-2">
84
+ <div>
85
+ <div className="flex items-center gap-2">
86
+ <h4
87
+ className={`font-semibold ${
88
+ isCompleted
89
+ ? 'text-green-500'
90
+ : isCurrent
91
+ ? 'text-blue-500'
92
+ : 'text-foreground'
93
+ }`}
94
+ >
95
+ {step.title}
96
+ </h4>
97
+ <span className="bg-muted text-muted-foreground rounded px-2 py-0.5 text-xs">
98
+ {surface}
99
+ </span>
100
+ </div>
101
+ <p className="text-muted-foreground mt-1 text-sm">
102
+ {step.description}
103
+ </p>
104
+ </div>
105
+ {step.xpReward && (
106
+ <span
107
+ className={`shrink-0 rounded-full px-2 py-1 text-xs font-semibold ${
108
+ isCompleted
109
+ ? 'bg-green-500/10 text-green-500'
110
+ : 'bg-muted text-muted-foreground'
111
+ }`}
112
+ >
113
+ +{step.xpReward} XP
114
+ </span>
115
+ )}
116
+ </div>
117
+
118
+ {/* Status */}
119
+ <div className="mt-3 text-xs">
120
+ {isCompleted ? (
121
+ <span className="text-green-500">βœ“ Completed</span>
122
+ ) : isCurrent ? (
123
+ <span className="text-blue-500">β†’ In Progress</span>
124
+ ) : (
125
+ <span className="text-muted-foreground">
126
+ β—‹ Not Started
127
+ </span>
128
+ )}
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ );
134
+ })}
135
+ </div>
136
+ </div>
137
+ </CardContent>
138
+ </Card>
139
+ </div>
140
+ );
141
+ }
@@ -0,0 +1,4 @@
1
+ export { Overview } from './Overview';
2
+ export { Steps } from './Steps';
3
+ export { Progress } from './Progress';
4
+ export { Timeline } from './Timeline';
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@contractspec/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
+ }
10
+