@academy-sdk/sdk 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/academy-sdk-sdk-v1.0.0.zip +0 -0
  2. package/dist/bundle.js +70 -0
  3. package/dist/manifest.json +5 -0
  4. package/dist/styles.css +3307 -0
  5. package/package.json +41 -46
  6. package/src/components/atoms/Avatar.tsx +38 -0
  7. package/src/components/atoms/Badge.tsx +32 -0
  8. package/src/components/atoms/Button.tsx +48 -0
  9. package/src/components/atoms/Card.tsx +33 -0
  10. package/src/components/atoms/Input.tsx +39 -0
  11. package/src/components/atoms/ProgressBar.tsx +52 -0
  12. package/src/components/atoms/Tabs.tsx +47 -0
  13. package/{dist/components/atoms/index.d.ts → src/components/atoms/index.ts} +0 -1
  14. package/{dist/components/index.d.ts → src/components/index.ts} +7 -1
  15. package/src/components/molecules/CourseCard.tsx +215 -0
  16. package/src/components/molecules/EmptyState.tsx +23 -0
  17. package/src/components/molecules/LoadingSpinner.tsx +27 -0
  18. package/src/components/molecules/PageHeader.tsx +22 -0
  19. package/src/components/molecules/Pagination.tsx +82 -0
  20. package/src/components/molecules/SearchInput.tsx +35 -0
  21. package/{dist/components/molecules/index.d.ts → src/components/molecules/index.ts} +0 -1
  22. package/src/components/organisms/CourseSidebar.tsx +276 -0
  23. package/src/components/organisms/LearnerNavbar.tsx +129 -0
  24. package/src/components/organisms/LearnerSidebar.tsx +148 -0
  25. package/src/components/organisms/LessonBookmarks.tsx +128 -0
  26. package/src/components/organisms/LessonNotes.tsx +153 -0
  27. package/{dist/components/organisms/index.d.ts → src/components/organisms/index.ts} +0 -1
  28. package/src/components/pages/BundleDetailPage.tsx +388 -0
  29. package/src/components/pages/CatalogBundlesPage.tsx +96 -0
  30. package/src/components/pages/CatalogCoursesPage.tsx +299 -0
  31. package/src/components/pages/CourseDetailPage.tsx +582 -0
  32. package/src/components/pages/CoursePlayerPage.tsx +481 -0
  33. package/src/components/pages/CreatorProfilePage.tsx +161 -0
  34. package/src/components/pages/LearnerSettingsPage.tsx +58 -0
  35. package/src/components/pages/ManualReviewDetailPage.tsx +254 -0
  36. package/src/components/pages/ManualReviewPage.tsx +228 -0
  37. package/src/components/pages/MessagesPage.tsx +285 -0
  38. package/src/components/pages/MyLearningPage.tsx +239 -0
  39. package/src/components/pages/PaymentCancelPage.tsx +74 -0
  40. package/src/components/pages/PaymentSuccessPage.tsx +73 -0
  41. package/{dist/components/pages/index.d.ts → src/components/pages/index.ts} +0 -1
  42. package/src/components/utils.ts +6 -0
  43. package/src/contracts/components.contract.ts +89 -0
  44. package/{dist/contracts/index.d.ts → src/contracts/index.ts} +0 -1
  45. package/src/contracts/layout.contract.ts +36 -0
  46. package/src/contracts/pages.contract.ts +275 -0
  47. package/src/contracts/template.contract.ts +100 -0
  48. package/src/default-template.tsx +52 -0
  49. package/{dist/hooks/index.d.ts → src/hooks/index.ts} +15 -1
  50. package/src/hooks/sdk-context.tsx +152 -0
  51. package/src/hooks/useAiCoach.ts +27 -0
  52. package/src/hooks/useBookmarks.ts +35 -0
  53. package/{dist/hooks/useCourseSearch.d.ts → src/hooks/useCourseSearch.ts} +8 -5
  54. package/{dist/hooks/useDebounce.d.ts → src/hooks/useDebounce.ts} +8 -2
  55. package/{dist/hooks/useMyBundles.d.ts → src/hooks/useMyBundles.ts} +8 -6
  56. package/{dist/hooks/useMyCourses.d.ts → src/hooks/useMyCourses.ts} +8 -6
  57. package/src/hooks/useNotes.ts +35 -0
  58. package/src/hooks/useNotifications.ts +16 -0
  59. package/{dist/hooks/useTheme.d.ts → src/hooks/useTheme.ts} +8 -5
  60. package/src/hooks/useToast.ts +17 -0
  61. package/{dist/hooks/useUser.d.ts → src/hooks/useUser.ts} +13 -9
  62. package/src/index.ts +33 -0
  63. package/src/layouts/DefaultLayout.tsx +58 -0
  64. package/src/manifest.json +5 -0
  65. package/src/styles.css +43 -0
  66. package/src/types/ai-coach.ts +25 -0
  67. package/src/types/bookmarks.ts +20 -0
  68. package/src/types/bundle.ts +119 -0
  69. package/src/types/common.ts +24 -0
  70. package/src/types/course.ts +135 -0
  71. package/src/types/enrollment.ts +35 -0
  72. package/{dist/types/index.d.ts → src/types/index.ts} +0 -1
  73. package/src/types/lesson.ts +106 -0
  74. package/src/types/manual-review.ts +116 -0
  75. package/src/types/messaging.ts +109 -0
  76. package/src/types/notification.ts +30 -0
  77. package/src/types/payment.ts +40 -0
  78. package/src/types/progress.ts +19 -0
  79. package/src/types/rating.ts +20 -0
  80. package/src/types/search.ts +31 -0
  81. package/src/types/user.ts +16 -0
  82. package/src/utils/formatters.ts +74 -0
  83. package/src/utils/index.ts +8 -0
  84. package/dist/components/atoms/Avatar.d.ts +0 -9
  85. package/dist/components/atoms/Avatar.d.ts.map +0 -1
  86. package/dist/components/atoms/Badge.d.ts +0 -10
  87. package/dist/components/atoms/Badge.d.ts.map +0 -1
  88. package/dist/components/atoms/Button.d.ts +0 -11
  89. package/dist/components/atoms/Button.d.ts.map +0 -1
  90. package/dist/components/atoms/Card.d.ts +0 -11
  91. package/dist/components/atoms/Card.d.ts.map +0 -1
  92. package/dist/components/atoms/Input.d.ts +0 -7
  93. package/dist/components/atoms/Input.d.ts.map +0 -1
  94. package/dist/components/atoms/ProgressBar.d.ts +0 -11
  95. package/dist/components/atoms/ProgressBar.d.ts.map +0 -1
  96. package/dist/components/atoms/Tabs.d.ts +0 -16
  97. package/dist/components/atoms/Tabs.d.ts.map +0 -1
  98. package/dist/components/atoms/index.cjs +0 -318
  99. package/dist/components/atoms/index.d.ts.map +0 -1
  100. package/dist/components/atoms/index.js +0 -288
  101. package/dist/components/index.cjs +0 -1275
  102. package/dist/components/index.d.ts.map +0 -1
  103. package/dist/components/index.js +0 -1245
  104. package/dist/components/molecules/CourseCard.d.ts +0 -25
  105. package/dist/components/molecules/CourseCard.d.ts.map +0 -1
  106. package/dist/components/molecules/EmptyState.d.ts +0 -10
  107. package/dist/components/molecules/EmptyState.d.ts.map +0 -1
  108. package/dist/components/molecules/LoadingSpinner.d.ts +0 -7
  109. package/dist/components/molecules/LoadingSpinner.d.ts.map +0 -1
  110. package/dist/components/molecules/PageHeader.d.ts +0 -8
  111. package/dist/components/molecules/PageHeader.d.ts.map +0 -1
  112. package/dist/components/molecules/Pagination.d.ts +0 -13
  113. package/dist/components/molecules/Pagination.d.ts.map +0 -1
  114. package/dist/components/molecules/SearchInput.d.ts +0 -8
  115. package/dist/components/molecules/SearchInput.d.ts.map +0 -1
  116. package/dist/components/molecules/index.cjs +0 -334
  117. package/dist/components/molecules/index.d.ts.map +0 -1
  118. package/dist/components/molecules/index.js +0 -311
  119. package/dist/components/organisms/CourseSidebar.d.ts +0 -37
  120. package/dist/components/organisms/CourseSidebar.d.ts.map +0 -1
  121. package/dist/components/organisms/LearnerNavbar.d.ts +0 -8
  122. package/dist/components/organisms/LearnerNavbar.d.ts.map +0 -1
  123. package/dist/components/organisms/LearnerSidebar.d.ts +0 -16
  124. package/dist/components/organisms/LearnerSidebar.d.ts.map +0 -1
  125. package/dist/components/organisms/LessonBookmarks.d.ts +0 -8
  126. package/dist/components/organisms/LessonBookmarks.d.ts.map +0 -1
  127. package/dist/components/organisms/LessonNotes.d.ts +0 -8
  128. package/dist/components/organisms/LessonNotes.d.ts.map +0 -1
  129. package/dist/components/organisms/index.cjs +0 -855
  130. package/dist/components/organisms/index.d.ts.map +0 -1
  131. package/dist/components/organisms/index.js +0 -825
  132. package/dist/components/pages/BundleDetailPage.d.ts +0 -3
  133. package/dist/components/pages/BundleDetailPage.d.ts.map +0 -1
  134. package/dist/components/pages/CatalogBundlesPage.d.ts +0 -3
  135. package/dist/components/pages/CatalogBundlesPage.d.ts.map +0 -1
  136. package/dist/components/pages/CatalogCoursesPage.d.ts +0 -3
  137. package/dist/components/pages/CatalogCoursesPage.d.ts.map +0 -1
  138. package/dist/components/pages/CourseDetailPage.d.ts +0 -3
  139. package/dist/components/pages/CourseDetailPage.d.ts.map +0 -1
  140. package/dist/components/pages/CoursePlayerPage.d.ts +0 -8
  141. package/dist/components/pages/CoursePlayerPage.d.ts.map +0 -1
  142. package/dist/components/pages/CreatorProfilePage.d.ts +0 -3
  143. package/dist/components/pages/CreatorProfilePage.d.ts.map +0 -1
  144. package/dist/components/pages/LearnerSettingsPage.d.ts +0 -3
  145. package/dist/components/pages/LearnerSettingsPage.d.ts.map +0 -1
  146. package/dist/components/pages/ManualReviewDetailPage.d.ts +0 -3
  147. package/dist/components/pages/ManualReviewDetailPage.d.ts.map +0 -1
  148. package/dist/components/pages/ManualReviewPage.d.ts +0 -3
  149. package/dist/components/pages/ManualReviewPage.d.ts.map +0 -1
  150. package/dist/components/pages/MessagesPage.d.ts +0 -3
  151. package/dist/components/pages/MessagesPage.d.ts.map +0 -1
  152. package/dist/components/pages/MyLearningPage.d.ts +0 -3
  153. package/dist/components/pages/MyLearningPage.d.ts.map +0 -1
  154. package/dist/components/pages/PaymentCancelPage.d.ts +0 -3
  155. package/dist/components/pages/PaymentCancelPage.d.ts.map +0 -1
  156. package/dist/components/pages/PaymentSuccessPage.d.ts +0 -3
  157. package/dist/components/pages/PaymentSuccessPage.d.ts.map +0 -1
  158. package/dist/components/pages/index.cjs +0 -3306
  159. package/dist/components/pages/index.d.ts.map +0 -1
  160. package/dist/components/pages/index.js +0 -3315
  161. package/dist/components/utils.d.ts +0 -3
  162. package/dist/components/utils.d.ts.map +0 -1
  163. package/dist/contracts/components.contract.d.ts +0 -87
  164. package/dist/contracts/components.contract.d.ts.map +0 -1
  165. package/dist/contracts/index.cjs +0 -52
  166. package/dist/contracts/index.d.ts.map +0 -1
  167. package/dist/contracts/index.js +0 -29
  168. package/dist/contracts/layout.contract.d.ts +0 -35
  169. package/dist/contracts/layout.contract.d.ts.map +0 -1
  170. package/dist/contracts/pages.contract.d.ts +0 -192
  171. package/dist/contracts/pages.contract.d.ts.map +0 -1
  172. package/dist/contracts/template.contract.d.ts +0 -49
  173. package/dist/contracts/template.contract.d.ts.map +0 -1
  174. package/dist/hooks/index.cjs +0 -165
  175. package/dist/hooks/index.d.ts.map +0 -1
  176. package/dist/hooks/index.js +0 -142
  177. package/dist/hooks/sdk-context.d.ts +0 -125
  178. package/dist/hooks/sdk-context.d.ts.map +0 -1
  179. package/dist/hooks/useAiCoach.d.ts +0 -32
  180. package/dist/hooks/useAiCoach.d.ts.map +0 -1
  181. package/dist/hooks/useBookmarks.d.ts +0 -31
  182. package/dist/hooks/useBookmarks.d.ts.map +0 -1
  183. package/dist/hooks/useCourseSearch.d.ts.map +0 -1
  184. package/dist/hooks/useDebounce.d.ts.map +0 -1
  185. package/dist/hooks/useMyBundles.d.ts.map +0 -1
  186. package/dist/hooks/useMyCourses.d.ts.map +0 -1
  187. package/dist/hooks/useNotes.d.ts +0 -31
  188. package/dist/hooks/useNotes.d.ts.map +0 -1
  189. package/dist/hooks/useNotifications.d.ts +0 -19
  190. package/dist/hooks/useNotifications.d.ts.map +0 -1
  191. package/dist/hooks/useTheme.d.ts.map +0 -1
  192. package/dist/hooks/useToast.d.ts +0 -17
  193. package/dist/hooks/useToast.d.ts.map +0 -1
  194. package/dist/hooks/useUser.d.ts.map +0 -1
  195. package/dist/index.cjs +0 -630
  196. package/dist/index.d.ts +0 -17
  197. package/dist/index.d.ts.map +0 -1
  198. package/dist/index.js +0 -600
  199. package/dist/layouts/DefaultLayout.d.ts +0 -9
  200. package/dist/layouts/DefaultLayout.d.ts.map +0 -1
  201. package/dist/types/ai-coach.d.ts +0 -22
  202. package/dist/types/ai-coach.d.ts.map +0 -1
  203. package/dist/types/bookmarks.d.ts +0 -19
  204. package/dist/types/bookmarks.d.ts.map +0 -1
  205. package/dist/types/bundle.d.ts +0 -114
  206. package/dist/types/bundle.d.ts.map +0 -1
  207. package/dist/types/common.d.ts +0 -23
  208. package/dist/types/common.d.ts.map +0 -1
  209. package/dist/types/course.d.ts +0 -127
  210. package/dist/types/course.d.ts.map +0 -1
  211. package/dist/types/enrollment.d.ts +0 -34
  212. package/dist/types/enrollment.d.ts.map +0 -1
  213. package/dist/types/index.cjs +0 -18
  214. package/dist/types/index.d.ts.map +0 -1
  215. package/dist/types/index.js +0 -0
  216. package/dist/types/lesson.d.ts +0 -105
  217. package/dist/types/lesson.d.ts.map +0 -1
  218. package/dist/types/manual-review.d.ts +0 -123
  219. package/dist/types/manual-review.d.ts.map +0 -1
  220. package/dist/types/messaging.d.ts +0 -101
  221. package/dist/types/messaging.d.ts.map +0 -1
  222. package/dist/types/notification.d.ts +0 -28
  223. package/dist/types/notification.d.ts.map +0 -1
  224. package/dist/types/payment.d.ts +0 -38
  225. package/dist/types/payment.d.ts.map +0 -1
  226. package/dist/types/progress.d.ts +0 -18
  227. package/dist/types/progress.d.ts.map +0 -1
  228. package/dist/types/rating.d.ts +0 -20
  229. package/dist/types/rating.d.ts.map +0 -1
  230. package/dist/types/search.d.ts +0 -28
  231. package/dist/types/search.d.ts.map +0 -1
  232. package/dist/types/user.d.ts +0 -15
  233. package/dist/types/user.d.ts.map +0 -1
  234. package/dist/utils/formatters.d.ts +0 -25
  235. package/dist/utils/formatters.d.ts.map +0 -1
  236. package/dist/utils/index.cjs +0 -80
  237. package/dist/utils/index.d.ts +0 -2
  238. package/dist/utils/index.d.ts.map +0 -1
  239. package/dist/utils/index.js +0 -57
