@pattern-stack/frontend-patterns 0.0.4 → 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 (283) hide show
  1. package/dist/frontend-patterns.css +1 -1
  2. package/dist/index.es.js +1917 -2
  3. package/dist/index.es.js.map +1 -1
  4. package/dist/index.js +1916 -1
  5. package/dist/index.js.map +1 -1
  6. package/package.json +6 -3
  7. package/src/App.tsx +11 -1
  8. package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
  9. package/src/atoms/composed/SalesPanel/index.ts +1 -0
  10. package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
  11. package/src/atoms/composed/index.ts +1 -0
  12. package/src/atoms/types/entity-config.ts +127 -0
  13. package/src/atoms/types/index.ts +2 -1
  14. package/src/atoms/utils/metric-engine.ts +236 -0
  15. package/src/atoms/utils/utils.ts +2 -1
  16. package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
  17. package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
  18. package/src/molecules/layout/Sidebar.tsx +10 -2
  19. package/src/molecules/layout/index.ts +1 -0
  20. package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
  21. package/src/organisms/entity/EntityListPanel.tsx +339 -0
  22. package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
  23. package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
  24. package/src/organisms/entity/index.ts +4 -0
  25. package/src/organisms/index.ts +4 -1
  26. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +77 -75
  27. package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
  28. package/src/pages/AdminShowcase/index.tsx +2 -1
  29. package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
  30. package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
  31. package/src/pages/EntityShowcase/index.ts +2 -0
  32. package/src/pages/EntityTemplateExample.tsx +229 -0
  33. package/src/pages/TestEntityTemplate.tsx +40 -0
  34. package/src/pages/index.ts +2 -1
  35. package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
  36. package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
  37. package/src/templates/entity/configs/financial-config.ts +141 -0
  38. package/src/templates/entity/configs/index.ts +1 -0
  39. package/src/templates/entity/index.ts +3 -0
  40. package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
  41. package/src/templates/index.ts +3 -0
  42. package/dist/atoms/composed/Accordion/Accordion.d.ts +0 -20
  43. package/dist/atoms/composed/Accordion/Accordion.d.ts.map +0 -1
  44. package/dist/atoms/composed/Accordion/index.d.ts +0 -2
  45. package/dist/atoms/composed/Accordion/index.d.ts.map +0 -1
  46. package/dist/atoms/composed/Alert/Alert.d.ts +0 -25
  47. package/dist/atoms/composed/Alert/Alert.d.ts.map +0 -1
  48. package/dist/atoms/composed/Alert/index.d.ts +0 -2
  49. package/dist/atoms/composed/Alert/index.d.ts.map +0 -1
  50. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +0 -17
  51. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +0 -1
  52. package/dist/atoms/composed/Breadcrumb/index.d.ts +0 -2
  53. package/dist/atoms/composed/Breadcrumb/index.d.ts.map +0 -1
  54. package/dist/atoms/composed/Chart/Chart.d.ts +0 -37
  55. package/dist/atoms/composed/Chart/Chart.d.ts.map +0 -1
  56. package/dist/atoms/composed/Chart/index.d.ts +0 -3
  57. package/dist/atoms/composed/Chart/index.d.ts.map +0 -1
  58. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +0 -19
  59. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +0 -1
  60. package/dist/atoms/composed/ColorSwatch/index.d.ts +0 -2
  61. package/dist/atoms/composed/ColorSwatch/index.d.ts.map +0 -1
  62. package/dist/atoms/composed/DarkModeToggle.d.ts +0 -4
  63. package/dist/atoms/composed/DarkModeToggle.d.ts.map +0 -1
  64. package/dist/atoms/composed/DataBadge/DataBadge.d.ts +0 -13
  65. package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +0 -1
  66. package/dist/atoms/composed/DataBadge/index.d.ts +0 -2
  67. package/dist/atoms/composed/DataBadge/index.d.ts.map +0 -1
  68. package/dist/atoms/composed/DataTable/DataTable.d.ts +0 -28
  69. package/dist/atoms/composed/DataTable/DataTable.d.ts.map +0 -1
  70. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +0 -10
  71. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +0 -1
  72. package/dist/atoms/composed/DataTable/index.d.ts +0 -3
  73. package/dist/atoms/composed/DataTable/index.d.ts.map +0 -1
  74. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +0 -45
  75. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  76. package/dist/atoms/composed/DateTimePicker/index.d.ts +0 -3
  77. package/dist/atoms/composed/DateTimePicker/index.d.ts.map +0 -1
  78. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +0 -30
  79. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +0 -1
  80. package/dist/atoms/composed/DetailedCard/index.d.ts +0 -3
  81. package/dist/atoms/composed/DetailedCard/index.d.ts.map +0 -1
  82. package/dist/atoms/composed/EmptyState/EmptyState.d.ts +0 -18
  83. package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +0 -1
  84. package/dist/atoms/composed/EmptyState/index.d.ts +0 -2
  85. package/dist/atoms/composed/EmptyState/index.d.ts.map +0 -1
  86. package/dist/atoms/composed/FileUpload/FileUpload.d.ts +0 -46
  87. package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +0 -1
  88. package/dist/atoms/composed/FileUpload/index.d.ts +0 -3
  89. package/dist/atoms/composed/FileUpload/index.d.ts.map +0 -1
  90. package/dist/atoms/composed/FormField/FormField.d.ts +0 -23
  91. package/dist/atoms/composed/FormField/FormField.d.ts.map +0 -1
  92. package/dist/atoms/composed/FormField/index.d.ts +0 -2
  93. package/dist/atoms/composed/FormField/index.d.ts.map +0 -1
  94. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +0 -8
  95. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +0 -1
  96. package/dist/atoms/composed/GlobalSearch/index.d.ts +0 -2
  97. package/dist/atoms/composed/GlobalSearch/index.d.ts.map +0 -1
  98. package/dist/atoms/composed/IconBadge/IconBadge.d.ts +0 -16
  99. package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +0 -1
  100. package/dist/atoms/composed/IconBadge/index.d.ts +0 -3
  101. package/dist/atoms/composed/IconBadge/index.d.ts.map +0 -1
  102. package/dist/atoms/composed/Modal/Modal.d.ts +0 -18
  103. package/dist/atoms/composed/Modal/Modal.d.ts.map +0 -1
  104. package/dist/atoms/composed/Modal/index.d.ts +0 -3
  105. package/dist/atoms/composed/Modal/index.d.ts.map +0 -1
  106. package/dist/atoms/composed/PaletteSwitcher.d.ts +0 -7
  107. package/dist/atoms/composed/PaletteSwitcher.d.ts.map +0 -1
  108. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +0 -25
  109. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +0 -1
  110. package/dist/atoms/composed/ProgressBar/index.d.ts +0 -2
  111. package/dist/atoms/composed/ProgressBar/index.d.ts.map +0 -1
  112. package/dist/atoms/composed/StatCard/StatCard.d.ts +0 -21
  113. package/dist/atoms/composed/StatCard/StatCard.d.ts.map +0 -1
  114. package/dist/atoms/composed/StatCard/index.d.ts +0 -2
  115. package/dist/atoms/composed/StatCard/index.d.ts.map +0 -1
  116. package/dist/atoms/composed/StyleGuide.d.ts +0 -3
  117. package/dist/atoms/composed/StyleGuide.d.ts.map +0 -1
  118. package/dist/atoms/composed/Toast/Toast.d.ts +0 -40
  119. package/dist/atoms/composed/Toast/Toast.d.ts.map +0 -1
  120. package/dist/atoms/composed/Toast/index.d.ts +0 -2
  121. package/dist/atoms/composed/Toast/index.d.ts.map +0 -1
  122. package/dist/atoms/composed/Tooltip/Tooltip.d.ts +0 -16
  123. package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +0 -1
  124. package/dist/atoms/composed/Tooltip/index.d.ts +0 -2
  125. package/dist/atoms/composed/Tooltip/index.d.ts.map +0 -1
  126. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +0 -8
  127. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +0 -1
  128. package/dist/atoms/composed/UserAvatar/index.d.ts +0 -2
  129. package/dist/atoms/composed/UserAvatar/index.d.ts.map +0 -1
  130. package/dist/atoms/composed/UserMenu/UserMenu.d.ts +0 -8
  131. package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +0 -1
  132. package/dist/atoms/composed/UserMenu/index.d.ts +0 -2
  133. package/dist/atoms/composed/UserMenu/index.d.ts.map +0 -1
  134. package/dist/atoms/composed/index.d.ts +0 -25
  135. package/dist/atoms/composed/index.d.ts.map +0 -1
  136. package/dist/atoms/hooks/useApi.d.ts +0 -25
  137. package/dist/atoms/hooks/useApi.d.ts.map +0 -1
  138. package/dist/atoms/hooks/useHealth.d.ts +0 -19
  139. package/dist/atoms/hooks/useHealth.d.ts.map +0 -1
  140. package/dist/atoms/index.d.ts +0 -9
  141. package/dist/atoms/index.d.ts.map +0 -1
  142. package/dist/atoms/services/api/client.d.ts +0 -20
  143. package/dist/atoms/services/api/client.d.ts.map +0 -1
  144. package/dist/atoms/services/auth-service.d.ts +0 -24
  145. package/dist/atoms/services/auth-service.d.ts.map +0 -1
  146. package/dist/atoms/services/health.d.ts +0 -7
  147. package/dist/atoms/services/health.d.ts.map +0 -1
  148. package/dist/atoms/services/index.d.ts +0 -4
  149. package/dist/atoms/services/index.d.ts.map +0 -1
  150. package/dist/atoms/shared/config/constants.d.ts +0 -15
  151. package/dist/atoms/shared/config/constants.d.ts.map +0 -1
  152. package/dist/atoms/shared/config/dashboard-sizes.d.ts +0 -83
  153. package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +0 -1
  154. package/dist/atoms/shared/config/environment.d.ts +0 -10
  155. package/dist/atoms/shared/config/environment.d.ts.map +0 -1
  156. package/dist/atoms/shared/index.d.ts +0 -4
  157. package/dist/atoms/shared/index.d.ts.map +0 -1
  158. package/dist/atoms/types/auth.d.ts +0 -56
  159. package/dist/atoms/types/auth.d.ts.map +0 -1
  160. package/dist/atoms/types/generated.d.ts +0 -1469
  161. package/dist/atoms/types/generated.d.ts.map +0 -1
  162. package/dist/atoms/types/index.d.ts +0 -4
  163. package/dist/atoms/types/index.d.ts.map +0 -1
  164. package/dist/atoms/types/loading.d.ts +0 -26
  165. package/dist/atoms/types/loading.d.ts.map +0 -1
  166. package/dist/atoms/ui/Badge.d.ts +0 -10
  167. package/dist/atoms/ui/Badge.d.ts.map +0 -1
  168. package/dist/atoms/ui/ErrorBoundary.d.ts +0 -18
  169. package/dist/atoms/ui/ErrorBoundary.d.ts.map +0 -1
  170. package/dist/atoms/ui/Select.d.ts +0 -28
  171. package/dist/atoms/ui/Select.d.ts.map +0 -1
  172. package/dist/atoms/ui/Switch.d.ts +0 -9
  173. package/dist/atoms/ui/Switch.d.ts.map +0 -1
  174. package/dist/atoms/ui/Tabs.d.ts +0 -30
  175. package/dist/atoms/ui/Tabs.d.ts.map +0 -1
  176. package/dist/atoms/ui/avatar.d.ts +0 -7
  177. package/dist/atoms/ui/avatar.d.ts.map +0 -1
  178. package/dist/atoms/ui/button.d.ts +0 -14
  179. package/dist/atoms/ui/button.d.ts.map +0 -1
  180. package/dist/atoms/ui/card.d.ts +0 -12
  181. package/dist/atoms/ui/card.d.ts.map +0 -1
  182. package/dist/atoms/ui/dropdown-menu.d.ts +0 -28
  183. package/dist/atoms/ui/dropdown-menu.d.ts.map +0 -1
  184. package/dist/atoms/ui/index.d.ts +0 -15
  185. package/dist/atoms/ui/index.d.ts.map +0 -1
  186. package/dist/atoms/ui/input.d.ts +0 -5
  187. package/dist/atoms/ui/input.d.ts.map +0 -1
  188. package/dist/atoms/ui/label.d.ts +0 -6
  189. package/dist/atoms/ui/label.d.ts.map +0 -1
  190. package/dist/atoms/ui/skeleton.d.ts +0 -3
  191. package/dist/atoms/ui/skeleton.d.ts.map +0 -1
  192. package/dist/atoms/ui/spinner.d.ts +0 -14
  193. package/dist/atoms/ui/spinner.d.ts.map +0 -1
  194. package/dist/atoms/ui/table.d.ts +0 -11
  195. package/dist/atoms/ui/table.d.ts.map +0 -1
  196. package/dist/atoms/utils/animations.d.ts +0 -65
  197. package/dist/atoms/utils/animations.d.ts.map +0 -1
  198. package/dist/atoms/utils/tooltip-helpers.d.ts +0 -71
  199. package/dist/atoms/utils/tooltip-helpers.d.ts.map +0 -1
  200. package/dist/atoms/utils/utils.d.ts +0 -4
  201. package/dist/atoms/utils/utils.d.ts.map +0 -1
  202. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  203. package/dist/features/auth/components/LoginForm.d.ts.map +0 -1
  204. package/dist/features/auth/components/LogoutButton.d.ts +0 -2
  205. package/dist/features/auth/components/LogoutButton.d.ts.map +0 -1
  206. package/dist/features/auth/components/ProtectedRoute.d.ts +0 -10
  207. package/dist/features/auth/components/ProtectedRoute.d.ts.map +0 -1
  208. package/dist/features/auth/components/index.d.ts +0 -4
  209. package/dist/features/auth/components/index.d.ts.map +0 -1
  210. package/dist/features/auth/hooks/index.d.ts +0 -3
  211. package/dist/features/auth/hooks/index.d.ts.map +0 -1
  212. package/dist/features/auth/hooks/useAuth.d.ts +0 -10
  213. package/dist/features/auth/hooks/useAuth.d.ts.map +0 -1
  214. package/dist/features/auth/hooks/usePermissions.d.ts +0 -13
  215. package/dist/features/auth/hooks/usePermissions.d.ts.map +0 -1
  216. package/dist/features/auth/index.d.ts +0 -3
  217. package/dist/features/auth/index.d.ts.map +0 -1
  218. package/dist/features/index.d.ts +0 -2
  219. package/dist/features/index.d.ts.map +0 -1
  220. package/dist/index.d.ts +0 -10
  221. package/dist/index.d.ts.map +0 -1
  222. package/dist/molecules/forms/FormGroup.d.ts +0 -17
  223. package/dist/molecules/forms/FormGroup.d.ts.map +0 -1
  224. package/dist/molecules/forms/SearchInput.d.ts +0 -36
  225. package/dist/molecules/forms/SearchInput.d.ts.map +0 -1
  226. package/dist/molecules/forms/index.d.ts +0 -3
  227. package/dist/molecules/forms/index.d.ts.map +0 -1
  228. package/dist/molecules/index.d.ts +0 -4
  229. package/dist/molecules/index.d.ts.map +0 -1
  230. package/dist/molecules/layout/AppHeader/AppHeader.d.ts +0 -7
  231. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +0 -1
  232. package/dist/molecules/layout/AppHeader/index.d.ts +0 -2
  233. package/dist/molecules/layout/AppHeader/index.d.ts.map +0 -1
  234. package/dist/molecules/layout/AppLayout.d.ts +0 -2
  235. package/dist/molecules/layout/AppLayout.d.ts.map +0 -1
  236. package/dist/molecules/layout/PageTemplate.d.ts +0 -19
  237. package/dist/molecules/layout/PageTemplate.d.ts.map +0 -1
  238. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +0 -24
  239. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +0 -1
  240. package/dist/molecules/layout/SectionHeader/index.d.ts +0 -2
  241. package/dist/molecules/layout/SectionHeader/index.d.ts.map +0 -1
  242. package/dist/molecules/layout/ShowcaseSection.d.ts +0 -22
  243. package/dist/molecules/layout/ShowcaseSection.d.ts.map +0 -1
  244. package/dist/molecules/layout/Sidebar.d.ts +0 -6
  245. package/dist/molecules/layout/Sidebar.d.ts.map +0 -1
  246. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +0 -13
  247. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +0 -1
  248. package/dist/molecules/layout/SidebarButton/index.d.ts +0 -2
  249. package/dist/molecules/layout/SidebarButton/index.d.ts.map +0 -1
  250. package/dist/molecules/layout/SidebarContext.d.ts +0 -12
  251. package/dist/molecules/layout/SidebarContext.d.ts.map +0 -1
  252. package/dist/molecules/layout/index.d.ts +0 -8
  253. package/dist/molecules/layout/index.d.ts.map +0 -1
  254. package/dist/molecules/navigation/NavMenu.d.ts +0 -20
  255. package/dist/molecules/navigation/NavMenu.d.ts.map +0 -1
  256. package/dist/molecules/navigation/Pagination.d.ts +0 -14
  257. package/dist/molecules/navigation/Pagination.d.ts.map +0 -1
  258. package/dist/molecules/navigation/index.d.ts +0 -3
  259. package/dist/molecules/navigation/index.d.ts.map +0 -1
  260. package/dist/organisms/index.d.ts +0 -2
  261. package/dist/organisms/index.d.ts.map +0 -1
  262. package/dist/organisms/showcase/ComponentShowcasePage.d.ts +0 -3
  263. package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +0 -1
  264. package/dist/templates/AuthTemplate.d.ts +0 -68
  265. package/dist/templates/AuthTemplate.d.ts.map +0 -1
  266. package/dist/templates/ComponentShowcaseTemplate.d.ts +0 -53
  267. package/dist/templates/ComponentShowcaseTemplate.d.ts.map +0 -1
  268. package/dist/templates/DashboardTemplate.d.ts +0 -62
  269. package/dist/templates/DashboardTemplate.d.ts.map +0 -1
  270. package/dist/templates/DataTemplate.d.ts +0 -78
  271. package/dist/templates/DataTemplate.d.ts.map +0 -1
  272. package/dist/templates/admin/AdminCRUDTemplate.d.ts +0 -105
  273. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +0 -1
  274. package/dist/templates/admin/AdminDashboardTemplate.d.ts +0 -89
  275. package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +0 -1
  276. package/dist/templates/admin/AdminDetailTemplate.d.ts +0 -132
  277. package/dist/templates/admin/AdminDetailTemplate.d.ts.map +0 -1
  278. package/dist/templates/admin/index.d.ts +0 -4
  279. package/dist/templates/admin/index.d.ts.map +0 -1
  280. package/dist/templates/factory.d.ts +0 -28
  281. package/dist/templates/factory.d.ts.map +0 -1
  282. package/dist/templates/index.d.ts +0 -7
  283. package/dist/templates/index.d.ts.map +0 -1
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+
3
+ // Simple test component to verify the entity templates work
4
+ export const TestEntityTemplate: React.FC = () => {
5
+ const testData = [
6
+ {
7
+ id: '1',
8
+ amount: 1000,
9
+ category: 'Test',
10
+ date: '2024-01-01'
11
+ }
12
+ ];
13
+
14
+ const testConfig = {
15
+ entityType: 'transactional' as const,
16
+ display: {
17
+ title: 'Test Dashboard',
18
+ category: 1 as const
19
+ },
20
+ metrics: [
21
+ {
22
+ key: 'amount',
23
+ label: 'Total Amount',
24
+ type: 'currency' as const
25
+ }
26
+ ]
27
+ };
28
+
29
+ return (
30
+ <div className="p-8">
31
+ <h1 className="text-2xl font-bold mb-4">Entity Template Test</h1>
32
+ <div className="bg-muted p-4 rounded-lg">
33
+ <p>Config: {JSON.stringify(testConfig, null, 2)}</p>
34
+ <p>Data: {JSON.stringify(testData, null, 2)}</p>
35
+ </div>
36
+ </div>
37
+ );
38
+ };
39
+
40
+ export default TestEntityTemplate;
@@ -1,2 +1,3 @@
1
1
  export { ComponentShowcase } from './ComponentShowcase';
