@pattern-stack/frontend-patterns 0.0.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 (400) hide show
  1. package/README.md +310 -0
  2. package/dist/atoms/composed/Accordion/Accordion.d.ts +20 -0
  3. package/dist/atoms/composed/Accordion/Accordion.d.ts.map +1 -0
  4. package/dist/atoms/composed/Accordion/index.d.ts +2 -0
  5. package/dist/atoms/composed/Accordion/index.d.ts.map +1 -0
  6. package/dist/atoms/composed/Alert/Alert.d.ts +25 -0
  7. package/dist/atoms/composed/Alert/Alert.d.ts.map +1 -0
  8. package/dist/atoms/composed/Alert/index.d.ts +2 -0
  9. package/dist/atoms/composed/Alert/index.d.ts.map +1 -0
  10. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +17 -0
  11. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +1 -0
  12. package/dist/atoms/composed/Breadcrumb/index.d.ts +2 -0
  13. package/dist/atoms/composed/Breadcrumb/index.d.ts.map +1 -0
  14. package/dist/atoms/composed/Chart/Chart.d.ts +37 -0
  15. package/dist/atoms/composed/Chart/Chart.d.ts.map +1 -0
  16. package/dist/atoms/composed/Chart/index.d.ts +3 -0
  17. package/dist/atoms/composed/Chart/index.d.ts.map +1 -0
  18. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +19 -0
  19. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +1 -0
  20. package/dist/atoms/composed/ColorSwatch/index.d.ts +2 -0
  21. package/dist/atoms/composed/ColorSwatch/index.d.ts.map +1 -0
  22. package/dist/atoms/composed/DarkModeToggle.d.ts +4 -0
  23. package/dist/atoms/composed/DarkModeToggle.d.ts.map +1 -0
  24. package/dist/atoms/composed/DataBadge/DataBadge.d.ts +13 -0
  25. package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +1 -0
  26. package/dist/atoms/composed/DataBadge/index.d.ts +2 -0
  27. package/dist/atoms/composed/DataBadge/index.d.ts.map +1 -0
  28. package/dist/atoms/composed/DataTable/DataTable.d.ts +28 -0
  29. package/dist/atoms/composed/DataTable/DataTable.d.ts.map +1 -0
  30. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +10 -0
  31. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +1 -0
  32. package/dist/atoms/composed/DataTable/index.d.ts +3 -0
  33. package/dist/atoms/composed/DataTable/index.d.ts.map +1 -0
  34. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +45 -0
  35. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +1 -0
  36. package/dist/atoms/composed/DateTimePicker/index.d.ts +3 -0
  37. package/dist/atoms/composed/DateTimePicker/index.d.ts.map +1 -0
  38. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +30 -0
  39. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +1 -0
  40. package/dist/atoms/composed/DetailedCard/index.d.ts +3 -0
  41. package/dist/atoms/composed/DetailedCard/index.d.ts.map +1 -0
  42. package/dist/atoms/composed/EmptyState/EmptyState.d.ts +18 -0
  43. package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +1 -0
  44. package/dist/atoms/composed/EmptyState/index.d.ts +2 -0
  45. package/dist/atoms/composed/EmptyState/index.d.ts.map +1 -0
  46. package/dist/atoms/composed/FileUpload/FileUpload.d.ts +46 -0
  47. package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +1 -0
  48. package/dist/atoms/composed/FileUpload/index.d.ts +3 -0
  49. package/dist/atoms/composed/FileUpload/index.d.ts.map +1 -0
  50. package/dist/atoms/composed/FormField/FormField.d.ts +23 -0
  51. package/dist/atoms/composed/FormField/FormField.d.ts.map +1 -0
  52. package/dist/atoms/composed/FormField/index.d.ts +2 -0
  53. package/dist/atoms/composed/FormField/index.d.ts.map +1 -0
  54. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +8 -0
  55. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +1 -0
  56. package/dist/atoms/composed/GlobalSearch/index.d.ts +2 -0
  57. package/dist/atoms/composed/GlobalSearch/index.d.ts.map +1 -0
  58. package/dist/atoms/composed/IconBadge/IconBadge.d.ts +16 -0
  59. package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +1 -0
  60. package/dist/atoms/composed/IconBadge/index.d.ts +3 -0
  61. package/dist/atoms/composed/IconBadge/index.d.ts.map +1 -0
  62. package/dist/atoms/composed/Modal/Modal.d.ts +18 -0
  63. package/dist/atoms/composed/Modal/Modal.d.ts.map +1 -0
  64. package/dist/atoms/composed/Modal/index.d.ts +3 -0
  65. package/dist/atoms/composed/Modal/index.d.ts.map +1 -0
  66. package/dist/atoms/composed/PaletteSwitcher.d.ts +7 -0
  67. package/dist/atoms/composed/PaletteSwitcher.d.ts.map +1 -0
  68. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +25 -0
  69. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +1 -0
  70. package/dist/atoms/composed/ProgressBar/index.d.ts +2 -0
  71. package/dist/atoms/composed/ProgressBar/index.d.ts.map +1 -0
  72. package/dist/atoms/composed/StatCard/StatCard.d.ts +21 -0
  73. package/dist/atoms/composed/StatCard/StatCard.d.ts.map +1 -0
  74. package/dist/atoms/composed/StatCard/index.d.ts +2 -0
  75. package/dist/atoms/composed/StatCard/index.d.ts.map +1 -0
  76. package/dist/atoms/composed/StyleGuide.d.ts +3 -0
  77. package/dist/atoms/composed/StyleGuide.d.ts.map +1 -0
  78. package/dist/atoms/composed/Toast/Toast.d.ts +40 -0
  79. package/dist/atoms/composed/Toast/Toast.d.ts.map +1 -0
  80. package/dist/atoms/composed/Toast/index.d.ts +2 -0
  81. package/dist/atoms/composed/Toast/index.d.ts.map +1 -0
  82. package/dist/atoms/composed/Tooltip/Tooltip.d.ts +16 -0
  83. package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +1 -0
  84. package/dist/atoms/composed/Tooltip/index.d.ts +2 -0
  85. package/dist/atoms/composed/Tooltip/index.d.ts.map +1 -0
  86. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +8 -0
  87. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +1 -0
  88. package/dist/atoms/composed/UserAvatar/index.d.ts +2 -0
  89. package/dist/atoms/composed/UserAvatar/index.d.ts.map +1 -0
  90. package/dist/atoms/composed/UserMenu/UserMenu.d.ts +8 -0
  91. package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +1 -0
  92. package/dist/atoms/composed/UserMenu/index.d.ts +2 -0
  93. package/dist/atoms/composed/UserMenu/index.d.ts.map +1 -0
  94. package/dist/atoms/composed/index.d.ts +25 -0
  95. package/dist/atoms/composed/index.d.ts.map +1 -0
  96. package/dist/atoms/hooks/useApi.d.ts +25 -0
  97. package/dist/atoms/hooks/useApi.d.ts.map +1 -0
  98. package/dist/atoms/hooks/useHealth.d.ts +19 -0
  99. package/dist/atoms/hooks/useHealth.d.ts.map +1 -0
  100. package/dist/atoms/index.d.ts +9 -0
  101. package/dist/atoms/index.d.ts.map +1 -0
  102. package/dist/atoms/services/api/client.d.ts +20 -0
  103. package/dist/atoms/services/api/client.d.ts.map +1 -0
  104. package/dist/atoms/services/auth-service.d.ts +24 -0
  105. package/dist/atoms/services/auth-service.d.ts.map +1 -0
  106. package/dist/atoms/services/health.d.ts +7 -0
  107. package/dist/atoms/services/health.d.ts.map +1 -0
  108. package/dist/atoms/services/index.d.ts +4 -0
  109. package/dist/atoms/services/index.d.ts.map +1 -0
  110. package/dist/atoms/shared/config/constants.d.ts +15 -0
  111. package/dist/atoms/shared/config/constants.d.ts.map +1 -0
  112. package/dist/atoms/shared/config/dashboard-sizes.d.ts +83 -0
  113. package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +1 -0
  114. package/dist/atoms/shared/config/environment.d.ts +10 -0
  115. package/dist/atoms/shared/config/environment.d.ts.map +1 -0
  116. package/dist/atoms/shared/index.d.ts +4 -0
  117. package/dist/atoms/shared/index.d.ts.map +1 -0
  118. package/dist/atoms/types/auth.d.ts +56 -0
  119. package/dist/atoms/types/auth.d.ts.map +1 -0
  120. package/dist/atoms/types/generated.d.ts +1469 -0
  121. package/dist/atoms/types/generated.d.ts.map +1 -0
  122. package/dist/atoms/types/index.d.ts +4 -0
  123. package/dist/atoms/types/index.d.ts.map +1 -0
  124. package/dist/atoms/types/loading.d.ts +26 -0
  125. package/dist/atoms/types/loading.d.ts.map +1 -0
  126. package/dist/atoms/ui/Badge.d.ts +10 -0
  127. package/dist/atoms/ui/Badge.d.ts.map +1 -0
  128. package/dist/atoms/ui/ErrorBoundary.d.ts +18 -0
  129. package/dist/atoms/ui/ErrorBoundary.d.ts.map +1 -0
  130. package/dist/atoms/ui/Select.d.ts +28 -0
  131. package/dist/atoms/ui/Select.d.ts.map +1 -0
  132. package/dist/atoms/ui/Switch.d.ts +9 -0
  133. package/dist/atoms/ui/Switch.d.ts.map +1 -0
  134. package/dist/atoms/ui/Tabs.d.ts +30 -0
  135. package/dist/atoms/ui/Tabs.d.ts.map +1 -0
  136. package/dist/atoms/ui/avatar.d.ts +7 -0
  137. package/dist/atoms/ui/avatar.d.ts.map +1 -0
  138. package/dist/atoms/ui/button.d.ts +14 -0
  139. package/dist/atoms/ui/button.d.ts.map +1 -0
  140. package/dist/atoms/ui/card.d.ts +12 -0
  141. package/dist/atoms/ui/card.d.ts.map +1 -0
  142. package/dist/atoms/ui/dropdown-menu.d.ts +28 -0
  143. package/dist/atoms/ui/dropdown-menu.d.ts.map +1 -0
  144. package/dist/atoms/ui/index.d.ts +15 -0
  145. package/dist/atoms/ui/index.d.ts.map +1 -0
  146. package/dist/atoms/ui/input.d.ts +5 -0
  147. package/dist/atoms/ui/input.d.ts.map +1 -0
  148. package/dist/atoms/ui/label.d.ts +6 -0
  149. package/dist/atoms/ui/label.d.ts.map +1 -0
  150. package/dist/atoms/ui/skeleton.d.ts +3 -0
  151. package/dist/atoms/ui/skeleton.d.ts.map +1 -0
  152. package/dist/atoms/ui/spinner.d.ts +14 -0
  153. package/dist/atoms/ui/spinner.d.ts.map +1 -0
  154. package/dist/atoms/ui/table.d.ts +11 -0
  155. package/dist/atoms/ui/table.d.ts.map +1 -0
  156. package/dist/atoms/utils/animations.d.ts +65 -0
  157. package/dist/atoms/utils/animations.d.ts.map +1 -0
  158. package/dist/atoms/utils/tooltip-helpers.d.ts +71 -0
  159. package/dist/atoms/utils/tooltip-helpers.d.ts.map +1 -0
  160. package/dist/atoms/utils/utils.d.ts +4 -0
  161. package/dist/atoms/utils/utils.d.ts.map +1 -0
  162. package/dist/features/auth/components/LoginForm.d.ts +2 -0
  163. package/dist/features/auth/components/LoginForm.d.ts.map +1 -0
  164. package/dist/features/auth/components/LogoutButton.d.ts +2 -0
  165. package/dist/features/auth/components/LogoutButton.d.ts.map +1 -0
  166. package/dist/features/auth/components/ProtectedRoute.d.ts +10 -0
  167. package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -0
  168. package/dist/features/auth/components/index.d.ts +4 -0
  169. package/dist/features/auth/components/index.d.ts.map +1 -0
  170. package/dist/features/auth/hooks/index.d.ts +3 -0
  171. package/dist/features/auth/hooks/index.d.ts.map +1 -0
  172. package/dist/features/auth/hooks/useAuth.d.ts +10 -0
  173. package/dist/features/auth/hooks/useAuth.d.ts.map +1 -0
  174. package/dist/features/auth/hooks/usePermissions.d.ts +13 -0
  175. package/dist/features/auth/hooks/usePermissions.d.ts.map +1 -0
  176. package/dist/features/auth/index.d.ts +3 -0
  177. package/dist/features/auth/index.d.ts.map +1 -0
  178. package/dist/features/index.d.ts +2 -0
  179. package/dist/features/index.d.ts.map +1 -0
  180. package/dist/frontend-patterns.css +567 -0
  181. package/dist/index.d.ts +10 -0
  182. package/dist/index.d.ts.map +1 -0
  183. package/dist/index.es.js +10152 -0
  184. package/dist/index.es.js.map +1 -0
  185. package/dist/index.js +10170 -0
  186. package/dist/index.js.map +1 -0
  187. package/dist/molecules/forms/FormGroup.d.ts +17 -0
  188. package/dist/molecules/forms/FormGroup.d.ts.map +1 -0
  189. package/dist/molecules/forms/SearchInput.d.ts +36 -0
  190. package/dist/molecules/forms/SearchInput.d.ts.map +1 -0
  191. package/dist/molecules/forms/index.d.ts +3 -0
  192. package/dist/molecules/forms/index.d.ts.map +1 -0
  193. package/dist/molecules/index.d.ts +4 -0
  194. package/dist/molecules/index.d.ts.map +1 -0
  195. package/dist/molecules/layout/AppHeader/AppHeader.d.ts +7 -0
  196. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +1 -0
  197. package/dist/molecules/layout/AppHeader/index.d.ts +2 -0
  198. package/dist/molecules/layout/AppHeader/index.d.ts.map +1 -0
  199. package/dist/molecules/layout/AppLayout.d.ts +2 -0
  200. package/dist/molecules/layout/AppLayout.d.ts.map +1 -0
  201. package/dist/molecules/layout/PageTemplate.d.ts +19 -0
  202. package/dist/molecules/layout/PageTemplate.d.ts.map +1 -0
  203. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +24 -0
  204. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +1 -0
  205. package/dist/molecules/layout/SectionHeader/index.d.ts +2 -0
  206. package/dist/molecules/layout/SectionHeader/index.d.ts.map +1 -0
  207. package/dist/molecules/layout/ShowcaseSection.d.ts +22 -0
  208. package/dist/molecules/layout/ShowcaseSection.d.ts.map +1 -0
  209. package/dist/molecules/layout/Sidebar.d.ts +6 -0
  210. package/dist/molecules/layout/Sidebar.d.ts.map +1 -0
  211. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +13 -0
  212. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -0
  213. package/dist/molecules/layout/SidebarButton/index.d.ts +2 -0
  214. package/dist/molecules/layout/SidebarButton/index.d.ts.map +1 -0
  215. package/dist/molecules/layout/SidebarContext.d.ts +12 -0
  216. package/dist/molecules/layout/SidebarContext.d.ts.map +1 -0
  217. package/dist/molecules/layout/index.d.ts +8 -0
  218. package/dist/molecules/layout/index.d.ts.map +1 -0
  219. package/dist/molecules/navigation/NavMenu.d.ts +20 -0
  220. package/dist/molecules/navigation/NavMenu.d.ts.map +1 -0
  221. package/dist/molecules/navigation/Pagination.d.ts +14 -0
  222. package/dist/molecules/navigation/Pagination.d.ts.map +1 -0
  223. package/dist/molecules/navigation/index.d.ts +3 -0
  224. package/dist/molecules/navigation/index.d.ts.map +1 -0
  225. package/dist/organisms/index.d.ts +2 -0
  226. package/dist/organisms/index.d.ts.map +1 -0
  227. package/dist/organisms/showcase/ComponentShowcasePage.d.ts +3 -0
  228. package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +1 -0
  229. package/dist/templates/AuthTemplate.d.ts +68 -0
  230. package/dist/templates/AuthTemplate.d.ts.map +1 -0
  231. package/dist/templates/ComponentShowcaseTemplate.d.ts +53 -0
  232. package/dist/templates/ComponentShowcaseTemplate.d.ts.map +1 -0
  233. package/dist/templates/DashboardTemplate.d.ts +62 -0
  234. package/dist/templates/DashboardTemplate.d.ts.map +1 -0
  235. package/dist/templates/DataTemplate.d.ts +78 -0
  236. package/dist/templates/DataTemplate.d.ts.map +1 -0
  237. package/dist/templates/admin/AdminCRUDTemplate.d.ts +105 -0
  238. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +1 -0
  239. package/dist/templates/admin/AdminDashboardTemplate.d.ts +89 -0
  240. package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +1 -0
  241. package/dist/templates/admin/AdminDetailTemplate.d.ts +132 -0
  242. package/dist/templates/admin/AdminDetailTemplate.d.ts.map +1 -0
  243. package/dist/templates/admin/index.d.ts +4 -0
  244. package/dist/templates/admin/index.d.ts.map +1 -0
  245. package/dist/templates/factory.d.ts +28 -0
  246. package/dist/templates/factory.d.ts.map +1 -0
  247. package/dist/templates/index.d.ts +7 -0
  248. package/dist/templates/index.d.ts.map +1 -0
  249. package/package.json +118 -0
  250. package/src/App.css +42 -0
  251. package/src/App.tsx +54 -0
  252. package/src/__tests__/README.md +221 -0
  253. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
  254. package/src/__tests__/atoms/ui/button.test.tsx +68 -0
  255. package/src/__tests__/atoms/utils/simple.test.ts +18 -0
  256. package/src/__tests__/atoms/utils/utils.test.ts +77 -0
  257. package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
  258. package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
  259. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
  260. package/src/__tests__/setup.ts +51 -0
  261. package/src/__tests__/utils.tsx +123 -0
  262. package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
  263. package/src/atoms/composed/Accordion/index.ts +1 -0
  264. package/src/atoms/composed/Alert/Alert.tsx +132 -0
  265. package/src/atoms/composed/Alert/index.ts +1 -0
  266. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
  267. package/src/atoms/composed/Breadcrumb/index.ts +1 -0
  268. package/src/atoms/composed/Chart/Chart.tsx +425 -0
  269. package/src/atoms/composed/Chart/index.ts +2 -0
  270. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
  271. package/src/atoms/composed/ColorSwatch/index.ts +1 -0
  272. package/src/atoms/composed/DarkModeToggle.tsx +66 -0
  273. package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
  274. package/src/atoms/composed/DataBadge/index.ts +1 -0
  275. package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
  276. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
  277. package/src/atoms/composed/DataTable/index.ts +2 -0
  278. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
  279. package/src/atoms/composed/DateTimePicker/index.ts +2 -0
  280. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
  281. package/src/atoms/composed/DetailedCard/index.ts +2 -0
  282. package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
  283. package/src/atoms/composed/EmptyState/index.ts +1 -0
  284. package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
  285. package/src/atoms/composed/FileUpload/index.ts +2 -0
  286. package/src/atoms/composed/FormField/FormField.tsx +92 -0
  287. package/src/atoms/composed/FormField/index.ts +1 -0
  288. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
  289. package/src/atoms/composed/GlobalSearch/index.ts +1 -0
  290. package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
  291. package/src/atoms/composed/IconBadge/index.ts +2 -0
  292. package/src/atoms/composed/Modal/Modal.tsx +223 -0
  293. package/src/atoms/composed/Modal/index.ts +2 -0
  294. package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
  295. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
  296. package/src/atoms/composed/ProgressBar/index.ts +1 -0
  297. package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
  298. package/src/atoms/composed/StatCard/index.ts +1 -0
  299. package/src/atoms/composed/StyleGuide.tsx +717 -0
  300. package/src/atoms/composed/Toast/Toast.tsx +219 -0
  301. package/src/atoms/composed/Toast/index.ts +1 -0
  302. package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
  303. package/src/atoms/composed/Tooltip/index.ts +1 -0
  304. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
  305. package/src/atoms/composed/UserAvatar/index.ts +1 -0
  306. package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
  307. package/src/atoms/composed/UserMenu/index.ts +1 -0
  308. package/src/atoms/composed/index.ts +29 -0
  309. package/src/atoms/hooks/useApi.ts +80 -0
  310. package/src/atoms/hooks/useHealth.ts +17 -0
  311. package/src/atoms/index.ts +13 -0
  312. package/src/atoms/services/api/client.ts +134 -0
  313. package/src/atoms/services/auth-service.ts +248 -0
  314. package/src/atoms/services/health.ts +15 -0
  315. package/src/atoms/services/index.ts +3 -0
  316. package/src/atoms/shared/config/constants.ts +17 -0
  317. package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
  318. package/src/atoms/shared/config/environment.ts +10 -0
  319. package/src/atoms/shared/index.ts +4 -0
  320. package/src/atoms/shared/styles/color-palettes.css +566 -0
  321. package/src/atoms/types/auth.ts +62 -0
  322. package/src/atoms/types/generated.ts +1469 -0
  323. package/src/atoms/types/index.ts +4 -0
  324. package/src/atoms/types/loading.ts +28 -0
  325. package/src/atoms/ui/Badge.tsx +30 -0
  326. package/src/atoms/ui/ErrorBoundary.tsx +59 -0
  327. package/src/atoms/ui/Select.tsx +53 -0
  328. package/src/atoms/ui/Switch.tsx +42 -0
  329. package/src/atoms/ui/Tabs.tsx +118 -0
  330. package/src/atoms/ui/avatar.tsx +48 -0
  331. package/src/atoms/ui/button.tsx +70 -0
  332. package/src/atoms/ui/card.tsx +76 -0
  333. package/src/atoms/ui/dropdown-menu.tsx +199 -0
  334. package/src/atoms/ui/index.ts +39 -0
  335. package/src/atoms/ui/input.tsx +23 -0
  336. package/src/atoms/ui/label.tsx +23 -0
  337. package/src/atoms/ui/skeleton.tsx +13 -0
  338. package/src/atoms/ui/spinner.tsx +49 -0
  339. package/src/atoms/ui/table.tsx +116 -0
  340. package/src/atoms/utils/animations.ts +135 -0
  341. package/src/atoms/utils/tooltip-helpers.ts +140 -0
  342. package/src/atoms/utils/utils.ts +9 -0
  343. package/src/features/auth/components/LoginForm.tsx +168 -0
  344. package/src/features/auth/components/LogoutButton.tsx +19 -0
  345. package/src/features/auth/components/ProtectedRoute.tsx +60 -0
  346. package/src/features/auth/components/index.ts +4 -0
  347. package/src/features/auth/hooks/index.ts +2 -0
  348. package/src/features/auth/hooks/useAuth.tsx +205 -0
  349. package/src/features/auth/hooks/usePermissions.ts +35 -0
  350. package/src/features/auth/index.ts +2 -0
  351. package/src/features/index.ts +2 -0
  352. package/src/index.css +704 -0
  353. package/src/index.ts +13 -0
  354. package/src/main.tsx +48 -0
  355. package/src/molecules/.gitkeep +0 -0
  356. package/src/molecules/forms/FormGroup.tsx +75 -0
  357. package/src/molecules/forms/SearchInput.tsx +259 -0
  358. package/src/molecules/forms/index.ts +4 -0
  359. package/src/molecules/index.ts +4 -0
  360. package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
  361. package/src/molecules/layout/AppHeader/index.ts +1 -0
  362. package/src/molecules/layout/AppLayout.tsx +29 -0
  363. package/src/molecules/layout/PageTemplate.tsx +87 -0
  364. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
  365. package/src/molecules/layout/SectionHeader/index.ts +1 -0
  366. package/src/molecules/layout/ShowcaseSection.tsx +57 -0
  367. package/src/molecules/layout/Sidebar.tsx +144 -0
  368. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
  369. package/src/molecules/layout/SidebarButton/index.ts +1 -0
  370. package/src/molecules/layout/SidebarContext.tsx +31 -0
  371. package/src/molecules/layout/index.ts +7 -0
  372. package/src/molecules/navigation/NavMenu.tsx +188 -0
  373. package/src/molecules/navigation/Pagination.tsx +172 -0
  374. package/src/molecules/navigation/index.ts +4 -0
  375. package/src/organisms/index.ts +5 -0
  376. package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
  377. package/src/organisms/showcase/index.ts +1 -0
  378. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
  379. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
  380. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
  381. package/src/pages/AdminShowcase/index.tsx +3 -0
  382. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
  383. package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
  384. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
  385. package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
  386. package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
  387. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
  388. package/src/pages/ComponentShowcase/index.tsx +188 -0
  389. package/src/pages/index.ts +2 -0
  390. package/src/templates/AuthTemplate.tsx +216 -0
  391. package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
  392. package/src/templates/DashboardTemplate.tsx +232 -0
  393. package/src/templates/DataTemplate.tsx +319 -0
  394. package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
  395. package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
  396. package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
  397. package/src/templates/admin/index.ts +29 -0
  398. package/src/templates/factory.tsx +169 -0
  399. package/src/templates/index.ts +37 -0
  400. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { cn } from '../../utils/utils';