package/package.json CHANGED
@@ -1,67 +1,58 @@
1
1
  {
2
2
  "name": "@academy-sdk/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "SDK for building custom learner templates for the Academy platform",
5
- "main": "./dist/index.cjs",
6
- "module": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
5
  "files": [
9
- "dist"
6
+ "dist",
7
+ "src"
10
8
  ],
11
9
  "publishConfig": {
12
10
  "access": "public"
13
11
  },
14
12
  "exports": {
15
13
  ".": {
16
- "types": "./dist/index.d.ts",
17
- "import": "./dist/index.js",
18
- "require": "./dist/index.cjs"
14
+ "types": "./src/index.ts",
15
+ "default": "./src/index.ts"
19
16
  },
20
17
  "./types": {
21
- "types": "./dist/types/index.d.ts",
22
- "import": "./dist/types/index.js",
23
- "require": "./dist/types/index.cjs"
18
+ "types": "./src/types/index.ts",
19
+ "default": "./src/types/index.ts"
24
20
  },
25
21
  "./hooks": {
26
- "types": "./dist/hooks/index.d.ts",
27
- "import": "./dist/hooks/index.js",
28
- "require": "./dist/hooks/index.cjs"
22
+ "types": "./src/hooks/index.ts",
23
+ "default": "./src/hooks/index.ts"
29
24
  },
30
25
  "./contracts": {
31
- "types": "./dist/contracts/index.d.ts",
32
- "import": "./dist/contracts/index.js",
33
- "require": "./dist/contracts/index.cjs"
26
+ "types": "./src/contracts/index.ts",
27
+ "default": "./src/contracts/index.ts"
34
28
  },
35
29
  "./utils": {
36
- "types": "./dist/utils/index.d.ts",
37
- "import": "./dist/utils/index.js",
38
- "require": "./dist/utils/index.cjs"
30
+ "types": "./src/utils/index.ts",
31
+ "default": "./src/utils/index.ts"
39
32
  },
40
33
  "./components": {
41
- "types": "./dist/components/index.d.ts",
42
- "import": "./dist/components/index.js",
43
- "require": "./dist/components/index.cjs"
34
+ "types": "./src/components/index.ts",
35
+ "default": "./src/components/index.ts"
44
36
  },
45
37
  "./components/atoms": {
46
- "types": "./dist/components/atoms/index.d.ts",
47
- "import": "./dist/components/atoms/index.js",
48
- "require": "./dist/components/atoms/index.cjs"
38
+ "types": "./src/components/atoms/index.ts",
39
+ "default": "./src/components/atoms/index.ts"
49
40
  },
50
41
  "./components/molecules": {
51
- "types": "./dist/components/molecules/index.d.ts",
52
- "import": "./dist/components/molecules/index.js",
53
- "require": "./dist/components/molecules/index.cjs"
42
+ "types": "./src/components/molecules/index.ts",
43
+ "default": "./src/components/molecules/index.ts"
54
44
  },
55
45
  "./components/organisms": {
56
- "types": "./dist/components/organisms/index.d.ts",
57
- "import": "./dist/components/organisms/index.js",
58
- "require": "./dist/components/organisms/index.cjs"
46
+ "types": "./src/components/organisms/index.ts",
47
+ "default": "./src/components/organisms/index.ts"
59
48
  },
60
49
  "./components/pages": {
61
- "types": "./dist/components/pages/index.d.ts",
62
- "import": "./dist/components/pages/index.js",
63
- "require": "./dist/components/pages/index.cjs"
64
- }
50
+ "types": "./src/components/pages/index.ts",
51
+ "default": "./src/components/pages/index.ts"
52
+ },
53
+ "./styles.css": "./dist/styles.css",
54
+ "./template": "./dist/bundle.js",
55
+ "./manifest": "./dist/manifest.json"
65
56
  },
