@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.
@@ -0,0 +1,22 @@
1
+ $ bun build:bundle && bun build:types
2
+ $ tsdown
3
+ ℹ tsdown v0.17.0 powered by rolldown v1.0.0-beta.53
4
+ ℹ config file: /home/runner/work/contractspec/contractspec/packages/examples/learning-journey-ui-coaching/tsdown.config.js
5
+ ℹ entry: src/index.ts, src/views/index.ts, src/components/index.ts
6
+ ℹ target: esnext
7
+ ℹ tsconfig: tsconfig.json
8
+ ℹ Build start
9
+ ℹ dist/index.mjs  2.38 kB │ gzip: 0.83 kB
10
+ ℹ dist/views/index.mjs  0.20 kB │ gzip: 0.15 kB
11
+ ℹ dist/components/index.mjs  0.17 kB │ gzip: 0.14 kB
12
+ ℹ dist/views-BTGcWRSz.mjs 16.11 kB │ gzip: 2.79 kB
13
+ ℹ dist/TipFeed-_1oKR35X.mjs  8.34 kB │ gzip: 2.01 kB
14
+ ℹ dist/components-kh0CpIG2.mjs  0.01 kB │ gzip: 0.03 kB
15
+ ℹ dist/index.d.mts  0.82 kB │ gzip: 0.41 kB
16
+ ℹ dist/views/index.d.mts  0.16 kB │ gzip: 0.13 kB
17
+ ℹ dist/components/index.d.mts  0.14 kB │ gzip: 0.11 kB
18
+ ℹ dist/index-DJtCFWjr.d.mts  1.16 kB │ gzip: 0.44 kB
19
+ ℹ dist/index-BjhNDTSh.d.mts  0.94 kB │ gzip: 0.34 kB
20
+ ℹ 11 files, total: 30.42 kB
21
+ ✔ Build complete in 7566ms
22
+ $ tsc --noEmit
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @lssm/example.learning-journey-ui-coaching
2
+
3
+ ## 0.0.0-canary-20251212210835
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [3086383]
8
+ - @lssm/lib.design-system@0.0.0-canary-20251212210835
9
+ - @lssm/lib.ui-kit-web@0.0.0-canary-20251212210835
10
+ - @lssm/example.learning-journey.crm-onboarding@0.0.0-canary-20251212210835
11
+ - @lssm/example.learning-journey-ui-shared@0.0.0-canary-20251212210835
12
+ - @lssm/example.learning-journey.ambient-coach@0.0.0-canary-20251212210835
13
+ - @lssm/module.learning-journey@0.0.0-canary-20251212210835
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @lssm/example.learning-journey-ui-coaching
2
+
3
+ Contextual coaching UI with tip cards and engagement tracking.
4
+
5
+ ## Features
6
+
7
+ - **Overview**: Active tips carousel with quick stats
8
+ - **Steps**: Stacked tip cards with action/dismiss buttons
9
+ - **Progress**: Engagement donut chart, streak counter, XP bar
10
+ - **Timeline**: Reverse-chronological feed of tip activity
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import { CoachingMiniApp } from '@lssm/example.learning-journey-ui-coaching';
16
+ import { moneyAmbientCoachTrack } from '@lssm/example.learning-journey.ambient-coach/track';
17
+
18
+ function MyApp() {
19
+ return (
20
+ <CoachingMiniApp track={moneyAmbientCoachTrack} initialView="overview" />
21
+ );
22
+ }
23
+ ```
24
+
25
+ ## Components
26
+
27
+ - **TipCard** - Individual coaching tip with action/dismiss options
28
+ - **EngagementMeter** - Donut chart showing actioned/acknowledged/pending
29
+ - **TipFeed** - Timeline feed of tip activity
30
+
@@ -0,0 +1,234 @@
1
+ import { Card, CardContent } from "@lssm/lib.ui-kit-web/ui/card";
2
+ import { Button } from "@lssm/lib.design-system";
3
+ import { cn } from "@lssm/lib.ui-kit-web/ui/utils";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/components/TipCard.tsx
7
+ const TIP_ICONS$1 = {
8
+ cash_buffer_too_high: "💰",
9
+ no_savings_goal: "🎯",
10
+ irregular_savings: "📅",
11
+ noise_late_evening: "🔇",
12
+ guest_frequency_high: "👥",
13
+ shared_space_conflicts: "🏠",
14
+ default: "💡"
15
+ };
16
+ function TipCard({ step, isCompleted, isCurrent, onComplete, onDismiss }) {
17
+ const icon = TIP_ICONS$1[step.metadata?.tipId ?? "default"] ?? TIP_ICONS$1.default;
18
+ return /* @__PURE__ */ jsx(Card, {
19
+ className: cn("transition-all", isCompleted && "opacity-60", isCurrent && "ring-2 ring-amber-500"),
20
+ children: /* @__PURE__ */ jsx(CardContent, {
21
+ className: "p-4",
22
+ children: /* @__PURE__ */ jsxs("div", {
23
+ className: "flex gap-4",
24
+ children: [/* @__PURE__ */ jsx("div", {
25
+ className: cn("flex h-12 w-12 shrink-0 items-center justify-center rounded-xl text-2xl", isCompleted ? "bg-green-500/10" : isCurrent ? "bg-amber-500/10" : "bg-muted"),
26
+ children: isCompleted ? "✓" : icon
27
+ }), /* @__PURE__ */ jsxs("div", {
28
+ className: "min-w-0 flex-1",
29
+ children: [
30
+ /* @__PURE__ */ jsxs("div", {
31
+ className: "flex items-start justify-between gap-2",
32
+ children: [/* @__PURE__ */ jsx("h4", {
33
+ className: "font-semibold",
34
+ children: step.title
35
+ }), step.xpReward && /* @__PURE__ */ jsxs("span", {
36
+ className: cn("shrink-0 rounded-full px-2 py-0.5 text-xs font-semibold", isCompleted ? "bg-green-500/10 text-green-500" : "bg-amber-500/10 text-amber-500"),
37
+ children: [
38
+ "+",
39
+ step.xpReward,
40
+ " XP"
41
+ ]
42
+ })]
43
+ }),
44
+ /* @__PURE__ */ jsx("p", {
45
+ className: "text-muted-foreground mt-1 text-sm",
46
+ children: step.description
47
+ }),
48
+ !isCompleted && /* @__PURE__ */ jsxs("div", {
49
+ className: "mt-3 flex flex-wrap gap-2",
50
+ children: [/* @__PURE__ */ jsx(Button, {
51
+ size: "sm",
52
+ onClick: onComplete,
53
+ children: "Take Action"
54
+ }), /* @__PURE__ */ jsx(Button, {
55
+ variant: "outline",
56
+ size: "sm",
57
+ onClick: onDismiss,
58
+ children: "Dismiss"
59
+ })]
60
+ }),
61
+ isCompleted && /* @__PURE__ */ jsx("p", {
62
+ className: "mt-2 text-sm text-green-500",
63
+ children: "✓ Tip acknowledged"
64
+ })
65
+ ]
66
+ })]
67
+ })
68
+ })
69
+ });
70
+ }
71
+
72
+ //#endregion
73
+ //#region src/components/EngagementMeter.tsx
74
+ function EngagementMeter({ acknowledged, actioned, pending, streak = 0 }) {
75
+ const total = acknowledged + actioned + pending;
76
+ const actionedPercent = total > 0 ? actioned / total * 100 : 0;
77
+ const acknowledgedPercent = total > 0 ? acknowledged / total * 100 : 0;
78
+ return /* @__PURE__ */ jsxs("div", {
79
+ className: "space-y-4",
80
+ children: [
81
+ /* @__PURE__ */ jsx("div", {
82
+ className: "flex items-center justify-center",
83
+ children: /* @__PURE__ */ jsxs("div", {
84
+ className: "relative h-32 w-32",
85
+ children: [/* @__PURE__ */ jsxs("svg", {
86
+ className: "h-full w-full -rotate-90",
87
+ viewBox: "0 0 100 100",
88
+ children: [
89
+ /* @__PURE__ */ jsx("circle", {
90
+ cx: "50",
91
+ cy: "50",
92
+ r: "40",
93
+ fill: "none",
94
+ strokeWidth: "12",
95
+ className: "stroke-muted"
96
+ }),
97
+ /* @__PURE__ */ jsx("circle", {
98
+ cx: "50",
99
+ cy: "50",
100
+ r: "40",
101
+ fill: "none",
102
+ strokeWidth: "12",
103
+ strokeLinecap: "round",
104
+ strokeDasharray: `${actionedPercent * 2.51} 251`,
105
+ className: "stroke-green-500 transition-all duration-500"
106
+ }),
107
+ /* @__PURE__ */ jsx("circle", {
108
+ cx: "50",
109
+ cy: "50",
110
+ r: "40",
111
+ fill: "none",
112
+ strokeWidth: "12",
113
+ strokeLinecap: "round",
114
+ strokeDasharray: `${acknowledgedPercent * 2.51} 251`,
115
+ strokeDashoffset: `${-actionedPercent * 2.51}`,
116
+ className: "stroke-amber-500 transition-all duration-500"
117
+ })
118
+ ]
119
+ }), /* @__PURE__ */ jsxs("div", {
120
+ className: "absolute inset-0 flex flex-col items-center justify-center",
121
+ children: [/* @__PURE__ */ jsx("span", {
122
+ className: "text-2xl font-bold",
123
+ children: total
124
+ }), /* @__PURE__ */ jsx("span", {
125
+ className: "text-muted-foreground text-xs",
126
+ children: "tips"
127
+ })]
128
+ })]
129
+ })
130
+ }),
131
+ /* @__PURE__ */ jsxs("div", {
132
+ className: "flex justify-center gap-4 text-sm",
133
+ children: [
134
+ /* @__PURE__ */ jsxs("div", {
135
+ className: "flex items-center gap-1.5",
136
+ children: [/* @__PURE__ */ jsx("div", { className: "h-3 w-3 rounded-full bg-green-500" }), /* @__PURE__ */ jsxs("span", { children: [
137
+ "Actioned (",
138
+ actioned,
139
+ ")"
140
+ ] })]
141
+ }),
142
+ /* @__PURE__ */ jsxs("div", {
143
+ className: "flex items-center gap-1.5",
144
+ children: [/* @__PURE__ */ jsx("div", { className: "h-3 w-3 rounded-full bg-amber-500" }), /* @__PURE__ */ jsxs("span", { children: [
145
+ "Acknowledged (",
146
+ acknowledged,
147
+ ")"
148
+ ] })]
149
+ }),
150
+ /* @__PURE__ */ jsxs("div", {
151
+ className: "flex items-center gap-1.5",
152
+ children: [/* @__PURE__ */ jsx("div", { className: "bg-muted h-3 w-3 rounded-full" }), /* @__PURE__ */ jsxs("span", { children: [
153
+ "Pending (",
154
+ pending,
155
+ ")"
156
+ ] })]
157
+ })
158
+ ]
159
+ }),
160
+ streak > 0 && /* @__PURE__ */ jsxs("div", {
161
+ className: "flex items-center justify-center gap-2 rounded-lg bg-orange-500/10 px-4 py-2",
162
+ children: [/* @__PURE__ */ jsx("span", {
163
+ className: "text-xl",
164
+ children: "🔥"
165
+ }), /* @__PURE__ */ jsxs("span", {
166
+ className: "font-semibold text-orange-500",
167
+ children: [streak, " day engagement streak!"]
168
+ })]
169
+ })
170
+ ]
171
+ });
172
+ }
173
+
174
+ //#endregion
175
+ //#region src/components/TipFeed.tsx
176
+ const TIP_ICONS = {
177
+ cash_buffer_too_high: "💰",
178
+ no_savings_goal: "🎯",
179
+ irregular_savings: "📅",
180
+ noise_late_evening: "🔇",
181
+ guest_frequency_high: "👥",
182
+ shared_space_conflicts: "🏠",
183
+ default: "💡"
184
+ };
185
+ function TipFeed({ items }) {
186
+ if (items.length === 0) return /* @__PURE__ */ jsx("div", {
187
+ className: "text-muted-foreground py-8 text-center",
188
+ children: "No tips yet. Start engaging with coaching tips!"
189
+ });
190
+ return /* @__PURE__ */ jsxs("div", {
191
+ className: "relative",
192
+ children: [/* @__PURE__ */ jsx("div", { className: "bg-border absolute top-0 left-4 h-full w-0.5" }), /* @__PURE__ */ jsx("div", {
193
+ className: "space-y-4",
194
+ children: items.map((item, index) => {
195
+ const icon = TIP_ICONS[item.step.metadata?.tipId ?? "default"] ?? TIP_ICONS.default;
196
+ return /* @__PURE__ */ jsxs("div", {
197
+ className: "relative flex gap-4 pl-2",
198
+ children: [/* @__PURE__ */ jsx("div", {
199
+ className: cn("relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm", item.isCompleted ? "bg-green-500 text-white" : "bg-muted text-muted-foreground"),
200
+ children: item.isCompleted ? "✓" : icon
201
+ }), /* @__PURE__ */ jsxs("div", {
202
+ className: "bg-card flex-1 rounded-lg border p-3",
203
+ children: [/* @__PURE__ */ jsxs("div", {
204
+ className: "flex items-start justify-between gap-2",
205
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
206
+ className: "font-medium",
207
+ children: item.step.title
208
+ }), /* @__PURE__ */ jsx("p", {
209
+ className: "text-muted-foreground mt-0.5 text-sm",
210
+ children: item.step.description
211
+ })] }), item.step.xpReward && /* @__PURE__ */ jsxs("span", {
212
+ className: cn("shrink-0 text-xs font-medium", item.isCompleted ? "text-green-500" : "text-muted-foreground"),
213
+ children: [
214
+ "+",
215
+ item.step.xpReward,
216
+ " XP"
217
+ ]
218
+ })]
219
+ }), /* @__PURE__ */ jsx("div", {
220
+ className: "text-muted-foreground mt-2 flex items-center gap-2 text-xs",
221
+ children: item.isCompleted ? /* @__PURE__ */ jsxs("span", {
222
+ className: "text-green-500",
223
+ children: ["✓ Completed", item.completedAt && ` • ${item.completedAt}`]
224
+ }) : /* @__PURE__ */ jsx("span", { children: "Pending action" })
225
+ })]
226
+ })]
227
+ }, item.step.id);
228
+ })
229
+ })]
230
+ });
231
+ }
232
+
233
+ //#endregion
234
+ export { EngagementMeter as n, TipCard as r, TipFeed as t };
@@ -0,0 +1,2 @@
1
+ import { n as EngagementMeter, r as TipCard, t as TipFeed } from "../index-DJtCFWjr.mjs";
2
+ export { EngagementMeter, TipCard, TipFeed };
@@ -0,0 +1,4 @@
1
+ import { n as EngagementMeter, r as TipCard, t as TipFeed } from "../TipFeed-_1oKR35X.mjs";
2
+ import "../components-kh0CpIG2.mjs";
3
+
4
+ export { EngagementMeter, TipCard, TipFeed };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,34 @@
1
+ import { LearningViewProps } from "@lssm/example.learning-journey-ui-shared";
2
+ import * as react_jsx_runtime4 from "react/jsx-runtime";
3
+
4
+ //#region src/views/Overview.d.ts
5
+ interface CoachingOverviewProps extends LearningViewProps {
6
+ onStart?: () => void;
7
+ }
8
+ declare function Overview({
9
+ track,
10
+ progress,
11
+ onStepComplete,
12
+ onStart
13
+ }: CoachingOverviewProps): react_jsx_runtime4.JSX.Element;
14
+ //#endregion
15
+ //#region src/views/Steps.d.ts
16
+ declare function Steps({
17
+ track,
18
+ progress,
19
+ onStepComplete
20
+ }: LearningViewProps): react_jsx_runtime4.JSX.Element;
21
+ //#endregion
22
+ //#region src/views/Progress.d.ts
23
+ declare function ProgressView({
24
+ track,
25
+ progress
26
+ }: LearningViewProps): react_jsx_runtime4.JSX.Element;
27
+ //#endregion
28
+ //#region src/views/Timeline.d.ts
29
+ declare function Timeline({
30
+ track,
31
+ progress
32
+ }: LearningViewProps): react_jsx_runtime4.JSX.Element;
33
+ //#endregion
34
+ export { Overview as i, ProgressView as n, Steps as r, Timeline as t };
@@ -0,0 +1,47 @@
1
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
2
+ import { LearningJourneyStepSpec } from "@lssm/module.learning-journey/track-spec";
3
+
4
+ //#region src/components/TipCard.d.ts
5
+ interface TipCardProps {
6
+ step: LearningJourneyStepSpec;
7
+ isCompleted: boolean;
8
+ isCurrent: boolean;
9
+ onComplete?: () => void;
10
+ onDismiss?: () => void;
11
+ }
12
+ declare function TipCard({
13
+ step,
14
+ isCompleted,
15
+ isCurrent,
16
+ onComplete,
17
+ onDismiss
18
+ }: TipCardProps): react_jsx_runtime1.JSX.Element;
19
+ //#endregion
20
+ //#region src/components/EngagementMeter.d.ts
21
+ interface EngagementMeterProps {
22
+ acknowledged: number;
23
+ actioned: number;
24
+ pending: number;
25
+ streak?: number;
26
+ }
27
+ declare function EngagementMeter({
28
+ acknowledged,
29
+ actioned,
30
+ pending,
31
+ streak
32
+ }: EngagementMeterProps): react_jsx_runtime1.JSX.Element;
33
+ //#endregion
34
+ //#region src/components/TipFeed.d.ts
35
+ interface TipFeedItem {
36
+ step: LearningJourneyStepSpec;
37
+ isCompleted: boolean;
38
+ completedAt?: string;
39
+ }
40
+ interface TipFeedProps {
41
+ items: TipFeedItem[];
42
+ }
43
+ declare function TipFeed({
44
+ items
45
+ }: TipFeedProps): react_jsx_runtime1.JSX.Element;
46
+ //#endregion
47
+ export { EngagementMeter as n, TipCard as r, TipFeed as t };
@@ -0,0 +1,18 @@
1
+ import { n as EngagementMeter, r as TipCard, t as TipFeed } from "./index-DJtCFWjr.mjs";
2
+ import { i as Overview, n as ProgressView, r as Steps, t as Timeline } from "./index-BjhNDTSh.mjs";
3
+ import { LearningMiniAppProps } from "@lssm/example.learning-journey-ui-shared";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
+
6
+ //#region src/CoachingMiniApp.d.ts
7
+ type CoachingMiniAppProps = Omit<LearningMiniAppProps, 'progress'> & {
8
+ progress?: LearningMiniAppProps['progress'];
9
+ };
10
+ declare function CoachingMiniApp({
11
+ track,
12
+ progress: externalProgress,
13
+ onStepComplete: externalOnStepComplete,
14
+ onViewChange,
15
+ initialView
16
+ }: CoachingMiniAppProps): react_jsx_runtime0.JSX.Element;
17
+ //#endregion
18
+ export { CoachingMiniApp, EngagementMeter, Overview, ProgressView as Progress, Steps, Timeline, TipCard, TipFeed };
package/dist/index.mjs ADDED
@@ -0,0 +1,59 @@
1
+ import { n as EngagementMeter, r as TipCard, t as TipFeed } from "./TipFeed-_1oKR35X.mjs";
2
+ import { i as Overview, n as ProgressView, r as Steps, t as Timeline } from "./views-BTGcWRSz.mjs";
3
+ import "./components-kh0CpIG2.mjs";
4
+ import { useCallback, useState } from "react";
5
+ import { Card, CardContent } from "@lssm/lib.ui-kit-web/ui/card";
6
+ import { ViewTabs, useLearningProgress } from "@lssm/example.learning-journey-ui-shared";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+
9
+ //#region src/CoachingMiniApp.tsx
10
+ function CoachingMiniApp({ track, progress: externalProgress, onStepComplete: externalOnStepComplete, onViewChange, initialView = "overview" }) {
11
+ const [currentView, setCurrentView] = useState(initialView);
12
+ const { progress: internalProgress, completeStep: internalCompleteStep } = useLearningProgress(track);
13
+ const progress = externalProgress ?? internalProgress;
14
+ const handleViewChange = useCallback((view) => {
15
+ setCurrentView(view);
16
+ onViewChange?.(view);
17
+ }, [onViewChange]);
18
+ const handleStepComplete = useCallback((stepId) => {
19
+ if (externalOnStepComplete) externalOnStepComplete(stepId);
20
+ else internalCompleteStep(stepId);
21
+ }, [externalOnStepComplete, internalCompleteStep]);
22
+ const handleStartFromOverview = useCallback(() => {
23
+ setCurrentView("steps");
24
+ onViewChange?.("steps");
25
+ }, [onViewChange]);
26
+ const renderView = () => {
27
+ const viewProps = {
28
+ track,
29
+ progress,
30
+ onStepComplete: handleStepComplete
31
+ };
32
+ switch (currentView) {
33
+ case "overview": return /* @__PURE__ */ jsx(Overview, {
34
+ ...viewProps,
35
+ onStart: handleStartFromOverview
36
+ });
37
+ case "steps": return /* @__PURE__ */ jsx(Steps, { ...viewProps });
38
+ case "progress": return /* @__PURE__ */ jsx(ProgressView, { ...viewProps });
39
+ case "timeline": return /* @__PURE__ */ jsx(Timeline, { ...viewProps });
40
+ default: return /* @__PURE__ */ jsx(Overview, {
41
+ ...viewProps,
42
+ onStart: handleStartFromOverview
43
+ });
44
+ }
45
+ };
46
+ return /* @__PURE__ */ jsxs("div", {
47
+ className: "space-y-6",
48
+ children: [/* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, {
49
+ className: "p-4",
50
+ children: /* @__PURE__ */ jsx(ViewTabs, {
51
+ currentView,
52
+ onViewChange: handleViewChange
53
+ })
54
+ }) }), renderView()]
55
+ });
56
+ }
57
+
58
+ //#endregion
59
+ export { CoachingMiniApp, EngagementMeter, Overview, ProgressView as Progress, Steps, Timeline, TipCard, TipFeed };
@@ -0,0 +1,2 @@
1
+ import { i as Overview, n as ProgressView, r as Steps, t as Timeline } from "../index-BjhNDTSh.mjs";
2
+ export { Overview, ProgressView as Progress, Steps, Timeline };
@@ -0,0 +1,4 @@
1
+ import "../TipFeed-_1oKR35X.mjs";
2
+ import { i as Overview, n as ProgressView, r as Steps, t as Timeline } from "../views-BTGcWRSz.mjs";
3
+
4
+ export { Overview, ProgressView as Progress, Steps, Timeline };