@academy-sdk/sdk 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/dist/bundle.js +70 -0
  2. package/dist/manifest.json +5 -0
  3. package/dist/styles.css +3307 -0
  4. package/package.json +40 -46
  5. package/src/components/atoms/Avatar.tsx +38 -0
  6. package/src/components/atoms/Badge.tsx +32 -0
  7. package/src/components/atoms/Button.tsx +48 -0
  8. package/src/components/atoms/Card.tsx +33 -0
  9. package/src/components/atoms/Input.tsx +39 -0
  10. package/src/components/atoms/ProgressBar.tsx +52 -0
  11. package/src/components/atoms/Tabs.tsx +47 -0
  12. package/{dist/components/atoms/index.d.ts → src/components/atoms/index.ts} +0 -1
  13. package/{dist/components/index.d.ts → src/components/index.ts} +7 -1
  14. package/src/components/molecules/CourseCard.tsx +215 -0
  15. package/src/components/molecules/EmptyState.tsx +23 -0
  16. package/src/components/molecules/LoadingSpinner.tsx +27 -0
  17. package/src/components/molecules/PageHeader.tsx +22 -0
  18. package/src/components/molecules/Pagination.tsx +82 -0
  19. package/src/components/molecules/SearchInput.tsx +35 -0
  20. package/{dist/components/molecules/index.d.ts → src/components/molecules/index.ts} +0 -1
  21. package/src/components/organisms/CourseSidebar.tsx +276 -0
  22. package/src/components/organisms/LearnerNavbar.tsx +129 -0
  23. package/src/components/organisms/LearnerSidebar.tsx +148 -0
  24. package/src/components/organisms/LessonBookmarks.tsx +128 -0
  25. package/src/components/organisms/LessonNotes.tsx +153 -0
  26. package/{dist/components/organisms/index.d.ts → src/components/organisms/index.ts} +0 -1
  27. package/src/components/pages/BundleDetailPage.tsx +388 -0
  28. package/src/components/pages/CatalogBundlesPage.tsx +96 -0
  29. package/src/components/pages/CatalogCoursesPage.tsx +299 -0
  30. package/src/components/pages/CourseDetailPage.tsx +582 -0
  31. package/src/components/pages/CoursePlayerPage.tsx +481 -0
  32. package/src/components/pages/CreatorProfilePage.tsx +161 -0
  33. package/src/components/pages/LearnerSettingsPage.tsx +58 -0
  34. package/src/components/pages/ManualReviewDetailPage.tsx +254 -0
  35. package/src/components/pages/ManualReviewPage.tsx +228 -0
  36. package/src/components/pages/MessagesPage.tsx +285 -0
  37. package/src/components/pages/MyLearningPage.tsx +239 -0
  38. package/src/components/pages/PaymentCancelPage.tsx +74 -0
  39. package/src/components/pages/PaymentSuccessPage.tsx +73 -0
  40. package/{dist/components/pages/index.d.ts → src/components/pages/index.ts} +0 -1
  41. package/src/components/utils.ts +6 -0
  42. package/src/contracts/components.contract.ts +89 -0
  43. package/{dist/contracts/index.d.ts → src/contracts/index.ts} +0 -1
  44. package/src/contracts/layout.contract.ts +36 -0
  45. package/src/contracts/pages.contract.ts +275 -0
  46. package/src/contracts/template.contract.ts +100 -0
  47. package/src/default-template.tsx +52 -0
  48. package/{dist/hooks/index.d.ts → src/hooks/index.ts} +15 -1
  49. package/src/hooks/sdk-context.tsx +152 -0
  50. package/src/hooks/useAiCoach.ts +27 -0
  51. package/src/hooks/useBookmarks.ts +35 -0
  52. package/{dist/hooks/useCourseSearch.d.ts → src/hooks/useCourseSearch.ts} +8 -5
  53. package/{dist/hooks/useDebounce.d.ts → src/hooks/useDebounce.ts} +8 -2
  54. package/{dist/hooks/useMyBundles.d.ts → src/hooks/useMyBundles.ts} +8 -6
  55. package/{dist/hooks/useMyCourses.d.ts → src/hooks/useMyCourses.ts} +8 -6
  56. package/src/hooks/useNotes.ts +35 -0
  57. package/src/hooks/useNotifications.ts +16 -0
  58. package/{dist/hooks/useTheme.d.ts → src/hooks/useTheme.ts} +8 -5
  59. package/src/hooks/useToast.ts +17 -0
  60. package/{dist/hooks/useUser.d.ts → src/hooks/useUser.ts} +13 -9
  61. package/src/index.ts +33 -0
  62. package/src/layouts/DefaultLayout.tsx +58 -0
  63. package/src/manifest.json +5 -0
  64. package/src/styles.css +43 -0
  65. package/src/types/ai-coach.ts +25 -0
  66. package/src/types/bookmarks.ts +20 -0
  67. package/src/types/bundle.ts +119 -0
  68. package/src/types/common.ts +24 -0
  69. package/src/types/course.ts +135 -0
  70. package/src/types/enrollment.ts +35 -0
  71. package/{dist/types/index.d.ts → src/types/index.ts} +0 -1
  72. package/src/types/lesson.ts +106 -0
  73. package/src/types/manual-review.ts +116 -0
  74. package/src/types/messaging.ts +109 -0
  75. package/src/types/notification.ts +30 -0
  76. package/src/types/payment.ts +40 -0
  77. package/src/types/progress.ts +19 -0
  78. package/src/types/rating.ts +20 -0
  79. package/src/types/search.ts +31 -0
  80. package/src/types/user.ts +16 -0
  81. package/src/utils/formatters.ts +74 -0
  82. package/src/utils/index.ts +8 -0
  83. package/dist/components/atoms/Avatar.d.ts +0 -9
  84. package/dist/components/atoms/Avatar.d.ts.map +0 -1
  85. package/dist/components/atoms/Badge.d.ts +0 -10
  86. package/dist/components/atoms/Badge.d.ts.map +0 -1
  87. package/dist/components/atoms/Button.d.ts +0 -11
  88. package/dist/components/atoms/Button.d.ts.map +0 -1
  89. package/dist/components/atoms/Card.d.ts +0 -11
  90. package/dist/components/atoms/Card.d.ts.map +0 -1
  91. package/dist/components/atoms/Input.d.ts +0 -7
  92. package/dist/components/atoms/Input.d.ts.map +0 -1
  93. package/dist/components/atoms/ProgressBar.d.ts +0 -11
  94. package/dist/components/atoms/ProgressBar.d.ts.map +0 -1
  95. package/dist/components/atoms/Tabs.d.ts +0 -16
  96. package/dist/components/atoms/Tabs.d.ts.map +0 -1
  97. package/dist/components/atoms/index.cjs +0 -318
  98. package/dist/components/atoms/index.d.ts.map +0 -1
  99. package/dist/components/atoms/index.js +0 -288
  100. package/dist/components/index.cjs +0 -1275
  101. package/dist/components/index.d.ts.map +0 -1
  102. package/dist/components/index.js +0 -1245
  103. package/dist/components/molecules/CourseCard.d.ts +0 -25
  104. package/dist/components/molecules/CourseCard.d.ts.map +0 -1
  105. package/dist/components/molecules/EmptyState.d.ts +0 -10
  106. package/dist/components/molecules/EmptyState.d.ts.map +0 -1
  107. package/dist/components/molecules/LoadingSpinner.d.ts +0 -7
  108. package/dist/components/molecules/LoadingSpinner.d.ts.map +0 -1
  109. package/dist/components/molecules/PageHeader.d.ts +0 -8
  110. package/dist/components/molecules/PageHeader.d.ts.map +0 -1
  111. package/dist/components/molecules/Pagination.d.ts +0 -13
  112. package/dist/components/molecules/Pagination.d.ts.map +0 -1
  113. package/dist/components/molecules/SearchInput.d.ts +0 -8
  114. package/dist/components/molecules/SearchInput.d.ts.map +0 -1
  115. package/dist/components/molecules/index.cjs +0 -334
  116. package/dist/components/molecules/index.d.ts.map +0 -1
  117. package/dist/components/molecules/index.js +0 -311
  118. package/dist/components/organisms/CourseSidebar.d.ts +0 -37
  119. package/dist/components/organisms/CourseSidebar.d.ts.map +0 -1
  120. package/dist/components/organisms/LearnerNavbar.d.ts +0 -8
  121. package/dist/components/organisms/LearnerNavbar.d.ts.map +0 -1
  122. package/dist/components/organisms/LearnerSidebar.d.ts +0 -16
  123. package/dist/components/organisms/LearnerSidebar.d.ts.map +0 -1
  124. package/dist/components/organisms/LessonBookmarks.d.ts +0 -8
  125. package/dist/components/organisms/LessonBookmarks.d.ts.map +0 -1
  126. package/dist/components/organisms/LessonNotes.d.ts +0 -8
  127. package/dist/components/organisms/LessonNotes.d.ts.map +0 -1
  128. package/dist/components/organisms/index.cjs +0 -855
  129. package/dist/components/organisms/index.d.ts.map +0 -1
  130. package/dist/components/organisms/index.js +0 -825
  131. package/dist/components/pages/BundleDetailPage.d.ts +0 -3
  132. package/dist/components/pages/BundleDetailPage.d.ts.map +0 -1
  133. package/dist/components/pages/CatalogBundlesPage.d.ts +0 -3
  134. package/dist/components/pages/CatalogBundlesPage.d.ts.map +0 -1
  135. package/dist/components/pages/CatalogCoursesPage.d.ts +0 -3
  136. package/dist/components/pages/CatalogCoursesPage.d.ts.map +0 -1
  137. package/dist/components/pages/CourseDetailPage.d.ts +0 -3
  138. package/dist/components/pages/CourseDetailPage.d.ts.map +0 -1
  139. package/dist/components/pages/CoursePlayerPage.d.ts +0 -8
  140. package/dist/components/pages/CoursePlayerPage.d.ts.map +0 -1
  141. package/dist/components/pages/CreatorProfilePage.d.ts +0 -3
  142. package/dist/components/pages/CreatorProfilePage.d.ts.map +0 -1
  143. package/dist/components/pages/LearnerSettingsPage.d.ts +0 -3
  144. package/dist/components/pages/LearnerSettingsPage.d.ts.map +0 -1
  145. package/dist/components/pages/ManualReviewDetailPage.d.ts +0 -3
  146. package/dist/components/pages/ManualReviewDetailPage.d.ts.map +0 -1
  147. package/dist/components/pages/ManualReviewPage.d.ts +0 -3
  148. package/dist/components/pages/ManualReviewPage.d.ts.map +0 -1
  149. package/dist/components/pages/MessagesPage.d.ts +0 -3
  150. package/dist/components/pages/MessagesPage.d.ts.map +0 -1
  151. package/dist/components/pages/MyLearningPage.d.ts +0 -3
  152. package/dist/components/pages/MyLearningPage.d.ts.map +0 -1
  153. package/dist/components/pages/PaymentCancelPage.d.ts +0 -3
  154. package/dist/components/pages/PaymentCancelPage.d.ts.map +0 -1
  155. package/dist/components/pages/PaymentSuccessPage.d.ts +0 -3
  156. package/dist/components/pages/PaymentSuccessPage.d.ts.map +0 -1
  157. package/dist/components/pages/index.cjs +0 -3306
  158. package/dist/components/pages/index.d.ts.map +0 -1
  159. package/dist/components/pages/index.js +0 -3315
  160. package/dist/components/utils.d.ts +0 -3
  161. package/dist/components/utils.d.ts.map +0 -1
  162. package/dist/contracts/components.contract.d.ts +0 -87
  163. package/dist/contracts/components.contract.d.ts.map +0 -1
  164. package/dist/contracts/index.cjs +0 -52
  165. package/dist/contracts/index.d.ts.map +0 -1
  166. package/dist/contracts/index.js +0 -29
  167. package/dist/contracts/layout.contract.d.ts +0 -35
  168. package/dist/contracts/layout.contract.d.ts.map +0 -1
  169. package/dist/contracts/pages.contract.d.ts +0 -192
  170. package/dist/contracts/pages.contract.d.ts.map +0 -1
  171. package/dist/contracts/template.contract.d.ts +0 -49
  172. package/dist/contracts/template.contract.d.ts.map +0 -1
  173. package/dist/hooks/index.cjs +0 -165
  174. package/dist/hooks/index.d.ts.map +0 -1
  175. package/dist/hooks/index.js +0 -142
  176. package/dist/hooks/sdk-context.d.ts +0 -125
  177. package/dist/hooks/sdk-context.d.ts.map +0 -1
  178. package/dist/hooks/useAiCoach.d.ts +0 -32
  179. package/dist/hooks/useAiCoach.d.ts.map +0 -1
  180. package/dist/hooks/useBookmarks.d.ts +0 -31
  181. package/dist/hooks/useBookmarks.d.ts.map +0 -1
  182. package/dist/hooks/useCourseSearch.d.ts.map +0 -1
  183. package/dist/hooks/useDebounce.d.ts.map +0 -1
  184. package/dist/hooks/useMyBundles.d.ts.map +0 -1
  185. package/dist/hooks/useMyCourses.d.ts.map +0 -1
  186. package/dist/hooks/useNotes.d.ts +0 -31
  187. package/dist/hooks/useNotes.d.ts.map +0 -1
  188. package/dist/hooks/useNotifications.d.ts +0 -19
  189. package/dist/hooks/useNotifications.d.ts.map +0 -1
  190. package/dist/hooks/useTheme.d.ts.map +0 -1
  191. package/dist/hooks/useToast.d.ts +0 -17
  192. package/dist/hooks/useToast.d.ts.map +0 -1
  193. package/dist/hooks/useUser.d.ts.map +0 -1
  194. package/dist/index.cjs +0 -630
  195. package/dist/index.d.ts +0 -17
  196. package/dist/index.d.ts.map +0 -1
  197. package/dist/index.js +0 -600
  198. package/dist/layouts/DefaultLayout.d.ts +0 -9
  199. package/dist/layouts/DefaultLayout.d.ts.map +0 -1
  200. package/dist/types/ai-coach.d.ts +0 -22
  201. package/dist/types/ai-coach.d.ts.map +0 -1
  202. package/dist/types/bookmarks.d.ts +0 -19
  203. package/dist/types/bookmarks.d.ts.map +0 -1
  204. package/dist/types/bundle.d.ts +0 -114
  205. package/dist/types/bundle.d.ts.map +0 -1
  206. package/dist/types/common.d.ts +0 -23
  207. package/dist/types/common.d.ts.map +0 -1
  208. package/dist/types/course.d.ts +0 -127
  209. package/dist/types/course.d.ts.map +0 -1
  210. package/dist/types/enrollment.d.ts +0 -34
  211. package/dist/types/enrollment.d.ts.map +0 -1
  212. package/dist/types/index.cjs +0 -18
  213. package/dist/types/index.d.ts.map +0 -1
  214. package/dist/types/index.js +0 -0
  215. package/dist/types/lesson.d.ts +0 -105
  216. package/dist/types/lesson.d.ts.map +0 -1
  217. package/dist/types/manual-review.d.ts +0 -123
  218. package/dist/types/manual-review.d.ts.map +0 -1
  219. package/dist/types/messaging.d.ts +0 -101
  220. package/dist/types/messaging.d.ts.map +0 -1
  221. package/dist/types/notification.d.ts +0 -28
  222. package/dist/types/notification.d.ts.map +0 -1
  223. package/dist/types/payment.d.ts +0 -38
  224. package/dist/types/payment.d.ts.map +0 -1
  225. package/dist/types/progress.d.ts +0 -18
  226. package/dist/types/progress.d.ts.map +0 -1
  227. package/dist/types/rating.d.ts +0 -20
  228. package/dist/types/rating.d.ts.map +0 -1
  229. package/dist/types/search.d.ts +0 -28
  230. package/dist/types/search.d.ts.map +0 -1
  231. package/dist/types/user.d.ts +0 -15
  232. package/dist/types/user.d.ts.map +0 -1
  233. package/dist/utils/formatters.d.ts +0 -25
  234. package/dist/utils/formatters.d.ts.map +0 -1
  235. package/dist/utils/index.cjs +0 -80
  236. package/dist/utils/index.d.ts +0 -2
  237. package/dist/utils/index.d.ts.map +0 -1
  238. package/dist/utils/index.js +0 -57
