@pattern-stack/frontend-patterns 0.0.1 → 0.0.3

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 (153) hide show
  1. package/README.md +6 -6
  2. package/package.json +3 -5
  3. package/src/App.css +0 -42
  4. package/src/App.tsx +0 -54
  5. package/src/__tests__/README.md +0 -221
  6. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +0 -44
  7. package/src/__tests__/atoms/ui/button.test.tsx +0 -68
  8. package/src/__tests__/atoms/utils/simple.test.ts +0 -18
  9. package/src/__tests__/atoms/utils/utils.test.ts +0 -77
  10. package/src/__tests__/features/auth/simple-auth.test.tsx +0 -40
  11. package/src/__tests__/molecules/layout/simple-layout.test.tsx +0 -81
  12. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +0 -167
  13. package/src/__tests__/setup.ts +0 -51
  14. package/src/__tests__/utils.tsx +0 -123
  15. package/src/atoms/composed/Accordion/Accordion.tsx +0 -271
  16. package/src/atoms/composed/Accordion/index.ts +0 -1
  17. package/src/atoms/composed/Alert/Alert.tsx +0 -132
  18. package/src/atoms/composed/Alert/index.ts +0 -1
  19. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +0 -83
  20. package/src/atoms/composed/Breadcrumb/index.ts +0 -1
  21. package/src/atoms/composed/Chart/Chart.tsx +0 -425
  22. package/src/atoms/composed/Chart/index.ts +0 -2
  23. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +0 -72
  24. package/src/atoms/composed/ColorSwatch/index.ts +0 -1
  25. package/src/atoms/composed/DarkModeToggle.tsx +0 -66
  26. package/src/atoms/composed/DataBadge/DataBadge.tsx +0 -81
  27. package/src/atoms/composed/DataBadge/index.ts +0 -1
  28. package/src/atoms/composed/DataTable/DataTable.tsx +0 -394
  29. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +0 -41
  30. package/src/atoms/composed/DataTable/index.ts +0 -2
  31. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +0 -611
  32. package/src/atoms/composed/DateTimePicker/index.ts +0 -2
  33. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +0 -181
  34. package/src/atoms/composed/DetailedCard/index.ts +0 -2
  35. package/src/atoms/composed/EmptyState/EmptyState.tsx +0 -90
  36. package/src/atoms/composed/EmptyState/index.ts +0 -1
  37. package/src/atoms/composed/FileUpload/FileUpload.tsx +0 -477
  38. package/src/atoms/composed/FileUpload/index.ts +0 -2
  39. package/src/atoms/composed/FormField/FormField.tsx +0 -92
  40. package/src/atoms/composed/FormField/index.ts +0 -1
  41. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +0 -37
  42. package/src/atoms/composed/GlobalSearch/index.ts +0 -1
  43. package/src/atoms/composed/IconBadge/IconBadge.tsx +0 -95
  44. package/src/atoms/composed/IconBadge/index.ts +0 -2
  45. package/src/atoms/composed/Modal/Modal.tsx +0 -223
  46. package/src/atoms/composed/Modal/index.ts +0 -2
  47. package/src/atoms/composed/PaletteSwitcher.tsx +0 -386
  48. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +0 -116
  49. package/src/atoms/composed/ProgressBar/index.ts +0 -1
  50. package/src/atoms/composed/StatCard/StatCard.tsx +0 -219
  51. package/src/atoms/composed/StatCard/index.ts +0 -1
  52. package/src/atoms/composed/StyleGuide.tsx +0 -717
  53. package/src/atoms/composed/Toast/Toast.tsx +0 -219
  54. package/src/atoms/composed/Toast/index.ts +0 -1
  55. package/src/atoms/composed/Tooltip/Tooltip.tsx +0 -213
  56. package/src/atoms/composed/Tooltip/index.ts +0 -1
  57. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +0 -139
  58. package/src/atoms/composed/UserAvatar/index.ts +0 -1
  59. package/src/atoms/composed/UserMenu/UserMenu.tsx +0 -16
  60. package/src/atoms/composed/UserMenu/index.ts +0 -1
  61. package/src/atoms/composed/index.ts +0 -29
  62. package/src/atoms/hooks/useApi.ts +0 -80
  63. package/src/atoms/hooks/useHealth.ts +0 -17
  64. package/src/atoms/index.ts +0 -13
  65. package/src/atoms/services/api/client.ts +0 -134
  66. package/src/atoms/services/auth-service.ts +0 -248
  67. package/src/atoms/services/health.ts +0 -15
  68. package/src/atoms/services/index.ts +0 -3
  69. package/src/atoms/shared/config/constants.ts +0 -17
  70. package/src/atoms/shared/config/dashboard-sizes.ts +0 -111
  71. package/src/atoms/shared/config/environment.ts +0 -10
  72. package/src/atoms/shared/index.ts +0 -4
  73. package/src/atoms/shared/styles/color-palettes.css +0 -566
  74. package/src/atoms/types/auth.ts +0 -62
  75. package/src/atoms/types/generated.ts +0 -1469
  76. package/src/atoms/types/index.ts +0 -4
  77. package/src/atoms/types/loading.ts +0 -28
  78. package/src/atoms/ui/Badge.tsx +0 -30
  79. package/src/atoms/ui/ErrorBoundary.tsx +0 -59
  80. package/src/atoms/ui/Select.tsx +0 -53
  81. package/src/atoms/ui/Switch.tsx +0 -42
  82. package/src/atoms/ui/Tabs.tsx +0 -118
  83. package/src/atoms/ui/avatar.tsx +0 -48
  84. package/src/atoms/ui/button.tsx +0 -70
  85. package/src/atoms/ui/card.tsx +0 -76
  86. package/src/atoms/ui/dropdown-menu.tsx +0 -199
  87. package/src/atoms/ui/index.ts +0 -39
  88. package/src/atoms/ui/input.tsx +0 -23
  89. package/src/atoms/ui/label.tsx +0 -23
  90. package/src/atoms/ui/skeleton.tsx +0 -13
  91. package/src/atoms/ui/spinner.tsx +0 -49
  92. package/src/atoms/ui/table.tsx +0 -116
  93. package/src/atoms/utils/animations.ts +0 -135
  94. package/src/atoms/utils/tooltip-helpers.ts +0 -140
  95. package/src/atoms/utils/utils.ts +0 -9
  96. package/src/features/auth/components/LoginForm.tsx +0 -168
  97. package/src/features/auth/components/LogoutButton.tsx +0 -19
  98. package/src/features/auth/components/ProtectedRoute.tsx +0 -60
  99. package/src/features/auth/components/index.ts +0 -4
  100. package/src/features/auth/hooks/index.ts +0 -2
  101. package/src/features/auth/hooks/useAuth.tsx +0 -205
  102. package/src/features/auth/hooks/usePermissions.ts +0 -35
  103. package/src/features/auth/index.ts +0 -2
  104. package/src/features/index.ts +0 -2
  105. package/src/index.css +0 -704
  106. package/src/index.ts +0 -13
  107. package/src/main.tsx +0 -48
  108. package/src/molecules/.gitkeep +0 -0
  109. package/src/molecules/forms/FormGroup.tsx +0 -75
  110. package/src/molecules/forms/SearchInput.tsx +0 -259
  111. package/src/molecules/forms/index.ts +0 -4
  112. package/src/molecules/index.ts +0 -4
  113. package/src/molecules/layout/AppHeader/AppHeader.tsx +0 -42
  114. package/src/molecules/layout/AppHeader/index.ts +0 -1
  115. package/src/molecules/layout/AppLayout.tsx +0 -29
  116. package/src/molecules/layout/PageTemplate.tsx +0 -87
  117. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +0 -87
  118. package/src/molecules/layout/SectionHeader/index.ts +0 -1
  119. package/src/molecules/layout/ShowcaseSection.tsx +0 -57
  120. package/src/molecules/layout/Sidebar.tsx +0 -144
  121. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +0 -99
  122. package/src/molecules/layout/SidebarButton/index.ts +0 -1
  123. package/src/molecules/layout/SidebarContext.tsx +0 -31
  124. package/src/molecules/layout/index.ts +0 -7
  125. package/src/molecules/navigation/NavMenu.tsx +0 -188
  126. package/src/molecules/navigation/Pagination.tsx +0 -172
  127. package/src/molecules/navigation/index.ts +0 -4
  128. package/src/organisms/index.ts +0 -5
  129. package/src/organisms/showcase/ComponentShowcasePage.tsx +0 -2496
  130. package/src/organisms/showcase/index.ts +0 -1
  131. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +0 -242
  132. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +0 -171
  133. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +0 -385
  134. package/src/pages/AdminShowcase/index.tsx +0 -3
  135. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +0 -188
  136. package/src/pages/ComponentShowcase/CardsShowcase.tsx +0 -392
  137. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +0 -207
  138. package/src/pages/ComponentShowcase/StatesShowcase.tsx +0 -485
  139. package/src/pages/ComponentShowcase/TablesShowcase.tsx +0 -134
  140. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +0 -255
  141. package/src/pages/ComponentShowcase/index.tsx +0 -188
  142. package/src/pages/index.ts +0 -2
  143. package/src/templates/AuthTemplate.tsx +0 -216
  144. package/src/templates/ComponentShowcaseTemplate.tsx +0 -173
  145. package/src/templates/DashboardTemplate.tsx +0 -232
  146. package/src/templates/DataTemplate.tsx +0 -319
  147. package/src/templates/admin/AdminCRUDTemplate.tsx +0 -630
  148. package/src/templates/admin/AdminDashboardTemplate.tsx +0 -351
  149. package/src/templates/admin/AdminDetailTemplate.tsx +0 -563
  150. package/src/templates/admin/index.ts +0 -29
  151. package/src/templates/factory.tsx +0 -169
  152. package/src/templates/index.ts +0 -37
  153. package/src/vite-env.d.ts +0 -1
