@academy-sdk/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,630 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __objRest = (source, exclude) => {
23
+ var target = {};
24
+ for (var prop in source)
25
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
26
+ target[prop] = source[prop];
27
+ if (source != null && __getOwnPropSymbols)
28
+ for (var prop of __getOwnPropSymbols(source)) {
29
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
30
+ target[prop] = source[prop];
31
+ }
32
+ return target;
33
+ };
34
+ var __export = (target, all) => {
35
+ for (var name in all)
36
+ __defProp(target, name, { get: all[name], enumerable: true });
37
+ };
38
+ var __copyProps = (to, from, except, desc) => {
39
+ if (from && typeof from === "object" || typeof from === "function") {
40
+ for (let key of __getOwnPropNames(from))
41
+ if (!__hasOwnProp.call(to, key) && key !== except)
42
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
43
+ }
44
+ return to;
45
+ };
46
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
47
+ // If the importer is in node compatibility mode or this is not an ESM
48
+ // file that has been converted to a CommonJS file using a Babel-
49
+ // compatible transform (i.e. "__esModule" has not been set), then set
50
+ // "default" to the CommonJS "module.exports" for node compatibility.
51
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
52
+ mod
53
+ ));
54
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
55
+
56
+ // src/index.ts
57
+ var src_exports = {};
58
+ __export(src_exports, {
59
+ DefaultLayout: () => DefaultLayout,
60
+ SDKProvider: () => SDKProvider,
61
+ calculateProgress: () => calculateProgress,
62
+ formatDate: () => formatDate,
63
+ formatDuration: () => formatDuration,
64
+ formatPrice: () => formatPrice,
65
+ formatRelativeTime: () => formatRelativeTime,
66
+ truncateText: () => truncateText,
67
+ useAiCoachAvailability: () => useAiCoachAvailability,
68
+ useAiCoachChat: () => useAiCoachChat,
69
+ useAiCoachHistory: () => useAiCoachHistory,
70
+ useCourseSearch: () => useCourseSearch,
71
+ useCreateBookmark: () => useCreateBookmark,
72
+ useCreateNote: () => useCreateNote,
73
+ useDebounce: () => useDebounce,
74
+ useDeleteBookmark: () => useDeleteBookmark,
75
+ useDeleteNote: () => useDeleteNote,
76
+ useLessonBookmarks: () => useLessonBookmarks,
77
+ useLessonNotes: () => useLessonNotes,
78
+ useLogout: () => useLogout,
79
+ useMyBundles: () => useMyBundles,
80
+ useMyCourses: () => useMyCourses,
81
+ useNotifications: () => useNotifications,
82
+ useSDK: () => useSDK,
83
+ useTheme: () => useTheme,
84
+ useToast: () => useToast,
85
+ useUpdateBookmark: () => useUpdateBookmark,
86
+ useUpdateNote: () => useUpdateNote,
87
+ useUser: () => useUser,
88
+ validateTemplateManifest: () => validateTemplateManifest
89
+ });
90
+ module.exports = __toCommonJS(src_exports);
91
+
92
+ // src/hooks/sdk-context.tsx
93
+ var import_react = require("react");
94
+ var import_jsx_runtime = require("react/jsx-runtime");
95
+ var SDKContext = (0, import_react.createContext)(null);
96
+ function SDKProvider({ implementations, children }) {
97
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SDKContext.Provider, { value: implementations, children });
98
+ }
99
+ function useSDK() {
100
+ const ctx = (0, import_react.useContext)(SDKContext);
101
+ if (!ctx) {
102
+ throw new Error(
103
+ "useSDK must be used within an SDKProvider. Make sure your learner routes are wrapped with <SDKProvider>."
104
+ );
105
+ }
106
+ return ctx;
107
+ }
108
+
109
+ // src/hooks/useUser.ts
110
+ function useUser() {
111
+ const sdk = useSDK();
112
+ return sdk.useUser();
113
+ }
114
+ function useLogout() {
115
+ const sdk = useSDK();
116
+ return sdk.useLogout();
117
+ }
118
+
119
+ // src/hooks/useMyCourses.ts
120
+ function useMyCourses(status, page, limit) {
121
+ const sdk = useSDK();
122
+ return sdk.useMyCourses(status, page, limit);
123
+ }
124
+
125
+ // src/hooks/useMyBundles.ts
126
+ function useMyBundles(status, page, limit, search) {
127
+ const sdk = useSDK();
128
+ return sdk.useMyBundles(status, page, limit, search);
129
+ }
130
+
131
+ // src/hooks/useCourseSearch.ts
132
+ function useCourseSearch(query) {
133
+ const sdk = useSDK();
134
+ return sdk.useCourseSearch(query);
135
+ }
136
+
137
+ // src/hooks/useNotes.ts
138
+ function useLessonNotes(activityId) {
139
+ const sdk = useSDK();
140
+ return sdk.useLessonNotes(activityId);
141
+ }
142
+ function useCreateNote() {
143
+ const sdk = useSDK();
144
+ return sdk.useCreateNote();
145
+ }
146
+ function useUpdateNote() {
147
+ const sdk = useSDK();
148
+ return sdk.useUpdateNote();
149
+ }
150
+ function useDeleteNote() {
151
+ const sdk = useSDK();
152
+ return sdk.useDeleteNote();
153
+ }
154
+
155
+ // src/hooks/useBookmarks.ts
156
+ function useLessonBookmarks(activityId) {
157
+ const sdk = useSDK();
158
+ return sdk.useLessonBookmarks(activityId);
159
+ }
160
+ function useCreateBookmark() {
161
+ const sdk = useSDK();
162
+ return sdk.useCreateBookmark();
163
+ }
164
+ function useUpdateBookmark() {
165
+ const sdk = useSDK();
166
+ return sdk.useUpdateBookmark();
167
+ }
168
+ function useDeleteBookmark() {
169
+ const sdk = useSDK();
170
+ return sdk.useDeleteBookmark();
171
+ }
172
+
173
+ // src/hooks/useAiCoach.ts
174
+ function useAiCoachAvailability(courseId) {
175
+ const sdk = useSDK();
176
+ return sdk.useAiCoachAvailability(courseId);
177
+ }
178
+ function useAiCoachChat(courseId) {
179
+ const sdk = useSDK();
180
+ return sdk.useAiCoachChat(courseId);
181
+ }
182
+ function useAiCoachHistory(courseId, enabled) {
183
+ const sdk = useSDK();
184
+ return sdk.useAiCoachHistory(courseId, enabled);
185
+ }
186
+
187
+ // src/hooks/useNotifications.ts
188
+ function useNotifications() {
189
+ const sdk = useSDK();
190
+ return sdk.useNotifications();
191
+ }
192
+
193
+ // src/hooks/useTheme.ts
194
+ function useTheme() {
195
+ const sdk = useSDK();
196
+ return sdk.useTheme();
197
+ }
198
+
199
+ // src/hooks/useDebounce.ts
200
+ function useDebounce(value, delay) {
201
+ const sdk = useSDK();
202
+ return sdk.useDebounce(value, delay);
203
+ }
204
+
205
+ // src/hooks/useToast.ts
206
+ function useToast() {
207
+ const sdk = useSDK();
208
+ return sdk.useToast();
209
+ }
210
+
211
+ // src/contracts/template.contract.ts
212
+ function validateTemplateManifest(manifest) {
213
+ if (!manifest.name) throw new Error("Template manifest must have a name");
214
+ if (!manifest.version) throw new Error("Template manifest must have a version");
215
+ if (!manifest.LearnerLayout) throw new Error("Template must provide LearnerLayout");
216
+ const requiredPages = [
217
+ "MyLearningPage",
218
+ "CatalogCoursesPage",
219
+ "CatalogBundlesPage",
220
+ "CourseDetailPage",
221
+ "BundleDetailPage",
222
+ "CoursePlayerPage",
223
+ "MessagesPage",
224
+ "ManualReviewPage",
225
+ "ManualReviewDetailPage",
226
+ "LearnerSettingsPage",
227
+ "PaymentSuccessPage",
228
+ "PaymentCancelPage",
229
+ "CreatorProfilePage"
230
+ ];
231
+ for (const page of requiredPages) {
232
+ if (!manifest.pages[page]) {
233
+ throw new Error(`Template must provide pages.${page}`);
234
+ }
235
+ }
236
+ }
237
+
238
+ // src/utils/formatters.ts
239
+ function formatDuration(seconds) {
240
+ if (!seconds || seconds < 0) return "0:00";
241
+ const hours = Math.floor(seconds / 3600);
242
+ const minutes = Math.floor(seconds % 3600 / 60);
243
+ const secs = Math.floor(seconds % 60);
244
+ if (hours > 0) {
245
+ return `${hours}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
246
+ }
247
+ return `${minutes}:${secs.toString().padStart(2, "0")}`;
248
+ }
249
+ function formatPrice(amount, currency = "USD") {
250
+ return new Intl.NumberFormat("en-US", {
251
+ style: "currency",
252
+ currency,
253
+ minimumFractionDigits: 0,
254
+ maximumFractionDigits: 2
255
+ }).format(amount);
256
+ }
257
+ function formatDate(dateString, options) {
258
+ const date = new Date(dateString);
259
+ return date.toLocaleDateString("en-US", options != null ? options : {
260
+ year: "numeric",
261
+ month: "short",
262
+ day: "numeric"
263
+ });
264
+ }
265
+ function formatRelativeTime(dateString) {
266
+ const date = new Date(dateString);
267
+ const now = /* @__PURE__ */ new Date();
268
+ const diffMs = now.getTime() - date.getTime();
269
+ const diffSecs = Math.floor(diffMs / 1e3);
270
+ const diffMins = Math.floor(diffSecs / 60);
271
+ const diffHours = Math.floor(diffMins / 60);
272
+ const diffDays = Math.floor(diffHours / 24);
273
+ if (diffSecs < 60) return "just now";
274
+ if (diffMins < 60) return `${diffMins}m ago`;
275
+ if (diffHours < 24) return `${diffHours}h ago`;
276
+ if (diffDays < 7) return `${diffDays}d ago`;
277
+ return formatDate(dateString);
278
+ }
279
+ function calculateProgress(completed, total) {
280
+ if (total === 0) return 0;
281
+ return Math.round(completed / total * 100);
282
+ }
283
+ function truncateText(text, maxLength) {
284
+ if (text.length <= maxLength) return text;
285
+ return text.slice(0, maxLength).trimEnd() + "...";
286
+ }
287
+
288
+ // src/layouts/DefaultLayout.tsx
289
+ var import_react3 = require("react");
290
+
291
+ // src/components/organisms/LearnerNavbar.tsx
292
+ var import_react2 = require("react");
293
+ var import_lucide_react = require("lucide-react");
294
+
295
+ // src/components/utils.ts
296
+ var import_clsx = require("clsx");
297
+ var import_tailwind_merge = require("tailwind-merge");
298
+ function cn(...inputs) {
299
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
300
+ }
301
+
302
+ // src/components/organisms/LearnerNavbar.tsx
303
+ var import_jsx_runtime2 = require("react/jsx-runtime");
304
+ function LearnerNavbar({ hideMenuButton = false, onMenuClick, onProfileClick, onLogoClick }) {
305
+ var _a;
306
+ const sdk = useSDK();
307
+ const { user } = sdk.useUser();
308
+ const { logout } = sdk.useLogout();
309
+ const [profileDropdownOpen, setProfileDropdownOpen] = (0, import_react2.useState)(false);
310
+ const closeTimeoutRef = (0, import_react2.useRef)(null);
311
+ (0, import_react2.useEffect)(() => {
312
+ return () => {
313
+ if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
314
+ };
315
+ }, []);
316
+ const openDropdown = () => {
317
+ if (closeTimeoutRef.current) {
318
+ clearTimeout(closeTimeoutRef.current);
319
+ closeTimeoutRef.current = null;
320
+ }
321
+ setProfileDropdownOpen(true);
322
+ };
323
+ const closeDropdownWithDelay = () => {
324
+ if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current);
325
+ closeTimeoutRef.current = setTimeout(() => {
326
+ setProfileDropdownOpen(false);
327
+ closeTimeoutRef.current = null;
328
+ }, 150);
329
+ };
330
+ 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: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center gap-4", children: !hideMenuButton && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
332
+ "button",
333
+ {
334
+ onClick: onMenuClick,
335
+ className: "rounded-lg p-2 hover:bg-theme-bg-tertiary lg:hidden",
336
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Menu, { className: "h-5 w-5 text-theme-text-primary" })
337
+ }
338
+ ) }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
340
+ "div",
341
+ {
342
+ className: "relative group ml-2 sm:ml-4 border-l border-theme-border-primary pl-3 sm:pl-5",
343
+ onMouseEnter: openDropdown,
344
+ onMouseLeave: closeDropdownWithDelay,
345
+ children: [
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
347
+ "button",
348
+ {
349
+ onClick: () => setProfileDropdownOpen((o) => !o),
350
+ className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 cursor-pointer transition-opacity",
351
+ children: [
352
+ /* @__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" }),
353
+ user && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hidden md:flex items-center gap-3", children: [
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-sm font-medium text-theme-text-primary", children: user.name }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
356
+ import_lucide_react.ChevronDown,
357
+ {
358
+ className: cn(
359
+ "h-4 w-4 text-theme-text-secondary transition-transform group-hover:rotate-180",
360
+ profileDropdownOpen && "rotate-180"
361
+ )
362
+ }
363
+ )
364
+ ] })
365
+ ]
366
+ }
367
+ ),
368
+ profileDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
369
+ "div",
370
+ {
371
+ 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",
372
+ onMouseEnter: openDropdown,
373
+ onMouseLeave: closeDropdownWithDelay,
374
+ children: [
375
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "border-b border-theme-border-primary p-3", children: [
376
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm font-medium text-theme-text-primary", children: user == null ? void 0 : user.name }),
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-theme-text-muted", children: user == null ? void 0 : user.email })
378
+ ] }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "p-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
380
+ "button",
381
+ {
382
+ onClick: () => {
383
+ onProfileClick == null ? void 0 : onProfileClick();
384
+ setProfileDropdownOpen(false);
385
+ },
386
+ 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",
387
+ children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.User, { className: "h-4 w-4" }),
389
+ "My Profile"
390
+ ]
391
+ }
392
+ ) }),
393
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "border-t border-theme-border-primary p-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
394
+ "button",
395
+ {
396
+ onClick: () => {
397
+ logout();
398
+ setProfileDropdownOpen(false);
399
+ },
400
+ 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",
401
+ children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.LogOut, { className: "h-4 w-4" }),
403
+ "Logout"
404
+ ]
405
+ }
406
+ ) })
407
+ ]
408
+ }
409
+ )
410
+ ]
411
+ }
412
+ ) })
413
+ ] }) });
414
+ }
415
+
416
+ // src/components/organisms/LearnerSidebar.tsx
417
+ var import_lucide_react2 = require("lucide-react");
418
+
419
+ // src/components/atoms/Button.tsx
420
+ var React = __toESM(require("react"));
421
+ var import_class_variance_authority = require("class-variance-authority");
422
+ var import_jsx_runtime3 = require("react/jsx-runtime");
423
+ var buttonVariants = (0, import_class_variance_authority.cva)(
424
+ "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",
425
+ {
426
+ variants: {
427
+ variant: {
428
+ default: "bg-theme-accent-primary text-white hover:opacity-90",
429
+ destructive: "bg-red-600 text-white hover:bg-red-700",
430
+ outline: "border border-theme-border-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary text-theme-text-primary",
431
+ secondary: "bg-theme-bg-tertiary text-theme-text-primary hover:opacity-80",
432
+ ghost: "hover:bg-theme-bg-tertiary text-theme-text-primary",
433
+ link: "text-[rgb(var(--accent-primary))] underline-offset-4 hover:underline"
434
+ },
435
+ size: {
436
+ default: "h-10 px-4 py-2",
437
+ sm: "h-9 rounded-md px-3",
438
+ lg: "h-11 rounded-md px-8",
439
+ icon: "h-10 w-10"
440
+ }
441
+ },
442
+ defaultVariants: {
443
+ variant: "default",
444
+ size: "default"
445
+ }
446
+ }
447
+ );
448
+ var Button = React.forwardRef(
449
+ (_a, ref) => {
450
+ var _b = _a, { className, variant, size } = _b, props = __objRest(_b, ["className", "variant", "size"]);
451
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
452
+ "button",
453
+ __spreadValues({
454
+ className: cn(buttonVariants({ variant, size, className })),
455
+ ref
456
+ }, props)
457
+ );
458
+ }
459
+ );
460
+ Button.displayName = "Button";
461
+
462
+ // src/components/organisms/LearnerSidebar.tsx
463
+ var import_jsx_runtime4 = require("react/jsx-runtime");
464
+ var defaultItems = [
465
+ { label: "My Learning", href: "/my-learning", icon: import_lucide_react2.BookOpen },
466
+ { label: "Catalog", href: "/catalog/courses", icon: import_lucide_react2.GraduationCap },
467
+ { label: "Messages", href: "/messages", icon: import_lucide_react2.MessageSquare },
468
+ { label: "Manual Review", href: "/manual-review", icon: import_lucide_react2.ClipboardCheck },
469
+ { label: "Account", href: "/account", icon: import_lucide_react2.UserCircle }
470
+ ];
471
+ function LearnerSidebar({
472
+ isOpen,
473
+ isCollapsed,
474
+ onClose,
475
+ onToggle,
476
+ currentPath,
477
+ onNavigate,
478
+ items = defaultItems
479
+ }) {
480
+ const sdk = useSDK();
481
+ const { user } = sdk.useUser();
482
+ const { logout } = sdk.useLogout();
483
+ const isActive = (href, label) => {
484
+ if (label === "Catalog" && currentPath.startsWith("/catalog")) return true;
485
+ return currentPath === href || currentPath.startsWith(href + "/");
486
+ };
487
+ const handleNav = (href) => {
488
+ onNavigate(href);
489
+ onClose();
490
+ };
491
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
492
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
493
+ "aside",
494
+ {
495
+ style: { top: "var(--preview-toolbar-h, 0px)", height: "calc(100vh - var(--preview-toolbar-h, 0px))" },
496
+ className: cn(
497
+ "fixed left-0 z-40 border-r border-theme-border-primary bg-theme-bg-secondary transition-all duration-300 flex flex-col",
498
+ isOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0",
499
+ isCollapsed ? "w-16" : "w-64"
500
+ ),
501
+ children: [
502
+ /* @__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: [
503
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
504
+ "button",
505
+ {
506
+ onClick: () => handleNav("/my-learning"),
507
+ className: "flex items-center gap-2 sm:gap-3 hover:opacity-80 transition-opacity cursor-pointer",
508
+ children: [
509
+ /* @__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" }) }),
510
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xl font-bold text-theme-text-primary leading-none whitespace-nowrap", children: "Academy" })
511
+ ]
512
+ }
513
+ ),
514
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
515
+ Button,
516
+ {
517
+ variant: "ghost",
518
+ size: "icon",
519
+ onClick: onToggle,
520
+ className: cn("hover:bg-theme-bg-tertiary rounded-lg transition-colors", isCollapsed && "mx-auto"),
521
+ 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" })
522
+ }
523
+ )
524
+ ] }),
525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: "flex-1 overflow-y-auto p-4 space-y-1", children: items.map((item) => {
526
+ const Icon = item.icon;
527
+ const active = isActive(item.href, item.label);
528
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
529
+ "button",
530
+ {
531
+ onClick: () => handleNav(item.href),
532
+ className: cn(
533
+ "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
534
+ active ? "bg-theme-accent-primary text-white hover:opacity-90" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
535
+ isCollapsed && "justify-center"
536
+ ),
537
+ title: isCollapsed ? item.label : void 0,
538
+ children: [
539
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { className: "h-5 w-5 shrink-0" }),
540
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: item.label })
541
+ ]
542
+ },
543
+ item.href
544
+ );
545
+ }) }),
546
+ 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: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
548
+ "button",
549
+ {
550
+ onClick: () => handleNav("/learner-settings"),
551
+ className: cn(
552
+ "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors cursor-pointer",
553
+ currentPath === "/learner-settings" ? "bg-theme-accent-primary text-white" : "text-theme-text-secondary hover:bg-theme-bg-tertiary hover:text-theme-text-primary",
554
+ isCollapsed && "justify-center"
555
+ ),
556
+ title: isCollapsed ? "Settings" : void 0,
557
+ children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Settings, { className: "h-5 w-5 shrink-0" }),
559
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: "Settings" })
560
+ ]
561
+ }
562
+ ),
563
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
564
+ "button",
565
+ {
566
+ onClick: logout,
567
+ className: cn(
568
+ "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",
569
+ isCollapsed && "justify-center"
570
+ ),
571
+ children: [
572
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.LogOut, { className: "h-5 w-5 shrink-0" }),
573
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap", children: "Logout" })
574
+ ]
575
+ }
576
+ )
577
+ ] }) })
578
+ ]
579
+ }
580
+ ),
581
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "fixed inset-0 z-30 bg-black/50 lg:hidden", onClick: onClose })
582
+ ] });
583
+ }
584
+
585
+ // src/layouts/DefaultLayout.tsx
586
+ var import_jsx_runtime5 = require("react/jsx-runtime");
587
+ function DefaultLayout({ children, currentPath = "/my-learning", onNavigate }) {
588
+ const [sidebarCollapsed, setSidebarCollapsed] = (0, import_react3.useState)(false);
589
+ const [sidebarOpen, setSidebarOpen] = (0, import_react3.useState)(false);
590
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-h-screen bg-theme-bg-primary", children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { position: "fixed", top: "var(--preview-toolbar-h, 0px)", left: 0, right: 0, zIndex: 40 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
592
+ LearnerNavbar,
593
+ {
594
+ onMenuClick: () => {
595
+ if (typeof window !== "undefined" && window.innerWidth >= 1024) {
596
+ setSidebarCollapsed((c) => !c);
597
+ } else {
598
+ setSidebarOpen((o) => !o);
599
+ }
600
+ }
601
+ }
602
+ ) }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { paddingTop: "calc(80px + var(--preview-toolbar-h, 0px))", display: "flex", minHeight: "100vh" }, children: [
604
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
605
+ LearnerSidebar,
606
+ {
607
+ currentPath,
608
+ isCollapsed: sidebarCollapsed,
609
+ isOpen: sidebarOpen,
610
+ onClose: () => setSidebarOpen(false),
611
+ onToggle: () => setSidebarCollapsed((c) => !c),
612
+ onNavigate: onNavigate != null ? onNavigate : () => {
613
+ }
614
+ }
615
+ ),
616
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
617
+ "main",
618
+ {
619
+ style: {
620
+ flex: 1,
621
+ minWidth: 0,
622
+ transition: "margin-left 0.3s"
623
+ },
624
+ className: "px-4 sm:px-6 lg:px-8 py-6",
625
+ children
626
+ }
627
+ )
628
+ ] })
629
+ ] });
630
+ }