@pattern-stack/frontend-patterns 0.0.3 → 0.0.5

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 (406) hide show
  1. package/dist/frontend-patterns.css +1 -1
  2. package/dist/index.es.js +1918 -3
  3. package/dist/index.es.js.map +1 -1
  4. package/dist/index.js +1917 -1
  5. package/dist/index.js.map +1 -1
  6. package/package.json +10 -5
  7. package/src/App.css +42 -0
  8. package/src/App.tsx +64 -0
  9. package/src/__tests__/README.md +221 -0
  10. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
  11. package/src/__tests__/atoms/ui/button.test.tsx +68 -0
  12. package/src/__tests__/atoms/utils/simple.test.ts +18 -0
  13. package/src/__tests__/atoms/utils/utils.test.ts +77 -0
  14. package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
  15. package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
  16. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
  17. package/src/__tests__/setup.ts +51 -0
  18. package/src/__tests__/utils.tsx +123 -0
  19. package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
  20. package/{dist/atoms/composed/Accordion/index.d.ts → src/atoms/composed/Accordion/index.ts} +1 -2
  21. package/src/atoms/composed/Alert/Alert.tsx +132 -0
  22. package/src/atoms/composed/Alert/index.ts +1 -0
  23. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
  24. package/src/atoms/composed/Breadcrumb/index.ts +1 -0
  25. package/src/atoms/composed/Chart/Chart.tsx +425 -0
  26. package/{dist/atoms/composed/Chart/index.d.ts → src/atoms/composed/Chart/index.ts} +1 -2
  27. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
  28. package/{dist/atoms/composed/ColorSwatch/index.d.ts → src/atoms/composed/ColorSwatch/index.ts} +1 -2
  29. package/src/atoms/composed/DarkModeToggle.tsx +66 -0
  30. package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
  31. package/src/atoms/composed/DataBadge/index.ts +1 -0
  32. package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
  33. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
  34. package/src/atoms/composed/DataTable/index.ts +2 -0
  35. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
  36. package/src/atoms/composed/DateTimePicker/index.ts +2 -0
  37. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
  38. package/src/atoms/composed/DetailedCard/index.ts +2 -0
  39. package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
  40. package/src/atoms/composed/EmptyState/index.ts +1 -0
  41. package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
  42. package/{dist/atoms/composed/FileUpload/index.d.ts → src/atoms/composed/FileUpload/index.ts} +1 -2
  43. package/src/atoms/composed/FormField/FormField.tsx +92 -0
  44. package/src/atoms/composed/FormField/index.ts +1 -0
  45. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
  46. package/src/atoms/composed/GlobalSearch/index.ts +1 -0
  47. package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
  48. package/src/atoms/composed/IconBadge/index.ts +2 -0
  49. package/src/atoms/composed/Modal/Modal.tsx +223 -0
  50. package/src/atoms/composed/Modal/index.ts +2 -0
  51. package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
  52. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
  53. package/{dist/atoms/composed/ProgressBar/index.d.ts → src/atoms/composed/ProgressBar/index.ts} +1 -2
  54. package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
  55. package/src/atoms/composed/SalesPanel/index.ts +1 -0
  56. package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
  57. package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
  58. package/src/atoms/composed/StatCard/index.ts +1 -0
  59. package/src/atoms/composed/StyleGuide.tsx +717 -0
  60. package/src/atoms/composed/Toast/Toast.tsx +219 -0
  61. package/{dist/atoms/composed/Toast/index.d.ts → src/atoms/composed/Toast/index.ts} +1 -2
  62. package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
  63. package/src/atoms/composed/Tooltip/index.ts +1 -0
  64. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
  65. package/src/atoms/composed/UserAvatar/index.ts +1 -0
  66. package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
  67. package/src/atoms/composed/UserMenu/index.ts +1 -0
  68. package/{dist/atoms/composed/index.d.ts → src/atoms/composed/index.ts} +7 -2
  69. package/src/atoms/hooks/useApi.ts +80 -0
  70. package/src/atoms/hooks/useHealth.ts +17 -0
  71. package/{dist/atoms/index.d.ts → src/atoms/index.ts} +6 -2
  72. package/src/atoms/services/api/client.ts +134 -0
  73. package/src/atoms/services/auth-service.ts +248 -0
  74. package/src/atoms/services/health.ts +15 -0
  75. package/{dist/atoms/services/index.d.ts → src/atoms/services/index.ts} +1 -2
  76. package/src/atoms/shared/config/constants.ts +17 -0
  77. package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
  78. package/src/atoms/shared/config/environment.ts +10 -0
  79. package/src/atoms/shared/index.ts +4 -0
  80. package/src/atoms/shared/styles/color-palettes.css +566 -0
  81. package/src/atoms/types/auth.ts +62 -0
  82. package/src/atoms/types/entity-config.ts +127 -0
  83. package/{dist/atoms/types/generated.d.ts → src/atoms/types/generated.ts} +1 -1
  84. package/{dist/atoms/types/index.d.ts → src/atoms/types/index.ts} +2 -1
  85. package/{dist/atoms/types/loading.d.ts → src/atoms/types/loading.ts} +10 -8
  86. package/src/atoms/ui/Badge.tsx +30 -0
  87. package/src/atoms/ui/ErrorBoundary.tsx +59 -0
  88. package/src/atoms/ui/Select.tsx +53 -0
  89. package/src/atoms/ui/Switch.tsx +42 -0
  90. package/src/atoms/ui/Tabs.tsx +118 -0
  91. package/src/atoms/ui/avatar.tsx +48 -0
  92. package/src/atoms/ui/button.tsx +70 -0
  93. package/src/atoms/ui/card.tsx +76 -0
  94. package/src/atoms/ui/dropdown-menu.tsx +199 -0
  95. package/{dist/atoms/ui/index.d.ts → src/atoms/ui/index.ts} +27 -3
  96. package/src/atoms/ui/input.tsx +23 -0
  97. package/src/atoms/ui/label.tsx +23 -0
  98. package/src/atoms/ui/skeleton.tsx +13 -0
  99. package/src/atoms/ui/spinner.tsx +49 -0
  100. package/src/atoms/ui/table.tsx +116 -0
  101. package/src/atoms/utils/animations.ts +135 -0
  102. package/src/atoms/utils/metric-engine.ts +236 -0
  103. package/src/atoms/utils/tooltip-helpers.ts +140 -0
  104. package/src/atoms/utils/utils.ts +10 -0
  105. package/src/features/auth/components/LoginForm.tsx +168 -0
  106. package/src/features/auth/components/LogoutButton.tsx +19 -0
  107. package/src/features/auth/components/ProtectedRoute.tsx +60 -0
  108. package/{dist/features/auth/components/index.d.ts → src/features/auth/components/index.ts} +1 -1
  109. package/src/features/auth/hooks/index.ts +2 -0
  110. package/src/features/auth/hooks/useAuth.tsx +205 -0
  111. package/src/features/auth/hooks/usePermissions.ts +35 -0
  112. package/src/features/auth/index.ts +2 -0
  113. package/src/features/index.ts +2 -0
  114. package/src/index.css +704 -0
  115. package/{dist/index.d.ts → src/index.ts} +5 -2
  116. package/src/main.tsx +48 -0
  117. package/src/molecules/.gitkeep +0 -0
  118. package/src/molecules/forms/FormGroup.tsx +75 -0
  119. package/src/molecules/forms/SearchInput.tsx +259 -0
  120. package/src/molecules/forms/index.ts +4 -0
  121. package/src/molecules/index.ts +4 -0
  122. package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
  123. package/src/molecules/layout/AppHeader/index.ts +1 -0
  124. package/src/molecules/layout/AppLayout.tsx +29 -0
  125. package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
  126. package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
  127. package/src/molecules/layout/PageTemplate.tsx +87 -0
  128. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
  129. package/{dist/molecules/layout/SectionHeader/index.d.ts → src/molecules/layout/SectionHeader/index.ts} +1 -2
  130. package/src/molecules/layout/ShowcaseSection.tsx +57 -0
  131. package/src/molecules/layout/Sidebar.tsx +152 -0
  132. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
  133. package/src/molecules/layout/SidebarButton/index.ts +1 -0
  134. package/src/molecules/layout/SidebarContext.tsx +31 -0
  135. package/{dist/molecules/layout/index.d.ts → src/molecules/layout/index.ts} +2 -2
  136. package/src/molecules/navigation/NavMenu.tsx +188 -0
  137. package/src/molecules/navigation/Pagination.tsx +172 -0
  138. package/src/molecules/navigation/index.ts +4 -0
  139. package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
  140. package/src/organisms/entity/EntityListPanel.tsx +339 -0
  141. package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
  142. package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
  143. package/src/organisms/entity/index.ts +4 -0
  144. package/src/organisms/index.ts +8 -0
  145. package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
  146. package/src/organisms/showcase/index.ts +1 -0
  147. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
  148. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +173 -0
  149. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
  150. package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
  151. package/src/pages/AdminShowcase/index.tsx +4 -0
  152. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
  153. package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
  154. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
  155. package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
  156. package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
  157. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
  158. package/src/pages/ComponentShowcase/index.tsx +188 -0
  159. package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
  160. package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
  161. package/src/pages/EntityShowcase/index.ts +2 -0
  162. package/src/pages/EntityTemplateExample.tsx +229 -0
  163. package/src/pages/TestEntityTemplate.tsx +40 -0
  164. package/src/pages/index.ts +3 -0
  165. package/src/templates/AuthTemplate.tsx +216 -0
  166. package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
  167. package/src/templates/DashboardTemplate.tsx +232 -0
  168. package/src/templates/DataTemplate.tsx +319 -0
  169. package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
  170. package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
  171. package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
  172. package/src/templates/admin/index.ts +29 -0
  173. package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
  174. package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
  175. package/src/templates/entity/configs/financial-config.ts +141 -0
  176. package/src/templates/entity/configs/index.ts +1 -0
  177. package/src/templates/entity/index.ts +3 -0
  178. package/src/templates/factory.tsx +169 -0
  179. package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
  180. package/src/templates/index.ts +40 -0
  181. package/src/vite-env.d.ts +1 -0
  182. package/dist/atoms/composed/Accordion/Accordion.d.ts +0 -20
  183. package/dist/atoms/composed/Accordion/Accordion.d.ts.map +0 -1
  184. package/dist/atoms/composed/Accordion/index.d.ts.map +0 -1
  185. package/dist/atoms/composed/Alert/Alert.d.ts +0 -25
  186. package/dist/atoms/composed/Alert/Alert.d.ts.map +0 -1
  187. package/dist/atoms/composed/Alert/index.d.ts +0 -2
  188. package/dist/atoms/composed/Alert/index.d.ts.map +0 -1
  189. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +0 -17
  190. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +0 -1
  191. package/dist/atoms/composed/Breadcrumb/index.d.ts +0 -2
  192. package/dist/atoms/composed/Breadcrumb/index.d.ts.map +0 -1
  193. package/dist/atoms/composed/Chart/Chart.d.ts +0 -37
  194. package/dist/atoms/composed/Chart/Chart.d.ts.map +0 -1
  195. package/dist/atoms/composed/Chart/index.d.ts.map +0 -1
  196. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +0 -19
  197. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +0 -1
  198. package/dist/atoms/composed/ColorSwatch/index.d.ts.map +0 -1
  199. package/dist/atoms/composed/DarkModeToggle.d.ts +0 -4
  200. package/dist/atoms/composed/DarkModeToggle.d.ts.map +0 -1
  201. package/dist/atoms/composed/DataBadge/DataBadge.d.ts +0 -13
  202. package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +0 -1
  203. package/dist/atoms/composed/DataBadge/index.d.ts +0 -2
  204. package/dist/atoms/composed/DataBadge/index.d.ts.map +0 -1
  205. package/dist/atoms/composed/DataTable/DataTable.d.ts +0 -28
  206. package/dist/atoms/composed/DataTable/DataTable.d.ts.map +0 -1
  207. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +0 -10
  208. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +0 -1
  209. package/dist/atoms/composed/DataTable/index.d.ts +0 -3
  210. package/dist/atoms/composed/DataTable/index.d.ts.map +0 -1
  211. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +0 -45
  212. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  213. package/dist/atoms/composed/DateTimePicker/index.d.ts +0 -3
  214. package/dist/atoms/composed/DateTimePicker/index.d.ts.map +0 -1
  215. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +0 -30
  216. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +0 -1
  217. package/dist/atoms/composed/DetailedCard/index.d.ts +0 -3
  218. package/dist/atoms/composed/DetailedCard/index.d.ts.map +0 -1
  219. package/dist/atoms/composed/EmptyState/EmptyState.d.ts +0 -18
  220. package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +0 -1
  221. package/dist/atoms/composed/EmptyState/index.d.ts +0 -2
  222. package/dist/atoms/composed/EmptyState/index.d.ts.map +0 -1
  223. package/dist/atoms/composed/FileUpload/FileUpload.d.ts +0 -46
  224. package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +0 -1
  225. package/dist/atoms/composed/FileUpload/index.d.ts.map +0 -1
  226. package/dist/atoms/composed/FormField/FormField.d.ts +0 -23
  227. package/dist/atoms/composed/FormField/FormField.d.ts.map +0 -1
  228. package/dist/atoms/composed/FormField/index.d.ts +0 -2
  229. package/dist/atoms/composed/FormField/index.d.ts.map +0 -1
  230. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +0 -8
  231. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +0 -1
  232. package/dist/atoms/composed/GlobalSearch/index.d.ts +0 -2
  233. package/dist/atoms/composed/GlobalSearch/index.d.ts.map +0 -1
  234. package/dist/atoms/composed/IconBadge/IconBadge.d.ts +0 -16
  235. package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +0 -1
  236. package/dist/atoms/composed/IconBadge/index.d.ts +0 -3
  237. package/dist/atoms/composed/IconBadge/index.d.ts.map +0 -1
  238. package/dist/atoms/composed/Modal/Modal.d.ts +0 -18
  239. package/dist/atoms/composed/Modal/Modal.d.ts.map +0 -1
  240. package/dist/atoms/composed/Modal/index.d.ts +0 -3
  241. package/dist/atoms/composed/Modal/index.d.ts.map +0 -1
  242. package/dist/atoms/composed/PaletteSwitcher.d.ts +0 -7
  243. package/dist/atoms/composed/PaletteSwitcher.d.ts.map +0 -1
  244. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +0 -25
  245. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +0 -1
  246. package/dist/atoms/composed/ProgressBar/index.d.ts.map +0 -1
  247. package/dist/atoms/composed/StatCard/StatCard.d.ts +0 -21
  248. package/dist/atoms/composed/StatCard/StatCard.d.ts.map +0 -1
  249. package/dist/atoms/composed/StatCard/index.d.ts +0 -2
  250. package/dist/atoms/composed/StatCard/index.d.ts.map +0 -1
  251. package/dist/atoms/composed/StyleGuide.d.ts +0 -3
  252. package/dist/atoms/composed/StyleGuide.d.ts.map +0 -1
  253. package/dist/atoms/composed/Toast/Toast.d.ts +0 -40
  254. package/dist/atoms/composed/Toast/Toast.d.ts.map +0 -1
  255. package/dist/atoms/composed/Toast/index.d.ts.map +0 -1
  256. package/dist/atoms/composed/Tooltip/Tooltip.d.ts +0 -16
  257. package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +0 -1
  258. package/dist/atoms/composed/Tooltip/index.d.ts +0 -2
  259. package/dist/atoms/composed/Tooltip/index.d.ts.map +0 -1
  260. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +0 -8
  261. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +0 -1
  262. package/dist/atoms/composed/UserAvatar/index.d.ts +0 -2
  263. package/dist/atoms/composed/UserAvatar/index.d.ts.map +0 -1
  264. package/dist/atoms/composed/UserMenu/UserMenu.d.ts +0 -8
  265. package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +0 -1
  266. package/dist/atoms/composed/UserMenu/index.d.ts +0 -2
  267. package/dist/atoms/composed/UserMenu/index.d.ts.map +0 -1
  268. package/dist/atoms/composed/index.d.ts.map +0 -1
  269. package/dist/atoms/hooks/useApi.d.ts +0 -25
  270. package/dist/atoms/hooks/useApi.d.ts.map +0 -1
  271. package/dist/atoms/hooks/useHealth.d.ts +0 -19
  272. package/dist/atoms/hooks/useHealth.d.ts.map +0 -1
  273. package/dist/atoms/index.d.ts.map +0 -1
  274. package/dist/atoms/services/api/client.d.ts +0 -20
  275. package/dist/atoms/services/api/client.d.ts.map +0 -1
  276. package/dist/atoms/services/auth-service.d.ts +0 -24
  277. package/dist/atoms/services/auth-service.d.ts.map +0 -1
  278. package/dist/atoms/services/health.d.ts +0 -7
  279. package/dist/atoms/services/health.d.ts.map +0 -1
  280. package/dist/atoms/services/index.d.ts.map +0 -1
  281. package/dist/atoms/shared/config/constants.d.ts +0 -15
  282. package/dist/atoms/shared/config/constants.d.ts.map +0 -1
  283. package/dist/atoms/shared/config/dashboard-sizes.d.ts +0 -83
  284. package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +0 -1
  285. package/dist/atoms/shared/config/environment.d.ts +0 -10
  286. package/dist/atoms/shared/config/environment.d.ts.map +0 -1
  287. package/dist/atoms/shared/index.d.ts +0 -4
  288. package/dist/atoms/shared/index.d.ts.map +0 -1
  289. package/dist/atoms/types/auth.d.ts +0 -56
  290. package/dist/atoms/types/auth.d.ts.map +0 -1
  291. package/dist/atoms/types/generated.d.ts.map +0 -1
  292. package/dist/atoms/types/index.d.ts.map +0 -1
  293. package/dist/atoms/types/loading.d.ts.map +0 -1
  294. package/dist/atoms/ui/Badge.d.ts +0 -10
  295. package/dist/atoms/ui/Badge.d.ts.map +0 -1
  296. package/dist/atoms/ui/ErrorBoundary.d.ts +0 -18
  297. package/dist/atoms/ui/ErrorBoundary.d.ts.map +0 -1
  298. package/dist/atoms/ui/Select.d.ts +0 -28
  299. package/dist/atoms/ui/Select.d.ts.map +0 -1
  300. package/dist/atoms/ui/Switch.d.ts +0 -9
  301. package/dist/atoms/ui/Switch.d.ts.map +0 -1
  302. package/dist/atoms/ui/Tabs.d.ts +0 -30
  303. package/dist/atoms/ui/Tabs.d.ts.map +0 -1
  304. package/dist/atoms/ui/avatar.d.ts +0 -7
  305. package/dist/atoms/ui/avatar.d.ts.map +0 -1
  306. package/dist/atoms/ui/button.d.ts +0 -14
  307. package/dist/atoms/ui/button.d.ts.map +0 -1
  308. package/dist/atoms/ui/card.d.ts +0 -12
  309. package/dist/atoms/ui/card.d.ts.map +0 -1
  310. package/dist/atoms/ui/dropdown-menu.d.ts +0 -28
  311. package/dist/atoms/ui/dropdown-menu.d.ts.map +0 -1
  312. package/dist/atoms/ui/index.d.ts.map +0 -1
  313. package/dist/atoms/ui/input.d.ts +0 -5
  314. package/dist/atoms/ui/input.d.ts.map +0 -1
  315. package/dist/atoms/ui/label.d.ts +0 -6
  316. package/dist/atoms/ui/label.d.ts.map +0 -1
  317. package/dist/atoms/ui/skeleton.d.ts +0 -3
  318. package/dist/atoms/ui/skeleton.d.ts.map +0 -1
  319. package/dist/atoms/ui/spinner.d.ts +0 -14
  320. package/dist/atoms/ui/spinner.d.ts.map +0 -1
  321. package/dist/atoms/ui/table.d.ts +0 -11
  322. package/dist/atoms/ui/table.d.ts.map +0 -1
  323. package/dist/atoms/utils/animations.d.ts +0 -65
  324. package/dist/atoms/utils/animations.d.ts.map +0 -1
  325. package/dist/atoms/utils/tooltip-helpers.d.ts +0 -71
  326. package/dist/atoms/utils/tooltip-helpers.d.ts.map +0 -1
  327. package/dist/atoms/utils/utils.d.ts +0 -4
  328. package/dist/atoms/utils/utils.d.ts.map +0 -1
  329. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  330. package/dist/features/auth/components/LoginForm.d.ts.map +0 -1
  331. package/dist/features/auth/components/LogoutButton.d.ts +0 -2
  332. package/dist/features/auth/components/LogoutButton.d.ts.map +0 -1
  333. package/dist/features/auth/components/ProtectedRoute.d.ts +0 -10
  334. package/dist/features/auth/components/ProtectedRoute.d.ts.map +0 -1
  335. package/dist/features/auth/components/index.d.ts.map +0 -1
  336. package/dist/features/auth/hooks/index.d.ts +0 -3
  337. package/dist/features/auth/hooks/index.d.ts.map +0 -1
  338. package/dist/features/auth/hooks/useAuth.d.ts +0 -10
  339. package/dist/features/auth/hooks/useAuth.d.ts.map +0 -1
  340. package/dist/features/auth/hooks/usePermissions.d.ts +0 -13
  341. package/dist/features/auth/hooks/usePermissions.d.ts.map +0 -1
  342. package/dist/features/auth/index.d.ts +0 -3
  343. package/dist/features/auth/index.d.ts.map +0 -1
  344. package/dist/features/index.d.ts +0 -2
  345. package/dist/features/index.d.ts.map +0 -1
  346. package/dist/index.d.ts.map +0 -1
  347. package/dist/molecules/forms/FormGroup.d.ts +0 -17
  348. package/dist/molecules/forms/FormGroup.d.ts.map +0 -1
  349. package/dist/molecules/forms/SearchInput.d.ts +0 -36
  350. package/dist/molecules/forms/SearchInput.d.ts.map +0 -1
  351. package/dist/molecules/forms/index.d.ts +0 -3
  352. package/dist/molecules/forms/index.d.ts.map +0 -1
  353. package/dist/molecules/index.d.ts +0 -4
  354. package/dist/molecules/index.d.ts.map +0 -1
  355. package/dist/molecules/layout/AppHeader/AppHeader.d.ts +0 -7
  356. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +0 -1
  357. package/dist/molecules/layout/AppHeader/index.d.ts +0 -2
  358. package/dist/molecules/layout/AppHeader/index.d.ts.map +0 -1
  359. package/dist/molecules/layout/AppLayout.d.ts +0 -2
  360. package/dist/molecules/layout/AppLayout.d.ts.map +0 -1
  361. package/dist/molecules/layout/PageTemplate.d.ts +0 -19
  362. package/dist/molecules/layout/PageTemplate.d.ts.map +0 -1
  363. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +0 -24
  364. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +0 -1
  365. package/dist/molecules/layout/SectionHeader/index.d.ts.map +0 -1
  366. package/dist/molecules/layout/ShowcaseSection.d.ts +0 -22
  367. package/dist/molecules/layout/ShowcaseSection.d.ts.map +0 -1
  368. package/dist/molecules/layout/Sidebar.d.ts +0 -6
  369. package/dist/molecules/layout/Sidebar.d.ts.map +0 -1
  370. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +0 -13
  371. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +0 -1
  372. package/dist/molecules/layout/SidebarButton/index.d.ts +0 -2
  373. package/dist/molecules/layout/SidebarButton/index.d.ts.map +0 -1
  374. package/dist/molecules/layout/SidebarContext.d.ts +0 -12
  375. package/dist/molecules/layout/SidebarContext.d.ts.map +0 -1
  376. package/dist/molecules/layout/index.d.ts.map +0 -1
  377. package/dist/molecules/navigation/NavMenu.d.ts +0 -20
  378. package/dist/molecules/navigation/NavMenu.d.ts.map +0 -1
  379. package/dist/molecules/navigation/Pagination.d.ts +0 -14
  380. package/dist/molecules/navigation/Pagination.d.ts.map +0 -1
  381. package/dist/molecules/navigation/index.d.ts +0 -3
  382. package/dist/molecules/navigation/index.d.ts.map +0 -1
  383. package/dist/organisms/index.d.ts +0 -2
  384. package/dist/organisms/index.d.ts.map +0 -1
  385. package/dist/organisms/showcase/ComponentShowcasePage.d.ts +0 -3
  386. package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +0 -1
  387. package/dist/templates/AuthTemplate.d.ts +0 -68
  388. package/dist/templates/AuthTemplate.d.ts.map +0 -1
  389. package/dist/templates/ComponentShowcaseTemplate.d.ts +0 -53
  390. package/dist/templates/ComponentShowcaseTemplate.d.ts.map +0 -1
  391. package/dist/templates/DashboardTemplate.d.ts +0 -62
  392. package/dist/templates/DashboardTemplate.d.ts.map +0 -1
  393. package/dist/templates/DataTemplate.d.ts +0 -78
  394. package/dist/templates/DataTemplate.d.ts.map +0 -1
  395. package/dist/templates/admin/AdminCRUDTemplate.d.ts +0 -105
  396. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +0 -1
  397. package/dist/templates/admin/AdminDashboardTemplate.d.ts +0 -89
  398. package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +0 -1
  399. package/dist/templates/admin/AdminDetailTemplate.d.ts +0 -132
  400. package/dist/templates/admin/AdminDetailTemplate.d.ts.map +0 -1
  401. package/dist/templates/admin/index.d.ts +0 -4
  402. package/dist/templates/admin/index.d.ts.map +0 -1
  403. package/dist/templates/factory.d.ts +0 -28
  404. package/dist/templates/factory.d.ts.map +0 -1
  405. package/dist/templates/index.d.ts +0 -7
  406. package/dist/templates/index.d.ts.map +0 -1
