@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.
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -0
- package/package.json +5 -3
- package/src/App.css +42 -0
- package/src/App.tsx +54 -0
- package/src/__tests__/README.md +221 -0
- package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
- package/src/__tests__/atoms/ui/button.test.tsx +68 -0
- package/src/__tests__/atoms/utils/simple.test.ts +18 -0
- package/src/__tests__/atoms/utils/utils.test.ts +77 -0
- package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
- package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
- package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
- package/src/__tests__/setup.ts +51 -0
- package/src/__tests__/utils.tsx +123 -0
- package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
- package/src/atoms/composed/Accordion/index.ts +1 -0
- package/src/atoms/composed/Alert/Alert.tsx +132 -0
- package/src/atoms/composed/Alert/index.ts +1 -0
- package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
- package/src/atoms/composed/Breadcrumb/index.ts +1 -0
- package/src/atoms/composed/Chart/Chart.tsx +425 -0
- package/src/atoms/composed/Chart/index.ts +2 -0
- package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
- package/src/atoms/composed/ColorSwatch/index.ts +1 -0
- package/src/atoms/composed/DarkModeToggle.tsx +66 -0
- package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
- package/src/atoms/composed/DataBadge/index.ts +1 -0
- package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
- package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
- package/src/atoms/composed/DataTable/index.ts +2 -0
- package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
- package/src/atoms/composed/DateTimePicker/index.ts +2 -0
- package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
- package/src/atoms/composed/DetailedCard/index.ts +2 -0
- package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
- package/src/atoms/composed/EmptyState/index.ts +1 -0
- package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
- package/src/atoms/composed/FileUpload/index.ts +2 -0
- package/src/atoms/composed/FormField/FormField.tsx +92 -0
- package/src/atoms/composed/FormField/index.ts +1 -0
- package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
- package/src/atoms/composed/GlobalSearch/index.ts +1 -0
- package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
- package/src/atoms/composed/IconBadge/index.ts +2 -0
- package/src/atoms/composed/Modal/Modal.tsx +223 -0
- package/src/atoms/composed/Modal/index.ts +2 -0
- package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
- package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
- package/src/atoms/composed/ProgressBar/index.ts +1 -0
- package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
- package/src/atoms/composed/StatCard/index.ts +1 -0
- package/src/atoms/composed/StyleGuide.tsx +717 -0
- package/src/atoms/composed/Toast/Toast.tsx +219 -0
- package/src/atoms/composed/Toast/index.ts +1 -0
- package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
- package/src/atoms/composed/Tooltip/index.ts +1 -0
- package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
- package/src/atoms/composed/UserAvatar/index.ts +1 -0
- package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
- package/src/atoms/composed/UserMenu/index.ts +1 -0
- package/src/atoms/composed/index.ts +29 -0
- package/src/atoms/hooks/useApi.ts +80 -0
- package/src/atoms/hooks/useHealth.ts +17 -0
- package/src/atoms/index.ts +13 -0
- package/src/atoms/services/api/client.ts +134 -0
- package/src/atoms/services/auth-service.ts +248 -0
- package/src/atoms/services/health.ts +15 -0
- package/src/atoms/services/index.ts +3 -0
- package/src/atoms/shared/config/constants.ts +17 -0
- package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
- package/src/atoms/shared/config/environment.ts +10 -0
- package/src/atoms/shared/index.ts +4 -0
- package/src/atoms/shared/styles/color-palettes.css +566 -0
- package/src/atoms/types/auth.ts +62 -0
- package/src/atoms/types/generated.ts +1469 -0
- package/src/atoms/types/index.ts +4 -0
- package/src/atoms/types/loading.ts +28 -0
- package/src/atoms/ui/Badge.tsx +30 -0
- package/src/atoms/ui/ErrorBoundary.tsx +59 -0
- package/src/atoms/ui/Select.tsx +53 -0
- package/src/atoms/ui/Switch.tsx +42 -0
- package/src/atoms/ui/Tabs.tsx +118 -0
- package/src/atoms/ui/avatar.tsx +48 -0
- package/src/atoms/ui/button.tsx +70 -0
- package/src/atoms/ui/card.tsx +76 -0
- package/src/atoms/ui/dropdown-menu.tsx +199 -0
- package/src/atoms/ui/index.ts +39 -0
- package/src/atoms/ui/input.tsx +23 -0
- package/src/atoms/ui/label.tsx +23 -0
- package/src/atoms/ui/skeleton.tsx +13 -0
- package/src/atoms/ui/spinner.tsx +49 -0
- package/src/atoms/ui/table.tsx +116 -0
- package/src/atoms/utils/animations.ts +135 -0
- package/src/atoms/utils/tooltip-helpers.ts +140 -0
- package/src/atoms/utils/utils.ts +9 -0
- package/src/features/auth/components/LoginForm.tsx +168 -0
- package/src/features/auth/components/LogoutButton.tsx +19 -0
- package/src/features/auth/components/ProtectedRoute.tsx +60 -0
- package/src/features/auth/components/index.ts +4 -0
- package/src/features/auth/hooks/index.ts +2 -0
- package/src/features/auth/hooks/useAuth.tsx +205 -0
- package/src/features/auth/hooks/usePermissions.ts +35 -0
- package/src/features/auth/index.ts +2 -0
- package/src/features/index.ts +2 -0
- package/src/index.css +704 -0
- package/src/index.ts +13 -0
- package/src/main.tsx +48 -0
- package/src/molecules/.gitkeep +0 -0
- package/src/molecules/forms/FormGroup.tsx +75 -0
- package/src/molecules/forms/SearchInput.tsx +259 -0
- package/src/molecules/forms/index.ts +4 -0
- package/src/molecules/index.ts +4 -0
- package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
- package/src/molecules/layout/AppHeader/index.ts +1 -0
- package/src/molecules/layout/AppLayout.tsx +29 -0
- package/src/molecules/layout/PageTemplate.tsx +87 -0
- package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
- package/src/molecules/layout/SectionHeader/index.ts +1 -0
- package/src/molecules/layout/ShowcaseSection.tsx +57 -0
- package/src/molecules/layout/Sidebar.tsx +144 -0
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
- package/src/molecules/layout/SidebarButton/index.ts +1 -0
- package/src/molecules/layout/SidebarContext.tsx +31 -0
- package/src/molecules/layout/index.ts +7 -0
- package/src/molecules/navigation/NavMenu.tsx +188 -0
- package/src/molecules/navigation/Pagination.tsx +172 -0
- package/src/molecules/navigation/index.ts +4 -0
- package/src/organisms/index.ts +5 -0
- package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
- package/src/organisms/showcase/index.ts +1 -0
- package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
- package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
- package/src/pages/AdminShowcase/index.tsx +3 -0
- package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
- package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
- package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
- package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
- package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
- package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
- package/src/pages/ComponentShowcase/index.tsx +188 -0
- package/src/pages/index.ts +2 -0
- package/src/templates/AuthTemplate.tsx +216 -0
- package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
- package/src/templates/DashboardTemplate.tsx +232 -0
- package/src/templates/DataTemplate.tsx +319 -0
- package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
- package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
- package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
- package/src/templates/admin/index.ts +29 -0
- package/src/templates/factory.tsx +169 -0
- package/src/templates/index.ts +37 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { cn } from '../atoms/utils/utils';
|
|
3
|
+
import { SectionHeader } from '../molecules/layout/SectionHeader';
|
|
4
|
+
import { DataTable, type Column } from '../atoms/composed/DataTable';
|
|
5
|
+
import { SearchInput, type SearchResult } from '../molecules/forms/SearchInput';
|
|
6
|
+
import { Pagination } from '../molecules/navigation/Pagination';
|
|
7
|
+
import { Card } from '../atoms/ui/card';
|
|
8
|
+
import { Button } from '../atoms/ui/button';
|
|
9
|
+
import { Plus, Download, Filter } from 'lucide-react';
|
|
10
|
+
|
|
11
|
+
export interface DataTemplateProps {
|
|
12
|
+
/** Page title */
|
|
13
|
+
title: string;
|
|
14
|
+
/** Page description */
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Search functionality */
|
|
17
|
+
searchConfig?: {
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
value?: string;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
results?: SearchResult[];
|
|
22
|
+
};
|
|
23
|
+
/** Filter functionality */
|
|
24
|
+
filterConfig?: {
|
|
25
|
+
onFilter?: () => void;
|
|
26
|
+
filterCount?: number;
|
|
27
|
+
};
|
|
28
|
+
/** Data table configuration */
|
|
29
|
+
tableConfig: {
|
|
30
|
+
data: Record<string, unknown>[];
|
|
31
|
+
columns: Column<Record<string, unknown>>[];
|
|
32
|
+
loading?: boolean;
|
|
33
|
+
onRowClick?: (item: Record<string, unknown>) => void;
|
|
34
|
+
};
|
|
35
|
+
/** Pagination configuration */
|
|
36
|
+
paginationConfig?: {
|
|
37
|
+
currentPage: number;
|
|
38
|
+
totalPages: number;
|
|
39
|
+
onPageChange: (page: number) => void;
|
|
40
|
+
};
|
|
41
|
+
/** Action buttons */
|
|
42
|
+
actions?: {
|
|
43
|
+
primary?: {
|
|
44
|
+
label: string;
|
|
45
|
+
onClick: () => void;
|
|
46
|
+
icon?: React.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
secondary?: Array<{
|
|
49
|
+
label: string;
|
|
50
|
+
onClick: () => void;
|
|
51
|
+
icon?: React.ReactNode;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
/** Additional CSS classes */
|
|
55
|
+
className?: string;
|
|
56
|
+
/** Category-based styling */
|
|
57
|
+
category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
58
|
+
/** Empty state content */
|
|
59
|
+
emptyState?: React.ReactNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const DataTemplate: React.FC<DataTemplateProps> = ({
|
|
63
|
+
title,
|
|
64
|
+
description,
|
|
65
|
+
searchConfig,
|
|
66
|
+
filterConfig,
|
|
67
|
+
tableConfig,
|
|
68
|
+
paginationConfig,
|
|
69
|
+
actions,
|
|
70
|
+
className,
|
|
71
|
+
category = 1,
|
|
72
|
+
emptyState
|
|
73
|
+
}) => {
|
|
74
|
+
const hasData = tableConfig.data && tableConfig.data.length > 0;
|
|
75
|
+
const showControls = searchConfig || filterConfig || actions;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={cn('flex flex-col min-h-0 flex-1', className)}>
|
|
79
|
+
{/* Header Section */}
|
|
80
|
+
<div className="flex-shrink-0 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
81
|
+
<div className="container mx-auto px-6 py-6">
|
|
82
|
+
<div className="flex items-start justify-between">
|
|
83
|
+
<div className="flex-1 min-w-0">
|
|
84
|
+
<SectionHeader
|
|
85
|
+
title={title}
|
|
86
|
+
description={description}
|
|
87
|
+
size="lg"
|
|
88
|
+
className="text-left"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{actions?.primary && (
|
|
93
|
+
<div className="ml-6 flex-shrink-0">
|
|
94
|
+
<Button
|
|
95
|
+
onClick={actions.primary.onClick}
|
|
96
|
+
className={cn(
|
|
97
|
+
`bg-category-${category} hover:bg-category-${category}/90`,
|
|
98
|
+
'text-white'
|
|
99
|
+
)}
|
|
100
|
+
>
|
|
101
|
+
{actions.primary.icon || <Plus className="w-4 h-4 mr-2" />}
|
|
102
|
+
{actions.primary.label}
|
|
103
|
+
</Button>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{/* Controls Section */}
|
|
111
|
+
{showControls && (
|
|
112
|
+
<div className="flex-shrink-0 border-b border-border bg-muted/30">
|
|
113
|
+
<div className="container mx-auto px-6 py-4">
|
|
114
|
+
<div className="flex items-center gap-4">
|
|
115
|
+
{/* Search */}
|
|
116
|
+
{searchConfig && (
|
|
117
|
+
<div className="flex-1 max-w-md">
|
|
118
|
+
<SearchInput
|
|
119
|
+
placeholder={searchConfig.placeholder}
|
|
120
|
+
value={searchConfig.value}
|
|
121
|
+
onChange={searchConfig.onChange}
|
|
122
|
+
results={searchConfig.results}
|
|
123
|
+
category={category}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
<div className="flex items-center gap-2">
|
|
129
|
+
{/* Filter Button */}
|
|
130
|
+
{filterConfig && (
|
|
131
|
+
<Button
|
|
132
|
+
variant="outline"
|
|
133
|
+
onClick={filterConfig.onFilter}
|
|
134
|
+
className="relative"
|
|
135
|
+
>
|
|
136
|
+
<Filter className="w-4 h-4 mr-2" />
|
|
137
|
+
Filters
|
|
138
|
+
{filterConfig.filterCount && filterConfig.filterCount > 0 && (
|
|
139
|
+
<span className={cn(
|
|
140
|
+
'absolute -top-2 -right-2 w-5 h-5 rounded-full text-xs font-bold',
|
|
141
|
+
'flex items-center justify-center text-white',
|
|
142
|
+
`bg-category-${category}`
|
|
143
|
+
)}>
|
|
144
|
+
{filterConfig.filterCount}
|
|
145
|
+
</span>
|
|
146
|
+
)}
|
|
147
|
+
</Button>
|
|
148
|
+
)}
|
|
149
|
+
|
|
150
|
+
{/* Secondary Actions */}
|
|
151
|
+
{actions?.secondary?.map((action, index) => (
|
|
152
|
+
<Button
|
|
153
|
+
key={index}
|
|
154
|
+
variant="outline"
|
|
155
|
+
onClick={action.onClick}
|
|
156
|
+
>
|
|
157
|
+
{action.icon || <Download className="w-4 h-4 mr-2" />}
|
|
158
|
+
{action.label}
|
|
159
|
+
</Button>
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{/* Main Content */}
|
|
168
|
+
<div className="flex-1 min-h-0 overflow-auto">
|
|
169
|
+
<div className="container mx-auto px-6 py-6">
|
|
170
|
+
{hasData ? (
|
|
171
|
+
<div className="space-y-6">
|
|
172
|
+
{/* Data Table */}
|
|
173
|
+
<Card category={category}>
|
|
174
|
+
<DataTable
|
|
175
|
+
data={tableConfig.data}
|
|
176
|
+
columns={tableConfig.columns}
|
|
177
|
+
isLoading={tableConfig.loading}
|
|
178
|
+
onRowClick={tableConfig.onRowClick}
|
|
179
|
+
hover={true}
|
|
180
|
+
/>
|
|
181
|
+
</Card>
|
|
182
|
+
|
|
183
|
+
{/* Pagination */}
|
|
184
|
+
{paginationConfig && paginationConfig.totalPages > 1 && (
|
|
185
|
+
<div className="flex justify-center">
|
|
186
|
+
<Pagination
|
|
187
|
+
currentPage={paginationConfig.currentPage}
|
|
188
|
+
totalPages={paginationConfig.totalPages}
|
|
189
|
+
onPageChange={paginationConfig.onPageChange}
|
|
190
|
+
category={category}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
) : (
|
|
196
|
+
<div className="flex items-center justify-center min-h-96">
|
|
197
|
+
{emptyState || (
|
|
198
|
+
<div className="text-center space-y-4">
|
|
199
|
+
<div className="text-muted-foreground text-lg">
|
|
200
|
+
No data available
|
|
201
|
+
</div>
|
|
202
|
+
<p className="text-sm text-muted-foreground max-w-md">
|
|
203
|
+
{actions?.primary ? (
|
|
204
|
+
<>Get started by creating your first {title.toLowerCase()}.</>
|
|
205
|
+
) : (
|
|
206
|
+
<>There's no data to display at the moment.</>
|
|
207
|
+
)}
|
|
208
|
+
</p>
|
|
209
|
+
{actions?.primary && (
|
|
210
|
+
<Button
|
|
211
|
+
onClick={actions.primary.onClick}
|
|
212
|
+
className={cn(
|
|
213
|
+
`bg-category-${category} hover:bg-category-${category}/90`,
|
|
214
|
+
'text-white'
|
|
215
|
+
)}
|
|
216
|
+
>
|
|
217
|
+
{actions.primary.icon || <Plus className="w-4 h-4 mr-2" />}
|
|
218
|
+
{actions.primary.label}
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
)}
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Data Detail Template
|
|
233
|
+
* Template for displaying detailed information about a single data item
|
|
234
|
+
*/
|
|
235
|
+
export interface DataDetailTemplateProps {
|
|
236
|
+
/** Page title */
|
|
237
|
+
title: string;
|
|
238
|
+
/** Page subtitle */
|
|
239
|
+
subtitle?: string;
|
|
240
|
+
/** Back navigation */
|
|
241
|
+
onBack?: () => void;
|
|
242
|
+
/** Detail content */
|
|
243
|
+
children: React.ReactNode;
|
|
244
|
+
/** Action buttons */
|
|
245
|
+
actions?: React.ReactNode;
|
|
246
|
+
/** Additional CSS classes */
|
|
247
|
+
className?: string;
|
|
248
|
+
/** Category-based styling */
|
|
249
|
+
category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
250
|
+
/** Sidebar content */
|
|
251
|
+
sidebar?: React.ReactNode;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const DataDetailTemplate: React.FC<DataDetailTemplateProps> = ({
|
|
255
|
+
title,
|
|
256
|
+
subtitle,
|
|
257
|
+
onBack,
|
|
258
|
+
children,
|
|
259
|
+
actions,
|
|
260
|
+
className,
|
|
261
|
+
sidebar
|
|
262
|
+
}) => {
|
|
263
|
+
return (
|
|
264
|
+
<div className={cn('flex flex-col min-h-0 flex-1', className)}>
|
|
265
|
+
{/* Header */}
|
|
266
|
+
<div className="flex-shrink-0 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
267
|
+
<div className="container mx-auto px-6 py-6">
|
|
268
|
+
<div className="flex items-start justify-between">
|
|
269
|
+
<div className="flex-1 min-w-0">
|
|
270
|
+
{onBack && (
|
|
271
|
+
<Button
|
|
272
|
+
variant="ghost"
|
|
273
|
+
onClick={onBack}
|
|
274
|
+
className="mb-4 -ml-3"
|
|
275
|
+
>
|
|
276
|
+
← Back
|
|
277
|
+
</Button>
|
|
278
|
+
)}
|
|
279
|
+
<SectionHeader
|
|
280
|
+
title={title}
|
|
281
|
+
subtitle={subtitle}
|
|
282
|
+
size="lg"
|
|
283
|
+
className="text-left"
|
|
284
|
+
/>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
{actions && (
|
|
288
|
+
<div className="ml-6 flex-shrink-0">
|
|
289
|
+
{actions}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
{/* Content */}
|
|
297
|
+
<div className="flex-1 min-h-0 overflow-auto">
|
|
298
|
+
<div className="container mx-auto px-6 py-6">
|
|
299
|
+
<div className={cn(
|
|
300
|
+
'grid gap-6',
|
|
301
|
+
sidebar ? 'grid-cols-1 lg:grid-cols-3' : 'grid-cols-1'
|
|
302
|
+
)}>
|
|
303
|
+
{/* Main Content */}
|
|
304
|
+
<div className={sidebar ? 'lg:col-span-2' : 'col-span-1'}>
|
|
305
|
+
{children}
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{/* Sidebar */}
|
|
309
|
+
{sidebar && (
|
|
310
|
+
<div className="lg:col-span-1">
|
|
311
|
+
{sidebar}
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
);
|
|
319
|
+
};
|