2
- export * from './AdminShowcase';
2
+ export * from './AdminShowcase';
3
+ export * from './EntityShowcase';
@@ -0,0 +1,430 @@
1
+ import React, { useState } from 'react';
2
+ import { AppLayout } from '../../molecules/layout/AppLayout';
3
+ import { PageTemplate } from '../../molecules/layout/PageTemplate';
4
+ import { SectionHeader } from '../../molecules/layout/SectionHeader';
5
+ import { EntityListPanel } from '../../organisms/entity';
6
+ import { Modal } from '../../atoms/composed/Modal';
7
+ import { Button } from '../../atoms/ui/button';
8
+ import { Card } from '../../atoms/ui/card';
9
+ import { Badge } from '../../atoms/ui/Badge';
10
+ import type { EntityTemplateConfig, EntityData, ActionConfig } from '../../atoms/types';
11
+ import { cn } from '../../atoms/utils/utils';
12
+ import { Plus, Trash2, Filter, Download } from 'lucide-react';
13
+
14
+ export interface EntityManagementTemplateProps<T extends EntityData> {
15
+ config: EntityTemplateConfig<T>;
16
+ data: T[];
17
+ isLoading?: boolean;
18
+ className?: string;
19
+
20
+ // View configuration
21
+ defaultView?: 'list' | 'grid' | 'cards';
22
+ enableViews?: boolean;
23
+ enableSearch?: boolean;
24
+ enableFiltering?: boolean;
25
+ enableBulkActions?: boolean;
26
+ enableExport?: boolean;
27
+ pageSize?: number;
28
+
29
+ // CRUD operations
30
+ onCreate?: (item: Partial<T>) => Promise<T> | T;
31
+ onUpdate?: (id: string | number, item: Partial<T>) => Promise<T> | T;
32
+ onDelete?: (id: string | number) => Promise<void> | void;
33
+ onBulkDelete?: (ids: (string | number)[]) => Promise<void> | void;
34
+ onView?: (item: T) => void;
35
+
36
+ // Extension points - Render Props
37
+ renderCreateForm?: (onSubmit: (item: Partial<T>) => void, onCancel: () => void) => React.ReactNode;
38
+ renderEditForm?: (item: T, onSubmit: (item: Partial<T>) => void, onCancel: () => void) => React.ReactNode;
39
+ renderDetailView?: (item: T, onEdit: () => void, onClose: () => void) => React.ReactNode;
40
+ renderCustomActions?: (item?: T) => React.ReactNode;
41
+ renderEmptyState?: () => React.ReactNode;
42
+ renderFilterPanel?: (onFilter: (filters: Record<string, unknown>) => void) => React.ReactNode;
43
+
44
+ // Extension points - Slots
45
+ headerSlot?: React.ReactNode;
46
+ footerSlot?: React.ReactNode;
47
+ toolbarSlot?: React.ReactNode;
48
+ listHeaderSlot?: React.ReactNode;
49
+ listFooterSlot?: React.ReactNode;
50
+
51
+ // Event handlers
52
+ onRefresh?: () => void;
53
+ onFilter?: (filters: Record<string, unknown>) => void;
54
+ onExport?: (data: T[], format: 'csv' | 'json') => void;
55
+ onSelectionChange?: (selectedItems: T[]) => void;
56
+ }
57
+
58
+ export const EntityManagementTemplate = <T extends EntityData>({
59
+ config,
60
+ data,
61
+ isLoading = false,
62
+ className,
63
+ enableSearch = true,
64
+ enableFiltering = true,
65
+ enableBulkActions = true,
66
+ enableExport = true,
67
+ pageSize = 10,
68
+ onCreate,
69
+ onUpdate,
70
+ onDelete,
71
+ onBulkDelete,
72
+ onView,
73
+ renderCreateForm,
74
+ renderEditForm,
75
+ renderDetailView,
76
+ renderCustomActions,
77
+ renderEmptyState,
78
+ renderFilterPanel,
79
+ headerSlot,
80
+ footerSlot,
81
+ toolbarSlot,
82
+ listHeaderSlot,
83
+ listFooterSlot,
84
+ onRefresh,
85
+ onFilter,
86
+ onExport
87
+ }: EntityManagementTemplateProps<T>) => {
88
+
89
+ const [showCreateModal, setShowCreateModal] = useState(false);
90
+ const [showEditModal, setShowEditModal] = useState(false);
91
+ const [showDetailModal, setShowDetailModal] = useState(false);
92
+ const [showFilterPanel, setShowFilterPanel] = useState(false);
93
+ const [selectedItem, setSelectedItem] = useState<T | null>(null);
94
+ const [selectedItems, setSelectedItems] = useState<T[]>([]);
95
+
96
+ const handleCreate = async (item: Partial<T>) => {
97
+ if (onCreate) {
98
+ try {
99
+ await onCreate(item);
100
+ setShowCreateModal(false);
101
+ onRefresh?.();
102
+ } catch (error) {
103
+ console.error('Create failed:', error);
104
+ }
105
+ }
106
+ };
107
+
108
+ const handleUpdate = async (item: Partial<T>) => {
109
+ if (onUpdate && selectedItem) {
110
+ try {
111
+ await onUpdate(selectedItem.id, item);
112
+ setShowEditModal(false);
113
+ setSelectedItem(null);
114
+ onRefresh?.();
115
+ } catch (error) {
116
+ console.error('Update failed:', error);
117
+ }
118
+ }
119
+ };
120
+
121
+
122
+ const handleBulkDelete = async () => {
123
+ if (onBulkDelete && selectedItems.length > 0 && confirm(`Are you sure you want to delete ${selectedItems.length} ${config.display.title.toLowerCase()}?`)) {
124
+ try {
125
+ const ids = selectedItems.map(item => item.id);
126
+ await onBulkDelete(ids);
127
+ setSelectedItems([]);
128
+ onRefresh?.();
129
+ } catch (error) {
130
+ console.error('Bulk delete failed:', error);
131
+ }
132
+ }
133
+ };
134
+
135
+ const handleView = (item: T) => {
136
+ if (onView) {
137
+ onView(item);
138
+ } else {
139
+ setSelectedItem(item);
140
+ setShowDetailModal(true);
141
+ }
142
+ };
143
+
144
+ const handleEdit = (item: T) => {
145
+ setSelectedItem(item);
146
+ setShowEditModal(true);
147
+ };
148
+
149
+ const handleRowClick = (item: T) => {
150
+ handleView(item);
151
+ };
152
+
153
+ const handleAction = (action: ActionConfig, selectedItems: T[]) => {
154
+ action.onClick({ selectedItems, config });
155
+ };
156
+
157
+ const generateDefaultActions = (): ActionConfig[] => {
158
+ const actions: ActionConfig[] = [];
159
+
160
+ if (onCreate) {
161
+ actions.push({
162
+ label: `Add ${config.display.title.slice(0, -1)}`,
163
+ type: 'primary',
164
+ icon: Plus,
165
+ onClick: () => setShowCreateModal(true)
166
+ });
167
+ }
168
+
169
+ return actions;
170
+ };
171
+
172
+
173
+ const renderPageHeader = () => {
174
+ const actions = [
175
+ ...generateDefaultActions(),
176
+ ...(config.actions || [])
177
+ ];
178
+
179
+ return (
180
+ <SectionHeader
181
+ title={config.display.title}
182
+ description={config.display.description}
183
+ category={config.display.category}
184
+ actions={
185
+ <div className="flex items-center gap-2">
186
+ {renderCustomActions && renderCustomActions()}
187
+
188
+ {enableFiltering && (
189
+ <Button
190
+ variant="outline"
191
+ size="sm"
192
+ onClick={() => setShowFilterPanel(!showFilterPanel)}
193
+ className={cn(showFilterPanel && "bg-muted")}
194
+ >
195
+ <Filter className="w-4 h-4 mr-2" />
196
+ Filters
197
+ </Button>
198
+ )}
199
+
200
+ {enableExport && (
201
+ <Button
202
+ variant="outline"
203
+ size="sm"
204
+ onClick={() => onExport?.(selectedItems.length > 0 ? selectedItems : data, 'csv')}
205
+ disabled={data.length === 0}
206
+ >
207
+ <Download className="w-4 h-4 mr-2" />
208
+ Export
209
+ </Button>
210
+ )}
211
+
212
+ {actions.map(action => (
213
+ <Button
214
+ key={action.label}
215
+ variant={action.type === 'primary' ? 'default' : 'outline'}
216
+ size="sm"
217
+ onClick={() => action.onClick({ data, config })}
218
+ >
219
+ {action.icon && <action.icon className="w-4 h-4 mr-2" />}
220
+ {action.label}
221
+ </Button>
222
+ ))}
223
+ </div>
224
+ }
225
+ />
226
+ );
227
+ };
228
+
229
+ const renderToolbar = () => {
230
+ if (selectedItems.length === 0) return null;
231
+
232
+ return (
233
+ <div className="bg-muted/50 p-4 rounded-lg mb-4">
234
+ <div className="flex items-center justify-between">
235
+ <div className="flex items-center gap-2">
236
+ <Badge variant="secondary">
237
+ {selectedItems.length} selected
238
+ </Badge>
239
+ <Button
240
+ variant="ghost"
241
+ size="sm"
242
+ onClick={() => setSelectedItems([])}
243
+ >
244
+ Clear selection
245
+ </Button>
246
+ </div>
247
+
248
+ <div className="flex items-center gap-2">
249
+ {onBulkDelete && (
250
+ <Button
251
+ variant="destructive"
252
+ size="sm"
253
+ onClick={handleBulkDelete}
254
+ >
255
+ <Trash2 className="w-4 h-4 mr-2" />
256
+ Delete Selected
257
+ </Button>
258
+ )}
259
+
260
+ {enableExport && (
261
+ <Button
262
+ variant="outline"
263
+ size="sm"
264
+ onClick={() => onExport?.(selectedItems, 'csv')}
265
+ >
266
+ <Download className="w-4 h-4 mr-2" />
267
+ Export Selected
268
+ </Button>
269
+ )}
270
+ </div>
271
+ </div>
272
+ </div>
273
+ );
274
+ };
275
+
276
+ const renderFilterPanelSection = () => {
277
+ if (!showFilterPanel) return null;
278
+
279
+ return (
280
+ <Card className="p-4 mb-4">
281
+ {renderFilterPanel ? (
282
+ renderFilterPanel(onFilter || (() => {}))
283
+ ) : (
284
+ <div className="text-center text-muted-foreground py-4">
285
+ Filter functionality not implemented yet
286
+ </div>
287
+ )}
288
+ </Card>
289
+ );
290
+ };
291
+
292
+ const renderCreateModal = () => {
293
+ if (!showCreateModal) return null;
294
+
295
+ return (
296
+ <Modal
297
+ isOpen={showCreateModal}
298
+ onClose={() => setShowCreateModal(false)}
299
+ title={`Create ${config.display.title.slice(0, -1)}`}
300
+ category={config.display.category}
301
+ >
302
+ {renderCreateForm ? (
303
+ renderCreateForm(handleCreate, () => setShowCreateModal(false))
304
+ ) : (
305
+ <div className="p-6 text-center text-muted-foreground">
306
+ Create form not implemented yet
307
+ </div>
308
+ )}
309
+ </Modal>
310
+ );
311
+ };
312
+
313
+ const renderEditModal = () => {
314
+ if (!showEditModal || !selectedItem) return null;
315
+
316
+ return (
317
+ <Modal
318
+ isOpen={showEditModal}
319
+ onClose={() => {
320
+ setShowEditModal(false);
321
+ setSelectedItem(null);
322
+ }}
323
+ title={`Edit ${config.display.title.slice(0, -1)}`}
324
+ category={config.display.category}
325
+ >
326
+ {renderEditForm ? (
327
+ renderEditForm(
328
+ selectedItem,
329
+ handleUpdate,
330
+ () => {
331
+ setShowEditModal(false);
332
+ setSelectedItem(null);
333
+ }
334
+ )
335
+ ) : (
336
+ <div className="p-6 text-center text-muted-foreground">
337
+ Edit form not implemented yet
338
+ </div>
339
+ )}
340
+ </Modal>
341
+ );
342
+ };
343
+
344
+ const renderDetailModal = () => {
345
+ if (!showDetailModal || !selectedItem) return null;
346
+
347
+ return (
348
+ <Modal
349
+ isOpen={showDetailModal}
350
+ onClose={() => {
351
+ setShowDetailModal(false);
352
+ setSelectedItem(null);
353
+ }}
354
+ title={`${config.display.title.slice(0, -1)} Details`}
355
+ category={config.display.category}
356
+ size="large"
357
+ >
358
+ {renderDetailView ? (
359
+ renderDetailView(
360
+ selectedItem,
361
+ () => {
362
+ setShowDetailModal(false);
363
+ handleEdit(selectedItem);
364
+ },
365
+ () => {
366
+ setShowDetailModal(false);
367
+ setSelectedItem(null);
368
+ }
369
+ )
370
+ ) : (
371
+ <div className="p-6">
372
+ <div className="space-y-4">
373
+ {Object.entries(selectedItem).map(([key, value]) => (
374
+ <div key={key} className="flex justify-between">
375
+ <span className="font-medium capitalize">{key}:</span>
376
+ <span>{value?.toString() || '-'}</span>
377
+ </div>
378
+ ))}
379
+ </div>
380
+ </div>
381
+ )}
382
+ </Modal>
383
+ );
384
+ };
385
+
386
+ return (
387
+ <AppLayout>
388
+ <PageTemplate
389
+ className={cn('container mx-auto px-4 py-6', className)}
390
+ data-component-name="EntityManagementTemplate"
391
+ >
392
+ {headerSlot}
393
+ {renderPageHeader()}
394
+ {toolbarSlot}
395
+ {renderFilterPanelSection()}
396
+ {renderToolbar()}
397
+
398
+ <div className="space-y-4">
399
+ {listHeaderSlot}
400
+
401
+ <EntityListPanel
402
+ config={config}
403
+ data={data}
404
+ isLoading={isLoading}
405
+ onRowClick={handleRowClick}
406
+ onAction={handleAction}
407
+ enableSelection={enableBulkActions}
408
+ enableBulkActions={enableBulkActions}
409
+ enableExport={enableExport}
410
+ enableRefresh={!!onRefresh}
411
+ showSearch={enableSearch}
412
+ showPagination={true}
413
+ pageSize={pageSize}
414
+ renderEmptyState={renderEmptyState}
415
+ onExport={onExport}
416
+ onRefresh={onRefresh}
417
+ />
418
+
419
+ {listFooterSlot}
420
+ </div>
421
+
422
+ {renderCreateModal()}
423
+ {renderEditModal()}
424
+ {renderDetailModal()}
425
+
426
+ {footerSlot}
427
+ </PageTemplate>
428
+ </AppLayout>
429
+ );
430
+ };