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