@pattern-stack/frontend-patterns 0.0.3 → 0.0.4

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 (154) hide show
  1. package/dist/index.es.js +1 -1
  2. package/dist/index.js +1 -0
  3. package/package.json +5 -3
  4. package/src/App.css +42 -0
  5. package/src/App.tsx +54 -0
  6. package/src/__tests__/README.md +221 -0
  7. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
  8. package/src/__tests__/atoms/ui/button.test.tsx +68 -0
  9. package/src/__tests__/atoms/utils/simple.test.ts +18 -0
  10. package/src/__tests__/atoms/utils/utils.test.ts +77 -0
  11. package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
  12. package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
  13. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
  14. package/src/__tests__/setup.ts +51 -0
  15. package/src/__tests__/utils.tsx +123 -0
  16. package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
  17. package/src/atoms/composed/Accordion/index.ts +1 -0
  18. package/src/atoms/composed/Alert/Alert.tsx +132 -0
  19. package/src/atoms/composed/Alert/index.ts +1 -0
  20. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
  21. package/src/atoms/composed/Breadcrumb/index.ts +1 -0
  22. package/src/atoms/composed/Chart/Chart.tsx +425 -0
  23. package/src/atoms/composed/Chart/index.ts +2 -0
  24. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
  25. package/src/atoms/composed/ColorSwatch/index.ts +1 -0
  26. package/src/atoms/composed/DarkModeToggle.tsx +66 -0
  27. package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
  28. package/src/atoms/composed/DataBadge/index.ts +1 -0
  29. package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
  30. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
  31. package/src/atoms/composed/DataTable/index.ts +2 -0
  32. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
  33. package/src/atoms/composed/DateTimePicker/index.ts +2 -0
  34. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
  35. package/src/atoms/composed/DetailedCard/index.ts +2 -0
  36. package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
  37. package/src/atoms/composed/EmptyState/index.ts +1 -0
  38. package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
  39. package/src/atoms/composed/FileUpload/index.ts +2 -0
  40. package/src/atoms/composed/FormField/FormField.tsx +92 -0
  41. package/src/atoms/composed/FormField/index.ts +1 -0
  42. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
  43. package/src/atoms/composed/GlobalSearch/index.ts +1 -0
  44. package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
  45. package/src/atoms/composed/IconBadge/index.ts +2 -0
  46. package/src/atoms/composed/Modal/Modal.tsx +223 -0
  47. package/src/atoms/composed/Modal/index.ts +2 -0
  48. package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
  49. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
  50. package/src/atoms/composed/ProgressBar/index.ts +1 -0
  51. package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
  52. package/src/atoms/composed/StatCard/index.ts +1 -0
  53. package/src/atoms/composed/StyleGuide.tsx +717 -0
  54. package/src/atoms/composed/Toast/Toast.tsx +219 -0
  55. package/src/atoms/composed/Toast/index.ts +1 -0
  56. package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
  57. package/src/atoms/composed/Tooltip/index.ts +1 -0
  58. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
  59. package/src/atoms/composed/UserAvatar/index.ts +1 -0
  60. package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
  61. package/src/atoms/composed/UserMenu/index.ts +1 -0
  62. package/src/atoms/composed/index.ts +29 -0
  63. package/src/atoms/hooks/useApi.ts +80 -0
  64. package/src/atoms/hooks/useHealth.ts +17 -0
  65. package/src/atoms/index.ts +13 -0
  66. package/src/atoms/services/api/client.ts +134 -0
  67. package/src/atoms/services/auth-service.ts +248 -0
  68. package/src/atoms/services/health.ts +15 -0
  69. package/src/atoms/services/index.ts +3 -0
  70. package/src/atoms/shared/config/constants.ts +17 -0
  71. package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
  72. package/src/atoms/shared/config/environment.ts +10 -0
  73. package/src/atoms/shared/index.ts +4 -0
  74. package/src/atoms/shared/styles/color-palettes.css +566 -0
  75. package/src/atoms/types/auth.ts +62 -0
  76. package/src/atoms/types/generated.ts +1469 -0
  77. package/src/atoms/types/index.ts +4 -0
  78. package/src/atoms/types/loading.ts +28 -0
  79. package/src/atoms/ui/Badge.tsx +30 -0
  80. package/src/atoms/ui/ErrorBoundary.tsx +59 -0
  81. package/src/atoms/ui/Select.tsx +53 -0
  82. package/src/atoms/ui/Switch.tsx +42 -0
  83. package/src/atoms/ui/Tabs.tsx +118 -0
  84. package/src/atoms/ui/avatar.tsx +48 -0
  85. package/src/atoms/ui/button.tsx +70 -0
  86. package/src/atoms/ui/card.tsx +76 -0
  87. package/src/atoms/ui/dropdown-menu.tsx +199 -0
  88. package/src/atoms/ui/index.ts +39 -0
  89. package/src/atoms/ui/input.tsx +23 -0
  90. package/src/atoms/ui/label.tsx +23 -0
  91. package/src/atoms/ui/skeleton.tsx +13 -0
  92. package/src/atoms/ui/spinner.tsx +49 -0
  93. package/src/atoms/ui/table.tsx +116 -0
  94. package/src/atoms/utils/animations.ts +135 -0
  95. package/src/atoms/utils/tooltip-helpers.ts +140 -0
  96. package/src/atoms/utils/utils.ts +9 -0
  97. package/src/features/auth/components/LoginForm.tsx +168 -0
  98. package/src/features/auth/components/LogoutButton.tsx +19 -0
  99. package/src/features/auth/components/ProtectedRoute.tsx +60 -0
  100. package/src/features/auth/components/index.ts +4 -0
  101. package/src/features/auth/hooks/index.ts +2 -0
  102. package/src/features/auth/hooks/useAuth.tsx +205 -0
  103. package/src/features/auth/hooks/usePermissions.ts +35 -0
  104. package/src/features/auth/index.ts +2 -0
  105. package/src/features/index.ts +2 -0
  106. package/src/index.css +704 -0
  107. package/src/index.ts +13 -0
  108. package/src/main.tsx +48 -0
  109. package/src/molecules/.gitkeep +0 -0
  110. package/src/molecules/forms/FormGroup.tsx +75 -0
  111. package/src/molecules/forms/SearchInput.tsx +259 -0
  112. package/src/molecules/forms/index.ts +4 -0
  113. package/src/molecules/index.ts +4 -0
  114. package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
  115. package/src/molecules/layout/AppHeader/index.ts +1 -0
  116. package/src/molecules/layout/AppLayout.tsx +29 -0
  117. package/src/molecules/layout/PageTemplate.tsx +87 -0
  118. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
  119. package/src/molecules/layout/SectionHeader/index.ts +1 -0
  120. package/src/molecules/layout/ShowcaseSection.tsx +57 -0
  121. package/src/molecules/layout/Sidebar.tsx +144 -0
  122. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
  123. package/src/molecules/layout/SidebarButton/index.ts +1 -0
  124. package/src/molecules/layout/SidebarContext.tsx +31 -0
  125. package/src/molecules/layout/index.ts +7 -0
  126. package/src/molecules/navigation/NavMenu.tsx +188 -0
  127. package/src/molecules/navigation/Pagination.tsx +172 -0
  128. package/src/molecules/navigation/index.ts +4 -0
  129. package/src/organisms/index.ts +5 -0
  130. package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
  131. package/src/organisms/showcase/index.ts +1 -0
  132. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
  133. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
  134. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
  135. package/src/pages/AdminShowcase/index.tsx +3 -0
  136. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
  137. package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
  138. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
  139. package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
  140. package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
  141. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
  142. package/src/pages/ComponentShowcase/index.tsx +188 -0
  143. package/src/pages/index.ts +2 -0
  144. package/src/templates/AuthTemplate.tsx +216 -0
  145. package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
  146. package/src/templates/DashboardTemplate.tsx +232 -0
  147. package/src/templates/DataTemplate.tsx +319 -0
  148. package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
  149. package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
  150. package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
  151. package/src/templates/admin/index.ts +29 -0
  152. package/src/templates/factory.tsx +169 -0
  153. package/src/templates/index.ts +37 -0
  154. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,181 @@
