@contractspec/example.learning-journey-ui-onboarding 0.0.0-canary-20260113170453

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 +494 -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 +7 -0
  30. package/dist/example.d.ts.map +1 -0
  31. package/dist/example.js +42 -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 +77 -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 +31 -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,204 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@contractspec/lib.design-system';
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@contractspec/lib.ui-kit-web/ui/card';
10
+ import { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';
11
+ import { XpBar } from '@contractspec/example.learning-journey-ui-shared';
12
+ import type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';
13
+
14
+ interface OnboardingOverviewProps extends LearningViewProps {
15
+ onStart?: () => void;
16
+ }
17
+
18
+ export function Overview({
19
+ track,
20
+ progress,
21
+ onStart,
22
+ }: OnboardingOverviewProps) {
23
+ const totalSteps = track.steps.length;
24
+ const completedSteps = progress.completedStepIds.length;
25
+ const percentComplete =
26
+ totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
27
+ const isComplete = completedSteps === totalSteps;
28
+
29
+ // Estimate time remaining (rough: 5 min per step)
30
+ const remainingSteps = totalSteps - completedSteps;
31
+ const estimatedMinutes = remainingSteps * 5;
32
+
33
+ const totalXp =
34
+ track.totalXp ??
35
+ track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) +
36
+ (track.completionRewards?.xpBonus ?? 0);
37
+
38
+ return (
39
+ <div className="space-y-6">
40
+ {/* Welcome Banner */}
41
+ <Card className="overflow-hidden bg-gradient-to-r from-blue-500/10 via-violet-500/10 to-purple-500/10">
42
+ <CardContent className="p-8">
43
+ <div className="flex flex-col items-center gap-6 text-center md:flex-row md:text-left">
44
+ <div className="flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-br from-blue-500 to-violet-600 text-4xl shadow-lg">
45
+ {isComplete ? 'πŸŽ‰' : 'πŸš€'}
46
+ </div>
47
+ <div className="flex-1">
48
+ <h1 className="text-2xl font-bold">{track.name}</h1>
49
+ <p className="text-muted-foreground mt-1 max-w-2xl">
50
+ {track.description}
51
+ </p>
52
+ {!isComplete && (
53
+ <p className="text-muted-foreground mt-3 text-sm">
54
+ ⏱️ Estimated time:{' '}
55
+ {estimatedMinutes > 0
56
+ ? `~${estimatedMinutes} minutes`
57
+ : 'Less than a minute'}
58
+ </p>
59
+ )}
60
+ </div>
61
+ {!isComplete && (
62
+ <Button size="lg" onClick={onStart}>
63
+ {completedSteps > 0 ? 'Continue' : 'Get Started'}
64
+ </Button>
65
+ )}
66
+ </div>
67
+ </CardContent>
68
+ </Card>
69
+
70
+ {/* Progress Overview */}
71
+ <div className="grid gap-4 md:grid-cols-3">
72
+ <Card>
73
+ <CardHeader className="pb-2">
74
+ <CardTitle className="text-muted-foreground text-sm font-medium">
75
+ Progress
76
+ </CardTitle>
77
+ </CardHeader>
78
+ <CardContent>
79
+ <div className="text-3xl font-bold">
80
+ {Math.round(percentComplete)}%
81
+ </div>
82
+ <Progress value={percentComplete} className="mt-2 h-2" />
83
+ <p className="text-muted-foreground mt-2 text-sm">
84
+ {completedSteps} of {totalSteps} steps completed
85
+ </p>
86
+ </CardContent>
87
+ </Card>
88
+
89
+ <Card>
90
+ <CardHeader className="pb-2">
91
+ <CardTitle className="text-muted-foreground text-sm font-medium">
92
+ XP Earned
93
+ </CardTitle>
94
+ </CardHeader>
95
+ <CardContent>
96
+ <div className="text-3xl font-bold text-blue-500">
97
+ {progress.xpEarned}
98
+ </div>
99
+ <XpBar
100
+ current={progress.xpEarned}
101
+ max={totalXp}
102
+ showLabel={false}
103
+ size="sm"
104
+ />
105
+ </CardContent>
106
+ </Card>
107
+
108
+ <Card>
109
+ <CardHeader className="pb-2">
110
+ <CardTitle className="text-muted-foreground text-sm font-medium">
111
+ Time Remaining
112
+ </CardTitle>
113
+ </CardHeader>
114
+ <CardContent>
115
+ <div className="text-3xl font-bold">
116
+ {isComplete ? 'βœ“' : `~${estimatedMinutes}m`}
117
+ </div>
118
+ <p className="text-muted-foreground mt-2 text-sm">
119
+ {isComplete ? 'All done!' : `${remainingSteps} steps to go`}
120
+ </p>
121
+ </CardContent>
122
+ </Card>
123
+ </div>
124
+
125
+ {/* Step Preview */}
126
+ <Card>
127
+ <CardHeader>
128
+ <CardTitle className="flex items-center gap-2">
129
+ <span>πŸ“‹</span>
130
+ <span>Your Journey</span>
131
+ </CardTitle>
132
+ </CardHeader>
133
+ <CardContent>
134
+ <div className="space-y-3">
135
+ {track.steps.map((step, index) => {
136
+ const isStepCompleted = progress.completedStepIds.includes(
137
+ step.id
138
+ );
139
+ const isCurrent =
140
+ !isStepCompleted &&
141
+ track.steps
142
+ .slice(0, index)
143
+ .every((s) => progress.completedStepIds.includes(s.id));
144
+
145
+ return (
146
+ <div
147
+ key={step.id}
148
+ className="flex items-center gap-4 rounded-lg border p-3"
149
+ >
150
+ <div
151
+ className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm font-semibold ${
152
+ isStepCompleted
153
+ ? 'bg-green-500 text-white'
154
+ : isCurrent
155
+ ? 'bg-blue-500 text-white'
156
+ : 'bg-muted text-muted-foreground'
157
+ }`}
158
+ >
159
+ {isStepCompleted ? 'βœ“' : index + 1}
160
+ </div>
161
+ <div className="min-w-0 flex-1">
162
+ <p
163
+ className={`font-medium ${
164
+ isStepCompleted
165
+ ? 'text-green-500'
166
+ : isCurrent
167
+ ? 'text-foreground'
168
+ : 'text-muted-foreground'
169
+ }`}
170
+ >
171
+ {step.title}
172
+ </p>
173
+ </div>
174
+ {step.xpReward && (
175
+ <span className="text-muted-foreground text-sm">
176
+ +{step.xpReward} XP
177
+ </span>
178
+ )}
179
+ </div>
180
+ );
181
+ })}
182
+ </div>
183
+ </CardContent>
184
+ </Card>
185
+
186
+ {/* Completion Message */}
187
+ {isComplete && (
188
+ <Card className="border-green-500/50 bg-green-500/5">
189
+ <CardContent className="flex items-center gap-4 p-6">
190
+ <div className="text-4xl">πŸŽ‰</div>
191
+ <div>
192
+ <h3 className="text-lg font-semibold text-green-500">
193
+ Onboarding Complete!
194
+ </h3>
195
+ <p className="text-muted-foreground">
196
+ You've completed all {totalSteps} steps. Welcome aboard!
197
+ </p>
198
+ </div>
199
+ </CardContent>
200
+ </Card>
201
+ )}
202
+ </div>
203
+ );
204
+ }
@@ -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
+