@pattern-stack/frontend-patterns 0.0.5 → 0.0.6

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 (282) hide show
  1. package/dist/atoms/composed/Accordion/Accordion.d.ts +20 -0
  2. package/dist/atoms/composed/Accordion/Accordion.d.ts.map +1 -0
  3. package/dist/atoms/composed/Accordion/index.d.ts +2 -0
  4. package/dist/atoms/composed/Accordion/index.d.ts.map +1 -0
  5. package/dist/atoms/composed/Alert/Alert.d.ts +25 -0
  6. package/dist/atoms/composed/Alert/Alert.d.ts.map +1 -0
  7. package/dist/atoms/composed/Alert/index.d.ts +2 -0
  8. package/dist/atoms/composed/Alert/index.d.ts.map +1 -0
  9. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +17 -0
  10. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +1 -0
  11. package/dist/atoms/composed/Breadcrumb/index.d.ts +2 -0
  12. package/dist/atoms/composed/Breadcrumb/index.d.ts.map +1 -0
  13. package/dist/atoms/composed/Chart/Chart.d.ts +37 -0
  14. package/dist/atoms/composed/Chart/Chart.d.ts.map +1 -0
  15. package/dist/atoms/composed/Chart/index.d.ts +3 -0
  16. package/dist/atoms/composed/Chart/index.d.ts.map +1 -0
  17. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +19 -0
  18. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +1 -0
  19. package/dist/atoms/composed/ColorSwatch/index.d.ts +2 -0
  20. package/dist/atoms/composed/ColorSwatch/index.d.ts.map +1 -0
  21. package/dist/atoms/composed/DarkModeToggle.d.ts +4 -0
  22. package/dist/atoms/composed/DarkModeToggle.d.ts.map +1 -0
  23. package/dist/atoms/composed/DataBadge/DataBadge.d.ts +13 -0
  24. package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +1 -0
  25. package/dist/atoms/composed/DataBadge/index.d.ts +2 -0
  26. package/dist/atoms/composed/DataBadge/index.d.ts.map +1 -0
  27. package/dist/atoms/composed/DataTable/DataTable.d.ts +28 -0
  28. package/dist/atoms/composed/DataTable/DataTable.d.ts.map +1 -0
  29. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +10 -0
  30. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +1 -0
  31. package/dist/atoms/composed/DataTable/index.d.ts +3 -0
  32. package/dist/atoms/composed/DataTable/index.d.ts.map +1 -0
  33. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +45 -0
  34. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +1 -0
  35. package/dist/atoms/composed/DateTimePicker/index.d.ts +3 -0
  36. package/dist/atoms/composed/DateTimePicker/index.d.ts.map +1 -0
  37. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +30 -0
  38. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +1 -0
  39. package/dist/atoms/composed/DetailedCard/index.d.ts +3 -0
  40. package/dist/atoms/composed/DetailedCard/index.d.ts.map +1 -0
  41. package/dist/atoms/composed/EmptyState/EmptyState.d.ts +18 -0
  42. package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +1 -0
  43. package/dist/atoms/composed/EmptyState/index.d.ts +2 -0
  44. package/dist/atoms/composed/EmptyState/index.d.ts.map +1 -0
  45. package/dist/atoms/composed/FileUpload/FileUpload.d.ts +46 -0
  46. package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +1 -0
  47. package/dist/atoms/composed/FileUpload/index.d.ts +3 -0
  48. package/dist/atoms/composed/FileUpload/index.d.ts.map +1 -0
  49. package/dist/atoms/composed/FormField/FormField.d.ts +23 -0
  50. package/dist/atoms/composed/FormField/FormField.d.ts.map +1 -0
  51. package/dist/atoms/composed/FormField/index.d.ts +2 -0
  52. package/dist/atoms/composed/FormField/index.d.ts.map +1 -0
  53. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +8 -0
  54. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +1 -0
  55. package/dist/atoms/composed/GlobalSearch/index.d.ts +2 -0
  56. package/dist/atoms/composed/GlobalSearch/index.d.ts.map +1 -0
  57. package/dist/atoms/composed/IconBadge/IconBadge.d.ts +16 -0
  58. package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +1 -0
  59. package/dist/atoms/composed/IconBadge/index.d.ts +3 -0
  60. package/dist/atoms/composed/IconBadge/index.d.ts.map +1 -0
  61. package/dist/atoms/composed/Modal/Modal.d.ts +18 -0
  62. package/dist/atoms/composed/Modal/Modal.d.ts.map +1 -0
  63. package/dist/atoms/composed/Modal/index.d.ts +3 -0
  64. package/dist/atoms/composed/Modal/index.d.ts.map +1 -0
  65. package/dist/atoms/composed/PaletteSwitcher.d.ts +7 -0
  66. package/dist/atoms/composed/PaletteSwitcher.d.ts.map +1 -0
  67. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +25 -0
  68. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +1 -0
  69. package/dist/atoms/composed/ProgressBar/index.d.ts +2 -0
  70. package/dist/atoms/composed/ProgressBar/index.d.ts.map +1 -0
  71. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts +19 -0
  72. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +1 -0
  73. package/dist/atoms/composed/SalesPanel/index.d.ts +2 -0
  74. package/dist/atoms/composed/SalesPanel/index.d.ts.map +1 -0
  75. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +63 -0
  76. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +1 -0
  77. package/dist/atoms/composed/StatCard/StatCard.d.ts +21 -0
  78. package/dist/atoms/composed/StatCard/StatCard.d.ts.map +1 -0
  79. package/dist/atoms/composed/StatCard/index.d.ts +2 -0
  80. package/dist/atoms/composed/StatCard/index.d.ts.map +1 -0
  81. package/dist/atoms/composed/StyleGuide.d.ts +3 -0
  82. package/dist/atoms/composed/StyleGuide.d.ts.map +1 -0
  83. package/dist/atoms/composed/Toast/Toast.d.ts +40 -0
  84. package/dist/atoms/composed/Toast/Toast.d.ts.map +1 -0
  85. package/dist/atoms/composed/Toast/index.d.ts +2 -0
  86. package/dist/atoms/composed/Toast/index.d.ts.map +1 -0
  87. package/dist/atoms/composed/Tooltip/Tooltip.d.ts +16 -0
  88. package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +1 -0
  89. package/dist/atoms/composed/Tooltip/index.d.ts +2 -0
  90. package/dist/atoms/composed/Tooltip/index.d.ts.map +1 -0
  91. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +8 -0
  92. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +1 -0
  93. package/dist/atoms/composed/UserAvatar/index.d.ts +2 -0
  94. package/dist/atoms/composed/UserAvatar/index.d.ts.map +1 -0
  95. package/dist/atoms/composed/UserMenu/UserMenu.d.ts +8 -0
  96. package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +1 -0
  97. package/dist/atoms/composed/UserMenu/index.d.ts +2 -0
  98. package/dist/atoms/composed/UserMenu/index.d.ts.map +1 -0
  99. package/dist/atoms/composed/index.d.ts +26 -0
  100. package/dist/atoms/composed/index.d.ts.map +1 -0
  101. package/dist/atoms/hooks/useApi.d.ts +25 -0
  102. package/dist/atoms/hooks/useApi.d.ts.map +1 -0
  103. package/dist/atoms/hooks/useHealth.d.ts +19 -0
  104. package/dist/atoms/hooks/useHealth.d.ts.map +1 -0
  105. package/dist/atoms/index.d.ts +9 -0
  106. package/dist/atoms/index.d.ts.map +1 -0
  107. package/dist/atoms/services/api/client.d.ts +20 -0
  108. package/dist/atoms/services/api/client.d.ts.map +1 -0
  109. package/dist/atoms/services/auth-service.d.ts +24 -0
  110. package/dist/atoms/services/auth-service.d.ts.map +1 -0
  111. package/dist/atoms/services/health.d.ts +7 -0
  112. package/dist/atoms/services/health.d.ts.map +1 -0
  113. package/dist/atoms/services/index.d.ts +4 -0
  114. package/dist/atoms/services/index.d.ts.map +1 -0
  115. package/dist/atoms/shared/config/constants.d.ts +15 -0
  116. package/dist/atoms/shared/config/constants.d.ts.map +1 -0
  117. package/dist/atoms/shared/config/dashboard-sizes.d.ts +83 -0
  118. package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +1 -0
  119. package/dist/atoms/shared/config/environment.d.ts +10 -0
  120. package/dist/atoms/shared/config/environment.d.ts.map +1 -0
  121. package/dist/atoms/shared/index.d.ts +4 -0
  122. package/dist/atoms/shared/index.d.ts.map +1 -0
  123. package/dist/atoms/types/auth.d.ts +56 -0
  124. package/dist/atoms/types/auth.d.ts.map +1 -0
  125. package/dist/atoms/types/entity-config.d.ts +117 -0
  126. package/dist/atoms/types/entity-config.d.ts.map +1 -0
  127. package/dist/atoms/types/generated.d.ts +1469 -0
  128. package/dist/atoms/types/generated.d.ts.map +1 -0
  129. package/dist/atoms/types/index.d.ts +6 -0
  130. package/dist/atoms/types/index.d.ts.map +1 -0
  131. package/dist/atoms/types/loading.d.ts +26 -0
  132. package/dist/atoms/types/loading.d.ts.map +1 -0
  133. package/dist/atoms/types/navigation.d.ts +30 -0
  134. package/dist/atoms/types/navigation.d.ts.map +1 -0
  135. package/dist/atoms/ui/Badge.d.ts +10 -0
  136. package/dist/atoms/ui/Badge.d.ts.map +1 -0
  137. package/dist/atoms/ui/ErrorBoundary.d.ts +18 -0
  138. package/dist/atoms/ui/ErrorBoundary.d.ts.map +1 -0
  139. package/dist/atoms/ui/Select.d.ts +28 -0
  140. package/dist/atoms/ui/Select.d.ts.map +1 -0
  141. package/dist/atoms/ui/Switch.d.ts +9 -0
  142. package/dist/atoms/ui/Switch.d.ts.map +1 -0
  143. package/dist/atoms/ui/Tabs.d.ts +30 -0
  144. package/dist/atoms/ui/Tabs.d.ts.map +1 -0
  145. package/dist/atoms/ui/avatar.d.ts +7 -0
  146. package/dist/atoms/ui/avatar.d.ts.map +1 -0
  147. package/dist/atoms/ui/button.d.ts +14 -0
  148. package/dist/atoms/ui/button.d.ts.map +1 -0
  149. package/dist/atoms/ui/card.d.ts +12 -0
  150. package/dist/atoms/ui/card.d.ts.map +1 -0
  151. package/dist/atoms/ui/dropdown-menu.d.ts +28 -0
  152. package/dist/atoms/ui/dropdown-menu.d.ts.map +1 -0
  153. package/dist/atoms/ui/index.d.ts +15 -0
  154. package/dist/atoms/ui/index.d.ts.map +1 -0
  155. package/dist/atoms/ui/input.d.ts +5 -0
  156. package/dist/atoms/ui/input.d.ts.map +1 -0
  157. package/dist/atoms/ui/label.d.ts +6 -0
  158. package/dist/atoms/ui/label.d.ts.map +1 -0
  159. package/dist/atoms/ui/skeleton.d.ts +3 -0
  160. package/dist/atoms/ui/skeleton.d.ts.map +1 -0
  161. package/dist/atoms/ui/spinner.d.ts +14 -0
  162. package/dist/atoms/ui/spinner.d.ts.map +1 -0
  163. package/dist/atoms/ui/table.d.ts +11 -0
  164. package/dist/atoms/ui/table.d.ts.map +1 -0
  165. package/dist/atoms/utils/animations.d.ts +65 -0
  166. package/dist/atoms/utils/animations.d.ts.map +1 -0
  167. package/dist/atoms/utils/icon-resolver.d.ts +72 -0
  168. package/dist/atoms/utils/icon-resolver.d.ts.map +1 -0
  169. package/dist/atoms/utils/metric-engine.d.ts +30 -0
  170. package/dist/atoms/utils/metric-engine.d.ts.map +1 -0
  171. package/dist/atoms/utils/tooltip-helpers.d.ts +71 -0
  172. package/dist/atoms/utils/tooltip-helpers.d.ts.map +1 -0
  173. package/dist/atoms/utils/utils.d.ts +6 -0
  174. package/dist/atoms/utils/utils.d.ts.map +1 -0
  175. package/dist/features/auth/components/LoginForm.d.ts +2 -0
  176. package/dist/features/auth/components/LoginForm.d.ts.map +1 -0
  177. package/dist/features/auth/components/LogoutButton.d.ts +2 -0
  178. package/dist/features/auth/components/LogoutButton.d.ts.map +1 -0
  179. package/dist/features/auth/components/ProtectedRoute.d.ts +10 -0
  180. package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -0
  181. package/dist/features/auth/components/index.d.ts +4 -0
  182. package/dist/features/auth/components/index.d.ts.map +1 -0
  183. package/dist/features/auth/hooks/index.d.ts +3 -0
  184. package/dist/features/auth/hooks/index.d.ts.map +1 -0
  185. package/dist/features/auth/hooks/useAuth.d.ts +10 -0
  186. package/dist/features/auth/hooks/useAuth.d.ts.map +1 -0
  187. package/dist/features/auth/hooks/usePermissions.d.ts +13 -0
  188. package/dist/features/auth/hooks/usePermissions.d.ts.map +1 -0
  189. package/dist/features/auth/index.d.ts +3 -0
  190. package/dist/features/auth/index.d.ts.map +1 -0
  191. package/dist/features/index.d.ts +2 -0
  192. package/dist/features/index.d.ts.map +1 -0
  193. package/dist/frontend-patterns.css +1 -1
  194. package/dist/index.d.ts +10 -0
  195. package/dist/index.d.ts.map +1 -0
  196. package/dist/index.es.js +131 -1658
  197. package/dist/index.es.js.map +1 -1
  198. package/dist/index.js +131 -1658
  199. package/dist/index.js.map +1 -1
  200. package/dist/molecules/forms/FormGroup.d.ts +17 -0
  201. package/dist/molecules/forms/FormGroup.d.ts.map +1 -0
  202. package/dist/molecules/forms/SearchInput.d.ts +36 -0
  203. package/dist/molecules/forms/SearchInput.d.ts.map +1 -0
  204. package/dist/molecules/forms/index.d.ts +3 -0
  205. package/dist/molecules/forms/index.d.ts.map +1 -0
  206. package/dist/molecules/index.d.ts +4 -0
  207. package/dist/molecules/index.d.ts.map +1 -0
  208. package/dist/molecules/layout/AppHeader/AppHeader.d.ts +7 -0
  209. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +1 -0
  210. package/dist/molecules/layout/AppHeader/index.d.ts +2 -0
  211. package/dist/molecules/layout/AppHeader/index.d.ts.map +1 -0
  212. package/dist/molecules/layout/AppLayout.d.ts +2 -0
  213. package/dist/molecules/layout/AppLayout.d.ts.map +1 -0
  214. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +16 -0
  215. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +1 -0
  216. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +2 -0
  217. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +1 -0
  218. package/dist/molecules/layout/NavigationContext.d.ts +15 -0
  219. package/dist/molecules/layout/NavigationContext.d.ts.map +1 -0
  220. package/dist/molecules/layout/PageTemplate.d.ts +19 -0
  221. package/dist/molecules/layout/PageTemplate.d.ts.map +1 -0
  222. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +24 -0
  223. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +1 -0
  224. package/dist/molecules/layout/SectionHeader/index.d.ts +2 -0
  225. package/dist/molecules/layout/SectionHeader/index.d.ts.map +1 -0
  226. package/dist/molecules/layout/ShowcaseSection.d.ts +22 -0
  227. package/dist/molecules/layout/ShowcaseSection.d.ts.map +1 -0
  228. package/dist/molecules/layout/Sidebar.d.ts +6 -0
  229. package/dist/molecules/layout/Sidebar.d.ts.map +1 -0
  230. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +15 -0
  231. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -0
  232. package/dist/molecules/layout/SidebarButton/index.d.ts +2 -0
  233. package/dist/molecules/layout/SidebarButton/index.d.ts.map +1 -0
  234. package/dist/molecules/layout/SidebarContext.d.ts +12 -0
  235. package/dist/molecules/layout/SidebarContext.d.ts.map +1 -0
  236. package/dist/molecules/layout/index.d.ts +11 -0
  237. package/dist/molecules/layout/index.d.ts.map +1 -0
  238. package/dist/molecules/navigation/NavMenu.d.ts +20 -0
  239. package/dist/molecules/navigation/NavMenu.d.ts.map +1 -0
  240. package/dist/molecules/navigation/Pagination.d.ts +14 -0
  241. package/dist/molecules/navigation/Pagination.d.ts.map +1 -0
  242. package/dist/molecules/navigation/index.d.ts +3 -0
  243. package/dist/molecules/navigation/index.d.ts.map +1 -0
  244. package/dist/organisms/index.d.ts +2 -0
  245. package/dist/organisms/index.d.ts.map +1 -0
  246. package/dist/organisms/showcase/ComponentShowcasePage.d.ts +3 -0
  247. package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +1 -0
  248. package/dist/templates/AuthTemplate.d.ts +68 -0
  249. package/dist/templates/AuthTemplate.d.ts.map +1 -0
  250. package/dist/templates/ComponentShowcaseTemplate.d.ts +53 -0
  251. package/dist/templates/ComponentShowcaseTemplate.d.ts.map +1 -0
  252. package/dist/templates/DashboardTemplate.d.ts +62 -0
  253. package/dist/templates/DashboardTemplate.d.ts.map +1 -0
  254. package/dist/templates/DataTemplate.d.ts +78 -0
  255. package/dist/templates/DataTemplate.d.ts.map +1 -0
  256. package/dist/templates/admin/AdminCRUDTemplate.d.ts +105 -0
  257. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +1 -0
  258. package/dist/templates/admin/AdminDashboardTemplate.d.ts +89 -0
  259. package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +1 -0
  260. package/dist/templates/admin/AdminDetailTemplate.d.ts +132 -0
  261. package/dist/templates/admin/AdminDetailTemplate.d.ts.map +1 -0
  262. package/dist/templates/admin/index.d.ts +4 -0
  263. package/dist/templates/admin/index.d.ts.map +1 -0
  264. package/dist/templates/factory.d.ts +29 -0
  265. package/dist/templates/factory.d.ts.map +1 -0
  266. package/dist/templates/index.d.ts +7 -0
  267. package/dist/templates/index.d.ts.map +1 -0
  268. package/package.json +3 -2
  269. package/src/__tests__/atoms/composed/databadge.test.tsx +106 -0
  270. package/src/__tests__/atoms/composed/statcard.test.tsx +133 -0
  271. package/src/__tests__/atoms/utils/icon-resolver.test.tsx +140 -0
  272. package/src/atoms/types/index.ts +1 -0
  273. package/src/atoms/types/navigation.ts +43 -0
  274. package/src/atoms/utils/icon-resolver.tsx +54 -0
  275. package/src/atoms/utils/utils.ts +3 -2
  276. package/src/molecules/layout/NavigationContext.tsx +63 -0
  277. package/src/molecules/layout/Sidebar.tsx +10 -31
  278. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +32 -10
  279. package/src/molecules/layout/index.ts +3 -1
  280. package/src/organisms/index.ts +2 -1
  281. package/src/templates/factory.tsx +14 -7
  282. package/src/templates/index.ts +2 -1
