@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,1275 +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/index.ts
60
- var components_exports = {};
61
- __export(components_exports, {
62
- Avatar: () => Avatar,
63
- Badge: () => Badge,
64
- Button: () => Button,
65
- Card: () => Card,
66
- CardContent: () => CardContent,
67
- CardDescription: () => CardDescription,
68
- CardFooter: () => CardFooter,
69
- CardHeader: () => CardHeader,
70
- CardTitle: () => CardTitle,
71
- CourseCard: () => CourseCard,
72
- CourseSidebar: () => CourseSidebar,
73
- EmptyState: () => EmptyState,
74
- Input: () => Input,
75
- LearnerNavbar: () => LearnerNavbar,
76
- LearnerSidebar: () => LearnerSidebar,
77
- LessonBookmarks: () => LessonBookmarks,
78
- LessonNotes: () => LessonNotes,
79
- LoadingSpinner: () => LoadingSpinner,
80
- PageHeader: () => PageHeader,
81
- Pagination: () => Pagination,
82
- ProgressBar: () => ProgressBar,
83
- SearchInput: () => SearchInput,
84
- Tabs: () => Tabs,
85
- badgeVariants: () => badgeVariants,
86
- buttonVariants: () => buttonVariants,
87
- cn: () => cn
88
- });
89
- module.exports = __toCommonJS(components_exports);
90
-
91
- // src/components/utils.ts
92
- var import_clsx = require("clsx");
93
- var import_tailwind_merge = require("tailwind-merge");
94
- function cn(...inputs) {
95
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
96
- }
97
-
98
- // src/components/atoms/Button.tsx
99
- var React = __toESM(require("react"));
100
- var import_class_variance_authority = require("class-variance-authority");
101
- var import_jsx_runtime = require("react/jsx-runtime");
102
- var buttonVariants = (0, import_class_variance_authority.cva)(
103
- "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",
104
- {
105
- variants: {
106
- variant: {
107
- default: "bg-theme-accent-primary text-white hover:opacity-90",
108
- destructive: "bg-red-600 text-white hover:bg-red-700",
109
- outline: "border border-theme-border-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary text-theme-text-primary",
110
- secondary: "bg-theme-bg-tertiary text-theme-text-primary hover:opacity-80",
111
- ghost: "hover:bg-theme-bg-tertiary text-theme-text-primary",
112
- link: "text-[rgb(var(--accent-primary))] underline-offset-4 hover:underline"
113
- },
114
- size: {
115
- default: "h-10 px-4 py-2",
116
- sm: "h-9 rounded-md px-3",
117
- lg: "h-11 rounded-md px-8",
118
- icon: "h-10 w-10"
119
- }
120
- },
121
- defaultVariants: {
122
- variant: "default",
123
- size: "default"
124
- }
125
- }
126
- );
127
- var Button = React.forwardRef(
128
- (_a, ref) => {
129
- var _b = _a, { className, variant, size } = _b, props = __objRest(_b, ["className", "variant", "size"]);
130
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
131
- "button",
132
- __spreadValues({
133
- className: cn(buttonVariants({ variant, size, className })),
134
- ref
135
- }, props)
136
- );
137
- }
138
- );
139
- Button.displayName = "Button";
140
-
141
- // src/components/atoms/Input.tsx
142
- var React2 = __toESM(require("react"));
143
- var import_jsx_runtime2 = require("react/jsx-runtime");
144
- var Input = React2.forwardRef(
145
- (_a, ref) => {
146
- var _b = _a, { className, type, label } = _b, props = __objRest(_b, ["className", "type", "label"]);
147
- const inputElement = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
148
- "input",
149
- __spreadValues({
150
- type,
151
- className: cn(
152
- "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",
153
- className
154
- ),
155
- ref
156
- }, props)
157
- );
158
- if (label) {
159
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
160
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "text-sm font-medium text-theme-text-primary", children: [
161
- label,
162
- props.required && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-red-500 ml-1", children: "*" })
163
- ] }),
164
- inputElement
165
- ] });
166
- }
167
- return inputElement;
168
- }
169
- );
170
- Input.displayName = "Input";
171
-
172
- // src/components/atoms/Card.tsx
173
- var import_jsx_runtime3 = require("react/jsx-runtime");
174
- function Card(_a) {
175
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
176
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
177
- "div",
178
- __spreadValues({
179
- className: cn("rounded-lg border border-theme-border-primary bg-theme-bg-primary shadow-sm", className)
180
- }, props)
181
- );
182
- }
183
- function CardHeader(_a) {
184
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
185
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", __spreadValues({ className: cn("flex flex-col space-y-1.5 p-6", className) }, props));
186
- }
187
- function CardTitle(_a) {
188
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
189
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", __spreadValues({ className: cn("text-2xl font-semibold leading-none tracking-tight", className) }, props));
190
- }
191
- function CardDescription(_a) {
192
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
193
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", __spreadValues({ className: cn("text-sm text-theme-text-secondary", className) }, props));
194
- }
195
- function CardContent(_a) {
196
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
197
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", __spreadValues({ className: cn("p-6 pt-0", className) }, props));
198
- }
199
- function CardFooter(_a) {
200
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
201
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", __spreadValues({ className: cn("flex items-center p-6 pt-0", className) }, props));
202
- }
203
-
204
- // src/components/atoms/Badge.tsx
205
- var import_class_variance_authority2 = require("class-variance-authority");
206
- var import_jsx_runtime4 = require("react/jsx-runtime");
207
- var badgeVariants = (0, import_class_variance_authority2.cva)(
208
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
209
- {
210
- variants: {
211
- variant: {
212
- default: "bg-theme-accent-primary text-white",
213
- secondary: "bg-theme-bg-tertiary text-theme-text-primary",
214
- success: "border-transparent bg-green-600 text-white",
215
- warning: "border-transparent bg-yellow-500 text-white",
216
- destructive: "border-transparent bg-red-600 text-white",
217
- outline: "text-theme-text-primary border-theme-border-primary"
218
- }
219
- },
220
- defaultVariants: {
221
- variant: "default"
222
- }
223
- }
224
- );
225
- function Badge(_a) {
226
- var _b = _a, { className, variant } = _b, props = __objRest(_b, ["className", "variant"]);
227
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", __spreadValues({ className: cn(badgeVariants({ variant }), className) }, props));
228
- }
229
-
230
- // src/components/atoms/Avatar.tsx
231
- var React3 = __toESM(require("react"));
232
- var import_jsx_runtime5 = require("react/jsx-runtime");
233
- var Avatar = React3.forwardRef(
234
- (_a, ref) => {
235
- var _b = _a, { className, src, alt, fallback } = _b, props = __objRest(_b, ["className", "src", "alt", "fallback"]);
236
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
237
- "div",
238
- __spreadProps(__spreadValues({
239
- ref,
240
- className: cn(
241
- "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
242
- className
243
- )
244
- }, props), {
245
- children: src ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
246
- "img",
247
- {
248
- src,
249
- alt: alt || "Avatar",
250
- className: "aspect-square h-full w-full object-cover"
251
- }
252
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex h-full w-full items-center justify-center bg-gray-200 text-gray-600 text-sm font-medium", children: fallback || "?" })
253
- })
254
- );
255
- }
256
- );
257
- Avatar.displayName = "Avatar";
258
-
259
- // src/components/atoms/ProgressBar.tsx
260
- var import_jsx_runtime6 = require("react/jsx-runtime");
261
- var sizeClasses = {
262
- sm: "h-1.5",
263
- md: "h-2.5",
264
- lg: "h-3.5"
265
- };
266
- var variantClasses = {
267
- default: "bg-theme-accent-primary",
268
- success: "bg-green-600",
269
- warning: "bg-yellow-500",
270
- error: "bg-red-600"
271
- };
272
- function ProgressBar(_a) {
273
- var _b = _a, {
274
- value,
275
- max = 100,
276
- showLabel = false,
277
- size = "md",
278
- variant = "default",
279
- className
280
- } = _b, props = __objRest(_b, [
281
- "value",
282
- "max",
283
- "showLabel",
284
- "size",
285
- "variant",
286
- "className"
287
- ]);
288
- const percentage = Math.min(Math.max(value / max * 100, 0), 100);
289
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", __spreadProps(__spreadValues({ className: cn("w-full", className) }, props), { children: [
290
- showLabel && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-1 flex items-center justify-between text-sm", children: [
291
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-theme-text-secondary", children: "Progress" }),
292
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "font-medium text-theme-text-primary", children: [
293
- Math.round(percentage),
294
- "%"
295
- ] })
296
- ] }),
297
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn("w-full overflow-hidden rounded-full bg-theme-bg-tertiary", sizeClasses[size]), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
298
- "div",
299
- {
300
- className: cn("h-full transition-all duration-300 ease-in-out", variantClasses[variant]),
301
- style: { width: `${percentage}%` }
302
- }
303
- ) })
304
- ] }));
305
- }
306
-
307
- // src/components/atoms/Tabs.tsx
308
- var import_jsx_runtime7 = require("react/jsx-runtime");
309
- function Tabs({ tabs, activeTab, onTabChange, className, actions }) {
310
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cn("border-b border-theme-border-primary", className), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "px-4 sm:px-6 pt-4", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "p-0.5 flex items-center justify-between overflow-x-auto", children: [
311
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("nav", { className: "flex gap-4 sm:gap-8 min-w-min", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
312
- "button",
313
- {
314
- onClick: () => onTabChange(tab.id),
315
- className: cn(
316
- "pb-3 px-1 text-sm font-medium border-b-2 transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap",
317
- activeTab === tab.id ? "border-theme-accent-primary text-theme-accent-primary" : "border-transparent text-theme-text-secondary hover:text-theme-text-primary hover:border-theme-accent-primary/40"
318
- ),
319
- children: [
320
- tab.icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "flex-shrink-0", children: tab.icon }),
321
- tab.label
322
- ]
323
- },
324
- tab.id
325
- )) }),
326
- actions && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "pb-3", children: actions })
327
- ] }) }) });
328
- }
329
-
330
- // src/components/molecules/CourseCard.tsx
331
- var import_react = require("react");
332
- var import_lucide_react = require("lucide-react");
333
- var import_jsx_runtime8 = require("react/jsx-runtime");
334
- function CourseCard({
335
- id,
336
- title,
337
- thumbnail,
338
- thumbnails,
339
- totalLessons,
340
- progress,
341
- status,
342
- isFree,
343
- price = 0,
344
- currency = "USD",
345
- description,
346
- showPrice = true,
347
- showProgress = true,
348
- category,
349
- duration,
350
- instructorName,
351
- instructorAvatar,
352
- originalPrice,
353
- isBundle = false,
354
- onClick,
355
- className
356
- }) {
357
- const [imageError, setImageError] = (0, import_react.useState)(false);
358
- const clampedProgress = Math.min(Math.max(progress, 0), 100);
359
- const stripHtml = (html) => {
360
- if (typeof document === "undefined") return html;
361
- const tmp = document.createElement("div");
362
- tmp.innerHTML = html;
363
- return tmp.textContent || tmp.innerText || "";
364
- };
365
- const displayCategory = category || "Design";
366
- const displayDuration = duration || "3 Month";
367
- const displayInstructorName = instructorName || (description ? stripHtml(description).slice(0, 15) : "Instructor");
368
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
369
- "div",
370
- {
371
- className: cn(
372
- "group overflow-hidden rounded-2xl border border-gray-100 bg-white shadow-sm hover:shadow-lg transition-all duration-300 hover:-translate-y-1 h-full flex flex-col min-w-0 w-full cursor-pointer",
373
- className
374
- ),
375
- onClick,
376
- children: [
377
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative h-[238.66px] w-full overflow-hidden mx-auto px-3 pt-3", children: [
378
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative w-full h-full rounded-xl overflow-hidden bg-gray-100", children: isBundle && thumbnails && thumbnails.length > 0 && !imageError ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative w-full h-full flex", children: [
379
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative h-full w-1/2", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
380
- "img",
381
- {
382
- src: thumbnails[0],
383
- alt: `${title} - image 1`,
384
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
385
- onError: () => setImageError(true)
386
- }
387
- ) }),
388
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col w-1/2 h-full", children: [
389
- thumbnails[1] && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn("relative w-full overflow-hidden", thumbnails.length > 2 ? "h-1/2" : "h-full"), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
390
- "img",
391
- {
392
- src: thumbnails[1],
393
- alt: `${title} - image 2`,
394
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
395
- onError: () => setImageError(true)
396
- }
397
- ) }),
398
- thumbnails.length > 2 && thumbnails[2] && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "relative w-full h-1/2 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
399
- "img",
400
- {
401
- src: thumbnails[2],
402
- alt: `${title} - image 3`,
403
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
404
- onError: () => setImageError(true)
405
- }
406
- ) })
407
- ] })
408
- ] }) : thumbnail && !imageError ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
409
- "img",
410
- {
411
- src: thumbnail,
412
- alt: title,
413
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
414
- onError: () => setImageError(true)
415
- }
416
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-full h-full flex items-center justify-center bg-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react.BookOpen, { className: "w-12 h-12 text-gray-400" }) }) }),
417
- status && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute right-6 top-5", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
418
- "span",
419
- {
420
- className: `rounded-full px-3 py-1.5 text-xs font-medium text-white shadow-md ${status === "completed" ? "bg-green-500" : "bg-[rgb(var(--accent-primary))]"}`,
421
- children: status
422
- }
423
- ) })
424
- ] }),
425
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-5 space-y-3 flex-1 flex flex-col min-w-0", children: [
426
- (displayCategory || displayDuration) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500", children: [
427
- displayCategory && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1.5", children: [
428
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react.Grid2X2, { className: "w-3.5 h-3.5 text-gray-400" }),
429
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: displayCategory })
430
- ] }),
431
- displayDuration && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1.5", children: [
432
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react.Clock, { className: "w-3.5 h-3.5 text-gray-400" }),
433
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: displayDuration })
434
- ] })
435
- ] }),
436
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-base font-bold text-gray-900 line-clamp-2 leading-snug", children: title }),
437
- description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-500 line-clamp-3 leading-relaxed", children: stripHtml(description) }),
438
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1" }),
439
- showProgress && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
440
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-2 overflow-hidden rounded-full bg-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
441
- "div",
442
- {
443
- className: "h-full rounded-full bg-[rgb(var(--accent-primary))] transition-all duration-300",
444
- style: { width: `${clampedProgress}%` }
445
- }
446
- ) }),
447
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between text-xs", children: [
448
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-gray-400" }),
449
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-gray-500", children: [
450
- isBundle ? "Course" : "Lesson",
451
- " ",
452
- Math.ceil(clampedProgress / 100 * totalLessons),
453
- " of ",
454
- totalLessons
455
- ] })
456
- ] })
457
- ] }),
458
- showPrice && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between pt-3 border-t border-gray-100", children: [
459
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
460
- instructorAvatar ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-7 h-7 rounded-full overflow-hidden flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: instructorAvatar, alt: displayInstructorName, className: "w-full h-full object-cover" }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-7 h-7 rounded-full bg-gray-200 flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] font-semibold text-gray-500", children: displayInstructorName.charAt(0).toUpperCase() }) }),
461
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm text-gray-600 font-medium truncate max-w-[80px]", children: displayInstructorName })
462
- ] }),
463
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
464
- !isFree && originalPrice && originalPrice > price && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-sm text-gray-400 line-through", children: [
465
- "$",
466
- originalPrice
467
- ] }),
468
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-lg font-bold text-[rgb(var(--accent-primary))]", children: isFree ? "Free" : `$${price}` })
469
- ] })
470
- ] })
471
- ] })
472
- ]
473
- }
474
- );
475
- }
476
-
477
- // src/components/molecules/Pagination.tsx
478
- var import_lucide_react2 = require("lucide-react");
479
- var import_jsx_runtime9 = require("react/jsx-runtime");
480
- function Pagination({
481
- currentPage,
482
- totalPages,
483
- total,
484
- pageSize,
485
- hasNextPage,
486
- hasPreviousPage,
487
- onPageChange,
488
- maxVisiblePages = 5,
489
- className
490
- }) {
491
- const renderPageNumbers = () => {
492
- const pages = [];
493
- let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
494
- let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
495
- if (endPage - startPage < maxVisiblePages - 1) {
496
- startPage = Math.max(1, endPage - maxVisiblePages + 1);
497
- }
498
- for (let i = startPage; i <= endPage; i++) {
499
- pages.push(
500
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
501
- "button",
502
- {
503
- onClick: () => onPageChange(i),
504
- className: cn(
505
- "px-3 py-1 rounded cursor-pointer transition-colors",
506
- i === currentPage ? "bg-theme-accent-primary text-white" : "text-theme-text-primary border border-theme-border-primary hover:bg-theme-bg-tertiary"
507
- ),
508
- children: i
509
- },
510
- i
511
- )
512
- );
513
- }
514
- return pages;
515
- };
516
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("flex items-center justify-between border-t border-theme-border-primary pt-4", className), children: [
517
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-sm text-theme-text-secondary", children: [
518
- "Showing ",
519
- (currentPage - 1) * pageSize + 1,
520
- " to ",
521
- Math.min(currentPage * pageSize, total),
522
- " of ",
523
- total,
524
- " results"
525
- ] }),
526
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
527
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
528
- "button",
529
- {
530
- onClick: () => onPageChange(currentPage - 1),
531
- disabled: !hasPreviousPage,
532
- className: "p-2 rounded border border-theme-border-primary hover:bg-theme-bg-tertiary disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition-colors",
533
- "aria-label": "Previous page",
534
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react2.ChevronLeft, { className: "h-4 w-4" })
535
- }
536
- ),
537
- renderPageNumbers(),
538
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
539
- "button",
540
- {
541
- onClick: () => onPageChange(currentPage + 1),
542
- disabled: !hasNextPage,
543
- className: "p-2 rounded border border-theme-border-primary hover:bg-theme-bg-tertiary disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition-colors",
544
- "aria-label": "Next page",
545
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react2.ChevronRight, { className: "h-4 w-4" })
546
- }
547
- )
548
- ] })
549
- ] });
550
- }
551
-
552
- // src/components/molecules/PageHeader.tsx
553
- var import_jsx_runtime10 = require("react/jsx-runtime");
554
- function PageHeader({ title, description, actions, className }) {
555
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6", className), children: [
556
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
557
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-2xl font-bold text-theme-text-primary", children: title }),
558
- description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-theme-text-secondary mt-1", children: description })
559
- ] }),
560
- actions && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex items-center gap-2", children: actions })
561
- ] });
562
- }
563
-
564
- // src/components/molecules/SearchInput.tsx
565
- var import_lucide_react3 = require("lucide-react");
566
- var import_jsx_runtime11 = require("react/jsx-runtime");
567
- function SearchInput({ value, onChange, placeholder = "Search...", className }) {
568
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: cn("relative", className), children: [
569
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-theme-text-muted" }),
570
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
571
- "input",
572
- {
573
- type: "text",
574
- value,
575
- onChange: (e) => onChange(e.target.value),
576
- placeholder,
577
- className: "w-full pl-10 pr-10 py-2 text-sm rounded-lg border border-theme-border-primary bg-theme-bg-primary text-theme-text-primary placeholder:text-[rgb(var(--text-muted))] focus:outline-none focus:border-theme-accent-primary transition-colors"
578
- }
579
- ),
580
- value && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
581
- "button",
582
- {
583
- onClick: () => onChange(""),
584
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-theme-text-muted hover:text-theme-text-primary cursor-pointer",
585
- "aria-label": "Clear search",
586
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.X, { className: "h-4 w-4" })
587
- }
588
- )
589
- ] });
590
- }
591
-
592
- // src/components/molecules/EmptyState.tsx
593
- var import_jsx_runtime12 = require("react/jsx-runtime");
594
- function EmptyState({ icon, title, description, action, className }) {
595
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: cn("flex flex-col items-center justify-center py-12 text-center", className), children: [
596
- icon && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mb-4 text-theme-text-muted", children: icon }),
597
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-theme-text-primary", children: title }),
598
- description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-2 text-sm text-theme-text-secondary max-w-md", children: description }),
599
- action && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-4", children: action })
600
- ] });
601
- }
602
-
603
- // src/components/molecules/LoadingSpinner.tsx
604
- var import_jsx_runtime13 = require("react/jsx-runtime");
605
- var sizeClasses2 = {
606
- sm: "h-4 w-4",
607
- md: "h-8 w-8",
608
- lg: "h-12 w-12"
609
- };
610
- function LoadingSpinner({ size = "md", className, text }) {
611
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3", className), children: [
612
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
613
- "div",
614
- {
615
- className: cn(
616
- "animate-spin rounded-full border-2 border-theme-bg-tertiary border-t-theme-accent-primary",
617
- sizeClasses2[size]
618
- )
619
- }
620
- ),
621
- text && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-theme-text-secondary", children: text })
622
- ] });
623
- }
624
-
625
- // src/components/organisms/LearnerNavbar.tsx
626
- var import_react3 = require("react");
627
- var import_lucide_react4 = require("lucide-react");
628
-
629
- // src/hooks/sdk-context.tsx
630
- var import_react2 = require("react");
631
- var import_jsx_runtime14 = require("react/jsx-runtime");
632
- var SDKContext = (0, import_react2.createContext)(null);
633
- function useSDK() {
634
- const ctx = (0, import_react2.useContext)(SDKContext);
635
- if (!ctx) {
636
- throw new Error(
637
- "useSDK must be used within an SDKProvider. Make sure your learner routes are wrapped with <SDKProvider>."
638
- );
639
- }
640
- return ctx;
641
- }
642
-
643
- // src/components/organisms/LearnerNavbar.tsx
644
- var import_jsx_runtime15 = require("react/jsx-runtime");
645
- function LearnerNavbar({ hideMenuButton = false, onMenuClick, onProfileClick, onLogoClick }) {
646
- var _a;
647
- const sdk = useSDK();
648
- const { user } = sdk.useUser();
649
- const { logout } = sdk.useLogout();
650
- const [profileDropdownOpen, setProfileDropdownOpen] = (0, import_react3.useState)(false);
651
- const closeTimeoutRef = (0, import_react3.useRef)(null);
652
- (0, import_react3.useEffect)(() => {
653
- return () => {
654
- if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
655
- };
656
- }, []);
657
- const openDropdown = () => {
658
- if (closeTimeoutRef.current) {
659
- clearTimeout(closeTimeoutRef.current);
660
- closeTimeoutRef.current = null;
661
- }
662
- setProfileDropdownOpen(true);
663
- };
664
- const closeDropdownWithDelay = () => {
665
- if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
666
- closeTimeoutRef.current = setTimeout(() => {
667
- setProfileDropdownOpen(false);
668
- closeTimeoutRef.current = null;
669
- }, 150);
670
- };
671
- return /* @__PURE__ */ (0, import_jsx_runtime15.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_runtime15.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: [
672
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-4", children: !hideMenuButton && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
673
- "button",
674
- {
675
- onClick: onMenuClick,
676
- className: "rounded-lg p-2 hover:bg-theme-bg-tertiary lg:hidden",
677
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react4.Menu, { className: "h-5 w-5 text-theme-text-primary" })
678
- }
679
- ) }),
680
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
681
- "div",
682
- {
683
- className: "relative group ml-2 sm:ml-4 border-l border-theme-border-primary pl-3 sm:pl-5",
684
- onMouseEnter: openDropdown,
685
- onMouseLeave: closeDropdownWithDelay,
686
- children: [
687
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
688
- "button",
689
- {
690
- onClick: () => setProfileDropdownOpen((o) => !o),
691
- className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 cursor-pointer transition-opacity",
692
- children: [
693
- /* @__PURE__ */ (0, import_jsx_runtime15.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_runtime15.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" }),
694
- user && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "hidden md:flex items-center gap-3", children: [
695
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-sm font-medium text-theme-text-primary", children: user.name }),
696
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
697
- import_lucide_react4.ChevronDown,
698
- {
699
- className: cn(
700
- "h-4 w-4 text-theme-text-secondary transition-transform group-hover:rotate-180",
701
- profileDropdownOpen && "rotate-180"
702
- )
703
- }
704
- )
705
- ] })
706
- ]
707
- }
708
- ),
709
- profileDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
710
- "div",
711
- {
712
- 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",
713
- onMouseEnter: openDropdown,
714
- onMouseLeave: closeDropdownWithDelay,
715
- children: [
716
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "border-b border-theme-border-primary p-3", children: [
717
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm font-medium text-theme-text-primary", children: user == null ? void 0 : user.name }),
718
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-xs text-theme-text-muted", children: user == null ? void 0 : user.email })
719
- ] }),
720
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
721
- "button",
722
- {
723
- onClick: () => {
724
- onProfileClick == null ? void 0 : onProfileClick();
725
- setProfileDropdownOpen(false);
726
- },
727
- 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",
728
- children: [
729
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react4.User, { className: "h-4 w-4" }),
730
- "My Profile"
731
- ]
732
- }
733
- ) }),
734
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-t border-theme-border-primary p-2", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
735
- "button",
736
- {
737
- onClick: () => {
738
- logout();
739
- setProfileDropdownOpen(false);
740
- },
741
- 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",
742
- children: [
743
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react4.LogOut, { className: "h-4 w-4" }),
744
- "Logout"
745
- ]
746
- }
747
- ) })
748
- ]
749
- }
750
- )
751
- ]
752
- }
753
- ) })
754
- ] }) });
755
- }
756
-
757
- // src/components/organisms/LearnerSidebar.tsx
758
- var import_lucide_react5 = require("lucide-react");
759
- var import_jsx_runtime16 = require("react/jsx-runtime");
760
- var defaultItems = [
761
- { label: "My Learning", href: "/my-learning", icon: import_lucide_react5.BookOpen },
762
- { label: "Catalog", href: "/catalog/courses", icon: import_lucide_react5.GraduationCap },
763
- { label: "Messages", href: "/messages", icon: import_lucide_react5.MessageSquare },
764
- { label: "Manual Review", href: "/manual-review", icon: import_lucide_react5.ClipboardCheck },
765
- { label: "Account", href: "/account", icon: import_lucide_react5.UserCircle }
766
- ];
767
- function LearnerSidebar({
768
- isOpen,
769
- isCollapsed,
770
- onClose,
771
- onToggle,
772
- currentPath,
773
- onNavigate,
774
- items = defaultItems
775
- }) {
776
- const sdk = useSDK();
777
- const { user } = sdk.useUser();
778
- const { logout } = sdk.useLogout();
779
- const isActive = (href, label) => {
780
- if (label === "Catalog" && currentPath.startsWith("/catalog")) return true;
781
- return currentPath === href || currentPath.startsWith(href + "/");
782
- };
783
- const handleNav = (href) => {
784
- onNavigate(href);
785
- onClose();
786
- };
787
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
788
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
789
- "aside",
790
- {
791
- style: { top: "var(--preview-toolbar-h, 0px)", height: "calc(100vh - var(--preview-toolbar-h, 0px))" },
792
- className: cn(
793
- "fixed left-0 z-40 border-r border-theme-border-primary bg-theme-bg-secondary transition-all duration-300 flex flex-col",
794
- isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0",
795
- isCollapsed ? "w-16" : "w-64"
796
- ),
797
- children: [
798
- /* @__PURE__ */ (0, import_jsx_runtime16.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: [
799
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
800
- "button",
801
- {
802
- onClick: () => handleNav("/my-learning"),
803
- className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 transition-opacity cursor-pointer",
804
- children: [
805
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex h-8 w-8 items-center justify-center rounded bg-theme-accent-primary", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.BookOpen, { className: "h-5 w-5 text-white" }) }),
806
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xl font-bold text-theme-text-primary leading-none whitespace-nowrap", children: "Academy" })
807
- ]
808
- }
809
- ),
810
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
811
- Button,
812
- {
813
- variant: "ghost",
814
- size: "icon",
815
- onClick: onToggle,
816
- className: cn("hover:bg-theme-bg-tertiary rounded-lg transition-colors", isCollapsed && "mx-auto"),
817
- children: isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ChevronRight, { className: "h-5 w-5" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ChevronLeft, { className: "h-5 w-5" })
818
- }
819
- )
820
- ] }),
821
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("nav", { className: "flex-1 overflow-y-auto p-4 space-y-1", children: items.map((item) => {
822
- const Icon = item.icon;
823
- const active = isActive(item.href, item.label);
824
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
825
- "button",
826
- {
827
- onClick: () => handleNav(item.href),
828
- className: cn(
829
- "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
830
- active ? "bg-theme-accent-primary text-white hover:opacity-90" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
831
- isCollapsed && "justify-center"
832
- ),
833
- title: isCollapsed ? item.label : void 0,
834
- children: [
835
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Icon, { className: "h-5 w-5 shrink-0" }),
836
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "whitespace-nowrap", children: item.label })
837
- ]
838
- },
839
- item.href
840
- );
841
- }) }),
842
- user && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "border-t border-theme-border-primary p-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-1", children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
844
- "button",
845
- {
846
- onClick: () => handleNav("/learner-settings"),
847
- className: cn(
848
- "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
849
- currentPath === "/learner-settings" ? "bg-theme-accent-primary text-white" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
850
- isCollapsed && "justify-center"
851
- ),
852
- title: isCollapsed ? "Settings" : void 0,
853
- children: [
854
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Settings, { className: "h-5 w-5 shrink-0" }),
855
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "whitespace-nowrap", children: "Settings" })
856
- ]
857
- }
858
- ),
859
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
860
- "button",
861
- {
862
- onClick: logout,
863
- className: cn(
864
- "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",
865
- isCollapsed && "justify-center"
866
- ),
867
- children: [
868
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.LogOut, { className: "h-5 w-5 shrink-0" }),
869
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "whitespace-nowrap", children: "Logout" })
870
- ]
871
- }
872
- )
873
- ] }) })
874
- ]
875
- }
876
- ),
877
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: onClose })
878
- ] });
879
- }
880
-
881
- // src/components/organisms/CourseSidebar.tsx
882
- var import_react4 = require("react");
883
- var import_lucide_react6 = require("lucide-react");
884
- var import_jsx_runtime17 = require("react/jsx-runtime");
885
- var lessonColors = [
886
- "bg-[#E2F0FF] border-l-4 border-[#3aa3a5]",
887
- "bg-[#fcddb5] border-l-4 border-[#d67a05]",
888
- "bg-[#FAD1CE] border-l-4 border-[#7ab3f0]"
889
- ];
890
- function CourseSidebar({
891
- courseTitle,
892
- modules,
893
- currentLessonId,
894
- expandedModules,
895
- completedCount,
896
- totalCount,
897
- onToggleModule,
898
- onSelectLesson,
899
- hideProgress = false,
900
- hideSearch = false,
901
- certificateEnabled = false,
902
- isCompleted = false,
903
- onDownloadCertificate,
904
- isDownloadingCertificate = false,
905
- onBack,
906
- showBackButton = true,
907
- className
908
- }) {
909
- const [searchQuery, setSearchQuery] = (0, import_react4.useState)("");
910
- const filteredModules = (0, import_react4.useMemo)(() => {
911
- if (!searchQuery.trim()) return modules;
912
- const query = searchQuery.toLowerCase();
913
- return modules.map((mod) => __spreadProps(__spreadValues({}, mod), {
914
- lessons: mod.lessons.filter((l) => l.title.toLowerCase().includes(query))
915
- })).filter((mod) => mod.lessons.length > 0);
916
- }, [modules, searchQuery]);
917
- const totalResults = filteredModules.reduce((sum, m) => sum + m.lessons.length, 0);
918
- const progressPct = totalCount > 0 ? completedCount / totalCount * 100 : 0;
919
- const formatDuration = (duration) => {
920
- if (!duration) return "";
921
- const text = duration.trim();
922
- if (!text) return "";
923
- if (/[a-zA-Z]/.test(text)) return text;
924
- const parts = text.split(":").map(Number);
925
- if (parts.some(isNaN)) return text;
926
- if (parts.length === 3) {
927
- const [hrs, mins] = parts;
928
- if (hrs > 0 && mins > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"} ${mins} min`;
929
- if (hrs > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"}`;
930
- return `${mins} min`;
931
- }
932
- if (parts.length === 2) {
933
- const [mins, secs] = parts;
934
- return `${mins > 0 ? mins : secs > 0 ? 1 : 0} min`;
935
- }
936
- return `${Math.max(1, Math.round(parts[0] / 60))} min`;
937
- };
938
- let globalLessonIndex = 0;
939
- return /* @__PURE__ */ (0, import_jsx_runtime17.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: [
940
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "p-6", children: [
941
- showBackButton && onBack && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
942
- "button",
943
- {
944
- onClick: onBack,
945
- 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",
946
- "aria-label": "Go back",
947
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.ArrowLeft, { className: "w-5 h-5" })
948
- }
949
- ),
950
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h3", { className: "text-3xl font-semibold text-[#1f2937] leading-tight", children: courseTitle ? `${courseTitle.charAt(0).toUpperCase()}${courseTitle.slice(1)}` : "" }),
951
- !hideProgress && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "mt-4", children: [
952
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ProgressBar, { value: progressPct, size: "sm" }),
953
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-xs text-gray-400 mt-1 text-right", children: [
954
- completedCount,
955
- " / ",
956
- totalCount,
957
- " lessons \xB7 ",
958
- Math.round(progressPct),
959
- "%"
960
- ] })
961
- ] }),
962
- certificateEnabled && isCompleted && onDownloadCertificate && completedCount === totalCount && totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
963
- Button,
964
- {
965
- onClick: onDownloadCertificate,
966
- disabled: isDownloadingCertificate,
967
- className: "w-full mt-3 bg-green-600 hover:bg-green-700 disabled:opacity-50 text-white",
968
- children: [
969
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.Award, { className: "w-4 h-4 mr-2" }),
970
- isDownloadingCertificate ? "Downloading..." : "Download Certificate"
971
- ]
972
- }
973
- ),
974
- !hideSearch && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "mt-3 relative", children: [
975
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }),
976
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
977
- "input",
978
- {
979
- type: "text",
980
- placeholder: "Search lessons...",
981
- value: searchQuery,
982
- onChange: (e) => setSearchQuery(e.target.value),
983
- 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"
984
- }
985
- ),
986
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
987
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
988
- "button",
989
- {
990
- onClick: () => setSearchQuery(""),
991
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 cursor-pointer",
992
- "aria-label": "Clear search",
993
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.X, { className: "w-4 h-4" })
994
- }
995
- ),
996
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-xs text-gray-400 mt-1", children: [
997
- totalResults,
998
- " ",
999
- totalResults === 1 ? "lesson" : "lessons",
1000
- " found"
1001
- ] })
1002
- ] })
1003
- ] })
1004
- ] }),
1005
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex-1 px-4 space-y-8 pb-10", children: filteredModules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "text-center py-8", children: [
1006
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.Search, { className: "w-10 h-10 text-gray-300 mx-auto mb-2" }),
1007
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-sm text-gray-400", children: [
1008
- "No lessons found for \u201C",
1009
- searchQuery,
1010
- "\u201D"
1011
- ] }),
1012
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { onClick: () => setSearchQuery(""), className: "mt-2 text-sm text-[#49BBBD] hover:underline font-medium cursor-pointer", children: "Clear search" })
1013
- ] }) : filteredModules.map((module2) => {
1014
- const isExpanded = expandedModules.has(module2.id) || !!searchQuery.trim();
1015
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
1016
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1017
- "button",
1018
- {
1019
- type: "button",
1020
- onClick: () => onToggleModule(module2.id),
1021
- "aria-expanded": isExpanded,
1022
- className: "w-full flex items-center justify-between text-left mb-3 px-2 cursor-pointer",
1023
- children: [
1024
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h3", { className: "text-sm font-bold text-gray-500 uppercase tracking-wider", children: module2.title }),
1025
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.ChevronRight, { className: cn("w-4 h-4 transition-transform", isExpanded ? "rotate-90 text-[#49BBBD]" : "text-gray-400") })
1026
- ]
1027
- }
1028
- ),
1029
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "space-y-3", children: module2.lessons.map((lesson) => {
1030
- const isCurrent = currentLessonId === lesson.id;
1031
- const isLocked = lesson.locked === true;
1032
- const colorClass = lessonColors[globalLessonIndex % lessonColors.length];
1033
- globalLessonIndex++;
1034
- let availabilityText = "";
1035
- if (isLocked) {
1036
- if (lesson.availableAt) {
1037
- const diffDays = Math.ceil((new Date(lesson.availableAt).getTime() - Date.now()) / 864e5);
1038
- availabilityText = diffDays > 0 ? `Available in ${diffDays}d` : `Available on ${new Date(lesson.availableAt).toLocaleDateString()}`;
1039
- } else if (lesson.hasPrerequisite) {
1040
- availabilityText = "Complete previous lesson";
1041
- }
1042
- }
1043
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1044
- "button",
1045
- {
1046
- title: lesson.title,
1047
- onClick: () => !isLocked && onSelectLesson(lesson.id),
1048
- disabled: isLocked,
1049
- className: cn(
1050
- "w-full text-left p-4 rounded-lg flex items-center justify-between transition-transform",
1051
- isLocked ? "opacity-50 cursor-not-allowed" : "hover:scale-[1.02] active:scale-[0.98] cursor-pointer",
1052
- isCurrent ? "bg-[#49BBBD] text-white shadow-md ring-2 ring-[#2a9ea0]/40" : colorClass
1053
- ),
1054
- children: [
1055
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-3 overflow-hidden", children: [
1056
- lesson.completed ? /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(import_lucide_react6.Check, { className: "w-3 h-3 text-white" }) }) : isLocked ? /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(import_lucide_react6.Lock, { className: "w-2.5 h-2.5 text-gray-500" }) }) : /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)("div", { className: cn("w-2.5 h-2.5 rounded-sm", isCurrent ? "bg-white/80" : "bg-[#1f2a44]") }) }),
1057
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "overflow-hidden", children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1059
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react6.ClipboardList, { className: cn("w-3.5 h-3.5 flex-shrink-0", isCurrent ? "text-white" : "text-gray-600") }) : null,
1060
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: cn("text-sm font-medium truncate", isCurrent ? "text-white" : "text-gray-800"), children: lesson.title })
1061
- ] }),
1062
- isLocked && availabilityText && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-xs text-gray-600 mt-0.5 truncate", children: availabilityText })
1063
- ] })
1064
- ] }),
1065
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.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
1066
- ]
1067
- },
1068
- lesson.id
1069
- );
1070
- }) })
1071
- ] }, module2.id);
1072
- }) })
1073
- ] });
1074
- }
1075
-
1076
- // src/components/organisms/LessonNotes.tsx
1077
- var import_react5 = require("react");
1078
- var import_lucide_react7 = require("lucide-react");
1079
- var import_jsx_runtime18 = require("react/jsx-runtime");
1080
- function formatTimestamp(seconds) {
1081
- if (seconds === void 0 || seconds === null) return "";
1082
- const total = Math.floor(seconds);
1083
- const hrs = Math.floor(total / 3600);
1084
- const mins = Math.floor(total % 3600 / 60);
1085
- const secs = Math.floor(total % 60);
1086
- if (hrs > 0) return `${String(hrs).padStart(2, "0")}:${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
1087
- return `${String(mins).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
1088
- }
1089
- function LessonNotes({ activityId, currentVideoTime, getLiveCurrentTime, onTimestampClick }) {
1090
- const sdk = useSDK();
1091
- const { data: apiNotes, isLoading, refetch } = sdk.useLessonNotes(activityId);
1092
- const { createNote, isLoading: isCreating } = sdk.useCreateNote();
1093
- const { updateNote, isLoading: isUpdating } = sdk.useUpdateNote();
1094
- const { deleteNote, isLoading: isDeleting } = sdk.useDeleteNote();
1095
- const [noteText, setNoteText] = (0, import_react5.useState)("");
1096
- const [editingNoteId, setEditingNoteId] = (0, import_react5.useState)(null);
1097
- const [editingText, setEditingText] = (0, import_react5.useState)("");
1098
- const notes = Array.isArray(apiNotes) ? apiNotes : [];
1099
- (0, import_react5.useEffect)(() => {
1100
- setNoteText("");
1101
- setEditingNoteId(null);
1102
- setEditingText("");
1103
- }, [activityId]);
1104
- const handleSaveNote = async () => {
1105
- if (!noteText.trim()) return;
1106
- const effectiveTime = getLiveCurrentTime ? getLiveCurrentTime() : currentVideoTime;
1107
- const result = await createNote({
1108
- activityId,
1109
- text: noteText.trim(),
1110
- timestamp: effectiveTime !== void 0 ? Math.floor(effectiveTime) : void 0
1111
- });
1112
- if (result) {
1113
- setNoteText("");
1114
- refetch();
1115
- }
1116
- };
1117
- const handleSaveEdit = async (noteId) => {
1118
- if (!editingText.trim()) return;
1119
- const result = await updateNote(noteId, { text: editingText.trim() });
1120
- if (result) {
1121
- setEditingNoteId(null);
1122
- setEditingText("");
1123
- refetch();
1124
- }
1125
- };
1126
- const handleDeleteNote = async (noteId) => {
1127
- const success = await deleteNote(noteId);
1128
- if (success) refetch();
1129
- };
1130
- const handleTimestampClick = (timestamp) => {
1131
- onTimestampClick == null ? void 0 : onTimestampClick(timestamp);
1132
- window.scrollTo({ top: 0, behavior: "smooth" });
1133
- };
1134
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-6", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-theme-text-secondary", children: "Loading notes..." }) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-3", children: [
1136
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1137
- "textarea",
1138
- {
1139
- placeholder: "Write a note...",
1140
- value: noteText,
1141
- onChange: (e) => setNoteText(e.target.value),
1142
- rows: notes.length === 0 ? 6 : 4,
1143
- 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"
1144
- }
1145
- ),
1146
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1147
- Button,
1148
- {
1149
- onClick: handleSaveNote,
1150
- disabled: !noteText.trim() || isCreating,
1151
- className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white",
1152
- children: isCreating ? "Saving..." : "Save note"
1153
- }
1154
- ) })
1155
- ] }),
1156
- notes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-2", children: [
1157
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "text-xl font-bold text-theme-text-primary", children: "No notes yet" }),
1158
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-base text-theme-text-secondary", children: "Add notes for this lesson to remember key points." })
1159
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-4", children: [
1160
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "text-lg font-bold text-theme-text-primary", children: "Your notes for this lesson" }),
1161
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-3", children: notes.map((note) => /* @__PURE__ */ (0, import_jsx_runtime18.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: [
1162
- editingNoteId !== note.id && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "absolute top-3 right-3 flex gap-2", children: [
1163
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { variant: "ghost", size: "sm", onClick: () => {
1164
- setEditingNoteId(note.id);
1165
- setEditingText(note.text);
1166
- }, disabled: isDeleting, className: "text-theme-accent-primary hover:text-theme-accent-primary/80 p-1 h-auto", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react7.Pencil, { className: "w-4 h-4" }) }),
1167
- /* @__PURE__ */ (0, import_jsx_runtime18.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_runtime18.jsx)(import_lucide_react7.Trash2, { className: "w-4 h-4" }) })
1168
- ] }),
1169
- note.timestamp !== void 0 && note.timestamp !== null && /* @__PURE__ */ (0, import_jsx_runtime18.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) }),
1170
- editingNoteId === note.id ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "space-y-2", children: [
1171
- /* @__PURE__ */ (0, import_jsx_runtime18.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" }),
1172
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex gap-2 justify-end", children: [
1173
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Button, { variant: "outline", onClick: () => {
1174
- setEditingNoteId(null);
1175
- setEditingText("");
1176
- }, disabled: isUpdating, children: "Cancel" }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime18.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" })
1178
- ] })
1179
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-theme-text-primary", children: note.text })
1180
- ] }, note.id)) })
1181
- ] })
1182
- ] }) });
1183
- }
1184
-
1185
- // src/components/organisms/LessonBookmarks.tsx
1186
- var import_react6 = require("react");
1187
- var import_lucide_react8 = require("lucide-react");
1188
- var import_jsx_runtime19 = require("react/jsx-runtime");
1189
- function formatTime(seconds) {
1190
- const hrs = Math.floor(seconds / 3600);
1191
- const mins = Math.floor(seconds % 3600 / 60);
1192
- const secs = Math.floor(seconds % 60);
1193
- if (hrs > 0) return `${hrs}:${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
1194
- return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
1195
- }
1196
- function LessonBookmarks({ activityId, currentVideoTime, getLiveCurrentTime, onTimestampClick }) {
1197
- const sdk = useSDK();
1198
- const { data: apiBookmarks, isLoading, refetch } = sdk.useLessonBookmarks(activityId);
1199
- const { createBookmark, isLoading: isCreating } = sdk.useCreateBookmark();
1200
- const { updateBookmark, isLoading: isUpdating } = sdk.useUpdateBookmark();
1201
- const { deleteBookmark, isLoading: isDeleting } = sdk.useDeleteBookmark();
1202
- const [label, setLabel] = (0, import_react6.useState)("");
1203
- const [editingId, setEditingId] = (0, import_react6.useState)(null);
1204
- const [editingLabel, setEditingLabel] = (0, import_react6.useState)("");
1205
- const bookmarks = Array.isArray(apiBookmarks) ? [...apiBookmarks].sort((a, b) => a.second - b.second) : [];
1206
- (0, import_react6.useEffect)(() => {
1207
- setLabel("");
1208
- setEditingId(null);
1209
- setEditingLabel("");
1210
- }, [activityId]);
1211
- const handleAdd = async () => {
1212
- const effectiveTime = getLiveCurrentTime ? getLiveCurrentTime() : currentVideoTime;
1213
- if (effectiveTime === void 0) return;
1214
- const result = await createBookmark({ activityId, second: Math.floor(effectiveTime), label: label.trim() || void 0 });
1215
- if (result) {
1216
- setLabel("");
1217
- await refetch();
1218
- }
1219
- };
1220
- const handleSaveEdit = async (id) => {
1221
- const result = await updateBookmark(id, { label: editingLabel.trim() || void 0 });
1222
- if (result) {
1223
- setEditingId(null);
1224
- setEditingLabel("");
1225
- refetch();
1226
- }
1227
- };
1228
- const handleDelete = async (id) => {
1229
- const success = await deleteBookmark(id);
1230
- if (success) refetch();
1231
- };
1232
- const handleClick = (second) => {
1233
- onTimestampClick == null ? void 0 : onTimestampClick(second);
1234
- window.scrollTo({ top: 0, behavior: "smooth" });
1235
- };
1236
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col h-full", children: [
1237
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "p-4 border-b border-theme-border-primary bg-theme-bg-secondary sticky top-0 z-10", children: [
1238
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "text-lg font-bold text-theme-text-primary mb-2", children: "Bookmarks for this lesson" }),
1239
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-base text-theme-text-secondary mb-4", children: "Save key moments in the video for quick access" }),
1240
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "space-y-2", children: [
1241
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Input, { value: label, onChange: (e) => setLabel(e.target.value), placeholder: "Optional label...", className: "w-full" }),
1242
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Button, { onClick: handleAdd, disabled: isCreating || currentVideoTime === void 0, className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white", children: [
1243
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Bookmark, { className: "h-4 w-4 mr-2" }),
1244
- "Add Bookmark"
1245
- ] }) })
1246
- ] })
1247
- ] }),
1248
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex-1 overflow-y-auto p-4", children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-theme-text-secondary", children: "Loading bookmarks..." }) : bookmarks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "text-center py-8", children: [
1249
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Bookmark, { className: "h-12 w-12 mx-auto text-theme-text-muted mb-3" }),
1250
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "text-xl font-bold text-theme-text-primary", children: "No bookmarks yet" }),
1251
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-base text-theme-text-secondary mt-2", children: "Click \u201CAdd Bookmark\u201D to save important moments" })
1252
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "space-y-3", children: bookmarks.map((bm) => /* @__PURE__ */ (0, import_jsx_runtime19.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_runtime19.jsxs)("div", { className: "space-y-2", children: [
1253
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Input, { value: editingLabel, onChange: (e) => setEditingLabel(e.target.value), placeholder: "Label...", className: "w-full", autoFocus: true }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-2 justify-end", children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { onClick: () => handleSaveEdit(bm.id), disabled: isUpdating, className: "bg-theme-accent-primary hover:bg-theme-accent-primary/90 text-white", children: "Save" }),
1256
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { variant: "outline", onClick: () => {
1257
- setEditingId(null);
1258
- setEditingLabel("");
1259
- }, disabled: isUpdating, children: "Cancel" })
1260
- ] })
1261
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1262
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 min-w-0", children: [
1263
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { onClick: () => handleClick(bm.second), className: "text-theme-accent-primary font-mono font-semibold hover:underline text-sm", children: formatTime(bm.second) }),
1264
- bm.label && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-theme-text-secondary text-sm mt-1 break-words", children: bm.label })
1265
- ] }),
1266
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1 flex-shrink-0", children: [
1267
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { onClick: () => {
1268
- setEditingId(bm.id);
1269
- setEditingLabel(bm.label || "");
1270
- }, 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_runtime19.jsx)(import_lucide_react8.Pencil, { className: "h-4 w-4" }) }),
1271
- /* @__PURE__ */ (0, import_jsx_runtime19.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_runtime19.jsx)(import_lucide_react8.Trash2, { className: "h-4 w-4" }) })
1272
- ] })
1273
- ] }) }, bm.id)) }) })
1274
- ] });
1275
- }