@contractspec/example.learning-journey-ui-onboarding 3.7.6 → 3.7.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.
- package/.turbo/turbo-build.log +3 -3
- package/AGENTS.md +50 -24
- package/CHANGELOG.md +34 -0
- package/README.md +66 -26
- package/dist/OnboardingMiniApp.js +280 -280
- package/dist/browser/OnboardingMiniApp.js +280 -280
- package/dist/browser/components/CodeSnippet.js +4 -4
- package/dist/browser/components/JourneyMap.js +1 -1
- package/dist/browser/components/StepChecklist.js +6 -6
- package/dist/browser/components/index.js +11 -11
- package/dist/browser/index.js +285 -284
- package/dist/browser/views/Overview.js +14 -14
- package/dist/browser/views/Progress.js +9 -9
- package/dist/browser/views/Steps.js +8 -8
- package/dist/browser/views/Timeline.js +6 -6
- package/dist/browser/views/index.js +276 -276
- package/dist/components/CodeSnippet.js +4 -4
- package/dist/components/JourneyMap.js +1 -1
- package/dist/components/StepChecklist.js +6 -6
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +11 -11
- package/dist/index.d.ts +4 -4
- package/dist/index.js +285 -284
- package/dist/node/OnboardingMiniApp.js +280 -280
- package/dist/node/components/CodeSnippet.js +4 -4
- package/dist/node/components/JourneyMap.js +1 -1
- package/dist/node/components/StepChecklist.js +6 -6
- package/dist/node/components/index.js +11 -11
- package/dist/node/index.js +285 -284
- package/dist/node/views/Overview.js +14 -14
- package/dist/node/views/Progress.js +9 -9
- package/dist/node/views/Steps.js +8 -8
- package/dist/node/views/Timeline.js +6 -6
- package/dist/node/views/index.js +276 -276
- package/dist/views/Overview.js +14 -14
- package/dist/views/Progress.js +9 -9
- package/dist/views/Steps.js +8 -8
- package/dist/views/Timeline.js +6 -6
- package/dist/views/index.d.ts +1 -1
- package/dist/views/index.js +276 -276
- package/package.json +13 -13
- package/src/OnboardingMiniApp.tsx +70 -70
- package/src/components/CodeSnippet.tsx +43 -43
- package/src/components/JourneyMap.tsx +70 -70
- package/src/components/StepChecklist.tsx +118 -118
- package/src/components/index.ts +1 -1
- package/src/docs/learning-journey-ui-onboarding.docblock.ts +11 -11
- package/src/example.ts +25 -25
- package/src/index.ts +5 -6
- package/src/learning-journey-ui-onboarding.feature.ts +12 -12
- package/src/views/Overview.tsx +182 -182
- package/src/views/Progress.tsx +167 -167
- package/src/views/Steps.tsx +79 -79
- package/src/views/Timeline.tsx +126 -126
- package/src/views/index.ts +1 -1
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -13
package/src/views/Progress.tsx
CHANGED
|
@@ -1,185 +1,185 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
|
|
4
|
+
import {
|
|
5
|
+
BadgeDisplay,
|
|
6
|
+
XpBar,
|
|
7
|
+
} from '@contractspec/example.learning-journey-ui-shared';
|
|
3
8
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
Card,
|
|
10
|
+
CardContent,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
8
13
|
} from '@contractspec/lib.ui-kit-web/ui/card';
|
|
9
14
|
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
15
|
|
|
16
16
|
export function ProgressView({ track, progress }: LearningViewProps) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const totalSteps = track.steps.length;
|
|
18
|
+
const completedSteps = progress.completedStepIds.length;
|
|
19
|
+
const percentComplete =
|
|
20
|
+
totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const totalXp =
|
|
23
|
+
track.totalXp ??
|
|
24
|
+
track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) +
|
|
25
|
+
(track.completionRewards?.xpBonus ?? 0);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const remainingSteps = totalSteps - completedSteps;
|
|
28
|
+
const estimatedMinutes = remainingSteps * 5;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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="font-bold text-3xl">
|
|
69
|
+
{Math.round(percentComplete)}%
|
|
70
|
+
</div>
|
|
71
|
+
<div className="text-muted-foreground text-sm">Complete</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
{/* Stats row */}
|
|
77
|
+
<div className="grid grid-cols-3 gap-4 text-center">
|
|
78
|
+
<div>
|
|
79
|
+
<div className="font-bold text-2xl text-green-500">
|
|
80
|
+
{completedSteps}
|
|
81
|
+
</div>
|
|
82
|
+
<div className="text-muted-foreground text-sm">Completed</div>
|
|
83
|
+
</div>
|
|
84
|
+
<div>
|
|
85
|
+
<div className="font-bold text-2xl text-orange-500">
|
|
86
|
+
{remainingSteps}
|
|
87
|
+
</div>
|
|
88
|
+
<div className="text-muted-foreground text-sm">Remaining</div>
|
|
89
|
+
</div>
|
|
90
|
+
<div>
|
|
91
|
+
<div className="font-bold text-2xl">{estimatedMinutes}m</div>
|
|
92
|
+
<div className="text-muted-foreground text-sm">Est. Time</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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="font-bold text-3xl 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
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
183
|
}
|
|
184
184
|
|
|
185
185
|
// Re-export with correct name
|
package/src/views/Steps.tsx
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
|
|
4
4
|
import { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';
|
|
5
|
+
import { useState } from 'react';
|
|
5
6
|
import { StepChecklist } from '../components/StepChecklist';
|
|
6
|
-
import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
|
|
7
7
|
|
|
8
8
|
export function Steps({ track, progress, onStepComplete }: LearningViewProps) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const completedSteps = progress.completedStepIds.length;
|
|
18
|
+
const totalSteps = track.steps.length;
|
|
19
|
+
const percentComplete =
|
|
20
|
+
totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const currentStepIndex = track.steps.findIndex(
|
|
23
|
+
(s) => !progress.completedStepIds.includes(s.id)
|
|
24
|
+
);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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="font-bold text-xl">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
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
92
|
}
|