@academy-sdk/sdk 0.1.1 → 0.2.1

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 (239) hide show
  1. package/dist/academy-sdk-sdk-v1.0.0.zip +0 -0
  2. package/dist/bundle.js +70 -0
  3. package/dist/manifest.json +5 -0
  4. package/dist/styles.css +3307 -0
  5. package/package.json +41 -46
  6. package/src/components/atoms/Avatar.tsx +38 -0
  7. package/src/components/atoms/Badge.tsx +32 -0
  8. package/src/components/atoms/Button.tsx +48 -0
  9. package/src/components/atoms/Card.tsx +33 -0
  10. package/src/components/atoms/Input.tsx +39 -0
  11. package/src/components/atoms/ProgressBar.tsx +52 -0
  12. package/src/components/atoms/Tabs.tsx +47 -0
  13. package/{dist/components/atoms/index.d.ts → src/components/atoms/index.ts} +0 -1
  14. package/{dist/components/index.d.ts → src/components/index.ts} +7 -1
  15. package/src/components/molecules/CourseCard.tsx +215 -0
  16. package/src/components/molecules/EmptyState.tsx +23 -0
  17. package/src/components/molecules/LoadingSpinner.tsx +27 -0
  18. package/src/components/molecules/PageHeader.tsx +22 -0
  19. package/src/components/molecules/Pagination.tsx +82 -0
  20. package/src/components/molecules/SearchInput.tsx +35 -0
  21. package/{dist/components/molecules/index.d.ts → src/components/molecules/index.ts} +0 -1
  22. package/src/components/organisms/CourseSidebar.tsx +276 -0
  23. package/src/components/organisms/LearnerNavbar.tsx +129 -0
  24. package/src/components/organisms/LearnerSidebar.tsx +148 -0
  25. package/src/components/organisms/LessonBookmarks.tsx +128 -0
  26. package/src/components/organisms/LessonNotes.tsx +153 -0
  27. package/{dist/components/organisms/index.d.ts → src/components/organisms/index.ts} +0 -1
  28. package/src/components/pages/BundleDetailPage.tsx +388 -0
  29. package/src/components/pages/CatalogBundlesPage.tsx +96 -0
  30. package/src/components/pages/CatalogCoursesPage.tsx +299 -0
  31. package/src/components/pages/CourseDetailPage.tsx +582 -0
  32. package/src/components/pages/CoursePlayerPage.tsx +481 -0
  33. package/src/components/pages/CreatorProfilePage.tsx +161 -0
  34. package/src/components/pages/LearnerSettingsPage.tsx +58 -0
  35. package/src/components/pages/ManualReviewDetailPage.tsx +254 -0
  36. package/src/components/pages/ManualReviewPage.tsx +228 -0
  37. package/src/components/pages/MessagesPage.tsx +285 -0
  38. package/src/components/pages/MyLearningPage.tsx +239 -0
  39. package/src/components/pages/PaymentCancelPage.tsx +74 -0
  40. package/src/components/pages/PaymentSuccessPage.tsx +73 -0
  41. package/{dist/components/pages/index.d.ts → src/components/pages/index.ts} +0 -1
  42. package/src/components/utils.ts +6 -0
  43. package/src/contracts/components.contract.ts +89 -0
  44. package/{dist/contracts/index.d.ts → src/contracts/index.ts} +0 -1
  45. package/src/contracts/layout.contract.ts +36 -0
  46. package/src/contracts/pages.contract.ts +275 -0
  47. package/src/contracts/template.contract.ts +100 -0
  48. package/src/default-template.tsx +52 -0
  49. package/{dist/hooks/index.d.ts → src/hooks/index.ts} +15 -1
  50. package/src/hooks/sdk-context.tsx +152 -0
  51. package/src/hooks/useAiCoach.ts +27 -0
  52. package/src/hooks/useBookmarks.ts +35 -0
  53. package/{dist/hooks/useCourseSearch.d.ts → src/hooks/useCourseSearch.ts} +8 -5
  54. package/{dist/hooks/useDebounce.d.ts → src/hooks/useDebounce.ts} +8 -2
  55. package/{dist/hooks/useMyBundles.d.ts → src/hooks/useMyBundles.ts} +8 -6
  56. package/{dist/hooks/useMyCourses.d.ts → src/hooks/useMyCourses.ts} +8 -6
  57. package/src/hooks/useNotes.ts +35 -0
  58. package/src/hooks/useNotifications.ts +16 -0
  59. package/{dist/hooks/useTheme.d.ts → src/hooks/useTheme.ts} +8 -5
  60. package/src/hooks/useToast.ts +17 -0
  61. package/{dist/hooks/useUser.d.ts → src/hooks/useUser.ts} +13 -9
  62. package/src/index.ts +33 -0
  63. package/src/layouts/DefaultLayout.tsx +58 -0
  64. package/src/manifest.json +5 -0
  65. package/src/styles.css +43 -0
  66. package/src/types/ai-coach.ts +25 -0
  67. package/src/types/bookmarks.ts +20 -0
  68. package/src/types/bundle.ts +119 -0
  69. package/src/types/common.ts +24 -0
  70. package/src/types/course.ts +135 -0
  71. package/src/types/enrollment.ts +35 -0
  72. package/{dist/types/index.d.ts → src/types/index.ts} +0 -1
  73. package/src/types/lesson.ts +106 -0
  74. package/src/types/manual-review.ts +116 -0
  75. package/src/types/messaging.ts +109 -0
  76. package/src/types/notification.ts +30 -0
  77. package/src/types/payment.ts +40 -0
  78. package/src/types/progress.ts +19 -0
  79. package/src/types/rating.ts +20 -0
  80. package/src/types/search.ts +31 -0
  81. package/src/types/user.ts +16 -0
  82. package/src/utils/formatters.ts +74 -0
  83. package/src/utils/index.ts +8 -0
  84. package/dist/components/atoms/Avatar.d.ts +0 -9
  85. package/dist/components/atoms/Avatar.d.ts.map +0 -1
  86. package/dist/components/atoms/Badge.d.ts +0 -10
  87. package/dist/components/atoms/Badge.d.ts.map +0 -1
  88. package/dist/components/atoms/Button.d.ts +0 -11
  89. package/dist/components/atoms/Button.d.ts.map +0 -1
  90. package/dist/components/atoms/Card.d.ts +0 -11
  91. package/dist/components/atoms/Card.d.ts.map +0 -1
  92. package/dist/components/atoms/Input.d.ts +0 -7
  93. package/dist/components/atoms/Input.d.ts.map +0 -1
  94. package/dist/components/atoms/ProgressBar.d.ts +0 -11
  95. package/dist/components/atoms/ProgressBar.d.ts.map +0 -1
  96. package/dist/components/atoms/Tabs.d.ts +0 -16
  97. package/dist/components/atoms/Tabs.d.ts.map +0 -1
  98. package/dist/components/atoms/index.cjs +0 -318
  99. package/dist/components/atoms/index.d.ts.map +0 -1
  100. package/dist/components/atoms/index.js +0 -288
  101. package/dist/components/index.cjs +0 -1275
  102. package/dist/components/index.d.ts.map +0 -1
  103. package/dist/components/index.js +0 -1245
  104. package/dist/components/molecules/CourseCard.d.ts +0 -25
  105. package/dist/components/molecules/CourseCard.d.ts.map +0 -1
  106. package/dist/components/molecules/EmptyState.d.ts +0 -10
  107. package/dist/components/molecules/EmptyState.d.ts.map +0 -1
  108. package/dist/components/molecules/LoadingSpinner.d.ts +0 -7
  109. package/dist/components/molecules/LoadingSpinner.d.ts.map +0 -1
  110. package/dist/components/molecules/PageHeader.d.ts +0 -8
  111. package/dist/components/molecules/PageHeader.d.ts.map +0 -1
  112. package/dist/components/molecules/Pagination.d.ts +0 -13
  113. package/dist/components/molecules/Pagination.d.ts.map +0 -1
  114. package/dist/components/molecules/SearchInput.d.ts +0 -8
  115. package/dist/components/molecules/SearchInput.d.ts.map +0 -1
  116. package/dist/components/molecules/index.cjs +0 -334
  117. package/dist/components/molecules/index.d.ts.map +0 -1
  118. package/dist/components/molecules/index.js +0 -311
  119. package/dist/components/organisms/CourseSidebar.d.ts +0 -37
  120. package/dist/components/organisms/CourseSidebar.d.ts.map +0 -1
  121. package/dist/components/organisms/LearnerNavbar.d.ts +0 -8
  122. package/dist/components/organisms/LearnerNavbar.d.ts.map +0 -1
  123. package/dist/components/organisms/LearnerSidebar.d.ts +0 -16
  124. package/dist/components/organisms/LearnerSidebar.d.ts.map +0 -1
  125. package/dist/components/organisms/LessonBookmarks.d.ts +0 -8
  126. package/dist/components/organisms/LessonBookmarks.d.ts.map +0 -1
  127. package/dist/components/organisms/LessonNotes.d.ts +0 -8
  128. package/dist/components/organisms/LessonNotes.d.ts.map +0 -1
  129. package/dist/components/organisms/index.cjs +0 -855
  130. package/dist/components/organisms/index.d.ts.map +0 -1
  131. package/dist/components/organisms/index.js +0 -825
  132. package/dist/components/pages/BundleDetailPage.d.ts +0 -3
  133. package/dist/components/pages/BundleDetailPage.d.ts.map +0 -1
  134. package/dist/components/pages/CatalogBundlesPage.d.ts +0 -3
  135. package/dist/components/pages/CatalogBundlesPage.d.ts.map +0 -1
  136. package/dist/components/pages/CatalogCoursesPage.d.ts +0 -3
  137. package/dist/components/pages/CatalogCoursesPage.d.ts.map +0 -1
  138. package/dist/components/pages/CourseDetailPage.d.ts +0 -3
  139. package/dist/components/pages/CourseDetailPage.d.ts.map +0 -1
  140. package/dist/components/pages/CoursePlayerPage.d.ts +0 -8
  141. package/dist/components/pages/CoursePlayerPage.d.ts.map +0 -1
  142. package/dist/components/pages/CreatorProfilePage.d.ts +0 -3
  143. package/dist/components/pages/CreatorProfilePage.d.ts.map +0 -1
  144. package/dist/components/pages/LearnerSettingsPage.d.ts +0 -3
  145. package/dist/components/pages/LearnerSettingsPage.d.ts.map +0 -1
  146. package/dist/components/pages/ManualReviewDetailPage.d.ts +0 -3
  147. package/dist/components/pages/ManualReviewDetailPage.d.ts.map +0 -1
  148. package/dist/components/pages/ManualReviewPage.d.ts +0 -3
  149. package/dist/components/pages/ManualReviewPage.d.ts.map +0 -1
  150. package/dist/components/pages/MessagesPage.d.ts +0 -3
  151. package/dist/components/pages/MessagesPage.d.ts.map +0 -1
  152. package/dist/components/pages/MyLearningPage.d.ts +0 -3
  153. package/dist/components/pages/MyLearningPage.d.ts.map +0 -1
  154. package/dist/components/pages/PaymentCancelPage.d.ts +0 -3
  155. package/dist/components/pages/PaymentCancelPage.d.ts.map +0 -1
  156. package/dist/components/pages/PaymentSuccessPage.d.ts +0 -3
  157. package/dist/components/pages/PaymentSuccessPage.d.ts.map +0 -1
  158. package/dist/components/pages/index.cjs +0 -3306
  159. package/dist/components/pages/index.d.ts.map +0 -1
  160. package/dist/components/pages/index.js +0 -3315
  161. package/dist/components/utils.d.ts +0 -3
  162. package/dist/components/utils.d.ts.map +0 -1
  163. package/dist/contracts/components.contract.d.ts +0 -87
  164. package/dist/contracts/components.contract.d.ts.map +0 -1
  165. package/dist/contracts/index.cjs +0 -52
  166. package/dist/contracts/index.d.ts.map +0 -1
  167. package/dist/contracts/index.js +0 -29
  168. package/dist/contracts/layout.contract.d.ts +0 -35
  169. package/dist/contracts/layout.contract.d.ts.map +0 -1
  170. package/dist/contracts/pages.contract.d.ts +0 -192
  171. package/dist/contracts/pages.contract.d.ts.map +0 -1
  172. package/dist/contracts/template.contract.d.ts +0 -49
  173. package/dist/contracts/template.contract.d.ts.map +0 -1
  174. package/dist/hooks/index.cjs +0 -165
  175. package/dist/hooks/index.d.ts.map +0 -1
  176. package/dist/hooks/index.js +0 -142
  177. package/dist/hooks/sdk-context.d.ts +0 -125
  178. package/dist/hooks/sdk-context.d.ts.map +0 -1
  179. package/dist/hooks/useAiCoach.d.ts +0 -32
  180. package/dist/hooks/useAiCoach.d.ts.map +0 -1
  181. package/dist/hooks/useBookmarks.d.ts +0 -31
  182. package/dist/hooks/useBookmarks.d.ts.map +0 -1
  183. package/dist/hooks/useCourseSearch.d.ts.map +0 -1
  184. package/dist/hooks/useDebounce.d.ts.map +0 -1
  185. package/dist/hooks/useMyBundles.d.ts.map +0 -1
  186. package/dist/hooks/useMyCourses.d.ts.map +0 -1
  187. package/dist/hooks/useNotes.d.ts +0 -31
  188. package/dist/hooks/useNotes.d.ts.map +0 -1
  189. package/dist/hooks/useNotifications.d.ts +0 -19
  190. package/dist/hooks/useNotifications.d.ts.map +0 -1
  191. package/dist/hooks/useTheme.d.ts.map +0 -1
  192. package/dist/hooks/useToast.d.ts +0 -17
  193. package/dist/hooks/useToast.d.ts.map +0 -1
  194. package/dist/hooks/useUser.d.ts.map +0 -1
  195. package/dist/index.cjs +0 -630
  196. package/dist/index.d.ts +0 -17
  197. package/dist/index.d.ts.map +0 -1
  198. package/dist/index.js +0 -600
  199. package/dist/layouts/DefaultLayout.d.ts +0 -9
  200. package/dist/layouts/DefaultLayout.d.ts.map +0 -1
  201. package/dist/types/ai-coach.d.ts +0 -22
  202. package/dist/types/ai-coach.d.ts.map +0 -1
  203. package/dist/types/bookmarks.d.ts +0 -19
  204. package/dist/types/bookmarks.d.ts.map +0 -1
  205. package/dist/types/bundle.d.ts +0 -114
  206. package/dist/types/bundle.d.ts.map +0 -1
  207. package/dist/types/common.d.ts +0 -23
  208. package/dist/types/common.d.ts.map +0 -1
  209. package/dist/types/course.d.ts +0 -127
  210. package/dist/types/course.d.ts.map +0 -1
  211. package/dist/types/enrollment.d.ts +0 -34
  212. package/dist/types/enrollment.d.ts.map +0 -1
  213. package/dist/types/index.cjs +0 -18
  214. package/dist/types/index.d.ts.map +0 -1
  215. package/dist/types/index.js +0 -0
  216. package/dist/types/lesson.d.ts +0 -105
  217. package/dist/types/lesson.d.ts.map +0 -1
  218. package/dist/types/manual-review.d.ts +0 -123
  219. package/dist/types/manual-review.d.ts.map +0 -1
  220. package/dist/types/messaging.d.ts +0 -101
  221. package/dist/types/messaging.d.ts.map +0 -1
  222. package/dist/types/notification.d.ts +0 -28
  223. package/dist/types/notification.d.ts.map +0 -1
  224. package/dist/types/payment.d.ts +0 -38
  225. package/dist/types/payment.d.ts.map +0 -1
  226. package/dist/types/progress.d.ts +0 -18
  227. package/dist/types/progress.d.ts.map +0 -1
  228. package/dist/types/rating.d.ts +0 -20
  229. package/dist/types/rating.d.ts.map +0 -1
  230. package/dist/types/search.d.ts +0 -28
  231. package/dist/types/search.d.ts.map +0 -1
  232. package/dist/types/user.d.ts +0 -15
  233. package/dist/types/user.d.ts.map +0 -1
  234. package/dist/utils/formatters.d.ts +0 -25
  235. package/dist/utils/formatters.d.ts.map +0 -1
  236. package/dist/utils/index.cjs +0 -80
  237. package/dist/utils/index.d.ts +0 -2
  238. package/dist/utils/index.d.ts.map +0 -1
  239. package/dist/utils/index.js +0 -57
