@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,219 @@
1
+ import React from 'react';
2
+ import { Card } from '../../ui/card';
3
+ import { Skeleton } from '../../ui/skeleton';
4
+ import { Tooltip } from '../Tooltip';
5
+ import { TrendingUp, TrendingDown, Minus, ArrowRight } from 'lucide-react';
6
+ import { cn } from '../../utils/utils';
7
+ import type { ILoadable } from '../../types';
8
+
9
+ export interface StatCardProps extends ILoadable {
10
+ title: string;
11
+ value: string | number;
12
+ subtitle?: string;
13
+ trend?: {
14
+ value: number;
15
+ label?: string;
16
+ };
17
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
18
+ icon?: React.ReactNode;
19
+ onClick?: () => void;
20
+ className?: string;
21
+ /** Tooltip content for the value (useful for showing full numbers when abbreviated) */
22
+ valueTooltip?: string;
23
+ /** Tooltip content for the icon */
24
+ iconTooltip?: string;
25
+ }
26
+
27
+ export const StatCard = ({
28
+ title,
29
+ value,
30
+ subtitle,
31
+ trend,
32
+ category,
33
+ icon,
34
+ onClick,
35
+ className,
36
+ valueTooltip,
37
+ iconTooltip,
38
+ isLoading = false
39
+ }: StatCardProps) => {
40
+ const getTrendIcon = () => {
41
+ if (!trend) return null;
42
+
43
+ if (trend.value > 0) {
44
+ return <TrendingUp className="w-4 h-4" />;
45
+ } else if (trend.value < 0) {
46
+ return <TrendingDown className="w-4 h-4" />;
47
+ }
48
+ return <Minus className="w-4 h-4" />;
49
+ };
50
+
51
+ const getTrendColor = () => {
52
+ if (!trend) return '';
53
+
54
+ if (trend.value > 0) return 'text-status-success';
55
+ if (trend.value < 0) return 'text-status-error';
56
+ return 'text-status-neutral';
57
+ };
58
+
59
+ // Loading state skeleton that matches StatCard dimensions
60
+ if (isLoading) {
61
+ return (
62
+ <Card
63
+ className={cn(
64
+ "card-container relative overflow-hidden",
65
+ className
66
+ )}
67
+ category={category}
68
+ data-component-name="StatCard"
69
+ >
70
+ <div className="space-y-3 relative">
71
+ {/* Hidden content that defines exact dimensions */}
72
+ <div className="flex items-center justify-between">
73
+ <div className="text-data-label font-medium" style={{ visibility: 'hidden' }}>
74
+ {title}
75
+ </div>
76
+ {icon && (
77
+ <div className={cn(
78
+ "p-2 rounded-md flex items-center justify-center transition-colors",
79
+ category ? `bg-muted/50 text-category-${category}` : "bg-muted/50 text-muted-foreground"
80
+ )} style={{ visibility: 'hidden' }}>
81
+ {icon}
82
+ </div>
83
+ )}
84
+ </div>
85
+
86
+ <div className="space-y-1">
87
+ <div className={cn(
88
+ "text-3xl font-bold transition-all group-hover:scale-105",
89
+ category ? `text-category-${category}` : "text-foreground"
90
+ )} style={{ visibility: 'hidden' }}>
91
+ {value}
92
+ </div>
93
+ {subtitle && (
94
+ <div className="text-data-meta" style={{ visibility: 'hidden' }}>
95
+ {subtitle}
96
+ </div>
97
+ )}
98
+ </div>
99
+
100
+ <div className="flex items-center justify-between">
101
+ {trend && (
102
+ <div className={cn("flex items-center gap-1.5 text-sm", "text-muted-foreground")} style={{ visibility: 'hidden' }}>
103
+ {/* Render actual trend content hidden */}
104
+ <span>{trend.value > 0 && '+'}{trend.value}%</span>
105
+ {trend.label && <span>{trend.label}</span>}
106
+ </div>
107
+ )}
108
+ {onClick && (
109
+ <div style={{ visibility: 'hidden' }}>→</div>
110
+ )}
111
+ </div>
112
+
113
+ {/* Skeleton overlay positioned absolutely */}
114
+ <div className="absolute inset-0 space-y-3">
115
+ <div className="flex items-center justify-between">
116
+ <Skeleton className="h-5 w-24" />
117
+ {icon && (
118
+ <div className="p-2 rounded flex items-center justify-center bg-muted/50">
119
+ <Skeleton className="h-5 w-5" />
120
+ </div>
121
+ )}
122
+ </div>
123
+ <div className="space-y-1">
124
+ <Skeleton className="h-9 w-20" />
125
+ {subtitle && <Skeleton className="h-4 w-32" />}
126
+ </div>
127
+ <div className="flex items-center justify-between">
128
+ {trend && <Skeleton className="h-4 w-16" />}
129
+ {onClick && <Skeleton className="h-4 w-4 ml-auto" />}
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </Card>
134
+ );
135
+ }
136
+
137
+ return (
138
+ <Card
139
+ className={cn(
140
+ "card-container relative overflow-hidden transition-all animate-slide-up",
141
+ onClick && "cursor-pointer hover:shadow-md hover:scale-105 active:scale-95",
142
+ category && `hover:shadow-category-${category}`,
143
+ "group",
144
+ className
145
+ )}
146
+ category={category} // Pass category directly to Card component
147
+ data-component-name="StatCard"
148
+ onClick={onClick}
149
+ role={onClick ? 'button' : undefined}
150
+ tabIndex={onClick ? 0 : undefined}
151
+ onKeyDown={onClick ? (e) => {
152
+ if (e.key === 'Enter' || e.key === ' ') {
153
+ e.preventDefault();
154
+ onClick();
155
+ }
156
+ } : undefined}
157
+ >
158
+ <div className="space-y-3">
159
+ {/* Header */}
160
+ <div className="flex items-center justify-between">
161
+ <div className="text-sm font-semibold text-foreground" data-component-name="StatCard">{title}</div>
162
+ {icon && (
163
+ <Tooltip
164
+ content={iconTooltip}
165
+ disabled={!iconTooltip}
166
+ position="left"
167
+ size="sm"
168
+ >
169
+ <div className={cn(
170
+ "p-2 rounded-md flex items-center justify-center transition-colors",
171
+ category ? `bg-muted/50 text-category-${category}` : "bg-muted/50 text-muted-foreground"
172
+ )} data-component-name="StatCardIcon">
173
+ {icon}
174
+ </div>
175
+ </Tooltip>
176
+ )}
177
+ </div>
178
+
179
+ {/* Value */}
180
+ <div className="space-y-1">
181
+ <Tooltip
182
+ content={valueTooltip}
183
+ disabled={!valueTooltip}
184
+ position="top"
185
+ size="sm"
186
+ >
187
+ <div className={cn(
188
+ "text-3xl font-bold transition-all group-hover:scale-105",
189
+ category ? `text-category-${category}` : "text-foreground"
190
+ )}>
191
+ {value}
192
+ </div>
193
+ </Tooltip>
194
+ {subtitle && (
195
+ <div className="text-data-meta">{subtitle}</div>
196
+ )}
197
+ </div>
198
+
199
+ {/* Trend or Action */}
200
+ <div className="flex items-center justify-between">
201
+ {trend && (
202
+ <div className={cn("flex items-center gap-1.5 text-sm", getTrendColor())}>
203
+ {getTrendIcon()}
204
+ <span className="font-medium">
205
+ {trend.value > 0 && '+'}{trend.value}%
206
+ </span>
207
+ {trend.label && (
208
+ <span className="text-data-meta">{trend.label}</span>
209
+ )}
210
+ </div>
211
+ )}
212
+ {onClick && (
213
+ <ArrowRight className="w-4 h-4 text-muted-foreground ml-auto transition-transform group-hover:translate-x-1" data-component-name="StatCardArrow" />
214
+ )}
215
+ </div>
216
+ </div>
217
+ </Card>
218
+ );
219
+ };
@@ -0,0 +1 @@
1
+ export * from './StatCard';