@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,161 @@
1
+ 'use client';
2
+
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@contractspec/lib.ui-kit-web/ui/card";
4
+ import { BadgeDisplay, XpBar } from "@contractspec/example.learning-journey-ui-shared";
5
+ import { Progress } from "@contractspec/lib.ui-kit-web/ui/progress";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/views/Progress.tsx
9
+ function ProgressView({ track, progress }) {
10
+ const totalSteps = track.steps.length;
11
+ const completedSteps = progress.completedStepIds.length;
12
+ const percentComplete = totalSteps > 0 ? completedSteps / totalSteps * 100 : 0;
13
+ const totalXp = track.totalXp ?? track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) + (track.completionRewards?.xpBonus ?? 0);
14
+ const remainingSteps = totalSteps - completedSteps;
15
+ const estimatedMinutes = remainingSteps * 5;
16
+ return /* @__PURE__ */ jsxs("div", {
17
+ className: "space-y-6",
18
+ children: [
19
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
20
+ className: "flex items-center gap-2",
21
+ children: [/* @__PURE__ */ jsx("span", { children: "📈" }), /* @__PURE__ */ jsx("span", { children: "Your Progress" })]
22
+ }) }), /* @__PURE__ */ jsxs(CardContent, {
23
+ className: "space-y-6",
24
+ children: [/* @__PURE__ */ jsx("div", {
25
+ className: "flex items-center justify-center",
26
+ children: /* @__PURE__ */ jsxs("div", {
27
+ className: "relative flex h-40 w-40 items-center justify-center",
28
+ children: [/* @__PURE__ */ jsxs("svg", {
29
+ className: "absolute h-full w-full -rotate-90",
30
+ viewBox: "0 0 100 100",
31
+ children: [/* @__PURE__ */ jsx("circle", {
32
+ cx: "50",
33
+ cy: "50",
34
+ r: "45",
35
+ fill: "none",
36
+ strokeWidth: "8",
37
+ className: "stroke-muted"
38
+ }), /* @__PURE__ */ jsx("circle", {
39
+ cx: "50",
40
+ cy: "50",
41
+ r: "45",
42
+ fill: "none",
43
+ strokeWidth: "8",
44
+ strokeLinecap: "round",
45
+ strokeDasharray: `${percentComplete * 2.83} 283`,
46
+ className: "stroke-blue-500 transition-all duration-500"
47
+ })]
48
+ }), /* @__PURE__ */ jsxs("div", {
49
+ className: "text-center",
50
+ children: [/* @__PURE__ */ jsxs("div", {
51
+ className: "text-3xl font-bold",
52
+ children: [Math.round(percentComplete), "%"]
53
+ }), /* @__PURE__ */ jsx("div", {
54
+ className: "text-muted-foreground text-sm",
55
+ children: "Complete"
56
+ })]
57
+ })]
58
+ })
59
+ }), /* @__PURE__ */ jsxs("div", {
60
+ className: "grid grid-cols-3 gap-4 text-center",
61
+ children: [
62
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
63
+ className: "text-2xl font-bold text-green-500",
64
+ children: completedSteps
65
+ }), /* @__PURE__ */ jsx("div", {
66
+ className: "text-muted-foreground text-sm",
67
+ children: "Completed"
68
+ })] }),
69
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
70
+ className: "text-2xl font-bold text-orange-500",
71
+ children: remainingSteps
72
+ }), /* @__PURE__ */ jsx("div", {
73
+ className: "text-muted-foreground text-sm",
74
+ children: "Remaining"
75
+ })] }),
76
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
77
+ className: "text-2xl font-bold",
78
+ children: [estimatedMinutes, "m"]
79
+ }), /* @__PURE__ */ jsx("div", {
80
+ className: "text-muted-foreground text-sm",
81
+ children: "Est. Time"
82
+ })] })
83
+ ]
84
+ })]
85
+ })] }),
86
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
87
+ className: "flex items-center gap-2",
88
+ children: [/* @__PURE__ */ jsx("span", { children: "⚡" }), /* @__PURE__ */ jsx("span", { children: "Experience Points" })]
89
+ }) }), /* @__PURE__ */ jsxs(CardContent, {
90
+ className: "space-y-4",
91
+ children: [/* @__PURE__ */ jsxs("div", {
92
+ className: "flex items-baseline gap-2",
93
+ children: [/* @__PURE__ */ jsx("span", {
94
+ className: "text-3xl font-bold text-blue-500",
95
+ children: progress.xpEarned
96
+ }), /* @__PURE__ */ jsxs("span", {
97
+ className: "text-muted-foreground",
98
+ children: [
99
+ "/ ",
100
+ totalXp,
101
+ " XP"
102
+ ]
103
+ })]
104
+ }), /* @__PURE__ */ jsx(XpBar, {
105
+ current: progress.xpEarned,
106
+ max: totalXp,
107
+ showLabel: false,
108
+ size: "lg"
109
+ })]
110
+ })] }),
111
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
112
+ className: "flex items-center gap-2",
113
+ children: [/* @__PURE__ */ jsx("span", { children: "🏅" }), /* @__PURE__ */ jsx("span", { children: "Achievements" })]
114
+ }) }), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsx(BadgeDisplay, {
115
+ badges: progress.badges,
116
+ size: "lg"
117
+ }), progress.badges.length === 0 && track.completionRewards?.badgeKey && /* @__PURE__ */ jsxs("p", {
118
+ className: "text-muted-foreground text-sm",
119
+ children: [
120
+ "Complete all steps to earn the \"",
121
+ track.completionRewards.badgeKey,
122
+ "\" badge!"
123
+ ]
124
+ })] })] }),
125
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
126
+ className: "flex items-center gap-2",
127
+ children: [/* @__PURE__ */ jsx("span", { children: "📋" }), /* @__PURE__ */ jsx("span", { children: "Step Details" })]
128
+ }) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", {
129
+ className: "space-y-3",
130
+ children: track.steps.map((step, index) => {
131
+ const isCompleted = progress.completedStepIds.includes(step.id);
132
+ const stepProgress = isCompleted ? 100 : 0;
133
+ return /* @__PURE__ */ jsxs("div", {
134
+ className: "space-y-1",
135
+ children: [/* @__PURE__ */ jsxs("div", {
136
+ className: "flex items-center justify-between text-sm",
137
+ children: [/* @__PURE__ */ jsxs("span", {
138
+ className: isCompleted ? "text-green-500" : "text-foreground",
139
+ children: [
140
+ index + 1,
141
+ ". ",
142
+ step.title
143
+ ]
144
+ }), /* @__PURE__ */ jsx("span", {
145
+ className: isCompleted ? "text-green-500" : "text-muted-foreground",
146
+ children: isCompleted ? "✓" : "Pending"
147
+ })]
148
+ }), /* @__PURE__ */ jsx(Progress, {
149
+ value: stepProgress,
150
+ className: "h-1"
151
+ })]
152
+ }, step.id);
153
+ })
154
+ }) })] })
155
+ ]
156
+ });
157
+ }
158
+
159
+ //#endregion
160
+ export { ProgressView as Progress, ProgressView };
161
+ //# sourceMappingURL=Progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Progress.js","names":[],"sources":["../../src/views/Progress.tsx"],"sourcesContent":["'use client';\n\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from '@contractspec/lib.ui-kit-web/ui/card';\nimport { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';\nimport {\n XpBar,\n BadgeDisplay,\n} from '@contractspec/example.learning-journey-ui-shared';\nimport type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';\n\nexport function ProgressView({ track, progress }: LearningViewProps) {\n const totalSteps = track.steps.length;\n const completedSteps = progress.completedStepIds.length;\n const percentComplete =\n totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;\n\n const totalXp =\n track.totalXp ??\n track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0) +\n (track.completionRewards?.xpBonus ?? 0);\n\n const remainingSteps = totalSteps - completedSteps;\n const estimatedMinutes = remainingSteps * 5;\n\n return (\n <div className=\"space-y-6\">\n {/* Main Progress */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>📈</span>\n <span>Your Progress</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n {/* Circular progress indicator */}\n <div className=\"flex items-center justify-center\">\n <div className=\"relative flex h-40 w-40 items-center justify-center\">\n <svg\n className=\"absolute h-full w-full -rotate-90\"\n viewBox=\"0 0 100 100\"\n >\n <circle\n cx=\"50\"\n cy=\"50\"\n r=\"45\"\n fill=\"none\"\n strokeWidth=\"8\"\n className=\"stroke-muted\"\n />\n <circle\n cx=\"50\"\n cy=\"50\"\n r=\"45\"\n fill=\"none\"\n strokeWidth=\"8\"\n strokeLinecap=\"round\"\n strokeDasharray={`${percentComplete * 2.83} 283`}\n className=\"stroke-blue-500 transition-all duration-500\"\n />\n </svg>\n <div className=\"text-center\">\n <div className=\"text-3xl font-bold\">\n {Math.round(percentComplete)}%\n </div>\n <div className=\"text-muted-foreground text-sm\">Complete</div>\n </div>\n </div>\n </div>\n\n {/* Stats row */}\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div>\n <div className=\"text-2xl font-bold text-green-500\">\n {completedSteps}\n </div>\n <div className=\"text-muted-foreground text-sm\">Completed</div>\n </div>\n <div>\n <div className=\"text-2xl font-bold text-orange-500\">\n {remainingSteps}\n </div>\n <div className=\"text-muted-foreground text-sm\">Remaining</div>\n </div>\n <div>\n <div className=\"text-2xl font-bold\">{estimatedMinutes}m</div>\n <div className=\"text-muted-foreground text-sm\">Est. Time</div>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* XP Progress */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>⚡</span>\n <span>Experience Points</span>\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-3xl font-bold text-blue-500\">\n {progress.xpEarned}\n </span>\n <span className=\"text-muted-foreground\">/ {totalXp} XP</span>\n </div>\n <XpBar\n current={progress.xpEarned}\n max={totalXp}\n showLabel={false}\n size=\"lg\"\n />\n </CardContent>\n </Card>\n\n {/* Badges */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>🏅</span>\n <span>Achievements</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n <BadgeDisplay badges={progress.badges} size=\"lg\" />\n {progress.badges.length === 0 &&\n track.completionRewards?.badgeKey && (\n <p className=\"text-muted-foreground text-sm\">\n Complete all steps to earn the \"\n {track.completionRewards.badgeKey}\" badge!\n </p>\n )}\n </CardContent>\n </Card>\n\n {/* Step-by-step breakdown */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>📋</span>\n <span>Step Details</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {track.steps.map((step, index) => {\n const isCompleted = progress.completedStepIds.includes(step.id);\n const stepProgress = isCompleted ? 100 : 0;\n\n return (\n <div key={step.id} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span\n className={\n isCompleted ? 'text-green-500' : 'text-foreground'\n }\n >\n {index + 1}. {step.title}\n </span>\n <span\n className={\n isCompleted ? 'text-green-500' : 'text-muted-foreground'\n }\n >\n {isCompleted ? '✓' : 'Pending'}\n </span>\n </div>\n <Progress value={stepProgress} className=\"h-1\" />\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n\n// Re-export with correct name\nexport { ProgressView as Progress };\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { LearningViewProps } from "@contractspec/example.learning-journey-ui-shared";
2
+ import * as react_jsx_runtime5 from "react/jsx-runtime";
3
+
4
+ //#region src/views/Steps.d.ts
5
+ declare function Steps({
6
+ track,
7
+ progress,
8
+ onStepComplete
9
+ }: LearningViewProps): react_jsx_runtime5.JSX.Element;
10
+ //#endregion
11
+ export { Steps };
12
+ //# sourceMappingURL=Steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Steps.d.ts","names":[],"sources":["../../src/views/Steps.tsx"],"sourcesContent":[],"mappings":";;;;iBAOgB,KAAA;;;;GAA2C,oBAAiB,kBAAA,CAAA,GAAA,CAAA"}
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import { StepChecklist } from "../components/StepChecklist.js";
4
+ import { useState } from "react";
5
+ import { Progress } from "@contractspec/lib.ui-kit-web/ui/progress";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/views/Steps.tsx
9
+ function Steps({ track, progress, onStepComplete }) {
10
+ const [expandedStepId, setExpandedStepId] = useState(() => {
11
+ return track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.id ?? null;
12
+ });
13
+ const completedSteps = progress.completedStepIds.length;
14
+ const totalSteps = track.steps.length;
15
+ const percentComplete = totalSteps > 0 ? completedSteps / totalSteps * 100 : 0;
16
+ const currentStepIndex = track.steps.findIndex((s) => !progress.completedStepIds.includes(s.id));
17
+ return /* @__PURE__ */ jsxs("div", {
18
+ className: "space-y-6",
19
+ children: [
20
+ /* @__PURE__ */ jsxs("div", {
21
+ className: "space-y-2",
22
+ children: [/* @__PURE__ */ jsxs("div", {
23
+ className: "flex items-center justify-between",
24
+ children: [/* @__PURE__ */ jsx("h2", {
25
+ className: "text-xl font-bold",
26
+ children: "Complete Each Step"
27
+ }), /* @__PURE__ */ jsxs("span", {
28
+ className: "text-muted-foreground text-sm",
29
+ children: [
30
+ completedSteps,
31
+ " / ",
32
+ totalSteps,
33
+ " completed"
34
+ ]
35
+ })]
36
+ }), /* @__PURE__ */ jsx(Progress, {
37
+ value: percentComplete,
38
+ className: "h-2"
39
+ })]
40
+ }),
41
+ /* @__PURE__ */ jsx("div", {
42
+ className: "space-y-3",
43
+ children: track.steps.map((step, index) => {
44
+ const isCompleted = progress.completedStepIds.includes(step.id);
45
+ const isCurrent = index === currentStepIndex;
46
+ return /* @__PURE__ */ jsx(StepChecklist, {
47
+ step,
48
+ stepNumber: index + 1,
49
+ isCompleted,
50
+ isCurrent,
51
+ isExpanded: expandedStepId === step.id,
52
+ onToggle: () => setExpandedStepId(expandedStepId === step.id ? null : step.id),
53
+ onComplete: () => {
54
+ onStepComplete?.(step.id);
55
+ const nextStep = track.steps[index + 1];
56
+ if (nextStep && !progress.completedStepIds.includes(nextStep.id)) setExpandedStepId(nextStep.id);
57
+ }
58
+ }, step.id);
59
+ })
60
+ }),
61
+ track.completionRewards && percentComplete < 100 && /* @__PURE__ */ jsx("div", {
62
+ className: "rounded-lg border border-blue-500/30 bg-blue-500/5 p-4",
63
+ children: /* @__PURE__ */ jsxs("p", {
64
+ className: "text-sm",
65
+ children: [
66
+ "🎁 Complete all steps to unlock:",
67
+ track.completionRewards.xpBonus && /* @__PURE__ */ jsxs("span", {
68
+ className: "ml-2 font-semibold text-blue-500",
69
+ children: [
70
+ "+",
71
+ track.completionRewards.xpBonus,
72
+ " XP bonus"
73
+ ]
74
+ }),
75
+ track.completionRewards.badgeKey && /* @__PURE__ */ jsxs("span", {
76
+ className: "ml-2 font-semibold text-amber-500",
77
+ children: [
78
+ "+ \"",
79
+ track.completionRewards.badgeKey,
80
+ "\" badge"
81
+ ]
82
+ })
83
+ ]
84
+ })
85
+ })
86
+ ]
87
+ });
88
+ }
89
+
90
+ //#endregion
91
+ export { Steps };
92
+ //# sourceMappingURL=Steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Steps.js","names":[],"sources":["../../src/views/Steps.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { Progress } from '@contractspec/lib.ui-kit-web/ui/progress';\nimport { StepChecklist } from '../components/StepChecklist';\nimport type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';\n\nexport function Steps({ track, progress, onStepComplete }: LearningViewProps) {\n const [expandedStepId, setExpandedStepId] = useState<string | null>(() => {\n // Auto-expand first incomplete step\n const firstIncomplete = track.steps.find(\n (s) => !progress.completedStepIds.includes(s.id)\n );\n return firstIncomplete?.id ?? null;\n });\n\n const completedSteps = progress.completedStepIds.length;\n const totalSteps = track.steps.length;\n const percentComplete =\n totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;\n\n const currentStepIndex = track.steps.findIndex(\n (s) => !progress.completedStepIds.includes(s.id)\n );\n\n return (\n <div className=\"space-y-6\">\n {/* Progress Header */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-xl font-bold\">Complete Each Step</h2>\n <span className=\"text-muted-foreground text-sm\">\n {completedSteps} / {totalSteps} completed\n </span>\n </div>\n <Progress value={percentComplete} className=\"h-2\" />\n </div>\n\n {/* Steps List */}\n <div className=\"space-y-3\">\n {track.steps.map((step, index) => {\n const isCompleted = progress.completedStepIds.includes(step.id);\n const isCurrent = index === currentStepIndex;\n\n return (\n <StepChecklist\n key={step.id}\n step={step}\n stepNumber={index + 1}\n isCompleted={isCompleted}\n isCurrent={isCurrent}\n isExpanded={expandedStepId === step.id}\n onToggle={() =>\n setExpandedStepId(expandedStepId === step.id ? null : step.id)\n }\n onComplete={() => {\n onStepComplete?.(step.id);\n // Auto-expand next step\n const nextStep = track.steps[index + 1];\n if (\n nextStep &&\n !progress.completedStepIds.includes(nextStep.id)\n ) {\n setExpandedStepId(nextStep.id);\n }\n }}\n />\n );\n })}\n </div>\n\n {/* Completion rewards hint */}\n {track.completionRewards && percentComplete < 100 && (\n <div className=\"rounded-lg border border-blue-500/30 bg-blue-500/5 p-4\">\n <p className=\"text-sm\">\n 🎁 Complete all steps to unlock:\n {track.completionRewards.xpBonus && (\n <span className=\"ml-2 font-semibold text-blue-500\">\n +{track.completionRewards.xpBonus} XP bonus\n </span>\n )}\n {track.completionRewards.badgeKey && (\n <span className=\"ml-2 font-semibold text-amber-500\">\n + \"{track.completionRewards.badgeKey}\" badge\n </span>\n )}\n </p>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,MAAM,EAAE,OAAO,UAAU,kBAAqC;CAC5E,MAAM,CAAC,gBAAgB,qBAAqB,eAA8B;AAKxE,SAHwB,MAAM,MAAM,MACjC,MAAM,CAAC,SAAS,iBAAiB,SAAS,EAAE,GAAG,CACjD,EACuB,MAAM;GAC9B;CAEF,MAAM,iBAAiB,SAAS,iBAAiB;CACjD,MAAM,aAAa,MAAM,MAAM;CAC/B,MAAM,kBACJ,aAAa,IAAK,iBAAiB,aAAc,MAAM;CAEzD,MAAM,mBAAmB,MAAM,MAAM,WAClC,MAAM,CAAC,SAAS,iBAAiB,SAAS,EAAE,GAAG,CACjD;AAED,QACE,qBAAC;EAAI,WAAU;;GAEb,qBAAC;IAAI,WAAU;eACb,qBAAC;KAAI,WAAU;gBACb,oBAAC;MAAG,WAAU;gBAAoB;OAAuB,EACzD,qBAAC;MAAK,WAAU;;OACb;OAAe;OAAI;OAAW;;OAC1B;MACH,EACN,oBAAC;KAAS,OAAO;KAAiB,WAAU;MAAQ;KAChD;GAGN,oBAAC;IAAI,WAAU;cACZ,MAAM,MAAM,KAAK,MAAM,UAAU;KAChC,MAAM,cAAc,SAAS,iBAAiB,SAAS,KAAK,GAAG;KAC/D,MAAM,YAAY,UAAU;AAE5B,YACE,oBAAC;MAEO;MACN,YAAY,QAAQ;MACP;MACF;MACX,YAAY,mBAAmB,KAAK;MACpC,gBACE,kBAAkB,mBAAmB,KAAK,KAAK,OAAO,KAAK,GAAG;MAEhE,kBAAkB;AAChB,wBAAiB,KAAK,GAAG;OAEzB,MAAM,WAAW,MAAM,MAAM,QAAQ;AACrC,WACE,YACA,CAAC,SAAS,iBAAiB,SAAS,SAAS,GAAG,CAEhD,mBAAkB,SAAS,GAAG;;QAjB7B,KAAK,GAoBV;MAEJ;KACE;GAGL,MAAM,qBAAqB,kBAAkB,OAC5C,oBAAC;IAAI,WAAU;cACb,qBAAC;KAAE,WAAU;;MAAU;MAEpB,MAAM,kBAAkB,WACvB,qBAAC;OAAK,WAAU;;QAAmC;QAC/C,MAAM,kBAAkB;QAAQ;;QAC7B;MAER,MAAM,kBAAkB,YACvB,qBAAC;OAAK,WAAU;;QAAoC;QAC9C,MAAM,kBAAkB;QAAS;;QAChC;;MAEP;KACA;;GAEJ"}
@@ -0,0 +1,11 @@
1
+ import { LearningViewProps } from "@contractspec/example.learning-journey-ui-shared";
2
+ import * as react_jsx_runtime6 from "react/jsx-runtime";
3
+
4
+ //#region src/views/Timeline.d.ts
5
+ declare function Timeline({
6
+ track,
7
+ progress
8
+ }: LearningViewProps): react_jsx_runtime6.JSX.Element;
9
+ //#endregion
10
+ export { Timeline };
11
+ //# sourceMappingURL=Timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Timeline.d.ts","names":[],"sources":["../../src/views/Timeline.tsx"],"sourcesContent":[],"mappings":";;;;iBAWgB,QAAA;;;GAA8B,oBAAiB,kBAAA,CAAA,GAAA,CAAA"}
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import { JourneyMap } from "../components/JourneyMap.js";
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@contractspec/lib.ui-kit-web/ui/card";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/views/Timeline.tsx
8
+ function Timeline({ track, progress }) {
9
+ const currentStepId = track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.id ?? null;
10
+ return /* @__PURE__ */ jsxs("div", {
11
+ className: "space-y-6",
12
+ children: [
13
+ /* @__PURE__ */ jsxs("div", {
14
+ className: "text-center",
15
+ children: [/* @__PURE__ */ jsx("h2", {
16
+ className: "text-xl font-bold",
17
+ children: "Your Learning Journey"
18
+ }), /* @__PURE__ */ jsx("p", {
19
+ className: "text-muted-foreground",
20
+ children: "Follow the path through each surface and feature"
21
+ })]
22
+ }),
23
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
24
+ className: "flex items-center gap-2",
25
+ children: [/* @__PURE__ */ jsx("span", { children: "🗺️" }), /* @__PURE__ */ jsx("span", { children: "Journey Map" })]
26
+ }) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(JourneyMap, {
27
+ steps: track.steps,
28
+ completedStepIds: progress.completedStepIds,
29
+ currentStepId
30
+ }) })] }),
31
+ /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
32
+ className: "flex items-center gap-2",
33
+ children: [/* @__PURE__ */ jsx("span", { children: "📍" }), /* @__PURE__ */ jsx("span", { children: "Step by Step" })]
34
+ }) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", {
35
+ className: "relative",
36
+ children: [/* @__PURE__ */ jsx("div", { className: "bg-border absolute top-0 left-4 h-full w-0.5" }), /* @__PURE__ */ jsx("div", {
37
+ className: "space-y-6",
38
+ children: track.steps.map((step, index) => {
39
+ const isCompleted = progress.completedStepIds.includes(step.id);
40
+ const isCurrent = step.id === currentStepId;
41
+ const surface = step.metadata?.surface ?? "general";
42
+ return /* @__PURE__ */ jsxs("div", {
43
+ className: "relative flex gap-4 pl-2",
44
+ children: [/* @__PURE__ */ jsx("div", {
45
+ className: `relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2 transition-all ${isCompleted ? "border-green-500 bg-green-500 text-white" : isCurrent ? "border-blue-500 bg-blue-500 text-white ring-4 ring-blue-500/20" : "border-border bg-background text-muted-foreground"}`,
46
+ children: isCompleted ? "✓" : index + 1
47
+ }), /* @__PURE__ */ jsx("div", {
48
+ className: "flex-1 pb-2",
49
+ children: /* @__PURE__ */ jsxs("div", {
50
+ className: "rounded-lg border p-4",
51
+ children: [/* @__PURE__ */ jsxs("div", {
52
+ className: "flex items-start justify-between gap-2",
53
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
54
+ className: "flex items-center gap-2",
55
+ children: [/* @__PURE__ */ jsx("h4", {
56
+ className: `font-semibold ${isCompleted ? "text-green-500" : isCurrent ? "text-blue-500" : "text-foreground"}`,
57
+ children: step.title
58
+ }), /* @__PURE__ */ jsx("span", {
59
+ className: "bg-muted text-muted-foreground rounded px-2 py-0.5 text-xs",
60
+ children: surface
61
+ })]
62
+ }), /* @__PURE__ */ jsx("p", {
63
+ className: "text-muted-foreground mt-1 text-sm",
64
+ children: step.description
65
+ })] }), step.xpReward && /* @__PURE__ */ jsxs("span", {
66
+ className: `shrink-0 rounded-full px-2 py-1 text-xs font-semibold ${isCompleted ? "bg-green-500/10 text-green-500" : "bg-muted text-muted-foreground"}`,
67
+ children: [
68
+ "+",
69
+ step.xpReward,
70
+ " XP"
71
+ ]
72
+ })]
73
+ }), /* @__PURE__ */ jsx("div", {
74
+ className: "mt-3 text-xs",
75
+ children: isCompleted ? /* @__PURE__ */ jsx("span", {
76
+ className: "text-green-500",
77
+ children: "✓ Completed"
78
+ }) : isCurrent ? /* @__PURE__ */ jsx("span", {
79
+ className: "text-blue-500",
80
+ children: "→ In Progress"
81
+ }) : /* @__PURE__ */ jsx("span", {
82
+ className: "text-muted-foreground",
83
+ children: "○ Not Started"
84
+ })
85
+ })]
86
+ })
87
+ })]
88
+ }, step.id);
89
+ })
90
+ })]
91
+ }) })] })
92
+ ]
93
+ });
94
+ }
95
+
96
+ //#endregion
97
+ export { Timeline };
98
+ //# sourceMappingURL=Timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Timeline.js","names":[],"sources":["../../src/views/Timeline.tsx"],"sourcesContent":["'use client';\n\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n} from '@contractspec/lib.ui-kit-web/ui/card';\nimport { JourneyMap } from '../components/JourneyMap';\nimport type { LearningViewProps } from '@contractspec/example.learning-journey-ui-shared';\n\nexport function Timeline({ track, progress }: LearningViewProps) {\n // Find current step\n const currentStepId =\n track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.id ??\n null;\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"text-center\">\n <h2 className=\"text-xl font-bold\">Your Learning Journey</h2>\n <p className=\"text-muted-foreground\">\n Follow the path through each surface and feature\n </p>\n </div>\n\n {/* Journey Map */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>🗺️</span>\n <span>Journey Map</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n <JourneyMap\n steps={track.steps}\n completedStepIds={progress.completedStepIds}\n currentStepId={currentStepId}\n />\n </CardContent>\n </Card>\n\n {/* Detailed Timeline */}\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <span>📍</span>\n <span>Step by Step</span>\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"relative\">\n {/* Vertical line */}\n <div className=\"bg-border absolute top-0 left-4 h-full w-0.5\" />\n\n {/* Steps */}\n <div className=\"space-y-6\">\n {track.steps.map((step, index) => {\n const isCompleted = progress.completedStepIds.includes(step.id);\n const isCurrent = step.id === currentStepId;\n const surface = (step.metadata?.surface as string) ?? 'general';\n\n return (\n <div key={step.id} className=\"relative flex gap-4 pl-2\">\n {/* Node */}\n <div\n className={`relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border-2 transition-all ${\n isCompleted\n ? 'border-green-500 bg-green-500 text-white'\n : isCurrent\n ? 'border-blue-500 bg-blue-500 text-white ring-4 ring-blue-500/20'\n : 'border-border bg-background text-muted-foreground'\n }`}\n >\n {isCompleted ? '✓' : index + 1}\n </div>\n\n {/* Content */}\n <div className=\"flex-1 pb-2\">\n <div className=\"rounded-lg border p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h4\n className={`font-semibold ${\n isCompleted\n ? 'text-green-500'\n : isCurrent\n ? 'text-blue-500'\n : 'text-foreground'\n }`}\n >\n {step.title}\n </h4>\n <span className=\"bg-muted text-muted-foreground rounded px-2 py-0.5 text-xs\">\n {surface}\n </span>\n </div>\n <p className=\"text-muted-foreground mt-1 text-sm\">\n {step.description}\n </p>\n </div>\n {step.xpReward && (\n <span\n className={`shrink-0 rounded-full px-2 py-1 text-xs font-semibold ${\n isCompleted\n ? 'bg-green-500/10 text-green-500'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n +{step.xpReward} XP\n </span>\n )}\n </div>\n\n {/* Status */}\n <div className=\"mt-3 text-xs\">\n {isCompleted ? (\n <span className=\"text-green-500\">✓ Completed</span>\n ) : isCurrent ? (\n <span className=\"text-blue-500\">→ In Progress</span>\n ) : (\n <span className=\"text-muted-foreground\">\n ○ Not Started\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,SAAS,EAAE,OAAO,YAA+B;CAE/D,MAAM,gBACJ,MAAM,MAAM,MAAM,MAAM,CAAC,SAAS,iBAAiB,SAAS,EAAE,GAAG,CAAC,EAAE,MACpE;AAEF,QACE,qBAAC;EAAI,WAAU;;GAEb,qBAAC;IAAI,WAAU;eACb,oBAAC;KAAG,WAAU;eAAoB;MAA0B,EAC5D,oBAAC;KAAE,WAAU;eAAwB;MAEjC;KACA;GAGN,qBAAC,mBACC,oBAAC,wBACC,qBAAC;IAAU,WAAU;eACnB,oBAAC,oBAAK,QAAU,EAChB,oBAAC,oBAAK,gBAAkB;KACd,GACD,EACb,oBAAC,yBACC,oBAAC;IACC,OAAO,MAAM;IACb,kBAAkB,SAAS;IACZ;KACf,GACU,IACT;GAGP,qBAAC,mBACC,oBAAC,wBACC,qBAAC;IAAU,WAAU;eACnB,oBAAC,oBAAK,OAAS,EACf,oBAAC,oBAAK,iBAAmB;KACf,GACD,EACb,oBAAC,yBACC,qBAAC;IAAI,WAAU;eAEb,oBAAC,SAAI,WAAU,iDAAiD,EAGhE,oBAAC;KAAI,WAAU;eACZ,MAAM,MAAM,KAAK,MAAM,UAAU;MAChC,MAAM,cAAc,SAAS,iBAAiB,SAAS,KAAK,GAAG;MAC/D,MAAM,YAAY,KAAK,OAAO;MAC9B,MAAM,UAAW,KAAK,UAAU,WAAsB;AAEtD,aACE,qBAAC;OAAkB,WAAU;kBAE3B,oBAAC;QACC,WAAW,wGACT,cACI,6CACA,YACE,mEACA;kBAGP,cAAc,MAAM,QAAQ;SACzB,EAGN,oBAAC;QAAI,WAAU;kBACb,qBAAC;SAAI,WAAU;oBACb,qBAAC;UAAI,WAAU;qBACb,qBAAC,oBACC,qBAAC;WAAI,WAAU;sBACb,oBAAC;YACC,WAAW,iBACT,cACI,mBACA,YACE,kBACA;sBAGP,KAAK;aACH,EACL,oBAAC;YAAK,WAAU;sBACb;aACI;YACH,EACN,oBAAC;WAAE,WAAU;qBACV,KAAK;YACJ,IACA,EACL,KAAK,YACJ,qBAAC;WACC,WAAW,yDACT,cACI,mCACA;;YAEP;YACG,KAAK;YAAS;;YACX;WAEL,EAGN,oBAAC;UAAI,WAAU;oBACZ,cACC,oBAAC;WAAK,WAAU;qBAAiB;YAAkB,GACjD,YACF,oBAAC;WAAK,WAAU;qBAAgB;YAAoB,GAEpD,oBAAC;WAAK,WAAU;qBAAwB;YAEjC;WAEL;UACF;SACF;SAjEE,KAAK,GAkET;OAER;MACE;KACF,GACM,IACT;;GACH"}
@@ -0,0 +1,5 @@
1
+ import { Overview } from "./Overview.js";
2
+ import { Steps } from "./Steps.js";
3
+ import { Progress as ProgressView } from "./Progress.js";
4
+ import { Timeline } from "./Timeline.js";
5
+ export { Overview, ProgressView as Progress, Steps, Timeline };
@@ -0,0 +1,6 @@
1
+ import { Overview } from "./Overview.js";
2
+ import { Steps } from "./Steps.js";
3
+ import { Progress as ProgressView } from "./Progress.js";
4
+ import { Timeline } from "./Timeline.js";
5
+
6
+ export { Overview, ProgressView as Progress, Steps, Timeline };
package/example.ts ADDED
@@ -0,0 +1 @@
1
+ export { default } from './src/example';
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@contractspec/example.learning-journey-ui-onboarding",
3
+ "version": "1.44.0",
4
+ "description": "Developer onboarding UI with checklists and journey maps.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./components": "./dist/components/index.js",
11
+ "./components/CodeSnippet": "./dist/components/CodeSnippet.js",
12
+ "./components/JourneyMap": "./dist/components/JourneyMap.js",
13
+ "./components/StepChecklist": "./dist/components/StepChecklist.js",
14
+ "./docs": "./dist/docs/index.js",
15
+ "./docs/learning-journey-ui-onboarding.docblock": "./dist/docs/learning-journey-ui-onboarding.docblock.js",
16
+ "./example": "./dist/example.js",
17
+ "./OnboardingMiniApp": "./dist/OnboardingMiniApp.js",
18
+ "./views": "./dist/views/index.js",
19
+ "./views/Overview": "./dist/views/Overview.js",
20
+ "./views/Progress": "./dist/views/Progress.js",
21
+ "./views/Steps": "./dist/views/Steps.js",
22
+ "./views/Timeline": "./dist/views/Timeline.js",
23
+ "./*": "./*"
24
+ },
25
+ "scripts": {
26
+ "publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
27
+ "publish:pkg:canary": "bun publish:pkg --tag canary",
28
+ "build": "bun build:types && bun build:bundle",
29
+ "build:bundle": "tsdown",
30
+ "build:types": "tsc --noEmit",
31
+ "dev": "bun build:bundle --watch",
32
+ "clean": "rimraf dist .turbo",
33
+ "lint": "bun lint:fix",
34
+ "lint:fix": "eslint src --fix",
35
+ "lint:check": "eslint src",
36
+ "test": "bun test"
37
+ },
38
+ "dependencies": {
39
+ "@contractspec/lib.ui-kit-core": "1.44.0",
40
+ "@contractspec/lib.schema": "1.44.0",
41
+ "@contractspec/lib.contracts": "1.44.0",
42
+ "@contractspec/example.learning-journey-ui-shared": "1.44.0",
43
+ "@contractspec/example.learning-journey-studio-onboarding": "1.44.0",
44
+ "@contractspec/example.learning-journey-platform-tour": "1.44.0",
45
+ "@contractspec/module.learning-journey": "1.44.0",
46
+ "@contractspec/lib.design-system": "1.44.0",
47
+ "@contractspec/lib.ui-kit-web": "1.44.0",
48
+ "react": "19.2.3"
49
+ },
50
+ "devDependencies": {
51
+ "@contractspec/tool.tsdown": "1.44.0",
52
+ "@contractspec/tool.typescript": "1.44.0",
53
+ "@types/react": "^19.1.6",
54
+ "tsdown": "^0.18.3",
55
+ "typescript": "^5.9.3"
56
+ },
57
+ "peerDependencies": {
58
+ "react": "^19.2.3"
59
+ },
60
+ "module": "./dist/index.js",
61
+ "publishConfig": {
62
+ "exports": {
63
+ ".": "./dist/index.js",
64
+ "./example": "./dist/example.js",
65
+ "./docs": "./dist/docs/index.js",
66
+ "./views": "./dist/views/index.js",
67
+ "./components": "./dist/components/index.js",
68
+ "./*": "./*"
69
+ },
70
+ "registry": "https://registry.npmjs.org/",
71
+ "access": "public"
72
+ },
73
+ "license": "MIT",
74
+ "repository": {
75
+ "type": "git",
76
+ "url": "https://github.com/lssm-tech/contractspec.git",
77
+ "directory": "packages/examples/learning-journey-ui-onboarding"
78
+ },
79
+ "homepage": "https://contractspec.io"
80
+ }
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback } from 'react';
4
+ import { Card, CardContent } from '@contractspec/lib.ui-kit-web/ui/card';
5
+ import {
6
+ ViewTabs,
7
+ useLearningProgress,
8
+ type LearningView,
9
+ type LearningMiniAppProps,
10
+ } from '@contractspec/example.learning-journey-ui-shared';
11
+ import { Overview } from './views/Overview';
12
+ import { Steps } from './views/Steps';
13
+ import { Progress } from './views/Progress';
14
+ import { Timeline } from './views/Timeline';
15
+
16
+ type OnboardingMiniAppProps = Omit<LearningMiniAppProps, 'progress'> & {
17
+ progress?: LearningMiniAppProps['progress'];
18
+ };
19
+
20
+ export function OnboardingMiniApp({
21
+ track,
22
+ progress: externalProgress,
23
+ onStepComplete: externalOnStepComplete,
24
+ onViewChange,
25
+ initialView = 'overview',
26
+ }: OnboardingMiniAppProps) {
27
+ const [currentView, setCurrentView] = useState<LearningView>(initialView);
28
+
29
+ // Use internal progress if not provided externally
30
+ const { progress: internalProgress, completeStep: internalCompleteStep } =
31
+ useLearningProgress(track);
32
+
33
+ const progress = externalProgress ?? internalProgress;
34
+
35
+ const handleViewChange = useCallback(
36
+ (view: LearningView) => {
37
+ setCurrentView(view);
38
+ onViewChange?.(view);
39
+ },
40
+ [onViewChange]
41
+ );
42
+
43
+ const handleStepComplete = useCallback(
44
+ (stepId: string) => {
45
+ if (externalOnStepComplete) {
46
+ externalOnStepComplete(stepId);
47
+ } else {
48
+ internalCompleteStep(stepId);
49
+ }
50
+ },
51
+ [externalOnStepComplete, internalCompleteStep]
52
+ );
53
+
54
+ const handleStartFromOverview = useCallback(() => {
55
+ setCurrentView('steps');
56
+ onViewChange?.('steps');
57
+ }, [onViewChange]);
58
+
59
+ const renderView = () => {
60
+ const viewProps = {
61
+ track,
62
+ progress,
63
+ onStepComplete: handleStepComplete,
64
+ };
65
+
66
+ switch (currentView) {
67
+ case 'overview':
68
+ return <Overview {...viewProps} onStart={handleStartFromOverview} />;
69
+ case 'steps':
70
+ return <Steps {...viewProps} />;
71
+ case 'progress':
72
+ return <Progress {...viewProps} />;
73
+ case 'timeline':
74
+ return <Timeline {...viewProps} />;
75
+ default:
76
+ return <Overview {...viewProps} onStart={handleStartFromOverview} />;
77
+ }
78
+ };
79
+
80
+ return (
81
+ <div className="space-y-6">
82
+ {/* Navigation */}
83
+ <Card>
84
+ <CardContent className="p-4">
85
+ <ViewTabs currentView={currentView} onViewChange={handleViewChange} />
86
+ </CardContent>
87
+ </Card>
88
+
89
+ {/* Current View */}
90
+ {renderView()}
91
+ </div>
92
+ );
93
+ }