@@ -0,0 +1,133 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { render, screen, userEvent } from '../../utils'
3
+ import { StatCard } from '../../../atoms/composed/StatCard'
4
+
5
+ describe('StatCard (Composed)', () => {
6
+ it('renders basic stat card with title and value', () => {
7
+ render(<StatCard title="Total Users" value="1,234" />)
8
+
9
+ expect(screen.getByText('Total Users')).toBeInTheDocument()
10
+ expect(screen.getByText('1,234')).toBeInTheDocument()
11
+ })
12
+
13
+ it('renders with subtitle', () => {
14
+ render(
15
+ <StatCard
16
+ title="Revenue"
17
+ value="$45,678"
18
+ subtitle="+12% from last month"
19
+ />
20
+ )
21
+
22
+ expect(screen.getByText('Revenue')).toBeInTheDocument()
23
+ expect(screen.getByText('$45,678')).toBeInTheDocument()
24
+ expect(screen.getByText('+12% from last month')).toBeInTheDocument()
25
+ })
26
+
27
+ it('renders with icon', () => {
28
+ render(
29
+ <StatCard
30
+ title="Orders"
31
+ value="567"
32
+ icon="ShoppingCart"
33
+ />
34
+ )
35
+
36
+ expect(screen.getByText('Orders')).toBeInTheDocument()
37
+ expect(screen.getByText('567')).toBeInTheDocument()
38
+ // Icon should be rendered (we can't easily test the actual icon, but we can test it doesn't crash)
39
+ })
40
+
41
+ it('applies category theming', () => {
42
+ render(<StatCard title="Test" value="123" category={3} />)
43
+
44
+ const card = screen.getByText('Test').closest('[data-component-name="StatCard"]')
45
+ expect(card).toBeInTheDocument()
46
+ expect(card).toHaveAttribute('data-component-name', 'StatCard')
47
+ })
48
+
49
+ it('handles click events', async () => {
50
+ const user = userEvent.setup()
51
+ const handleClick = vi.fn()
52
+
53
+ render(
54
+ <StatCard
55
+ title="Clickable Card"
56
+ value="999"
57
+ onClick={handleClick}
58
+ />
59
+ )
60
+
61
+ const card = screen.getByText('Clickable Card').closest('[role="button"]')
62
+ expect(card).toBeInTheDocument()
63
+
64
+ await user.click(card!)
65
+ expect(handleClick).toHaveBeenCalledOnce()
66
+ })
67
+
68
+ it('applies hover effects when clickable', () => {
69
+ render(
70
+ <StatCard
71
+ title="Hoverable"
72
+ value="555"
73
+ onClick={() => {}}
74
+ />
75
+ )
76
+
77
+ const card = screen.getByText('Hoverable').closest('[role="button"]')
78
+ expect(card).toHaveClass('cursor-pointer')
79
+ expect(card).toHaveClass('hover:shadow-md')
80
+ })
81
+
82
+ it('does not apply click styles when not clickable', () => {
83
+ render(<StatCard title="Static Card" value="111" />)
84
+
85
+ const card = screen.getByText('Static Card').closest('div')
86
+ expect(card).not.toHaveAttribute('role', 'button')
87
+ expect(card).not.toHaveClass('cursor-pointer')
88
+ })
89
+
90
+ it('displays numbers as provided', () => {
91
+ render(<StatCard title="Big Number" value={1234567} />)
92
+
93
+ expect(screen.getByText('1234567')).toBeInTheDocument()
94
+ })
95
+
96
+ it('handles string and number values', () => {
97
+ const { rerender } = render(<StatCard title="String" value="Custom Text" />)
98
+ expect(screen.getByText('Custom Text')).toBeInTheDocument()
99
+
100
+ rerender(<StatCard title="Number" value={42} />)
101
+ expect(screen.getByText('42')).toBeInTheDocument()
102
+ })
103
+
104
+ it('applies custom className', () => {
105
+ render(
106
+ <StatCard
107
+ title="Custom"
108
+ value="123"
109
+ className="custom-stat-card"
110
+ />
111
+ )
112
+
113
+ const card = screen.getByText('Custom').closest('.custom-stat-card')
114
+ expect(card).toBeInTheDocument()
115
+ })
116
+
117
+ it('renders loading state', () => {
118
+ render(<StatCard title="Loading" value="..." isLoading={true} />)
119
+
120
+ const card = screen.getByText('Loading').closest('[data-component-name="StatCard"]')
121
+ expect(card).toBeInTheDocument()
122
+ // Check for skeleton elements in loading state
123
+ const skeletons = card?.querySelectorAll('.animate-pulse')
124
+ expect(skeletons?.length).toBeGreaterThan(0)
125
+ })
126
+
127
+ it('renders with data component name', () => {
128
+ render(<StatCard title="Test Card" value="123" />)
129
+
130
+ const card = screen.getByText('Test Card').closest('[data-component-name="StatCard"]')
131
+ expect(card).toHaveAttribute('data-component-name', 'StatCard')
132
+ })
133
+ })
@@ -0,0 +1,140 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { render, screen } from '../../utils'
3
+ import { Icon, getIcon, iconMap } from '../../../atoms/utils/icon-resolver'
4
+
5
+ // Mock console.warn to avoid noise in tests
6
+ const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
7
+
8
+ describe('Icon Resolver', () => {
9
+ afterEach(() => {
10
+ consoleSpy.mockClear()
11
+ })
12
+
13
+ describe('Icon component', () => {
14
+ it('renders valid icon', () => {
15
+ render(<Icon name="Home" data-testid="home-icon" />)
16
+
17
+ const icon = screen.getByTestId('home-icon')
18
+ expect(icon).toBeInTheDocument()
19
+ })
20
+
21
+ it('applies default className', () => {
22
+ render(<Icon name="Users" data-testid="users-icon" />)
23
+
24
+ const icon = screen.getByTestId('users-icon')
25
+ expect(icon).toHaveClass('w-5', 'h-5')
26
+ })
27
+
28
+ it('applies custom className', () => {
29
+ render(<Icon name="Settings" className="w-8 h-8 text-blue-500" data-testid="settings-icon" />)
30
+
31
+ const icon = screen.getByTestId('settings-icon')
32
+ expect(icon).toHaveClass('w-8', 'h-8', 'text-blue-500')
33
+ })
34
+
35
+ it('applies size prop', () => {
36
+ render(<Icon name="Search" size={24} data-testid="search-icon" />)
37
+
38
+ const icon = screen.getByTestId('search-icon')
39
+ expect(icon).toHaveAttribute('width', '24')
40
+ expect(icon).toHaveAttribute('height', '24')
41
+ })
42
+
43
+ it('renders fallback icon for invalid name', () => {
44
+ render(<Icon name="InvalidIcon" data-testid="fallback-icon" />)
45
+
46
+ const icon = screen.getByTestId('fallback-icon')
47
+ expect(icon).toBeInTheDocument()
48
+ expect(consoleSpy).toHaveBeenCalledWith('Icon "InvalidIcon" not found. Using default Menu icon.')
49
+ })
50
+
51
+ it('handles undefined icon name gracefully', () => {
52
+ render(<Icon name={undefined as any} data-testid="undefined-icon" />)
53
+
54
+ const icon = screen.getByTestId('undefined-icon')
55
+ expect(icon).toBeInTheDocument()
56
+ expect(consoleSpy).toHaveBeenCalled()
57
+ })
58
+ })
59
+
60
+ describe('getIcon function', () => {
61
+ it('returns icon component for valid name', () => {
62
+ const HomeIcon = getIcon('Home')
63
+ expect(HomeIcon).toBeDefined()
64
+ expect(typeof HomeIcon).toBe('function')
65
+ })
66
+
67
+ it('returns Menu icon for invalid name', () => {
68
+ const InvalidIcon = getIcon('InvalidIcon' as any)
69
+ expect(InvalidIcon).toBeDefined()
70
+ expect(typeof InvalidIcon).toBe('function')
71
+ })
72
+
73
+ it('returns Menu icon for undefined name', () => {
74
+ const UndefinedIcon = getIcon(undefined as any)
75
+ expect(UndefinedIcon).toBeDefined()
76
+ })
77
+ })
78
+
79
+ describe('iconMap', () => {
80
+ it('contains expected common icons', () => {
81
+ const expectedIcons = [
82
+ 'Home', 'Users', 'Settings', 'Search', 'Menu',
83
+ 'ChevronDown', 'ChevronRight', 'X', 'Plus',
84
+ 'ShoppingCart', 'Calendar', 'Mail', 'Phone'
85
+ ]
86
+
87
+ expectedIcons.forEach(iconName => {
88
+ expect(iconMap).toHaveProperty(iconName)
89
+ expect(typeof iconMap[iconName]).toBe('function')
90
+ })
91
+ })
92
+
93
+ it('has reasonable number of icons', () => {
94
+ const iconCount = Object.keys(iconMap).length
95
+ expect(iconCount).toBeGreaterThan(30)
96
+ expect(iconCount).toBeLessThan(100) // Keep it manageable
97
+ })
98
+
99
+ it('all icon map values are functions', () => {
100
+ Object.values(iconMap).forEach(iconComponent => {
101
+ expect(typeof iconComponent).toBe('function')
102
+ })
103
+ })
104
+ })
105
+
106
+ describe('Icon rendering variations', () => {
107
+ const testIcons = ['Home', 'Users', 'Settings', 'Search', 'Bell']
108
+
109
+ testIcons.forEach(iconName => {
110
+ it(`renders ${iconName} icon without errors`, () => {
111
+ render(<Icon name={iconName} data-testid={`${iconName.toLowerCase()}-icon`} />)
112
+
113
+ const icon = screen.getByTestId(`${iconName.toLowerCase()}-icon`)
114
+ expect(icon).toBeInTheDocument()
115
+ })
116
+ })
117
+ })
118
+
119
+ describe('Integration with real icon names', () => {
120
+ it('renders navigation icons correctly', () => {
121
+ const navIcons = ['Home', 'Users', 'Settings', 'FileText', 'BarChart']
122
+
123
+ navIcons.forEach(iconName => {
124
+ const { unmount } = render(<Icon name={iconName} />)
125
+ // If it renders without throwing, it's valid
126
+ unmount()
127
+ })
128
+ })
129
+
130
+ it('renders action icons correctly', () => {
131
+ const actionIcons = ['Plus', 'Edit', 'Trash2', 'Save', 'Download']
132
+
133
+ actionIcons.forEach(iconName => {
134
+ const { unmount } = render(<Icon name={iconName} />)
135
+ // If it renders without throwing, it's valid
136
+ unmount()
137
+ })
138
+ })
139
+ })
140
+ })
@@ -2,4 +2,5 @@
2
2
  export * from './generated';