@@ -0,0 +1,172 @@
1
+ import React from 'react';
2
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
3
+ import { Button } from '../../atoms/ui/button';
4
+ import { cn } from '../../atoms/utils/utils';
5
+
6
+ interface PaginationProps {
7
+ currentPage: number;
8
+ totalPages: number;
9
+ onPageChange: (page: number) => void;
10
+ showFirstLast?: boolean;
11
+ maxVisiblePages?: number;
12
+ className?: string;
13
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
14
+ size?: 'sm' | 'md' | 'lg';
15
+ }
16
+
17
+ export const Pagination: React.FC<PaginationProps> = ({
18
+ currentPage,
19
+ totalPages,
20
+ onPageChange,
21
+ showFirstLast = true,
22
+ maxVisiblePages = 7,
23
+ className,
24
+ category = 1,
25
+ size = 'md'
26
+ }) => {
27
+ const getVisiblePages = () => {
28
+ if (totalPages <= maxVisiblePages) {
29
+ return Array.from({ length: totalPages }, (_, i) => i + 1);
30
+ }
31
+
32
+ const halfVisible = Math.floor(maxVisiblePages / 2);
33
+ let start = Math.max(1, currentPage - halfVisible);
34
+ const end = Math.min(totalPages, start + maxVisiblePages - 1);
35
+
36
+ if (end - start + 1 < maxVisiblePages) {
37
+ start = Math.max(1, end - maxVisiblePages + 1);
38
+ }
39
+
40
+ const pages = [];
41
+
42
+ // Add first page if not in range
43
+ if (start > 1) {
44
+ pages.push(1);
45
+ if (start > 2) {
46
+ pages.push('ellipsis-start');
47
+ }
48
+ }
49
+
50
+ // Add visible range
51
+ for (let i = start; i <= end; i++) {
52
+ pages.push(i);
53
+ }
54
+
55
+ // Add last page if not in range
56
+ if (end < totalPages) {
57
+ if (end < totalPages - 1) {
58
+ pages.push('ellipsis-end');
59
+ }
60
+ pages.push(totalPages);
61
+ }
62
+
63
+ return pages;
64
+ };
65
+
66
+ const buttonSizes = {
67
+ sm: 'h-8 w-8 text-xs',
68
+ md: 'h-9 w-9 text-sm',
69
+ lg: 'h-10 w-10 text-sm'
70
+ };
71
+
72
+ const visiblePages = getVisiblePages();
73
+
74
+ return (
75
+ <nav
76
+ className={cn('flex items-center space-x-1', className)}
77
+ aria-label="Pagination"
78
+ data-component-name="Pagination"
79
+ >
80
+ {/* First page button */}
81
+ {showFirstLast && currentPage > 1 && (
82
+ <Button
83
+ variant="outline"
84
+ size="sm"
85
+ onClick={() => onPageChange(1)}
86
+ className={cn(buttonSizes[size])}
87
+ aria-label="Go to first page"
88
+ >
89
+ ««
90
+ </Button>
91
+ )}
92
+
93
+ {/* Previous page button */}
94
+ <Button
95
+ variant="outline"
96
+ size="sm"
97
+ onClick={() => onPageChange(Math.max(1, currentPage - 1))}
98
+ disabled={currentPage === 1}
99
+ className={cn(buttonSizes[size])}
100
+ aria-label="Go to previous page"
101
+ >
102
+ <ChevronLeft className="w-4 h-4" />
103
+ </Button>
104
+
105
+ {/* Page number buttons */}
106
+ {visiblePages.map((page, index) => {
107
+ if (typeof page === 'string') {
108
+ return (
109
+ <span
110
+ key={index}
111
+ className={cn(
112
+ 'flex items-center justify-center text-muted-foreground',
113
+ buttonSizes[size]
114
+ )}
115
+ >
116
+ <MoreHorizontal className="w-4 h-4" />
117
+ </span>
118
+ );
119
+ }
120
+
121
+ const isActive = page === currentPage;
122
+
123
+ return (
124
+ <Button
125
+ key={page}
126
+ variant={isActive ? "default" : "outline"}
127
+ size="sm"
128
+ onClick={() => onPageChange(page)}
129
+ className={cn(
130
+ buttonSizes[size],
131
+ isActive && [
132
+ `bg-category-${category}/15 hover:bg-category-${category}/25`,
133
+ `border-category-${category}/30`,
134
+ `text-category-${category}`,
135
+ 'shadow-sm'
136
+ ]
137
+ )}
138
+ aria-label={`Go to page ${page}`}
139
+ aria-current={isActive ? 'page' : undefined}
140
+ >
141
+ {page}
142
+ </Button>
143
+ );
144
+ })}
145
+
146
+ {/* Next page button */}
147
+ <Button
148
+ variant="outline"
149
+ size="sm"
150
+ onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
151
+ disabled={currentPage === totalPages}
152
+ className={cn(buttonSizes[size])}
153
+ aria-label="Go to next page"
154
+ >
155
+ <ChevronRight className="w-4 h-4" />
156
+ </Button>
157
+
158
+ {/* Last page button */}
159
+ {showFirstLast && currentPage < totalPages && (
160
+ <Button
161
+ variant="outline"
162
+ size="sm"
163
+ onClick={() => onPageChange(totalPages)}
164
+ className={cn(buttonSizes[size])}
165
+ aria-label="Go to last page"
166
+ >
167
+ »»
168
+ </Button>
169
+ )}
170
+ </nav>
171
+ );
172
+ };
@@ -0,0 +1,4 @@
1
+ // Navigation molecules - reusable navigation workflows
2
+ // Note: Breadcrumb moved to @/atoms/composed as it is a business UI component
3
+ export { Pagination } from './Pagination';
4
+ export { NavMenu, type NavMenuItem } from './NavMenu';
@@ -0,0 +1,427 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import { Chart } from '../../atoms/composed/Chart';
3
+ import type { ChartDataPoint } from '../../atoms/composed/Chart';
4
+ import { Card } from '../../atoms/ui/card';
5
+ import { Button } from '../../atoms/ui/button';
6
+ import { Badge } from '../../atoms/ui/Badge';
7
+ import { DataBadge } from '../../atoms/composed/DataBadge';
8
+ import { MetricCalculationEngine } from '../../atoms/utils/metric-engine';
9
+ import type { CategoryConfig, EntityData, CategoryBreakdown } from '../../atoms/types';
10
+ import { cn } from '../../atoms/utils/utils';
11
+ import {
12
+ ChevronRight,
13
+ ChevronDown,
14
+ PieChart,
15
+ BarChart3,
16
+ List,
17
+ Download,
18
+ Filter
19
+ } from 'lucide-react';
20
+
21
+ export interface CategoryBreakdownPanelProps {
22
+ data: EntityData[];
23
+ categoryConfig: CategoryConfig;
24
+ valueField: string;
25
+ isLoading?: boolean;
26
+ className?: string;
27
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
28
+
29
+ // Display configuration
30
+ title?: string;
31
+ subtitle?: string;
32
+ defaultView?: 'chart' | 'list' | 'both';
33
+ defaultChartType?: 'pie' | 'bar';
34
+ maxCategories?: number;
35
+ showPercentages?: boolean;
36
+ enableDrillDown?: boolean;
37
+
38
+ // Extension points
39
+ renderHeader?: () => React.ReactNode;
40
+ renderFooter?: () => React.ReactNode;
41
+ renderCustomCategory?: (category: CategoryBreakdown, index: number) => React.ReactNode;
42
+ renderDrillDownContent?: (category: string, subcategories: CategoryBreakdown[]) => React.ReactNode;
43
+ headerSlot?: React.ReactNode;
44
+ footerSlot?: React.ReactNode;
45
+
46
+ // Event handlers
47
+ onCategoryClick?: (category: CategoryBreakdown) => void;
48
+ onDrillDown?: (category: string, level: number) => void;
49
+ onExport?: (data: CategoryBreakdown[]) => void;
50
+ }
51
+
52
+ export const CategoryBreakdownPanel: React.FC<CategoryBreakdownPanelProps> = ({
53
+ data,
54
+ categoryConfig,
55
+ valueField,
56
+ isLoading = false,
57
+ className,
58
+ category,
59
+ title = 'Category Breakdown',
60
+ subtitle,
61
+ defaultView = 'both',
62
+ defaultChartType = 'pie',
63
+ maxCategories = 8,
64
+ showPercentages = true,
65
+ enableDrillDown = true,
66
+ renderHeader,
67
+ renderFooter,
68
+ renderCustomCategory,
69
+ renderDrillDownContent,
70
+ headerSlot,
71
+ footerSlot,
72
+ onCategoryClick,
73
+ onDrillDown,
74
+ onExport
75
+ }) => {
76
+ const [currentView, setCurrentView] = useState<'chart' | 'list' | 'both'>(defaultView);
77
+ const [chartType, setChartType] = useState<'pie' | 'bar'>(defaultChartType);
78
+ const [expandedCategories, setExpandedCategories] = useState<Set<string>>(new Set());
79
+ const [drillDownLevel, setDrillDownLevel] = useState(0);
80
+ const [drillDownPath, setDrillDownPath] = useState<string[]>([]);
81
+
82
+ // Calculate category breakdown
83
+ const categoryBreakdown = useMemo(() => {
84
+ if (!data.length) return [];
85
+
86
+ const currentCategoryField = categoryConfig?.hierarchy?.[drillDownLevel] || categoryConfig?.defaultGroupBy || 'category';
87
+
88
+ return MetricCalculationEngine.calculateCategoryBreakdown(
89
+ data,
90
+ currentCategoryField,
91
+ valueField,
92
+ maxCategories
93
+ );
94
+ }, [data, categoryConfig, valueField, maxCategories, drillDownLevel]);
95
+
96
+ // Convert to chart data
97
+ const chartData = useMemo((): ChartDataPoint[] => {
98
+ return categoryBreakdown.map((item, index) => ({
99
+ label: item.category,
100
+ value: item.value,
101
+ category: ((index % 8) + 1) as 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
102
+ }));
103
+ }, [categoryBreakdown]);
104
+
105
+ // Calculate total value
106
+ const totalValue = useMemo(() => {
107
+ return categoryBreakdown.reduce((sum, item) => sum + item.value, 0);
108
+ }, [categoryBreakdown]);
109
+
110
+ const handleCategoryClick = (categoryItem: CategoryBreakdown) => {
111
+ onCategoryClick?.(categoryItem);
112
+
113
+ if (enableDrillDown && categoryConfig?.enableDrillDown && drillDownLevel < (categoryConfig?.hierarchy?.length || 0) - 1) {
114
+ setDrillDownLevel(prev => prev + 1);
115
+ setDrillDownPath(prev => [...prev, categoryItem.category]);
116
+ onDrillDown?.(categoryItem.category, drillDownLevel + 1);
117
+ }
118
+ };
119
+
120
+ const handleDrillUp = (level: number) => {
121
+ if (level < drillDownLevel) {
122
+ setDrillDownLevel(level);
123
+ setDrillDownPath(prev => prev.slice(0, level));
124
+ onDrillDown?.(level > 0 ? drillDownPath[level - 1] : '', level);
125
+ }
126
+ };
127
+
128
+ const toggleCategoryExpansion = (categoryName: string) => {
129
+ setExpandedCategories(prev => {
130
+ const newSet = new Set(prev);
131
+ if (newSet.has(categoryName)) {
132
+ newSet.delete(categoryName);
133
+ } else {
134
+ newSet.add(categoryName);
135
+ }
136
+ return newSet;
137
+ });
138
+ };
139
+
140
+ const handleExport = () => {
141
+ if (onExport) {
142
+ onExport(categoryBreakdown);
143
+ } else {
144
+ // Default CSV export
145
+ const csvContent = [
146
+ 'Category,Value,Percentage',
147
+ ...categoryBreakdown.map(item =>
148
+ `${item.category},${item.value},${item.percentage.toFixed(2)}%`
149
+ )
150
+ ].join('\n');
151
+
152
+ const blob = new Blob([csvContent], { type: 'text/csv' });
153
+ const url = URL.createObjectURL(blob);
154
+ const a = document.createElement('a');
155
+ a.href = url;
156
+ a.download = `category-breakdown-${Date.now()}.csv`;
157
+ a.click();
158
+ URL.revokeObjectURL(url);
159
+ }
160
+ };
161
+
162
+ const renderControls = () => {
163
+ return (
164
+ <div className="flex items-center justify-between mb-4">
165
+ <div className="flex items-center gap-4">
166
+ <div className="flex items-center gap-2">
167
+ <PieChart className="w-4 h-4 text-muted-foreground" />
168
+ <h3 className="text-lg font-semibold text-foreground">{title}</h3>
169
+ </div>
170
+
171
+ {subtitle && (
172
+ <Badge variant="outline" className="text-xs">
173
+ {subtitle}
174
+ </Badge>
175
+ )}
176
+
177
+ {totalValue > 0 && (
178
+ <Badge variant="outline">
179
+ Total: {new Intl.NumberFormat().format(totalValue)}
180
+ </Badge>
181
+ )}
182
+ </div>
183
+
184
+ <div className="flex items-center gap-2">
185
+ {/* View toggle */}
186
+ <div className="flex items-center border rounded">
187
+ <Button
188
+ variant={currentView === 'chart' ? 'default' : 'ghost'}
189
+ size="sm"
190
+ onClick={() => setCurrentView('chart')}
191
+ >
192
+ <PieChart className="w-4 h-4" />
193
+ </Button>
194
+ <Button
195
+ variant={currentView === 'list' ? 'default' : 'ghost'}
196
+ size="sm"
197
+ onClick={() => setCurrentView('list')}
198
+ >
199
+ <List className="w-4 h-4" />
200
+ </Button>
201
+ <Button
202
+ variant={currentView === 'both' ? 'default' : 'ghost'}
203
+ size="sm"
204
+ onClick={() => setCurrentView('both')}
205
+ >
206
+ Both
207
+ </Button>
208
+ </div>
209
+
210
+ {/* Chart type toggle */}
211
+ {(currentView === 'chart' || currentView === 'both') && (
212
+ <div className="flex items-center border rounded">
213
+ <Button
214
+ variant={chartType === 'pie' ? 'default' : 'ghost'}
215
+ size="sm"
216
+ onClick={() => setChartType('pie')}
217
+ >
218
+ <PieChart className="w-4 h-4" />
219
+ </Button>
220
+ <Button
221
+ variant={chartType === 'bar' ? 'default' : 'ghost'}
222
+ size="sm"
223
+ onClick={() => setChartType('bar')}
224
+ >
225
+ <BarChart3 className="w-4 h-4" />
226
+ </Button>
227
+ </div>
228
+ )}
229
+
230
+ <Button
231
+ variant="outline"
232
+ size="sm"
233
+ onClick={handleExport}
234
+ disabled={categoryBreakdown.length === 0}
235
+ >
236
+ <Download className="w-4 h-4" />
237
+ </Button>
238
+ </div>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ const renderBreadcrumb = () => {
244
+ if (drillDownLevel === 0) return null;
245
+
246
+ return (
247
+ <div className="flex items-center gap-2 mb-4 text-sm text-muted-foreground">
248
+ <Button
249
+ variant="ghost"
250
+ size="sm"
251
+ onClick={() => handleDrillUp(0)}
252
+ className="p-0 h-auto font-normal"
253
+ >
254
+ All
255
+ </Button>
256
+ {drillDownPath.map((segment, index) => (
257
+ <React.Fragment key={index}>
258
+ <ChevronRight className="w-3 h-3" />
259
+ <Button
260
+ variant="ghost"
261
+ size="sm"
262
+ onClick={() => handleDrillUp(index + 1)}
263
+ className="p-0 h-auto font-normal"
264
+ >
265
+ {segment}
266
+ </Button>
267
+ </React.Fragment>
268
+ ))}
269
+ </div>
270
+ );
271
+ };
272
+
273
+ const renderChart = () => {
274
+ if (currentView === 'list') return null;
275
+
276
+ return (
277
+ <div className={cn(currentView === 'both' && 'mb-6')}>
278
+ <Chart
279
+ title=""
280
+ data={chartData}
281
+ type={chartType}
282
+ category={category}
283
+ showLegend={true}
284
+ height="medium"
285
+ isLoading={isLoading}
286
+ noWrapper={true}
287
+ onClick={() => {}}
288
+ />
289
+ </div>
290
+ );
291
+ };
292
+
293
+ const renderCategoryList = () => {
294
+ if (currentView === 'chart') return null;
295
+
296
+ return (
297
+ <div className="space-y-2">
298
+ {categoryBreakdown.map((item, index) => {
299
+ if (renderCustomCategory) {
300
+ const customContent = renderCustomCategory(item, index);
301
+ if (customContent) return customContent;
302
+ }
303
+
304
+ const canExpand = enableDrillDown && categoryConfig.enableDrillDown && 'subcategories' in item && item.subcategories && Array.isArray(item.subcategories) && item.subcategories.length > 0;
305
+ const isExpanded = expandedCategories.has(item.category);
306
+
307
+ return (
308
+ <div key={item.category} className="border rounded-lg">
309
+ <div
310
+ className={cn(
311
+ "flex items-center justify-between p-3 cursor-pointer hover:bg-muted/50",
312
+ canExpand && "cursor-pointer"
313
+ )}
314
+ onClick={() => handleCategoryClick(item)}
315
+ >
316
+ <div className="flex items-center gap-3">
317
+ {canExpand && (
318
+ <Button
319
+ variant="ghost"
320
+ size="sm"
321
+ className="p-0 w-4 h-4"
322
+ onClick={(e) => {
323
+ e.stopPropagation();
324
+ toggleCategoryExpansion(item.category);
325
+ }}
326
+ >
327
+ {isExpanded ? (
328
+ <ChevronDown className="w-3 h-3" />
329
+ ) : (
330
+ <ChevronRight className="w-3 h-3" />
331
+ )}
332
+ </Button>
333
+ )}
334
+
335
+ <DataBadge
336
+ variant="category"
337
+ category={((index % 8) + 1) as 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8}
338
+ size="sm"
339
+ >
340
+ {item.category}
341
+ </DataBadge>
342
+ </div>
343
+
344
+ <div className="flex items-center gap-3">
345
+ <div className="text-right">
346
+ <div className="font-semibold">
347
+ {new Intl.NumberFormat().format(item.value)}
348
+ </div>
349
+ {showPercentages && (
350
+ <div className="text-xs text-muted-foreground">
351
+ {item.percentage.toFixed(1)}%
352
+ </div>
353
+ )}
354
+ </div>
355
+
356
+ {/* Progress bar */}
357
+ <div className="w-20 h-2 bg-muted rounded-full overflow-hidden">
358
+ <div
359
+ className={cn(
360
+ "h-full transition-all duration-300",
361
+ `bg-category-${((index % 8) + 1)}`
362
+ )}
363
+ style={{ width: `${item.percentage}%` }}
364
+ />
365
+ </div>
366
+ </div>
367
+ </div>
368
+
369
+ {/* Subcategories */}
370
+ {canExpand && isExpanded && 'subcategories' in item && item.subcategories && (
371
+ <div className="border-t bg-muted/20">
372
+ {renderDrillDownContent ? (
373
+ renderDrillDownContent(item.category, item.subcategories)
374
+ ) : (
375
+ <div className="p-3 space-y-1">
376
+ {Array.isArray(item.subcategories) && item.subcategories.map((subItem: CategoryBreakdown) => (
377
+ <div key={subItem.category} className="flex items-center justify-between text-sm pl-4">
378
+ <span className="text-muted-foreground">{subItem.category}</span>
379
+ <span className="font-medium">{new Intl.NumberFormat().format(subItem.value)}</span>
380
+ </div>
381
+ ))}
382
+ </div>
383
+ )}
384
+ </div>
385
+ )}
386
+ </div>
387
+ );
388
+ })}
389
+ </div>
390
+ );
391
+ };
392
+
393
+ const renderEmptyState = () => {
394
+ if (categoryBreakdown.length > 0) return null;
395
+
396
+ return (
397
+ <div className="text-center py-12">
398
+ <Filter className="w-8 h-8 text-muted-foreground mx-auto mb-4" />
399
+ <div className="text-muted-foreground">
400
+ No category data available
401
+ </div>
402
+ </div>
403
+ );
404
+ };
405
+
406
+ return (
407
+ <Card className={cn('p-6', className)} category={category} data-component-name="CategoryBreakdownPanel">
408
+ {headerSlot}
409
+ {renderHeader && renderHeader()}
410
+
411
+ {renderControls()}
412
+ {renderBreadcrumb()}
413
+
414
+ {categoryBreakdown.length === 0 ? (
415
+ renderEmptyState()
416
+ ) : (
417
+ <>
418
+ {renderChart()}
419
+ {renderCategoryList()}
420
+ </>
421
+ )}
422
+
423
+ {renderFooter && renderFooter()}
424
+ {footerSlot}
425
+ </Card>
426
+ );
427
+ };