66
57
  "scripts": {
67
58
  "build": "node build.mjs",
@@ -69,23 +60,27 @@
69
60
  "preview": "node preview.mjs"
70
61
  },
71
62
  "peerDependencies": {
63
+ "class-variance-authority": ">=0.7.0",
64
+ "clsx": ">=2.0.0",
65
+ "lucide-react": ">=0.300.0",
72
66
  "react": ">=18.0.0",
73
67
  "react-dom": ">=18.0.0",
74
- "clsx": ">=2.0.0",
75
- "tailwind-merge": ">=2.0.0",
76
- "class-variance-authority": ">=0.7.0",
77
- "lucide-react": ">=0.300.0"
68
+ "tailwind-merge": ">=2.0.0"
78
69
  },
79
70
  "devDependencies": {
80
- "esbuild": "0.25.5",
81
- "typescript": "5.9.3",
82
- "react": "19.2.0",
83
- "react-dom": "19.2.0",
71
+ "@tailwindcss/postcss": "^4.2.1",
84
72
  "@types/react": "19.2.7",
85
73
  "@types/react-dom": "19.2.3",
74
+ "archiver": "^7.0.1",
75
+ "class-variance-authority": "0.7.1",
86
76
  "clsx": "2.1.1",
77
+ "esbuild": "0.25.5",
78
+ "lucide-react": "0.555.0",
79
+ "postcss": "^8.5.8",
80
+ "react": "19.2.0",
81
+ "react-dom": "19.2.0",
87
82
  "tailwind-merge": "3.4.0",
88
- "class-variance-authority": "0.7.1",
89
- "lucide-react": "0.555.0"
83
+ "tailwindcss": "^4.2.1",
84
+ "typescript": "5.9.3"
90
85
  }
