@contractspec/example.learning-journey-registry 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.
- package/.turbo/turbo-build$colon$bundle.log +50 -0
- package/.turbo/turbo-build.log +51 -0
- package/CHANGELOG.md +612 -0
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/api-types.d.ts +41 -0
- package/dist/api-types.d.ts.map +1 -0
- package/dist/api-types.js +0 -0
- package/dist/api.d.ts +13 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +171 -0
- package/dist/api.js.map +1 -0
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/docs/learning-journey-registry.docblock.d.ts +1 -0
- package/dist/docs/learning-journey-registry.docblock.js +38 -0
- package/dist/docs/learning-journey-registry.docblock.js.map +1 -0
- package/dist/example.d.ts +7 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +42 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/learning-journey-registry.feature.d.ts +12 -0
- package/dist/learning-journey-registry.feature.d.ts.map +1 -0
- package/dist/learning-journey-registry.feature.js +75 -0
- package/dist/learning-journey-registry.feature.js.map +1 -0
- package/dist/presentations/index.d.ts +10 -0
- package/dist/presentations/index.d.ts.map +1 -0
- package/dist/presentations/index.js +71 -0
- package/dist/presentations/index.js.map +1 -0
- package/dist/progress-store.d.ts +11 -0
- package/dist/progress-store.d.ts.map +1 -0
- package/dist/progress-store.js +31 -0
- package/dist/progress-store.js.map +1 -0
- package/dist/tracks.d.ts +40 -0
- package/dist/tracks.d.ts.map +1 -0
- package/dist/tracks.js +48 -0
- package/dist/tracks.js.map +1 -0
- package/dist/ui/LearningMiniApp.d.ts +24 -0
- package/dist/ui/LearningMiniApp.d.ts.map +1 -0
- package/dist/ui/LearningMiniApp.js +80 -0
- package/dist/ui/LearningMiniApp.js.map +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +3 -0
- package/example.ts +1 -0
- package/package.json +86 -0
- package/src/api-types.ts +43 -0
- package/src/api.test.ts +46 -0
- package/src/api.ts +301 -0
- package/src/docs/index.ts +1 -0
- package/src/docs/learning-journey-registry.docblock.ts +36 -0
- package/src/example.ts +31 -0
- package/src/index.ts +8 -0
- package/src/learning-journey-registry.feature.ts +64 -0
- package/src/presentations/index.ts +65 -0
- package/src/progress-store.ts +39 -0
- package/src/tracks.ts +91 -0
- package/src/ui/LearningMiniApp.tsx +121 -0
- package/src/ui/index.ts +5 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +17 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-store.js","names":[],"sources":["../src/progress-store.ts"],"sourcesContent":["import type { LearningJourneyTrackSpec } from '@contractspec/module.learning-journey/track-spec';\n\nimport type { TrackProgress } from './api-types';\n\nexport const progressStore = new Map<string, Map<string, TrackProgress>>();\n\nexport const getTrackResolver =\n (tracks: LearningJourneyTrackSpec[]) =>\n (trackId: string): LearningJourneyTrackSpec | undefined =>\n tracks.find((t) => t.id === trackId);\n\nexport const getLearnerTracks = (learnerId: string) => {\n const existing = progressStore.get(learnerId);\n if (existing) return existing;\n const map = new Map<string, TrackProgress>();\n progressStore.set(learnerId, map);\n return map;\n};\n\nexport const initProgress = (\n learnerId: string,\n track: LearningJourneyTrackSpec\n): TrackProgress => ({\n learnerId,\n trackId: track.id,\n progress: 0,\n isCompleted: false,\n xpEarned: 0,\n steps: track.steps.map((step) => ({\n id: step.id,\n status: 'PENDING',\n xpEarned: 0,\n occurrences: 0,\n masteryCount: 0,\n })),\n startedAt: undefined,\n completedAt: undefined,\n lastActivityAt: undefined,\n});\n"],"mappings":";AAIA,MAAa,gCAAgB,IAAI,KAAyC;AAE1E,MAAa,oBACV,YACA,YACC,OAAO,MAAM,MAAM,EAAE,OAAO,QAAQ;AAExC,MAAa,oBAAoB,cAAsB;CACrD,MAAM,WAAW,cAAc,IAAI,UAAU;AAC7C,KAAI,SAAU,QAAO;CACrB,MAAM,sBAAM,IAAI,KAA4B;AAC5C,eAAc,IAAI,WAAW,IAAI;AACjC,QAAO;;AAGT,MAAa,gBACX,WACA,WACmB;CACnB;CACA,SAAS,MAAM;CACf,UAAU;CACV,aAAa;CACb,UAAU;CACV,OAAO,MAAM,MAAM,KAAK,UAAU;EAChC,IAAI,KAAK;EACT,QAAQ;EACR,UAAU;EACV,aAAa;EACb,cAAc;EACf,EAAE;CACH,WAAW;CACX,aAAa;CACb,gBAAgB;CACjB"}
|
package/dist/tracks.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { crmLearningTracks } from "@contractspec/example.learning-journey-crm-onboarding/track";
|
|
2
|
+
import { platformLearningTracks } from "@contractspec/example.learning-journey-platform-tour/track";
|
|
3
|
+
import { studioLearningTracks } from "@contractspec/example.learning-journey-studio-onboarding/track";
|
|
4
|
+
import { LearningJourneyStepSpec, LearningJourneyTrackSpec, StepAvailabilitySpec, StepCompletionConditionSpec } from "@contractspec/module.learning-journey/track-spec";
|
|
5
|
+
|
|
6
|
+
//#region src/tracks.d.ts
|
|
7
|
+
interface OnboardingStepDto {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
completionEvent: string;
|
|
12
|
+
completionCondition?: StepCompletionConditionSpec;
|
|
13
|
+
xpReward?: number;
|
|
14
|
+
isRequired?: boolean;
|
|
15
|
+
canSkip?: boolean;
|
|
16
|
+
actionUrl?: string;
|
|
17
|
+
actionLabel?: string;
|
|
18
|
+
availability?: StepAvailabilitySpec;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface OnboardingTrackDto {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
productId?: string;
|
|
26
|
+
targetUserSegment?: string;
|
|
27
|
+
targetRole?: string;
|
|
28
|
+
totalXp?: number;
|
|
29
|
+
streakRule?: LearningJourneyTrackSpec['streakRule'];
|
|
30
|
+
completionRewards?: LearningJourneyTrackSpec['completionRewards'];
|
|
31
|
+
steps: OnboardingStepDto[];
|
|
32
|
+
metadata?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
declare const mapStep: (step: LearningJourneyStepSpec) => OnboardingStepDto;
|
|
35
|
+
declare const mapTrackSpecToDto: (track: LearningJourneyTrackSpec) => OnboardingTrackDto;
|
|
36
|
+
declare const learningJourneyTracks: LearningJourneyTrackSpec[];
|
|
37
|
+
declare const onboardingTrackCatalog: OnboardingTrackDto[];
|
|
38
|
+
//#endregion
|
|
39
|
+
export { OnboardingStepDto, OnboardingTrackDto, crmLearningTracks, learningJourneyTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, studioLearningTracks };
|
|
40
|
+
//# sourceMappingURL=tracks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracks.d.ts","names":[],"sources":["../src/tracks.ts"],"sourcesContent":[],"mappings":";;;;;;UAaiB,iBAAA;;EAAA,KAAA,EAAA,MAAA;EAKO,WAAA,CAAA,EAAA,MAAA;EAMP,eAAA,EAAA,MAAA;EACJ,mBAAA,CAAA,EAPW,2BAOX;EAAM,QAAA,CAAA,EAAA,MAAA;EAGF,UAAA,CAAA,EAAA,OAAA;EAQF,OAAA,CAAA,EAAA,OAAA;EACO,SAAA,CAAA,EAAA,MAAA;EACb,WAAA,CAAA,EAAA,MAAA;EACI,YAAA,CAAA,EAfI,oBAeJ;EAAM,QAAA,CAAA,EAdN,MAcM,CAAA,MAAA,EAAA,OAAA,CAAA;AAClB;AAiBY,UA7BI,kBAAA,CA8BR;EAeI,EAAA,EAAA,MAAA;EASA,IAAA,EAAA,MAAA;;;;;;eA9CE;sBACO;SACb;aACI;;cAGP,gBAAiB,4BAA0B;cAepC,2BACJ,6BACN;cAcU,uBAAuB;cASvB,wBAAwB"}
|
package/dist/tracks.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { crmLearningTracks } from "@contractspec/example.learning-journey-crm-onboarding/track";
|
|
2
|
+
import { drillTracks } from "@contractspec/example.learning-journey-duo-drills/track";
|
|
3
|
+
import { ambientCoachTracks } from "@contractspec/example.learning-journey-ambient-coach/track";
|
|
4
|
+
import { questTracks } from "@contractspec/example.learning-journey-quest-challenges/track";
|
|
5
|
+
import { platformLearningTracks } from "@contractspec/example.learning-journey-platform-tour/track";
|
|
6
|
+
import { studioLearningTracks } from "@contractspec/example.learning-journey-studio-onboarding/track";
|
|
7
|
+
|
|
8
|
+
//#region src/tracks.ts
|
|
9
|
+
const mapStep = (step) => ({
|
|
10
|
+
id: step.id,
|
|
11
|
+
title: step.title,
|
|
12
|
+
description: step.description,
|
|
13
|
+
completionEvent: step.completion.eventName,
|
|
14
|
+
completionCondition: step.completion,
|
|
15
|
+
xpReward: step.xpReward,
|
|
16
|
+
isRequired: step.isRequired,
|
|
17
|
+
canSkip: step.canSkip,
|
|
18
|
+
actionUrl: step.actionUrl,
|
|
19
|
+
actionLabel: step.actionLabel,
|
|
20
|
+
availability: step.availability,
|
|
21
|
+
metadata: step.metadata
|
|
22
|
+
});
|
|
23
|
+
const mapTrackSpecToDto = (track) => ({
|
|
24
|
+
id: track.id,
|
|
25
|
+
name: track.name,
|
|
26
|
+
description: track.description,
|
|
27
|
+
productId: track.productId,
|
|
28
|
+
targetUserSegment: track.targetUserSegment,
|
|
29
|
+
targetRole: track.targetRole,
|
|
30
|
+
totalXp: track.totalXp,
|
|
31
|
+
streakRule: track.streakRule,
|
|
32
|
+
completionRewards: track.completionRewards,
|
|
33
|
+
steps: track.steps.map(mapStep),
|
|
34
|
+
metadata: track.metadata
|
|
35
|
+
});
|
|
36
|
+
const learningJourneyTracks = [
|
|
37
|
+
...studioLearningTracks,
|
|
38
|
+
...platformLearningTracks,
|
|
39
|
+
...crmLearningTracks,
|
|
40
|
+
...drillTracks,
|
|
41
|
+
...ambientCoachTracks,
|
|
42
|
+
...questTracks
|
|
43
|
+
];
|
|
44
|
+
const onboardingTrackCatalog = learningJourneyTracks.map(mapTrackSpecToDto);
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { crmLearningTracks, learningJourneyTracks, mapStep, mapTrackSpecToDto, onboardingTrackCatalog, platformLearningTracks, studioLearningTracks };
|
|
48
|
+
//# sourceMappingURL=tracks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracks.js","names":[],"sources":["../src/tracks.ts"],"sourcesContent":["import type {\n LearningJourneyStepSpec,\n LearningJourneyTrackSpec,\n StepAvailabilitySpec,\n StepCompletionConditionSpec,\n} from '@contractspec/module.learning-journey/track-spec';\nimport { crmLearningTracks } from '@contractspec/example.learning-journey-crm-onboarding/track';\nimport { drillTracks } from '@contractspec/example.learning-journey-duo-drills/track';\nimport { ambientCoachTracks } from '@contractspec/example.learning-journey-ambient-coach/track';\nimport { questTracks } from '@contractspec/example.learning-journey-quest-challenges/track';\nimport { platformLearningTracks } from '@contractspec/example.learning-journey-platform-tour/track';\nimport { studioLearningTracks } from '@contractspec/example.learning-journey-studio-onboarding/track';\n\nexport interface OnboardingStepDto {\n id: string;\n title: string;\n description?: string;\n completionEvent: string;\n completionCondition?: StepCompletionConditionSpec;\n xpReward?: number;\n isRequired?: boolean;\n canSkip?: boolean;\n actionUrl?: string;\n actionLabel?: string;\n availability?: StepAvailabilitySpec;\n metadata?: Record<string, unknown>;\n}\n\nexport interface OnboardingTrackDto {\n id: string;\n name: string;\n description?: string;\n productId?: string;\n targetUserSegment?: string;\n targetRole?: string;\n totalXp?: number;\n streakRule?: LearningJourneyTrackSpec['streakRule'];\n completionRewards?: LearningJourneyTrackSpec['completionRewards'];\n steps: OnboardingStepDto[];\n metadata?: Record<string, unknown>;\n}\n\nconst mapStep = (step: LearningJourneyStepSpec): OnboardingStepDto => ({\n id: step.id,\n title: step.title,\n description: step.description,\n completionEvent: step.completion.eventName,\n completionCondition: step.completion,\n xpReward: step.xpReward,\n isRequired: step.isRequired,\n canSkip: step.canSkip,\n actionUrl: step.actionUrl,\n actionLabel: step.actionLabel,\n availability: step.availability,\n metadata: step.metadata,\n});\n\nexport const mapTrackSpecToDto = (\n track: LearningJourneyTrackSpec\n): OnboardingTrackDto => ({\n id: track.id,\n name: track.name,\n description: track.description,\n productId: track.productId,\n targetUserSegment: track.targetUserSegment,\n targetRole: track.targetRole,\n totalXp: track.totalXp,\n streakRule: track.streakRule,\n completionRewards: track.completionRewards,\n steps: track.steps.map(mapStep),\n metadata: track.metadata,\n});\n\nexport const learningJourneyTracks: LearningJourneyTrackSpec[] = [\n ...studioLearningTracks,\n ...platformLearningTracks,\n ...crmLearningTracks,\n ...drillTracks,\n ...ambientCoachTracks,\n ...questTracks,\n];\n\nexport const onboardingTrackCatalog: OnboardingTrackDto[] =\n learningJourneyTracks.map(mapTrackSpecToDto);\n\nexport {\n studioLearningTracks,\n platformLearningTracks,\n crmLearningTracks,\n mapStep,\n};\n"],"mappings":";;;;;;;;AA0CA,MAAM,WAAW,UAAsD;CACrE,IAAI,KAAK;CACT,OAAO,KAAK;CACZ,aAAa,KAAK;CAClB,iBAAiB,KAAK,WAAW;CACjC,qBAAqB,KAAK;CAC1B,UAAU,KAAK;CACf,YAAY,KAAK;CACjB,SAAS,KAAK;CACd,WAAW,KAAK;CAChB,aAAa,KAAK;CAClB,cAAc,KAAK;CACnB,UAAU,KAAK;CAChB;AAED,MAAa,qBACX,WACwB;CACxB,IAAI,MAAM;CACV,MAAM,MAAM;CACZ,aAAa,MAAM;CACnB,WAAW,MAAM;CACjB,mBAAmB,MAAM;CACzB,YAAY,MAAM;CAClB,SAAS,MAAM;CACf,YAAY,MAAM;CAClB,mBAAmB,MAAM;CACzB,OAAO,MAAM,MAAM,IAAI,QAAQ;CAC/B,UAAU,MAAM;CACjB;AAED,MAAa,wBAAoD;CAC/D,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACJ;AAED,MAAa,yBACX,sBAAsB,IAAI,kBAAkB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import { LearningView } from "@contractspec/example.learning-journey-ui-shared";
|
|
3
|
+
|
|
4
|
+
//#region src/ui/LearningMiniApp.d.ts
|
|
5
|
+
/** Template IDs that map to learning journey tracks */
|
|
6
|
+
type LearningTemplateId = 'learning-journey-duo-drills' | 'learning-journey-quest-challenges' | 'learning-journey-studio-onboarding' | 'learning-journey-platform-tour' | 'learning-journey-ambient-coach' | 'learning-journey-crm-onboarding';
|
|
7
|
+
interface LearningMiniAppProps {
|
|
8
|
+
templateId: string;
|
|
9
|
+
initialView?: LearningView;
|
|
10
|
+
onViewChange?: (view: LearningView) => void;
|
|
11
|
+
}
|
|
12
|
+
/** Router component that picks the correct mini-app based on template ID */
|
|
13
|
+
declare function LearningMiniApp({
|
|
14
|
+
templateId,
|
|
15
|
+
initialView,
|
|
16
|
+
onViewChange
|
|
17
|
+
}: LearningMiniAppProps): react_jsx_runtime0.JSX.Element;
|
|
18
|
+
/** Check if a template ID is a learning journey template */
|
|
19
|
+
declare function isLearningTemplate(templateId: string): templateId is LearningTemplateId;
|
|
20
|
+
/** Get all learning template IDs */
|
|
21
|
+
declare function getLearningTemplateIds(): LearningTemplateId[];
|
|
22
|
+
//#endregion
|
|
23
|
+
export { LearningMiniApp, getLearningTemplateIds, isLearningTemplate };
|
|
24
|
+
//# sourceMappingURL=LearningMiniApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LearningMiniApp.d.ts","names":[],"sources":["../../src/ui/LearningMiniApp.tsx"],"sourcesContent":[],"mappings":";;;;;KAUK,kBAAA;UA+BK,oBAAA;EA/BL,UAAA,EAAA,MAAA;EA+BK,WAAA,CAAA,EAEM,YAFc;EAOd,YAAA,CAAA,EAAA,CAAA,IAAe,EAJP,YAIO,EAAA,GAAA,IAAA;;;AAG7B,iBAHc,eAAA,CAGd;EAAA,UAAA;EAAA,WAAA;EAAA;AAAA,CAAA,EACC,oBADD,CAAA,EACqB,kBAAA,CAAA,GAAA,CAAA,OADrB;;AACqB,iBA2DP,kBAAA,CA3DO,UAAA,EAAA,MAAA,CAAA,EAAA,UAAA,IA6DN,kBA7DM;;AA2DP,iBAOA,sBAAA,CAAA,CALC,EAKyB,kBALP,EAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { learningJourneyTracks } from "../tracks.js";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { GamifiedMiniApp } from "@contractspec/example.learning-journey-ui-gamified";
|
|
6
|
+
import { OnboardingMiniApp } from "@contractspec/example.learning-journey-ui-onboarding";
|
|
7
|
+
import { CoachingMiniApp } from "@contractspec/example.learning-journey-ui-coaching";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
|
|
10
|
+
//#region src/ui/LearningMiniApp.tsx
|
|
11
|
+
/** Map template IDs to track IDs */
|
|
12
|
+
const TEMPLATE_TO_TRACK = {
|
|
13
|
+
"learning-journey-duo-drills": "drills_language_basics",
|
|
14
|
+
"learning-journey-quest-challenges": "money_reset_7day",
|
|
15
|
+
"learning-journey-studio-onboarding": "studio_getting_started",
|
|
16
|
+
"learning-journey-platform-tour": "platform_tour",
|
|
17
|
+
"learning-journey-ambient-coach": "money_ambient_coach",
|
|
18
|
+
"learning-journey-crm-onboarding": "crm_first_win"
|
|
19
|
+
};
|
|
20
|
+
/** Map template IDs to mini-app type */
|
|
21
|
+
const TEMPLATE_TO_APP_TYPE = {
|
|
22
|
+
"learning-journey-duo-drills": "gamified",
|
|
23
|
+
"learning-journey-quest-challenges": "gamified",
|
|
24
|
+
"learning-journey-studio-onboarding": "onboarding",
|
|
25
|
+
"learning-journey-platform-tour": "onboarding",
|
|
26
|
+
"learning-journey-ambient-coach": "coaching",
|
|
27
|
+
"learning-journey-crm-onboarding": "coaching"
|
|
28
|
+
};
|
|
29
|
+
/** Router component that picks the correct mini-app based on template ID */
|
|
30
|
+
function LearningMiniApp({ templateId, initialView = "overview", onViewChange }) {
|
|
31
|
+
const track = useMemo(() => {
|
|
32
|
+
const trackId = TEMPLATE_TO_TRACK[templateId];
|
|
33
|
+
if (!trackId) return null;
|
|
34
|
+
return learningJourneyTracks.find((t) => t.id === trackId);
|
|
35
|
+
}, [templateId]);
|
|
36
|
+
const appType = TEMPLATE_TO_APP_TYPE[templateId];
|
|
37
|
+
if (!track) return /* @__PURE__ */ jsx("div", {
|
|
38
|
+
className: "rounded-lg border border-amber-500/50 bg-amber-500/10 p-6 text-center",
|
|
39
|
+
children: /* @__PURE__ */ jsxs("p", {
|
|
40
|
+
className: "text-amber-500",
|
|
41
|
+
children: ["Unknown learning template: ", templateId]
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
switch (appType) {
|
|
45
|
+
case "gamified": return /* @__PURE__ */ jsx(GamifiedMiniApp, {
|
|
46
|
+
track,
|
|
47
|
+
initialView,
|
|
48
|
+
onViewChange
|
|
49
|
+
});
|
|
50
|
+
case "onboarding": return /* @__PURE__ */ jsx(OnboardingMiniApp, {
|
|
51
|
+
track,
|
|
52
|
+
initialView,
|
|
53
|
+
onViewChange
|
|
54
|
+
});
|
|
55
|
+
case "coaching": return /* @__PURE__ */ jsx(CoachingMiniApp, {
|
|
56
|
+
track,
|
|
57
|
+
initialView,
|
|
58
|
+
onViewChange
|
|
59
|
+
});
|
|
60
|
+
default: return /* @__PURE__ */ jsx("div", {
|
|
61
|
+
className: "rounded-lg border border-red-500/50 bg-red-500/10 p-6 text-center",
|
|
62
|
+
children: /* @__PURE__ */ jsxs("p", {
|
|
63
|
+
className: "text-red-500",
|
|
64
|
+
children: ["Unknown app type for template: ", templateId]
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Check if a template ID is a learning journey template */
|
|
70
|
+
function isLearningTemplate(templateId) {
|
|
71
|
+
return templateId in TEMPLATE_TO_TRACK;
|
|
72
|
+
}
|
|
73
|
+
/** Get all learning template IDs */
|
|
74
|
+
function getLearningTemplateIds() {
|
|
75
|
+
return Object.keys(TEMPLATE_TO_TRACK);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
export { LearningMiniApp, getLearningTemplateIds, isLearningTemplate };
|
|
80
|
+
//# sourceMappingURL=LearningMiniApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LearningMiniApp.js","names":[],"sources":["../../src/ui/LearningMiniApp.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\nimport { GamifiedMiniApp } from '@contractspec/example.learning-journey-ui-gamified';\nimport { OnboardingMiniApp } from '@contractspec/example.learning-journey-ui-onboarding';\nimport { CoachingMiniApp } from '@contractspec/example.learning-journey-ui-coaching';\nimport type { LearningView } from '@contractspec/example.learning-journey-ui-shared';\nimport { learningJourneyTracks } from '../tracks';\n\n/** Template IDs that map to learning journey tracks */\ntype LearningTemplateId =\n | 'learning-journey-duo-drills'\n | 'learning-journey-quest-challenges'\n | 'learning-journey-studio-onboarding'\n | 'learning-journey-platform-tour'\n | 'learning-journey-ambient-coach'\n | 'learning-journey-crm-onboarding';\n\n/** Map template IDs to track IDs */\nconst TEMPLATE_TO_TRACK: Record<LearningTemplateId, string> = {\n 'learning-journey-duo-drills': 'drills_language_basics',\n 'learning-journey-quest-challenges': 'money_reset_7day',\n 'learning-journey-studio-onboarding': 'studio_getting_started',\n 'learning-journey-platform-tour': 'platform_tour',\n 'learning-journey-ambient-coach': 'money_ambient_coach',\n 'learning-journey-crm-onboarding': 'crm_first_win',\n};\n\n/** Map template IDs to mini-app type */\nconst TEMPLATE_TO_APP_TYPE: Record<\n LearningTemplateId,\n 'gamified' | 'onboarding' | 'coaching'\n> = {\n 'learning-journey-duo-drills': 'gamified',\n 'learning-journey-quest-challenges': 'gamified',\n 'learning-journey-studio-onboarding': 'onboarding',\n 'learning-journey-platform-tour': 'onboarding',\n 'learning-journey-ambient-coach': 'coaching',\n 'learning-journey-crm-onboarding': 'coaching',\n};\n\ninterface LearningMiniAppProps {\n templateId: string;\n initialView?: LearningView;\n onViewChange?: (view: LearningView) => void;\n}\n\n/** Router component that picks the correct mini-app based on template ID */\nexport function LearningMiniApp({\n templateId,\n initialView = 'overview',\n onViewChange,\n}: LearningMiniAppProps) {\n // Find the track for this template\n const track = useMemo(() => {\n const trackId = TEMPLATE_TO_TRACK[templateId as LearningTemplateId];\n if (!trackId) return null;\n return learningJourneyTracks.find((t) => t.id === trackId);\n }, [templateId]);\n\n // Determine app type\n const appType = TEMPLATE_TO_APP_TYPE[templateId as LearningTemplateId];\n\n if (!track) {\n return (\n <div className=\"rounded-lg border border-amber-500/50 bg-amber-500/10 p-6 text-center\">\n <p className=\"text-amber-500\">\n Unknown learning template: {templateId}\n </p>\n </div>\n );\n }\n\n // Render the appropriate mini-app\n switch (appType) {\n case 'gamified':\n return (\n <GamifiedMiniApp\n track={track}\n initialView={initialView}\n onViewChange={onViewChange}\n />\n );\n case 'onboarding':\n return (\n <OnboardingMiniApp\n track={track}\n initialView={initialView}\n onViewChange={onViewChange}\n />\n );\n case 'coaching':\n return (\n <CoachingMiniApp\n track={track}\n initialView={initialView}\n onViewChange={onViewChange}\n />\n );\n default:\n return (\n <div className=\"rounded-lg border border-red-500/50 bg-red-500/10 p-6 text-center\">\n <p className=\"text-red-500\">\n Unknown app type for template: {templateId}\n </p>\n </div>\n );\n }\n}\n\n/** Check if a template ID is a learning journey template */\nexport function isLearningTemplate(\n templateId: string\n): templateId is LearningTemplateId {\n return templateId in TEMPLATE_TO_TRACK;\n}\n\n/** Get all learning template IDs */\nexport function getLearningTemplateIds(): LearningTemplateId[] {\n return Object.keys(TEMPLATE_TO_TRACK) as LearningTemplateId[];\n}\n"],"mappings":";;;;;;;;;;;AAmBA,MAAM,oBAAwD;CAC5D,+BAA+B;CAC/B,qCAAqC;CACrC,sCAAsC;CACtC,kCAAkC;CAClC,kCAAkC;CAClC,mCAAmC;CACpC;;AAGD,MAAM,uBAGF;CACF,+BAA+B;CAC/B,qCAAqC;CACrC,sCAAsC;CACtC,kCAAkC;CAClC,kCAAkC;CAClC,mCAAmC;CACpC;;AASD,SAAgB,gBAAgB,EAC9B,YACA,cAAc,YACd,gBACuB;CAEvB,MAAM,QAAQ,cAAc;EAC1B,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,sBAAsB,MAAM,MAAM,EAAE,OAAO,QAAQ;IACzD,CAAC,WAAW,CAAC;CAGhB,MAAM,UAAU,qBAAqB;AAErC,KAAI,CAAC,MACH,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAE,WAAU;cAAiB,+BACA;IAC1B;GACA;AAKV,SAAQ,SAAR;EACE,KAAK,WACH,QACE,oBAAC;GACQ;GACM;GACC;IACd;EAEN,KAAK,aACH,QACE,oBAAC;GACQ;GACM;GACC;IACd;EAEN,KAAK,WACH,QACE,oBAAC;GACQ;GACM;GACC;IACd;EAEN,QACE,QACE,oBAAC;GAAI,WAAU;aACb,qBAAC;IAAE,WAAU;eAAe,mCACM;KAC9B;IACA;;;;AAMd,SAAgB,mBACd,YACkC;AAClC,QAAO,cAAc;;;AAIvB,SAAgB,yBAA+C;AAC7D,QAAO,OAAO,KAAK,kBAAkB"}
|
package/dist/ui/index.js
ADDED
package/example.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './src/example';
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contractspec/example.learning-journey-registry",
|
|
3
|
+
"version": "0.0.0-canary-20260113170453",
|
|
4
|
+
"description": "Registry that aggregates learning journey example tracks.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./api": "./dist/api.js",
|
|
10
|
+
"./api-types": "./dist/api-types.js",
|
|
11
|
+
"./docs": "./dist/docs/index.js",
|
|
12
|
+
"./docs/learning-journey-registry.docblock": "./dist/docs/learning-journey-registry.docblock.js",
|
|
13
|
+
"./example": "./dist/example.js",
|
|
14
|
+
"./learning-journey-registry.feature": "./dist/learning-journey-registry.feature.js",
|
|
15
|
+
"./presentations": "./dist/presentations/index.js",
|
|
16
|
+
"./progress-store": "./dist/progress-store.js",
|
|
17
|
+
"./tracks": "./dist/tracks.js",
|
|
18
|
+
"./ui": "./dist/ui/index.js",
|
|
19
|
+
"./ui/LearningMiniApp": "./dist/ui/LearningMiniApp.js",
|
|
20
|
+
"./*": "./*"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
24
|
+
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
25
|
+
"build": "bun build:types && bun build:bundle",
|
|
26
|
+
"build:bundle": "tsdown",
|
|
27
|
+
"build:types": "tsc --noEmit",
|
|
28
|
+
"dev": "bun build:bundle --watch",
|
|
29
|
+
"clean": "rimraf dist .turbo",
|
|
30
|
+
"lint": "bun lint:fix",
|
|
31
|
+
"test": "bun test",
|
|
32
|
+
"lint:fix": "eslint src --fix",
|
|
33
|
+
"lint:check": "eslint src"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@contractspec/lib.contracts": "0.0.0-canary-20260113170453",
|
|
37
|
+
"@contractspec/example.learning-journey-ambient-coach": "0.0.0-canary-20260113170453",
|
|
38
|
+
"@contractspec/example.learning-journey-duo-drills": "0.0.0-canary-20260113170453",
|
|
39
|
+
"@contractspec/example.learning-journey-crm-onboarding": "0.0.0-canary-20260113170453",
|
|
40
|
+
"@contractspec/example.learning-journey-platform-tour": "0.0.0-canary-20260113170453",
|
|
41
|
+
"@contractspec/example.learning-journey-quest-challenges": "0.0.0-canary-20260113170453",
|
|
42
|
+
"@contractspec/example.learning-journey-studio-onboarding": "0.0.0-canary-20260113170453",
|
|
43
|
+
"@contractspec/example.learning-journey-ui-shared": "0.0.0-canary-20260113170453",
|
|
44
|
+
"@contractspec/example.learning-journey-ui-gamified": "0.0.0-canary-20260113170453",
|
|
45
|
+
"@contractspec/example.learning-journey-ui-onboarding": "0.0.0-canary-20260113170453",
|
|
46
|
+
"@contractspec/example.learning-journey-ui-coaching": "0.0.0-canary-20260113170453",
|
|
47
|
+
"@contractspec/module.learning-journey": "0.0.0-canary-20260113170453",
|
|
48
|
+
"react": "19.2.3"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@contractspec/tool.tsdown": "0.0.0-canary-20260113170453",
|
|
52
|
+
"@contractspec/tool.typescript": "0.0.0-canary-20260113170453",
|
|
53
|
+
"@types/react": "^19.2.8",
|
|
54
|
+
"tsdown": "^0.19.0",
|
|
55
|
+
"typescript": "^5.9.3"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": "^19.2.3"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"exports": {
|
|
62
|
+
".": "./dist/index.js",
|
|
63
|
+
"./api": "./dist/api.js",
|
|
64
|
+
"./api-types": "./dist/api-types.js",
|
|
65
|
+
"./docs": "./dist/docs/index.js",
|
|
66
|
+
"./docs/learning-journey-registry.docblock": "./dist/docs/learning-journey-registry.docblock.js",
|
|
67
|
+
"./example": "./dist/example.js",
|
|
68
|
+
"./learning-journey-registry.feature": "./dist/learning-journey-registry.feature.js",
|
|
69
|
+
"./presentations": "./dist/presentations/index.js",
|
|
70
|
+
"./progress-store": "./dist/progress-store.js",
|
|
71
|
+
"./tracks": "./dist/tracks.js",
|
|
72
|
+
"./ui": "./dist/ui/index.js",
|
|
73
|
+
"./ui/LearningMiniApp": "./dist/ui/LearningMiniApp.js",
|
|
74
|
+
"./*": "./*"
|
|
75
|
+
},
|
|
76
|
+
"registry": "https://registry.npmjs.org/",
|
|
77
|
+
"access": "public"
|
|
78
|
+
},
|
|
79
|
+
"license": "MIT",
|
|
80
|
+
"repository": {
|
|
81
|
+
"type": "git",
|
|
82
|
+
"url": "https://github.com/lssm-tech/contractspec.git",
|
|
83
|
+
"directory": "packages/examples/learning-journey-registry"
|
|
84
|
+
},
|
|
85
|
+
"homepage": "https://contractspec.io"
|
|
86
|
+
}
|
package/src/api-types.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LearningJourneyTrackSpec } from '@contractspec/module.learning-journey/track-spec';
|
|
2
|
+
|
|
3
|
+
export interface LearningEvent {
|
|
4
|
+
name: string;
|
|
5
|
+
version?: number;
|
|
6
|
+
sourceModule?: string;
|
|
7
|
+
payload?: Record<string, unknown>;
|
|
8
|
+
occurredAt?: Date;
|
|
9
|
+
learnerId: string;
|
|
10
|
+
trackId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type StepStatus = 'PENDING' | 'COMPLETED';
|
|
14
|
+
|
|
15
|
+
export interface StepProgress {
|
|
16
|
+
id: string;
|
|
17
|
+
status: StepStatus;
|
|
18
|
+
xpEarned: number;
|
|
19
|
+
completedAt?: Date;
|
|
20
|
+
triggeringEvent?: string;
|
|
21
|
+
eventPayload?: Record<string, unknown>;
|
|
22
|
+
occurrences?: number;
|
|
23
|
+
counterStartedAt?: Date;
|
|
24
|
+
availableAt?: Date;
|
|
25
|
+
dueAt?: Date;
|
|
26
|
+
masteryCount?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TrackProgress {
|
|
30
|
+
learnerId: string;
|
|
31
|
+
trackId: string;
|
|
32
|
+
progress: number;
|
|
33
|
+
isCompleted: boolean;
|
|
34
|
+
xpEarned: number;
|
|
35
|
+
startedAt?: Date;
|
|
36
|
+
completedAt?: Date;
|
|
37
|
+
lastActivityAt?: Date;
|
|
38
|
+
steps: StepProgress[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type TrackResolver = (
|
|
42
|
+
trackId: string
|
|
43
|
+
) => LearningJourneyTrackSpec | undefined;
|
package/src/api.test.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { recordEvent, getProgress, listTracks } from './api';
|
|
4
|
+
import { learningJourneyTracks } from './tracks';
|
|
5
|
+
|
|
6
|
+
const learnerId = 'learner-1';
|
|
7
|
+
|
|
8
|
+
describe('learning journey registry api', () => {
|
|
9
|
+
it('completes studio track and applies streak bonus', () => {
|
|
10
|
+
const track = learningJourneyTracks.find(
|
|
11
|
+
(t) => t.id === 'studio_getting_started'
|
|
12
|
+
);
|
|
13
|
+
expect(track).toBeDefined();
|
|
14
|
+
|
|
15
|
+
const events = [
|
|
16
|
+
{ name: 'studio.template.instantiated' },
|
|
17
|
+
{ name: 'spec.changed', payload: { scope: 'sandbox' } },
|
|
18
|
+
{ name: 'regeneration.completed' },
|
|
19
|
+
{ name: 'module.navigated', payload: { moduleId: 'canvas' } },
|
|
20
|
+
{ name: 'studio.evolution.applied' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
events.forEach((evt) =>
|
|
24
|
+
recordEvent({
|
|
25
|
+
...evt,
|
|
26
|
+
learnerId,
|
|
27
|
+
occurredAt: new Date(),
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const progress = getProgress('studio_getting_started', learnerId);
|
|
32
|
+
expect(progress).toBeDefined();
|
|
33
|
+
if (!progress) return;
|
|
34
|
+
|
|
35
|
+
expect(progress.isCompleted).toBeTrue();
|
|
36
|
+
expect(progress.progress).toBe(100);
|
|
37
|
+
// base xp: track totalXp (110) + completion bonus (25) + streak bonus (25)
|
|
38
|
+
expect(progress.xpEarned).toBeGreaterThanOrEqual(160);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('lists tracks with empty progress for new learner', () => {
|
|
42
|
+
const result = listTracks('new-learner');
|
|
43
|
+
expect(result.tracks.length).toBeGreaterThan(0);
|
|
44
|
+
expect(result.progress.length).toBe(0);
|
|
45
|
+
});
|
|
46
|
+
});
|