@pattern-stack/frontend-patterns 0.0.4 → 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 (86) hide show
  1. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts +19 -0
  2. package/dist/atoms/composed/SalesPanel/SalesPanel.d.ts.map +1 -0
  3. package/dist/atoms/composed/SalesPanel/index.d.ts +2 -0
  4. package/dist/atoms/composed/SalesPanel/index.d.ts.map +1 -0
  5. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts +63 -0
  6. package/dist/atoms/composed/SalesPanel/mockSalesData.d.ts.map +1 -0
  7. package/dist/atoms/composed/index.d.ts +1 -0
  8. package/dist/atoms/composed/index.d.ts.map +1 -1
  9. package/dist/atoms/types/entity-config.d.ts +117 -0
  10. package/dist/atoms/types/entity-config.d.ts.map +1 -0
  11. package/dist/atoms/types/index.d.ts +2 -0
  12. package/dist/atoms/types/index.d.ts.map +1 -1
  13. package/dist/atoms/types/navigation.d.ts +30 -0
  14. package/dist/atoms/types/navigation.d.ts.map +1 -0
  15. package/dist/atoms/ui/ErrorBoundary.d.ts +1 -1
  16. package/dist/atoms/ui/button.d.ts +1 -1
  17. package/dist/atoms/utils/icon-resolver.d.ts +72 -0
  18. package/dist/atoms/utils/icon-resolver.d.ts.map +1 -0
  19. package/dist/atoms/utils/metric-engine.d.ts +30 -0
  20. package/dist/atoms/utils/metric-engine.d.ts.map +1 -0
  21. package/dist/atoms/utils/utils.d.ts +2 -0
  22. package/dist/atoms/utils/utils.d.ts.map +1 -1
  23. package/dist/features/auth/components/ProtectedRoute.d.ts +1 -1
  24. package/dist/frontend-patterns.css +1 -1
  25. package/dist/index.es.js +402 -14
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/index.js +402 -14
  28. package/dist/index.js.map +1 -1
  29. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts +16 -0
  30. package/dist/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.d.ts.map +1 -0
  31. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts +2 -0
  32. package/dist/molecules/layout/DashboardWithSidePanel/index.d.ts.map +1 -0
  33. package/dist/molecules/layout/NavigationContext.d.ts +15 -0
  34. package/dist/molecules/layout/NavigationContext.d.ts.map +1 -0
  35. package/dist/molecules/layout/Sidebar.d.ts.map +1 -1
  36. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +2 -0
  37. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -1
  38. package/dist/molecules/layout/index.d.ts +3 -0
  39. package/dist/molecules/layout/index.d.ts.map +1 -1
  40. package/dist/templates/factory.d.ts +2 -1
  41. package/dist/templates/factory.d.ts.map +1 -1
  42. package/dist/templates/index.d.ts.map +1 -1
  43. package/package.json +7 -3
  44. package/src/App.tsx +11 -1
  45. package/src/__tests__/atoms/composed/databadge.test.tsx +106 -0
  46. package/src/__tests__/atoms/composed/statcard.test.tsx +133 -0
  47. package/src/__tests__/atoms/utils/icon-resolver.test.tsx +140 -0
  48. package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
  49. package/src/atoms/composed/SalesPanel/index.ts +1 -0
  50. package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
  51. package/src/atoms/composed/index.ts +1 -0
  52. package/src/atoms/types/entity-config.ts +127 -0
  53. package/src/atoms/types/index.ts +3 -1
  54. package/src/atoms/types/navigation.ts +43 -0
  55. package/src/atoms/utils/icon-resolver.tsx +54 -0
  56. package/src/atoms/utils/metric-engine.ts +236 -0
  57. package/src/atoms/utils/utils.ts +4 -2
  58. package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
  59. package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
  60. package/src/molecules/layout/NavigationContext.tsx +63 -0
  61. package/src/molecules/layout/Sidebar.tsx +10 -23
  62. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +32 -10
  63. package/src/molecules/layout/index.ts +4 -1
  64. package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
  65. package/src/organisms/entity/EntityListPanel.tsx +339 -0
  66. package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
  67. package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
  68. package/src/organisms/entity/index.ts +4 -0
  69. package/src/organisms/index.ts +5 -1
  70. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +77 -75
  71. package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
  72. package/src/pages/AdminShowcase/index.tsx +2 -1
  73. package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
  74. package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
  75. package/src/pages/EntityShowcase/index.ts +2 -0
  76. package/src/pages/EntityTemplateExample.tsx +229 -0
  77. package/src/pages/TestEntityTemplate.tsx +40 -0
  78. package/src/pages/index.ts +2 -1
  79. package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
  80. package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
  81. package/src/templates/entity/configs/financial-config.ts +141 -0
  82. package/src/templates/entity/configs/index.ts +1 -0
  83. package/src/templates/entity/index.ts +3 -0
  84. package/src/templates/factory.tsx +14 -7
  85. package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
  86. package/src/templates/index.ts +4 -0
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ interface DashboardWithSidePanelProps {
3
+ /** Main dashboard content */
4
+ children: React.ReactNode;
5
+ /** Side panel component to display */
6
+ sidePanel?: React.ReactNode;
7
+ /** Whether to show the side panel */
8
+ showSidePanel?: boolean;
9
+ /** Side panel width in Tailwind units (e.g., 72, 80) */
10
+ sidePanelWidth?: number;
11
+ /** Additional CSS classes */
12
+ className?: string;
13
+ }
14
+ export declare const DashboardWithSidePanel: React.FC<DashboardWithSidePanelProps>;
15
+ export {};
16
+ //# sourceMappingURL=DashboardWithSidePanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardWithSidePanel.d.ts","sourceRoot":"","sources":["../../../../src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,2BAA2B;IACnC,6BAA6B;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,sCAAsC;IACtC,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,qCAAqC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAyBxE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DashboardWithSidePanel } from './DashboardWithSidePanel';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/molecules/layout/DashboardWithSidePanel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { NavigationConfig, NavigationItem } from '../../atoms/types';
3
+ interface NavigationContextType {
4
+ navigation: NavigationConfig;
5
+ setNavigation: (config: NavigationConfig) => void;
6
+ }
7
+ interface NavigationProviderProps {
8
+ children: ReactNode;
9
+ initialNavigation?: NavigationConfig;
10
+ }
11
+ export declare const NavigationProvider: ({ children, initialNavigation }: NavigationProviderProps) => import("react/jsx-runtime").JSX.Element;
12
+ export declare const useNavigation: () => NavigationContextType;
13
+ export declare const getNavigationItems: (config: NavigationConfig) => NavigationItem[];
14
+ export {};
15
+ //# sourceMappingURL=NavigationContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigationContext.d.ts","sourceRoot":"","sources":["../../../src/molecules/layout/NavigationContext.tsx"],"names":[],"mappings":"AAAA,OAAc,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEzE,UAAU,qBAAqB;IAC7B,UAAU,EAAE,gBAAgB,CAAA;IAC5B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAA;CAClD;AAID,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,SAAS,CAAA;IACnB,iBAAiB,CAAC,EAAE,gBAAgB,CAAA;CACrC;AAaD,eAAO,MAAM,kBAAkB,GAAI,iCAAiC,uBAAuB,4CAc1F,CAAA;AAED,eAAO,MAAM,aAAa,6BAMzB,CAAA;AAGD,eAAO,MAAM,kBAAkB,GAAI,QAAQ,gBAAgB,KAAG,cAAc,EAW3E,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/molecules/layout/Sidebar.tsx"],"names":[],"mappings":"AAqBA,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,OAAO,GAAI,eAAe,YAAY,4CAsHlD,CAAC"}
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/molecules/layout/Sidebar.tsx"],"names":[],"mappings":"AAQA,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,OAAO,GAAI,eAAe,YAAY,4CAsHlD,CAAC"}
@@ -7,6 +7,8 @@ interface SidebarButtonProps {
7
7
  expanded?: boolean;
8
8
  onClick?: () => void;
9
9
  className?: string;
10
+ badge?: string | number;
11
+ disabled?: boolean;
10
12
  }