@@ -1,855 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __defProps = Object.defineProperties;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
- var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
- var __getProtoOf = Object.getPrototypeOf;
10
- var __hasOwnProp = Object.prototype.hasOwnProperty;
11
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
- var __spreadValues = (a, b) => {
14
- for (var prop in b || (b = {}))
15
- if (__hasOwnProp.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- if (__getOwnPropSymbols)
18
- for (var prop of __getOwnPropSymbols(b)) {
19
- if (__propIsEnum.call(b, prop))
20
- __defNormalProp(a, prop, b[prop]);
21
- }
22
- return a;
23
- };
24
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
- var __objRest = (source, exclude) => {
26
- var target = {};
27
- for (var prop in source)
28
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
29
- target[prop] = source[prop];
30
- if (source != null && __getOwnPropSymbols)
31
- for (var prop of __getOwnPropSymbols(source)) {
32
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
33
- target[prop] = source[prop];
34
- }
35
- return target;
36
- };
37
- var __export = (target, all) => {
38
- for (var name in all)
39
- __defProp(target, name, { get: all[name], enumerable: true });
40
- };
41
- var __copyProps = (to, from, except, desc) => {
42
- if (from && typeof from === "object" || typeof from === "function") {
43
- for (let key of __getOwnPropNames(from))
44
- if (!__hasOwnProp.call(to, key) && key !== except)
45
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
46
- }
47
- return to;
48
- };
49
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
50
- // If the importer is in node compatibility mode or this is not an ESM
51
- // file that has been converted to a CommonJS file using a Babel-
52
- // compatible transform (i.e. "__esModule" has not been set), then set
53
- // "default" to the CommonJS "module.exports" for node compatibility.
54
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
55
- mod
56
- ));
57
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
58
-
59
- // src/components/organisms/index.ts
60
- var organisms_exports = {};
61
- __export(organisms_exports, {
62
- CourseSidebar: () => CourseSidebar,
63
- LearnerNavbar: () => LearnerNavbar,
64
- LearnerSidebar: () => LearnerSidebar,
65
- LessonBookmarks: () => LessonBookmarks,
66
- LessonNotes: () => LessonNotes
67
- });
68
- module.exports = __toCommonJS(organisms_exports);
69
-
70
- // src/components/organisms/LearnerNavbar.tsx
71
- var import_react2 = require("react");
72
- var import_lucide_react = require("lucide-react");
73
-
74
- // src/components/utils.ts
75
- var import_clsx = require("clsx");
76
- var import_tailwind_merge = require("tailwind-merge");
77
- function cn(...inputs) {
78
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
79
- }
80
-
81
- // src/hooks/sdk-context.tsx
82
- var import_react = require("react");
83
- var import_jsx_runtime = require("react/jsx-runtime");
84
- var SDKContext = (0, import_react.createContext)(null);
85
- function useSDK() {
86
- const ctx = (0, import_react.useContext)(SDKContext);
87
- if (!ctx) {
88
- throw new Error(
89
- "useSDK must be used within an SDKProvider. Make sure your learner routes are wrapped with <SDKProvider>."
90
- );
91
- }
92
- return ctx;
93
- }
94
-
95
- // src/components/organisms/LearnerNavbar.tsx
96
- var import_jsx_runtime2 = require("react/jsx-runtime");
97
- function LearnerNavbar({ hideMenuButton = false, onMenuClick, onProfileClick, onLogoClick }) {
98
- var _a;
99
- const sdk = useSDK();
100
- const { user } = sdk.useUser();
101
- const { logout } = sdk.useLogout();
102
- const [profileDropdownOpen, setProfileDropdownOpen] = (0, import_react2.useState)(false);
103
- const closeTimeoutRef = (0, import_react2.useRef)(null);
104
- (0, import_react2.useEffect)(() => {
105
- return () => {
106
- if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
107
- };
108
- }, []);
109
- const openDropdown = () => {
110
- if (closeTimeoutRef.current) {
111
- clearTimeout(closeTimeoutRef.current);
112
- closeTimeoutRef.current = null;
113
- }
114
- setProfileDropdownOpen(true);
115
- };
116
- const closeDropdownWithDelay = () => {
117
- if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
118
- closeTimeoutRef.current = setTimeout(() => {
119
- setProfileDropdownOpen(false);
120
- closeTimeoutRef.current = null;
121
- }, 150);
122
- };
123
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("header", { style: { top: "var(--preview-toolbar-h, 0px)" }, className: "fixed left-0 right-0 z-30 h-20 border-b border-theme-border-primary bg-theme-bg-secondary", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex h-full items-center justify-between px-4 sm:px-6 lg:px-10 xl:px-12 2xl:px-16", children: [
124
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center gap-4", children: !hideMenuButton && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
125
- "button",
126
- {
127
- onClick: onMenuClick,
128
- className: "rounded-lg p-2 hover:bg-theme-bg-tertiary lg:hidden",
129
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Menu, { className: "h-5 w-5 text-theme-text-primary" })
130
- }
131
- ) }),
132
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
133
- "div",
134
- {
135
- className: "relative group ml-2 sm:ml-4 border-l border-theme-border-primary pl-3 sm:pl-5",
136
- onMouseEnter: openDropdown,
137
- onMouseLeave: closeDropdownWithDelay,
138
- children: [
139
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
140
- "button",
141
- {
142
- onClick: () => setProfileDropdownOpen((o) => !o),
143
- className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 cursor-pointer transition-opacity",
144
- children: [
145
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "relative flex h-8 w-8 sm:h-10 sm:w-10 items-center justify-center rounded-full bg-theme-accent-primary text-sm font-semibold text-white overflow-hidden", children: (user == null ? void 0 : user.profileImage) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { src: user.profileImage, alt: "Profile", className: "w-full h-full object-cover" }) : ((_a = user == null ? void 0 : user.name) == null ? void 0 : _a.charAt(0)) || "S" }),
146
- user && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hidden md:flex items-center gap-3", children: [
147
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-sm font-medium text-theme-text-primary", children: user.name }),
148
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
149
- import_lucide_react.ChevronDown,
150
- {
151
- className: cn(
152
- "h-4 w-4 text-theme-text-secondary transition-transform group-hover:rotate-180",
153
- profileDropdownOpen && "rotate-180"
154
- )
155
- }
156
- )
157
- ] })
158
- ]
159
- }
160
- ),
161
- profileDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
162
- "div",
163
- {
164
- className: "absolute right-0 top-full z-50 mt-2 w-56 rounded-lg border border-theme-border-primary bg-theme-bg-secondary shadow-lg",
165
- onMouseEnter: openDropdown,
166
- onMouseLeave: closeDropdownWithDelay,
167
- children: [
168
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "border-b border-theme-border-primary p-3", children: [
169
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm font-medium text-theme-text-primary", children: user == null ? void 0 : user.name }),
170
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-theme-text-muted", children: user == null ? void 0 : user.email })
171
- ] }),
172
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
173
- "button",
174
- {
175
- onClick: () => {
176
- onProfileClick == null ? void 0 : onProfileClick();
177
- setProfileDropdownOpen(false);
178
- },
179
- className: "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-theme-text-secondary transition-colors hover:bg-[rgb(var(--accent-primary))]/15 hover:text-[rgb(var(--accent-primary))] cursor-pointer",
180
- children: [
181
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.User, { className: "h-4 w-4" }),
182
- "My Profile"
183
- ]
184
- }
185
- ) }),
186
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "border-t border-theme-border-primary p-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
187
- "button",
188
- {
189
- onClick: () => {
190
- logout();
191
- setProfileDropdownOpen(false);
192
- },
193
- className: "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-theme-text-secondary transition-colors hover:bg-[rgb(var(--accent-primary))]/15 hover:text-[rgb(var(--accent-primary))] cursor-pointer",
194
- children: [
195
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.LogOut, { className: "h-4 w-4" }),
196
- "Logout"
197
- ]
198
- }
199
- ) })
200
- ]
201
- }
202
- )
203
- ]
204
- }
205
- ) })
206
- ] }) });
207
- }
208
-
209
- // src/components/organisms/LearnerSidebar.tsx
210
- var import_lucide_react2 = require("lucide-react");
211
-
212
- // src/components/atoms/Button.tsx
213
- var React = __toESM(require("react"));
214
- var import_class_variance_authority = require("class-variance-authority");
215
- var import_jsx_runtime3 = require("react/jsx-runtime");
216
- var buttonVariants = (0, import_class_variance_authority.cva)(
217
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 cursor-pointer",
218
- {
219
- variants: {
220
- variant: {
221
- default: "bg-theme-accent-primary text-white hover:opacity-90",
222
- destructive: "bg-red-600 text-white hover:bg-red-700",
223
- outline: "border border-theme-border-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary text-theme-text-primary",
224
- secondary: "bg-theme-bg-tertiary text-theme-text-primary hover:opacity-80",
225
- ghost: "hover:bg-theme-bg-tertiary text-theme-text-primary",
226
- link: "text-[rgb(var(--accent-primary))] underline-offset-4 hover:underline"
227
- },
228
- size: {
229
- default: "h-10 px-4 py-2",
230
- sm: "h-9 rounded-md px-3",
231
- lg: "h-11 rounded-md px-8",
232
- icon: "h-10 w-10"
233
- }
234
- },
235
- defaultVariants: {
236
- variant: "default",
237
- size: "default"
238
- }
239
- }
240
- );
241
- var Button = React.forwardRef(
242
- (_a, ref) => {
243
- var _b = _a, { className, variant, size } = _b, props = __objRest(_b, ["className", "variant", "size"]);
244
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
245
- "button",
246
- __spreadValues({
247
- className: cn(buttonVariants({ variant, size, className })),
248
- ref
249
- }, props)
250
- );
251
- }
252
- );
253
- Button.displayName = "Button";
254
-
255
- // src/components/organisms/LearnerSidebar.tsx
256
- var import_jsx_runtime4 = require("react/jsx-runtime");
257
- var defaultItems = [
258
- { label: "My Learning", href: "/my-learning", icon: import_lucide_react2.BookOpen },
259
- { label: "Catalog", href: "/catalog/courses", icon: import_lucide_react2.GraduationCap },
260
- { label: "Messages", href: "/messages", icon: import_lucide_react2.MessageSquare },
261
- { label: "Manual Review", href: "/manual-review", icon: import_lucide_react2.ClipboardCheck },
262
- { label: "Account", href: "/account", icon: import_lucide_react2.UserCircle }
263
- ];
264
- function LearnerSidebar({
265
- isOpen,
266
- isCollapsed,
267
- onClose,
268
- onToggle,
269
- currentPath,
270
- onNavigate,
271
- items = defaultItems
272
- }) {
273
- const sdk = useSDK();
274
- const { user } = sdk.useUser();
275
- const { logout } = sdk.useLogout();
276
- const isActive = (href, label) => {
277
- if (label === "Catalog" && currentPath.startsWith("/catalog")) return true;
278
- return currentPath === href || currentPath.startsWith(href + "/");
279
- };
280
- const handleNav = (href) => {
281
- onNavigate(href);
282
- onClose();
283
- };
284
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
285
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
286
- "aside",
287
- {
288
- style: { top: "var(--preview-toolbar-h, 0px)", height: "calc(100vh - var(--preview-toolbar-h, 0px))" },
289
- className: cn(
290
- "fixed left-0 z-40 border-r border-theme-border-primary bg-theme-bg-secondary transition-all duration-300 flex flex-col",
291
- isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0",
292
- isCollapsed ? "w-16" : "w-64"
293
- ),
294
- children: [
295
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex h-20 items-center justify-between border-b border-theme-border-primary px-4 bg-theme-bg-secondary/50 backdrop-blur-sm", children: [
296
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
297
- "button",
298
- {
299
- onClick: () => handleNav("/my-learning"),
300
- className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 transition-opacity cursor-pointer",
301
- children: [
302
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex h-8 w-8 items-center justify-center rounded bg-theme-accent-primary", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.BookOpen, { className: "h-5 w-5 text-white" }) }),
303
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xl font-bold text-theme-text-primary leading-none whitespace-nowrap", children: "Academy" })
304
- ]
305
- }
306
- ),
307
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
308
- Button,
309
- {
310
- variant: "ghost",
311
- size: "icon",
312
- onClick: onToggle,
313
- className: cn("hover:bg-theme-bg-tertiary rounded-lg transition-colors", isCollapsed && "mx-auto"),
314
- children: isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.ChevronRight, { className: "h-5 w-5" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.ChevronLeft, { className: "h-5 w-5" })
315
- }
316
- )
317
- ] }),
318
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: "flex-1 overflow-y-auto p-4 space-y-1", children: items.map((item) => {
319
- const Icon = item.icon;
320
- const active = isActive(item.href, item.label);
321
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
322
- "button",
323
- {
324
- onClick: () => handleNav(item.href),
325
- className: cn(
326
- "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
327
- active ? "bg-theme-accent-primary text-white hover:opacity-90" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
328
- isCollapsed && "justify-center"
329
- ),
330
- title: isCollapsed ? item.label : void 0,
331
- children: [
332
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "h-5 w-5 shrink-0" }),
333
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: item.label })
334
- ]
335
- },
336
- item.href
337
- );
338
- }) }),
339
- user && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "border-t border-theme-border-primary p-4", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-1", children: [
340
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
341
- "button",
342
- {
343
- onClick: () => handleNav("/learner-settings"),
344
- className: cn(
345
- "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
346
- currentPath === "/learner-settings" ? "bg-theme-accent-primary text-white" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
347
- isCollapsed && "justify-center"
348
- ),
349
- title: isCollapsed ? "Settings" : void 0,
350
- children: [
351
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Settings, { className: "h-5 w-5 shrink-0" }),
352
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: "Settings" })
353
- ]
354
- }
355
- ),
356
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
357
- "button",
358
- {
359
- onClick: logout,
360
- className: cn(
361
- "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:bg-red-50 hover:text-red-600 text-theme-text-secondary cursor-pointer",
362
- isCollapsed && "justify-center"
363
- ),
364
- children: [
365
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.LogOut, { className: "h-5 w-5 shrink-0" }),
366
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: "Logout" })
367
- ]
368
- }
369
- )
370
- ] }) })
371
- ]
372
- }
373
- ),
374
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: onClose })
375
- ] });
376
- }
377
-
378
- // src/components/organisms/CourseSidebar.tsx
379
- var import_react3 = require("react");
380
- var import_lucide_react3 = require("lucide-react");
381
-
382
- // src/components/atoms/ProgressBar.tsx
383
- var import_jsx_runtime5 = require("react/jsx-runtime");
384
- var sizeClasses = {
385
- sm: "h-1.5",
386
- md: "h-2.5",
387
- lg: "h-3.5"
388
- };
389
- var variantClasses = {
390
- default: "bg-theme-accent-primary",
391
- success: "bg-green-600",
392
- warning: "bg-yellow-500",
393
- error: "bg-red-600"
394
- };
395
- function ProgressBar(_a) {
396
- var _b = _a, {
397
- value,
398
- max = 100,
399
- showLabel = false,
400
- size = "md",
401
- variant = "default",
402
- className
403
- } = _b, props = __objRest(_b, [
404
- "value",
405
- "max",
406
- "showLabel",
407
- "size",
408
- "variant",
409
- "className"
410
- ]);
411
- const percentage = Math.min(Math.max(value / max * 100, 0), 100);
412
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", __spreadProps(__spreadValues({ className: cn("w-full", className) }, props), { children: [
413
- showLabel && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mb-1 flex items-center justify-between text-sm", children: [
414
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-theme-text-secondary", children: "Progress" }),
415
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "font-medium text-theme-text-primary", children: [
416
- Math.round(percentage),
417
- "%"
418
- ] })
419
- ] }),
420
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn("w-full overflow-hidden rounded-full bg-theme-bg-tertiary", sizeClasses[size]), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
421
- "div",
422
- {
423
- className: cn("h-full transition-all duration-300 ease-in-out", variantClasses[variant]),
424
- style: { width: `${percentage}%` }
425
- }
426
- ) })
427
- ] }));
428
- }
429
-
430
- // src/components/organisms/CourseSidebar.tsx
431
- var import_jsx_runtime6 = require("react/jsx-runtime");
432
- var lessonColors = [
433
- "bg-[#E2F0FF] border-l-4 border-[#3aa3a5]",
434
- "bg-[#fcddb5] border-l-4 border-[#d67a05]",
435
- "bg-[#FAD1CE] border-l-4 border-[#7ab3f0]"
436
- ];
437
- function CourseSidebar({
438
- courseTitle,
439
- modules,
440
- currentLessonId,
441
- expandedModules,
442
- completedCount,
443
- totalCount,
444
- onToggleModule,
445
- onSelectLesson,
446
- hideProgress = false,
447
- hideSearch = false,
448
- certificateEnabled = false,
449
- isCompleted = false,
450
- onDownloadCertificate,
451
- isDownloadingCertificate = false,
452
- onBack,
453
- showBackButton = true,
454
- className
455
- }) {
456
- const [searchQuery, setSearchQuery] = (0, import_react3.useState)("");
457
- const filteredModules = (0, import_react3.useMemo)(() => {
458
- if (!searchQuery.trim()) return modules;
459
- const query = searchQuery.toLowerCase();
460
- return modules.map((mod) => __spreadProps(__spreadValues({}, mod), {
461
- lessons: mod.lessons.filter((l) => l.title.toLowerCase().includes(query))
462
- })).filter((mod) => mod.lessons.length > 0);
463
- }, [modules, searchQuery]);
464
- const totalResults = filteredModules.reduce((sum, m) => sum + m.lessons.length, 0);
465
- const progressPct = totalCount > 0 ? completedCount / totalCount * 100 : 0;
466
- const formatDuration = (duration) => {
467
- if (!duration) return "";
468
- const text = duration.trim();
469
- if (!text) return "";
470
- if (/[a-zA-Z]/.test(text)) return text;
471
- const parts = text.split(":").map(Number);
472
- if (parts.some(isNaN)) return text;
473
- if (parts.length === 3) {
474
- const [hrs, mins] = parts;
475
- if (hrs > 0 && mins > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"} ${mins} min`;
476
- if (hrs > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"}`;
477
- return `${mins} min`;
478
- }
479
- if (parts.length === 2) {
480
- const [mins, secs] = parts;
481
- return `${mins > 0 ? mins : secs > 0 ? 1 : 0} min`;
482
- }
483
- return `${Math.max(1, Math.round(parts[0] / 60))} min`;
484
- };
485
- let globalLessonIndex = 0;
486
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("w-full lg:w-[350px] bg-white flex-shrink-0 flex flex-col border-r border-gray-100 lg:h-screen lg:sticky lg:top-0 overflow-y-auto", className), children: [
487
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-6", children: [
488
- showBackButton && onBack && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
489
- "button",
490
- {
491
- onClick: onBack,
492
- className: "w-10 h-10 bg-[#4ecdc4] rounded-md flex items-center justify-center text-white hover:bg-[#3dbdb4] transition-colors mb-6 cursor-pointer",
493
- "aria-label": "Go back",
494
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.ArrowLeft, { className: "w-5 h-5" })
495
- }
496
- ),
497
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-3xl font-semibold text-[#1f2937] leading-tight", children: courseTitle ? `${courseTitle.charAt(0).toUpperCase()}${courseTitle.slice(1)}` : "" }),
498
- !hideProgress && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-4", children: [
499
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProgressBar, { value: progressPct, size: "sm" }),
500
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-xs text-gray-400 mt-1 text-right", children: [
501
- completedCount,
502
- " / ",
503
- totalCount,
504
- " lessons \xB7 ",
505
- Math.round(progressPct),
506
- "%"
507
- ] })
508
- ] }),
509
- certificateEnabled && isCompleted && onDownloadCertificate && completedCount === totalCount && totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
510
- Button,
511
- {
512
- onClick: onDownloadCertificate,
513
- disabled: isDownloadingCertificate,
514
- className: "w-full mt-3 bg-green-600 hover:bg-green-700 disabled:opacity-50 text-white",
515
- children: [
516
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Award, { className: "w-4 h-4 mr-2" }),
517
- isDownloadingCertificate ? "Downloading..." : "Download Certificate"
518
- ]
519
- }
520
- ),
521
- !hideSearch && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 relative", children: [
522
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }),
523
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
524
- "input",
525
- {
526
- type: "text",
527
- placeholder: "Search lessons...",
528
- value: searchQuery,
529
- onChange: (e) => setSearchQuery(e.target.value),
530
- className: "w-full pl-9 pr-9 py-2 text-sm bg-gray-50 border border-gray-200 text-gray-700 placeholder:text-gray-400 rounded-lg focus:outline-none focus:border-[#49BBBD] transition-colors"
531
- }
532
- ),
533
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
534
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
535
- "button",
536
- {
537
- onClick: () => setSearchQuery(""),
538
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 cursor-pointer",
539
- "aria-label": "Clear search",
540
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.X, { className: "w-4 h-4" })
541
- }
542
- ),
543
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-xs text-gray-400 mt-1", children: [
544
- totalResults,
545
- " ",
546
- totalResults === 1 ? "lesson" : "lessons",
547
- " found"
548
- ] })
549
- ] })
550
- ] })
551
- ] }),
552
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 px-4 space-y-8 pb-10", children: filteredModules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-center py-8", children: [
553
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Search, { className: "w-10 h-10 text-gray-300 mx-auto mb-2" }),
554
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-sm text-gray-400", children: [
555
- "No lessons found for \u201C",
556
- searchQuery,
557
- "\u201D"
558
- ] }),
559
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => setSearchQuery(""), className: "mt-2 text-sm text-[#49BBBD] hover:underline font-medium cursor-pointer", children: "Clear search" })
560
- ] }) : filteredModules.map((module2) => {
561
- const isExpanded = expandedModules.has(module2.id) || !!searchQuery.trim();
562
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
563
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
564
- "button",
565
- {
566
- type: "button",
567
- onClick: () => onToggleModule(module2.id),
568
- "aria-expanded": isExpanded,
569
- className: "w-full flex items-center justify-between text-left mb-3 px-2 cursor-pointer",
570
- children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-sm font-bold text-gray-500 uppercase tracking-wider", children: module2.title }),
572
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.ChevronRight, { className: cn("w-4 h-4 transition-transform", isExpanded ? "rotate-90 text-[#49BBBD]" : "text-gray-400") })
573
- ]
574
- }
575
- ),
576
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-3", children: module2.lessons.map((lesson) => {
577
- const isCurrent = currentLessonId === lesson.id;
578
- const isLocked = lesson.locked === true;
579
- const colorClass = lessonColors[globalLessonIndex % lessonColors.length];
580
- globalLessonIndex++;
581
- let availabilityText = "";
582
- if (isLocked) {
583
- if (lesson.availableAt) {
584
- const diffDays = Math.ceil((new Date(lesson.availableAt).getTime() - Date.now()) / 864e5);
585
- availabilityText = diffDays > 0 ? `Available in ${diffDays}d` : `Available on ${new Date(lesson.availableAt).toLocaleDateString()}`;
586
- } else if (lesson.hasPrerequisite) {
587
- availabilityText = "Complete previous lesson";
588
- }
589
- }
590
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
591
- "button",
592
- {
593
- title: lesson.title,
594
- onClick: () => !isLocked && onSelectLesson(lesson.id),
595
- disabled: isLocked,
596
- className: cn(
597
- "w-full text-left p-4 rounded-lg flex items-center justify-between transition-transform",
598
- isLocked ? "opacity-50 cursor-not-allowed" : "hover:scale-[1.02] active:scale-[0.98] cursor-pointer",
599
- isCurrent ? "bg-[#49BBBD] text-white shadow-md ring-2 ring-[#2a9ea0]/40" : colorClass
600
- ),
601
- children: [
602
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-3 overflow-hidden", children: [
603
- lesson.completed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-5 h-5 rounded-full border-2 bg-green-500 border-green-500 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Check, { className: "w-3 h-3 text-white" }) }) : isLocked ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-5 h-5 rounded-full border-2 border-gray-400 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Lock, { className: "w-2.5 h-2.5 text-gray-500" }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("w-5 h-5 rounded-full border-2 flex items-center justify-center flex-shrink-0", isCurrent ? "border-white/60" : "border-gray-400"), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("w-2.5 h-2.5 rounded-sm", isCurrent ? "bg-white/80" : "bg-[#1f2a44]") }) }),
604
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "overflow-hidden", children: [
605
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-1.5", children: [
606
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.ClipboardList, { className: cn("w-3.5 h-3.5 flex-shrink-0", isCurrent ? "text-white" : "text-gray-600") }) : null,
607
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn("text-sm font-medium truncate", isCurrent ? "text-white" : "text-gray-800"), children: lesson.title })
608
- ] }),
609
- isLocked && availabilityText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-gray-600 mt-0.5 truncate", children: availabilityText })
610
- ] })
611
- ] }),
612
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn("text-xs font-semibold flex-shrink-0 ml-2", isCurrent ? "text-white/90" : "text-gray-600"), children: "Quiz" }) : lesson.duration ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn("text-xs font-semibold flex-shrink-0 ml-2", isCurrent ? "text-white/90" : "text-gray-600"), children: formatDuration(lesson.duration) }) : null
613
- ]
614
- },
615
- lesson.id
616
- );
617
- }) })
618
- ] }, module2.id);
619
- }) })
620
- ] });
621
- }
622
-
623
- // src/components/organisms/LessonNotes.tsx
624
- var import_react4 = require("react");
625
- var import_lucide_react4 = require("lucide-react");
626
- var import_jsx_runtime7 = require("react/jsx-runtime");
627
- function formatTimestamp(seconds) {
628
- if (seconds === void 0 || seconds === null) return "";
629
- const total = Math.floor(seconds);
630
- const hrs = Math.floor(total / 3600);
631
- const mins = Math.floor(total % 3600 / 60);
632
- const secs = Math.floor(total % 60);
633
- if (hrs > 0) return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
634
- return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
635
- }
636
- function LessonNotes({ activityId, currentVideoTime, getLiveCurrentTime, onTimestampClick }) {
637
- const sdk = useSDK();
638
- const { data: apiNotes, isLoading, refetch } = sdk.useLessonNotes(activityId);
639
- const { createNote, isLoading: isCreating } = sdk.useCreateNote();
640
- const { updateNote, isLoading: isUpdating } = sdk.useUpdateNote();
641
- const { deleteNote, isLoading: isDeleting } = sdk.useDeleteNote();
642
- const [noteText, setNoteText] = (0, import_react4.useState)("");
643
- const [editingNoteId, setEditingNoteId] = (0, import_react4.useState)(null);
644
- const [editingText, setEditingText] = (0, import_react4.useState)("");
645
- const notes = Array.isArray(apiNotes) ? apiNotes : [];
646
- (0, import_react4.useEffect)(() => {
647
- setNoteText("");
648
- setEditingNoteId(null);
649
- setEditingText("");
650
- }, [activityId]);
651
- const handleSaveNote = async () => {
652
- if (!noteText.trim()) return;
653
- const effectiveTime = getLiveCurrentTime ? getLiveCurrentTime() : currentVideoTime;
654
- const result = await createNote({
655
- activityId,
656
- text: noteText.trim(),
657
- timestamp: effectiveTime !== void 0 ? Math.floor(effectiveTime) : void 0
658
- });
659
- if (result) {
660
- setNoteText("");
661
- refetch();
662
- }
663
- };
664
- const handleSaveEdit = async (noteId) => {
665
- if (!editingText.trim()) return;
666
- const result = await updateNote(noteId, { text: editingText.trim() });
667
- if (result) {
668
- setEditingNoteId(null);
669
- setEditingText("");
670
- refetch();
671
- }
672
- };
673
- const handleDeleteNote = async (noteId) => {
674
- const success = await deleteNote(noteId);
675
- if (success) refetch();
676
- };
677
- const handleTimestampClick = (timestamp) => {
678
- onTimestampClick == null ? void 0 : onTimestampClick(timestamp);
679
- window.scrollTo({ top: 0, behavior: "smooth" });
680
- };
681
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-6", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-theme-text-secondary", children: "Loading notes..." }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
682
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-3", children: [
683
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
684
- "textarea",
685
- {
686
- placeholder: "Write a note...",
687
- value: noteText,
688
- onChange: (e) => setNoteText(e.target.value),
689
- rows: notes.length === 0 ? 6 : 4,
690
- className: "w-full resize-none rounded-md border border-theme-border-primary bg-theme-bg-primary px-3 py-2 text-sm text-theme-text-primary placeholder:text-[rgb(var(--text-muted))] focus-visible:outline-none focus-visible:border-theme-accent-primary transition-colors"
691
- }
692
- ),
693
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
694
- Button,
695
- {
696
- onClick: handleSaveNote,
697
- disabled: !noteText.trim() || isCreating,
698
- className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white",
699
- children: isCreating ? "Saving..." : "Save note"
700
- }
701
- ) })
702
- ] }),
703
- notes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
704
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-xl font-bold text-theme-text-primary", children: "No notes yet" }),
705
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-base text-theme-text-secondary", children: "Add notes for this lesson to remember key points." })
706
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-4", children: [
707
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-lg font-bold text-theme-text-primary", children: "Your notes for this lesson" }),
708
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-3", children: notes.map((note) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "border border-theme-border-primary bg-theme-bg-secondary rounded-lg p-3 md:p-4 space-y-2 relative w-full", children: [
709
- editingNoteId !== note.id && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "absolute top-3 right-3 flex gap-2", children: [
710
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => {
711
- setEditingNoteId(note.id);
712
- setEditingText(note.text);
713
- }, disabled: isDeleting, className: "text-theme-accent-primary hover:text-theme-accent-primary/80 p-1 h-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Pencil, { className: "w-4 h-4" }) }),
714
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => handleDeleteNote(note.id), disabled: isDeleting, className: "text-red-600 hover:text-red-800 p-1 h-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Trash2, { className: "w-4 h-4" }) })
715
- ] }),
716
- note.timestamp !== void 0 && note.timestamp !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: () => handleTimestampClick(note.timestamp), className: "text-theme-accent-primary font-mono font-semibold hover:underline text-sm", disabled: !onTimestampClick, children: note.formattedTimestamp || formatTimestamp(note.timestamp) }),
717
- editingNoteId === note.id ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-2", children: [
718
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("textarea", { value: editingText, onChange: (e) => setEditingText(e.target.value), rows: 3, className: "w-full resize-none rounded-md border border-theme-border-primary bg-theme-bg-primary px-3 py-2 text-sm text-theme-text-primary focus-visible:outline-none focus-visible:border-theme-accent-primary transition-colors" }),
719
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-2 justify-end", children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { variant: "outline", onClick: () => {
721
- setEditingNoteId(null);
722
- setEditingText("");
723
- }, disabled: isUpdating, children: "Cancel" }),
724
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { onClick: () => handleSaveEdit(note.id), disabled: !editingText.trim() || isUpdating, className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white", children: isUpdating ? "Saving..." : "Save" })
725
- ] })
726
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-theme-text-primary", children: note.text })
727
- ] }, note.id)) })
728
- ] })
729
- ] }) });
730
- }
731
-
732
- // src/components/organisms/LessonBookmarks.tsx
733
- var import_react5 = require("react");
734
- var import_lucide_react5 = require("lucide-react");
735
-
736
- // src/components/atoms/Input.tsx
737
- var React2 = __toESM(require("react"));
738
- var import_jsx_runtime8 = require("react/jsx-runtime");
739
- var Input = React2.forwardRef(
740
- (_a, ref) => {
741
- var _b = _a, { className, type, label } = _b, props = __objRest(_b, ["className", "type", "label"]);
742
- const inputElement = /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
743
- "input",
744
- __spreadValues({
745
- type,
746
- className: cn(
747
- "flex h-10 w-full rounded-md border border-theme-border-primary bg-theme-bg-primary px-3 py-2 text-sm text-theme-text-primary file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[rgb(var(--text-muted))] focus-visible:outline-none focus-visible:border-theme-accent-primary transition-colors disabled:cursor-not-allowed disabled:opacity-50",
748
- className
749
- ),
750
- ref
751
- }, props)
752
- );
753
- if (label) {
754
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
755
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { className: "text-sm font-medium text-theme-text-primary", children: [
756
- label,
757
- props.required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500 ml-1", children: "*" })
758
- ] }),
759
- inputElement
760
- ] });
761
- }
762
- return inputElement;
763
- }
764
- );
765
- Input.displayName = "Input";
766
-
767
- // src/components/organisms/LessonBookmarks.tsx
768
- var import_jsx_runtime9 = require("react/jsx-runtime");
769
- function formatTime(seconds) {
770
- const hrs = Math.floor(seconds / 3600);
771
- const mins = Math.floor(seconds % 3600 / 60);
772
- const secs = Math.floor(seconds % 60);
773
- if (hrs > 0) return `${hrs}:${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
774
- return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
775
- }
776
- function LessonBookmarks({ activityId, currentVideoTime, getLiveCurrentTime, onTimestampClick }) {
777
- const sdk = useSDK();
778
- const { data: apiBookmarks, isLoading, refetch } = sdk.useLessonBookmarks(activityId);
779
- const { createBookmark, isLoading: isCreating } = sdk.useCreateBookmark();
780
- const { updateBookmark, isLoading: isUpdating } = sdk.useUpdateBookmark();
781
- const { deleteBookmark, isLoading: isDeleting } = sdk.useDeleteBookmark();
782
- const [label, setLabel] = (0, import_react5.useState)("");
783
- const [editingId, setEditingId] = (0, import_react5.useState)(null);
784
- const [editingLabel, setEditingLabel] = (0, import_react5.useState)("");
785
- const bookmarks = Array.isArray(apiBookmarks) ? [...apiBookmarks].sort((a, b) => a.second - b.second) : [];
786
- (0, import_react5.useEffect)(() => {
787
- setLabel("");
788
- setEditingId(null);
789
- setEditingLabel("");
790
- }, [activityId]);
791
- const handleAdd = async () => {
792
- const effectiveTime = getLiveCurrentTime ? getLiveCurrentTime() : currentVideoTime;
793
- if (effectiveTime === void 0) return;
794
- const result = await createBookmark({ activityId, second: Math.floor(effectiveTime), label: label.trim() || void 0 });
795
- if (result) {
796
- setLabel("");
797
- await refetch();
798
- }
799
- };
800
- const handleSaveEdit = async (id) => {
801
- const result = await updateBookmark(id, { label: editingLabel.trim() || void 0 });
802
- if (result) {
803
- setEditingId(null);
804
- setEditingLabel("");
805
- refetch();
806
- }
807
- };
808
- const handleDelete = async (id) => {
809
- const success = await deleteBookmark(id);
810
- if (success) refetch();
811
- };
812
- const handleClick = (second) => {
813
- onTimestampClick == null ? void 0 : onTimestampClick(second);
814
- window.scrollTo({ top: 0, behavior: "smooth" });
815
- };
816
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col h-full", children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "p-4 border-b border-theme-border-primary bg-theme-bg-secondary sticky top-0 z-10", children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "text-lg font-bold text-theme-text-primary mb-2", children: "Bookmarks for this lesson" }),
819
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-base text-theme-text-secondary mb-4", children: "Save key moments in the video for quick access" }),
820
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Input, { value: label, onChange: (e) => setLabel(e.target.value), placeholder: "Optional label...", className: "w-full" }),
822
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Button, { onClick: handleAdd, disabled: isCreating || currentVideoTime === void 0, className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white", children: [
823
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Bookmark, { className: "h-4 w-4 mr-2" }),
824
- "Add Bookmark"
825
- ] }) })
826
- ] })
827
- ] }),
828
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-1 overflow-y-auto p-4", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-theme-text-secondary", children: "Loading bookmarks..." }) : bookmarks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-center py-8", children: [
829
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Bookmark, { className: "h-12 w-12 mx-auto text-theme-text-muted mb-3" }),
830
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "text-xl font-bold text-theme-text-primary", children: "No bookmarks yet" }),
831
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-base text-theme-text-secondary mt-2", children: "Click \u201CAdd Bookmark\u201D to save important moments" })
832
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-3", children: bookmarks.map((bm) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "rounded-lg border border-theme-border-primary bg-theme-bg-secondary p-3 hover:border-theme-accent-primary transition-colors", children: editingId === bm.id ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
833
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Input, { value: editingLabel, onChange: (e) => setEditingLabel(e.target.value), placeholder: "Label...", className: "w-full", autoFocus: true }),
834
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-2 justify-end", children: [
835
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { onClick: () => handleSaveEdit(bm.id), disabled: isUpdating, className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white", children: "Save" }),
836
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { variant: "outline", onClick: () => {
837
- setEditingId(null);
838
- setEditingLabel("");
839
- }, disabled: isUpdating, children: "Cancel" })
840
- ] })
841
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
842
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex-1 min-w-0", children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { onClick: () => handleClick(bm.second), className: "text-theme-accent-primary font-mono font-semibold hover:underline text-sm", children: formatTime(bm.second) }),
844
- bm.label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-theme-text-secondary text-sm mt-1 break-words", children: bm.label })
845
- ] }),
846
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-1 flex-shrink-0", children: [
847
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { onClick: () => {
848
- setEditingId(bm.id);
849
- setEditingLabel(bm.label || "");
850
- }, variant: "ghost", size: "icon", className: "text-theme-accent-primary hover:text-theme-accent-primary/80 h-8 w-8", title: "Edit", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Pencil, { className: "h-4 w-4" }) }),
851
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { onClick: () => handleDelete(bm.id), disabled: isDeleting, variant: "ghost", size: "icon", className: "text-red-600 hover:text-red-800 h-8 w-8", title: "Delete", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.Trash2, { className: "h-4 w-4" }) })
852
- ] })
853
- ] }) }, bm.id)) }) })
854
- ] });
855
- }