@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.
@@ -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
+
@@ -0,0 +1,5 @@
1
+ export { Overview } from './Overview';
2
+ export { Steps } from './Steps';
3
+ export { Progress } from './Progress';
4
+ export { Timeline } from './Timeline';
5
+
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@lssm/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
+ }
10
+