@lssm/example.learning-journey-ui-coaching 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 +30 -0
- package/dist/TipFeed-_1oKR35X.mjs +234 -0
- package/dist/components/index.d.mts +2 -0
- package/dist/components/index.mjs +4 -0
- package/dist/components-kh0CpIG2.mjs +1 -0
- package/dist/index-BjhNDTSh.d.mts +34 -0
- package/dist/index-DJtCFWjr.d.mts +47 -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-BTGcWRSz.mjs +421 -0
- package/package.json +53 -0
- package/src/CoachingMiniApp.tsx +94 -0
- package/src/components/EngagementMeter.tsx +96 -0
- package/src/components/TipCard.tsx +102 -0
- package/src/components/TipFeed.tsx +102 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +9 -0
- package/src/views/Overview.tsx +155 -0
- package/src/views/Progress.tsx +165 -0
- package/src/views/Steps.tsx +64 -0
- package/src/views/Timeline.tsx +115 -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,421 @@
|
|
|
1
|
+
import { n as EngagementMeter, r as TipCard, t as TipFeed } from "./TipFeed-_1oKR35X.mjs";
|
|
2
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@lssm/lib.ui-kit-web/ui/card";
|
|
3
|
+
import { BadgeDisplay, StreakCounter, XpBar } from "@lssm/example.learning-journey-ui-shared";
|
|
4
|
+
import { Button } from "@lssm/lib.design-system";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/views/Overview.tsx
|
|
8
|
+
function Overview({ track, progress, onStepComplete, onStart }) {
|
|
9
|
+
const totalXp = track.totalXp ?? track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0);
|
|
10
|
+
const completedSteps = progress.completedStepIds.length;
|
|
11
|
+
const totalSteps = track.steps.length;
|
|
12
|
+
const pendingSteps = totalSteps - completedSteps;
|
|
13
|
+
const activeTips = track.steps.filter((s) => !progress.completedStepIds.includes(s.id)).slice(0, 3);
|
|
14
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
15
|
+
className: "space-y-6",
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx(Card, {
|
|
18
|
+
className: "overflow-hidden bg-gradient-to-br from-amber-500/10 via-orange-500/10 to-red-500/10",
|
|
19
|
+
children: /* @__PURE__ */ jsx(CardContent, {
|
|
20
|
+
className: "p-6",
|
|
21
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
22
|
+
className: "flex flex-col items-center gap-4 text-center md:flex-row md:text-left",
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ jsx("div", {
|
|
25
|
+
className: "flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-amber-500 to-orange-600 text-3xl shadow-lg",
|
|
26
|
+
children: "💡"
|
|
27
|
+
}),
|
|
28
|
+
/* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "flex-1",
|
|
30
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
31
|
+
className: "text-2xl font-bold",
|
|
32
|
+
children: track.name
|
|
33
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
34
|
+
className: "text-muted-foreground mt-1",
|
|
35
|
+
children: track.description
|
|
36
|
+
})]
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("div", {
|
|
39
|
+
className: "flex items-center gap-4",
|
|
40
|
+
children: /* @__PURE__ */ jsx(StreakCounter, {
|
|
41
|
+
days: progress.streakDays,
|
|
42
|
+
size: "lg"
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
]
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ jsxs("div", {
|
|
50
|
+
className: "grid gap-4 md:grid-cols-3",
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, {
|
|
53
|
+
className: "pb-2",
|
|
54
|
+
children: /* @__PURE__ */ jsx(CardTitle, {
|
|
55
|
+
className: "text-muted-foreground text-sm font-medium",
|
|
56
|
+
children: "Active Tips"
|
|
57
|
+
})
|
|
58
|
+
}), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsx("div", {
|
|
59
|
+
className: "text-3xl font-bold text-amber-500",
|
|
60
|
+
children: pendingSteps
|
|
61
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
62
|
+
className: "text-muted-foreground text-sm",
|
|
63
|
+
children: "tips for you today"
|
|
64
|
+
})] })] }),
|
|
65
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, {
|
|
66
|
+
className: "pb-2",
|
|
67
|
+
children: /* @__PURE__ */ jsx(CardTitle, {
|
|
68
|
+
className: "text-muted-foreground text-sm font-medium",
|
|
69
|
+
children: "Tips Actioned"
|
|
70
|
+
})
|
|
71
|
+
}), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsx("div", {
|
|
72
|
+
className: "text-3xl font-bold text-green-500",
|
|
73
|
+
children: completedSteps
|
|
74
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
75
|
+
className: "text-muted-foreground text-sm",
|
|
76
|
+
children: [
|
|
77
|
+
"out of ",
|
|
78
|
+
totalSteps,
|
|
79
|
+
" total"
|
|
80
|
+
]
|
|
81
|
+
})] })] }),
|
|
82
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, {
|
|
83
|
+
className: "pb-2",
|
|
84
|
+
children: /* @__PURE__ */ jsx(CardTitle, {
|
|
85
|
+
className: "text-muted-foreground text-sm font-medium",
|
|
86
|
+
children: "XP Earned"
|
|
87
|
+
})
|
|
88
|
+
}), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsx("div", {
|
|
89
|
+
className: "text-3xl font-bold text-orange-500",
|
|
90
|
+
children: progress.xpEarned
|
|
91
|
+
}), /* @__PURE__ */ jsx(XpBar, {
|
|
92
|
+
current: progress.xpEarned,
|
|
93
|
+
max: totalXp,
|
|
94
|
+
showLabel: false,
|
|
95
|
+
size: "sm"
|
|
96
|
+
})] })] })
|
|
97
|
+
]
|
|
98
|
+
}),
|
|
99
|
+
activeTips.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsxs(CardHeader, {
|
|
100
|
+
className: "flex flex-row items-center justify-between",
|
|
101
|
+
children: [/* @__PURE__ */ jsxs(CardTitle, {
|
|
102
|
+
className: "flex items-center gap-2",
|
|
103
|
+
children: [/* @__PURE__ */ jsx("span", { children: "💡" }), /* @__PURE__ */ jsx("span", { children: "Tips for You" })]
|
|
104
|
+
}), activeTips.length < pendingSteps && /* @__PURE__ */ jsxs(Button, {
|
|
105
|
+
variant: "outline",
|
|
106
|
+
size: "sm",
|
|
107
|
+
onClick: onStart,
|
|
108
|
+
children: [
|
|
109
|
+
"View All (",
|
|
110
|
+
pendingSteps,
|
|
111
|
+
")"
|
|
112
|
+
]
|
|
113
|
+
})]
|
|
114
|
+
}), /* @__PURE__ */ jsx(CardContent, {
|
|
115
|
+
className: "space-y-3",
|
|
116
|
+
children: activeTips.map((step) => /* @__PURE__ */ jsx(TipCard, {
|
|
117
|
+
step,
|
|
118
|
+
isCompleted: false,
|
|
119
|
+
isCurrent: step.id === activeTips[0]?.id,
|
|
120
|
+
onComplete: () => onStepComplete?.(step.id)
|
|
121
|
+
}, step.id))
|
|
122
|
+
})] }),
|
|
123
|
+
pendingSteps === 0 && /* @__PURE__ */ jsx(Card, {
|
|
124
|
+
className: "border-green-500/50 bg-green-500/5",
|
|
125
|
+
children: /* @__PURE__ */ jsxs(CardContent, {
|
|
126
|
+
className: "flex items-center gap-4 p-6",
|
|
127
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
128
|
+
className: "text-4xl",
|
|
129
|
+
children: "🎉"
|
|
130
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
|
|
131
|
+
className: "text-lg font-semibold text-green-500",
|
|
132
|
+
children: "All Tips Actioned!"
|
|
133
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
134
|
+
className: "text-muted-foreground",
|
|
135
|
+
children: [
|
|
136
|
+
"Great job! You've addressed all ",
|
|
137
|
+
totalSteps,
|
|
138
|
+
" coaching tips."
|
|
139
|
+
]
|
|
140
|
+
})] })]
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/views/Steps.tsx
|
|
149
|
+
function Steps({ track, progress, onStepComplete }) {
|
|
150
|
+
const completedSteps = progress.completedStepIds.length;
|
|
151
|
+
const totalSteps = track.steps.length;
|
|
152
|
+
const sortedSteps = [...track.steps].sort((a, b) => {
|
|
153
|
+
const aCompleted = progress.completedStepIds.includes(a.id);
|
|
154
|
+
if (aCompleted === progress.completedStepIds.includes(b.id)) return 0;
|
|
155
|
+
return aCompleted ? 1 : -1;
|
|
156
|
+
});
|
|
157
|
+
const currentStepId = track.steps.find((s) => !progress.completedStepIds.includes(s.id))?.id;
|
|
158
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
159
|
+
className: "space-y-6",
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsxs("div", {
|
|
162
|
+
className: "text-center",
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ jsx("h2", {
|
|
165
|
+
className: "text-xl font-bold",
|
|
166
|
+
children: "Coaching Tips"
|
|
167
|
+
}),
|
|
168
|
+
/* @__PURE__ */ jsx("p", {
|
|
169
|
+
className: "text-muted-foreground",
|
|
170
|
+
children: "Review and take action on personalized tips"
|
|
171
|
+
}),
|
|
172
|
+
/* @__PURE__ */ jsxs("p", {
|
|
173
|
+
className: "text-muted-foreground mt-2 text-sm",
|
|
174
|
+
children: [
|
|
175
|
+
completedSteps,
|
|
176
|
+
" of ",
|
|
177
|
+
totalSteps,
|
|
178
|
+
" tips actioned"
|
|
179
|
+
]
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ jsx("div", {
|
|
184
|
+
className: "space-y-3",
|
|
185
|
+
children: sortedSteps.map((step) => {
|
|
186
|
+
return /* @__PURE__ */ jsx(TipCard, {
|
|
187
|
+
step,
|
|
188
|
+
isCompleted: progress.completedStepIds.includes(step.id),
|
|
189
|
+
isCurrent: step.id === currentStepId,
|
|
190
|
+
onComplete: () => onStepComplete?.(step.id),
|
|
191
|
+
onDismiss: () => onStepComplete?.(step.id)
|
|
192
|
+
}, step.id);
|
|
193
|
+
})
|
|
194
|
+
}),
|
|
195
|
+
completedSteps === totalSteps && /* @__PURE__ */ jsxs("div", {
|
|
196
|
+
className: "text-muted-foreground text-center",
|
|
197
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
198
|
+
className: "text-2xl",
|
|
199
|
+
children: "✨"
|
|
200
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
201
|
+
className: "mt-2",
|
|
202
|
+
children: "All tips have been addressed!"
|
|
203
|
+
})]
|
|
204
|
+
})
|
|
205
|
+
]
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/views/Progress.tsx
|
|
211
|
+
function ProgressView({ track, progress }) {
|
|
212
|
+
const totalXp = track.totalXp ?? track.steps.reduce((sum, s) => sum + (s.xpReward ?? 0), 0);
|
|
213
|
+
const completedSteps = progress.completedStepIds.length;
|
|
214
|
+
const pendingSteps = track.steps.length - completedSteps;
|
|
215
|
+
const actioned = Math.floor(completedSteps * .7);
|
|
216
|
+
const acknowledged = completedSteps - actioned;
|
|
217
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
218
|
+
className: "space-y-6",
|
|
219
|
+
children: [
|
|
220
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
221
|
+
className: "flex items-center gap-2",
|
|
222
|
+
children: [/* @__PURE__ */ jsx("span", { children: "📊" }), /* @__PURE__ */ jsx("span", { children: "Engagement Overview" })]
|
|
223
|
+
}) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(EngagementMeter, {
|
|
224
|
+
actioned,
|
|
225
|
+
acknowledged,
|
|
226
|
+
pending: pendingSteps,
|
|
227
|
+
streak: progress.streakDays
|
|
228
|
+
}) })] }),
|
|
229
|
+
/* @__PURE__ */ jsxs("div", {
|
|
230
|
+
className: "grid gap-4 md:grid-cols-2",
|
|
231
|
+
children: [/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, {
|
|
232
|
+
className: "pb-2",
|
|
233
|
+
children: /* @__PURE__ */ jsx(CardTitle, {
|
|
234
|
+
className: "text-muted-foreground text-sm font-medium",
|
|
235
|
+
children: "XP Earned"
|
|
236
|
+
})
|
|
237
|
+
}), /* @__PURE__ */ jsxs(CardContent, {
|
|
238
|
+
className: "space-y-3",
|
|
239
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
240
|
+
className: "flex items-baseline gap-2",
|
|
241
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
242
|
+
className: "text-3xl font-bold text-orange-500",
|
|
243
|
+
children: progress.xpEarned
|
|
244
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
245
|
+
className: "text-muted-foreground",
|
|
246
|
+
children: [
|
|
247
|
+
"/ ",
|
|
248
|
+
totalXp,
|
|
249
|
+
" XP"
|
|
250
|
+
]
|
|
251
|
+
})]
|
|
252
|
+
}), /* @__PURE__ */ jsx(XpBar, {
|
|
253
|
+
current: progress.xpEarned,
|
|
254
|
+
max: totalXp,
|
|
255
|
+
showLabel: false,
|
|
256
|
+
size: "lg"
|
|
257
|
+
})]
|
|
258
|
+
})] }), /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, {
|
|
259
|
+
className: "pb-2",
|
|
260
|
+
children: /* @__PURE__ */ jsx(CardTitle, {
|
|
261
|
+
className: "text-muted-foreground text-sm font-medium",
|
|
262
|
+
children: "Engagement Streak"
|
|
263
|
+
})
|
|
264
|
+
}), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", {
|
|
265
|
+
className: "flex items-center gap-4",
|
|
266
|
+
children: [/* @__PURE__ */ jsx(StreakCounter, {
|
|
267
|
+
days: progress.streakDays,
|
|
268
|
+
size: "lg"
|
|
269
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
270
|
+
className: "text-muted-foreground text-sm",
|
|
271
|
+
children: progress.streakDays > 0 ? "Keep going!" : "Start your streak today!"
|
|
272
|
+
})]
|
|
273
|
+
}) })] })]
|
|
274
|
+
}),
|
|
275
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
276
|
+
className: "flex items-center gap-2",
|
|
277
|
+
children: [/* @__PURE__ */ jsx("span", { children: "🏅" }), /* @__PURE__ */ jsx("span", { children: "Achievements" })]
|
|
278
|
+
}) }), /* @__PURE__ */ jsxs(CardContent, { children: [/* @__PURE__ */ jsx(BadgeDisplay, {
|
|
279
|
+
badges: progress.badges,
|
|
280
|
+
size: "lg",
|
|
281
|
+
maxVisible: 10
|
|
282
|
+
}), progress.badges.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
283
|
+
className: "text-muted-foreground text-sm",
|
|
284
|
+
children: "Action tips to unlock achievements!"
|
|
285
|
+
})] })] }),
|
|
286
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
287
|
+
className: "flex items-center gap-2",
|
|
288
|
+
children: [/* @__PURE__ */ jsx("span", { children: "💡" }), /* @__PURE__ */ jsx("span", { children: "Tip Status" })]
|
|
289
|
+
}) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", {
|
|
290
|
+
className: "space-y-3",
|
|
291
|
+
children: track.steps.map((step) => {
|
|
292
|
+
const isCompleted = progress.completedStepIds.includes(step.id);
|
|
293
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
294
|
+
className: "flex items-center justify-between rounded-lg border p-3",
|
|
295
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
296
|
+
className: "flex items-center gap-3",
|
|
297
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
298
|
+
className: isCompleted ? "text-green-500" : "text-amber-500",
|
|
299
|
+
children: isCompleted ? "✓" : "○"
|
|
300
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
301
|
+
className: isCompleted ? "text-muted-foreground" : "text-foreground",
|
|
302
|
+
children: step.title
|
|
303
|
+
})]
|
|
304
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
305
|
+
className: `text-sm ${isCompleted ? "text-green-500" : "text-muted-foreground"}`,
|
|
306
|
+
children: isCompleted ? "Actioned" : "Pending"
|
|
307
|
+
})]
|
|
308
|
+
}, step.id);
|
|
309
|
+
})
|
|
310
|
+
}) })] })
|
|
311
|
+
]
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/views/Timeline.tsx
|
|
317
|
+
function Timeline({ track, progress }) {
|
|
318
|
+
const feedItems = track.steps.map((step) => ({
|
|
319
|
+
step,
|
|
320
|
+
isCompleted: progress.completedStepIds.includes(step.id),
|
|
321
|
+
completedAt: progress.completedStepIds.includes(step.id) ? "Recently" : void 0
|
|
322
|
+
})).sort((a, b) => {
|
|
323
|
+
if (a.isCompleted && !b.isCompleted) return -1;
|
|
324
|
+
if (!a.isCompleted && b.isCompleted) return 1;
|
|
325
|
+
return 0;
|
|
326
|
+
});
|
|
327
|
+
const completedCount = feedItems.filter((f) => f.isCompleted).length;
|
|
328
|
+
const pendingCount = feedItems.length - completedCount;
|
|
329
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
330
|
+
className: "space-y-6",
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ jsxs("div", {
|
|
333
|
+
className: "text-center",
|
|
334
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
335
|
+
className: "text-xl font-bold",
|
|
336
|
+
children: "Activity Timeline"
|
|
337
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
338
|
+
className: "text-muted-foreground",
|
|
339
|
+
children: "Your coaching journey and tip history"
|
|
340
|
+
})]
|
|
341
|
+
}),
|
|
342
|
+
/* @__PURE__ */ jsxs("div", {
|
|
343
|
+
className: "grid grid-cols-2 gap-4",
|
|
344
|
+
children: [/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, {
|
|
345
|
+
className: "p-4 text-center",
|
|
346
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
347
|
+
className: "text-2xl font-bold text-green-500",
|
|
348
|
+
children: completedCount
|
|
349
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
350
|
+
className: "text-muted-foreground text-sm",
|
|
351
|
+
children: "Tips Actioned"
|
|
352
|
+
})]
|
|
353
|
+
}) }), /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, {
|
|
354
|
+
className: "p-4 text-center",
|
|
355
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
356
|
+
className: "text-2xl font-bold text-amber-500",
|
|
357
|
+
children: pendingCount
|
|
358
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
359
|
+
className: "text-muted-foreground text-sm",
|
|
360
|
+
children: "Tips Pending"
|
|
361
|
+
})]
|
|
362
|
+
}) })]
|
|
363
|
+
}),
|
|
364
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
365
|
+
className: "flex items-center gap-2",
|
|
366
|
+
children: [/* @__PURE__ */ jsx("span", { children: "📝" }), /* @__PURE__ */ jsx("span", { children: "Coaching Feed" })]
|
|
367
|
+
}) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(TipFeed, { items: feedItems }) })] }),
|
|
368
|
+
/* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, {
|
|
369
|
+
className: "flex items-center gap-2",
|
|
370
|
+
children: [/* @__PURE__ */ jsx("span", { children: "📈" }), /* @__PURE__ */ jsx("span", { children: "Journey Stats" })]
|
|
371
|
+
}) }), /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", {
|
|
372
|
+
className: "space-y-4",
|
|
373
|
+
children: [
|
|
374
|
+
/* @__PURE__ */ jsxs("div", {
|
|
375
|
+
className: "flex items-center justify-between",
|
|
376
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
377
|
+
className: "text-muted-foreground",
|
|
378
|
+
children: "Total Tips"
|
|
379
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
380
|
+
className: "font-semibold",
|
|
381
|
+
children: track.steps.length
|
|
382
|
+
})]
|
|
383
|
+
}),
|
|
384
|
+
/* @__PURE__ */ jsxs("div", {
|
|
385
|
+
className: "flex items-center justify-between",
|
|
386
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
387
|
+
className: "text-muted-foreground",
|
|
388
|
+
children: "Completed"
|
|
389
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
390
|
+
className: "font-semibold text-green-500",
|
|
391
|
+
children: completedCount
|
|
392
|
+
})]
|
|
393
|
+
}),
|
|
394
|
+
/* @__PURE__ */ jsxs("div", {
|
|
395
|
+
className: "flex items-center justify-between",
|
|
396
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
397
|
+
className: "text-muted-foreground",
|
|
398
|
+
children: "XP Earned"
|
|
399
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
400
|
+
className: "font-semibold text-orange-500",
|
|
401
|
+
children: progress.xpEarned
|
|
402
|
+
})]
|
|
403
|
+
}),
|
|
404
|
+
/* @__PURE__ */ jsxs("div", {
|
|
405
|
+
className: "flex items-center justify-between",
|
|
406
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
407
|
+
className: "text-muted-foreground",
|
|
408
|
+
children: "Current Streak"
|
|
409
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
410
|
+
className: "font-semibold",
|
|
411
|
+
children: progress.streakDays > 0 ? `🔥 ${progress.streakDays} days` : "Start today!"
|
|
412
|
+
})]
|
|
413
|
+
})
|
|
414
|
+
]
|
|
415
|
+
}) })] })
|
|
416
|
+
]
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
//#endregion
|
|
421
|
+
export { Overview as i, ProgressView as n, Steps as r, Timeline as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lssm/example.learning-journey-ui-coaching",
|
|
3
|
+
"version": "0.0.0-canary-20251212210835",
|
|
4
|
+
"description": "Contextual coaching UI with tip cards and engagement tracking.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts",
|
|
10
|
+
"./views": "./src/views/index.ts",
|
|
11
|
+
"./components": "./src/components/index.ts",
|
|
12
|
+
"./*": "./*"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "bun build:bundle && bun build:types",
|
|
16
|
+
"build:bundle": "tsdown",
|
|
17
|
+
"build:types": "tsc --noEmit",
|
|
18
|
+
"dev": "bun build:bundle --watch",
|
|
19
|
+
"clean": "rimraf dist .turbo",
|
|
20
|
+
"lint": "bun lint:fix",
|
|
21
|
+
"lint:fix": "eslint src --fix",
|
|
22
|
+
"lint:check": "eslint src",
|
|
23
|
+
"test": "bun test"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@lssm/example.learning-journey-ui-shared": "workspace:*",
|
|
27
|
+
"@lssm/example.learning-journey.ambient-coach": "workspace:*",
|
|
28
|
+
"@lssm/example.learning-journey.crm-onboarding": "workspace:*",
|
|
29
|
+
"@lssm/module.learning-journey": "workspace:*",
|
|
30
|
+
"@lssm/lib.design-system": "workspace:*",
|
|
31
|
+
"@lssm/lib.ui-kit-web": "workspace:*",
|
|
32
|
+
"react": "^19.1.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@lssm/tool.tsdown": "workspace:*",
|
|
36
|
+
"@lssm/tool.typescript": "workspace:*",
|
|
37
|
+
"@types/react": "^19.1.6",
|
|
38
|
+
"tsdown": "^0.17.0",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
43
|
+
},
|
|
44
|
+
"module": "./dist/index.js",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"exports": {
|
|
47
|
+
".": "./dist/index.js",
|
|
48
|
+
"./views": "./dist/views/index.js",
|
|
49
|
+
"./components": "./dist/components/index.js",
|
|
50
|
+
"./*": "./*"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
import { Card, CardContent } from '@lssm/lib.ui-kit-web/ui/card';
|
|
5
|
+
import {
|
|
6
|
+
ViewTabs,
|
|
7
|
+
useLearningProgress,
|
|
8
|
+
type LearningView,
|
|
9
|
+
type LearningMiniAppProps,
|
|
10
|
+
} from '@lssm/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 CoachingMiniAppProps = Omit<LearningMiniAppProps, 'progress'> & {
|
|
17
|
+
progress?: LearningMiniAppProps['progress'];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function CoachingMiniApp({
|
|
21
|
+
track,
|
|
22
|
+
progress: externalProgress,
|
|
23
|
+
onStepComplete: externalOnStepComplete,
|
|
24
|
+
onViewChange,
|
|
25
|
+
initialView = 'overview',
|
|
26
|
+
}: CoachingMiniAppProps) {
|
|
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
|
+
}
|
|
94
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@lssm/lib.ui-kit-web/ui/utils';
|
|
4
|
+
|
|
5
|
+
interface EngagementMeterProps {
|
|
6
|
+
acknowledged: number;
|
|
7
|
+
actioned: number;
|
|
8
|
+
pending: number;
|
|
9
|
+
streak?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function EngagementMeter({
|
|
13
|
+
acknowledged,
|
|
14
|
+
actioned,
|
|
15
|
+
pending,
|
|
16
|
+
streak = 0,
|
|
17
|
+
}: EngagementMeterProps) {
|
|
18
|
+
const total = acknowledged + actioned + pending;
|
|
19
|
+
const actionedPercent = total > 0 ? (actioned / total) * 100 : 0;
|
|
20
|
+
const acknowledgedPercent = total > 0 ? (acknowledged / total) * 100 : 0;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="space-y-4">
|
|
24
|
+
{/* Donut chart */}
|
|
25
|
+
<div className="flex items-center justify-center">
|
|
26
|
+
<div className="relative h-32 w-32">
|
|
27
|
+
<svg className="h-full w-full -rotate-90" viewBox="0 0 100 100">
|
|
28
|
+
{/* Background */}
|
|
29
|
+
<circle
|
|
30
|
+
cx="50"
|
|
31
|
+
cy="50"
|
|
32
|
+
r="40"
|
|
33
|
+
fill="none"
|
|
34
|
+
strokeWidth="12"
|
|
35
|
+
className="stroke-muted"
|
|
36
|
+
/>
|
|
37
|
+
{/* Actioned (green) */}
|
|
38
|
+
<circle
|
|
39
|
+
cx="50"
|
|
40
|
+
cy="50"
|
|
41
|
+
r="40"
|
|
42
|
+
fill="none"
|
|
43
|
+
strokeWidth="12"
|
|
44
|
+
strokeLinecap="round"
|
|
45
|
+
strokeDasharray={`${actionedPercent * 2.51} 251`}
|
|
46
|
+
className="stroke-green-500 transition-all duration-500"
|
|
47
|
+
/>
|
|
48
|
+
{/* Acknowledged (amber) - offset by actioned */}
|
|
49
|
+
<circle
|
|
50
|
+
cx="50"
|
|
51
|
+
cy="50"
|
|
52
|
+
r="40"
|
|
53
|
+
fill="none"
|
|
54
|
+
strokeWidth="12"
|
|
55
|
+
strokeLinecap="round"
|
|
56
|
+
strokeDasharray={`${acknowledgedPercent * 2.51} 251`}
|
|
57
|
+
strokeDashoffset={`${-actionedPercent * 2.51}`}
|
|
58
|
+
className="stroke-amber-500 transition-all duration-500"
|
|
59
|
+
/>
|
|
60
|
+
</svg>
|
|
61
|
+
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
|
62
|
+
<span className="text-2xl font-bold">{total}</span>
|
|
63
|
+
<span className="text-muted-foreground text-xs">tips</span>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{/* Legend */}
|
|
69
|
+
<div className="flex justify-center gap-4 text-sm">
|
|
70
|
+
<div className="flex items-center gap-1.5">
|
|
71
|
+
<div className="h-3 w-3 rounded-full bg-green-500" />
|
|
72
|
+
<span>Actioned ({actioned})</span>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="flex items-center gap-1.5">
|
|
75
|
+
<div className="h-3 w-3 rounded-full bg-amber-500" />
|
|
76
|
+
<span>Acknowledged ({acknowledged})</span>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="flex items-center gap-1.5">
|
|
79
|
+
<div className="bg-muted h-3 w-3 rounded-full" />
|
|
80
|
+
<span>Pending ({pending})</span>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
{/* Streak */}
|
|
85
|
+
{streak > 0 && (
|
|
86
|
+
<div className="flex items-center justify-center gap-2 rounded-lg bg-orange-500/10 px-4 py-2">
|
|
87
|
+
<span className="text-xl">🔥</span>
|
|
88
|
+
<span className="font-semibold text-orange-500">
|
|
89
|
+
{streak} day engagement streak!
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|