@lssm/example.learning-journey-ui-onboarding 0.0.0-canary-20251212210835
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 +22 -0
- package/CHANGELOG.md +13 -0
- package/README.md +33 -0
- package/dist/JourneyMap-C6APUTsI.mjs +120 -0
- package/dist/components/index.d.mts +2 -0
- package/dist/components/index.mjs +4 -0
- package/dist/components-Cd2J4qQ5.mjs +48 -0
- package/dist/index-jaGULFWy.d.mts +48 -0
- package/dist/index-v1pn71sC.d.mts +33 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +59 -0
- package/dist/views/index.d.mts +2 -0
- package/dist/views/index.mjs +4 -0
- package/dist/views-DBF-jKGQ.mjs +504 -0
- package/package.json +53 -0
- package/src/OnboardingMiniApp.tsx +94 -0
- package/src/components/CodeSnippet.tsx +58 -0
- package/src/components/JourneyMap.tsx +87 -0
- package/src/components/StepChecklist.tsx +138 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +9 -0
- package/src/views/Overview.tsx +205 -0
- package/src/views/Progress.tsx +184 -0
- package/src/views/Steps.tsx +93 -0
- package/src/views/Timeline.tsx +142 -0
- package/src/views/index.ts +5 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +13 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Progress } from '@lssm/lib.ui-kit-web/ui/progress';
|
|
5
|
+
import { StepChecklist } from '../components/StepChecklist';
|
|
6
|
+
import type { LearningViewProps } from '@lssm/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
|
+
}
|
|
93
|
+
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardHeader,
|
|
7
|
+
CardTitle,
|
|
8
|
+
} from '@lssm/lib.ui-kit-web/ui/card';
|
|
9
|
+
import { JourneyMap } from '../components/JourneyMap';
|
|
10
|
+
import type { LearningViewProps } from '@lssm/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
|
+
}
|
|
142
|
+
|