11
13
  export declare const SidebarButton: React.FC<SidebarButtonProps>;
12
14
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"SidebarButton.d.ts","sourceRoot":"","sources":["../../../../src/molecules/layout/SidebarButton/SidebarButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,kBAAkB;IAC1B,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAoFtD,CAAC"}
1
+ {"version":3,"file":"SidebarButton.d.ts","sourceRoot":"","sources":["../../../../src/molecules/layout/SidebarButton/SidebarButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,kBAAkB;IAC1B,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAwGtD,CAAC"}
@@ -1,8 +1,11 @@
1
1
  export { PageTemplate, type PageTemplateProps } from './PageTemplate';
2
2
  export { ShowcaseSection } from './ShowcaseSection';
3
3
  export { AppLayout } from './AppLayout';
4
+ export { DashboardWithSidePanel } from './DashboardWithSidePanel';
4
5
  export { SectionHeader } from './SectionHeader';
5
6
  export { SidebarButton } from './SidebarButton';
6
7
  export { AppHeader } from './AppHeader';
7
8
  export { SidebarProvider, useSidebar } from './SidebarContext';
9
+ export { NavigationProvider, useNavigation, getNavigationItems } from './NavigationContext';
10
+ export { Sidebar } from './Sidebar';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/molecules/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/molecules/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import type { ReactNode } from 'react';
3
- import type { AuthConfig } from '../atoms/types';
3
+ import type { AuthConfig, NavigationConfig } from '../atoms/types';
4
4
  export interface AppConfig {
5
5
  title: string;
6
6
  description?: string;
@@ -14,6 +14,7 @@ export interface AppConfig {
14
14
  theme?: string;
15
15
  darkMode?: boolean;
16
16
  auth?: AuthConfig;
17
+ navigation?: NavigationConfig;
17
18
  customProviders?: React.ComponentType<{
18
19
  children: ReactNode;
19
20
  }>[];
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/templates/factory.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAStC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,EAAE,CAAA;CACjE;AAGD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,SAAS,CAAA;IACvB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;IACvD,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAGD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW,CA6HtE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAE1D"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/templates/factory.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAQtC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGlE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,eAAe,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,EAAE,CAAA;CACjE;AAGD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,SAAS,CAAA;IACvB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,KAAK,IAAI,CAAA;IACvD,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAGD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW,CAoItE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAE1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAGxB,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAGxB,cAAc,SAAS,CAAC;AAOxB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,WAAW,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@pattern-stack/frontend-patterns",
3
3
  "description": "Production-ready React frontend template with atomic architecture patterns. Build ultra-lean applications by importing shared UI foundation patterns.",
4
4
  "private": false,
5
- "version": "0.0.4",
5
+ "version": "0.0.6",
6
6
  "keywords": [
7
7
  "react",
8
8
  "typescript",
@@ -44,7 +44,8 @@
44
44
  "scripts": {
45
45
  "dev": "vite",
46
46
  "build": "tsc -b && vite build",
47
- "build:lib": "npm run clean && vite build --mode library && tsc -p tsconfig.lib.json",
47
+ "build:lib": "npm run clean && vite build --mode library && npm run build:types",
48
+ "build:types": "tsc --project tsconfig.lib.json",
48
49
  "clean": "rm -rf dist",
49
50
  "lint": "eslint .",
50
51
  "lint:fix": "eslint . --fix",
@@ -102,9 +103,12 @@
102
103
  "@vitejs/plugin-react": "^4.4.1",
103
104
  "@vitest/coverage-v8": "^3.2.1",
104
105
  "autoprefixer": "^10.4.21",
106
+ "concurrently": "^9.1.2",
107
+ "cors": "^2.8.5",
105
108
  "eslint": "^9.25.0",
106
109
  "eslint-plugin-react-hooks": "^5.2.0",
107
110
  "eslint-plugin-react-refresh": "^0.4.19",
111
+ "express": "^5.1.0",
108
112
  "globals": "^16.0.0",
109
113
  "jsdom": "^26.1.0",
110
114
  "postcss": "^8.5.3",
@@ -115,4 +119,4 @@
115
119
  "vite": "^6.3.5",
116
120
  "vitest": "^3.2.1"
117
121
  }
118
- }
122
+ }
package/src/App.tsx CHANGED
@@ -15,8 +15,12 @@ import {
15
15
  ComponentShowcase,
16
16
  AdminDashboardShowcase,
17
17
  AdminCRUDShowcase,
18
- AdminDetailShowcase
18
+ AdminDetailShowcase,
19
+ SalesPerformanceDashboard,
20
+ EntityPerformanceShowcase,
21
+ EntityManagementShowcase
19
22
  } from './pages';
23
+ import { EntityTemplateExample } from './pages/EntityTemplateExample';
20
24
 
21
25
  function App() {
22
26
  return (
@@ -41,6 +45,12 @@ function App() {
41
45
  <Route path="admin/dashboard" element={<ErrorBoundary><AdminDashboardShowcase /></ErrorBoundary>} />
42
46
  <Route path="admin/users" element={<ErrorBoundary><AdminCRUDShowcase /></ErrorBoundary>} />
43
47
  <Route path="admin/user/:id" element={<ErrorBoundary><AdminDetailShowcase /></ErrorBoundary>} />
48
+ <Route path="admin/sales" element={<ErrorBoundary><SalesPerformanceDashboard /></ErrorBoundary>} />
49
+
50
+ {/* Entity Template Showcases */}
51
+ <Route path="entity/performance" element={<ErrorBoundary><EntityPerformanceShowcase /></ErrorBoundary>} />
52
+ <Route path="entity/management" element={<ErrorBoundary><EntityManagementShowcase /></ErrorBoundary>} />
53
+ <Route path="entity/template-example" element={<ErrorBoundary><EntityTemplateExample /></ErrorBoundary>} />
44
54
 
45
55
  {/* Fallback */}
46
56
  <Route path="*" element={<ErrorBoundary><ComponentShowcasePage /></ErrorBoundary>} />
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { render, screen } from '../../utils'
3
+ import { DataBadge } from '../../../atoms/composed/DataBadge'
4
+
5
+ describe('DataBadge (Composed)', () => {
6
+ describe('status variant', () => {
7
+ it('renders success status badge', () => {
8
+ render(<DataBadge variant="status" status="success">Active</DataBadge>)
9
+
10
+ const badge = screen.getByText('Active')
11
+ expect(badge).toBeInTheDocument()
12
+ expect(badge.className).toContain('status-success')
13
+ })
14
+
15
+ it('renders warning status badge', () => {
16
+ render(<DataBadge variant="status" status="warning">Pending</DataBadge>)
17
+
18
+ const badge = screen.getByText('Pending')
19
+ expect(badge.className).toContain('status-warning')
20
+ })
21
+
22
+ it('renders error status badge', () => {
23
+ render(<DataBadge variant="status" status="error">Failed</DataBadge>)
24
+
25
+ const badge = screen.getByText('Failed')
26
+ expect(badge.className).toContain('status-error')
27
+ })
28
+
29
+ it('renders info status badge', () => {
30
+ render(<DataBadge variant="status" status="info">Processing</DataBadge>)
31
+
32
+ const badge = screen.getByText('Processing')
33
+ expect(badge.className).toContain('status-info')
34
+ })
35
+
36
+ it('renders neutral status badge', () => {
37
+ render(<DataBadge variant="status" status="neutral">Draft</DataBadge>)
38
+
39
+ const badge = screen.getByText('Draft')
40
+ expect(badge.className).toContain('status-neutral')
41
+ })
42
+ })
43
+
44
+ describe('category variant', () => {
45
+ it('renders category 1 badge', () => {
46
+ render(<DataBadge variant="category" category={1}>Category 1</DataBadge>)
47
+
48
+ const badge = screen.getByText('Category 1')
49
+ expect(badge).toBeInTheDocument()
50
+ expect(badge.className).toContain('badge-category-1')
51
+ })
52
+
53
+ it('renders category 5 badge', () => {
54
+ render(<DataBadge variant="category" category={5}>Category 5</DataBadge>)
55
+
56
+ const badge = screen.getByText('Category 5')
57
+ expect(badge.className).toContain('badge-category-5')
58
+ })
59
+
60
+ it('renders category 8 badge', () => {
61
+ render(<DataBadge variant="category" category={8}>Category 8</DataBadge>)
62
+
63
+ const badge = screen.getByText('Category 8')
64
+ expect(badge.className).toContain('badge-category-8')
65
+ })
66
+ })
67
+
68
+ it('applies additional className props', () => {
69
+ render(
70
+ <DataBadge variant="status" status="success" className="custom-class">
71
+ Custom
72
+ </DataBadge>
73
+ )
74
+
75
+ const badge = screen.getByText('Custom')
76
+ expect(badge.className).toContain('custom-class')
77
+ })
78
+
79
+ it('renders with component data attribute', () => {
80
+ render(
81
+ <DataBadge variant="status" status="info">
82
+ Test
83
+ </DataBadge>
84
+ )
85
+
86
+ expect(screen.getByText('Test')).toHaveAttribute('data-component-name', 'DataBadge')
87
+ })
88
+
89
+ it('automatically uses default category', () => {
90
+ render(<DataBadge>Default</DataBadge>)
91
+
92
+ const badge = screen.getByText('Default')
93
+ expect(badge.className).toContain('badge-category-1')
94
+ })
95
+
96
+ it('handles string and number status values', () => {
97
+ const { rerender } = render(
98
+ <DataBadge variant="status" status="success">String Status</DataBadge>
99
+ )
100
+
101
+ expect(screen.getByText('String Status')).toBeInTheDocument()
102
+
103
+ rerender(<DataBadge variant="category" category={3}>Number Category</DataBadge>)
104
+ expect(screen.getByText('Number Category')).toBeInTheDocument()
105
+ })
106
+ })
@@ -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
+ })