@@ -1,3306 +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/pages/index.ts
60
- var pages_exports = {};
61
- __export(pages_exports, {
62
- BundleDetailPage: () => BundleDetailPage,
63
- CatalogBundlesPage: () => CatalogBundlesPage,
64
- CatalogCoursesPage: () => CatalogCoursesPage,
65
- CourseDetailPage: () => CourseDetailPage,
66
- CoursePlayerPage: () => CoursePlayerPage,
67
- CreatorProfilePage: () => CreatorProfilePage,
68
- LearnerSettingsPage: () => LearnerSettingsPage,
69
- ManualReviewDetailPage: () => ManualReviewDetailPage,
70
- ManualReviewPage: () => ManualReviewPage,
71
- MessagesPage: () => MessagesPage,
72
- MyLearningPage: () => MyLearningPage,
73
- PaymentCancelPage: () => PaymentCancelPage,
74
- PaymentSuccessPage: () => PaymentSuccessPage
75
- });
76
- module.exports = __toCommonJS(pages_exports);
77
-
78
- // src/components/pages/MyLearningPage.tsx
79
- var import_react2 = require("react");
80
- var import_lucide_react3 = require("lucide-react");
81
-
82
- // src/components/molecules/CourseCard.tsx
83
- var import_react = require("react");
84
- var import_lucide_react = require("lucide-react");
85
-
86
- // src/components/utils.ts
87
- var import_clsx = require("clsx");
88
- var import_tailwind_merge = require("tailwind-merge");
89
- function cn(...inputs) {
90
- return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
91
- }
92
-
93
- // src/components/molecules/CourseCard.tsx
94
- var import_jsx_runtime = require("react/jsx-runtime");
95
- function CourseCard({
96
- id,
97
- title,
98
- thumbnail,
99
- thumbnails,
100
- totalLessons,
101
- progress,
102
- status,
103
- isFree,
104
- price = 0,
105
- currency = "USD",
106
- description,
107
- showPrice = true,
108
- showProgress = true,
109
- category,
110
- duration,
111
- instructorName,
112
- instructorAvatar,
113
- originalPrice,
114
- isBundle = false,
115
- onClick,
116
- className
117
- }) {
118
- const [imageError, setImageError] = (0, import_react.useState)(false);
119
- const clampedProgress = Math.min(Math.max(progress, 0), 100);
120
- const stripHtml = (html) => {
121
- if (typeof document === "undefined") return html;
122
- const tmp = document.createElement("div");
123
- tmp.innerHTML = html;
124
- return tmp.textContent || tmp.innerText || "";
125
- };
126
- const displayCategory = category || "Design";
127
- const displayDuration = duration || "3 Month";
128
- const displayInstructorName = instructorName || (description ? stripHtml(description).slice(0, 15) : "Instructor");
129
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
130
- "div",
131
- {
132
- className: cn(
133
- "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",
134
- className
135
- ),
136
- onClick,
137
- children: [
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative h-[238.66px] w-full overflow-hidden mx-auto px-3 pt-3", children: [
139
- /* @__PURE__ */ (0, import_jsx_runtime.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_runtime.jsxs)("div", { className: "relative w-full h-full flex", children: [
140
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative h-full w-1/2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
141
- "img",
142
- {
143
- src: thumbnails[0],
144
- alt: `${title} - image 1`,
145
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
146
- onError: () => setImageError(true)
147
- }
148
- ) }),
149
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col w-1/2 h-full", children: [
150
- thumbnails[1] && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("relative w-full overflow-hidden", thumbnails.length > 2 ? "h-1/2" : "h-full"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
151
- "img",
152
- {
153
- src: thumbnails[1],
154
- alt: `${title} - image 2`,
155
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
156
- onError: () => setImageError(true)
157
- }
158
- ) }),
159
- thumbnails.length > 2 && thumbnails[2] && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "relative w-full h-1/2 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
160
- "img",
161
- {
162
- src: thumbnails[2],
163
- alt: `${title} - image 3`,
164
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
165
- onError: () => setImageError(true)
166
- }
167
- ) })
168
- ] })
169
- ] }) : thumbnail && !imageError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
170
- "img",
171
- {
172
- src: thumbnail,
173
- alt: title,
174
- className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",
175
- onError: () => setImageError(true)
176
- }
177
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full h-full flex items-center justify-center bg-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.BookOpen, { className: "w-12 h-12 text-gray-400" }) }) }),
178
- status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute right-6 top-5", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
179
- "span",
180
- {
181
- 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))]"}`,
182
- children: status
183
- }
184
- ) })
185
- ] }),
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-5 space-y-3 flex-1 flex flex-col min-w-0", children: [
187
- (displayCategory || displayDuration) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500", children: [
188
- displayCategory && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5", children: [
189
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Grid2X2, { className: "w-3.5 h-3.5 text-gray-400" }),
190
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: displayCategory })
191
- ] }),
192
- displayDuration && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5", children: [
193
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Clock, { className: "w-3.5 h-3.5 text-gray-400" }),
194
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: displayDuration })
195
- ] })
196
- ] }),
197
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-base font-bold text-gray-900 line-clamp-2 leading-snug", children: title }),
198
- description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-gray-500 line-clamp-3 leading-relaxed", children: stripHtml(description) }),
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
200
- showProgress && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-2 overflow-hidden rounded-full bg-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
202
- "div",
203
- {
204
- className: "h-full rounded-full bg-[rgb(var(--accent-primary))] transition-all duration-300",
205
- style: { width: `${clampedProgress}%` }
206
- }
207
- ) }),
208
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between text-xs", children: [
209
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-gray-400" }),
210
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-gray-500", children: [
211
- isBundle ? "Course" : "Lesson",
212
- " ",
213
- Math.ceil(clampedProgress / 100 * totalLessons),
214
- " of ",
215
- totalLessons
216
- ] })
217
- ] })
218
- ] }),
219
- showPrice && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between pt-3 border-t border-gray-100", children: [
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
221
- instructorAvatar ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-7 h-7 rounded-full overflow-hidden flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: instructorAvatar, alt: displayInstructorName, className: "w-full h-full object-cover" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.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_runtime.jsx)("span", { className: "text-[10px] font-semibold text-gray-500", children: displayInstructorName.charAt(0).toUpperCase() }) }),
222
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm text-gray-600 font-medium truncate max-w-[80px]", children: displayInstructorName })
223
- ] }),
224
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
225
- !isFree && originalPrice && originalPrice > price && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-sm text-gray-400 line-through", children: [
226
- "$",
227
- originalPrice
228
- ] }),
229
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-lg font-bold text-[rgb(var(--accent-primary))]", children: isFree ? "Free" : `$${price}` })
230
- ] })
231
- ] })
232
- ] })
233
- ]
234
- }
235
- );
236
- }
237
-
238
- // src/components/molecules/Pagination.tsx
239
- var import_lucide_react2 = require("lucide-react");
240
- var import_jsx_runtime2 = require("react/jsx-runtime");
241
- function Pagination({
242
- currentPage,
243
- totalPages,
244
- total,
245
- pageSize,
246
- hasNextPage,
247
- hasPreviousPage,
248
- onPageChange,
249
- maxVisiblePages = 5,
250
- className
251
- }) {
252
- const renderPageNumbers = () => {
253
- const pages = [];
254
- let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
255
- let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
256
- if (endPage - startPage < maxVisiblePages - 1) {
257
- startPage = Math.max(1, endPage - maxVisiblePages + 1);
258
- }
259
- for (let i = startPage; i <= endPage; i++) {
260
- pages.push(
261
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
262
- "button",
263
- {
264
- onClick: () => onPageChange(i),
265
- className: cn(
266
- "px-3 py-1 rounded cursor-pointer transition-colors",
267
- i === currentPage ? "bg-theme-accent-primary text-white" : "text-theme-text-primary border border-theme-border-primary hover:bg-theme-bg-tertiary"
268
- ),
269
- children: i
270
- },
271
- i
272
- )
273
- );
274
- }
275
- return pages;
276
- };
277
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("flex items-center justify-between border-t border-theme-border-primary pt-4", className), children: [
278
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-sm text-theme-text-secondary", children: [
279
- "Showing ",
280
- (currentPage - 1) * pageSize + 1,
281
- " to ",
282
- Math.min(currentPage * pageSize, total),
283
- " of ",
284
- total,
285
- " results"
286
- ] }),
287
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
288
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
289
- "button",
290
- {
291
- onClick: () => onPageChange(currentPage - 1),
292
- disabled: !hasPreviousPage,
293
- 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",
294
- "aria-label": "Previous page",
295
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ChevronLeft, { className: "h-4 w-4" })
296
- }
297
- ),
298
- renderPageNumbers(),
299
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
300
- "button",
301
- {
302
- onClick: () => onPageChange(currentPage + 1),
303
- disabled: !hasNextPage,
304
- 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",
305
- "aria-label": "Next page",
306
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ChevronRight, { className: "h-4 w-4" })
307
- }
308
- )
309
- ] })
310
- ] });
311
- }
312
-
313
- // src/components/pages/MyLearningPage.tsx
314
- var import_jsx_runtime3 = require("react/jsx-runtime");
315
- function MyLearningPage(props) {
316
- const noop = () => {
317
- };
318
- const {
319
- courses = [],
320
- coursesMeta,
321
- coursesLoading,
322
- coursesPage = 1,
323
- onCoursesPageChange = noop,
324
- bundles = [],
325
- bundlesMeta,
326
- bundlesLoading,
327
- bundlesPage = 1,
328
- onBundlesPageChange = noop,
329
- searchQuery = "",
330
- onSearchChange = noop,
331
- activeTab = "courses",
332
- onTabChange = noop,
333
- statusFilter = "all",
334
- onStatusFilterChange = noop,
335
- onCourseClick = noop,
336
- onBundleClick = noop
337
- } = props;
338
- const [showStatusDropdown, setShowStatusDropdown] = (0, import_react2.useState)(false);
339
- const isLoading = activeTab === "courses" ? !!coursesLoading : !!bundlesLoading;
340
- const currentPage = activeTab === "courses" ? coursesPage : bundlesPage;
341
- const meta = activeTab === "courses" ? coursesMeta : bundlesMeta;
342
- const onPageChange = activeTab === "courses" ? onCoursesPageChange : onBundlesPageChange;
343
- const isSearchMode = Boolean(searchQuery.trim());
344
- const getStatusLabel = () => {
345
- switch (statusFilter) {
346
- case "in-progress":
347
- return "In Progress";
348
- case "completed":
349
- return "Completed";
350
- default:
351
- return "All Status";
352
- }
353
- };
354
- const handleStatusFilterChange = (status) => {
355
- onStatusFilterChange(status);
356
- setShowStatusDropdown(false);
357
- };
358
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "container mx-auto space-y-6", children: [
359
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
360
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "text-3xl font-bold text-theme-text-primary", children: "My Learning Content" }),
361
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-2 text-lg text-theme-text-secondary", children: "Continue your learning journey with courses and bundles" })
362
- ] }),
363
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-4", children: [
364
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative flex-1", children: [
365
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Search, { className: "absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-theme-text-secondary z-10" }),
366
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
367
- "input",
368
- {
369
- type: "text",
370
- placeholder: `Search ${activeTab}...`,
371
- value: searchQuery,
372
- onChange: (e) => onSearchChange(e.target.value),
373
- className: "w-full rounded-lg border border-theme-border-primary bg-theme-bg-secondary pl-10 pr-10 py-2 text-sm text-theme-text-primary placeholder:text-theme-text-secondary focus:outline-none focus:ring-2 focus:ring-theme-accent-primary/30 focus:border-theme-accent-primary transition-all"
374
- }
375
- ),
376
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
377
- "button",
378
- {
379
- onClick: () => onSearchChange(""),
380
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-theme-text-secondary hover:text-theme-text-primary transition-colors",
381
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.X, { className: "h-5 w-5" })
382
- }
383
- )
384
- ] }),
385
- !isSearchMode && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative", children: [
386
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
387
- "button",
388
- {
389
- onClick: () => setShowStatusDropdown(!showStatusDropdown),
390
- className: "flex items-center gap-2 min-w-[150px] justify-between px-4 py-2 border border-theme-border-primary rounded-lg text-sm text-theme-text-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary transition-colors cursor-pointer",
391
- children: [
392
- getStatusLabel(),
393
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronDown, { className: "h-4 w-4" })
394
- ]
395
- }
396
- ),
397
- showStatusDropdown && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute right-0 mt-2 w-48 rounded-lg border border-theme-border-primary bg-theme-bg-secondary shadow-lg z-10", children: ["all", "in-progress", "completed"].map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
398
- "button",
399
- {
400
- onClick: () => handleStatusFilterChange(s),
401
- className: `w-full text-left px-4 py-2 hover:bg-theme-bg-tertiary transition-colors ${statusFilter === s ? "bg-theme-bg-tertiary text-theme-accent-primary font-medium" : "text-theme-text-primary"}`,
402
- children: s === "all" ? "All Status" : s === "in-progress" ? "In Progress" : "Completed"
403
- },
404
- s
405
- )) })
406
- ] })
407
- ] }),
408
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex gap-6 border-b border-theme-border-primary", children: ["courses", "bundles"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
409
- "button",
410
- {
411
- onClick: () => {
412
- onTabChange(tab);
413
- onSearchChange("");
414
- onStatusFilterChange("all");
415
- },
416
- className: `pb-3 px-0 bg-transparent border-0 font-medium text-sm cursor-pointer capitalize ${activeTab === tab ? "border-b-2 border-theme-accent-primary text-theme-accent-primary" : "text-theme-text-secondary hover:text-theme-text-primary border-b-2 border-transparent"}`,
417
- children: tab.charAt(0).toUpperCase() + tab.slice(1)
418
- },
419
- tab
420
- )) }),
421
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center justify-center py-16", children: [
422
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-theme-border-primary border-t-theme-accent-primary" }),
423
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "mt-4 text-theme-text-secondary", children: [
424
- "Loading ",
425
- activeTab,
426
- "..."
427
- ] })
428
- ] }),
429
- !isLoading && activeTab === "courses" && courses.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-theme-border-primary bg-theme-bg-secondary py-16 text-center", children: [
430
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-4xl mb-4", children: "\u{1F4DA}" }),
431
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "text-xl font-semibold text-theme-text-primary", children: "No courses found" }),
432
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-2 max-w-md text-theme-text-secondary", children: statusFilter === "all" ? "You don't have any enrolled courses yet" : `You don't have any ${statusFilter === "in-progress" ? "in progress" : "completed"} courses yet` })
433
- ] }),
434
- !isLoading && activeTab === "bundles" && bundles.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-theme-border-primary bg-theme-bg-secondary py-16 text-center", children: [
435
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-4xl mb-4", children: "\u{1F4E6}" }),
436
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "text-xl font-semibold text-theme-text-primary", children: "No bundles found" }),
437
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-2 max-w-md text-theme-text-secondary", children: statusFilter === "all" ? "You don't have any enrolled bundles yet" : `You don't have any ${statusFilter === "in-progress" ? "in progress" : "completed"} bundles yet` })
438
- ] }),
439
- !isLoading && activeTab === "courses" && courses.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
440
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: courses.map((enrollment) => {
441
- const course = enrollment.course || enrollment;
442
- const title = (course == null ? void 0 : course.title) || enrollment.title || "";
443
- const thumbnail = (course == null ? void 0 : course.thumbnail) || enrollment.thumbnail;
444
- const totalLessons = enrollment.totalNumberOfActivities || enrollment.totalLessons || 0;
445
- const progress = Math.min(enrollment.progressPercentage || enrollment.progress || 0, 100);
446
- const status = enrollment.status === "COMPLETED" ? "completed" : enrollment.status === "IN_PROGRESS" ? "in-progress" : void 0;
447
- const description = ((course == null ? void 0 : course.summary) || enrollment.description || "").replace(/<[^>]*>/g, "");
448
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
449
- CourseCard,
450
- {
451
- id: enrollment.id,
452
- title,
453
- thumbnail,
454
- totalLessons,
455
- progress,
456
- status,
457
- isFree: true,
458
- price: 0,
459
- showPrice: false,
460
- showProgress: true,
461
- description,
462
- onClick: () => onCourseClick((course == null ? void 0 : course.id) || enrollment.courseId || enrollment.id, enrollment.id)
463
- },
464
- enrollment.id
465
- );
466
- }) }),
467
- meta && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
468
- Pagination,
469
- {
470
- currentPage,
471
- totalPages: meta.totalPages,
472
- total: meta.total,
473
- pageSize: meta.limit || 12,
474
- hasNextPage: currentPage < meta.totalPages,
475
- hasPreviousPage: currentPage > 1,
476
- onPageChange
477
- }
478
- )
479
- ] }),
480
- !isLoading && activeTab === "bundles" && bundles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
481
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: bundles.map((enrollment) => {
482
- var _a, _b, _c;
483
- const thumbnails = (_a = enrollment.courses) == null ? void 0 : _a.slice(0, 3).map((c) => c.thumbnail).filter(Boolean);
484
- const description = ((_c = (_b = enrollment.courses) == null ? void 0 : _b[0]) == null ? void 0 : _c.summary) || "";
485
- const status = enrollment.status === "COMPLETED" ? "completed" : enrollment.status === "IN_PROGRESS" ? "in-progress" : void 0;
486
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
487
- CourseCard,
488
- {
489
- id: enrollment.id,
490
- title: enrollment.bundleTitle || enrollment.title || "",
491
- thumbnails,
492
- totalLessons: enrollment.totalCourses || 0,
493
- progress: Math.min(enrollment.progressPercentage || 0, 100),
494
- status,
495
- isFree: true,
496
- price: 0,
497
- showPrice: false,
498
- showProgress: true,
499
- description,
500
- isBundle: true,
501
- onClick: () => onBundleClick(enrollment.sourceBundleId || enrollment.bundleId || enrollment.id)
502
- },
503
- enrollment.id
504
- );
505
- }) }),
506
- meta && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
507
- Pagination,
508
- {
509
- currentPage,
510
- totalPages: meta.totalPages,
511
- total: meta.total,
512
- pageSize: meta.limit || 12,
513
- hasNextPage: currentPage < meta.totalPages,
514
- hasPreviousPage: currentPage > 1,
515
- onPageChange
516
- }
517
- )
518
- ] })
519
- ] });
520
- }
521
-
522
- // src/components/pages/CatalogCoursesPage.tsx
523
- var import_react3 = require("react");
524
- var import_lucide_react4 = require("lucide-react");
525
- var import_jsx_runtime4 = require("react/jsx-runtime");
526
- var FILE_URL = "https://fe-academy-file.dev.magicai-testing-domain.work";
527
- var FEATURES = [
528
- "Free E-book, video & consolation",
529
- "Top instructors from around world",
530
- "Top courses from your team"
531
- ];
532
- var DEALS = [
533
- { id: 1, discount: "50%", title: "Lorem ipsum dolor", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor", image: `${FILE_URL}/deal-1.png` },
534
- { id: 2, discount: "10%", title: "Lorem ipsum dolor", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor", image: `${FILE_URL}/deal-2.png` },
535
- { id: 3, discount: "50%", title: "Lorem ipsum dolor", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor", image: `${FILE_URL}/deal-3.png` }
536
- ];
537
- var RECOMMENDED = [
538
- { id: "d87b963f", title: "testtgfsdfg dfs", thumbnail: `${FILE_URL}/uploads/4e2fd752-e8c0-440a-8622-3102d33e56e7-1771918805146.png`, totalLessons: 1, progress: 0, isFree: true, price: 0 },
539
- { id: "30ceedf8", title: "tessst 11", thumbnail: `${FILE_URL}/uploads/ec2ebd93-f06e-48ce-914a-116b1b91c480-1771878020911.jpeg`, totalLessons: 1, progress: 0, isFree: true, price: 0 },
540
- { id: "eae7f568", title: "React", thumbnail: `${FILE_URL}/uploads/ca3257ea-618c-4a61-a4b6-5560c2bf5ec1-1771488717244.png`, totalLessons: 2, progress: 0, isFree: true, price: 0 },
541
- { id: "26a52485", title: "ll1", thumbnail: `${FILE_URL}/uploads/9bf7f1ca-214b-4a82-862d-f334801bcd53-1771486627052.png`, totalLessons: 1, progress: 0, isFree: true, price: 0 }
542
- ];
543
- var SIDE_AVATARS = [`${FILE_URL}/avatar-1.png`, `${FILE_URL}/avatar-2.png`, `${FILE_URL}/avatar-3.png`, `${FILE_URL}/avatar-4.png`];
544
- function CatalogCoursesPage(props) {
545
- const noop = (..._args) => {
546
- };
547
- const {
548
- courses = [],
549
- meta,
550
- loading = false,
551
- searchQuery = "",
552
- page = 1,
553
- onSearchChange = noop,
554
- onPageChange = noop,
555
- onCourseClick = noop,
556
- activeTab = "courses",
557
- onTabChange,
558
- bundleCourses = [],
559
- bundleMeta,
560
- bundlesLoading = false,
561
- bundlePage = 1,
562
- onBundlePageChange = noop,
563
- onBundleClick = noop
564
- } = props;
565
- const [localQuery, setLocalQuery] = (0, import_react3.useState)(searchQuery);
566
- const defaultHeroImage = `${FILE_URL}/catalog-hero-banner.png`;
567
- const handleSearchSubmit = (e) => {
568
- e.preventDefault();
569
- onSearchChange(localQuery);
570
- };
571
- const handleSearchChange = (val) => {
572
- setLocalQuery(val);
573
- onSearchChange(val);
574
- };
575
- const displayCourses = activeTab === "courses" ? courses : bundleCourses;
576
- const displayMeta = activeTab === "courses" ? meta : bundleMeta;
577
- const displayLoading = activeTab === "courses" ? loading : bundlesLoading;
578
- const displayPage = activeTab === "courses" ? page : bundlePage;
579
- const displayPageChange = activeTab === "courses" ? onPageChange : onBundlePageChange;
580
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "container mx-auto space-y-10", children: [
581
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative w-full overflow-hidden rounded-2xl", children: [
582
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
583
- "div",
584
- {
585
- className: "absolute inset-0 z-0 bg-cover bg-center",
586
- style: { backgroundImage: `var(--catalog-hero-image, url(${defaultHeroImage}))` },
587
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
588
- "div",
589
- {
590
- className: "absolute inset-0 backdrop-blur-[1px]",
591
- style: { background: "linear-gradient(135deg, rgba(73,187,189,0.75) 0%, rgba(73,187,189,0.55) 50%, rgba(73,187,189,0.40) 100%)" }
592
- }
593
- )
594
- }
595
- ),
596
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative z-10 flex flex-col items-center justify-center px-4 py-8 sm:px-6 sm:py-10 gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
597
- "form",
598
- {
599
- onSubmit: handleSearchSubmit,
600
- className: "flex w-full max-w-[680px] bg-white rounded-2xl sm:rounded-full p-1 sm:p-[5px] sm:pl-0 shadow-lg flex-col sm:flex-row gap-1 sm:gap-0 sm:items-center",
601
- children: [
602
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative flex-1 flex items-center w-full", children: [
603
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react4.Search, { className: "absolute left-4 sm:left-5 w-5 h-5 text-gray-400 pointer-events-none" }),
604
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
605
- "input",
606
- {
607
- type: "text",
608
- placeholder: activeTab === "courses" ? "Search your favourite course" : "Search your favourite bundle",
609
- value: localQuery,
610
- onChange: (e) => handleSearchChange(e.target.value),
611
- className: "w-full py-3 sm:py-3.5 pl-12 sm:pl-13 pr-4 bg-transparent text-gray-700 text-sm sm:text-[15px] rounded-full border-none outline-none placeholder:text-gray-400"
612
- }
613
- )
614
- ] }),
615
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
616
- "button",
617
- {
618
- type: "submit",
619
- className: "shrink-0 px-8 py-3 bg-theme-accent-primary text-white font-semibold text-sm sm:text-[15px] rounded-xl sm:rounded-full cursor-pointer transition-all duration-150 hover:brightness-110 hover:scale-[1.02] active:scale-[0.98] w-full sm:w-auto",
620
- children: "Search"
621
- }
622
- )
623
- ]
624
- }
625
- ) })
626
- ] }),
627
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex gap-2", children: [{ id: "courses", label: "Courses", Icon: import_lucide_react4.BookOpen }, { id: "bundles", label: "Bundles", Icon: import_lucide_react4.Package }].map(({ id, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
628
- "button",
629
- {
630
- onClick: () => onTabChange == null ? void 0 : onTabChange(id),
631
- className: `flex items-center gap-2 px-4 py-2.5 font-medium transition-colors cursor-pointer ${activeTab === id ? "border-b-2 border-theme-accent-primary text-theme-accent-primary" : "text-theme-text-secondary hover:text-theme-text-primary"}`,
632
- children: [
633
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "h-4 w-4" }),
634
- label
635
- ]
636
- },
637
- id
638
- )) }),
639
- displayLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col items-center justify-center py-16", children: [
640
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-theme-border-primary border-t-theme-accent-primary" }),
641
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "mt-4 text-theme-text-secondary", children: [
642
- "Loading ",
643
- activeTab,
644
- "..."
645
- ] })
646
- ] }) : displayCourses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-theme-border-primary bg-theme-bg-secondary py-16 text-center", children: [
647
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-4xl mb-4", children: activeTab === "courses" ? "\u{1F4DA}" : "\u{1F4E6}" }),
648
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { className: "text-xl font-semibold text-theme-text-primary", children: [
649
- "No ",
650
- activeTab,
651
- " found"
652
- ] }),
653
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mt-2 max-w-md text-theme-text-secondary", children: "Try adjusting your search query or check back later" })
654
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
655
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: displayCourses.map((course) => {
656
- var _a;
657
- const description = (course.summary || course.description || "").replace(/<[^>]*>/g, "");
658
- const price = (_a = course.price) != null ? _a : 0;
659
- const isFree = course.isFree !== void 0 ? course.isFree : price === 0;
660
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
661
- CourseCard,
662
- {
663
- id: course.id,
664
- title: course.title || "",
665
- thumbnail: course.thumbnail || "",
666
- totalLessons: course.totalLessons || 0,
667
- progress: 0,
668
- showProgress: false,
669
- isFree,
670
- price,
671
- showPrice: true,
672
- currency: course.currency,
673
- description,
674
- instructorName: course.instructorName,
675
- instructorAvatar: course.instructorAvatar,
676
- category: course.category,
677
- originalPrice: course.originalPrice,
678
- isBundle: activeTab === "bundles",
679
- thumbnails: course.thumbnails,
680
- onClick: () => activeTab === "courses" ? onCourseClick(course.id) : onBundleClick(course.id)
681
- },
682
- course.id
683
- );
684
- }) }),
685
- displayMeta && displayMeta.totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
686
- Pagination,
687
- {
688
- currentPage: displayPage,
689
- totalPages: displayMeta.totalPages,
690
- total: displayMeta.total,
691
- pageSize: displayMeta.limit || 8,
692
- hasNextPage: displayPage < displayMeta.totalPages,
693
- hasPreviousPage: displayPage > 1,
694
- onPageChange: displayPageChange
695
- }
696
- )
697
- ] }),
698
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { className: "rounded-2xl bg-[#EEF4FA] p-6 sm:p-8 lg:p-10 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col items-center gap-8 lg:flex-row lg:items-center lg:justify-between px-10", children: [
699
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-6 lg:max-w-md shrink-0 py-4", children: [
700
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h2", { className: "text-2xl font-bold text-gray-900 sm:text-3xl leading-snug", children: [
701
- "Know about learning",
702
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("br", {}),
703
- "learning platform"
704
- ] }),
705
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "space-y-5", children: FEATURES.map((feature, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "flex items-center gap-4 text-sm text-gray-600 font-medium", children: [
706
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "h-4 w-4 rounded-full bg-[#B8F0E8] shrink-0" }),
707
- feature
708
- ] }, i)) }),
709
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "inline-block rounded-lg bg-theme-accent-primary px-8 py-3.5 text-sm font-bold text-white shadow-md transition-all hover:brightness-110 hover:scale-[1.02] active:scale-[0.98]", children: "Start learning now" })
710
- ] }),
711
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative w-full max-w-[520px] lg:w-[55%]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "aspect-[16/10] w-full flex items-center justify-center bg-[#dbeafe] rounded-xl", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-gray-400 text-sm", children: "Platform Preview" }) }) })
712
- ] }) }),
713
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative w-screen left-1/2 right-1/2 -ml-[50vw] -mr-[50vw] bg-[#EEF4FA]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "space-y-6", children: [
714
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between", children: [
715
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-xl font-bold text-gray-900 sm:text-2xl", children: "Recommended for you" }),
716
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "shrink-0 text-sm font-semibold text-[rgb(var(--accent-primary))] hover:underline cursor-pointer", children: "See all" })
717
- ] }),
718
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: RECOMMENDED.map((course) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
719
- CourseCard,
720
- {
721
- id: course.id,
722
- title: course.title,
723
- thumbnail: course.thumbnail,
724
- totalLessons: course.totalLessons,
725
- progress: course.progress,
726
- showProgress: false,
727
- isFree: course.isFree,
728
- price: course.price,
729
- showPrice: true,
730
- onClick: () => onCourseClick(course.id)
731
- },
732
- course.id
733
- )) })
734
- ] }) }) }),
735
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative w-screen left-1/2 right-1/2 -ml-[50vw] -mr-[50vw] bg-[#EEF4FA]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "space-y-6", children: [
736
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-xl font-bold text-gray-900 sm:text-2xl", children: "What our students have to say" }),
737
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rounded-2xl bg-white p-6 sm:p-8 lg:p-14 shadow-sm relative overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col items-center gap-6 lg:flex-row lg:items-center lg:gap-16", children: [
738
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative shrink-0 w-48 h-48 sm:w-64 sm:h-64 bg-gray-100 rounded-full flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: `${FILE_URL}/student.png`, alt: "Student", className: "w-full h-full object-cover rounded-full", onError: (e) => {
739
- e.target.style.display = "none";
740
- } }) }),
741
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-1 space-y-4 text-center lg:text-left min-w-0 z-10", children: [
742
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-1", children: [
743
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-xl font-bold text-gray-900", children: "Savannah Nguyen" }),
744
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm text-gray-500 font-medium tracking-wide", children: "tanya.hill@example.com" })
745
- ] }),
746
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-2 text-sm text-gray-500 leading-relaxed max-w-lg", children: [
747
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" }),
748
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" }),
749
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" })
750
- ] }),
751
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex items-center gap-3 pt-2 justify-center lg:justify-start", children: [import_lucide_react4.Twitter, import_lucide_react4.Facebook, import_lucide_react4.Instagram].map((Icon, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-theme-accent-primary text-white transition-all hover:scale-110 hover:shadow-md", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "h-4 w-4" }) }, i)) })
752
- ] }),
753
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "hidden lg:flex flex-col gap-5 shrink-0 z-10", children: SIDE_AVATARS.map((avatar, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative h-14 w-14 overflow-hidden rounded-full border-2 border-white shadow-md transition-all hover:scale-110 cursor-pointer hover:border-[#49BBBD]", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: avatar, alt: `Student ${i + 1}`, className: "w-full h-full object-cover", onError: (e) => {
754
- e.target.style.display = "none";
755
- } }) }, i)) })
756
- ] }) })
757
- ] }) }) }),
758
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "space-y-6", children: [
759
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between", children: [
760
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-xl font-bold text-gray-900 sm:text-2xl", children: "Top Education offers and deals are listed here" }),
761
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "shrink-0 text-sm font-semibold text-[rgb(var(--accent-primary))] hover:underline cursor-pointer", children: "See all" })
762
- ] }),
763
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: DEALS.map((deal) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "group relative overflow-hidden rounded-2xl cursor-pointer h-[320px] sm:h-[350px]", children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: deal.image, alt: deal.title, className: "absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-110", onError: (e) => {
765
- e.target.style.background = "#e5e7eb";
766
- } }),
767
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-black/10" }),
768
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "absolute inset-0 flex flex-col justify-between p-5 sm:p-6", children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "inline-block rounded-lg bg-[rgb(var(--accent-primary))] px-4 py-2 text-xl font-bold text-white sm:text-2xl", children: deal.discount }) }),
770
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-2", children: [
771
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-lg font-bold text-white sm:text-xl", children: deal.title }),
772
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm text-white/80 leading-relaxed line-clamp-2", children: deal.description })
773
- ] })
774
- ] })
775
- ] }, deal.id)) })
776
- ] })
777
- ] });
778
- }
779
-
780
- // src/components/pages/CatalogBundlesPage.tsx
781
- var import_lucide_react5 = require("lucide-react");
782
- var import_jsx_runtime5 = require("react/jsx-runtime");
783
- function CatalogBundlesPage(props) {
784
- const noop = (..._args) => {
785
- };
786
- const { bundles = [], meta, loading = false, searchQuery = "", page = 1, onSearchChange = noop, onPageChange = noop, onBundleClick = noop } = props;
787
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-6", children: [
788
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative", children: [
789
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.Search, { className: "absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-theme-text-secondary z-10" }),
790
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
791
- "input",
792
- {
793
- type: "text",
794
- placeholder: "Search bundles...",
795
- value: searchQuery,
796
- onChange: (e) => onSearchChange(e.target.value),
797
- className: "w-full rounded-lg border border-theme-border-primary bg-theme-bg-secondary pl-10 pr-10 py-2 text-sm text-theme-text-primary placeholder:text-theme-text-secondary focus:outline-none focus:ring-2 focus:ring-theme-accent-primary/30 focus:border-theme-accent-primary transition-all"
798
- }
799
- ),
800
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
801
- "button",
802
- {
803
- onClick: () => onSearchChange(""),
804
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-theme-text-secondary hover:text-theme-text-primary transition-colors",
805
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.X, { className: "h-5 w-5" })
806
- }
807
- )
808
- ] }),
809
- loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center justify-center py-16", children: [
810
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-theme-border-primary border-t-theme-accent-primary" }),
811
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-4 text-theme-text-secondary", children: "Loading bundles..." })
812
- ] }) : bundles.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-theme-border-primary bg-theme-bg-secondary py-16 text-center", children: [
813
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-4xl mb-4", children: "\u{1F4E6}" }),
814
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "text-xl font-semibold text-theme-text-primary", children: "No bundles found" }),
815
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mt-2 max-w-md text-theme-text-secondary", children: "Try adjusting your search query or check back later for new bundles" })
816
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
817
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: bundles.map((bundle) => {
818
- var _a, _b, _c;
819
- const priceValue = bundle.price !== void 0 && bundle.price !== null ? bundle.price : ((_a = bundle.settings) == null ? void 0 : _a.price) || 0;
820
- const pricingType = bundle.pricingType || ((_b = bundle.settings) == null ? void 0 : _b.pricingType);
821
- const isFree = pricingType === "FREE" || !priceValue || priceValue === 0;
822
- const thumbnails = bundle.courses && bundle.courses.length > 0 ? bundle.courses.sort((a, b) => a.orderIndex - b.orderIndex).slice(0, 3).map((c) => c.thumbnail) : bundle.thumbnails || [];
823
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
824
- CourseCard,
825
- {
826
- id: bundle.id,
827
- title: bundle.title || "",
828
- thumbnail: thumbnails[0],
829
- thumbnails,
830
- totalLessons: bundle.totalCourses || ((_c = bundle.courses) == null ? void 0 : _c.length) || 0,
831
- progress: 0,
832
- showProgress: false,
833
- isFree,
834
- price: priceValue,
835
- showPrice: true,
836
- currency: bundle.currency,
837
- description: bundle.description || "",
838
- isBundle: true,
839
- onClick: () => onBundleClick(bundle.id)
840
- },
841
- bundle.id
842
- );
843
- }) }),
844
- meta && meta.totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
845
- Pagination,
846
- {
847
- currentPage: page,
848
- totalPages: meta.totalPages,
849
- total: meta.total,
850
- pageSize: meta.limit || 12,
851
- hasNextPage: page < meta.totalPages,
852
- hasPreviousPage: page > 1,
853
- onPageChange
854
- }
855
- )
856
- ] })
857
- ] });
858
- }
859
-
860
- // src/components/pages/CourseDetailPage.tsx
861
- var import_react4 = require("react");
862
- var import_lucide_react6 = require("lucide-react");
863
-
864
- // src/components/molecules/LoadingSpinner.tsx
865
- var import_jsx_runtime6 = require("react/jsx-runtime");
866
- var sizeClasses = {
867
- sm: "h-4 w-4",
868
- md: "h-8 w-8",
869
- lg: "h-12 w-12"
870
- };
871
- function LoadingSpinner({ size = "md", className, text }) {
872
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("flex flex-col items-center justify-center gap-3", className), children: [
873
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
874
- "div",
875
- {
876
- className: cn(
877
- "animate-spin rounded-full border-2 border-theme-bg-tertiary border-t-theme-accent-primary",
878
- sizeClasses[size]
879
- )
880
- }
881
- ),
882
- text && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-theme-text-secondary", children: text })
883
- ] });
884
- }
885
-
886
- // src/components/molecules/EmptyState.tsx
887
- var import_jsx_runtime7 = require("react/jsx-runtime");
888
- function EmptyState({ icon, title, description, action, className }) {
889
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("flex flex-col items-center justify-center py-12 text-center", className), children: [
890
- icon && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mb-4 text-theme-text-muted", children: icon }),
891
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-lg font-semibold text-theme-text-primary", children: title }),
892
- description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mt-2 text-sm text-theme-text-secondary max-w-md", children: description }),
893
- action && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-4", children: action })
894
- ] });
895
- }
896
-
897
- // src/components/pages/CourseDetailPage.tsx
898
- var import_jsx_runtime8 = require("react/jsx-runtime");
899
- function formatCoursePrice(price, isFree, currency = "USD") {
900
- if (isFree || price === 0) return "Free";
901
- try {
902
- return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(price);
903
- } catch (e) {
904
- return `${currency} ${price.toFixed(2)}`;
905
- }
906
- }
907
- function formatDuration(seconds) {
908
- const mins = Math.floor(seconds / 60);
909
- if (mins >= 60) {
910
- const hours = Math.floor(mins / 60);
911
- const remainMins = mins % 60;
912
- return `${hours}h ${remainMins}m`;
913
- }
914
- const secs = seconds % 60;
915
- return `${mins}:${secs.toString().padStart(2, "0")}`;
916
- }
917
- function CourseDetailPage(props) {
918
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
919
- const noop = (..._args) => {
920
- };
921
- const {
922
- course,
923
- isLoading = false,
924
- notFound = false,
925
- isEnrolled = false,
926
- enrollmentId = null,
927
- onEnroll = noop,
928
- onStartLearning = noop,
929
- isProcessingEnrollment = false,
930
- paymentProviders = [],
931
- onCheckout = noop,
932
- isProcessingCheckout = false,
933
- couponCode = "",
934
- onCouponCodeChange = noop,
935
- onApplyCoupon = noop,
936
- appliedCoupon = null,
937
- couponError = null,
938
- isApplyingCoupon = false,
939
- discountedPrice,
940
- activeTab = "overview",
941
- onTabChange = noop,
942
- relatedCourses = [],
943
- isRelatedLoading = false,
944
- onRelatedCourseClick = noop,
945
- ratings,
946
- ratingSummary
947
- } = props;
948
- const [expandedSections, setExpandedSections] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
949
- const [selectedLesson, setSelectedLesson] = (0, import_react4.useState)(null);
950
- const [showCouponInput, setShowCouponInput] = (0, import_react4.useState)(false);
951
- const [imgError, setImgError] = (0, import_react4.useState)(false);
952
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LoadingSpinner, { className: "py-20", text: "Loading course..." });
953
- if (notFound || !course) {
954
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
955
- EmptyState,
956
- {
957
- icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.BookOpen, { className: "w-16 h-16" }),
958
- title: "Course not found",
959
- description: "The course you're looking for doesn't exist."
960
- }
961
- );
962
- }
963
- const pricingType = ((_a = course.settings) == null ? void 0 : _a.pricingType) || course.pricingType || "FREE";
964
- const price = (_d = (_c = (_b = course.settings) == null ? void 0 : _b.price) != null ? _c : course.price) != null ? _d : 0;
965
- const currency = (_g = (_f = (_e = course.settings) == null ? void 0 : _e.currency) != null ? _f : course.currency) != null ? _g : "USD";
966
- const isFree = pricingType === "FREE" || price === 0;
967
- const bannerImage = ((_h = course.settings) == null ? void 0 : _h.banner) || course.bannerImage;
968
- const thumbnail = course.thumbnail || "";
969
- const totalLessons = (course.sections || []).reduce(
970
- (sum, s) => {
971
- var _a2;
972
- return sum + (((_a2 = s.activities) == null ? void 0 : _a2.length) || 0);
973
- },
974
- 0
975
- );
976
- const sections = course.sections || [];
977
- const hasBanner = bannerImage && bannerImage.trim();
978
- const toggleSection = (sectionId) => {
979
- const next = new Set(expandedSections);
980
- if (next.has(sectionId)) next.delete(sectionId);
981
- else next.add(sectionId);
982
- setExpandedSections(next);
983
- };
984
- const getButtonText = () => {
985
- if (isEnrolled) return "Continue Learning";
986
- if (isFree) return "Enroll for Free";
987
- if (pricingType === "SUBSCRIPTION") return "Subscribe to Access";
988
- if (pricingType === "INSTALLMENT") return "Make a Payment";
989
- return "Buy Course";
990
- };
991
- const handleEnrollClick = () => {
992
- if (isEnrolled && enrollmentId) {
993
- onStartLearning(enrollmentId);
994
- return;
995
- }
996
- if (isFree) {
997
- onEnroll();
998
- return;
999
- }
1000
- if (paymentProviders.length === 1) {
1001
- const p = paymentProviders[0];
1002
- onCheckout(p.paymentProvider || p.id);
1003
- return;
1004
- }
1005
- onEnroll();
1006
- };
1007
- const renderEnrollmentCard = () => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "bg-white p-4 rounded-xl shadow-lg border border-gray-100", children: [
1008
- thumbnail && !imgError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1009
- "img",
1010
- {
1011
- src: thumbnail,
1012
- alt: course.title,
1013
- className: "w-full h-48 object-cover rounded-lg mb-4",
1014
- onError: () => setImgError(true)
1015
- }
1016
- ),
1017
- isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center justify-center gap-2 text-green-600", children: [
1018
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.Check, { className: "h-5 w-5" }),
1019
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "font-semibold text-sm", children: "You're enrolled" })
1020
- ] }),
1021
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-center mb-4", children: appliedCoupon && discountedPrice !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-1", children: [
1022
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-2xl font-bold text-gray-900", children: formatCoursePrice(discountedPrice, false, currency) }),
1023
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-base line-through text-gray-400", children: formatCoursePrice(price, isFree, currency) }),
1024
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-sm font-semibold text-gray-900", children: appliedCoupon.discountType === "PERCENTAGE" ? `${appliedCoupon.discountValue}% Off` : formatCoursePrice(appliedCoupon.discountValue, false, currency) })
1025
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-2xl font-bold text-gray-900", children: [
1026
- formatCoursePrice(price, isFree, currency),
1027
- pricingType === "SUBSCRIPTION" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-lg font-normal text-gray-500", children: " / month" })
1028
- ] }) }),
1029
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1030
- "button",
1031
- {
1032
- onClick: handleEnrollClick,
1033
- disabled: isProcessingEnrollment || isProcessingCheckout,
1034
- className: "w-full bg-theme-accent-primary hover:bg-[#3da5a7] text-white font-semibold py-3 px-6 rounded-md transition-colors mb-4 cursor-pointer disabled:opacity-50",
1035
- children: isProcessingEnrollment || isProcessingCheckout ? "Processing..." : getButtonText()
1036
- }
1037
- ),
1038
- !isEnrolled && !isFree && paymentProviders.length > 1 && paymentProviders.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1039
- "button",
1040
- {
1041
- onClick: () => onCheckout(provider.paymentProvider || provider.id),
1042
- disabled: isProcessingCheckout,
1043
- className: "w-full bg-theme-accent-primary hover:bg-[#3da5a7] text-white font-semibold py-3 px-6 rounded-md transition-colors mb-2 cursor-pointer disabled:opacity-50",
1044
- children: [
1045
- "Pay with ",
1046
- provider.paymentProvider
1047
- ]
1048
- },
1049
- provider.paymentProvider || provider.id
1050
- )),
1051
- !isFree && !isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-6", children: appliedCoupon ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-3 bg-theme-accent-primary/10 border border-[#49BBBD]/20 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
1052
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
1053
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.Tag, { className: "h-4 w-4 text-[#49BBBD]" }),
1054
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-sm font-medium text-[#49BBBD]", children: [
1055
- 'Coupon "',
1056
- appliedCoupon.code,
1057
- '" applied'
1058
- ] })
1059
- ] }),
1060
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1061
- "button",
1062
- {
1063
- onClick: () => {
1064
- onCouponCodeChange("");
1065
- },
1066
- className: "text-[#49BBBD] hover:text-[#3da5a7] transition-colors cursor-pointer",
1067
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.X, { className: "h-4 w-4" })
1068
- }
1069
- )
1070
- ] }) }) : showCouponInput ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
1071
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-2", children: [
1072
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1073
- "input",
1074
- {
1075
- type: "text",
1076
- placeholder: "Enter coupon code",
1077
- value: couponCode,
1078
- onChange: (e) => onCouponCodeChange(e.target.value.toUpperCase()),
1079
- className: `flex-1 text-sm border rounded-md px-3 py-2 focus:outline-none focus:border-[#49BBBD] ${couponError ? "border-red-500" : "border-gray-300"}`
1080
- }
1081
- ),
1082
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1083
- "button",
1084
- {
1085
- onClick: onApplyCoupon,
1086
- disabled: !couponCode || isApplyingCoupon,
1087
- className: "px-3 py-2 text-sm bg-theme-accent-primary text-white rounded-md hover:bg-[#3da5a7] disabled:opacity-50 cursor-pointer",
1088
- children: isApplyingCoupon ? "Applying..." : "Apply"
1089
- }
1090
- )
1091
- ] }),
1092
- couponError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-xs text-red-600", children: couponError })
1093
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1094
- "a",
1095
- {
1096
- href: "#",
1097
- onClick: (e) => {
1098
- e.preventDefault();
1099
- setShowCouponInput(true);
1100
- },
1101
- className: "block text-center text-sm font-medium text-[#49BBBD] hover:text-[#3da5a7] hover:underline cursor-pointer",
1102
- children: "Apply Coupon"
1103
- }
1104
- ) }),
1105
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "border-t border-gray-200 pt-4", children: [
1106
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "font-semibold text-gray-900 mb-3 text-sm", children: "This course included" }),
1107
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2.5", children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1109
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.FileText, { className: "h-2.5 w-2.5 text-white" }) }),
1110
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-sm", children: [
1111
- totalLessons,
1112
- " lessons"
1113
- ] })
1114
- ] }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.Clock, { className: "h-2.5 w-2.5 text-white" }) }),
1117
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm", children: "Self-paced learning" })
1118
- ] }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1120
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.Monitor, { className: "h-2.5 w-2.5 text-white" }) }),
1121
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm", children: "Access on mobile and desktop" })
1122
- ] })
1123
- ] })
1124
- ] })
1125
- ] });
1126
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "-mx-4 sm:-mx-4 md:-mx-4 lg:-mx-6 xl:-mx-6", children: [
1127
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1128
- "div",
1129
- {
1130
- className: "relative text-white px-4 lg:px-8 py-8 lg:py-40 mb-8",
1131
- style: hasBanner ? { backgroundImage: `url(${bannerImage})`, backgroundSize: "cover", backgroundPosition: "center" } : { backgroundColor: "#1a2332" },
1132
- children: [
1133
- hasBanner && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-0 bg-gradient-to-r from-black/60 via-black/50 to-black/40" }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative z-10", children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "max-w-7xl lg:pr-110", children: [
1136
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "text-3xl lg:text-4xl font-bold mb-2", children: course.title }),
1137
- course.brief && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-base lg:text-lg text-gray-300 mb-4 leading-relaxed", children: course.brief }),
1138
- course.creator && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 mt-4 lg:mt-6", children: [
1139
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-10 h-10 rounded-full bg-theme-accent-primary flex items-center justify-center text-white font-semibold", children: (course.creator.name || "C").charAt(0).toUpperCase() }),
1140
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm", children: course.creator.name })
1141
- ] })
1142
- ] }),
1143
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "lg:hidden mt-6", children: renderEnrollmentCard() })
1144
- ] }),
1145
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hidden lg:block absolute right-8 top-15 w-80 z-20", children: renderEnrollmentCard() })
1146
- ]
1147
- }
1148
- ),
1149
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 lg:px-6 mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex gap-3", children: ["overview", "curriculum"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1150
- "button",
1151
- {
1152
- onClick: () => onTabChange(tab),
1153
- className: `px-6 py-2.5 rounded-full text-sm font-medium transition-all cursor-pointer ${activeTab === tab ? "bg-theme-accent-primary text-white shadow-md" : "bg-white text-gray-600 border border-gray-200 hover:border-[#49BBBD] hover:text-[#49BBBD]"}`,
1154
- children: tab.charAt(0).toUpperCase() + tab.slice(1)
1155
- },
1156
- tab
1157
- )) }) }),
1158
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "px-4 lg:px-6", children: [
1159
- activeTab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "lg:pr-110", children: [
1160
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8 pb-8 border-b border-gray-300", children: [
1161
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "About This Course" }),
1162
- course.summary && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1163
- "div",
1164
- {
1165
- className: "text-gray-700 text-sm leading-relaxed mb-6",
1166
- dangerouslySetInnerHTML: { __html: course.summary }
1167
- }
1168
- ),
1169
- course.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1170
- "div",
1171
- {
1172
- className: "text-gray-700 text-sm leading-relaxed",
1173
- dangerouslySetInnerHTML: { __html: course.description }
1174
- }
1175
- )
1176
- ] }),
1177
- course.whatYouWillLearn && course.whatYouWillLearn.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8 pb-8 border-b border-gray-300", children: [
1178
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "What You'll Learn" }),
1179
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid md:grid-cols-2 gap-3", children: course.whatYouWillLearn.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-start gap-3", children: [
1180
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.CheckCircle2, { className: "h-5 w-5 text-green-600 flex-shrink-0" }),
1181
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm text-gray-700", children: item })
1182
- ] }, i)) })
1183
- ] }),
1184
- course.requirements && course.requirements.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8 pb-8 border-b border-gray-300", children: [
1185
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "Requirements" }),
1186
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-2", children: course.requirements.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex items-start gap-2", children: [
1187
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-gray-400 text-sm", children: "\u2022" }),
1188
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm text-gray-700", children: item })
1189
- ] }, i)) })
1190
- ] }),
1191
- course.targetAudience && course.targetAudience.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8 pb-8 border-b border-gray-300", children: [
1192
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "Who This Course Is For" }),
1193
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "space-y-2", children: course.targetAudience.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "flex items-start gap-2", children: [
1194
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-gray-400 text-sm", children: "\u2022" }),
1195
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm text-gray-700", children: item })
1196
- ] }, i)) })
1197
- ] }),
1198
- ratingSummary && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8", children: [
1199
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-4 mb-6", children: [
1200
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-5xl font-bold text-yellow-500", children: (_i = ratingSummary.averageRating) == null ? void 0 : _i.toFixed(1) }),
1201
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1202
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-1 mb-1", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1203
- "svg",
1204
- {
1205
- className: `w-4 h-4 ${star <= Math.round(ratingSummary.averageRating || 0) ? "text-yellow-400 fill-yellow-400" : "text-gray-300 fill-gray-300"}`,
1206
- viewBox: "0 0 24 24",
1207
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" })
1208
- },
1209
- star
1210
- )) }),
1211
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "text-sm text-gray-600", children: [
1212
- "Course Rating \u2022 ",
1213
- ratingSummary.totalRatings,
1214
- " ratings"
1215
- ] })
1216
- ] })
1217
- ] }),
1218
- ratings && ratings.slice(0, 2).map((review) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 p-4 border border-gray-200 rounded-lg", children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 mb-2", children: [
1220
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-9 h-9 rounded-full bg-theme-accent-primary flex items-center justify-center text-white font-semibold text-sm", children: (review.reviewerName || "U").charAt(0) }),
1221
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1222
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "font-medium text-sm", children: review.reviewerName }),
1223
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-1", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1224
- "svg",
1225
- {
1226
- className: `w-3 h-3 ${star <= (review.rating || 0) ? "text-yellow-400 fill-yellow-400" : "text-gray-300 fill-gray-300"}`,
1227
- viewBox: "0 0 24 24",
1228
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" })
1229
- },
1230
- star
1231
- )) })
1232
- ] })
1233
- ] }),
1234
- review.review && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-gray-700", children: review.review })
1235
- ] }, review.id))
1236
- ] })
1237
- ] }),
1238
- activeTab === "curriculum" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-8", children: [
1239
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "Curriculum" }),
1240
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-1", children: sections.map((section) => {
1241
- var _a2, _b2;
1242
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "border border-gray-200 rounded overflow-hidden", children: [
1243
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1244
- "button",
1245
- {
1246
- onClick: () => toggleSection(section.id),
1247
- className: "w-full flex items-center justify-between p-3 hover:bg-gray-50 transition-colors text-left cursor-pointer",
1248
- children: [
1249
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
1250
- expandedSections.has(section.id) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.ChevronDown, { className: "h-4 w-4 text-gray-600" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.ChevronRight, { className: "h-4 w-4 text-gray-600" }),
1251
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1252
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm font-medium text-gray-900", children: section.title }),
1253
- section.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-xs text-gray-500 mt-0.5", children: section.description })
1254
- ] })
1255
- ] }),
1256
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-xs text-gray-500", children: [
1257
- ((_a2 = section.activities) == null ? void 0 : _a2.length) || 0,
1258
- " lessons"
1259
- ] })
1260
- ]
1261
- }
1262
- ),
1263
- expandedSections.has(section.id) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "bg-gray-50 border-t border-gray-200", children: (_b2 = section.activities) == null ? void 0 : _b2.map((activity) => {
1264
- const isSelected = selectedLesson === activity.id;
1265
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1266
- "button",
1267
- {
1268
- onClick: () => setSelectedLesson(activity.id),
1269
- className: `w-full flex items-start justify-between gap-3 px-10 py-3 border-b border-gray-100 last:border-b-0 transition-colors text-left cursor-pointer ${isSelected ? "bg-blue-50 border-l-4 border-l-blue-600" : "hover:bg-gray-100"}`,
1270
- children: [
1271
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-start gap-3 flex-1", children: [
1272
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1273
- import_lucide_react6.PlayCircle,
1274
- {
1275
- className: `h-4 w-4 flex-shrink-0 mt-0.5 ${isSelected ? "text-blue-600" : "text-gray-400"}`
1276
- }
1277
- ),
1278
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
1279
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `text-xs font-medium ${isSelected ? "text-blue-600" : "text-gray-700"}`, children: activity.title }),
1280
- activity.freePreview && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs px-2 py-0.5 bg-blue-100 text-blue-700 rounded", children: "Preview" })
1281
- ] }) })
1282
- ] }),
1283
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3 flex-shrink-0", children: [
1284
- activity.downloadable && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react6.Download, { className: "h-3 w-3 text-gray-400" }),
1285
- activity.duration && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs text-gray-500", children: typeof activity.duration === "number" ? formatDuration(activity.duration) : activity.duration })
1286
- ] })
1287
- ]
1288
- },
1289
- activity.id
1290
- );
1291
- }) })
1292
- ] }, section.id);
1293
- }) })
1294
- ] })
1295
- ] }),
1296
- activeTab === "overview" && (relatedCourses.length > 0 || isRelatedLoading) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-8 bg-[#EEF4FA] border-t border-gray-200 pb-8", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-4 lg:px-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "py-10", children: [
1297
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between mb-6", children: [
1298
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-bold text-gray-900", children: "Related Courses" }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { className: "text-sm font-medium text-[#49BBBD] hover:underline cursor-pointer", children: "See all" })
1300
- ] }),
1301
- isRelatedLoading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", children: [...Array(4)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "rounded-2xl bg-gray-100 animate-pulse h-64" }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", children: relatedCourses.map((c) => {
1302
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2;
1303
- const cIsFree = ((_a2 = c.settings) == null ? void 0 : _a2.pricingType) === "FREE" || !((_b2 = c.settings) == null ? void 0 : _b2.price) && !c.price;
1304
- const cPrice = (_e2 = (_d2 = (_c2 = c.settings) == null ? void 0 : _c2.price) != null ? _d2 : c.price) != null ? _e2 : 0;
1305
- const cCurrency = (_h2 = (_g2 = (_f2 = c.settings) == null ? void 0 : _f2.currency) != null ? _g2 : c.currency) != null ? _h2 : "USD";
1306
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1307
- "div",
1308
- {
1309
- onClick: () => onRelatedCourseClick(c.id),
1310
- className: "bg-white rounded-2xl overflow-hidden shadow-sm border border-gray-100 hover:shadow-md transition-shadow cursor-pointer",
1311
- children: [
1312
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-40 bg-gray-100 overflow-hidden", children: c.thumbnail && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1313
- "img",
1314
- {
1315
- src: c.thumbnail,
1316
- alt: c.title,
1317
- className: "w-full h-full object-cover"
1318
- }
1319
- ) }),
1320
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-4", children: [
1321
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "font-semibold text-gray-900 text-sm mb-2 line-clamp-2", children: c.title }),
1322
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between text-xs text-gray-500", children: [
1323
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
1324
- (_i2 = c.totalLessons) != null ? _i2 : 0,
1325
- " lessons"
1326
- ] }),
1327
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: cIsFree ? "text-green-600 font-medium" : "font-semibold text-gray-900", children: cIsFree ? "Free" : formatCoursePrice(cPrice, false, cCurrency) })
1328
- ] })
1329
- ] })
1330
- ]
1331
- },
1332
- c.id
1333
- );
1334
- }) })
1335
- ] }) }) })
1336
- ] });
1337
- }
1338
-
1339
- // src/components/pages/BundleDetailPage.tsx
1340
- var import_react5 = require("react");
1341
- var import_lucide_react7 = require("lucide-react");
1342
- var import_jsx_runtime9 = require("react/jsx-runtime");
1343
- function formatCoursePrice2(price, isFree, currency = "USD") {
1344
- if (isFree || price === 0) return "Free";
1345
- try {
1346
- return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(price);
1347
- } catch (e) {
1348
- return `${currency} ${price.toFixed(2)}`;
1349
- }
1350
- }
1351
- function BundleDetailPage(props) {
1352
- var _a, _b, _c, _d;
1353
- const noop = (..._args) => {
1354
- };
1355
- const {
1356
- bundle,
1357
- isLoading = false,
1358
- notFound = false,
1359
- isEnrolled = false,
1360
- onEnroll = noop,
1361
- isProcessingEnrollment = false,
1362
- paymentProviders = [],
1363
- onCheckout = noop,
1364
- isProcessingCheckout = false,
1365
- couponCode = "",
1366
- onCouponCodeChange = noop,
1367
- onApplyCoupon = noop,
1368
- appliedCoupon = null,
1369
- couponError = null,
1370
- onCourseClick = noop
1371
- } = props;
1372
- const [expandedCourses, setExpandedCourses] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
1373
- const [showCouponInput, setShowCouponInput] = (0, import_react5.useState)(false);
1374
- const [imgErrors, setImgErrors] = (0, import_react5.useState)({});
1375
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LoadingSpinner, { className: "py-20", text: "Loading bundle..." });
1376
- if (notFound || !bundle) {
1377
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1378
- EmptyState,
1379
- {
1380
- icon: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Package, { className: "w-16 h-16" }),
1381
- title: "Bundle not found",
1382
- description: "The bundle you're looking for doesn't exist."
1383
- }
1384
- );
1385
- }
1386
- const b = bundle;
1387
- const pricingType = b.pricingType || "FREE";
1388
- const price = (_a = b.price) != null ? _a : 0;
1389
- const currency = (_b = b.currency) != null ? _b : "USD";
1390
- const isFree = pricingType === "FREE" || price === 0;
1391
- const totalCourses = ((_c = b.courses) == null ? void 0 : _c.length) || 0;
1392
- const creatorName = ((_d = b.creator) == null ? void 0 : _d.name) || "Academy Team";
1393
- const courses = b.courses || [];
1394
- const toggleCourse = (courseId) => {
1395
- const next = new Set(expandedCourses);
1396
- if (next.has(courseId)) next.delete(courseId);
1397
- else next.add(courseId);
1398
- setExpandedCourses(next);
1399
- };
1400
- const getButtonText = () => {
1401
- if (isEnrolled) return "You are enrolled";
1402
- if (isFree) return "Enroll for free";
1403
- if (pricingType === "SUBSCRIPTION") return "Subscribe to access";
1404
- if (pricingType === "INSTALLMENT") return "Make a payment";
1405
- return "Buy bundle";
1406
- };
1407
- const handleEnrollClick = () => {
1408
- if (isEnrolled) return;
1409
- if (isFree) {
1410
- onEnroll();
1411
- return;
1412
- }
1413
- if (paymentProviders.length === 1) {
1414
- const p = paymentProviders[0];
1415
- onCheckout(p.paymentProvider || p.id);
1416
- return;
1417
- }
1418
- onEnroll();
1419
- };
1420
- const thumbnails = courses.slice(0, 3).map((c) => {
1421
- var _a2;
1422
- return c.thumbnail || ((_a2 = c.course) == null ? void 0 : _a2.thumbnail);
1423
- }).filter(Boolean);
1424
- const renderEnrollmentCard = () => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bg-white p-4 rounded-xl shadow-lg border border-gray-100", children: [
1425
- thumbnails.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative w-full h-48 flex mb-4 rounded-lg overflow-hidden", children: [
1426
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "relative h-full w-1/2 bg-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1427
- "img",
1428
- {
1429
- src: thumbnails[0],
1430
- alt: "Bundle course 1",
1431
- className: "w-full h-full object-cover",
1432
- onError: () => setImgErrors((p) => __spreadProps(__spreadValues({}, p), { t0: true }))
1433
- }
1434
- ) }),
1435
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col w-1/2 h-full", children: [
1436
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `relative w-full overflow-hidden bg-gray-100 ${thumbnails.length > 1 ? "h-1/2" : "h-full"}`, children: thumbnails[1] && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1437
- "img",
1438
- {
1439
- src: thumbnails[1],
1440
- alt: "Bundle course 2",
1441
- className: "w-full h-full object-cover",
1442
- onError: () => setImgErrors((p) => __spreadProps(__spreadValues({}, p), { t1: true }))
1443
- }
1444
- ) }),
1445
- thumbnails.length > 2 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "relative w-full h-1/2 overflow-hidden bg-gray-100", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1446
- "img",
1447
- {
1448
- src: thumbnails[2],
1449
- alt: "Bundle course 3",
1450
- className: "w-full h-full object-cover",
1451
- onError: () => setImgErrors((p) => __spreadProps(__spreadValues({}, p), { t2: true }))
1452
- }
1453
- ) })
1454
- ] })
1455
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-full h-48 bg-gray-100 rounded-lg mb-4 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Package, { className: "w-16 h-16 text-gray-300" }) }),
1456
- isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mb-4 flex items-center gap-2 text-green-600", children: [
1457
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Check, { className: "h-5 w-5" }),
1458
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "font-semibold text-sm", children: "Enrolled in this bundle" })
1459
- ] }),
1460
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-center mb-4", children: [
1461
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "text-2xl font-bold text-gray-900", children: [
1462
- formatCoursePrice2(price, isFree, currency),
1463
- pricingType === "SUBSCRIPTION" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-lg font-normal text-gray-500", children: " / month" })
1464
- ] }),
1465
- !isFree && !isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-[#49BBBD] font-medium mt-1", children: "Bundle Deal" })
1466
- ] }),
1467
- !isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1468
- "button",
1469
- {
1470
- onClick: handleEnrollClick,
1471
- disabled: isProcessingEnrollment || isProcessingCheckout,
1472
- className: "w-full bg-theme-accent-primary hover:bg-[#3da5a7] text-white font-semibold py-3 px-6 rounded-md transition-colors mb-4 cursor-pointer disabled:opacity-50",
1473
- children: isProcessingEnrollment || isProcessingCheckout ? "Processing..." : getButtonText()
1474
- }
1475
- ),
1476
- !isEnrolled && !isFree && paymentProviders.length > 1 && paymentProviders.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1477
- "button",
1478
- {
1479
- onClick: () => onCheckout(provider.paymentProvider || provider.id),
1480
- disabled: isProcessingCheckout,
1481
- className: "w-full bg-theme-accent-primary hover:bg-[#3da5a7] text-white font-semibold py-3 px-6 rounded-md transition-colors mb-2 cursor-pointer disabled:opacity-50",
1482
- children: [
1483
- "Pay with ",
1484
- provider.paymentProvider
1485
- ]
1486
- },
1487
- provider.paymentProvider || provider.id
1488
- )),
1489
- !isFree && !isEnrolled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mb-6", children: appliedCoupon ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "p-3 bg-theme-accent-primary/10 border border-[#49BBBD]/20 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center justify-between", children: [
1490
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
1491
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Tag, { className: "h-4 w-4 text-[#49BBBD]" }),
1492
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-sm font-medium text-[#49BBBD]", children: [
1493
- 'Coupon "',
1494
- appliedCoupon.code,
1495
- '" applied'
1496
- ] })
1497
- ] }),
1498
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1499
- "button",
1500
- {
1501
- onClick: () => onCouponCodeChange(""),
1502
- className: "text-[#49BBBD] hover:text-[#3da5a7] transition-colors cursor-pointer",
1503
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.X, { className: "h-4 w-4" })
1504
- }
1505
- )
1506
- ] }) }) : showCouponInput ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2", children: [
1507
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-2", children: [
1508
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1509
- "input",
1510
- {
1511
- type: "text",
1512
- placeholder: "Enter coupon code",
1513
- value: couponCode,
1514
- onChange: (e) => onCouponCodeChange(e.target.value.toUpperCase()),
1515
- className: `flex-1 text-sm border rounded-md px-3 py-2 focus:outline-none focus:border-[#49BBBD] ${couponError ? "border-red-500" : "border-gray-300"}`
1516
- }
1517
- ),
1518
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1519
- "button",
1520
- {
1521
- onClick: onApplyCoupon,
1522
- disabled: !couponCode,
1523
- className: "px-3 py-2 text-sm bg-theme-accent-primary text-white rounded-md hover:bg-[#3da5a7] disabled:opacity-50 cursor-pointer",
1524
- children: "Apply"
1525
- }
1526
- )
1527
- ] }),
1528
- couponError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-red-600", children: couponError })
1529
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1530
- "a",
1531
- {
1532
- href: "#",
1533
- onClick: (e) => {
1534
- e.preventDefault();
1535
- setShowCouponInput(true);
1536
- },
1537
- className: "block text-center text-sm font-medium text-[#49BBBD] hover:text-[#3da5a7] hover:underline cursor-pointer",
1538
- children: "Apply Coupon"
1539
- }
1540
- ) }),
1541
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "border-t border-gray-200 pt-4", children: [
1542
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "font-semibold text-gray-900 mb-3 text-sm", children: "This bundle included" }),
1543
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "space-y-2.5", children: [
1544
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1545
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.FileText, { className: "h-2.5 w-2.5 text-white" }) }),
1546
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-sm", children: [
1547
- totalCourses,
1548
- " courses"
1549
- ] })
1550
- ] }),
1551
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1552
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Clock, { className: "h-2.5 w-2.5 text-white" }) }),
1553
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm", children: "Self-paced learning" })
1554
- ] }),
1555
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2.5 text-gray-700", children: [
1556
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-4 h-4 rounded-full bg-theme-accent-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Monitor, { className: "h-2.5 w-2.5 text-white" }) }),
1557
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm", children: "Access on mobile and desktop" })
1558
- ] })
1559
- ] })
1560
- ] })
1561
- ] });
1562
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "-mx-4 sm:-mx-4 md:-mx-4 lg:-mx-6 xl:-mx-6", children: [
1563
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative bg-[#1a2332] text-white px-4 lg:px-8 py-8 lg:py-12 mb-8", children: [
1564
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "max-w-7xl lg:pr-110", children: [
1565
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "text-3xl lg:text-4xl font-bold mb-2", children: b.title }),
1566
- b.description && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-base lg:text-lg text-gray-300 mb-4 leading-relaxed", children: b.description }),
1567
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-3 mt-4 lg:mt-6 mb-6 lg:mb-0", children: [
1568
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-10 h-10 rounded-full bg-theme-accent-primary flex items-center justify-center text-white font-semibold", children: creatorName.charAt(0).toUpperCase() }),
1569
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm", children: creatorName })
1570
- ] })
1571
- ] }),
1572
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "lg:hidden mt-6", children: renderEnrollmentCard() }),
1573
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "hidden lg:block absolute right-30 top-15 w-80 z-10", children: renderEnrollmentCard() })
1574
- ] }),
1575
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "px-4 lg:px-6", children: [
1576
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "lg:pr-110 pr-4 mb-8 pb-8 border-b border-gray-300", children: [
1577
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: "About This Bundle" }),
1578
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-gray-700 text-sm leading-relaxed", children: b.description || "This comprehensive bundle includes multiple courses designed to help you master your learning goals." })
1579
- ] }),
1580
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mb-8", children: [
1581
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h2", { className: "text-xl font-bold text-gray-900 mb-4", children: [
1582
- "Included Courses",
1583
- totalCourses > 0 ? ` (${totalCourses})` : ""
1584
- ] }),
1585
- courses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-center py-8 text-gray-500", children: "No courses included in this bundle yet." }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "space-y-1 lg:max-w-[70%] md:max-w-[100%]", children: courses.sort((a, b2) => {
1586
- var _a2, _b2;
1587
- return ((_a2 = a.orderIndex) != null ? _a2 : 0) - ((_b2 = b2.orderIndex) != null ? _b2 : 0);
1588
- }).map((bundleCourse, index) => {
1589
- const course = bundleCourse.course || bundleCourse;
1590
- const courseId = bundleCourse.courseId || bundleCourse.id || course.id;
1591
- const courseThumbnail = course.thumbnail || bundleCourse.thumbnail;
1592
- const isExpanded = expandedCourses.has(courseId);
1593
- const isCompleted = bundleCourse.isCompleted;
1594
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "border border-gray-200 rounded overflow-hidden", children: [
1595
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1596
- "button",
1597
- {
1598
- onClick: () => toggleCourse(courseId),
1599
- className: "w-full flex items-center justify-between p-3 hover:bg-gray-50 transition-colors text-left cursor-pointer",
1600
- children: [
1601
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-3", children: [
1602
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.ChevronDown, { className: "h-4 w-4 text-gray-600 flex-shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.ChevronRight, { className: "h-4 w-4 text-gray-600 flex-shrink-0" }),
1603
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-8 h-8 rounded-full bg-purple-100 text-purple-700 flex items-center justify-center font-bold text-sm flex-shrink-0", children: index + 1 }),
1604
- courseThumbnail && !imgErrors[`c-${courseId}`] && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1605
- "img",
1606
- {
1607
- src: courseThumbnail,
1608
- alt: course.title,
1609
- className: "w-12 h-8 object-cover rounded flex-shrink-0",
1610
- onError: () => setImgErrors((p) => __spreadProps(__spreadValues({}, p), { [`c-${courseId}`]: true }))
1611
- }
1612
- ),
1613
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex-1 min-w-0", children: [
1614
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm font-medium text-gray-900 truncate block", children: course.title || bundleCourse.title || "Course" }),
1615
- course.summary && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-xs text-gray-500 truncate", children: course.summary })
1616
- ] })
1617
- ] }),
1618
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex items-center gap-2 flex-shrink-0 ml-2", children: isCompleted && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.Check, { className: "h-4 w-4 text-green-600" }) })
1619
- ]
1620
- }
1621
- ),
1622
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
1623
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-4 text-xs text-gray-500 mb-3", children: [
1624
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1", children: [
1625
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react7.BookOpen, { className: "h-4 w-4" }),
1626
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
1627
- "Course ",
1628
- index + 1
1629
- ] })
1630
- ] }),
1631
- course.status && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-1", children: [
1632
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `w-2 h-2 rounded-full ${course.status === "PUBLISHED" ? "bg-green-500" : "bg-gray-400"}` }),
1633
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "capitalize", children: course.status.toLowerCase() })
1634
- ] })
1635
- ] }),
1636
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1637
- "button",
1638
- {
1639
- onClick: () => onCourseClick(courseId, bundleCourse.enrollmentId),
1640
- className: "text-sm font-medium text-[#49BBBD] hover:text-[#3da5a7] hover:underline cursor-pointer",
1641
- children: isEnrolled ? "Go to course \u2192" : "View course \u2192"
1642
- }
1643
- )
1644
- ] })
1645
- ] }, courseId);
1646
- }) })
1647
- ] })
1648
- ] })
1649
- ] });
1650
- }
1651
-
1652
- // src/components/pages/CoursePlayerPage.tsx
1653
- var import_react7 = require("react");
1654
- var import_lucide_react9 = require("lucide-react");
1655
-
1656
- // src/components/organisms/CourseSidebar.tsx
1657
- var import_react6 = require("react");
1658
- var import_lucide_react8 = require("lucide-react");
1659
-
1660
- // src/components/atoms/Button.tsx
1661
- var React = __toESM(require("react"));
1662
- var import_class_variance_authority = require("class-variance-authority");
1663
- var import_jsx_runtime10 = require("react/jsx-runtime");
1664
- var buttonVariants = (0, import_class_variance_authority.cva)(
1665
- "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",
1666
- {
1667
- variants: {
1668
- variant: {
1669
- default: "bg-theme-accent-primary text-white hover:opacity-90",
1670
- destructive: "bg-red-600 text-white hover:bg-red-700",
1671
- outline: "border border-theme-border-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary text-theme-text-primary",
1672
- secondary: "bg-theme-bg-tertiary text-theme-text-primary hover:opacity-80",
1673
- ghost: "hover:bg-theme-bg-tertiary text-theme-text-primary",
1674
- link: "text-[rgb(var(--accent-primary))] underline-offset-4 hover:underline"
1675
- },
1676
- size: {
1677
- default: "h-10 px-4 py-2",
1678
- sm: "h-9 rounded-md px-3",
1679
- lg: "h-11 rounded-md px-8",
1680
- icon: "h-10 w-10"
1681
- }
1682
- },
1683
- defaultVariants: {
1684
- variant: "default",
1685
- size: "default"
1686
- }
1687
- }
1688
- );
1689
- var Button = React.forwardRef(
1690
- (_a, ref) => {
1691
- var _b = _a, { className, variant, size } = _b, props = __objRest(_b, ["className", "variant", "size"]);
1692
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1693
- "button",
1694
- __spreadValues({
1695
- className: cn(buttonVariants({ variant, size, className })),
1696
- ref
1697
- }, props)
1698
- );
1699
- }
1700
- );
1701
- Button.displayName = "Button";
1702
-
1703
- // src/components/atoms/ProgressBar.tsx
1704
- var import_jsx_runtime11 = require("react/jsx-runtime");
1705
- var sizeClasses2 = {
1706
- sm: "h-1.5",
1707
- md: "h-2.5",
1708
- lg: "h-3.5"
1709
- };
1710
- var variantClasses = {
1711
- default: "bg-theme-accent-primary",
1712
- success: "bg-green-600",
1713
- warning: "bg-yellow-500",
1714
- error: "bg-red-600"
1715
- };
1716
- function ProgressBar(_a) {
1717
- var _b = _a, {
1718
- value,
1719
- max = 100,
1720
- showLabel = false,
1721
- size = "md",
1722
- variant = "default",
1723
- className
1724
- } = _b, props = __objRest(_b, [
1725
- "value",
1726
- "max",
1727
- "showLabel",
1728
- "size",
1729
- "variant",
1730
- "className"
1731
- ]);
1732
- const percentage = Math.min(Math.max(value / max * 100, 0), 100);
1733
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", __spreadProps(__spreadValues({ className: cn("w-full", className) }, props), { children: [
1734
- showLabel && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-1 flex items-center justify-between text-sm", children: [
1735
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-theme-text-secondary", children: "Progress" }),
1736
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "font-medium text-theme-text-primary", children: [
1737
- Math.round(percentage),
1738
- "%"
1739
- ] })
1740
- ] }),
1741
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("w-full overflow-hidden rounded-full bg-theme-bg-tertiary", sizeClasses2[size]), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1742
- "div",
1743
- {
1744
- className: cn("h-full transition-all duration-300 ease-in-out", variantClasses[variant]),
1745
- style: { width: `${percentage}%` }
1746
- }
1747
- ) })
1748
- ] }));
1749
- }
1750
-
1751
- // src/components/organisms/CourseSidebar.tsx
1752
- var import_jsx_runtime12 = require("react/jsx-runtime");
1753
- var lessonColors = [
1754
- "bg-[#E2F0FF] border-l-4 border-[#3aa3a5]",
1755
- "bg-[#fcddb5] border-l-4 border-[#d67a05]",
1756
- "bg-[#FAD1CE] border-l-4 border-[#7ab3f0]"
1757
- ];
1758
- function CourseSidebar({
1759
- courseTitle,
1760
- modules,
1761
- currentLessonId,
1762
- expandedModules,
1763
- completedCount,
1764
- totalCount,
1765
- onToggleModule,
1766
- onSelectLesson,
1767
- hideProgress = false,
1768
- hideSearch = false,
1769
- certificateEnabled = false,
1770
- isCompleted = false,
1771
- onDownloadCertificate,
1772
- isDownloadingCertificate = false,
1773
- onBack,
1774
- showBackButton = true,
1775
- className
1776
- }) {
1777
- const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
1778
- const filteredModules = (0, import_react6.useMemo)(() => {
1779
- if (!searchQuery.trim()) return modules;
1780
- const query = searchQuery.toLowerCase();
1781
- return modules.map((mod) => __spreadProps(__spreadValues({}, mod), {
1782
- lessons: mod.lessons.filter((l) => l.title.toLowerCase().includes(query))
1783
- })).filter((mod) => mod.lessons.length > 0);
1784
- }, [modules, searchQuery]);
1785
- const totalResults = filteredModules.reduce((sum, m) => sum + m.lessons.length, 0);
1786
- const progressPct = totalCount > 0 ? completedCount / totalCount * 100 : 0;
1787
- const formatDuration2 = (duration) => {
1788
- if (!duration) return "";
1789
- const text = duration.trim();
1790
- if (!text) return "";
1791
- if (/[a-zA-Z]/.test(text)) return text;
1792
- const parts = text.split(":").map(Number);
1793
- if (parts.some(isNaN)) return text;
1794
- if (parts.length === 3) {
1795
- const [hrs, mins] = parts;
1796
- if (hrs > 0 && mins > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"} ${mins} min`;
1797
- if (hrs > 0) return `${hrs} ${hrs === 1 ? "hour" : "hours"}`;
1798
- return `${mins} min`;
1799
- }
1800
- if (parts.length === 2) {
1801
- const [mins, secs] = parts;
1802
- return `${mins > 0 ? mins : secs > 0 ? 1 : 0} min`;
1803
- }
1804
- return `${Math.max(1, Math.round(parts[0] / 60))} min`;
1805
- };
1806
- let globalLessonIndex = 0;
1807
- return /* @__PURE__ */ (0, import_jsx_runtime12.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: [
1808
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-6", children: [
1809
- showBackButton && onBack && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1810
- "button",
1811
- {
1812
- onClick: onBack,
1813
- 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",
1814
- "aria-label": "Go back",
1815
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ArrowLeft, { className: "w-5 h-5" })
1816
- }
1817
- ),
1818
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-3xl font-semibold text-[#1f2937] leading-tight", children: courseTitle ? `${courseTitle.charAt(0).toUpperCase()}${courseTitle.slice(1)}` : "" }),
1819
- !hideProgress && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mt-4", children: [
1820
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ProgressBar, { value: progressPct, size: "sm" }),
1821
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-gray-400 mt-1 text-right", children: [
1822
- completedCount,
1823
- " / ",
1824
- totalCount,
1825
- " lessons \xB7 ",
1826
- Math.round(progressPct),
1827
- "%"
1828
- ] })
1829
- ] }),
1830
- certificateEnabled && isCompleted && onDownloadCertificate && completedCount === totalCount && totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1831
- Button,
1832
- {
1833
- onClick: onDownloadCertificate,
1834
- disabled: isDownloadingCertificate,
1835
- className: "w-full mt-3 bg-green-600 hover:bg-green-700 disabled:opacity-50 text-white",
1836
- children: [
1837
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.Award, { className: "w-4 h-4 mr-2" }),
1838
- isDownloadingCertificate ? "Downloading..." : "Download Certificate"
1839
- ]
1840
- }
1841
- ),
1842
- !hideSearch && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mt-3 relative", children: [
1843
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }),
1844
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1845
- "input",
1846
- {
1847
- type: "text",
1848
- placeholder: "Search lessons...",
1849
- value: searchQuery,
1850
- onChange: (e) => setSearchQuery(e.target.value),
1851
- 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"
1852
- }
1853
- ),
1854
- searchQuery && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1855
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1856
- "button",
1857
- {
1858
- onClick: () => setSearchQuery(""),
1859
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 cursor-pointer",
1860
- "aria-label": "Clear search",
1861
- children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.X, { className: "w-4 h-4" })
1862
- }
1863
- ),
1864
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-xs text-gray-400 mt-1", children: [
1865
- totalResults,
1866
- " ",
1867
- totalResults === 1 ? "lesson" : "lessons",
1868
- " found"
1869
- ] })
1870
- ] })
1871
- ] })
1872
- ] }),
1873
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex-1 px-4 space-y-8 pb-10", children: filteredModules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-center py-8", children: [
1874
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.Search, { className: "w-10 h-10 text-gray-300 mx-auto mb-2" }),
1875
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-sm text-gray-400", children: [
1876
- "No lessons found for \u201C",
1877
- searchQuery,
1878
- "\u201D"
1879
- ] }),
1880
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: () => setSearchQuery(""), className: "mt-2 text-sm text-[#49BBBD] hover:underline font-medium cursor-pointer", children: "Clear search" })
1881
- ] }) : filteredModules.map((module2) => {
1882
- const isExpanded = expandedModules.has(module2.id) || !!searchQuery.trim();
1883
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
1884
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1885
- "button",
1886
- {
1887
- type: "button",
1888
- onClick: () => onToggleModule(module2.id),
1889
- "aria-expanded": isExpanded,
1890
- className: "w-full flex items-center justify-between text-left mb-3 px-2 cursor-pointer",
1891
- children: [
1892
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-sm font-bold text-gray-500 uppercase tracking-wider", children: module2.title }),
1893
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ChevronRight, { className: cn("w-4 h-4 transition-transform", isExpanded ? "rotate-90 text-[#49BBBD]" : "text-gray-400") })
1894
- ]
1895
- }
1896
- ),
1897
- isExpanded && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "space-y-3", children: module2.lessons.map((lesson) => {
1898
- const isCurrent = currentLessonId === lesson.id;
1899
- const isLocked = lesson.locked === true;
1900
- const colorClass = lessonColors[globalLessonIndex % lessonColors.length];
1901
- globalLessonIndex++;
1902
- let availabilityText = "";
1903
- if (isLocked) {
1904
- if (lesson.availableAt) {
1905
- const diffDays = Math.ceil((new Date(lesson.availableAt).getTime() - Date.now()) / 864e5);
1906
- availabilityText = diffDays > 0 ? `Available in ${diffDays}d` : `Available on ${new Date(lesson.availableAt).toLocaleDateString()}`;
1907
- } else if (lesson.hasPrerequisite) {
1908
- availabilityText = "Complete previous lesson";
1909
- }
1910
- }
1911
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1912
- "button",
1913
- {
1914
- title: lesson.title,
1915
- onClick: () => !isLocked && onSelectLesson(lesson.id),
1916
- disabled: isLocked,
1917
- className: cn(
1918
- "w-full text-left p-4 rounded-lg flex items-center justify-between transition-transform",
1919
- isLocked ? "opacity-50 cursor-not-allowed" : "hover:scale-[1.02] active:scale-[0.98] cursor-pointer",
1920
- isCurrent ? "bg-[#49BBBD] text-white shadow-md ring-2 ring-[#2a9ea0]/40" : colorClass
1921
- ),
1922
- children: [
1923
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-3 overflow-hidden", children: [
1924
- lesson.completed ? /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)(import_lucide_react8.Check, { className: "w-3 h-3 text-white" }) }) : isLocked ? /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)(import_lucide_react8.Lock, { className: "w-2.5 h-2.5 text-gray-500" }) }) : /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)("div", { className: cn("w-2.5 h-2.5 rounded-sm", isCurrent ? "bg-white/80" : "bg-[#1f2a44]") }) }),
1925
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "overflow-hidden", children: [
1926
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1927
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react8.ClipboardList, { className: cn("w-3.5 h-3.5 flex-shrink-0", isCurrent ? "text-white" : "text-gray-600") }) : null,
1928
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: cn("text-sm font-medium truncate", isCurrent ? "text-white" : "text-gray-800"), children: lesson.title })
1929
- ] }),
1930
- isLocked && availabilityText && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-gray-600 mt-0.5 truncate", children: availabilityText })
1931
- ] })
1932
- ] }),
1933
- lesson.type === "ASSESSMENT" ? /* @__PURE__ */ (0, import_jsx_runtime12.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_runtime12.jsx)("span", { className: cn("text-xs font-semibold flex-shrink-0 ml-2", isCurrent ? "text-white/90" : "text-gray-600"), children: formatDuration2(lesson.duration) }) : null
1934
- ]
1935
- },
1936
- lesson.id
1937
- );
1938
- }) })
1939
- ] }, module2.id);
1940
- }) })
1941
- ] });
1942
- }
1943
-
1944
- // src/components/pages/CoursePlayerPage.tsx
1945
- var import_jsx_runtime13 = require("react/jsx-runtime");
1946
- function formatHeaderDuration(duration) {
1947
- if (!duration) return "";
1948
- const text = duration.trim();
1949
- if (!text) return "";
1950
- if (/[a-zA-Z]/.test(text)) return text;
1951
- const parts = text.split(":").map(Number);
1952
- if (parts.some(isNaN)) return text;
1953
- if (parts.length === 3) {
1954
- const [h, m] = parts;
1955
- if (h > 0 && m > 0) return `${h} ${h === 1 ? "hour" : "hours"} ${m} min`;
1956
- if (h > 0) return `${h} ${h === 1 ? "hour" : "hours"}`;
1957
- return `${m} min`;
1958
- }
1959
- if (parts.length === 2) {
1960
- const [m, s2] = parts;
1961
- return `${m > 0 ? m : s2 > 0 ? 1 : 0} min`;
1962
- }
1963
- const s = parts[0];
1964
- return `${s > 0 ? Math.max(1, Math.round(s / 60)) : 0} min`;
1965
- }
1966
- function CoursePlayerPage(props) {
1967
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1968
- const {
1969
- course,
1970
- isLoading = false,
1971
- courseNotFound = false,
1972
- onBack,
1973
- onNavigateToLesson
1974
- } = props;
1975
- const allLessons = (0, import_react7.useMemo)(
1976
- () => {
1977
- var _a2;
1978
- return ((_a2 = course == null ? void 0 : course.modules) != null ? _a2 : []).flatMap((m) => m.lessons);
1979
- },
1980
- [course]
1981
- );
1982
- const [currentLessonIdx, setCurrentLessonIdx] = (0, import_react7.useState)(0);
1983
- const [expandedModules, setExpandedModules] = (0, import_react7.useState)(
1984
- () => {
1985
- var _a2, _b2;
1986
- return new Set(((_b2 = (_a2 = course == null ? void 0 : course.modules) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.id) ? [course.modules[0].id] : []);
1987
- }
1988
- );
1989
- const [activeTab, setActiveTab] = (0, import_react7.useState)("overview");
1990
- const [isMobileSidebarOpen, setIsMobileSidebarOpen] = (0, import_react7.useState)(false);
1991
- const currentLesson = (_a = allLessons[currentLessonIdx]) != null ? _a : null;
1992
- const completedCount = allLessons.filter((l) => l.completed).length;
1993
- const totalCount = allLessons.length;
1994
- const goToLesson = (lessonId) => {
1995
- const idx = allLessons.findIndex((l) => l.id === lessonId);
1996
- if (idx === -1) return;
1997
- const lesson = allLessons[idx];
1998
- if (lesson == null ? void 0 : lesson.locked) return;
1999
- setCurrentLessonIdx(idx);
2000
- setIsMobileSidebarOpen(false);
2001
- onNavigateToLesson == null ? void 0 : onNavigateToLesson(lessonId);
2002
- };
2003
- const goPrev = () => {
2004
- if (currentLessonIdx > 0) {
2005
- setCurrentLessonIdx((i) => i - 1);
2006
- onNavigateToLesson == null ? void 0 : onNavigateToLesson(allLessons[currentLessonIdx - 1].id);
2007
- }
2008
- };
2009
- const goNext = () => {
2010
- if (currentLessonIdx < allLessons.length - 1) {
2011
- setCurrentLessonIdx((i) => i + 1);
2012
- onNavigateToLesson == null ? void 0 : onNavigateToLesson(allLessons[currentLessonIdx + 1].id);
2013
- }
2014
- };
2015
- const toggleModule = (id) => {
2016
- setExpandedModules((prev) => {
2017
- const s = new Set(prev);
2018
- s.has(id) ? s.delete(id) : s.add(id);
2019
- return s;
2020
- });
2021
- };
2022
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(LoadingSpinner, { className: "py-20", text: "Loading course..." });
2023
- if (courseNotFound || !course) {
2024
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "text-center space-y-4", children: [
2025
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-6xl", children: "\u{1F4DA}" }),
2026
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h1", { className: "text-3xl font-bold text-theme-text-primary", children: "Course Not Found" }),
2027
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-theme-text-secondary max-w-md mx-auto", children: "The course you're looking for doesn't exist or has been removed." }),
2028
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2029
- "button",
2030
- {
2031
- onClick: onBack != null ? onBack : () => window.history.back(),
2032
- className: "mt-4 px-6 py-2.5 rounded-lg bg-theme-accent-primary text-white font-medium hover:opacity-90 transition-opacity cursor-pointer",
2033
- children: "Go Back"
2034
- }
2035
- )
2036
- ] }) });
2037
- }
2038
- const sidebarProps = {
2039
- courseTitle: course.title,
2040
- modules: course.modules,
2041
- currentLessonId: (_b = currentLesson == null ? void 0 : currentLesson.id) != null ? _b : "",
2042
- expandedModules,
2043
- completedCount,
2044
- totalCount,
2045
- onToggleModule: toggleModule,
2046
- onSelectLesson: goToLesson,
2047
- onBack: onBack != null ? onBack : () => window.history.back()
2048
- };
2049
- const tabs = [
2050
- { id: "overview", label: "Overview", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FileText, { className: "w-4 h-4" }) },
2051
- { id: "notes", label: "Notes", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.StickyNote, { className: "w-4 h-4" }) },
2052
- { id: "bookmarks", label: "Bookmarks", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Bookmark, { className: "w-4 h-4" }) },
2053
- { id: "transcript", label: "Transcript", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FileAudio, { className: "w-4 h-4" }) },
2054
- { id: "resources", label: "Resources", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FolderOpen, { className: "w-4 h-4" }) },
2055
- { id: "faq", label: "FAQ", icon: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.HelpCircle, { className: "w-4 h-4" }) }
2056
- ];
2057
- const videoUrl = (_c = currentLesson == null ? void 0 : currentLesson.videoUrl) != null ? _c : "";
2058
- const videoProvider = (_d = currentLesson == null ? void 0 : currentLesson.videoProvider) != null ? _d : "YOUTUBE";
2059
- const getEmbedSrc = () => {
2060
- if (!videoUrl) return null;
2061
- if (videoProvider === "YOUTUBE") return `https://www.youtube.com/embed/${videoUrl}?rel=0`;
2062
- if (videoProvider === "VIMEO") return `https://player.vimeo.com/video/${videoUrl}`;
2063
- if (videoProvider === "LOOM") return `https://www.loom.com/embed/${videoUrl}`;
2064
- return videoUrl;
2065
- };
2066
- const embedSrc = getEmbedSrc();
2067
- const lessonLocked = (_e = currentLesson == null ? void 0 : currentLesson.locked) != null ? _e : false;
2068
- const lessonType = currentLesson == null ? void 0 : currentLesson.type;
2069
- const duration = formatHeaderDuration(currentLesson == null ? void 0 : currentLesson.duration);
2070
- const summary = (_g = (_f = currentLesson == null ? void 0 : currentLesson.summary) != null ? _f : course.description) != null ? _g : "";
2071
- const learningObjectives = (_h = course.learningObjectives) != null ? _h : [];
2072
- const faqs = (_j = (_i = currentLesson == null ? void 0 : currentLesson.settings) == null ? void 0 : _i.faqs) != null ? _j : [];
2073
- const secondaryResources = (_k = currentLesson == null ? void 0 : currentLesson.secondaryResources) != null ? _k : [];
2074
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "min-h-screen bg-[#f7f9fc] flex lg:flex-row lg:-mx-6", children: [
2075
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "hidden lg:block", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CourseSidebar, __spreadValues({}, sidebarProps)) }),
2076
- isMobileSidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "lg:hidden fixed inset-0 z-50", children: [
2077
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2078
- "button",
2079
- {
2080
- onClick: () => setIsMobileSidebarOpen(false),
2081
- className: "absolute inset-0 bg-black/40 cursor-pointer",
2082
- "aria-label": "Close lessons drawer"
2083
- }
2084
- ),
2085
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative h-full w-[88vw] max-w-[360px] bg-white shadow-xl overflow-y-auto", children: [
2086
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2087
- "button",
2088
- {
2089
- onClick: () => setIsMobileSidebarOpen(false),
2090
- className: "absolute top-3 right-3 z-10 w-9 h-9 rounded-full bg-white/90 border border-gray-200 text-gray-700 flex items-center justify-center cursor-pointer",
2091
- "aria-label": "Close",
2092
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.X, { className: "w-4 h-4" })
2093
- }
2094
- ),
2095
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CourseSidebar, __spreadProps(__spreadValues({}, sidebarProps), { onSelectLesson: (id) => {
2096
- goToLesson(id);
2097
- setIsMobileSidebarOpen(false);
2098
- } }))
2099
- ] })
2100
- ] }),
2101
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col min-w-0 bg-[#dce8f5]", children: [
2102
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "lg:hidden sticky top-0 z-20 bg-white/95 backdrop-blur border-b border-gray-200 px-4 py-3 flex items-center justify-between", children: [
2103
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2104
- "button",
2105
- {
2106
- onClick: () => setIsMobileSidebarOpen(true),
2107
- className: "inline-flex items-center gap-2 px-3 py-2 rounded-md bg-theme-accent-primary text-white text-sm font-medium cursor-pointer",
2108
- children: [
2109
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.List, { className: "w-4 h-4" }),
2110
- "Lessons"
2111
- ]
2112
- }
2113
- ),
2114
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-xs text-gray-600 font-medium", children: [
2115
- "Lesson ",
2116
- currentLessonIdx + 1,
2117
- " / ",
2118
- allLessons.length
2119
- ] })
2120
- ] }),
2121
- lessonLocked ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center justify-center min-h-[70vh] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "max-w-lg w-full bg-white rounded-2xl border border-gray-200 p-12 text-center space-y-6", children: [
2122
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Lock, { className: "w-16 h-16 text-orange-500" }) }),
2123
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h1", { className: "text-2xl font-bold text-theme-text-primary", children: "Lesson Locked" }),
2124
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-theme-text-secondary", children: "Complete the previous lesson to unlock this content." }),
2125
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-lg font-semibold text-theme-text-primary", children: currentLesson == null ? void 0 : currentLesson.title })
2126
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2127
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "bg-theme-accent-primary px-4 sm:px-6 lg:px-8 py-4 sm:py-6 text-white", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-start", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-w-0", children: [
2128
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "text-2xl lg:text-3xl font-bold", children: (currentLesson == null ? void 0 : currentLesson.title) ? `${currentLesson.title.charAt(0).toUpperCase()}${currentLesson.title.slice(1)}` : "Select a lesson" }),
2129
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-1 flex items-center gap-3", children: [
2130
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-white/90 text-sm lg:text-base opacity-90 line-clamp-1 flex-1 min-w-0", children: summary }),
2131
- duration && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "inline-flex items-center gap-1.5 whitespace-nowrap text-white/90 text-sm lg:text-base shrink-0", children: [
2132
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Clock3, { className: "w-4 h-4" }),
2133
- duration
2134
- ] })
2135
- ] })
2136
- ] }) }) }),
2137
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "p-4 lg:p-8 space-y-6 lg:space-y-8 max-w-5xl w-full", children: [
2138
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "bg-white rounded-2xl shadow-sm", children: [
2139
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative group", children: [
2140
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "overflow-hidden rounded-t-2xl", children: embedSrc ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "relative w-full", style: { paddingBottom: "56.25%" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2141
- "iframe",
2142
- {
2143
- src: embedSrc,
2144
- className: "absolute inset-0 w-full h-full",
2145
- allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
2146
- allowFullScreen: true,
2147
- title: (_l = currentLesson == null ? void 0 : currentLesson.title) != null ? _l : "Lesson video"
2148
- }
2149
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "relative w-full bg-gray-900 flex items-center justify-center", style: { paddingBottom: "56.25%" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 text-white/60", children: [
2150
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.PlayCircle, { className: "w-16 h-16 opacity-40" }),
2151
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm", children: "No video available for this lesson" })
2152
- ] }) }) }),
2153
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2154
- "button",
2155
- {
2156
- onClick: goPrev,
2157
- disabled: currentLessonIdx === 0,
2158
- className: "hidden md:flex absolute -left-5 top-1/2 -translate-y-1/2 z-10 w-10 h-10 items-center justify-center rounded-full bg-[#F48C06] text-white opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed",
2159
- title: "Previous lesson",
2160
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronLeft, { className: "w-5 h-5" })
2161
- }
2162
- ),
2163
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2164
- "button",
2165
- {
2166
- onClick: goNext,
2167
- disabled: currentLessonIdx >= allLessons.length - 1,
2168
- className: "hidden md:flex absolute -right-5 top-1/2 -translate-y-1/2 z-10 w-10 h-10 items-center justify-center rounded-full bg-[#F48C06] text-white opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed",
2169
- title: "Next lesson",
2170
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronRight, { className: "w-5 h-5" })
2171
- }
2172
- )
2173
- ] }),
2174
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-4 sm:px-6 py-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 border-t border-gray-100", children: [
2175
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
2176
- (currentLesson == null ? void 0 : currentLesson.completed) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "inline-flex items-center gap-1 text-xs text-green-600 font-medium", children: [
2177
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.CheckCircle2, { className: "w-4 h-4" }),
2178
- " Completed"
2179
- ] }),
2180
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-sm text-gray-500", children: [
2181
- "Lesson ",
2182
- currentLessonIdx + 1,
2183
- " of ",
2184
- allLessons.length
2185
- ] })
2186
- ] }),
2187
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
2188
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2189
- "button",
2190
- {
2191
- onClick: goPrev,
2192
- disabled: currentLessonIdx === 0,
2193
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-gray-200 text-sm text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer",
2194
- children: [
2195
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronLeft, { className: "w-4 h-4" }),
2196
- " Prev"
2197
- ]
2198
- }
2199
- ),
2200
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2201
- "button",
2202
- {
2203
- onClick: goNext,
2204
- disabled: currentLessonIdx >= allLessons.length - 1,
2205
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-theme-accent-primary text-white text-sm hover:opacity-90 transition-opacity disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer",
2206
- children: [
2207
- "Next ",
2208
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronRight, { className: "w-4 h-4" })
2209
- ]
2210
- }
2211
- )
2212
- ] })
2213
- ] })
2214
- ] }),
2215
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "bg-white border border-gray-200 rounded-lg overflow-hidden", children: [
2216
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex overflow-x-auto border-b border-gray-200 scrollbar-hide", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2217
- "button",
2218
- {
2219
- onClick: () => setActiveTab(tab.id),
2220
- className: `flex items-center gap-2 px-4 py-3 text-sm font-medium whitespace-nowrap border-b-2 transition-colors cursor-pointer ${activeTab === tab.id ? "border-theme-accent-primary text-theme-accent-primary" : "border-transparent text-gray-500 hover:text-gray-700"}`,
2221
- children: [
2222
- tab.icon,
2223
- tab.label
2224
- ]
2225
- },
2226
- tab.id
2227
- )) }),
2228
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-6 py-6", style: { minHeight: 480 }, children: [
2229
- activeTab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "space-y-6", children: [
2230
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2231
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { className: "text-base font-semibold text-gray-900 mb-2", children: "About This Lesson" }),
2232
- summary ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-gray-700 leading-relaxed", children: summary }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-gray-400 italic", children: "No description available." })
2233
- ] }),
2234
- learningObjectives.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2235
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { className: "text-base font-semibold text-gray-900 mb-3", children: "What You'll Learn" }),
2236
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("ul", { className: "space-y-2", children: learningObjectives.map((obj, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("li", { className: "flex items-start gap-2.5 text-sm text-gray-700", children: [
2237
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.CheckCircle2, { className: "w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" }),
2238
- obj
2239
- ] }, i)) })
2240
- ] })
2241
- ] }),
2242
- activeTab === "notes" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center gap-3", children: [
2243
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.StickyNote, { className: "w-12 h-12 text-gray-300" }),
2244
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-gray-500", children: "Your notes will appear here" }),
2245
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-gray-400", children: "Take notes while watching to revisit key points later." })
2246
- ] }),
2247
- activeTab === "bookmarks" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center gap-3", children: [
2248
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.Bookmark, { className: "w-12 h-12 text-gray-300" }),
2249
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-gray-500", children: "No bookmarks yet" }),
2250
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-gray-400", children: "Bookmark key moments in the video to revisit them." })
2251
- ] }),
2252
- activeTab === "transcript" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center gap-3", children: [
2253
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FileAudio, { className: "w-12 h-12 text-gray-300" }),
2254
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-gray-500", children: "Transcript not available" }),
2255
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-gray-400", children: "The transcript for this lesson has not been generated yet." })
2256
- ] }),
2257
- activeTab === "resources" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { children: secondaryResources.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "space-y-3", children: secondaryResources.map((res, i) => {
2258
- var _a2, _b2, _c2, _d2, _e2;
2259
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2260
- "a",
2261
- {
2262
- href: (_c2 = (_b2 = res.url) != null ? _b2 : (_a2 = res.metadataJson) == null ? void 0 : _a2.url) != null ? _c2 : "#",
2263
- target: "_blank",
2264
- rel: "noopener noreferrer",
2265
- className: "flex items-center gap-3 p-3 rounded-lg border border-gray-200 hover:border-theme-accent-primary hover:bg-theme-accent-primary/5 transition-colors",
2266
- children: [
2267
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FolderOpen, { className: "w-5 h-5 text-theme-accent-primary flex-shrink-0" }),
2268
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm text-gray-700", children: (_e2 = (_d2 = res.title) != null ? _d2 : res.name) != null ? _e2 : `Resource ${i + 1}` })
2269
- ]
2270
- },
2271
- i
2272
- );
2273
- }) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center gap-3", children: [
2274
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.FolderOpen, { className: "w-12 h-12 text-gray-300" }),
2275
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-gray-500", children: "No resources available" }),
2276
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-gray-400", children: "Downloadable files and links will appear here." })
2277
- ] }) }),
2278
- activeTab === "faq" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { children: faqs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FAQAccordion, { faqs }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center gap-3", children: [
2279
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.HelpCircle, { className: "w-12 h-12 text-gray-300" }),
2280
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-gray-500", children: "No FAQs for this lesson" })
2281
- ] }) })
2282
- ] })
2283
- ] })
2284
- ] })
2285
- ] })
2286
- ] })
2287
- ] });
2288
- }
2289
- function FAQAccordion({ faqs }) {
2290
- const [openIdx, setOpenIdx] = (0, import_react7.useState)(null);
2291
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "space-y-2", children: faqs.map((faq, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "border border-gray-200 rounded-lg overflow-hidden", children: [
2292
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2293
- "button",
2294
- {
2295
- onClick: () => setOpenIdx(openIdx === i ? null : i),
2296
- className: "w-full flex items-center justify-between px-4 py-3 text-left text-sm font-medium text-gray-800 hover:bg-gray-50 transition-colors cursor-pointer",
2297
- children: [
2298
- faq.question,
2299
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react9.ChevronLeft, { className: `w-4 h-4 text-gray-400 transition-transform duration-200 ${openIdx === i ? "-rotate-90" : "rotate-180"}` })
2300
- ]
2301
- }
2302
- ),
2303
- openIdx === i && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-4 pb-4 text-sm text-gray-600 border-t border-gray-100 pt-3", children: faq.answer })
2304
- ] }, i)) });
2305
- }
2306
-
2307
- // src/components/pages/MessagesPage.tsx
2308
- var import_react8 = require("react");
2309
- var import_lucide_react10 = require("lucide-react");
2310
- var import_jsx_runtime14 = require("react/jsx-runtime");
2311
- function formatTime(timestamp) {
2312
- try {
2313
- const date = new Date(timestamp);
2314
- const now = /* @__PURE__ */ new Date();
2315
- const diffDays = Math.floor((now.getTime() - date.getTime()) / (1e3 * 60 * 60 * 24));
2316
- if (diffDays === 0) return date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
2317
- if (diffDays === 1) return "Yesterday";
2318
- if (diffDays < 7) return date.toLocaleDateString("en-US", { weekday: "short" });
2319
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
2320
- } catch (e) {
2321
- return "";
2322
- }
2323
- }
2324
- function timeAgo(timestamp) {
2325
- try {
2326
- const diff = Date.now() - new Date(timestamp).getTime();
2327
- const mins = Math.floor(diff / 6e4);
2328
- if (mins < 1) return "just now";
2329
- if (mins < 60) return `${mins}m`;
2330
- const hrs = Math.floor(mins / 60);
2331
- if (hrs < 24) return `${hrs}h`;
2332
- return `${Math.floor(hrs / 24)}d`;
2333
- } catch (e) {
2334
- return "";
2335
- }
2336
- }
2337
- function MessagesPage(props) {
2338
- var _a, _b;
2339
- const noop = (..._args) => {
2340
- };
2341
- const {
2342
- conversations = [],
2343
- isLoadingConversations = false,
2344
- selectedConversation,
2345
- onSelectConversation = noop,
2346
- messages = [],
2347
- isLoadingMessages = false,
2348
- isLoadingMore = false,
2349
- hasMoreMessages = false,
2350
- onLoadMoreMessages = noop,
2351
- onSendMessage = noop,
2352
- isSending = false,
2353
- searchQuery = "",
2354
- onSearchChange = noop,
2355
- showNewMessageDialog = false,
2356
- onNewMessageDialogChange = noop,
2357
- mobileView = "list",
2358
- onMobileViewChange = noop,
2359
- currentUserId = "student-current"
2360
- } = props;
2361
- const [messageText, setMessageText] = (0, import_react8.useState)("");
2362
- const messagesEndRef = (0, import_react8.useRef)(null);
2363
- (0, import_react8.useEffect)(() => {
2364
- var _a2;
2365
- (_a2 = messagesEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
2366
- }, [messages]);
2367
- const handleSend = async () => {
2368
- if (!messageText.trim()) return;
2369
- const text = messageText.trim();
2370
- setMessageText("");
2371
- await onSendMessage(text);
2372
- };
2373
- const selectedName = ((_a = selectedConversation == null ? void 0 : selectedConversation.participant) == null ? void 0 : _a.name) || (selectedConversation == null ? void 0 : selectedConversation.name) || "Conversation";
2374
- const selectedCourse = ((_b = selectedConversation == null ? void 0 : selectedConversation.courseContext) == null ? void 0 : _b.courseName) || "";
2375
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex h-[calc(100vh-4rem)] -mx-4 lg:-mx-6 lg:-my-4 pt-3 overflow-hidden bg-theme-bg-secondary", children: [
2376
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn(
2377
- "w-full lg:w-80 shrink-0 border-r border-theme-border-primary flex flex-col bg-theme-bg-secondary",
2378
- mobileView === "chat" ? "hidden lg:flex" : "flex"
2379
- ), children: [
2380
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "px-4 pt-5 pb-3", children: [
2381
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
2382
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h1", { className: "text-xl font-bold text-theme-text-primary", children: "Messages" }),
2383
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2384
- "button",
2385
- {
2386
- onClick: () => onNewMessageDialogChange(true),
2387
- title: "New message",
2388
- className: "inline-flex items-center justify-center w-8 h-8 rounded-full bg-[rgb(var(--accent-primary))] text-white hover:opacity-90 transition-opacity shadow-sm cursor-pointer",
2389
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Plus, { className: "h-4 w-4" })
2390
- }
2391
- )
2392
- ] }),
2393
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative", children: [
2394
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-theme-text-secondary" }),
2395
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2396
- "input",
2397
- {
2398
- type: "text",
2399
- value: searchQuery,
2400
- onChange: (e) => onSearchChange(e.target.value),
2401
- placeholder: "Search messages...",
2402
- className: "w-full rounded-lg border border-theme-border-primary bg-theme-bg-primary pl-10 pr-4 py-2.5 text-sm text-theme-text-primary placeholder:text-theme-text-secondary focus:outline-none focus:ring-2 focus:ring-theme-accent-primary/30 focus:border-theme-accent-primary transition-colors"
2403
- }
2404
- )
2405
- ] })
2406
- ] }),
2407
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-1 overflow-y-auto", children: isLoadingConversations ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-theme-border-primary border-t-theme-accent-primary" }) }) : conversations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 px-4 text-center", children: [
2408
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-16 h-16 rounded-full bg-theme-bg-tertiary flex items-center justify-center mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.MessageSquare, { className: "w-8 h-8 text-theme-text-secondary" }) }),
2409
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "No conversations found" })
2410
- ] }) : conversations.map((conv) => {
2411
- var _a2, _b2, _c, _d, _e;
2412
- const convId = conv.id || conv.conversationId;
2413
- const isSelected = (selectedConversation == null ? void 0 : selectedConversation.id) === convId;
2414
- const name = ((_a2 = conv.participant) == null ? void 0 : _a2.name) || conv.name || ((_b2 = conv.courseContext) == null ? void 0 : _b2.courseName) || "Conversation";
2415
- const initials = name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2);
2416
- const lastMsg = ((_c = conv.lastMessage) == null ? void 0 : _c.content) || "";
2417
- const lastTime = (_d = conv.lastMessage) == null ? void 0 : _d.timestamp;
2418
- const courseName = (_e = conv.courseContext) == null ? void 0 : _e.courseName;
2419
- const unread = conv.unreadCount || 0;
2420
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2421
- "button",
2422
- {
2423
- onClick: () => {
2424
- onSelectConversation(conv);
2425
- onMobileViewChange("chat");
2426
- },
2427
- className: cn(
2428
- "w-full flex items-start gap-3 px-4 py-3.5 text-left transition-colors border-l-[3px] border-transparent hover:bg-theme-bg-tertiary/50 cursor-pointer",
2429
- isSelected && "bg-theme-accent-primary/5 border-l-[rgb(var(--accent-primary))]"
2430
- ),
2431
- children: [
2432
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative shrink-0", children: [
2433
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-10 h-10 rounded-full bg-theme-accent-primary/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-semibold text-[rgb(var(--accent-primary))]", children: initials }) }),
2434
- unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "absolute -top-1 -right-1 w-5 h-5 rounded-full bg-[rgb(var(--accent-primary))] text-white text-[10px] font-bold flex items-center justify-center", children: unread > 9 ? "9+" : unread })
2435
- ] }),
2436
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex-1 min-w-0", children: [
2437
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
2438
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-semibold text-theme-text-primary truncate", children: name }),
2439
- lastTime && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-xs text-theme-text-secondary whitespace-nowrap", children: timeAgo(lastTime) })
2440
- ] }),
2441
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-xs text-theme-text-secondary truncate mt-0.5", children: lastMsg }),
2442
- courseName && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-xs text-[rgb(var(--accent-primary))] mt-1 truncate", children: courseName })
2443
- ] })
2444
- ]
2445
- },
2446
- convId
2447
- );
2448
- }) })
2449
- ] }),
2450
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn(
2451
- "flex-1 min-w-0 flex flex-col bg-theme-bg-primary",
2452
- mobileView === "list" ? "hidden lg:flex" : "flex"
2453
- ), children: !selectedConversation ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex-1 flex flex-col items-center justify-center gap-3 text-center px-4", children: [
2454
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-20 h-20 rounded-full bg-theme-bg-tertiary flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.MessageSquare, { className: "w-10 h-10 text-theme-text-secondary" }) }),
2455
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h2", { className: "text-lg font-semibold text-theme-text-primary", children: "Select a conversation" }),
2456
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-theme-text-secondary max-w-xs", children: "Choose a conversation from the list or start a new one" }),
2457
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2458
- "button",
2459
- {
2460
- onClick: () => onNewMessageDialogChange(true),
2461
- className: "mt-2 inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[rgb(var(--accent-primary))] text-white text-sm font-medium hover:opacity-90 transition-opacity cursor-pointer",
2462
- children: [
2463
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Plus, { className: "w-4 h-4" }),
2464
- "New Message"
2465
- ]
2466
- }
2467
- )
2468
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2469
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "px-4 py-3 border-b border-theme-border-primary flex items-center gap-3 bg-theme-bg-secondary", children: [
2470
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2471
- "button",
2472
- {
2473
- onClick: () => onMobileViewChange("list"),
2474
- className: "lg:hidden text-theme-text-secondary hover:text-theme-text-primary cursor-pointer",
2475
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.ArrowLeft, { className: "w-5 h-5" })
2476
- }
2477
- ),
2478
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-9 h-9 rounded-full bg-theme-accent-primary/10 flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-semibold text-[rgb(var(--accent-primary))]", children: selectedName.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) }) }),
2479
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0", children: [
2480
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "font-semibold text-sm text-theme-text-primary truncate", children: selectedName }),
2481
- selectedCourse && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-xs text-[rgb(var(--accent-primary))] truncate", children: selectedCourse })
2482
- ] })
2483
- ] }),
2484
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-3", children: [
2485
- hasMoreMessages && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex justify-center pb-2", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2486
- "button",
2487
- {
2488
- onClick: onLoadMoreMessages,
2489
- disabled: isLoadingMore,
2490
- className: "inline-flex items-center gap-1 text-xs text-[rgb(var(--accent-primary))] hover:opacity-80 cursor-pointer disabled:opacity-50",
2491
- children: [
2492
- isLoadingMore ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Loader2, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.ChevronUp, { className: "w-3 h-3" }),
2493
- "Load older messages"
2494
- ]
2495
- }
2496
- ) }),
2497
- isLoadingMessages ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-theme-border-primary border-t-theme-accent-primary" }) }) : messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.MessageSquare, { className: "w-10 h-10 text-theme-text-secondary mb-3" }),
2499
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "No messages yet. Say hello!" })
2500
- ] }) : messages.map((msg) => {
2501
- const isOwn = msg.senderId === currentUserId || msg.senderRole === "STUDENT";
2502
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn("flex", isOwn ? "justify-end" : "justify-start"), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("flex items-end gap-2 max-w-[75%]", isOwn && "flex-row-reverse"), children: [
2503
- !isOwn && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-7 h-7 rounded-full bg-theme-accent-primary/10 flex items-center justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[10px] font-semibold text-[rgb(var(--accent-primary))]", children: selectedName.charAt(0).toUpperCase() }) }),
2504
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
2505
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: cn(
2506
- "rounded-2xl px-4 py-2.5 text-sm leading-relaxed",
2507
- isOwn ? "bg-[rgb(var(--accent-primary))] text-white rounded-br-md" : "bg-theme-bg-tertiary text-theme-text-primary rounded-bl-md"
2508
- ), children: msg.content }),
2509
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: cn("text-[10px] text-theme-text-muted mt-1", isOwn ? "text-right" : "text-left"), children: msg.timestamp ? formatTime(msg.timestamp) : "" })
2510
- ] })
2511
- ] }) }, msg.id);
2512
- }),
2513
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: messagesEndRef })
2514
- ] }),
2515
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-4 py-3 border-t border-theme-border-primary bg-theme-bg-secondary", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2", children: [
2516
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2517
- "input",
2518
- {
2519
- type: "text",
2520
- value: messageText,
2521
- onChange: (e) => setMessageText(e.target.value),
2522
- onKeyDown: (e) => {
2523
- if (e.key === "Enter" && !e.shiftKey) {
2524
- e.preventDefault();
2525
- handleSend();
2526
- }
2527
- },
2528
- placeholder: "Type a message...",
2529
- className: "flex-1 h-10 px-4 text-sm rounded-full border border-theme-border-primary bg-theme-bg-primary text-theme-text-primary placeholder:text-theme-text-muted focus:outline-none focus:border-theme-accent-primary transition-colors"
2530
- }
2531
- ),
2532
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2533
- "button",
2534
- {
2535
- onClick: handleSend,
2536
- disabled: isSending || !messageText.trim(),
2537
- className: "flex items-center justify-center w-10 h-10 rounded-full bg-[rgb(var(--accent-primary))] text-white hover:opacity-90 disabled:opacity-50 transition-opacity shrink-0 cursor-pointer",
2538
- children: isSending ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Loader2, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react10.Send, { className: "w-4 h-4" })
2539
- }
2540
- )
2541
- ] }) })
2542
- ] }) })
2543
- ] });
2544
- }
2545
-
2546
- // src/components/pages/ManualReviewPage.tsx
2547
- var import_lucide_react11 = require("lucide-react");
2548
-
2549
- // src/components/atoms/Badge.tsx
2550
- var import_class_variance_authority2 = require("class-variance-authority");
2551
- var import_jsx_runtime15 = require("react/jsx-runtime");
2552
- var badgeVariants = (0, import_class_variance_authority2.cva)(
2553
- "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",
2554
- {
2555
- variants: {
2556
- variant: {
2557
- default: "bg-theme-accent-primary text-white",
2558
- secondary: "bg-theme-bg-tertiary text-theme-text-primary",
2559
- success: "border-transparent bg-green-600 text-white",
2560
- warning: "border-transparent bg-yellow-500 text-white",
2561
- destructive: "border-transparent bg-red-600 text-white",
2562
- outline: "text-theme-text-primary border-theme-border-primary"
2563
- }
2564
- },
2565
- defaultVariants: {
2566
- variant: "default"
2567
- }
2568
- }
2569
- );
2570
- function Badge(_a) {
2571
- var _b = _a, { className, variant } = _b, props = __objRest(_b, ["className", "variant"]);
2572
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", __spreadValues({ className: cn(badgeVariants({ variant }), className) }, props));
2573
- }
2574
-
2575
- // src/components/atoms/Input.tsx
2576
- var React2 = __toESM(require("react"));
2577
- var import_jsx_runtime16 = require("react/jsx-runtime");
2578
- var Input = React2.forwardRef(
2579
- (_a, ref) => {
2580
- var _b = _a, { className, type, label } = _b, props = __objRest(_b, ["className", "type", "label"]);
2581
- const inputElement = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2582
- "input",
2583
- __spreadValues({
2584
- type,
2585
- className: cn(
2586
- "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",
2587
- className
2588
- ),
2589
- ref
2590
- }, props)
2591
- );
2592
- if (label) {
2593
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "space-y-2", children: [
2594
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "text-sm font-medium text-theme-text-primary", children: [
2595
- label,
2596
- props.required && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-red-500 ml-1", children: "*" })
2597
- ] }),
2598
- inputElement
2599
- ] });
2600
- }
2601
- return inputElement;
2602
- }
2603
- );
2604
- Input.displayName = "Input";
2605
-
2606
- // src/components/pages/ManualReviewPage.tsx
2607
- var import_jsx_runtime17 = require("react/jsx-runtime");
2608
- var statusDisplay = {
2609
- IN_REVIEW: { label: "Pending Review", variant: "warning" },
2610
- REQUEST_CHANGE: { label: "Changes Requested", variant: "destructive" },
2611
- GRADED: { label: "Approved", variant: "success" }
2612
- };
2613
- function formatDate(dateString) {
2614
- return new Date(dateString).toLocaleDateString("en-US", {
2615
- month: "short",
2616
- day: "numeric",
2617
- year: "numeric"
2618
- });
2619
- }
2620
- function ManualReviewPage(props) {
2621
- var _a, _b, _c, _d;
2622
- const noop = (..._args) => {
2623
- };
2624
- const {
2625
- submissions = [],
2626
- summary,
2627
- pagination,
2628
- isLoading = false,
2629
- filterStatus = "ALL",
2630
- onFilterStatusChange = noop,
2631
- searchQuery = "",
2632
- onSearchChange = noop,
2633
- currentPage = 1,
2634
- onPageChange = noop,
2635
- onSubmissionClick = noop
2636
- } = props;
2637
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "space-y-6", children: [
2638
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2639
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("h1", { className: "text-3xl font-bold text-theme-text-primary", children: "Manual Review" }),
2640
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-theme-text-secondary mt-1", children: "Track your assignment submissions and review status" })
2641
- ] }) }),
2642
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "grid grid-cols-2 gap-6 md:grid-cols-4", children: [
2643
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2644
- "button",
2645
- {
2646
- onClick: () => {
2647
- onFilterStatusChange("IN_REVIEW");
2648
- onPageChange(1);
2649
- },
2650
- className: cn(
2651
- "relative rounded-lg border-2 p-4 text-left transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-md hover:z-20 cursor-pointer",
2652
- filterStatus === "IN_REVIEW" ? "border-yellow-500 bg-yellow-50 dark:bg-yellow-900/20 scale-105 shadow-md z-20" : "border-theme-border-primary hover:border-yellow-500"
2653
- ),
2654
- children: [
2655
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
2656
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2657
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-theme-text-muted", children: "Pending Review" }),
2658
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-2xl font-bold text-yellow-600", children: (_a = summary == null ? void 0 : summary.inReviewSubmissions) != null ? _a : 0 })
2659
- ] }),
2660
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.ClipboardList, { className: "h-6 w-6 text-yellow-600" })
2661
- ] }),
2662
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-1 text-xs text-theme-text-muted", children: "Awaiting review" })
2663
- ]
2664
- }
2665
- ),
2666
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2667
- "button",
2668
- {
2669
- onClick: () => {
2670
- onFilterStatusChange("REQUEST_CHANGE");
2671
- onPageChange(1);
2672
- },
2673
- className: cn(
2674
- "relative rounded-lg border-2 p-4 text-left transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-md hover:z-20 cursor-pointer",
2675
- filterStatus === "REQUEST_CHANGE" ? "border-red-500 bg-red-50 dark:bg-red-900/20 scale-105 shadow-md z-20" : "border-theme-border-primary hover:border-red-500"
2676
- ),
2677
- children: [
2678
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
2679
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-theme-text-muted", children: "Changes Requested" }),
2681
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-2xl font-bold text-red-600", children: (_b = summary == null ? void 0 : summary.requestChangeSubmissions) != null ? _b : 0 })
2682
- ] }),
2683
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.AlertCircle, { className: "h-6 w-6 text-red-600" })
2684
- ] }),
2685
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-1 text-xs text-theme-text-muted", children: "Needs revision" })
2686
- ]
2687
- }
2688
- ),
2689
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2690
- "button",
2691
- {
2692
- onClick: () => {
2693
- onFilterStatusChange("GRADED");
2694
- onPageChange(1);
2695
- },
2696
- className: cn(
2697
- "relative rounded-lg border-2 p-4 text-left transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-md hover:z-20 cursor-pointer",
2698
- filterStatus === "GRADED" ? "border-green-500 bg-green-50 dark:bg-green-900/20 scale-105 shadow-md z-20" : "border-theme-border-primary hover:border-green-500"
2699
- ),
2700
- children: [
2701
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
2702
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2703
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-theme-text-muted", children: "Approved" }),
2704
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-2xl font-bold text-green-600", children: (_c = summary == null ? void 0 : summary.gradedSubmissions) != null ? _c : 0 })
2705
- ] }),
2706
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.CheckCircle, { className: "h-6 w-6 text-green-600" })
2707
- ] }),
2708
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-1 text-xs text-theme-text-muted", children: "Completed" })
2709
- ]
2710
- }
2711
- ),
2712
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2713
- "button",
2714
- {
2715
- onClick: () => {
2716
- onFilterStatusChange("ALL");
2717
- onPageChange(1);
2718
- },
2719
- className: cn(
2720
- "relative rounded-lg border-2 p-4 text-left transition-all duration-300 ease-in-out hover:scale-105 hover:shadow-md hover:z-20 cursor-pointer",
2721
- filterStatus === "ALL" ? "border-blue-500 bg-blue-50 dark:bg-blue-900/20 scale-105 shadow-md z-20" : "border-theme-border-primary hover:border-blue-500"
2722
- ),
2723
- children: [
2724
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
2725
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
2726
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-sm font-medium text-theme-text-muted", children: "Total" }),
2727
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-2xl font-bold text-blue-600", children: (_d = summary == null ? void 0 : summary.totalSubmissions) != null ? _d : 0 })
2728
- ] }),
2729
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.FileText, { className: "h-6 w-6 text-blue-600" })
2730
- ] }),
2731
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "mt-1 text-xs text-theme-text-muted", children: "All submissions" })
2732
- ]
2733
- }
2734
- )
2735
- ] }),
2736
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "space-y-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col gap-3 sm:flex-row", children: [
2737
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative flex-1", children: [
2738
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-theme-text-tertiary z-10 pointer-events-none" }),
2739
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2740
- Input,
2741
- {
2742
- type: "text",
2743
- placeholder: "Search learner, course, or assignment...",
2744
- value: searchQuery,
2745
- onChange: (e) => onSearchChange(e.target.value),
2746
- className: "pl-10"
2747
- }
2748
- )
2749
- ] }),
2750
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "w-full sm:w-auto", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2751
- "select",
2752
- {
2753
- value: filterStatus,
2754
- onChange: (e) => {
2755
- onFilterStatusChange(e.target.value);
2756
- onPageChange(1);
2757
- },
2758
- className: "w-full h-10 px-3 rounded-md border border-theme-border-primary bg-theme-bg-primary text-theme-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-theme-accent-primary cursor-pointer",
2759
- children: [
2760
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "ALL", children: "All Statuses" }),
2761
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "IN_REVIEW", children: "Pending Review" }),
2762
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "REQUEST_CHANGE", children: "Changes Requested" }),
2763
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("option", { value: "GRADED", children: "Approved" })
2764
- ]
2765
- }
2766
- ) })
2767
- ] }) }),
2768
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LoadingSpinner, { className: "py-20", text: "Loading submissions..." }) : submissions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2769
- EmptyState,
2770
- {
2771
- icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.ClipboardList, { className: "w-16 h-16" }),
2772
- title: "No submissions found",
2773
- description: "You haven't submitted anything for manual review yet."
2774
- }
2775
- ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2776
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "bg-theme-bg-primary rounded-lg border border-theme-border-primary", children: [
2777
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hidden sm:grid grid-cols-12 gap-4 px-5 py-3 bg-theme-bg-secondary text-xs font-semibold text-theme-text-muted uppercase tracking-wider border-b border-theme-border-primary rounded-t-lg", children: [
2778
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "col-span-5", children: "Assignment" }),
2779
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "col-span-2", children: "Submitted" }),
2780
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "col-span-3", children: "Status" }),
2781
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "col-span-2 text-right", children: "Score" })
2782
- ] }),
2783
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "divide-y divide-theme-border-primary", children: submissions.map((sub) => {
2784
- var _a2, _b2, _c2, _d2, _e, _f;
2785
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2786
- "div",
2787
- {
2788
- onClick: () => onSubmissionClick(sub.id),
2789
- className: "grid grid-cols-1 sm:grid-cols-12 gap-2 sm:gap-4 px-5 py-4 hover:bg-theme-bg-secondary/50 transition-colors cursor-pointer group",
2790
- children: [
2791
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sm:col-span-5", children: [
2792
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "font-medium text-theme-text-primary group-hover:text-theme-accent-primary transition-colors truncate max-w-xs", children: ((_a2 = sub.assignment) == null ? void 0 : _a2.title) || sub.lessonTitle || sub.title || "Submission" }),
2793
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-xs text-theme-text-tertiary", children: ((_b2 = sub.assignment) == null ? void 0 : _b2.courseName) || sub.courseName || "" })
2794
- ] }),
2795
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sm:col-span-2 flex items-center gap-1 text-sm text-theme-text-secondary", children: [
2796
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react11.Calendar, { className: "h-4 w-4" }),
2797
- sub.submittedAt ? formatDate(sub.submittedAt) : "\u2014"
2798
- ] }),
2799
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sm:col-span-3 flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Badge, { variant: ((_c2 = statusDisplay[sub.status]) == null ? void 0 : _c2.variant) || "secondary", children: ((_d2 = statusDisplay[sub.status]) == null ? void 0 : _d2.label) || sub.status }) }),
2800
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sm:col-span-2 flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "font-medium text-theme-text-primary", children: [
2801
- (_e = sub.score) != null ? _e : 0,
2802
- "/",
2803
- (_f = sub.totalPoints) != null ? _f : 0
2804
- ] }) })
2805
- ]
2806
- },
2807
- sub.id
2808
- );
2809
- }) })
2810
- ] }),
2811
- pagination && pagination.totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2812
- Pagination,
2813
- {
2814
- currentPage,
2815
- totalPages: pagination.totalPages,
2816
- total: pagination.totalItems,
2817
- pageSize: pagination.itemsPerPage || 10,
2818
- hasNextPage: pagination.hasNextPage,
2819
- hasPreviousPage: pagination.hasPreviousPage,
2820
- onPageChange,
2821
- className: "mt-4"
2822
- }
2823
- )
2824
- ] })
2825
- ] });
2826
- }
2827
-
2828
- // src/components/pages/ManualReviewDetailPage.tsx
2829
- var import_lucide_react12 = require("lucide-react");
2830
-
2831
- // src/components/atoms/Card.tsx
2832
- var import_jsx_runtime18 = require("react/jsx-runtime");
2833
- function Card(_a) {
2834
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2835
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2836
- "div",
2837
- __spreadValues({
2838
- className: cn("rounded-lg border border-theme-border-primary bg-theme-bg-primary shadow-sm", className)
2839
- }, props)
2840
- );
2841
- }
2842
- function CardHeader(_a) {
2843
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2844
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", __spreadValues({ className: cn("flex flex-col space-y-1.5 p-6", className) }, props));
2845
- }
2846
- function CardTitle(_a) {
2847
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2848
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", __spreadValues({ className: cn("text-2xl font-semibold leading-none tracking-tight", className) }, props));
2849
- }
2850
- function CardContent(_a) {
2851
- var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
2852
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", __spreadValues({ className: cn("p-6 pt-0", className) }, props));
2853
- }
2854
-
2855
- // src/components/pages/ManualReviewDetailPage.tsx
2856
- var import_jsx_runtime19 = require("react/jsx-runtime");
2857
- function formatDate2(dateString) {
2858
- return new Date(dateString).toLocaleDateString("en-US", {
2859
- month: "long",
2860
- day: "numeric",
2861
- year: "numeric",
2862
- hour: "2-digit",
2863
- minute: "2-digit"
2864
- });
2865
- }
2866
- function formatQuestionType(type) {
2867
- switch (type) {
2868
- case "TRUE_FALSE":
2869
- return "True/False";
2870
- case "SINGLE_CHOICE":
2871
- return "Single Choice";
2872
- case "MULTIPLE_SELECT":
2873
- return "Multiple Select";
2874
- case "TEXT":
2875
- return "Short Answer";
2876
- case "ESSAY":
2877
- return "Essay";
2878
- default:
2879
- return type;
2880
- }
2881
- }
2882
- function getStatusBadge(status) {
2883
- switch (status) {
2884
- case "IN_REVIEW":
2885
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Badge, { variant: "warning", className: "flex items-center gap-1", children: [
2886
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.Clock, { className: "h-3 w-3" }),
2887
- "Pending Review"
2888
- ] });
2889
- case "REQUEST_CHANGE":
2890
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Badge, { variant: "destructive", className: "flex items-center gap-1", children: [
2891
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.AlertCircle, { className: "h-3 w-3" }),
2892
- "Changes Requested"
2893
- ] });
2894
- case "GRADED":
2895
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Badge, { variant: "success", className: "flex items-center gap-1", children: [
2896
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.CheckCircle, { className: "h-3 w-3" }),
2897
- "Approved"
2898
- ] });
2899
- default:
2900
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Badge, { variant: "secondary", children: status });
2901
- }
2902
- }
2903
- function ManualReviewDetailPage(props) {
2904
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
2905
- const noop = (..._args) => {
2906
- };
2907
- const { submission, isLoading = false, notFound = false, onBack = noop } = props;
2908
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LoadingSpinner, { className: "py-20", text: "Loading submission..." });
2909
- if (notFound || !submission) {
2910
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "container mx-auto p-6", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "text-center py-12", children: [
2911
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.AlertCircle, { className: "h-12 w-12 text-theme-text-tertiary mx-auto mb-4" }),
2912
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { className: "text-xl font-semibold text-theme-text-primary mb-2", children: "Submission Not Found" }),
2913
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-theme-text-secondary mb-4", children: "The submission you're looking for doesn't exist or has been removed." }),
2914
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { onClick: onBack, children: "Back to Manual Review" })
2915
- ] }) });
2916
- }
2917
- const detail = submission;
2918
- const status = ((_a = detail.context) == null ? void 0 : _a.status) || detail.status || "IN_REVIEW";
2919
- const assignmentTitle = ((_c = (_b = detail.context) == null ? void 0 : _b.assignment) == null ? void 0 : _c.title) || detail.lessonTitle || "Submission Detail";
2920
- const courseName = ((_e = (_d = detail.context) == null ? void 0 : _d.course) == null ? void 0 : _e.name) || detail.courseName || "";
2921
- const submittedAt = ((_f = detail.context) == null ? void 0 : _f.submittedAt) || detail.submittedAt || "";
2922
- const totalQuestions = ((_h = (_g = detail.context) == null ? void 0 : _g.assignment) == null ? void 0 : _h.totalQuestions) || 0;
2923
- const totalPoints = ((_j = (_i = detail.context) == null ? void 0 : _i.assignment) == null ? void 0 : _j.totalPoints) || 0;
2924
- const score = (_n = (_m = (_k = detail.latestReview) == null ? void 0 : _k.score) != null ? _m : (_l = detail.answers) == null ? void 0 : _l.reduce((sum, a) => sum + (a.pointsAwarded || 0), 0)) != null ? _n : 0;
2925
- const answers = detail.answers || [];
2926
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "container mx-auto space-y-6 max-w-6xl", children: [
2927
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-4", children: [
2928
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Button, { variant: "ghost", size: "icon", onClick: onBack, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.ArrowLeft, { className: "h-5 w-5" }) }),
2929
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1", children: [
2930
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h1", { className: "text-3xl font-bold text-theme-text-primary", children: assignmentTitle }),
2931
- courseName && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-theme-text-secondary mt-1", children: courseName })
2932
- ] }),
2933
- getStatusBadge(status)
2934
- ] }),
2935
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [
2936
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardContent, { className: "pt-6", children: [
2937
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.Calendar, { className: "h-5 w-5 text-purple-600 dark:text-purple-400 mb-3" }),
2938
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
2939
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "Submitted" }),
2940
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "font-medium text-theme-text-primary", children: submittedAt ? formatDate2(submittedAt) : "\u2014" })
2941
- ] })
2942
- ] }) }),
2943
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardContent, { className: "pt-6", children: [
2944
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.BookOpen, { className: "h-5 w-5 text-green-600 dark:text-green-400 mb-3" }),
2945
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
2946
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "Assignment Info" }),
2947
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "font-medium text-theme-text-primary", children: [
2948
- totalQuestions,
2949
- " Questions"
2950
- ] })
2951
- ] })
2952
- ] }) }),
2953
- status === "IN_REVIEW" ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardContent, { className: "pt-6", children: [
2954
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.Clock, { className: "h-5 w-5 text-yellow-600 dark:text-yellow-400 mb-3" }),
2955
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
2956
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "Status" }),
2957
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "font-medium text-theme-text-primary", children: "Waiting for Review" }),
2958
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs text-theme-text-tertiary mt-1", children: "Your submission is being reviewed by the instructor." })
2959
- ] })
2960
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardContent, { className: "pt-6", children: [
2961
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.Award, { className: "h-5 w-5 text-blue-600 dark:text-blue-400 mb-3" }),
2962
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
2963
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "Score" }),
2964
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-2xl font-bold text-theme-accent-primary", children: [
2965
- score,
2966
- "/",
2967
- totalPoints
2968
- ] }),
2969
- status === "REQUEST_CHANGE" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs text-theme-text-tertiary mt-1", children: "Score is provisional until review completes." })
2970
- ] })
2971
- ] }) })
2972
- ] }),
2973
- status === "REQUEST_CHANGE" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Card, { className: "border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/10", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CardContent, { className: "pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-start gap-4", children: [
2974
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.AlertCircle, { className: "h-6 w-6 text-red-600 dark:text-red-400 flex-shrink-0 mt-1" }),
2975
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1", children: [
2976
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "font-semibold text-theme-text-primary mb-2", children: "Changes Requested" }),
2977
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-theme-text-secondary", children: "Your instructor has requested changes to your submission. Please review the feedback below and resubmit your assignment through the course player." })
2978
- ] })
2979
- ] }) }) }),
2980
- answers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Card, { children: [
2981
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CardHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(CardTitle, { className: "flex items-center gap-2", children: [
2982
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react12.FileText, { className: "h-5 w-5" }),
2983
- "Submitted Answers (",
2984
- answers.length,
2985
- ")"
2986
- ] }) }),
2987
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(CardContent, { className: "space-y-6", children: answers.map((answer, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2988
- "div",
2989
- {
2990
- className: "pb-6 border-b border-theme-border-primary last:border-b-0 last:pb-0",
2991
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-start gap-3 mb-3", children: [
2992
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex-shrink-0 w-8 h-8 rounded-full bg-theme-accent-primary text-white flex items-center justify-center font-semibold text-sm", children: index + 1 }),
2993
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1", children: [
2994
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-start justify-between gap-4 mb-2", children: [
2995
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h4", { className: "font-medium text-theme-text-primary", children: answer.questionText }),
2996
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
2997
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Badge, { variant: "secondary", className: "text-xs", children: formatQuestionType(answer.questionType) }),
2998
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-sm text-theme-text-secondary", children: [
2999
- answer.questionPoints,
3000
- " pts"
3001
- ] })
3002
- ] })
3003
- ] }),
3004
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "mt-3 space-y-3", children: [
3005
- answer.selectedOptionText && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "p-4 bg-theme-bg-secondary rounded-lg border border-theme-border-primary", children: [
3006
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs font-medium text-theme-text-secondary mb-2", children: "Your Answer:" }),
3007
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-theme-text-primary font-medium", children: answer.selectedOptionText })
3008
- ] }),
3009
- answer.selectedOptionIds && answer.selectedOptionIds.length > 0 && !answer.selectedOptionText && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "p-4 bg-theme-bg-secondary rounded-lg border border-theme-border-primary", children: [
3010
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs font-medium text-theme-text-secondary mb-2", children: "Selected Options:" }),
3011
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "space-y-2", children: answer.selectedOptions && answer.selectedOptions.length > 0 ? answer.selectedOptions.map((option, idx) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
3012
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "h-2 w-2 rounded-full bg-theme-accent-primary" }),
3013
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-theme-text-primary", children: option.text })
3014
- ] }, idx)) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-theme-text-secondary text-sm", children: [
3015
- answer.selectedOptionIds.length,
3016
- " option",
3017
- answer.selectedOptionIds.length !== 1 ? "s" : "",
3018
- " selected"
3019
- ] }) })
3020
- ] }),
3021
- answer.answerText && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "p-4 bg-theme-bg-secondary rounded-lg border border-theme-border-primary", children: [
3022
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs font-medium text-theme-text-secondary mb-2", children: "Your Answer:" }),
3023
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-theme-text-primary whitespace-pre-wrap", children: answer.answerText })
3024
- ] })
3025
- ] })
3026
- ] })
3027
- ] })
3028
- },
3029
- answer.questionId || index
3030
- )) })
3031
- ] })
3032
- ] });
3033
- }
3034
-
3035
- // src/components/pages/LearnerSettingsPage.tsx
3036
- var import_react9 = require("react");
3037
- var import_lucide_react13 = require("lucide-react");
3038
- var import_jsx_runtime20 = require("react/jsx-runtime");
3039
- function LearnerSettingsPage(props) {
3040
- const [marketingEmails, setMarketingEmails] = (0, import_react9.useState)(true);
3041
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-8", children: [
3042
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
3043
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h1", { className: "text-3xl font-bold text-theme-text-primary", children: "Settings" }),
3044
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-2 text-lg text-theme-text-secondary", children: "Manage your account settings and preferences" })
3045
- ] }),
3046
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "space-y-4", children: [
3047
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
3048
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h2", { className: "text-2xl font-bold text-theme-text-primary", children: "Email Preferences" }),
3049
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1 text-theme-text-secondary", children: "Control what emails you receive from us" })
3050
- ] }),
3051
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "p-6 w-full max-w-2xl rounded-lg border border-theme-border-primary bg-theme-bg-secondary", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-start justify-between", children: [
3052
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-start gap-4", children: [
3053
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "rounded-lg bg-theme-bg-tertiary p-3", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react13.Mail, { className: "h-5 w-5 text-theme-accent-primary" }) }),
3054
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
3055
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "text-base font-semibold text-theme-text-primary", children: "Marketing Emails" }),
3056
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1 text-sm text-theme-text-secondary", children: "Receive promotional updates, new courses, and special offers about our academy" })
3057
- ] })
3058
- ] }),
3059
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3060
- "button",
3061
- {
3062
- onClick: () => setMarketingEmails(!marketingEmails),
3063
- className: `relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${marketingEmails ? "bg-theme-accent-primary" : "bg-gray-200"}`,
3064
- role: "switch",
3065
- "aria-checked": marketingEmails,
3066
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3067
- "span",
3068
- {
3069
- className: `pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200 ${marketingEmails ? "translate-x-5" : "translate-x-0"}`
3070
- }
3071
- )
3072
- }
3073
- )
3074
- ] }) })
3075
- ] })
3076
- ] });
3077
- }
3078
-
3079
- // src/components/pages/PaymentSuccessPage.tsx
3080
- var import_react10 = require("react");
3081
- var import_lucide_react14 = require("lucide-react");
3082
- var import_jsx_runtime21 = require("react/jsx-runtime");
3083
- function PaymentSuccessPage({ onContinueLearning = () => {
3084
- } }) {
3085
- const [countdown, setCountdown] = (0, import_react10.useState)(5);
3086
- const [isRedirecting, setIsRedirecting] = (0, import_react10.useState)(false);
3087
- (0, import_react10.useEffect)(() => {
3088
- const timer = setInterval(() => {
3089
- setCountdown((prev) => {
3090
- if (prev <= 1) {
3091
- clearInterval(timer);
3092
- setIsRedirecting(true);
3093
- onContinueLearning();
3094
- return 0;
3095
- }
3096
- return prev - 1;
3097
- });
3098
- }, 1e3);
3099
- return () => clearInterval(timer);
3100
- }, []);
3101
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 flex items-center justify-center px-4 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-lg w-full", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
3102
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mb-8 flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative", children: [
3103
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute inset-0 bg-green-400 rounded-full animate-ping opacity-20" }),
3104
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "relative bg-gradient-to-br from-green-400 to-green-500 rounded-full p-5 shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react14.CheckCircle, { className: "h-16 w-16 text-white", strokeWidth: 2.5 }) })
3105
- ] }) }),
3106
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h1", { className: "text-2xl font-bold text-gray-900 mb-3 text-center", children: "Payment Successful!" }),
3107
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-gray-600 text-sm mb-8 text-center leading-relaxed", children: "Thank you for your purchase. You're now enrolled in the course and can start learning immediately." }),
3108
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mb-8 p-4 bg-blue-50 rounded-xl border border-blue-100", children: isRedirecting ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-center gap-2 text-blue-600", children: [
3109
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react14.Loader2, { className: "h-5 w-5 animate-spin" }),
3110
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-sm font-medium", children: "Redirecting..." })
3111
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: "text-sm text-blue-600 text-center font-medium", children: [
3112
- "Redirecting to course page in ",
3113
- countdown,
3114
- " second",
3115
- countdown !== 1 ? "s" : "",
3116
- "..."
3117
- ] }) }),
3118
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3119
- "button",
3120
- {
3121
- onClick: onContinueLearning,
3122
- className: "inline-flex items-center gap-2 px-6 py-2 bg-green-500 text-white rounded-lg font-medium hover:bg-green-600 transition-colors cursor-pointer",
3123
- children: "Continue Learning"
3124
- }
3125
- ) })
3126
- ] }) }) });
3127
- }
3128
-
3129
- // src/components/pages/PaymentCancelPage.tsx
3130
- var import_react11 = require("react");
3131
- var import_lucide_react15 = require("lucide-react");
3132
- var import_jsx_runtime22 = require("react/jsx-runtime");
3133
- function PaymentCancelPage({ onRetry = () => {
3134
- }, onGoBack = () => {
3135
- } }) {
3136
- const [countdown, setCountdown] = (0, import_react11.useState)(10);
3137
- (0, import_react11.useEffect)(() => {
3138
- const timer = setInterval(() => {
3139
- setCountdown((prev) => {
3140
- if (prev <= 1) {
3141
- clearInterval(timer);
3142
- onGoBack();
3143
- return 0;
3144
- }
3145
- return prev - 1;
3146
- });
3147
- }, 1e3);
3148
- return () => clearInterval(timer);
3149
- }, []);
3150
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 flex items-center justify-center px-4 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-w-lg w-full", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
3151
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mb-8 flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "bg-gradient-to-br from-orange-400 to-orange-500 rounded-full p-5 shadow-lg", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react15.XCircle, { className: "h-16 w-16 text-white", strokeWidth: 2.5 }) }) }),
3152
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h1", { className: "text-2xl font-bold text-gray-900 mb-3 text-center", children: "Payment Cancelled" }),
3153
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-gray-600 text-sm mb-8 text-center leading-relaxed", children: "Your payment was cancelled. No charges have been made to your account." }),
3154
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mb-6 p-4 bg-blue-50 rounded-xl border border-blue-100", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-sm text-blue-600 text-center font-medium leading-relaxed", children: "You can complete your purchase anytime. The course will be waiting for you!" }) }),
3155
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "mb-8 p-4 bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl border border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "text-sm text-gray-600 text-center font-medium", children: [
3156
- "Redirecting in ",
3157
- countdown,
3158
- " second",
3159
- countdown !== 1 ? "s" : "",
3160
- "..."
3161
- ] }) }),
3162
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex gap-4 justify-center", children: [
3163
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3164
- "button",
3165
- {
3166
- onClick: onRetry,
3167
- className: "px-6 py-2 bg-theme-accent-primary text-white rounded-lg font-medium hover:opacity-90 transition-opacity cursor-pointer",
3168
- children: "Try Again"
3169
- }
3170
- ),
3171
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3172
- "button",
3173
- {
3174
- onClick: onGoBack,
3175
- className: "px-6 py-2 border border-theme-border-primary rounded-lg font-medium text-theme-text-primary hover:bg-theme-bg-secondary transition-colors cursor-pointer",
3176
- children: "Go Back"
3177
- }
3178
- )
3179
- ] })
3180
- ] }) }) });
3181
- }
3182
-
3183
- // src/components/pages/CreatorProfilePage.tsx
3184
- var import_lucide_react16 = require("lucide-react");
3185
- var import_lucide_react17 = require("lucide-react");
3186
- var import_jsx_runtime23 = require("react/jsx-runtime");
3187
- function CreatorProfilePage(props) {
3188
- var _a;
3189
- const noop = (..._args) => {
3190
- };
3191
- const { creator, courses = [], isLoading = false, onCourseClick = noop } = props;
3192
- if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(LoadingSpinner, { className: "py-20" });
3193
- if (!creator) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EmptyState, { icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react17.User, { className: "w-16 h-16" }), title: "Creator not found" });
3194
- const name = creator.name || "Course Creator";
3195
- const email = creator.email || "";
3196
- const bio = creator.bio || "An experienced educator and software developer with expertise in modern web development and software engineering. With years of industry experience, they create comprehensive courses that help students master cutting-edge technologies and build real-world applications.";
3197
- const expertise = creator.expertise || [];
3198
- const avatarInitial = name.charAt(0).toUpperCase();
3199
- const totalCourses = (_a = creator.totalCourses) != null ? _a : courses.length;
3200
- const totalLessons = creator.totalLessons;
3201
- const totalStudents = creator.totalStudents;
3202
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "min-h-screen", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
3203
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "mb-12", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col md:flex-row items-center gap-10", children: [
3204
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex-1 text-gray-900 text-center md:text-left space-y-4", children: [
3205
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "inline-flex items-center gap-2 bg-indigo-100 px-4 py-2 rounded-full text-sm font-medium text-indigo-700", children: [
3206
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react16.Award, { className: "h-4 w-4" }),
3207
- "Professional Creator"
3208
- ] }),
3209
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h1", { className: "text-5xl md:text-6xl font-bold tracking-tight", children: name }),
3210
- email && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2 text-gray-600 justify-center md:justify-start", children: [
3211
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react16.Mail, { className: "h-4 w-4" }),
3212
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-lg", children: email })
3213
- ] }),
3214
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-6 pt-4 justify-center md:justify-start", children: [
3215
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "text-center", children: [
3216
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-3xl font-bold text-gray-900", children: totalCourses }),
3217
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-sm text-gray-600", children: "Courses" })
3218
- ] }),
3219
- totalLessons != null && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
3220
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-px h-12 bg-gray-300" }),
3221
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "text-center", children: [
3222
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-3xl font-bold text-gray-900", children: totalLessons }),
3223
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-sm text-gray-600", children: "Lessons" })
3224
- ] })
3225
- ] }),
3226
- totalStudents != null && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
3227
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-px h-12 bg-gray-300" }),
3228
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "text-center", children: [
3229
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-3xl font-bold text-gray-900", children: totalStudents }),
3230
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "text-sm text-gray-600", children: "Students" })
3231
- ] })
3232
- ] })
3233
- ] })
3234
- ] }),
3235
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "relative", children: [
3236
- creator.profileImage ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3237
- "img",
3238
- {
3239
- src: creator.profileImage,
3240
- alt: name,
3241
- className: "w-48 h-48 rounded-full object-cover shadow-2xl"
3242
- }
3243
- ) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "w-48 h-48 rounded-full bg-gradient-to-br from-indigo-600 to-purple-600 flex items-center justify-center text-white text-7xl font-bold shadow-2xl", children: avatarInitial }),
3244
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "absolute -bottom-2 -right-2 bg-green-500 h-12 w-12 rounded-full border-4 border-white shadow-lg" })
3245
- ] }) })
3246
- ] }) }),
3247
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "mb-12", children: [
3248
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-3 mb-6", children: [
3249
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "h-1 w-12 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full" }),
3250
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-3xl font-bold text-gray-900", children: "About" })
3251
- ] }),
3252
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-gray-700 leading-relaxed text-lg", children: bio })
3253
- ] }),
3254
- expertise.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "mb-12", children: [
3255
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-3 mb-6", children: [
3256
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "h-1 w-12 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full" }),
3257
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-3xl font-bold text-gray-900", children: "Expertise" })
3258
- ] }),
3259
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex flex-wrap gap-3", children: expertise.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3260
- "span",
3261
- {
3262
- className: "px-4 py-2 bg-indigo-50 text-indigo-700 rounded-full text-sm font-medium",
3263
- children: skill
3264
- },
3265
- skill
3266
- )) })
3267
- ] }),
3268
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
3269
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between mb-8", children: [
3270
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-3", children: [
3271
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "h-1 w-12 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full" }),
3272
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { className: "text-3xl font-bold text-gray-900", children: "Featured Courses" })
3273
- ] }),
3274
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center gap-2 bg-indigo-50 px-4 py-2 rounded-full", children: [
3275
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react16.BookOpen, { className: "h-5 w-5 text-indigo-600" }),
3276
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "font-semibold text-indigo-600", children: [
3277
- courses.length,
3278
- " Courses"
3279
- ] })
3280
- ] })
3281
- ] }),
3282
- courses.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EmptyState, { title: "No courses yet" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: courses.map((course) => {
3283
- var _a2, _b;
3284
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3285
- CourseCard,
3286
- {
3287
- id: course.id,
3288
- title: course.title || "",
3289
- thumbnail: course.thumbnail,
3290
- totalLessons: course.totalLessons || 0,
3291
- progress: 0,
3292
- isFree: (_a2 = course.isFree) != null ? _a2 : false,
3293
- price: course.price,
3294
- showProgress: false,
3295
- showPrice: true,
3296
- description: course.description || ((_b = course.summary) == null ? void 0 : _b.replace(/<[^>]*>/g, "")) || "",
3297
- instructorName: creator.name,
3298
- category: course.category,
3299
- onClick: () => onCourseClick(course.id)
3300
- },
3301
- course.id
3302
- );
3303
- }) })
3304
- ] })
3305
- ] }) });
3306
- }