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