3
3
  export * from './loading';
4
4
  export * from './auth';
5
+ export * from './navigation';
5
6
  export * from './entity-config';
@@ -0,0 +1,43 @@
1
+ // Navigation configuration types for createReactApp factory
2
+
3
+ export type IconName =
4
+ | 'Palette' | 'Menu' | 'X' | 'Shield' | 'Users' | 'BarChart3'
5
+ | 'Database' | 'TrendingUp' | 'Layout' | 'Home' | 'Settings'
6
+ | 'Bell' | 'Search' | 'Plus' | 'Edit' | 'Trash2' | 'Eye'
7
+ | 'Download' | 'Upload' | 'Share' | 'Lock' | 'Unlock'
8
+ | 'Mail' | 'Phone' | 'Calendar' | 'Clock' | 'MapPin'
9
+ | 'Star' | 'Heart' | 'Bookmark' | 'Tag' | 'Flag'
10
+ | 'File' | 'Folder' | 'Image' | 'Video' | 'Music'
11
+ | 'ChevronRight' | 'ChevronDown' | 'ChevronLeft' | 'ChevronUp'
12
+ | 'ArrowRight' | 'ArrowLeft' | 'ArrowUp' | 'ArrowDown'
13
+ | 'Check' | 'AlertCircle' | 'Info' | 'HelpCircle'
14
+
15
+ export interface NavigationItem {
16
+ /** Unique identifier for the navigation item */
17
+ value: string
18
+ /** Display label for the navigation item */
19
+ label: string
20
+ /** Path to navigate to when clicked */
21
+ path: string
22
+ /** Icon name from Lucide React icons */
23
+ icon: IconName
24
+ /** Optional category for color theming (1-8) */
25
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
26
+ /** Optional badge text/count to display */
27
+ badge?: string | number
28
+ /** Whether this item is disabled */
29
+ disabled?: boolean
30
+ /** Child navigation items for nested menus */
31
+ children?: NavigationItem[]
32
+ }
33
+
34
+ export interface NavigationConfig {
35
+ /** Main navigation items */
36
+ items: NavigationItem[]
37
+ /** Whether to show the default showcase navigation as fallback */
38
+ showDefaultNavigation?: boolean
39
+ /** Custom logo or title for the sidebar header */
40
+ logo?: string
41
+ /** Whether the sidebar starts expanded */
42
+ defaultExpanded?: boolean
43
+ }
@@ -0,0 +1,54 @@
1
+ import {
2
+ Palette, Menu, X, Shield, Users, BarChart3, Database, TrendingUp, Layout,
3
+ Home, Settings, Bell, Search, Plus, Edit, Trash2, Eye, Download, Upload,
4
+ Share, Lock, Unlock, Mail, Phone, Calendar, Clock, MapPin, Star, Heart,
5
+ Bookmark, Tag, Flag, File, Folder, Image, Video, Music, ChevronRight,
6
+ ChevronDown, ChevronLeft, ChevronUp, ArrowRight, ArrowLeft, ArrowUp,
7
+ ArrowDown, Check, AlertCircle, Info, HelpCircle
8
+ } from 'lucide-react'
9
+ import type { IconName } from '../types'
10
+
11
+ // Map of icon names to Lucide React components
12
+ export const iconMap = {
13
+ Palette, Menu, X, Shield, Users, BarChart3, Database, TrendingUp, Layout,
14
+ Home, Settings, Bell, Search, Plus, Edit, Trash2, Eye, Download, Upload,
15
+ Share, Lock, Unlock, Mail, Phone, Calendar, Clock, MapPin, Star, Heart,
16
+ Bookmark, Tag, Flag, File, Folder, Image, Video, Music, ChevronRight,
17
+ ChevronDown, ChevronLeft, ChevronUp, ArrowRight, ArrowLeft, ArrowUp,
18
+ ArrowDown, Check, AlertCircle, Info, HelpCircle
19
+ }
20
+
21
+ interface IconProps {
22
+ name: IconName
23
+ className?: string
24
+ size?: number
25
+ [key: string]: any // Allow any additional props to be passed through
26
+ }
27
+
28
+ /**
29
+ * Resolves an icon name to the corresponding Lucide React component
30
+ */
31
+ export const Icon = ({ name, className = "w-5 h-5", size, ...props }: IconProps) => {
32
+ const IconComponent = iconMap[name]
33
+
34
+ if (!IconComponent) {
35
+ console.warn(`Icon "${name}" not found. Using default Menu icon.`)
36
+ return <Menu className={className} size={size} {...props} />
37
+ }
38
+
39
+ return <IconComponent className={className} size={size} {...props} />
40
+ }
41
+
42
+ /**
43
+ * Get the icon component directly for custom rendering
44
+ */
45
+ export const getIcon = (name: IconName) => {
46
+ return iconMap[name] || Menu
47
+ }
48
+
49
+ /**
50
+ * Check if an icon name is valid
51
+ */
52
+ export const isValidIcon = (name: string): name is IconName => {
53
+ return name in iconMap
54
+ }
@@ -5,6 +5,7 @@ export function cn(...inputs: ClassValue[]) {
5
5
  return twMerge(clsx(inputs))
6
6
  }