3
+
4
+ export interface ColorSwatchProps {
5
+ /** CSS color value (hex, hsl, rgb, or CSS variable) */
6
+ color: string;
7
+ /** Display name for the color */
8
+ name: string;
9
+ /** Optional size variant */
10
+ size?: 'sm' | 'md' | 'lg';
11
+ /** Optional label to show below the swatch */
12
+ label?: string;
13
+ /** Additional CSS classes */
14
+ className?: string;
15
+ /** Whether the swatch is interactive */
16
+ interactive?: boolean;
17
+ /** Click handler for interactive swatches */
18
+ onClick?: () => void;
19
+ }
20
+
21
+ export const ColorSwatch: React.FC<ColorSwatchProps> = ({
22
+ color,
23
+ name,
24
+ size = 'md',
25
+ label,
26
+ className,
27
+ interactive = false,
28
+ onClick
29
+ }) => {
30
+ const sizeClasses = {
31
+ sm: 'w-8 h-8',
32
+ md: 'w-16 h-16',
33
+ lg: 'w-24 h-24'
34
+ };
35
+
36
+ const labelSizes = {
37
+ sm: 'text-xs',
38
+ md: 'text-sm',
39
+ lg: 'text-base'
40
+ };
41
+
42
+ return (
43
+ <div
44
+ className={cn(
45
+ 'flex flex-col items-center space-y-2',
46
+ interactive && 'cursor-pointer group',
47
+ className
48
+ )}
49
+ data-component-name="ColorSwatch"
50
+ onClick={onClick}
51
+ >
52
+ <div
53
+ className={cn(
54
+ 'rounded border border-border/20 flex items-center justify-center text-white font-medium',
55
+ sizeClasses[size],
56
+ interactive && 'transition-all hover:scale-105 hover:shadow group-hover:border-border'
57
+ )}
58
+ style={{ backgroundColor: color }}
59
+ title={`${name}: ${color}`}
60
+ >
61
+ {size === 'lg' && <span className="text-sm">{name}</span>}
62
+ </div>
63
+
64
+ {label && (
65
+ <div className={cn('text-center space-y-1', labelSizes[size])}>
66
+ <div className="font-medium text-foreground">{label}</div>
67
+ <div className="text-muted-foreground font-mono text-xs">{color}</div>
68
+ </div>
69
+ )}
70
+ </div>
71
+ );
72
+ };
@@ -0,0 +1 @@
1
+ export { ColorSwatch, type ColorSwatchProps } from './ColorSwatch';
@@ -0,0 +1,66 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Moon, Sun } from 'lucide-react';
3
+ import { cn } from '../utils/utils';
4
+
5
+ export const DarkModeToggle = ({ className }: { className?: string }) => {
6
+ const [isDark, setIsDark] = useState(false);
7
+
8
+ useEffect(() => {
9
+ // Check for saved preference or system preference
10
+ const savedTheme = localStorage.getItem('theme');
11
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
12
+
13
+ if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
14
+ setIsDark(true);
15
+ document.documentElement.classList.add('dark');
16
+ } else {
17
+ setIsDark(false);
18
+ document.documentElement.classList.remove('dark');
19
+ }
20
+ }, []);
21
+
22
+ const toggleDarkMode = () => {
23
+ if (isDark) {
24
+ document.documentElement.classList.remove('dark');
25
+ localStorage.setItem('theme', 'light');
26
+ setIsDark(false);
27
+ } else {
28
+ document.documentElement.classList.add('dark');
29
+ localStorage.setItem('theme', 'dark');
30
+ setIsDark(true);
31
+ }
32
+ };
33
+
34
+ return (
35
+ <button
36
+ onClick={toggleDarkMode}
37
+ className={cn(
38
+ "relative inline-flex h-10 w-20 items-center justify-center rounded-full",
39
+ "bg-muted dark:bg-muted",
40
+ "transition-all",
41
+ "hover:bg-muted/80 dark:hover:bg-muted/80",
42
+ "focus:outline-none focus:ring-2 focus:ring-primary",
43
+ className
44
+ )}
45
+ data-component-name="DarkModeToggle"
46
+ data-state={isDark ? 'dark' : 'light'}
47
+ aria-label="Toggle dark mode"
48
+ >
49
+ <span
50
+ className={cn(
51
+ "absolute left-1 inline-flex h-8 w-8 items-center justify-center rounded-full",
52
+ "bg-background dark:bg-background",
53
+ "shadow transition-transform",
54
+ isDark && "translate-x-10"
55
+ )}
56
+ data-component-name="DarkModeToggleThumb"
57
+ >
58
+ {isDark ? (
59
+ <Moon className="h-4 w-4 text-category-4" data-component-name="DarkModeToggleIcon" />
60
+ ) : (
61
+ <Sun className="h-4 w-4 text-category-3" data-component-name="DarkModeToggleIcon" />
62
+ )}
63
+ </span>
64
+ </button>
65
+ );
66
+ };
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import { cn, useTextOverflow } from '../../utils/utils';
3
+ import { Tooltip } from '../Tooltip';
4
+ import { getAnimationClasses, animationPresets } from '../../utils/animations';
5
+
6
+ export interface DataBadgeProps {
7
+ children: React.ReactNode;
8
+ variant?: 'category' | 'status';
9
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
10
+ status?: 'success' | 'warning' | 'error' | 'info' | 'neutral';
11
+ size?: 'sm' | 'md' | 'lg';
12
+ interactive?: boolean;
13
+ onClick?: () => void;
14
+ className?: string;
15
+ }
16
+
17
+ export const DataBadge = ({
18
+ children,
19
+ variant = 'category',
20
+ category = 1,
21
+ status = 'neutral',
22
+ size = 'md',
23
+ interactive = false,
24
+ onClick,
25
+ className
26
+ }: DataBadgeProps) => {
27
+ const { ref, isOverflowing } = useTextOverflow();
28
+
29
+ const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {
30
+ sm: 'px-2 py-0.5 text-xs h-5 min-w-[1.5rem] max-w-[150px]',
31
+ md: 'px-2.5 py-1 text-sm h-7 min-w-[2rem] max-w-[200px]',
32
+ lg: 'px-3 py-1.5 text-base h-9 min-w-[2.5rem] max-w-[250px]',
33
+ };
34
+
35
+ const baseClasses = cn(
36
+ 'inline-flex items-center font-medium rounded-lg border truncate',
37
+ sizeClasses[size],
38
+ interactive && [
39
+ 'cursor-pointer',
40
+ getAnimationClasses({
41
+ ...animationPresets.dataBadge,
42
+ size: size === 'lg' ? 'lg' : size === 'sm' ? 'sm' : 'md'
43
+ })
44
+ ],
45
+ className
46
+ );
47
+
48
+ const variantClasses = variant === 'category'
49
+ ? `badge-category-${category}`
50
+ : `status-${status}`;
51
+
52
+ const badge = (
53
+ <span
54
+ ref={ref as React.RefObject<HTMLSpanElement>}
55
+ className={cn(baseClasses, variantClasses, 'animate-fade-in')}
56
+ onClick={onClick}
57
+ role={interactive ? 'button' : undefined}
58
+ tabIndex={interactive ? 0 : undefined}
59
+ onKeyDown={interactive ? (e) => {
60
+ if (e.key === 'Enter' || e.key === ' ') {
61
+ e.preventDefault();
62
+ onClick?.();
63
+ }
64
+ } : undefined}
65
+ data-component-name="DataBadge"
66
+ >
67
+ {children}
68
+ </span>
69
+ );
70
+
71
+ // Show tooltip when text is truncated
72
+ if (isOverflowing && typeof children === 'string') {
73
+ return (
74
+ <Tooltip content={children} position="top" size="sm">
75
+ {badge}
76
+ </Tooltip>
77
+ );
78
+ }
79
+
80
+ return badge;
81
+ };
@@ -0,0 +1 @@
1
+ export * from './DataBadge';
@@ -0,0 +1,394 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import {
3
+ Table,
4
+ TableBody,
5
+ TableCell,
6
+ TableHead,
7
+ TableHeader,
8
+ TableRow,
9
+ } from '../../ui/table';
10
+ import { Input } from '../../ui/input';
11
+ import { Button } from '../../ui/button';
12
+ import { Skeleton } from '../../ui/skeleton';
13
+ import { DataBadge } from '../DataBadge';
14
+ import { cn, tooltipContent } from '../../utils/utils';
15
+ import type { IListLoadable } from '../../types';
16
+ import {
17
+ ChevronDown,
18
+ ChevronUp,
19
+ ChevronsUpDown,
20
+ Search,
21
+ X
22
+ } from 'lucide-react';
23
+
24
+ export interface Column<T> {
25
+ key: string;
26
+ header: string | React.ReactNode;
27
+ cell?: (item: T) => React.ReactNode;
28
+ sortable?: boolean;
29
+ filterable?: boolean;
30
+ width?: string;
31
+ /** Auto-render badges for status/category columns */
32
+ type?: 'status' | 'category' | 'default';
33
+ }
34
+
35
+ interface DataTableProps<T> extends IListLoadable {
36
+ data: T[];
37
+ columns: Column<T>[];
38
+ searchPlaceholder?: string;
39
+ pageSize?: number;
40
+ showPagination?: boolean;
41
+ showSearch?: boolean;
42
+ onRowClick?: (item: T) => void;
43
+ emptyMessage?: string;
44
+ className?: string;
45
+ /** Enable hover effects on table rows */
46
+ hover?: boolean;
47
+ }
48
+
49
+ type SortDirection = 'asc' | 'desc' | null;
50
+
51
+ export function DataTable<T extends Record<string, unknown>>({
52
+ data,
53
+ columns,
54
+ searchPlaceholder = "Search...",
55
+ pageSize = 10,
56
+ showPagination = true,
57
+ showSearch = true,
58
+ onRowClick,
59
+ emptyMessage = "No data available",
60
+ className = "",
61
+ hover = false,
62
+ isLoading = false,
63
+ loadingItemCount = 5
64
+ }: DataTableProps<T>) {
65
+ const [searchTerm, setSearchTerm] = useState('');
66
+ const [sortColumn, setSortColumn] = useState<string | null>(null);
67
+ const [sortDirection, setSortDirection] = useState<SortDirection>(null);
68
+ const [currentPage, setCurrentPage] = useState(1);
69
+
70
+ // Filter data based on search term
71
+ const filteredData = useMemo(() => {
72
+ if (!searchTerm) return data;
73
+
74
+ return data.filter(item => {
75
+ const searchableColumns = columns.filter(col => col.filterable !== false);
76
+ return searchableColumns.some(col => {
77
+ const value = item[col.key];
78
+ if (value === null || value === undefined) return false;
79
+ return value.toString().toLowerCase().includes(searchTerm.toLowerCase());
80
+ });
81
+ });
82
+ }, [data, searchTerm, columns]);
83
+
84
+ // Sort data
85
+ const sortedData = useMemo(() => {
86
+ if (!sortColumn || !sortDirection) return filteredData;
87
+
88
+ return [...filteredData].sort((a, b) => {
89
+ const aVal = a[sortColumn];
90
+ const bVal = b[sortColumn];
91
+
92
+ if (aVal === null || aVal === undefined) return 1;
93
+ if (bVal === null || bVal === undefined) return -1;
94
+
95
+ const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
96
+ return sortDirection === 'asc' ? comparison : -comparison;
97
+ });
98
+ }, [filteredData, sortColumn, sortDirection]);
99
+
100
+ // Paginate data
101
+ const paginatedData = useMemo(() => {
102
+ if (!showPagination) return sortedData;
103
+
104
+ const start = (currentPage - 1) * pageSize;
105
+ const end = start + pageSize;
106
+ return sortedData.slice(start, end);
107
+ }, [sortedData, currentPage, pageSize, showPagination]);
108
+
109
+ const totalPages = Math.ceil(sortedData.length / pageSize);
110
+
111
+ const handleSort = (columnKey: string) => {
112
+ if (sortColumn === columnKey) {
113
+ if (sortDirection === 'asc') {
114
+ setSortDirection('desc');
115
+ } else if (sortDirection === 'desc') {
116
+ setSortDirection(null);
117
+ setSortColumn(null);
118
+ }
119
+ } else {
120
+ setSortColumn(columnKey);
121
+ setSortDirection('asc');
122
+ }
123
+ };
124
+
125
+ const getSortIcon = (columnKey: string) => {
126
+ if (sortColumn !== columnKey) {
127
+ return <ChevronsUpDown className="w-4 h-4 text-muted-foreground" />;
128
+ }
129
+ if (sortDirection === 'asc') {
130
+ return <ChevronUp className="w-4 h-4" />;
131
+ }
132
+ return <ChevronDown className="w-4 h-4" />;
133
+ };
134
+
135
+ // Smart cell renderer that auto-detects and renders badges
136
+ const renderCell = (column: Column<T>, item: T) => {
137
+ // If custom cell renderer is provided, use it
138
+ if (column.cell) {
139
+ return column.cell(item);
140
+ }
141
+
142
+ const value = item[column.key];
143
+
144
+ // Auto-detect column type from key name if not specified
145
+ const columnType = column.type || (
146
+ column.key.toLowerCase().includes('status') ? 'status' :
147
+ column.key.toLowerCase().includes('category') ? 'category' :
148
+ 'default'
149
+ );
150
+
151
+ // Render badges for status and category columns
152
+ if (columnType === 'status' && typeof value === 'string') {
153
+ const status = value as 'success' | 'warning' | 'error' | 'info' | 'neutral';
154
+ return (
155
+ <DataBadge variant="status" status={status} size="sm">
156
+ {value.toString()}
157
+ </DataBadge>
158
+ );
159
+ }
160
+
161
+ if (columnType === 'category') {
162
+ // For category, try to map value to category number (1-8)
163
+ const categoryMap: Record<string, number> = {
164
+ 'Analytics': 1, 'Finance': 2, 'Product': 3, 'Sales': 4,
165
+ 'Marketing': 5, 'Operations': 6, 'Engineering': 7, 'Data': 8
166
+ };
167
+ const categoryNum = typeof value === 'string' ? categoryMap[value] || 1 : 1;
168
+
169
+ return (
170
+ <DataBadge variant="category" category={categoryNum as 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8} size="sm">
171
+ {value?.toString()}
172
+ </DataBadge>
173
+ );
174
+ }
175
+
176
+ // Default rendering
177
+ return value?.toString() || '-';
178
+ };
179
+
180
+ // Loading state skeleton that matches DataTable dimensions
181
+ if (isLoading) {
182
+ return (
183
+ <div className={cn("space-y-4", className)} data-component-name="DataTable">
184
+ {/* Search bar skeleton */}
185
+ {showSearch && (
186
+ <div className="flex items-center gap-2">
187
+ <div className="relative flex-1 max-w-sm">
188
+ <Skeleton className="h-10 w-full" />
189
+ </div>
190
+ </div>
191
+ )}
192
+
193
+ {/* Table skeleton */}
194
+ <div className="rounded border overflow-hidden">
195
+ <Table>
196
+ <TableHeader>
197
+ <TableRow>
198
+ {columns.map(column => (
199
+ <TableHead key={column.key} style={{ width: column.width }}>
200
+ <div className="flex items-center gap-2">
201
+ <Skeleton className="h-4 w-20" />
202
+ </div>
203
+ </TableHead>
204
+ ))}
205
+ </TableRow>
206
+ </TableHeader>
207
+ <TableBody>
208
+ {Array.from({ length: loadingItemCount }, (_, index) => (
209
+ <TableRow key={index}>
210
+ {columns.map(column => (
211
+ <TableCell key={column.key}>
212
+ <Skeleton className="h-4 w-full max-w-32" />
213
+ </TableCell>
214
+ ))}
215
+ </TableRow>
216
+ ))}
217
+ </TableBody>
218
+ </Table>
219
+ </div>
220
+
221
+ {/* Pagination skeleton */}
222
+ {showPagination && (
223
+ <div className="flex items-center justify-between">
224
+ <Skeleton className="h-4 w-48" />
225
+ <div className="flex items-center gap-2">
226
+ <Skeleton className="h-8 w-20" />
227
+ <div className="flex items-center gap-1">
228
+ {Array.from({ length: 3 }, (_, i) => (
229
+ <Skeleton key={i} className="h-8 w-8" />
230
+ ))}
231
+ </div>
232
+ <Skeleton className="h-8 w-16" />
233
+ </div>
234
+ </div>
235
+ )}
236
+ </div>
237
+ );
238
+ }
239
+
240
+ return (
241
+ <div className={cn("space-y-4", className)} data-component-name="DataTable">
242
+ {/* Search bar */}
243
+ {showSearch && (
244
+ <div className="flex items-center gap-2">
245
+ <div className="relative flex-1 max-w-sm">
246
+ <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" data-component-name="DataTableSearch" />
247
+ <Input
248
+ type="text"
249
+ placeholder={searchPlaceholder}
250
+ value={searchTerm}
251
+ onChange={(e) => {
252
+ setSearchTerm(e.target.value);
253
+ setCurrentPage(1);
254
+ }}
255
+ className="pl-10 pr-10"
256
+ />
257
+ {searchTerm && (
258
+ <Button
259
+ onClick={() => {
260
+ setSearchTerm('');
261
+ setCurrentPage(1);
262
+ }}
263
+ variant="ghost"
264
+ size="icon"
265
+ className="absolute right-1 top-1/2 transform -translate-y-1/2 h-8 w-8"
266
+ tooltip={tooltipContent.clearSearch}
267
+ >
268
+ <X className="w-4 h-4" data-component-name="DataTableClear" />
269
+ </Button>
270
+ )}
271
+ </div>
272
+ {searchTerm && (
273
+ <div className="text-muted-foreground text-sm">
274
+ {sortedData.length} result{sortedData.length !== 1 ? 's' : ''}
275
+ </div>
276
+ )}
277
+ </div>
278
+ )}
279
+
280
+ {/* Table */}
281
+ <div className="rounded border overflow-hidden">
282
+ <Table>
283
+ <TableHeader>
284
+ <TableRow>
285
+ {columns.map(column => (
286
+ <TableHead
287
+ key={column.key}
288
+ style={{ width: column.width }}
289
+ className={column.sortable !== false && typeof column.header === 'string' ? 'cursor-pointer select-none' : ''}
290
+ onClick={() => column.sortable !== false && typeof column.header === 'string' && handleSort(column.key)}
291
+ >
292
+ <div className="flex items-center gap-2">
293
+ {typeof column.header === 'string' ? (
294
+ <>
295
+ <span>{column.header}</span>
296
+ {column.sortable !== false && getSortIcon(column.key)}
297
+ </>
298
+ ) : (
299
+ column.header
300
+ )}
301
+ </div>
302
+ </TableHead>
303
+ ))}
304
+ </TableRow>
305
+ </TableHeader>
306
+ <TableBody>
307
+ {paginatedData.length === 0 ? (
308
+ <TableRow>
309
+ <TableCell
310
+ colSpan={columns.length}
311
+ className="text-center py-8 text-muted-foreground"
312
+ >
313
+ {emptyMessage}
314
+ </TableCell>
315
+ </TableRow>
316
+ ) : (
317
+ paginatedData.map((item, index) => (
318
+ <TableRow
319
+ key={index}
320
+ className={cn(
321
+ onRowClick && 'cursor-pointer',
322
+ (hover || onRowClick) && 'hover:bg-muted/50'
323
+ )}
324
+ onClick={() => onRowClick?.(item)}
325
+ >
326
+ {columns.map(column => (
327
+ <TableCell key={column.key}>
328
+ {renderCell(column, item)}
329
+ </TableCell>
330
+ ))}
331
+ </TableRow>
332
+ ))
333
+ )}
334
+ </TableBody>
335
+ </Table>
336
+ </div>
337
+
338
+ {/* Pagination */}
339
+ {showPagination && totalPages > 1 && (
340
+ <div className="flex items-center justify-between">
341
+ <div className="text-muted-foreground text-sm">
342
+ Showing {((currentPage - 1) * pageSize) + 1} to {Math.min(currentPage * pageSize, sortedData.length)} of {sortedData.length} entries
343
+ </div>
344
+ <div className="flex items-center gap-2">
345
+ <Button
346
+ variant="outline"
347
+ size="sm"
348
+ onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))}
349
+ disabled={currentPage === 1}
350
+ tooltip={tooltipContent.previous}
351
+ >
352
+ Previous
353
+ </Button>
354
+ <div className="flex items-center gap-1">
355
+ {Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
356
+ let pageNum;
357
+ if (totalPages <= 5) {
358
+ pageNum = i + 1;
359
+ } else if (currentPage <= 3) {
360
+ pageNum = i + 1;
361
+ } else if (currentPage >= totalPages - 2) {
362
+ pageNum = totalPages - 4 + i;
363
+ } else {
364
+ pageNum = currentPage - 2 + i;
365
+ }
366
+
367
+ return (
368
+ <Button
369
+ key={pageNum}
370
+ variant={currentPage === pageNum ? "default" : "outline"}
371
+ size="sm"
372
+ onClick={() => setCurrentPage(pageNum)}
373
+ className="w-8 h-8"
374
+ >
375
+ {pageNum}
376
+ </Button>
377
+ );
378
+ })}
379
+ </div>
380
+ <Button
381
+ variant="outline"
382
+ size="sm"
383
+ onClick={() => setCurrentPage(prev => Math.min(totalPages, prev + 1))}
384
+ disabled={currentPage === totalPages}
385
+ tooltip={tooltipContent.next}
386
+ >
387
+ Next
388
+ </Button>
389
+ </div>
390
+ </div>
391
+ )}
392
+ </div>
393
+ );
394
+ }
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import { TableCell } from '../../ui/table';
3
+ import { Tooltip } from '../Tooltip';
4
+ import { useTextOverflow } from '../../utils/utils';
5
+
6
+ interface TableCellWithTooltipProps {
7
+ children: React.ReactNode;
8
+ className?: string;
9
+ /** Maximum width before truncating */
10
+ maxWidth?: string;
11
+ }
12
+
13
+ export const TableCellWithTooltip: React.FC<TableCellWithTooltipProps> = ({
14
+ children,
15
+ className,
16
+ maxWidth = '200px'
17
+ }) => {
18
+ const { ref, isOverflowing } = useTextOverflow();
19
+
20
+ const cellContent = (
21
+ <div
22
+ ref={ref as React.RefObject<HTMLDivElement>}
23
+ className="truncate"
24
+ style={{ maxWidth }}
25
+ >
26
+ {children}
27
+ </div>
28
+ );
29
+
30
+ return (
31
+ <TableCell className={className}>
32
+ {isOverflowing && typeof children === 'string' ? (
33
+ <Tooltip content={children} position="top" size="sm">
34
+ {cellContent}
35
+ </Tooltip>
36
+ ) : (
37
+ cellContent
38
+ )}
39
+ </TableCell>
40
+ );
41
+ };
@@ -0,0 +1,2 @@
1
+ export * from './DataTable';
2
+ export * from './TableCellWithTooltip';