91
86
  }
@@ -0,0 +1,38 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils';
3
+
4
+ interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ src?: string;
6
+ alt?: string;
7
+ fallback?: string;
8
+ }
9
+
10
+ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
11
+ ({ className, src, alt, fallback, ...props }, ref) => {
12
+ return (
13
+ <div
14
+ ref={ref}
15
+ className={cn(
16
+ 'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ {src ? (
22
+ <img
23
+ src={src}
24
+ alt={alt || 'Avatar'}
25
+ className="aspect-square h-full w-full object-cover"
26
+ />
27
+ ) : (
28
+ <div className="flex h-full w-full items-center justify-center bg-gray-200 text-gray-600 text-sm font-medium">
29
+ {fallback || '?'}
30
+ </div>
31
+ )}
32
+ </div>
33
+ );
34
+ }
35
+ );
36
+ Avatar.displayName = 'Avatar';
37
+
38
+ export { Avatar };
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../utils';
4
+
5
+ const badgeVariants = cva(
6
+ 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: 'bg-theme-accent-primary text-white',
11
+ secondary: 'bg-theme-bg-tertiary text-theme-text-primary',
12
+ success: 'border-transparent bg-green-600 text-white',
13
+ warning: 'border-transparent bg-yellow-500 text-white',
14
+ destructive: 'border-transparent bg-red-600 text-white',
15
+ outline: 'text-theme-text-primary border-theme-border-primary',
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ variant: 'default',
20
+ },
21
+ }
22
+ );
23
+
24
+ export interface BadgeProps
25
+ extends React.HTMLAttributes<HTMLDivElement>,
26
+ VariantProps<typeof badgeVariants> {}
27
+
28
+ export function Badge({ className, variant, ...props }: BadgeProps) {
29
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
30
+ }
31
+
32
+ export { badgeVariants };
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../utils';
4
+
5
+ const buttonVariants = cva(
6
+ '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',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: 'bg-theme-accent-primary text-white hover:opacity-90',
11
+ destructive: 'bg-red-600 text-white hover:bg-red-700',
12
+ outline: 'border border-theme-border-primary bg-theme-bg-secondary hover:bg-theme-bg-tertiary text-theme-text-primary',
13
+ secondary: 'bg-theme-bg-tertiary text-theme-text-primary hover:opacity-80',
14
+ ghost: 'hover:bg-theme-bg-tertiary text-theme-text-primary',
15
+ link: 'text-[rgb(var(--accent-primary))] underline-offset-4 hover:underline',
16
+ },
17
+ size: {
18
+ default: 'h-10 px-4 py-2',
19
+ sm: 'h-9 rounded-md px-3',
20
+ lg: 'h-11 rounded-md px-8',
21
+ icon: 'h-10 w-10',
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: 'default',
26
+ size: 'default',
27
+ },
28
+ }
29
+ );
30
+
31
+ export interface ButtonProps
32
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
33
+ VariantProps<typeof buttonVariants> {}
34
+
35
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
36
+ ({ className, variant, size, ...props }, ref) => {
37
+ return (
38
+ <button
39
+ className={cn(buttonVariants({ variant, size, className }))}
40
+ ref={ref}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+ );
46
+ Button.displayName = 'Button';
47
+
48
+ export { Button, buttonVariants };
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils';
3
+
4
+ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
5
+
6
+ export function Card({ className, ...props }: CardProps) {
7
+ return (
8
+ <div
9
+ className={cn('rounded-lg border border-theme-border-primary bg-theme-bg-primary shadow-sm', className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
16
+ return <div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />;
17
+ }
18
+
19
+ export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
20
+ return <h3 className={cn('text-2xl font-semibold leading-none tracking-tight', className)} {...props} />;
21
+ }
22
+
23
+ export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
24
+ return <p className={cn('text-sm text-theme-text-secondary', className)} {...props} />;
25
+ }
26
+
27
+ export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
28
+ return <div className={cn('p-6 pt-0', className)} {...props} />;
29
+ }
30
+
31
+ export function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
32
+ return <div className={cn('flex items-center p-6 pt-0', className)} {...props} />;
33
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils';
3
+
4
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
5
+ label?: string;
6
+ }
7
+
8
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
9
+ ({ className, type, label, ...props }, ref) => {
10
+ const inputElement = (
11
+ <input
12
+ type={type}
13
+ className={cn(
14
+ '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',
15
+ className
16
+ )}
17
+ ref={ref}
18
+ {...props}
19
+ />
20
+ );
21
+
22
+ if (label) {
23
+ return (
24
+ <div className="space-y-2">
25
+ <label className="text-sm font-medium text-theme-text-primary">
26
+ {label}
27
+ {props.required && <span className="text-red-500 ml-1">*</span>}
28
+ </label>
29
+ {inputElement}
30
+ </div>
31
+ );
32
+ }
33
+
34
+ return inputElement;
35
+ }
36
+ );
37
+ Input.displayName = 'Input';
38
+
39
+ export { Input };
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils';
3
+
4
+ interface ProgressBarProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ value: number;
6
+ max?: number;
7
+ showLabel?: boolean;
8
+ size?: 'sm' | 'md' | 'lg';
9
+ variant?: 'default' | 'success' | 'warning' | 'error';
10
+ }
11
+
12
+ const sizeClasses = {
13
+ sm: 'h-1.5',
14
+ md: 'h-2.5',
15
+ lg: 'h-3.5',
16
+ };
17
+
18
+ const variantClasses = {
19
+ default: 'bg-theme-accent-primary',
20
+ success: 'bg-green-600',
21
+ warning: 'bg-yellow-500',
22
+ error: 'bg-red-600',
23
+ };
24
+
25
+ export function ProgressBar({
26
+ value,
27
+ max = 100,
28
+ showLabel = false,
29
+ size = 'md',
30
+ variant = 'default',
31
+ className,
32
+ ...props
33
+ }: ProgressBarProps) {
34
+ const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
35
+
36
+ return (
37
+ <div className={cn('w-full', className)} {...props}>
38
+ {showLabel && (
39
+ <div className="mb-1 flex items-center justify-between text-sm">
40
+ <span className="text-theme-text-secondary">Progress</span>
41
+ <span className="font-medium text-theme-text-primary">{Math.round(percentage)}%</span>
42
+ </div>
43
+ )}
44
+ <div className={cn('w-full overflow-hidden rounded-full bg-theme-bg-tertiary', sizeClasses[size])}>
45
+ <div
46
+ className={cn('h-full transition-all duration-300 ease-in-out', variantClasses[variant])}
47
+ style={{ width: `${percentage}%` }}
48
+ />
49
+ </div>
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { cn } from '../utils';
5
+
6
+ interface Tab {
7
+ id: string;
8
+ label: string;
9
+ icon?: React.ReactNode;
10
+ }
11
+
12
+ interface TabsProps {
13
+ tabs: Tab[];
14
+ activeTab: string;
15
+ onTabChange: (tabId: string) => void;
16
+ className?: string;
17
+ actions?: React.ReactNode;
18
+ }
19
+
20
+ export function Tabs({ tabs, activeTab, onTabChange, className, actions }: TabsProps) {
21
+ return (
22
+ <div className={cn('border-b border-theme-border-primary', className)}>
23
+ <div className="px-4 sm:px-6 pt-4">
24
+ <div className="p-0.5 flex items-center justify-between overflow-x-auto">
25
+ <nav className="flex gap-4 sm:gap-8 min-w-min">
26
+ {tabs.map((tab) => (
27
+ <button
28
+ key={tab.id}
29
+ onClick={() => onTabChange(tab.id)}
30
+ className={cn(
31
+ 'pb-3 px-1 text-sm font-medium border-b-2 transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap',
32
+ activeTab === tab.id
33
+ ? 'border-theme-accent-primary text-theme-accent-primary'
34
+ : 'border-transparent text-theme-text-secondary hover:text-theme-text-primary hover:border-theme-accent-primary/40'
35
+ )}
36
+ >
37
+ {tab.icon && <span className="flex-shrink-0">{tab.icon}</span>}
38
+ {tab.label}
39
+ </button>
40
+ ))}
41
+ </nav>
42
+ {actions && <div className="pb-3">{actions}</div>}
43
+ </div>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -8,4 +8,3 @@ export type { BadgeProps } from './Badge';
8
8
  export { Avatar } from './Avatar';
9
9
  export { ProgressBar } from './ProgressBar';
10
10
  export { Tabs } from './Tabs';
11
- //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,11 @@
1
+ // Utility
1
2
  export { cn } from './utils';
3
+
4
+ // Atoms
2
5
  export * from './atoms';
6
+
7
+ // Molecules
3
8
  export * from './molecules';
9
+
10
+ // Organisms
4
11
  export * from './organisms';
5
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,215 @@
1
+ import { useState } from 'react';
2
+ import { BookOpen, Clock, Grid2X2 } from 'lucide-react';
3
+ import { cn } from '../utils';
4
+ import { ProgressBar } from '../atoms/ProgressBar';
5
+
6
+ export interface CourseCardProps {
7
+ id: string;
8
+ title: string;
9
+ thumbnail?: string;
10
+ thumbnails?: string[];
11
+ totalLessons: number;
12
+ progress: number;
13
+ status?: 'in-progress' | 'completed';
14
+ isFree: boolean;
15
+ price?: number;
16
+ currency?: string;
17
+ description?: string;
18
+ showPrice?: boolean;
19
+ showProgress?: boolean;
20
+ category?: string;
21
+ duration?: string;
22
+ instructorName?: string;
23
+ instructorAvatar?: string;
24
+ originalPrice?: number;
25
+ isBundle?: boolean;
26
+ onClick?: () => void;
27
+ className?: string;
28
+ }
29
+
30
+ export function CourseCard({
31
+ id,
32
+ title,
33
+ thumbnail,
34
+ thumbnails,
35
+ totalLessons,
36
+ progress,
37
+ status,
38
+ isFree,
39
+ price = 0,
40
+ currency = 'USD',
41
+ description,
42
+ showPrice = true,
43
+ showProgress = true,
44
+ category,
45
+ duration,
46
+ instructorName,
47
+ instructorAvatar,
48
+ originalPrice,
49
+ isBundle = false,
50
+ onClick,
51
+ className,
52
+ }: CourseCardProps) {
53
+ const [imageError, setImageError] = useState(false);
54
+ const clampedProgress = Math.min(Math.max(progress, 0), 100);
55
+
56
+ const stripHtml = (html: string): string => {
57
+ if (typeof document === 'undefined') return html;
58
+ const tmp = document.createElement('div');
59
+ tmp.innerHTML = html;
60
+ return tmp.textContent || tmp.innerText || '';
61
+ };
62
+
63
+ const displayCategory = category || 'Design';
64
+ const displayDuration = duration || '3 Month';
65
+ const displayInstructorName = instructorName || (description ? stripHtml(description).slice(0, 15) : 'Instructor');
66
+
67
+ return (
68
+ <div
69
+ className={cn(
70
+ 'group overflow-hidden rounded-2xl border border-gray-100 bg-white shadow-sm hover:shadow-lg transition-all duration-300 hover:-translate-y-1 h-full flex flex-col min-w-0 w-full cursor-pointer',
71
+ className
72
+ )}
73
+ onClick={onClick}
74
+ >
75
+ {/* Image */}
76
+ <div className="relative h-[238.66px] w-full overflow-hidden mx-auto px-3 pt-3">
77
+ <div className="relative w-full h-full rounded-xl overflow-hidden bg-gray-100">
78
+ {isBundle && thumbnails && thumbnails.length > 0 && !imageError ? (
79
+ <div className="relative w-full h-full flex">
80
+ <div className="relative h-full w-1/2">
81
+ <img
82
+ src={thumbnails[0]}
83
+ alt={`${title} - image 1`}
84
+ className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
85
+ onError={() => setImageError(true)}
86
+ />
87
+ </div>
88
+ <div className="flex flex-col w-1/2 h-full">
89
+ {thumbnails[1] && (
90
+ <div className={cn('relative w-full overflow-hidden', thumbnails.length > 2 ? 'h-1/2' : 'h-full')}>
91
+ <img
92
+ src={thumbnails[1]}
93
+ alt={`${title} - image 2`}
94
+ className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
95
+ onError={() => setImageError(true)}
96
+ />
97
+ </div>
98
+ )}
99
+ {thumbnails.length > 2 && thumbnails[2] && (
100
+ <div className="relative w-full h-1/2 overflow-hidden">
101
+ <img
102
+ src={thumbnails[2]}
103
+ alt={`${title} - image 3`}
104
+ className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
105
+ onError={() => setImageError(true)}
106
+ />
107
+ </div>
108
+ )}
109
+ </div>
110
+ </div>
111
+ ) : thumbnail && !imageError ? (
112
+ <img
113
+ src={thumbnail}
114
+ alt={title}
115
+ className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-110"
116
+ onError={() => setImageError(true)}
117
+ />
118
+ ) : (
119
+ <div className="w-full h-full flex items-center justify-center bg-gray-200">
120
+ <BookOpen className="w-12 h-12 text-gray-400" />
121
+ </div>
122
+ )}
123
+ </div>
124
+
125
+ {status && (
126
+ <div className="absolute right-6 top-5">
127
+ <span
128
+ className={`rounded-full px-3 py-1.5 text-xs font-medium text-white shadow-md ${status === 'completed' ? 'bg-green-500' : 'bg-[rgb(var(--accent-primary))]'}`}
129
+ >
130
+ {status}
131
+ </span>
132
+ </div>
133
+ )}
134
+ </div>
135
+
136
+ {/* Content */}
137
+ <div className="p-5 space-y-3 flex-1 flex flex-col min-w-0">
138
+ {(displayCategory || displayDuration) && (
139
+ <div className="flex items-center justify-between text-xs text-gray-500">
140
+ {displayCategory && (
141
+ <div className="flex items-center gap-1.5">
142
+ <Grid2X2 className="w-3.5 h-3.5 text-gray-400" />
143
+ <span>{displayCategory}</span>
144
+ </div>
145
+ )}
146
+ {displayDuration && (
147
+ <div className="flex items-center gap-1.5">
148
+ <Clock className="w-3.5 h-3.5 text-gray-400" />
149
+ <span>{displayDuration}</span>
150
+ </div>
151
+ )}
152
+ </div>
153
+ )}
154
+
155
+ <h3 className="text-base font-bold text-gray-900 line-clamp-2 leading-snug">
156
+ {title}
157
+ </h3>
158
+
159
+ {description && (
160
+ <p className="text-sm text-gray-500 line-clamp-3 leading-relaxed">
161
+ {stripHtml(description)}
162
+ </p>
163
+ )}
164
+
165
+ <div className="flex-1" />
166
+
167
+ {showProgress && (
168
+ <div className="space-y-2">
169
+ <div className="h-2 overflow-hidden rounded-full bg-gray-100">
170
+ <div
171
+ className="h-full rounded-full bg-[rgb(var(--accent-primary))] transition-all duration-300"
172
+ style={{ width: `${clampedProgress}%` }}
173
+ />
174
+ </div>
175
+ <div className="flex items-center justify-between text-xs">
176
+ <span className="text-gray-400"></span>
177
+ <span className="text-gray-500">
178
+ {isBundle ? 'Course' : 'Lesson'} {Math.ceil((clampedProgress / 100) * totalLessons)} of {totalLessons}
179
+ </span>
180
+ </div>
181
+ </div>
182
+ )}
183
+
184
+ {showPrice && (
185
+ <div className="flex items-center justify-between pt-3 border-t border-gray-100">
186
+ <div className="flex items-center gap-2">
187
+ {instructorAvatar ? (
188
+ <div className="w-7 h-7 rounded-full overflow-hidden flex-shrink-0">
189
+ <img src={instructorAvatar} alt={displayInstructorName} className="w-full h-full object-cover" />
190
+ </div>
191
+ ) : (
192
+ <div className="w-7 h-7 rounded-full bg-gray-200 flex items-center justify-center flex-shrink-0">
193
+ <span className="text-[10px] font-semibold text-gray-500">
194
+ {displayInstructorName.charAt(0).toUpperCase()}
195
+ </span>
196
+ </div>
197
+ )}
198
+ <span className="text-sm text-gray-600 font-medium truncate max-w-[80px]">
199
+ {displayInstructorName}
200
+ </span>
201
+ </div>
202
+ <div className="flex items-center gap-2">
203
+ {!isFree && originalPrice && originalPrice > price && (
204
+ <span className="text-sm text-gray-400 line-through">${originalPrice}</span>
205
+ )}
206
+ <span className="text-lg font-bold text-[rgb(var(--accent-primary))]">
207
+ {isFree ? 'Free' : `$${price}`}
208
+ </span>
209
+ </div>
210
+ </div>
211
+ )}
212
+ </div>
213
+ </div>
214
+ );
215
+ }