7
7
 
8
- // Re-export tooltip helpers
8
+ // Re-export utilities
9
9
  export * from './tooltip-helpers';
10
- export * from './metric-engine';
10
+ export * from './metric-engine';
11
+ export { Icon, getIcon, isValidIcon } from './icon-resolver';
@@ -0,0 +1,63 @@
1
+ import React, { createContext, useContext, type ReactNode } from 'react'
2
+ import type { NavigationConfig, NavigationItem } from '../../atoms/types'
3
+
4
+ interface NavigationContextType {
5
+ navigation: NavigationConfig
6
+ setNavigation: (config: NavigationConfig) => void
7
+ }
8
+
9
+ const NavigationContext = createContext<NavigationContextType | undefined>(undefined)
10
+
11
+ interface NavigationProviderProps {
12
+ children: ReactNode
13
+ initialNavigation?: NavigationConfig
14
+ }
15
+
16
+ // Default showcase navigation for backwards compatibility
17
+ const defaultShowcaseNavigation: NavigationItem[] = [
18
+ { value: 'showcase', label: 'Showcase', icon: 'Palette', path: '/showcase', category: 5 },
19
+ { value: 'admin-dashboard', label: 'Admin Dashboard', icon: 'Shield', path: '/admin/dashboard', category: 2 },
20
+ { value: 'admin-users', label: 'User Management', icon: 'Users', path: '/admin/users', category: 3 },
21
+ { value: 'admin-sales', label: 'Sales Dashboard', icon: 'TrendingUp', path: '/admin/sales', category: 4 },
22
+ { value: 'entity-performance', label: 'Performance Dashboard', icon: 'BarChart3', path: '/entity/performance', category: 6 },
23
+ { value: 'entity-management', label: 'Entity Management', icon: 'Database', path: '/entity/management', category: 7 },
24
+ { value: 'entity-template', label: 'Template Example', icon: 'Layout', path: '/entity/template-example', category: 1 }
25
+ ]
26
+
27
+ export const NavigationProvider = ({ children, initialNavigation }: NavigationProviderProps) => {
28
+ const [navigation, setNavigation] = React.useState<NavigationConfig>(
29
+ initialNavigation || {
30
+ items: defaultShowcaseNavigation,
31
+ showDefaultNavigation: true,
32
+ defaultExpanded: true
33
+ }
34
+ )
35
+
36
+ return (
37
+ <NavigationContext.Provider value={{ navigation, setNavigation }}>
38
+ {children}
39
+ </NavigationContext.Provider>
40
+ )
41
+ }
42
+
43
+ export const useNavigation = () => {
44
+ const context = useContext(NavigationContext)
45
+ if (context === undefined) {
46
+ throw new Error('useNavigation must be used within a NavigationProvider')
47
+ }
48
+ return context
49
+ }
50
+
51
+ // Helper to get navigation items with fallback
52
+ export const getNavigationItems = (config: NavigationConfig): NavigationItem[] => {
53
+ if (config.items.length > 0) {
54
+ return config.items
55
+ }
56
+
57
+ // Fallback to showcase navigation if no items provided
58
+ if (config.showDefaultNavigation !== false) {
59
+ return defaultShowcaseNavigation
60
+ }
61
+
62
+ return []
63
+ }
@@ -1,27 +1,10 @@
1
- import React from 'react';
2
1
  import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