1
+ import React from 'react';
2
+ import { Card } from '../../ui/card';
3
+ import { DataBadge } from '../DataBadge/DataBadge';
4
+ import { ArrowRight, TrendingUp, TrendingDown, Minus } from 'lucide-react';
5
+ import { cn } from '../../utils/utils';
6
+
7
+ export interface DetailedCardProps {
8
+ title: string;
9
+ subtitle?: string;
10
+ description?: string;
11
+ primaryMetric: {
12
+ label: string;
13
+ value: string | number;
14
+ trend?: {
15
+ value: number;
16
+ label?: string;
17
+ };
18
+ };
19
+ secondaryMetrics?: Array<{
20
+ label: string;
21
+ value: string | number;
22
+ status?: 'success' | 'warning' | 'error' | 'info' | 'neutral';
23
+ }>;
24
+ tags?: Array<{
25
+ label: string;
26
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
27
+ status?: 'success' | 'warning' | 'error' | 'info' | 'neutral';
28
+ }>;
29
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
30
+ icon?: React.ReactNode;
31
+ onClick?: () => void;
32
+ className?: string;
33
+ }
34
+
35
+ export const DetailedCard = ({
36
+ title,
37
+ subtitle,
38
+ description,
39
+ primaryMetric,
40
+ secondaryMetrics = [],
41
+ tags = [],
42
+ category,
43
+ icon,
44
+ onClick,
45
+ className
46
+ }: DetailedCardProps) => {
47
+ const getTrendIcon = (trendValue: number) => {
48
+ if (trendValue > 0) {
49
+ return <TrendingUp className="w-3 h-3" />;
50
+ } else if (trendValue < 0) {
51
+ return <TrendingDown className="w-3 h-3" />;
52
+ }
53
+ return <Minus className="w-3 h-3" />;
54
+ };
55
+
56
+ const getTrendColor = (trendValue: number) => {
57
+ if (trendValue > 0) return 'text-status-success';
58
+ if (trendValue < 0) return 'text-status-error';
59
+ return 'text-status-neutral';
60
+ };
61
+
62
+ // We don't need to create category classes manually - let the Card component handle it
63
+
64
+ return (
65
+ <Card
66
+ className={cn(
67
+ "card-container relative overflow-hidden transition-all animate-slide-up",
68
+ onClick && "cursor-pointer hover:shadow-md hover:scale-[1.01] active:scale-[0.99]",
69
+ category && `hover:shadow-category-${category}`,
70
+ "group",
71
+ className
72
+ )}
73
+ category={category} // Pass category directly to Card component
74
+ data-component-name="DetailedCard"
75
+ onClick={onClick}
76
+ role={onClick ? 'button' : undefined}
77
+ tabIndex={onClick ? 0 : undefined}
78
+ onKeyDown={onClick ? (e) => {
79
+ if (e.key === 'Enter' || e.key === ' ') {
80
+ e.preventDefault();
81
+ onClick();
82
+ }
83
+ } : undefined}
84
+ >
85
+ <div className="space-y-4">
86
+ {/* Header */}
87
+ <div className="flex items-start justify-between">
88
+ <div className="flex-1 min-w-0">
89
+ <div className="flex items-center gap-2 mb-1">
90
+ <h3 className="text-xl font-bold text-foreground truncate">{title}</h3>
91
+ {icon && (
92
+ <div className={cn(
93
+ "p-3 rounded-lg flex items-center justify-center flex-shrink-0 transition-colors",
94
+ category ? `bg-category-${category}/8 text-category-${category}` : "bg-muted/50 text-muted-foreground"
95
+ )}>
96
+ <div className="scale-110">
97
+ {icon}
98
+ </div>
99
+ </div>
100
+ )}
101
+ </div>
102
+ {subtitle && (
103
+ <p className="text-data-meta text-sm">{subtitle}</p>
104
+ )}
105
+ </div>
106
+ {onClick && (
107
+ <ArrowRight className="w-4 h-4 text-muted-foreground ml-2 flex-shrink-0 transition-transform group-hover:translate-x-1" data-component-name="DetailedCardArrow" />
108
+ )}
109
+ </div>
110
+
111
+ {/* Description */}
112
+ {description && (
113
+ <p className="text-sm text-muted-foreground leading-relaxed">{description}</p>
114
+ )}
115
+
116
+ {/* Primary Metric */}
117
+ <div className="space-y-2">
118
+ <div className="text-data-label text-xs">{primaryMetric.label}</div>
119
+ <div className="flex items-baseline gap-3">
120
+ <span className={cn(
121
+ "text-2xl font-bold transition-all group-hover:scale-105",
122
+ category ? `text-category-${category}` : "text-foreground"
123
+ )}>
124
+ {primaryMetric.value}
125
+ </span>
126
+ {primaryMetric.trend && (
127
+ <div className={cn(
128
+ "flex items-center gap-1 text-xs",
129
+ getTrendColor(primaryMetric.trend.value)
130
+ )}>
131
+ {getTrendIcon(primaryMetric.trend.value)}
132
+ <span className="font-medium">
133
+ {primaryMetric.trend.value > 0 && '+'}{primaryMetric.trend.value}%
134
+ </span>
135
+ {primaryMetric.trend.label && (
136
+ <span className="text-data-meta ml-1">{primaryMetric.trend.label}</span>
137
+ )}
138
+ </div>
139
+ )}
140
+ </div>
141
+ </div>
142
+
143
+ {/* Secondary Metrics */}
144
+ {secondaryMetrics.length > 0 && (
145
+ <div className="grid grid-cols-2 gap-3 py-2">
146
+ {secondaryMetrics.map((metric, index) => (
147
+ <div key={index} className="space-y-1">
148
+ <div className="text-data-label text-xs">{metric.label}</div>
149
+ <div className="flex items-center gap-2">
150
+ <span className="text-sm font-semibold text-foreground">{metric.value}</span>
151
+ {metric.status && (
152
+ <DataBadge variant="status" status={metric.status} size="sm">
153
+ {metric.status}
154
+ </DataBadge>
155
+ )}
156
+ </div>
157
+ </div>
158
+ ))}
159
+ </div>
160
+ )}
161
+
162
+ {/* Tags */}
163
+ {tags.length > 0 && (
164
+ <div className="flex flex-wrap gap-2 pt-2 border-t border-border">
165
+ {tags.map((tag, index) => (
166
+ <DataBadge
167
+ key={index}
168
+ variant={tag.category ? 'category' : 'status'}
169
+ category={tag.category}
170
+ status={tag.status}
171
+ size="sm"
172
+ >
173
+ {tag.label}
174
+ </DataBadge>
175
+ ))}
176
+ </div>
177
+ )}
178
+ </div>
179
+ </Card>
180
+ );
181
+ };
@@ -0,0 +1,2 @@
1
+ export { DetailedCard } from './DetailedCard';
2
+ export type { DetailedCardProps } from './DetailedCard';
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import { Button } from '../../ui/button';
3
+ import { cn } from '../../utils/utils';
4
+ import { FileX, Search, AlertCircle, Database } from 'lucide-react';
5
+
6
+ export interface EmptyStateProps {
7
+ variant?: 'no-data' | 'no-results' | 'error' | 'loading';
8
+ title: string;
9
+ description?: string;
10
+ icon?: React.ReactNode;
11
+ action?: {
12
+ label: string;
13
+ onClick: () => void;
14
+ };
15
+ secondaryAction?: {
16
+ label: string;
17
+ onClick: () => void;
18
+ };
19
+ className?: string;
20
+ }
21
+
22
+ const defaultIcons = {
23
+ 'no-data': <Database className="w-12 h-12" />,
24
+ 'no-results': <Search className="w-12 h-12" />,
25
+ 'error': <AlertCircle className="w-12 h-12" />,
26
+ 'loading': <FileX className="w-12 h-12" />,
27
+ };
28
+
29
+ export const EmptyState = ({
30
+ variant = 'no-data',
31
+ title,
32
+ description,
33
+ icon,
34
+ action,
35
+ secondaryAction,
36
+ className
37
+ }: EmptyStateProps) => {
38
+ const displayIcon = icon || defaultIcons[variant];
39
+
40
+ return (
41
+ <div className={cn(
42
+ "flex flex-col items-center justify-center py-12 px-4 text-center",
43
+ className
44
+ )}
45
+ data-component-name="EmptyState"
46
+ data-variant={variant}
47
+ >
48
+ {/* Icon */}
49
+ <div className={cn(
50
+ "mb-4 transition-all",
51
+ variant === 'no-data' && "text-category-1 opacity-60",
52
+ variant === 'no-results' && "text-category-2 opacity-60",
53
+ variant === 'error' && "text-status-error opacity-60",
54
+ variant === 'loading' && "text-category-3 opacity-60 animate-pulse"
55
+ )}
56
+ data-component-name="EmptyStateIcon"
57
+ >
58
+ {displayIcon}
59
+ </div>
60
+
61
+ {/* Title */}
62
+ <h3 className="text-lg font-semibold text-foreground mb-2" data-component-name="EmptyStateTitle">
63
+ {title}
64
+ </h3>
65
+
66
+ {/* Description */}
67
+ {description && (
68
+ <p className="text-muted-foreground max-w-md mb-6" data-component-name="EmptyStateDescription">
69
+ {description}
70
+ </p>
71
+ )}
72
+
73
+ {/* Actions */}
74
+ {(action || secondaryAction) && (
75
+ <div className="flex flex-col sm:flex-row gap-3" data-component-name="EmptyStateActions">
76
+ {action && (
77
+ <Button onClick={action.onClick}>
78
+ {action.label}
79
+ </Button>
80
+ )}
81
+ {secondaryAction && (
82
+ <Button variant="outline" onClick={secondaryAction.onClick}>
83
+ {secondaryAction.label}
84
+ </Button>
85
+ )}
86
+ </div>
87
+ )}
88
+ </div>
89
+ );
90
+ };
@@ -0,0 +1 @@
1
+ export * from './EmptyState';