@@ -1,563 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { cn } from '../../atoms/utils/utils';
3
- import { SectionHeader } from '../../molecules/layout/SectionHeader';
4
- import { Card } from '../../atoms/ui/card';
5
- import { Button } from '../../atoms/ui/button';
6
- import { DataBadge } from '../../atoms/composed/DataBadge';
7
- import { UserAvatar } from '../../atoms/composed/UserAvatar';
8
- import { DataTable, type Column } from '../../atoms/composed/DataTable';
9
- import {
10
- ArrowLeft,
11
- Edit,
12
- Copy,
13
- ExternalLink,
14
- History,
15
- Activity,
16
- FileText,
17
- Save,
18
- X
19
- } from 'lucide-react';
20
-
21
- export interface TabConfig {
22
- /** Tab ID */
23
- id: string;
24
- /** Tab label */
25
- label: string;
26
- /** Tab icon */
27
- icon?: React.ReactNode;
28
- /** Tab content */
29
- content: React.ReactNode;
30
- /** Whether tab is disabled */
31
- disabled?: boolean;
32
- /** Badge text for tab */
33
- badge?: string | number;
34
- }
35
-
36
- export interface ActionConfig {
37
- /** Action ID */
38
- id: string;
39
- /** Action label */
40
- label: string;
41
- /** Action icon */
42
- icon?: React.ReactNode;
43
- /** Action variant */
44
- variant?: 'default' | 'outline' | 'destructive' | 'secondary';
45
- /** Action handler */
46
- onClick: () => void;
47
- /** Whether action is disabled */
48
- disabled?: boolean;
49
- /** Action permission required */
50
- permission?: string;
51
- }
52
-
53
- export interface FieldSection {
54
- /** Section title */
55
- title: string;
56
- /** Section description */
57
- description?: string;
58
- /** Section fields */
59
- fields: DetailField[];
60
- /** Section category for styling */
61
- category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
62
- /** Whether section is collapsible */
63
- collapsible?: boolean;
64
- /** Whether section is initially collapsed */
65
- defaultCollapsed?: boolean;
66
- }
67
-
68
- export interface DetailField {
69
- /** Field key */
70
- key: string;
71
- /** Field label */
72
- label: string;
73
- /** Field type for rendering */
74
- type?: 'text' | 'email' | 'phone' | 'url' | 'date' | 'datetime' | 'boolean' | 'badge' | 'avatar' | 'json' | 'custom';
75
- /** Custom render function */
76
- render?: (value: unknown, data: Record<string, unknown>) => React.ReactNode;
77
- /** Whether field is editable inline */
78
- editable?: boolean;
79
- /** Field validation for editing */
80
- validation?: Record<string, unknown>;
81
- /** Copy to clipboard button */
82
- copyable?: boolean;
83
- }
84
-
85
- export interface RelatedData {
86
- /** Related data title */
87
- title: string;
88
- /** Related data description */
89
- description?: string;
90
- /** Related data items */
91
- data: Record<string, unknown>[];
92
- /** Columns for related data table */
93
- columns: Column<Record<string, unknown>>[];
94
- /** Actions for related data */
95
- actions?: ActionConfig[];
96
- /** Whether to show pagination */
97
- showPagination?: boolean;
98
- /** Category for styling */
99
- category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
100
- }
101
-
102
- export interface AuditEntry {
103
- /** Entry ID */
104
- id: string;
105
- /** Action performed */
106
- action: string;
107
- /** User who performed action */
108
- user: string;
109
- /** Timestamp */
110
- timestamp: Date;
111
- /** Fields changed */
112
- changes?: Array<{
113
- field: string;
114
- oldValue?: unknown;
115
- newValue?: unknown;
116
- }>;
117
- /** Additional metadata */
118
- metadata?: Record<string, unknown>;
119
- }
120
-
121
- export interface AdminDetailTemplateProps {
122
- /** Resource title */
123
- title: string;
124
- /** Resource subtitle */
125
- subtitle?: string;
126
- /** Resource type */
127
- resourceType: string;
128
- /** Main resource data */
129
- data: Record<string, unknown>;
130
- /** Field sections to display */
131
- sections: FieldSection[];
132
- /** Tab configuration */
133
- tabs?: TabConfig[];
134
- /** Available actions */
135
- actions?: ActionConfig[];
136
- /** Related data sections */
137
- relatedData?: RelatedData[];
138
- /** Audit trail */
139
- auditTrail?: AuditEntry[];
140
- /** Back navigation handler */
141
- onBack?: () => void;
142
- /** Data update handler */
143
- onUpdate?: (field: string, value: unknown) => Promise<void>;
144
- /** Whether data is loading */
145
- isLoading?: boolean;
146
- /** Additional CSS classes */
147
- className?: string;
148
- /** Category for styling */
149
- category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
150
- /** Custom sidebar content */
151
- sidebar?: React.ReactNode;
152
- /** Whether to show audit trail */
153
- showAuditTrail?: boolean;
154
- }
155
-
156
- export const AdminDetailTemplate: React.FC<AdminDetailTemplateProps> = ({
157
- title,
158
- subtitle,
159
- resourceType,
160
- data,
161
- sections,
162
- tabs = [],
163
- actions = [],
164
- relatedData = [],
165
- auditTrail = [],
166
- onBack,
167
- onUpdate,
168
- isLoading = false,
169
- className,
170
- category = 1,
171
- sidebar,
172
- showAuditTrail = true
173
- }) => {
174
- const [activeTab, setActiveTab] = useState(tabs[0]?.id || 'overview');
175
- const [editingField, setEditingField] = useState<string | null>(null);
176
- const [editValue, setEditValue] = useState<unknown>('');
177
-
178
- const handleEdit = (field: DetailField, currentValue: unknown) => {
179
- setEditingField(field.key);
180
- setEditValue(currentValue);
181
- };
182
-
183
- const handleSaveEdit = async () => {
184
- if (editingField && onUpdate) {
185
- try {
186
- await onUpdate(editingField, editValue);
187
- setEditingField(null);
188
- setEditValue('');
189
- } catch (error) {
190
- console.error('Update failed:', error);
191
- }
192
- }
193
- };
194
-
195
- const handleCancelEdit = () => {
196
- setEditingField(null);
197
- setEditValue('');
198
- };
199
-
200
- const copyToClipboard = (value: string) => {
201
- navigator.clipboard.writeText(value);
202
- // You could show a toast notification here
203
- };
204
-
205
- const renderFieldValue = (field: DetailField, value: unknown) => {
206
- // If currently editing this field
207
- if (editingField === field.key) {
208
- return (
209
- <div className="flex items-center gap-2">
210
- <input
211
- type="text"
212
- value={String(editValue)}
213
- onChange={(e) => setEditValue(e.target.value)}
214
- className="flex-1 px-2 py-1 text-sm border border-input rounded"
215
- autoFocus
216
- />
217
- <Button size="sm" onClick={handleSaveEdit}>
218
- <Save className="w-3 h-3" />
219
- </Button>
220
- <Button size="sm" variant="outline" onClick={handleCancelEdit}>
221
- <X className="w-3 h-3" />
222
- </Button>
223
- </div>
224
- );
225
- }
226
-
227
- // Custom render function
228
- if (field.render) {
229
- return field.render(value, data);
230
- }
231
-
232
- // Default rendering based on type
233
- const stringValue = String(value || '');
234
- let displayValue: React.ReactNode;
235
-
236
- switch (field.type) {
237
- case 'email':
238
- displayValue = value ? (
239
- <a href={`mailto:${stringValue}`} className="text-category-1 hover:underline">
240
- {stringValue}
241
- </a>
242
- ) : '';
243
- break;
244
- case 'phone':
245
- displayValue = value ? (
246
- <a href={`tel:${stringValue}`} className="text-category-2 hover:underline">
247
- {stringValue}
248
- </a>
249
- ) : '';
250
- break;
251
- case 'url':
252
- displayValue = value ? (
253
- <a href={stringValue} target="_blank" rel="noopener noreferrer" className="text-category-3 hover:underline inline-flex items-center">
254
- {stringValue} <ExternalLink className="w-3 h-3 ml-1" />
255
- </a>
256
- ) : '';
257
- break;
258
- case 'date':
259
- displayValue = value ? new Date(stringValue).toLocaleDateString() : '';
260
- break;
261
- case 'datetime':
262
- displayValue = value ? new Date(stringValue).toLocaleString() : '';
263
- break;
264
- case 'boolean':
265
- displayValue = (
266
- <DataBadge
267
- variant="status"
268
- status={value ? 'success' : 'neutral'}
269
- size="sm"
270
- >
271
- {value ? 'Yes' : 'No'}
272
- </DataBadge>
273
- );
274
- break;
275
- case 'badge':
276
- displayValue = <DataBadge variant="category" category={1} size="sm">{stringValue}</DataBadge>;
277
- break;
278
- case 'avatar':
279
- displayValue = <UserAvatar />;
280
- break;
281
- case 'json':
282
- displayValue = (
283
- <pre className="text-xs bg-muted p-2 rounded-md overflow-auto max-w-md">
284
- {JSON.stringify(value, null, 2)}
285
- </pre>
286
- );
287
- break;
288
- default:
289
- displayValue = stringValue;
290
- break;
291
- }
292
-
293
- return (
294
- <div className="flex items-center gap-2">
295
- <span>{displayValue as React.ReactNode}</span>
296
- {field.copyable && value != null && (
297
- <Button
298
- size="sm"
299
- variant="ghost"
300
- onClick={() => copyToClipboard(stringValue)}
301
- className="h-6 w-6 p-0"
302
- >
303
- <Copy className="w-3 h-3" />
304
- </Button>
305
- )}
306
- {field.editable && onUpdate && (
307
- <Button
308
- size="sm"
309
- variant="ghost"
310
- onClick={() => handleEdit(field, value)}
311
- className="h-6 w-6 p-0"
312
- >
313
- <Edit className="w-3 h-3" />
314
- </Button>
315
- )}
316
- </div>
317
- );
318
- };
319
-
320
- const renderSection = (section: FieldSection) => (
321
- <Card key={section.title} className="p-6">
322
- <div className="mb-4">
323
- <h3 className="text-lg font-semibold text-foreground">{section.title}</h3>
324
- {section.description && (
325
- <p className="text-sm text-muted-foreground mt-1">{section.description}</p>
326
- )}
327
- </div>
328
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
329
- {section.fields.map((field) => (
330
- <div key={field.key} className="space-y-1">
331
- <label className="text-sm font-medium text-muted-foreground">
332
- {field.label}
333
- </label>
334
- <div className="text-sm text-foreground">
335
- {renderFieldValue(field, data[field.key])}
336
- </div>
337
- </div>
338
- ))}
339
- </div>
340
- </Card>
341
- );
342
-
343
- const renderAuditTrail = () => (
344
- <Card className="p-6">
345
- <div className="mb-4">
346
- <h3 className="text-lg font-semibold flex items-center">
347
- <History className="w-5 h-5 mr-2" />
348
- Audit Trail
349
- </h3>
350
- </div>
351
- <div className="space-y-4">
352
- {auditTrail.map((entry) => (
353
- <div key={entry.id} className="flex items-start space-x-3 pb-4 border-b border-border last:border-b-0">
354
- <div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center flex-shrink-0">
355
- <Activity className="w-4 h-4 text-muted-foreground" />
356
- </div>
357
- <div className="flex-1 min-w-0">
358
- <div className="flex items-center justify-between">
359
- <p className="text-sm font-medium text-foreground">
360
- {entry.action}
361
- </p>
362
- <span className="text-xs text-muted-foreground">
363
- {entry.timestamp.toLocaleString()}
364
- </span>
365
- </div>
366
- <p className="text-xs text-muted-foreground mt-1">
367
- by {entry.user}
368
- </p>
369
- {entry.changes && entry.changes.length > 0 && (
370
- <div className="mt-2 space-y-1">
371
- {entry.changes.map((change, index) => (
372
- <div key={index} className="text-xs bg-muted p-2 rounded-md">
373
- <span className="font-medium">{change.field}:</span>{' '}
374
- <span className="text-category-8">{String(change.oldValue || 'null')}</span>{' '}
375
- →{' '}
376
- <span className="text-category-2">{String(change.newValue || 'null')}</span>
377
- </div>
378
- ))}
379
- </div>
380
- )}
381
- </div>
382
- </div>
383
- ))}
384
- </div>
385
- </Card>
386
- );
387
-
388
- const renderRelatedData = (related: RelatedData) => (
389
- <Card key={related.title} className="p-6">
390
- <div className="mb-4 flex items-center justify-between">
391
- <div>
392
- <h3 className="text-lg font-semibold">{related.title}</h3>
393
- {related.description && (
394
- <p className="text-sm text-muted-foreground mt-1">{related.description}</p>
395
- )}
396
- </div>
397
- {related.actions && related.actions.length > 0 && (
398
- <div className="flex gap-2">
399
- {related.actions.map((action) => (
400
- <Button
401
- key={action.id}
402
- variant={action.variant || 'outline'}
403
- size="sm"
404
- onClick={action.onClick}
405
- disabled={action.disabled}
406
- >
407
- {action.icon}
408
- {action.label}
409
- </Button>
410
- ))}
411
- </div>
412
- )}
413
- </div>
414
- <DataTable
415
- data={related.data}
416
- columns={related.columns}
417
- isLoading={isLoading}
418
- hover
419
- />
420
- </Card>
421
- );
422
-
423
- // Build tabs
424
- const allTabs = [
425
- {
426
- id: 'overview',
427
- label: 'Overview',
428
- icon: <FileText className="w-4 h-4" />,
429
- content: (
430
- <div className="space-y-6">
431
- {sections.map(renderSection)}
432
- </div>
433
- ),
434
- },
435
- ...tabs,
436
- ...(relatedData.length > 0 ? [{
437
- id: 'related',
438
- label: 'Related Data',
439
- icon: <Activity className="w-4 h-4" />,
440
- content: (
441
- <div className="space-y-6">
442
- {relatedData.map(renderRelatedData)}
443
- </div>
444
- ),
445
- }] : []),
446
- ...(showAuditTrail && auditTrail.length > 0 ? [{
447
- id: 'audit',
448
- label: 'Audit Trail',
449
- icon: <History className="w-4 h-4" />,
450
- badge: auditTrail.length,
451
- content: renderAuditTrail(),
452
- }] : []),
453
- ];
454
-
455
- return (
456
- <div className={cn('flex flex-col min-h-0 flex-1', className)}>
457
- {/* Header Section */}
458
- <div className="flex-shrink-0 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
459
- <div className="container mx-auto px-6 py-6">
460
- <div className="flex items-start justify-between">
461
- <div className="flex-1 min-w-0">
462
- {onBack && (
463
- <Button
464
- variant="ghost"
465
- onClick={onBack}
466
- className="mb-4 -ml-3"
467
- >
468
- <ArrowLeft className="w-4 h-4 mr-2" />
469
- Back to {resourceType}
470
- </Button>
471
- )}
472
- <SectionHeader
473
- title={title}
474
- subtitle={subtitle}
475
- size="lg"
476
- className="text-left"
477
- />
478
- </div>
479
-
480
- {actions.length > 0 && (
481
- <div className="ml-6 flex-shrink-0 flex items-center gap-2">
482
- {actions.map((action) => (
483
- <Button
484
- key={action.id}
485
- variant={action.variant || 'outline'}
486
- onClick={action.onClick}
487
- disabled={action.disabled}
488
- >
489
- {action.icon}
490
- {action.label}
491
- </Button>
492
- ))}
493
- </div>
494
- )}
495
- </div>
496
- </div>
497
- </div>
498
-
499
- {/* Main Content */}
500
- <div className="flex-1 min-h-0 overflow-auto">
501
- <div className="container mx-auto px-6 py-6">
502
- <div className={cn(
503
- 'grid gap-6',
504
- sidebar ? 'grid-cols-1 lg:grid-cols-4' : 'grid-cols-1'
505
- )}>
506
-
507
- {/* Main Content */}
508
- <div className={sidebar ? 'lg:col-span-3' : 'col-span-1'}>
509
- {allTabs.length > 1 ? (
510
- <div className="space-y-6">
511
- {/* Tab Navigation */}
512
- <div className="border-b border-border">
513
- <nav className="-mb-px flex space-x-8">
514
- {allTabs.map((tab) => (
515
- <button
516
- key={tab.id}
517
- onClick={() => setActiveTab(tab.id)}
518
- className={cn(
519
- 'py-2 px-1 border-b-2 font-medium text-sm whitespace-nowrap',
520
- activeTab === tab.id
521
- ? `border-category-${category} text-category-${category}`
522
- : 'border-transparent text-muted-foreground hover:text-foreground hover:border-border'
523
- )}
524
- >
525
- <div className="flex items-center gap-2">
526
- {tab.icon}
527
- {tab.label}
528
- {tab.badge && (
529
- <span className={cn(
530
- 'ml-2 inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium',
531
- `bg-category-${category}/20 text-category-${category}`
532
- )}>
533
- {tab.badge}
534
- </span>
535
- )}
536
- </div>
537
- </button>
538
- ))}
539
- </nav>
540
- </div>
541
-
542
- {/* Tab Content */}
543
- <div>
544
- {allTabs.find(tab => tab.id === activeTab)?.content}
545
- </div>
546
- </div>
547
- ) : (
548
- allTabs[0]?.content
549
- )}
550
- </div>
551
-
552
- {/* Sidebar */}
553
- {sidebar && (
554
- <div className="lg:col-span-1">
555
- {sidebar}
556
- </div>
557
- )}
558
- </div>
559
- </div>
560
- </div>
561
- </div>
562
- );
563
- };
@@ -1,29 +0,0 @@
1
- // Admin template exports
2
- export {
3
- AdminDashboardTemplate,
4
- type AdminDashboardTemplateProps,
5
- type MetricCard,
6
- type ChartConfig,
7
- type ActivityItem,
8
- type AlertItem
9
- } from './AdminDashboardTemplate';
10
-
11
- export {
12
- AdminCRUDTemplate,
13
- type AdminCRUDTemplateProps,
14
- type ResourceSchema,
15
- type ResourceField,
16
- type CRUDPermissions,
17
- type CRUDActions
18
- } from './AdminCRUDTemplate';
19
-
20
- export {
21
- AdminDetailTemplate,
22
- type AdminDetailTemplateProps,
23
- type TabConfig,
24
- type ActionConfig,
25
- type FieldSection,
26
- type DetailField,
27
- type RelatedData,
28
- type AuditEntry
29
- } from './AdminDetailTemplate';