3
- import { cn } from '../../atoms/utils/utils';
2
+ import { cn, Icon } from '../../atoms/utils/utils';
4
3
  import { useSidebar } from './SidebarContext';
4
+ import { useNavigation, getNavigationItems } from './NavigationContext';
5
5
  import { SidebarButton } from './SidebarButton';
6
- import {
7
- Palette,
8
- Menu,
9
- X,
10
- Shield,
11
- Users,
12
- BarChart3,
13
- Database,
14
- TrendingUp,
15
- Layout
16
- } from 'lucide-react';
17
-
18
- interface SidebarItem {
19
- value: string;
20
- label: string;
21
- icon: React.ReactNode;
22
- path: string;
23
- category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
24
- }
6
+ import { Menu, X } from 'lucide-react';
7
+ import type { NavigationItem } from '../../atoms/types';
25
8
 
26
9
  interface SidebarProps {
27
10
  className?: string;
@@ -29,19 +12,13 @@ interface SidebarProps {
29
12
 
30
13
  export const Sidebar = ({ className }: SidebarProps) => {
31
14
  const { isExpanded, toggleSidebar } = useSidebar();
15
+ const { navigation } = useNavigation();
32
16
  const location = useLocation();
33
17
  const navigate = useNavigate();
34
18
  const [searchParams] = useSearchParams();
35
19
 
36
- const items: SidebarItem[] = [
37
- { value: 'showcase', label: 'Showcase', icon: <Palette className="w-5 h-5" />, path: '/showcase', category: 5 },
38
- { value: 'admin-dashboard', label: 'Admin Dashboard', icon: <Shield className="w-5 h-5" />, path: '/admin/dashboard', category: 2 },
39
- { value: 'admin-users', label: 'User Management', icon: <Users className="w-5 h-5" />, path: '/admin/users', category: 3 },
40
- { value: 'admin-sales', label: 'Sales Dashboard', icon: <TrendingUp className="w-5 h-5" />, path: '/admin/sales', category: 4 },
41
- { value: 'entity-performance', label: 'Performance Dashboard', icon: <BarChart3 className="w-5 h-5" />, path: '/entity/performance', category: 6 },
42
- { value: 'entity-management', label: 'Entity Management', icon: <Database className="w-5 h-5" />, path: '/entity/management', category: 7 },
43
- { value: 'entity-template', label: 'Template Example', icon: <Layout className="w-5 h-5" />, path: '/entity/template-example', category: 1 }
44
- ];
20
+ // Get navigation items from context
21
+ const items: NavigationItem[] = getNavigationItems(navigation);
45
22
 
46
23
  const handleNavigation = (path: string) => {
47
24
  if (path.includes('?')) {
@@ -108,12 +85,14 @@ export const Sidebar = ({ className }: SidebarProps) => {
108
85
  return (
109
86
  <SidebarButton
110
87
  key={item.value}
111
- icon={item.icon}
88
+ icon={<Icon name={item.icon} className="w-5 h-5" />}
112
89
  label={item.label}
113
90
  active={isActive}
114
91
  category={item.category}
115
92
  expanded={isExpanded}
116
93
  onClick={() => handleNavigation(item.path)}
94
+ badge={item.badge}
95
+ disabled={item.disabled}
117
96
  />
118
97
  );
119
98
  })}
@@ -10,6 +10,8 @@ interface SidebarButtonProps {
10
10
  expanded?: boolean;
11
11
  onClick?: () => void;
12
12
  className?: string;
13
+ badge?: string | number;
14
+ disabled?: boolean;
13
15
  }
14
16
 
15
17
  export const SidebarButton: React.FC<SidebarButtonProps> = ({
@@ -19,12 +21,15 @@ export const SidebarButton: React.FC<SidebarButtonProps> = ({
19
21
  category = 1,
20
22
  expanded = false,
21
23
  onClick,
22
- className
24
+ className,
25
+ badge,
26
+ disabled = false
23
27
  }) => {
24
28
  return (
25
29
  <Button
26
30
  variant={active ? "secondary" : "ghost"}
27
31
  onClick={onClick}
32
+ disabled={disabled}
28
33
  tooltip={!expanded ? label : undefined}
29
34
  className={cn(
30
35
  "relative w-full justify-start gap-3 h-12",
@@ -74,8 +79,15 @@ export const SidebarButton: React.FC<SidebarButtonProps> = ({
74
79
  {label}
75
80
  </span>
76
81
 
77
- {/* Active indicator dot */}
78
- {active && (
82
+ {/* Badge or Active indicator */}
83
+ {badge ? (
84
+ <span className={cn(
85
+ "px-2 py-0.5 text-xs font-medium rounded-full flex-shrink-0",
86
+ "bg-primary/10 text-primary"
87
+ )}>
88
+ {badge}
89
+ </span>
90
+ ) : active && (
79
91
  <div className={cn(
80
92
  "w-2 h-2 rounded-full flex-shrink-0",
81
93
  `bg-category-${category}`
@@ -84,14 +96,24 @@ export const SidebarButton: React.FC<SidebarButtonProps> = ({
84
96
  </>
85
97
  )}
86
98
 
87
- {/* Collapsed active indicator */}
88
- {!expanded && active && (
99
+ {/* Collapsed indicators */}
100
+ {!expanded && (badge || active) && (
89
101
  <div className="absolute -top-1 -right-1">
90
- <div className={cn(
91
- "w-2.5 h-2.5 rounded-full",
92
- `bg-category-${category}`,
93
- "ring-2 ring-background"
94
- )} />
102
+ {badge ? (
103
+ <span className={cn(
104
+ "px-1.5 py-0.5 text-xs font-bold rounded-full",
105
+ "bg-primary text-primary-foreground",
106
+ "ring-2 ring-background"
107
+ )}>
108
+ {badge}
109
+ </span>
110
+ ) : active && (
111
+ <div className={cn(
112
+ "w-2.5 h-2.5 rounded-full",
113
+ `bg-category-${category}`,
114
+ "ring-2 ring-background"
115
+ )} />
116
+ )}
95
117
  </div>
96
118
  )}
97
119
  </Button>
@@ -5,4 +5,6 @@ export { DashboardWithSidePanel } from './DashboardWithSidePanel';
5
5
  export { SectionHeader } from './SectionHeader';
6
6
  export { SidebarButton } from './SidebarButton';
7
7
  export { AppHeader } from './AppHeader';
8
- export { SidebarProvider, useSidebar } from './SidebarContext';
8
+ export { SidebarProvider, useSidebar } from './SidebarContext';
9
+ export { NavigationProvider, useNavigation, getNavigationItems } from './NavigationContext';
10
+ export { Sidebar } from './Sidebar';
@@ -5,4 +5,5 @@
5
5
  export { ComponentShowcasePage } from './showcase/ComponentShowcasePage';
6
6
 
7
7
  // Entity components - Cross-domain configurable panels
8
- export * from './entity';
8
+ // TODO: Fix TypeScript errors in entity components before uncommenting
9
+ // export * from './entity';
@@ -2,13 +2,12 @@ import React from 'react'
2
2
  import type { ReactNode } from 'react'
3
3
  import { createRoot } from 'react-dom/client'
4
4
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
5
- import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
6
5
  import { BrowserRouter, Routes, Route } from 'react-router-dom'
7
6
  import { AuthProvider } from '../features/auth'
8
- import { SidebarProvider } from '../molecules/layout'
7
+ import { SidebarProvider, NavigationProvider } from '../molecules/layout'
9
8
  import { AppLayout } from '../molecules/layout'
10
9
  import { DashboardTemplate } from './DashboardTemplate'
11
- import type { AuthConfig } from '../atoms/types'
10
+ import type { AuthConfig, NavigationConfig } from '../atoms/types'
12
11
 
13
12
  // App configuration interface
14
13
  export interface AppConfig {
@@ -24,6 +23,7 @@ export interface AppConfig {
24
23
  theme?: string
25
24
  darkMode?: boolean
26
25
  auth?: AuthConfig
26
+ navigation?: NavigationConfig
27
27
  customProviders?: React.ComponentType<{ children: ReactNode }>[]
28
28
  }
29
29
 
@@ -50,6 +50,7 @@ export function createReactApp(config: AppConfig | string): AppInstance {
50
50
  enableQuery = true,
51
51
  enableRouting = true,
52
52
  auth,
53
+ navigation,
53
54
  customProviders = []
54
55
  } = appConfig
55
56
 
@@ -94,9 +95,11 @@ export function createReactApp(config: AppConfig | string): AppInstance {
94
95
  // Wrap with layout providers
95
96
  if (enableRouting) {
96
97
  tree = (
97
- <SidebarProvider>
98
- {tree}
99
- </SidebarProvider>
98
+ <NavigationProvider initialNavigation={navigation}>
99
+ <SidebarProvider>
100
+ {tree}
101
+ </SidebarProvider>
102
+ </NavigationProvider>
100
103
  )
101
104
  }
102
105
 
@@ -110,7 +113,11 @@ export function createReactApp(config: AppConfig | string): AppInstance {
110
113
  tree = (
111
114
  <QueryClientProvider client={queryClient}>
112
115
  {tree}
113
- {import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} />}
116
+ {import.meta.env.DEV && (
117
+ import('@tanstack/react-query-devtools').then(({ ReactQueryDevtools }) => (
118
+ <ReactQueryDevtools initialIsOpen={false} />
119
+ ))
120
+ )}
114
121
  </QueryClientProvider>
115
122
  )
116
123
  }