@burdenoff/microfe-technospam 2026.1030.4 → 2026.1030.6

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 (67) hide show
  1. package/dist/generated/wspace-types.d.ts +1501 -11
  2. package/dist/generated/wspace-types.d.ts.map +1 -1
  3. package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
  4. package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
  5. package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
  6. package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
  7. package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
  8. package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
  9. package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
  10. package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
  11. package/dist/node_modules/sonner/dist/index.js.map +1 -1
  12. package/dist/stories/fixtures/technospamFixtures.d.ts +1 -0
  13. package/dist/stories/fixtures/technospamFixtures.d.ts.map +1 -1
  14. package/dist/stories/journeys/Courses.stories.d.ts +5 -2
  15. package/dist/stories/journeys/Courses.stories.d.ts.map +1 -1
  16. package/dist/stories/journeys/Dashboard.stories.d.ts +2 -2
  17. package/dist/stories/journeys/Dashboard.stories.d.ts.map +1 -1
  18. package/dist/technospam/TechnoSpamRoutes.d.ts.map +1 -1
  19. package/dist/technospam/TechnoSpamRoutes.js +31 -27
  20. package/dist/technospam/TechnoSpamRoutes.js.map +1 -1
  21. package/dist/technospam/api/technospamApi.d.ts +101 -5
  22. package/dist/technospam/api/technospamApi.d.ts.map +1 -1
  23. package/dist/technospam/api/technospamApi.js +278 -146
  24. package/dist/technospam/api/technospamApi.js.map +1 -1
  25. package/dist/technospam/components/lesson/InteractiveWorkspace.d.ts +15 -0
  26. package/dist/technospam/components/lesson/InteractiveWorkspace.d.ts.map +1 -0
  27. package/dist/technospam/components/lesson/InteractiveWorkspace.js +198 -0
  28. package/dist/technospam/components/lesson/InteractiveWorkspace.js.map +1 -0
  29. package/dist/technospam/components/lesson/LessonKindFields.d.ts +15 -0
  30. package/dist/technospam/components/lesson/LessonKindFields.d.ts.map +1 -0
  31. package/dist/technospam/components/lesson/LessonKindFields.js +150 -0
  32. package/dist/technospam/components/lesson/LessonKindFields.js.map +1 -0
  33. package/dist/technospam/components/lesson/QuizLesson.d.ts +14 -0
  34. package/dist/technospam/components/lesson/QuizLesson.d.ts.map +1 -0
  35. package/dist/technospam/components/lesson/QuizLesson.js +105 -0
  36. package/dist/technospam/components/lesson/QuizLesson.js.map +1 -0
  37. package/dist/technospam/components/lesson/lessonAuthoring.d.ts +47 -0
  38. package/dist/technospam/components/lesson/lessonAuthoring.d.ts.map +1 -0
  39. package/dist/technospam/components/lesson/lessonAuthoring.js +80 -0
  40. package/dist/technospam/components/lesson/lessonAuthoring.js.map +1 -0
  41. package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts +1 -1
  42. package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts.map +1 -1
  43. package/dist/technospam/hooks/useTechnoSpamAnalytics.js.map +1 -1
  44. package/dist/technospam/pages/AdminAdminDashboardPagePage.d.ts.map +1 -1
  45. package/dist/technospam/pages/AdminAdminDashboardPagePage.js +230 -207
  46. package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
  47. package/dist/technospam/pages/CoursesCourseDetailsPagePage.d.ts.map +1 -1
  48. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +130 -132
  49. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
  50. package/dist/technospam/pages/CoursesLessonPlayerPagePage.d.ts +9 -0
  51. package/dist/technospam/pages/CoursesLessonPlayerPagePage.d.ts.map +1 -0
  52. package/dist/technospam/pages/CoursesLessonPlayerPagePage.js +291 -0
  53. package/dist/technospam/pages/CoursesLessonPlayerPagePage.js.map +1 -0
  54. package/dist/technospam/pages/CoursesMyLearningPagePage.d.ts.map +1 -1
  55. package/dist/technospam/pages/CoursesMyLearningPagePage.js +80 -78
  56. package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
  57. package/dist/technospam/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  58. package/dist/technospam/pages/DashboardDashboardPagePage.js +136 -133
  59. package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
  60. package/dist/technospam/utils/lessonPath.d.ts +15 -0
  61. package/dist/technospam/utils/lessonPath.d.ts.map +1 -0
  62. package/dist/technospam/utils/lessonPath.js +15 -0
  63. package/dist/technospam/utils/lessonPath.js.map +1 -0
  64. package/dist/technospam/utils/pageContext.d.ts.map +1 -1
  65. package/dist/technospam/utils/pageContext.js +10 -0
  66. package/dist/technospam/utils/pageContext.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/technospam/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowRight,\n Award,\n BookOpen,\n Calendar,\n CalendarClock,\n CheckCircle2,\n Clock,\n Code2,\n GraduationCap,\n PlayCircle,\n Trophy,\n Users,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecCourse,\n type TecEnrollment,\n type TecHackathon,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { useTr } from '../hooks/useTr';\n\n/* --------------------------------------------------------------- helpers */\n\nfunction SectionHeader({\n title,\n actionLabel,\n onAction,\n}: {\n title: string;\n actionLabel?: string;\n onAction?: () => void;\n}) {\n return (\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n {actionLabel && onAction ? (\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={onAction}>\n {actionLabel} <ArrowRight size={13} />\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction ProgressBar({ pct }: { pct: number }) {\n return (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${Math.max(0, Math.min(100, pct))}%` }}\n />\n </div>\n );\n}\n\nfunction ContinueLearningCard({ e, onOpen }: { e: TecEnrollment; onOpen: () => void }) {\n const tr = useTr();\n const title = e.course?.title ?? tr('technospam.common.courseFallback', 'Course');\n const seed = `${e.course?.topic ?? ''} ${title}`;\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={seed} icon={topicIcon(e.course?.topic ?? title)} height=\"h-20\">\n {e.course?.level ? <StatusBadge status={e.course.level} kind=\"level\" /> : <span />}\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-white/15 text-white backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <PlayCircle size={16} />\n </span>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-2.5 p-3.5\">\n <div className=\"flex items-start gap-2\">\n <h3 className=\"line-clamp-2 flex-1 text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {title}\n </h3>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-auto flex items-center gap-2.5\">\n <div className=\"flex-1\">\n <ProgressBar pct={e.progressPct} />\n </div>\n <span className=\"shrink-0 text-xs font-semibold text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n </div>\n </button>\n );\n}\n\nfunction RecommendedCourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n height=\"h-20\"\n >\n <StatusBadge status={course.level} kind=\"level\" />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-1.5 p-3.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{course.summary}</p>\n ) : null}\n {course.topic ? (\n <span className=\"mt-auto truncate pt-0.5 text-xs text-text-muted\">{course.topic}</span>\n ) : null}\n </div>\n </button>\n );\n}\n\nfunction SessionRow({ s }: { s: TecMentorSession }) {\n return (\n <div className=\"-mx-2 flex items-start gap-3 rounded-lg border-b border-border-seam px-2 py-3 transition-colors last:border-0 hover:bg-bg-sunken\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <CalendarClock size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{s.topic}</span>\n <StatusBadge status={s.status} />\n </div>\n {s.mentor?.headline ? (\n <div className=\"truncate text-xs text-text-muted\">{s.mentor.headline}</div>\n ) : null}\n <div className=\"mt-0.5 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock size={12} className=\"text-text-muted\" />\n {formatDateTime(s.scheduledAt)}\n </div>\n </div>\n </div>\n );\n}\n\nfunction HackathonBanner({ h, onOpen }: { h: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group block w-full overflow-hidden rounded-xl border border-border-seam text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={`${h.theme ?? ''} ${h.title}`} icon={Trophy} height=\"h-24\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-white/80\">\n <Trophy size={12} /> {tr('technospam.common.openHackathon', 'Open hackathon')}\n </div>\n <h3 className=\"mt-0.5 truncate text-sm font-semibold text-white drop-shadow-sm\">\n {h.title}\n </h3>\n </div>\n {h.prizePool > 0 ? (\n <span className=\"shrink-0 rounded-full bg-white/20 px-2.5 py-1 text-xs font-semibold text-white backdrop-blur\">\n {formatMoney(h.prizePool, h.currency)}\n </span>\n ) : null}\n </MediaCover>\n <div className=\"flex items-center justify-between gap-2 bg-bg-surface px-3.5 py-2.5\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Calendar size={12} />\n {formatDate(h.startAt)} – {formatDate(h.endAt)}\n </span>\n <span className=\"flex items-center gap-1 text-xs font-medium text-accent-primary opacity-0 transition-opacity group-hover:opacity-100\">\n {tr('technospam.common.register', 'Register')} <ArrowRight size={12} />\n </span>\n </div>\n </button>\n );\n}\n\n/* ------------------------------------------------------------------ page */\n\nexport function DashboardDashboardPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const tr = useTr();\n const learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.data?.tecListEnrollments?.items ?? [];\n const submissions = submissionsQ.data?.tecListSubmissions?.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions?.items ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\n const courses = coursesQ.data?.tecListCourses?.items ?? [];\n const hackathons = hackathonsQ.data?.tecListHackathons?.items ?? [];\n\n const greetingName =\n currentUser?.firstName || currentUser?.name || currentUser?.email?.split('@')[0] || 'there';\n\n const inProgress = useMemo(\n () => enrollments.filter((e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'),\n [enrollments]\n );\n\n const continueLearning = useMemo(\n () =>\n [...inProgress]\n .sort(\n (a, b) =>\n new Date(b.lastActiveAt ?? b.startedAt).getTime() -\n new Date(a.lastActiveAt ?? a.startedAt).getTime()\n )\n .slice(0, 3),\n [inProgress]\n );\n\n const problemsSolved = useMemo(\n () => new Set(submissions.filter((s) => s.verdict === 'ACCEPTED').map((s) => s.problemId)).size,\n [submissions]\n );\n\n // Stable \"now\" captured once on mount (state initializers may call impure fns).\n const [now] = useState(() => Date.now());\n const upcomingSessions = useMemo(() => {\n return [...sessions]\n .filter(\n (s) =>\n (s.status === 'CONFIRMED' || s.status === 'REQUESTED') &&\n new Date(s.scheduledAt).getTime() > now\n )\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime());\n }, [sessions, now]);\n\n const recommended = useMemo(() => {\n const enrolledIds = new Set(enrollments.map((e) => e.courseId));\n return courses.filter((c) => !enrolledIds.has(c.id)).slice(0, 4);\n }, [courses, enrollments]);\n\n const openHackathons = hackathons.slice(0, 4);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.dashboard.title', 'Welcome back, {{name}}', { name: greetingName })}\n description={tr(\n 'technospam.dashboard.description',\n \"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.dashboard.myLearning', 'My Learning')}\n </Button>\n }\n >\n {/* Top metrics */}\n <div className=\"mb-5 grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatCard\n label={tr('technospam.dashboard.coursesInProgress', 'Courses in progress')}\n value={inProgress.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.dashboard.problemsSolved', 'Problems solved')}\n value={problemsSolved}\n icon={Code2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.dashboard.upcomingSessions', 'Upcoming sessions')}\n value={upcomingSessions.length}\n icon={CalendarClock}\n tone=\"amber\"\n />\n <StatCard\n label={tr('technospam.dashboard.certificates', 'Certificates')}\n value={certificates.length}\n icon={Award}\n tone=\"purple\"\n />\n </div>\n\n {/* Quick actions */}\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/courses/courses-page')}\n >\n <BookOpen size={18} />\n <span className=\"text-xs\">{tr('technospam.common.browseCourses', 'Browse courses')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/applications/problems-page')}\n >\n <Code2 size={18} />\n <span className=\"text-xs\">\n {tr('technospam.common.practiceProblems', 'Practice problems')}\n </span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/mentorship/mentors-page')}\n >\n <Users size={18} />\n <span className=\"text-xs\">{tr('technospam.common.findMentor', 'Find a mentor')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/hackathons/hackathons-page')}\n >\n <Trophy size={18} />\n <span className=\"text-xs\">{tr('technospam.common.hackathons', 'Hackathons')}</span>\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* Continue learning */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.continueLearning', 'Continue learning')}\n actionLabel={tr('technospam.dashboard.myLearning', 'My Learning')}\n onAction={() => navigate('/courses/my-learning-page')}\n />\n {enrollmentsQ.loading && !enrollments.length ? (\n <LoadingRows count={2} height=\"h-16\" />\n ) : enrollmentsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load your courses\")}\n error={enrollmentsQ.error}\n />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title={tr(\n 'technospam.dashboard.noCoursesInProgressTitle',\n 'No courses in progress'\n )}\n description={tr(\n 'technospam.dashboard.noCoursesInProgressDesc',\n 'Enroll in a course to start building skill by doing — your active courses show up here.'\n )}\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>\n {tr('technospam.common.browseCourses', 'Browse courses')}\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {continueLearning.map((e) => (\n <ContinueLearningCard\n key={e.id}\n e={e}\n onOpen={() => navigate(`/courses/course-details-page?id=${e.courseId}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.recommendedForYou', 'Recommended for you')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/courses/courses-page')}\n />\n {coursesQ.loading && !courses.length ? (\n <LoadingRows count={2} height=\"h-20\" />\n ) : coursesQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={coursesQ.error}\n />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title={tr(\n 'technospam.dashboard.noRecommendationsTitle',\n 'No courses to recommend yet'\n )}\n description={tr(\n 'technospam.dashboard.noRecommendationsDesc',\n 'New courses are on the way. TechnoSpam is pre-launch — check back soon.'\n )}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {recommended.map((c) => (\n <RecommendedCourseCard\n key={c.id}\n course={c}\n onOpen={() => navigate(`/courses/course-details-page?id=${c.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Side column */}\n <div className=\"space-y-6\">\n {/* Upcoming sessions */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.upcomingSessionsTitle', 'Upcoming sessions')}\n actionLabel={tr('technospam.common.allSessions', 'All sessions')}\n onAction={() => navigate('/mentorship/sessions-page')}\n />\n {sessionsQ.loading && !sessions.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : sessionsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadSessions', \"Couldn't load sessions\")}\n error={sessionsQ.error}\n />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title={tr('technospam.dashboard.noSessionsBookedTitle', 'No sessions booked')}\n description={tr(\n 'technospam.dashboard.noSessionsBookedDesc',\n 'Book time with a mentor to get unblocked — your upcoming sessions appear here.'\n )}\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n />\n ) : (\n <div>\n {upcomingSessions.slice(0, 5).map((s) => (\n <SessionRow key={s.id} s={s} />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Open hackathons */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.openHackathons', 'Open hackathons')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/hackathons/hackathons-page')}\n />\n {hackathonsQ.loading && !hackathons.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : hackathonsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={hackathonsQ.error}\n />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title={tr('technospam.dashboard.noOpenHackathonsTitle', 'No open hackathons')}\n description={tr(\n 'technospam.dashboard.noOpenHackathonsDesc',\n 'When registration opens for a hackathon, it will show up here to join.'\n )}\n />\n ) : (\n <div className=\"space-y-3\">\n {openHackathons.map((h) => (\n <HackathonBanner\n key={h.id}\n h={h}\n onOpen={() => navigate(`/hackathons/hackathon-details-page?id=${h.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Certificates summary */}\n {certificates.length > 0 ? (\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.common.latestCertificate', 'Latest certificate')}\n />\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-accent-primary/12 text-accent-teal\">\n <CheckCircle2 size={20} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {certificates[0]?.course?.title ??\n tr('technospam.common.courseFallback', 'Course')}\n </div>\n <div className=\"text-xs text-text-muted\">\n {tr('technospam.common.score', 'Score')} {certificates[0]?.score} ·{' '}\n {tr('technospam.common.issued', 'Issued')}{' '}\n {formatDate(certificates[0]?.issuedAt)}\n </div>\n </div>\n </div>\n </GlassCard>\n ) : null}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardDashboardPagePage.js","names":[],"sources":["../../../src/technospam/pages/DashboardDashboardPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowRight,\n Award,\n BookOpen,\n Calendar,\n CalendarClock,\n CheckCircle2,\n Clock,\n Code2,\n GraduationCap,\n PlayCircle,\n Trophy,\n Users,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\n useMyNextLessonsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecCourse,\n type TecEnrollment,\n type TecHackathon,\n type TecMentorSession,\n} from '../api/technospamApi';\nimport { formatDate, formatDateTime, formatMoney } from '../utils/format';\nimport { useTr } from '../hooks/useTr';\nimport { resumePath } from '../utils/lessonPath';\n\n/* --------------------------------------------------------------- helpers */\n\nfunction SectionHeader({\n title,\n actionLabel,\n onAction,\n}: {\n title: string;\n actionLabel?: string;\n onAction?: () => void;\n}) {\n return (\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">{title}</h2>\n {actionLabel && onAction ? (\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={onAction}>\n {actionLabel} <ArrowRight size={13} />\n </Button>\n ) : null}\n </div>\n );\n}\n\nfunction ProgressBar({ pct }: { pct: number }) {\n return (\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${Math.max(0, Math.min(100, pct))}%` }}\n />\n </div>\n );\n}\n\nfunction ContinueLearningCard({\n e,\n resumeLessonId,\n onOpen,\n}: {\n e: TecEnrollment;\n /** The lesson this card resumes into, once known (BOFF-4107). */\n resumeLessonId?: string | null;\n onOpen: () => void;\n}) {\n const tr = useTr();\n const title = e.course?.title ?? tr('technospam.common.courseFallback', 'Course');\n const seed = `${e.course?.topic ?? ''} ${title}`;\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n data-resume-lesson={resumeLessonId ?? undefined}\n className=\"group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={seed} icon={topicIcon(e.course?.topic ?? title)} height=\"h-20\">\n {e.course?.level ? <StatusBadge status={e.course.level} kind=\"level\" /> : <span />}\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-white/15 text-white backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <PlayCircle size={16} />\n </span>\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-2.5 p-3.5\">\n <div className=\"flex items-start gap-2\">\n <h3 className=\"line-clamp-2 flex-1 text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {title}\n </h3>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-auto flex items-center gap-2.5\">\n <div className=\"flex-1\">\n <ProgressBar pct={e.progressPct} />\n </div>\n <span className=\"shrink-0 text-xs font-semibold text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n </div>\n </button>\n );\n}\n\nfunction RecommendedCourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group flex w-full flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n height=\"h-20\"\n >\n <StatusBadge status={course.level} kind=\"level\" />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-1.5 p-3.5\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{course.summary}</p>\n ) : null}\n {course.topic ? (\n <span className=\"mt-auto truncate pt-0.5 text-xs text-text-muted\">{course.topic}</span>\n ) : null}\n </div>\n </button>\n );\n}\n\nfunction SessionRow({ s }: { s: TecMentorSession }) {\n return (\n <div className=\"-mx-2 flex items-start gap-3 rounded-lg border-b border-border-seam px-2 py-3 transition-colors last:border-0 hover:bg-bg-sunken\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-accent-teal/15 text-accent-teal ring-1 ring-accent-teal/20\">\n <CalendarClock size={16} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{s.topic}</span>\n <StatusBadge status={s.status} />\n </div>\n {s.mentor?.headline ? (\n <div className=\"truncate text-xs text-text-muted\">{s.mentor.headline}</div>\n ) : null}\n <div className=\"mt-0.5 flex items-center gap-1.5 text-xs text-text-secondary\">\n <Clock size={12} className=\"text-text-muted\" />\n {formatDateTime(s.scheduledAt)}\n </div>\n </div>\n </div>\n );\n}\n\nfunction HackathonBanner({ h, onOpen }: { h: TecHackathon; onOpen: () => void }) {\n const tr = useTr();\n return (\n <button\n type=\"button\"\n onClick={onOpen}\n className=\"group block w-full overflow-hidden rounded-xl border border-border-seam text-left transition-all duration-200 hover:-translate-y-1 hover:shadow-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <MediaCover seed={`${h.theme ?? ''} ${h.title}`} icon={Trophy} height=\"h-24\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-white/80\">\n <Trophy size={12} /> {tr('technospam.common.openHackathon', 'Open hackathon')}\n </div>\n <h3 className=\"mt-0.5 truncate text-sm font-semibold text-white drop-shadow-sm\">\n {h.title}\n </h3>\n </div>\n {h.prizePool > 0 ? (\n <span className=\"shrink-0 rounded-full bg-white/20 px-2.5 py-1 text-xs font-semibold text-white backdrop-blur\">\n {formatMoney(h.prizePool, h.currency)}\n </span>\n ) : null}\n </MediaCover>\n <div className=\"flex items-center justify-between gap-2 bg-bg-surface px-3.5 py-2.5\">\n <span className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Calendar size={12} />\n {formatDate(h.startAt)} – {formatDate(h.endAt)}\n </span>\n <span className=\"flex items-center gap-1 text-xs font-medium text-accent-primary opacity-0 transition-opacity group-hover:opacity-100\">\n {tr('technospam.common.register', 'Register')} <ArrowRight size={12} />\n </span>\n </div>\n </button>\n );\n}\n\n/* ------------------------------------------------------------------ page */\n\nexport function DashboardDashboardPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const tr = useTr();\n const learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n // BOFF-4107: Continue Learning cards resume into the next incomplete lesson.\n const nextLessonsQ = useMyNextLessonsQuery();\n const nextLessonByCourse = useMemo(\n () =>\n new Map(\n (nextLessonsQ.data?.tecMyEnrollments?.items ?? []).map((n) => [\n n.courseId,\n n.nextLessonId ?? null,\n ])\n ),\n [nextLessonsQ.data]\n );\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({ status: 'REGISTRATION_OPEN' });\n\n const enrollments = enrollmentsQ.data?.tecListEnrollments?.items ?? [];\n const submissions = submissionsQ.data?.tecListSubmissions?.items ?? [];\n const sessions = sessionsQ.data?.tecListSessions?.items ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\n const courses = coursesQ.data?.tecListCourses?.items ?? [];\n const hackathons = hackathonsQ.data?.tecListHackathons?.items ?? [];\n\n const greetingName =\n currentUser?.firstName || currentUser?.name || currentUser?.email?.split('@')[0] || 'there';\n\n const inProgress = useMemo(\n () => enrollments.filter((e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'),\n [enrollments]\n );\n\n const continueLearning = useMemo(\n () =>\n [...inProgress]\n .sort(\n (a, b) =>\n new Date(b.lastActiveAt ?? b.startedAt).getTime() -\n new Date(a.lastActiveAt ?? a.startedAt).getTime()\n )\n .slice(0, 3),\n [inProgress]\n );\n\n const problemsSolved = useMemo(\n () => new Set(submissions.filter((s) => s.verdict === 'ACCEPTED').map((s) => s.problemId)).size,\n [submissions]\n );\n\n // Stable \"now\" captured once on mount (state initializers may call impure fns).\n const [now] = useState(() => Date.now());\n const upcomingSessions = useMemo(() => {\n return [...sessions]\n .filter(\n (s) =>\n (s.status === 'CONFIRMED' || s.status === 'REQUESTED') &&\n new Date(s.scheduledAt).getTime() > now\n )\n .sort((a, b) => new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime());\n }, [sessions, now]);\n\n const recommended = useMemo(() => {\n const enrolledIds = new Set(enrollments.map((e) => e.courseId));\n return courses.filter((c) => !enrolledIds.has(c.id)).slice(0, 4);\n }, [courses, enrollments]);\n\n const openHackathons = hackathons.slice(0, 4);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.dashboard.title', 'Welcome back, {{name}}', { name: greetingName })}\n description={tr(\n 'technospam.dashboard.description',\n \"Your learning hub — pick up where you left off, keep your skills sharp, and jump into what's next.\"\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.dashboard.myLearning', 'My Learning')}\n </Button>\n }\n >\n {/* Top metrics */}\n <div className=\"mb-5 grid grid-cols-2 gap-3 lg:grid-cols-4\">\n <StatCard\n label={tr('technospam.dashboard.coursesInProgress', 'Courses in progress')}\n value={inProgress.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.dashboard.problemsSolved', 'Problems solved')}\n value={problemsSolved}\n icon={Code2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.dashboard.upcomingSessions', 'Upcoming sessions')}\n value={upcomingSessions.length}\n icon={CalendarClock}\n tone=\"amber\"\n />\n <StatCard\n label={tr('technospam.dashboard.certificates', 'Certificates')}\n value={certificates.length}\n icon={Award}\n tone=\"purple\"\n />\n </div>\n\n {/* Quick actions */}\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/courses/courses-page')}\n >\n <BookOpen size={18} />\n <span className=\"text-xs\">{tr('technospam.common.browseCourses', 'Browse courses')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/applications/problems-page')}\n >\n <Code2 size={18} />\n <span className=\"text-xs\">\n {tr('technospam.common.practiceProblems', 'Practice problems')}\n </span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/mentorship/mentors-page')}\n >\n <Users size={18} />\n <span className=\"text-xs\">{tr('technospam.common.findMentor', 'Find a mentor')}</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"h-auto flex-col gap-2 py-4\"\n onClick={() => navigate('/hackathons/hackathons-page')}\n >\n <Trophy size={18} />\n <span className=\"text-xs\">{tr('technospam.common.hackathons', 'Hackathons')}</span>\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"space-y-6 lg:col-span-2\">\n {/* Continue learning */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.continueLearning', 'Continue learning')}\n actionLabel={tr('technospam.dashboard.myLearning', 'My Learning')}\n onAction={() => navigate('/courses/my-learning-page')}\n />\n {enrollmentsQ.loading && !enrollments.length ? (\n <LoadingRows count={2} height=\"h-16\" />\n ) : enrollmentsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load your courses\")}\n error={enrollmentsQ.error}\n />\n ) : continueLearning.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={24} />}\n title={tr(\n 'technospam.dashboard.noCoursesInProgressTitle',\n 'No courses in progress'\n )}\n description={tr(\n 'technospam.dashboard.noCoursesInProgressDesc',\n 'Enroll in a course to start building skill by doing — your active courses show up here.'\n )}\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>\n {tr('technospam.common.browseCourses', 'Browse courses')}\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {continueLearning.map((e) => (\n <ContinueLearningCard\n key={e.id}\n e={e}\n resumeLessonId={nextLessonByCourse.get(e.courseId)}\n onOpen={() =>\n navigate(resumePath(e.courseId, nextLessonByCourse.get(e.courseId)))\n }\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Recommended courses */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.recommendedForYou', 'Recommended for you')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/courses/courses-page')}\n />\n {coursesQ.loading && !courses.length ? (\n <LoadingRows count={2} height=\"h-20\" />\n ) : coursesQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadCourses', \"Couldn't load courses\")}\n error={coursesQ.error}\n />\n ) : recommended.length === 0 ? (\n <EmptyView\n icon={<GraduationCap size={24} />}\n title={tr(\n 'technospam.dashboard.noRecommendationsTitle',\n 'No courses to recommend yet'\n )}\n description={tr(\n 'technospam.dashboard.noRecommendationsDesc',\n 'New courses are on the way. TechnoSpam is pre-launch — check back soon.'\n )}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {recommended.map((c) => (\n <RecommendedCourseCard\n key={c.id}\n course={c}\n onOpen={() => navigate(`/courses/course-details-page?id=${c.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Side column */}\n <div className=\"space-y-6\">\n {/* Upcoming sessions */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.upcomingSessionsTitle', 'Upcoming sessions')}\n actionLabel={tr('technospam.common.allSessions', 'All sessions')}\n onAction={() => navigate('/mentorship/sessions-page')}\n />\n {sessionsQ.loading && !sessions.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : sessionsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadSessions', \"Couldn't load sessions\")}\n error={sessionsQ.error}\n />\n ) : upcomingSessions.length === 0 ? (\n <EmptyView\n icon={<CalendarClock size={24} />}\n title={tr('technospam.dashboard.noSessionsBookedTitle', 'No sessions booked')}\n description={tr(\n 'technospam.dashboard.noSessionsBookedDesc',\n 'Book time with a mentor to get unblocked — your upcoming sessions appear here.'\n )}\n action={\n <Button variant=\"outline\" onClick={() => navigate('/mentorship/mentors-page')}>\n {tr('technospam.common.findMentor', 'Find a mentor')}\n </Button>\n }\n />\n ) : (\n <div>\n {upcomingSessions.slice(0, 5).map((s) => (\n <SessionRow key={s.id} s={s} />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Open hackathons */}\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.dashboard.openHackathons', 'Open hackathons')}\n actionLabel={tr('technospam.common.viewAll', 'View all')}\n onAction={() => navigate('/hackathons/hackathons-page')}\n />\n {hackathonsQ.loading && !hackathons.length ? (\n <LoadingRows count={2} height=\"h-14\" />\n ) : hackathonsQ.error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadHackathons', \"Couldn't load hackathons\")}\n error={hackathonsQ.error}\n />\n ) : openHackathons.length === 0 ? (\n <EmptyView\n icon={<Trophy size={24} />}\n title={tr('technospam.dashboard.noOpenHackathonsTitle', 'No open hackathons')}\n description={tr(\n 'technospam.dashboard.noOpenHackathonsDesc',\n 'When registration opens for a hackathon, it will show up here to join.'\n )}\n />\n ) : (\n <div className=\"space-y-3\">\n {openHackathons.map((h) => (\n <HackathonBanner\n key={h.id}\n h={h}\n onOpen={() => navigate(`/hackathons/hackathon-details-page?id=${h.id}`)}\n />\n ))}\n </div>\n )}\n </GlassCard>\n\n {/* Certificates summary */}\n {certificates.length > 0 ? (\n <GlassCard className=\"p-5\">\n <SectionHeader\n title={tr('technospam.common.latestCertificate', 'Latest certificate')}\n />\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-accent-primary/12 text-accent-teal\">\n <CheckCircle2 size={20} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {certificates[0]?.course?.title ??\n tr('technospam.common.courseFallback', 'Course')}\n </div>\n <div className=\"text-xs text-text-muted\">\n {tr('technospam.common.score', 'Score')} {certificates[0]?.score} ·{' '}\n {tr('technospam.common.issued', 'Issued')}{' '}\n {formatDate(certificates[0]?.issuedAt)}\n </div>\n </div>\n </div>\n </GlassCard>\n ) : null}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * MFE-relative route of one lesson inside the lesson player (BOFF-4107):
3
+ * `/courses/lesson-player-page?id=<courseId>&lessonId=<lessonId>`. The same
4
+ * focus params as the course page, so the assistant's page context names the
5
+ * lesson the learner is on.
6
+ */
7
+ export declare function lessonPlayerPath(courseId: string, lessonId: string): string;
8
+ /** Course detail route, used when there is no lesson to resume into. */
9
+ export declare function courseDetailsPath(courseId: string): string;
10
+ /**
11
+ * Where "Resume" / "Continue learning" should land: the enrollment's next
12
+ * incomplete lesson when the API knows it, otherwise the course page.
13
+ */
14
+ export declare function resumePath(courseId: string, nextLessonId?: string | null): string;
15
+ //# sourceMappingURL=lessonPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessonPath.d.ts","sourceRoot":"","sources":["../../../src/technospam/utils/lessonPath.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEjF"}
@@ -0,0 +1,15 @@
1
+ import { FOCUS_PARAMS as e } from "./pageContext.js";
2
+ //#region src/technospam/utils/lessonPath.ts
3
+ function t(t, n) {
4
+ return `/courses/lesson-player-page?${e.id}=${encodeURIComponent(t)}&${e.lesson}=${encodeURIComponent(n)}`;
5
+ }
6
+ function n(t) {
7
+ return `/courses/course-details-page?${e.id}=${encodeURIComponent(t)}`;
8
+ }
9
+ function r(e, r) {
10
+ return r ? t(e, r) : n(e);
11
+ }
12
+ //#endregion
13
+ export { n as courseDetailsPath, t as lessonPlayerPath, r as resumePath };
14
+
15
+ //# sourceMappingURL=lessonPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessonPath.js","names":[],"sources":["../../../src/technospam/utils/lessonPath.ts"],"sourcesContent":["import { FOCUS_PARAMS } from './pageContext';\n\n/**\n * MFE-relative route of one lesson inside the lesson player (BOFF-4107):\n * `/courses/lesson-player-page?id=<courseId>&lessonId=<lessonId>`. The same\n * focus params as the course page, so the assistant's page context names the\n * lesson the learner is on.\n */\nexport function lessonPlayerPath(courseId: string, lessonId: string): string {\n return `/courses/lesson-player-page?${FOCUS_PARAMS.id}=${encodeURIComponent(courseId)}&${FOCUS_PARAMS.lesson}=${encodeURIComponent(lessonId)}`;\n}\n\n/** Course detail route, used when there is no lesson to resume into. */\nexport function courseDetailsPath(courseId: string): string {\n return `/courses/course-details-page?${FOCUS_PARAMS.id}=${encodeURIComponent(courseId)}`;\n}\n\n/**\n * Where \"Resume\" / \"Continue learning\" should land: the enrollment's next\n * incomplete lesson when the API knows it, otherwise the course page.\n */\nexport function resumePath(courseId: string, nextLessonId?: string | null): string {\n return nextLessonId ? lessonPlayerPath(courseId, nextLessonId) : courseDetailsPath(courseId);\n}\n"],"mappings":";;AAQA,SAAgB,EAAiB,GAAkB,GAA0B;AAC3E,QAAO,+BAA+B,EAAa,GAAG,GAAG,mBAAmB,EAAS,CAAC,GAAG,EAAa,OAAO,GAAG,mBAAmB,EAAS;;AAI9I,SAAgB,EAAkB,GAA0B;AAC1D,QAAO,gCAAgC,EAAa,GAAG,GAAG,mBAAmB,EAAS;;AAOxF,SAAgB,EAAW,GAAkB,GAAsC;AACjF,QAAO,IAAe,EAAiB,GAAU,EAAa,GAAG,EAAkB,EAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../../src/technospam/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,wLAW1B,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE1E,UAAU,YAAY;IACpB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,UAAU,cAAc;IACtB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CACnC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAqCrD,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAG1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IAGpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD,uFAAuF;AACvF,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAA6B,GACtC,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAA6B,GACtC,wBAAwB,CA4B1B;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAmB/C"}
1
+ {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../../src/technospam/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,wLAW1B,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;;;;CAQf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE1E,UAAU,YAAY;IACpB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,UAAU,cAAc;IACtB,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;CACnC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EA4CrD,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAG1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IAGpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAUD,uFAAuF;AACvF,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAA6B,GACtC,MAAM,GAAG,IAAI,CAIf;AAED,wBAAgB,oCAAoC,CAClD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAA6B,GACtC,wBAAwB,CA4B1B;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAmB/C"}
@@ -18,6 +18,16 @@ var e = "/technospam", t = {
18
18
  param: t.id
19
19
  }]
20
20
  },
21
+ {
22
+ route: "courses/lesson-player-page",
23
+ entities: [{
24
+ type: "TEC_LESSON",
25
+ param: t.lesson
26
+ }, {
27
+ type: "TEC_COURSE",
28
+ param: t.id
29
+ }]
30
+ },
21
31
  {
22
32
  route: "courses/my-learning-page",
23
33
  entities: [{
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.js","names":[],"sources":["../../../src/technospam/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n *\n * Routes mirror the live wspace-technospam-svc surface exposed in\n * TechnoSpamRoutes.tsx. Removed/redirected modules are not resolved.\n *\n * The forwarded payload is the cross-product contract from\n * `dev/platform/context/automation/ai-assistant` (Step 4): exactly\n * `currentUrl`, `currentPage`, `entityType`, `entityId`, `platform`,\n * `version` and `viewportSize`. Anything else (user agent, timestamps) stays\n * in the browser — the agent drops unknown keys anyway, and forwarding them is\n * the kind of drift that eventually becomes a leak.\n */\n\n/** Base path the app shell mounts the TechnoSpam microfrontend under. */\nexport const TECHNOSPAM_BASE_PATH = '/technospam';\n\n/**\n * Every entity type the page context can name. The ai-assistant content guide\n * (`context/api-calls/products/technospam.md`) documents how the agent reads\n * each one, so a new value here must be added there too.\n */\nexport const TECHNOSPAM_ENTITY_TYPES = [\n 'TEC_COURSE',\n 'TEC_LESSON',\n 'TEC_ENROLLMENT',\n 'TEC_CERTIFICATE',\n 'TEC_PROBLEM',\n 'TEC_SUBMISSION',\n 'TEC_MENTOR',\n 'TEC_MENTOR_SESSION',\n 'TEC_HACKATHON',\n 'TEC_HACKATHON_TEAM',\n] as const;\n\nexport type TechnoSpamEntityType = (typeof TECHNOSPAM_ENTITY_TYPES)[number];\n\n/**\n * Search params that name the record a page is focused on. Detail pages carry\n * their own record in `id`; a child record the user has focused on that page\n * (an expanded lesson, a selected submission or team) or a record focused on a\n * list page carries its own param. Pages write these through\n * `useFocusedRecordParam`, so the assistant can act on \"this session\" without\n * asking which one, and the focus survives a reload or a shared link.\n */\nexport const FOCUS_PARAMS = {\n id: 'id',\n lesson: 'lessonId',\n enrollment: 'enrollmentId',\n certificate: 'certificateId',\n submission: 'submissionId',\n session: 'sessionId',\n team: 'teamId',\n} as const;\n\nexport type FocusParam = (typeof FOCUS_PARAMS)[keyof typeof FOCUS_PARAMS];\n\ninterface EntitySource {\n type: TechnoSpamEntityType;\n param: FocusParam;\n}\n\ninterface TechnoSpamPage {\n /** Route relative to the MFE base path, as declared in TechnoSpamRoutes.tsx. */\n route: string;\n /** Checked in order: the first param present wins, so child focus beats the parent. */\n entities: readonly EntitySource[];\n}\n\n/**\n * Route → entity table. Children come first so a focused lesson, submission\n * or team is named instead of the course, problem or hackathon around it.\n */\nexport const TECHNOSPAM_PAGES: readonly TechnoSpamPage[] = [\n {\n route: 'courses/course-details-page',\n entities: [\n { type: 'TEC_LESSON', param: FOCUS_PARAMS.lesson },\n { type: 'TEC_COURSE', param: FOCUS_PARAMS.id },\n ],\n },\n {\n route: 'courses/my-learning-page',\n entities: [\n { type: 'TEC_CERTIFICATE', param: FOCUS_PARAMS.certificate },\n { type: 'TEC_ENROLLMENT', param: FOCUS_PARAMS.enrollment },\n ],\n },\n {\n route: 'applications/problem-details-page',\n entities: [\n { type: 'TEC_SUBMISSION', param: FOCUS_PARAMS.submission },\n { type: 'TEC_PROBLEM', param: FOCUS_PARAMS.id },\n ],\n },\n {\n route: 'mentorship/mentor-profile-page',\n entities: [{ type: 'TEC_MENTOR', param: FOCUS_PARAMS.id }],\n },\n {\n route: 'mentorship/sessions-page',\n entities: [{ type: 'TEC_MENTOR_SESSION', param: FOCUS_PARAMS.session }],\n },\n {\n route: 'hackathons/hackathon-details-page',\n entities: [\n { type: 'TEC_HACKATHON_TEAM', param: FOCUS_PARAMS.team },\n { type: 'TEC_HACKATHON', param: FOCUS_PARAMS.id },\n ],\n },\n];\n\nexport interface ConversationContextShape {\n entityType?: TechnoSpamEntityType;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PageContext {\n // `currentUrl` (NOT `activeUrl`) — must match the agent's allowed-context-key\n // contract, otherwise the agent filters it out.\n currentUrl: string;\n currentPage: string;\n // The entity the user is currently looking at, resolved from the route. Lets\n // the assistant act on \"this / current / here\" without asking for an id.\n entityType?: string;\n entityId?: string;\n platform: string;\n version: string;\n viewportSize: string;\n}\n\n/** Record ids are UUIDs or slug-like keys; anything else is not forwarded. */\nconst SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;\n\nfunction readId(params: URLSearchParams, param: FocusParam): string | undefined {\n const value = params.get(param)?.trim();\n return value && SAFE_ID.test(value) ? value : undefined;\n}\n\n/** The MFE-relative route (`courses/course-details-page`), or null outside the MFE. */\nexport function technoSpamRoute(\n pathname: string,\n basePath: string = TECHNOSPAM_BASE_PATH\n): string | null {\n const base = basePath.replace(/\\/+$/, '');\n if (pathname !== base && !pathname.startsWith(`${base}/`)) return null;\n return pathname.slice(base.length).split('/').filter(Boolean).join('/');\n}\n\nexport function resolveTechnoSpamConversationContext(\n pathname: string,\n search: string,\n basePath: string = TECHNOSPAM_BASE_PATH\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n const route = technoSpamRoute(pathname, basePath);\n\n const page = route === null ? undefined : TECHNOSPAM_PAGES.find((p) => p.route === route);\n if (page) {\n for (const source of page.entities) {\n const entityId = readId(params, source.param);\n if (entityId) {\n return {\n entityType: source.type,\n entityId,\n metadata: { route: page.route, param: source.param },\n };\n }\n }\n }\n\n const segments = (route ?? '').split('/').filter(Boolean);\n const activeTab =\n params.get('tab') ?? (segments.length > 1 ? segments[segments.length - 1] : undefined);\n\n return {\n activeTab: activeTab ?? undefined,\n metadata: {\n ...(activeTab ? { activeTab } : {}),\n },\n };\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const entity = resolveTechnoSpamConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n ...(entity.entityType && entity.entityId\n ? { entityType: entity.entityType, entityId: entity.entityId }\n : {}),\n platform: 'technospam',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.705.2',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n };\n}\n"],"mappings":";AAgBA,IAAa,IAAuB,eA8BvB,IAAe;CAC1B,IAAI;CACJ,QAAQ;CACR,YAAY;CACZ,aAAa;CACb,YAAY;CACZ,SAAS;CACT,MAAM;CACP,EAoBY,IAA8C;CACzD;EACE,OAAO;EACP,UAAU,CACR;GAAE,MAAM;GAAc,OAAO,EAAa;GAAQ,EAClD;GAAE,MAAM;GAAc,OAAO,EAAa;GAAI,CAC/C;EACF;CACD;EACE,OAAO;EACP,UAAU,CACR;GAAE,MAAM;GAAmB,OAAO,EAAa;GAAa,EAC5D;GAAE,MAAM;GAAkB,OAAO,EAAa;GAAY,CAC3D;EACF;CACD;EACE,OAAO;EACP,UAAU,CACR;GAAE,MAAM;GAAkB,OAAO,EAAa;GAAY,EAC1D;GAAE,MAAM;GAAe,OAAO,EAAa;GAAI,CAChD;EACF;CACD;EACE,OAAO;EACP,UAAU,CAAC;GAAE,MAAM;GAAc,OAAO,EAAa;GAAI,CAAC;EAC3D;CACD;EACE,OAAO;EACP,UAAU,CAAC;GAAE,MAAM;GAAsB,OAAO,EAAa;GAAS,CAAC;EACxE;CACD;EACE,OAAO;EACP,UAAU,CACR;GAAE,MAAM;GAAsB,OAAO,EAAa;GAAM,EACxD;GAAE,MAAM;GAAiB,OAAO,EAAa;GAAI,CAClD;EACF;CACF,EAwBK,IAAU;AAEhB,SAAS,EAAO,GAAyB,GAAuC;CAC9E,IAAM,IAAQ,EAAO,IAAI,EAAM,EAAE,MAAM;AACvC,QAAO,KAAS,EAAQ,KAAK,EAAM,GAAG,IAAQ,KAAA;;AAIhD,SAAgB,EACd,GACA,IAAmB,GACJ;CACf,IAAM,IAAO,EAAS,QAAQ,QAAQ,GAAG;AAEzC,QADI,MAAa,KAAQ,CAAC,EAAS,WAAW,GAAG,EAAK,GAAG,GAAS,OAC3D,EAAS,MAAM,EAAK,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;;AAGzE,SAAgB,EACd,GACA,GACA,IAAmB,GACO;CAC1B,IAAM,IAAS,IAAI,gBAAgB,EAAO,EACpC,IAAQ,EAAgB,GAAU,EAAS,EAE3C,IAAO,MAAU,OAAO,KAAA,IAAY,EAAiB,MAAM,MAAM,EAAE,UAAU,EAAM;AACzF,KAAI,EACF,MAAK,IAAM,KAAU,EAAK,UAAU;EAClC,IAAM,IAAW,EAAO,GAAQ,EAAO,MAAM;AAC7C,MAAI,EACF,QAAO;GACL,YAAY,EAAO;GACnB;GACA,UAAU;IAAE,OAAO,EAAK;IAAO,OAAO,EAAO;IAAO;GACrD;;CAKP,IAAM,KAAY,KAAS,IAAI,MAAM,IAAI,CAAC,OAAO,QAAQ,EACnD,IACJ,EAAO,IAAI,MAAM,KAAK,EAAS,SAAS,IAAI,EAAS,EAAS,SAAS,KAAK,KAAA;AAE9E,QAAO;EACL,WAAW,KAAa,KAAA;EACxB,UAAU,EACR,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE,EACnC;EACF;;AAGH,SAAgB,IAAiC;CAC/C,IAAM,IAAM,OAAO,SAAS,MAEtB,IADe,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,OAAO,QAAQ,CACvC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QACpD,IAAS,EACb,OAAO,SAAS,UAChB,OAAO,SAAS,OACjB;AAED,QAAO;EACL,YAAY;EACZ;EACA,GAAI,EAAO,cAAc,EAAO,WAC5B;GAAE,YAAY,EAAO;GAAY,UAAU,EAAO;GAAU,GAC5D,EAAE;EACN,UAAU;EACV,SAA6C;EAC7C,cAAc,GAAG,OAAO,WAAW,GAAG,OAAO;EAC9C"}
1
+ {"version":3,"file":"pageContext.js","names":[],"sources":["../../../src/technospam/utils/pageContext.ts"],"sourcesContent":["/**\n * Page context gathering — captures the user's current browser context\n * to be sent alongside every assistant prompt for better AI awareness.\n *\n * Routes mirror the live wspace-technospam-svc surface exposed in\n * TechnoSpamRoutes.tsx. Removed/redirected modules are not resolved.\n *\n * The forwarded payload is the cross-product contract from\n * `dev/platform/context/automation/ai-assistant` (Step 4): exactly\n * `currentUrl`, `currentPage`, `entityType`, `entityId`, `platform`,\n * `version` and `viewportSize`. Anything else (user agent, timestamps) stays\n * in the browser — the agent drops unknown keys anyway, and forwarding them is\n * the kind of drift that eventually becomes a leak.\n */\n\n/** Base path the app shell mounts the TechnoSpam microfrontend under. */\nexport const TECHNOSPAM_BASE_PATH = '/technospam';\n\n/**\n * Every entity type the page context can name. The ai-assistant content guide\n * (`context/api-calls/products/technospam.md`) documents how the agent reads\n * each one, so a new value here must be added there too.\n */\nexport const TECHNOSPAM_ENTITY_TYPES = [\n 'TEC_COURSE',\n 'TEC_LESSON',\n 'TEC_ENROLLMENT',\n 'TEC_CERTIFICATE',\n 'TEC_PROBLEM',\n 'TEC_SUBMISSION',\n 'TEC_MENTOR',\n 'TEC_MENTOR_SESSION',\n 'TEC_HACKATHON',\n 'TEC_HACKATHON_TEAM',\n] as const;\n\nexport type TechnoSpamEntityType = (typeof TECHNOSPAM_ENTITY_TYPES)[number];\n\n/**\n * Search params that name the record a page is focused on. Detail pages carry\n * their own record in `id`; a child record the user has focused on that page\n * (an expanded lesson, a selected submission or team) or a record focused on a\n * list page carries its own param. Pages write these through\n * `useFocusedRecordParam`, so the assistant can act on \"this session\" without\n * asking which one, and the focus survives a reload or a shared link.\n */\nexport const FOCUS_PARAMS = {\n id: 'id',\n lesson: 'lessonId',\n enrollment: 'enrollmentId',\n certificate: 'certificateId',\n submission: 'submissionId',\n session: 'sessionId',\n team: 'teamId',\n} as const;\n\nexport type FocusParam = (typeof FOCUS_PARAMS)[keyof typeof FOCUS_PARAMS];\n\ninterface EntitySource {\n type: TechnoSpamEntityType;\n param: FocusParam;\n}\n\ninterface TechnoSpamPage {\n /** Route relative to the MFE base path, as declared in TechnoSpamRoutes.tsx. */\n route: string;\n /** Checked in order: the first param present wins, so child focus beats the parent. */\n entities: readonly EntitySource[];\n}\n\n/**\n * Route → entity table. Children come first so a focused lesson, submission\n * or team is named instead of the course, problem or hackathon around it.\n */\nexport const TECHNOSPAM_PAGES: readonly TechnoSpamPage[] = [\n {\n route: 'courses/course-details-page',\n entities: [\n { type: 'TEC_LESSON', param: FOCUS_PARAMS.lesson },\n { type: 'TEC_COURSE', param: FOCUS_PARAMS.id },\n ],\n },\n {\n route: 'courses/lesson-player-page',\n entities: [\n { type: 'TEC_LESSON', param: FOCUS_PARAMS.lesson },\n { type: 'TEC_COURSE', param: FOCUS_PARAMS.id },\n ],\n },\n {\n route: 'courses/my-learning-page',\n entities: [\n { type: 'TEC_CERTIFICATE', param: FOCUS_PARAMS.certificate },\n { type: 'TEC_ENROLLMENT', param: FOCUS_PARAMS.enrollment },\n ],\n },\n {\n route: 'applications/problem-details-page',\n entities: [\n { type: 'TEC_SUBMISSION', param: FOCUS_PARAMS.submission },\n { type: 'TEC_PROBLEM', param: FOCUS_PARAMS.id },\n ],\n },\n {\n route: 'mentorship/mentor-profile-page',\n entities: [{ type: 'TEC_MENTOR', param: FOCUS_PARAMS.id }],\n },\n {\n route: 'mentorship/sessions-page',\n entities: [{ type: 'TEC_MENTOR_SESSION', param: FOCUS_PARAMS.session }],\n },\n {\n route: 'hackathons/hackathon-details-page',\n entities: [\n { type: 'TEC_HACKATHON_TEAM', param: FOCUS_PARAMS.team },\n { type: 'TEC_HACKATHON', param: FOCUS_PARAMS.id },\n ],\n },\n];\n\nexport interface ConversationContextShape {\n entityType?: TechnoSpamEntityType;\n entityId?: string;\n activeTab?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface PageContext {\n // `currentUrl` (NOT `activeUrl`) — must match the agent's allowed-context-key\n // contract, otherwise the agent filters it out.\n currentUrl: string;\n currentPage: string;\n // The entity the user is currently looking at, resolved from the route. Lets\n // the assistant act on \"this / current / here\" without asking for an id.\n entityType?: string;\n entityId?: string;\n platform: string;\n version: string;\n viewportSize: string;\n}\n\n/** Record ids are UUIDs or slug-like keys; anything else is not forwarded. */\nconst SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;\n\nfunction readId(params: URLSearchParams, param: FocusParam): string | undefined {\n const value = params.get(param)?.trim();\n return value && SAFE_ID.test(value) ? value : undefined;\n}\n\n/** The MFE-relative route (`courses/course-details-page`), or null outside the MFE. */\nexport function technoSpamRoute(\n pathname: string,\n basePath: string = TECHNOSPAM_BASE_PATH\n): string | null {\n const base = basePath.replace(/\\/+$/, '');\n if (pathname !== base && !pathname.startsWith(`${base}/`)) return null;\n return pathname.slice(base.length).split('/').filter(Boolean).join('/');\n}\n\nexport function resolveTechnoSpamConversationContext(\n pathname: string,\n search: string,\n basePath: string = TECHNOSPAM_BASE_PATH\n): ConversationContextShape {\n const params = new URLSearchParams(search);\n const route = technoSpamRoute(pathname, basePath);\n\n const page = route === null ? undefined : TECHNOSPAM_PAGES.find((p) => p.route === route);\n if (page) {\n for (const source of page.entities) {\n const entityId = readId(params, source.param);\n if (entityId) {\n return {\n entityType: source.type,\n entityId,\n metadata: { route: page.route, param: source.param },\n };\n }\n }\n }\n\n const segments = (route ?? '').split('/').filter(Boolean);\n const activeTab =\n params.get('tab') ?? (segments.length > 1 ? segments[segments.length - 1] : undefined);\n\n return {\n activeTab: activeTab ?? undefined,\n metadata: {\n ...(activeTab ? { activeTab } : {}),\n },\n };\n}\n\nexport function gatherPageContext(): PageContext {\n const url = window.location.href;\n const pathSegments = window.location.pathname.split('/').filter(Boolean);\n const currentPage = pathSegments.slice(0, 3).join('/') || 'home';\n const entity = resolveTechnoSpamConversationContext(\n window.location.pathname,\n window.location.search\n );\n\n return {\n currentUrl: url,\n currentPage,\n ...(entity.entityType && entity.entityId\n ? { entityType: entity.entityType, entityId: entity.entityId }\n : {}),\n platform: 'technospam',\n version: import.meta.env.VITE_APP_VERSION ?? '2026.705.2',\n viewportSize: `${window.innerWidth}x${window.innerHeight}`,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-technospam",
3
- "version": "2026.1030.4",
3
+ "version": "2026.1030.6",
4
4
  "description": "Burdenoff TechnoSpam Microfrontend",
5
5
  "type": "module",
6
6
  "files": [