@ancatag/at-design 0.3.0
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/README.md +537 -0
- package/dist/components/Sheet.d.ts +20 -0
- package/dist/components/Sheet.d.ts.map +1 -0
- package/dist/components/Sheet.js +26 -0
- package/dist/components/Sheet.js.map +1 -0
- package/dist/components/alert-dialog.d.ts +21 -0
- package/dist/components/alert-dialog.d.ts.map +1 -0
- package/dist/components/alert-dialog.js +27 -0
- package/dist/components/alert-dialog.js.map +1 -0
- package/dist/components/alert.d.ts +9 -0
- package/dist/components/alert.d.ts.map +1 -0
- package/dist/components/alert.js +23 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.d.ts +7 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.js +12 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +10 -0
- package/dist/components/badge.d.ts.map +1 -0
- package/dist/components/badge.js +21 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/bottom-sheet.d.ts +16 -0
- package/dist/components/bottom-sheet.d.ts.map +1 -0
- package/dist/components/bottom-sheet.js +13 -0
- package/dist/components/bottom-sheet.js.map +1 -0
- package/dist/components/button.d.ts +12 -0
- package/dist/components/button.d.ts.map +1 -0
- package/dist/components/button.js +34 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/card.d.ts +9 -0
- package/dist/components/card.d.ts.map +1 -0
- package/dist/components/card.js +17 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/checkbox.d.ts +5 -0
- package/dist/components/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox.js +10 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/combobox.d.ts +19 -0
- package/dist/components/combobox.d.ts.map +1 -0
- package/dist/components/combobox.js +101 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/dialog.d.ts +20 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.js +24 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +28 -0
- package/dist/components/dropdown-menu.d.ts.map +1 -0
- package/dist/components/dropdown-menu.js +36 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/error-page.d.ts +105 -0
- package/dist/components/error-page.d.ts.map +1 -0
- package/dist/components/error-page.js +99 -0
- package/dist/components/error-page.js.map +1 -0
- package/dist/components/glass-card.d.ts +12 -0
- package/dist/components/glass-card.d.ts.map +1 -0
- package/dist/components/glass-card.js +15 -0
- package/dist/components/glass-card.js.map +1 -0
- package/dist/components/icon-badge.d.ts +15 -0
- package/dist/components/icon-badge.d.ts.map +1 -0
- package/dist/components/icon-badge.js +77 -0
- package/dist/components/icon-badge.js.map +1 -0
- package/dist/components/input.d.ts +6 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +9 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/label.d.ts +6 -0
- package/dist/components/label.d.ts.map +1 -0
- package/dist/components/label.js +11 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/progress.d.ts +5 -0
- package/dist/components/progress.d.ts.map +1 -0
- package/dist/components/progress.js +9 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.d.ts +6 -0
- package/dist/components/radio-group.d.ts.map +1 -0
- package/dist/components/radio-group.js +15 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/responsive-table.d.ts +21 -0
- package/dist/components/responsive-table.d.ts.map +1 -0
- package/dist/components/responsive-table.js +48 -0
- package/dist/components/responsive-table.js.map +1 -0
- package/dist/components/select.d.ts +14 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/select.js +27 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/skeleton.d.ts +3 -0
- package/dist/components/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton.js +7 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/slider.d.ts +5 -0
- package/dist/components/slider.d.ts.map +1 -0
- package/dist/components/slider.js +9 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/stat-card.d.ts +17 -0
- package/dist/components/stat-card.d.ts.map +1 -0
- package/dist/components/stat-card.js +23 -0
- package/dist/components/stat-card.js.map +1 -0
- package/dist/components/switch.d.ts +5 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.js +9 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/table.d.ts +11 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.js +21 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/tabs.d.ts +8 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.js +14 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +6 -0
- package/dist/components/textarea.d.ts.map +1 -0
- package/dist/components/textarea.js +9 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toast.d.ts +6 -0
- package/dist/components/toast.d.ts.map +1 -0
- package/dist/components/toast.js +14 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/tooltip.d.ts +8 -0
- package/dist/components/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip.js +12 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +6 -0
- package/dist/utils/cn.js.map +1 -0
- package/package.json +96 -0
- package/src/components/Sheet.tsx +117 -0
- package/src/components/alert-dialog.tsx +138 -0
- package/src/components/alert.tsx +58 -0
- package/src/components/avatar.tsx +48 -0
- package/src/components/badge.tsx +36 -0
- package/src/components/bottom-sheet.tsx +68 -0
- package/src/components/button.tsx +56 -0
- package/src/components/card.tsx +85 -0
- package/src/components/checkbox.tsx +30 -0
- package/src/components/combobox.tsx +246 -0
- package/src/components/dialog.tsx +122 -0
- package/src/components/dropdown-menu.tsx +198 -0
- package/src/components/error-page.tsx +368 -0
- package/src/components/glass-card.tsx +37 -0
- package/src/components/icon-badge.tsx +126 -0
- package/src/components/input.tsx +25 -0
- package/src/components/label.tsx +26 -0
- package/src/components/progress.tsx +28 -0
- package/src/components/radio-group.tsx +42 -0
- package/src/components/responsive-table.tsx +191 -0
- package/src/components/select.tsx +158 -0
- package/src/components/skeleton.tsx +15 -0
- package/src/components/slider.tsx +27 -0
- package/src/components/stat-card.tsx +69 -0
- package/src/components/switch.tsx +29 -0
- package/src/components/table.tsx +117 -0
- package/src/components/tabs.tsx +55 -0
- package/src/components/textarea.tsx +23 -0
- package/src/components/toast.tsx +26 -0
- package/src/components/tooltip.tsx +34 -0
- package/src/index.ts +136 -0
- package/src/styles/animations.css +55 -0
- package/src/styles/base.css +51 -0
- package/src/styles/tokens.css +37 -0
- package/src/utils/cn.ts +6 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { ArrowLeft, Home, Search } from "lucide-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from "./card";
|
|
12
|
+
|
|
13
|
+
export interface ErrorPageProps {
|
|
14
|
+
/**
|
|
15
|
+
* Error code (e.g., "404", "500", "403")
|
|
16
|
+
*/
|
|
17
|
+
code?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Main error title
|
|
20
|
+
*/
|
|
21
|
+
title?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Error description/message
|
|
24
|
+
*/
|
|
25
|
+
description?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Show home button
|
|
28
|
+
*/
|
|
29
|
+
showHomeButton?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Show back button
|
|
32
|
+
*/
|
|
33
|
+
showBackButton?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Show search button
|
|
36
|
+
*/
|
|
37
|
+
showSearchButton?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Custom home button label
|
|
40
|
+
*/
|
|
41
|
+
homeButtonLabel?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Custom back button label
|
|
44
|
+
*/
|
|
45
|
+
backButtonLabel?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Custom search button label
|
|
48
|
+
*/
|
|
49
|
+
searchButtonLabel?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Home button action
|
|
52
|
+
*/
|
|
53
|
+
onHomeClick?: () => void;
|
|
54
|
+
/**
|
|
55
|
+
* Back button action
|
|
56
|
+
*/
|
|
57
|
+
onBackClick?: () => void;
|
|
58
|
+
/**
|
|
59
|
+
* Search button action
|
|
60
|
+
*/
|
|
61
|
+
onSearchClick?: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Custom icon component
|
|
64
|
+
*/
|
|
65
|
+
icon?: React.ComponentType<{ className?: string }>;
|
|
66
|
+
/**
|
|
67
|
+
* Additional CSS classes
|
|
68
|
+
*/
|
|
69
|
+
className?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Show as card or full page
|
|
72
|
+
*/
|
|
73
|
+
variant?: "card" | "full";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* ErrorPage - A beautiful, customizable error page component
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* <ErrorPage
|
|
82
|
+
* code="404"
|
|
83
|
+
* title="Page Not Found"
|
|
84
|
+
* description="The page you're looking for doesn't exist or has been moved."
|
|
85
|
+
* showHomeButton
|
|
86
|
+
* showBackButton
|
|
87
|
+
* />
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const ErrorPage = React.forwardRef<HTMLDivElement, ErrorPageProps>(
|
|
91
|
+
(
|
|
92
|
+
{
|
|
93
|
+
code = "404",
|
|
94
|
+
title = "Page Not Found",
|
|
95
|
+
description = "The page you're looking for doesn't exist or has been moved.",
|
|
96
|
+
showHomeButton = true,
|
|
97
|
+
showBackButton = true,
|
|
98
|
+
showSearchButton = false,
|
|
99
|
+
homeButtonLabel = "Go Home",
|
|
100
|
+
backButtonLabel = "Go Back",
|
|
101
|
+
searchButtonLabel = "Search",
|
|
102
|
+
onHomeClick,
|
|
103
|
+
onBackClick,
|
|
104
|
+
onSearchClick,
|
|
105
|
+
icon: Icon,
|
|
106
|
+
className,
|
|
107
|
+
variant = "full",
|
|
108
|
+
},
|
|
109
|
+
ref,
|
|
110
|
+
) => {
|
|
111
|
+
const handleBack = () => {
|
|
112
|
+
if (onBackClick) {
|
|
113
|
+
onBackClick();
|
|
114
|
+
} else {
|
|
115
|
+
window.history.back();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleHome = () => {
|
|
120
|
+
if (onHomeClick) {
|
|
121
|
+
onHomeClick();
|
|
122
|
+
} else {
|
|
123
|
+
window.location.href = "/";
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleSearch = () => {
|
|
128
|
+
if (onSearchClick) {
|
|
129
|
+
onSearchClick();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const content = (
|
|
134
|
+
<div
|
|
135
|
+
ref={ref}
|
|
136
|
+
className={cn(
|
|
137
|
+
"flex flex-col items-center justify-center text-center",
|
|
138
|
+
variant === "card"
|
|
139
|
+
? "p-6"
|
|
140
|
+
: "min-h-screen px-4 py-16 sm:py-20 lg:py-24 sm:px-6 lg:px-8",
|
|
141
|
+
className,
|
|
142
|
+
)}
|
|
143
|
+
>
|
|
144
|
+
{/* Error Code - Much Larger with Gradient */}
|
|
145
|
+
<div className="mb-8 sm:mb-12 relative">
|
|
146
|
+
<div
|
|
147
|
+
className={cn(
|
|
148
|
+
"font-bold select-none",
|
|
149
|
+
"text-[120px] sm:text-[180px] lg:text-[240px] xl:text-[280px]",
|
|
150
|
+
"leading-none tracking-tight",
|
|
151
|
+
"bg-gradient-to-br from-primary via-primary/80 to-primary/40",
|
|
152
|
+
"bg-clip-text text-transparent",
|
|
153
|
+
"drop-shadow-2xl",
|
|
154
|
+
)}
|
|
155
|
+
style={{
|
|
156
|
+
animation: "fade-in 0.8s ease-out forwards",
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{code}
|
|
160
|
+
</div>
|
|
161
|
+
{/* Decorative glow effect */}
|
|
162
|
+
<div
|
|
163
|
+
className="absolute inset-0 -z-10 blur-3xl opacity-20"
|
|
164
|
+
style={{
|
|
165
|
+
background:
|
|
166
|
+
"radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)",
|
|
167
|
+
}}
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
{/* Icon */}
|
|
172
|
+
{Icon && (
|
|
173
|
+
<div
|
|
174
|
+
className="mb-8 sm:mb-10"
|
|
175
|
+
style={{
|
|
176
|
+
animation: "fade-in 0.8s ease-out 0.2s forwards",
|
|
177
|
+
opacity: 0,
|
|
178
|
+
}}
|
|
179
|
+
>
|
|
180
|
+
<Icon className="h-20 w-20 sm:h-24 sm:w-24 lg:h-28 lg:w-28 text-muted-foreground/60" />
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
{/* Title - Larger */}
|
|
185
|
+
<h1
|
|
186
|
+
className={cn(
|
|
187
|
+
"font-bold text-foreground mb-6 sm:mb-8",
|
|
188
|
+
"text-4xl sm:text-5xl lg:text-6xl xl:text-7xl",
|
|
189
|
+
"leading-tight tracking-tight",
|
|
190
|
+
)}
|
|
191
|
+
style={{
|
|
192
|
+
animation: "fade-in 0.8s ease-out 0.3s forwards",
|
|
193
|
+
opacity: 0,
|
|
194
|
+
}}
|
|
195
|
+
>
|
|
196
|
+
{title}
|
|
197
|
+
</h1>
|
|
198
|
+
|
|
199
|
+
{/* Description - Larger with better spacing */}
|
|
200
|
+
<p
|
|
201
|
+
className={cn(
|
|
202
|
+
"text-muted-foreground max-w-lg sm:max-w-xl lg:max-w-2xl mb-10 sm:mb-12",
|
|
203
|
+
"text-lg sm:text-xl lg:text-2xl",
|
|
204
|
+
"leading-relaxed",
|
|
205
|
+
)}
|
|
206
|
+
style={{
|
|
207
|
+
animation: "fade-in 0.8s ease-out 0.4s forwards",
|
|
208
|
+
opacity: 0,
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
{description}
|
|
212
|
+
</p>
|
|
213
|
+
|
|
214
|
+
{/* Action Buttons - Larger with better spacing */}
|
|
215
|
+
<div
|
|
216
|
+
className={cn("flex flex-col sm:flex-row gap-4 sm:gap-5")}
|
|
217
|
+
style={{
|
|
218
|
+
animation: "fade-in 0.8s ease-out 0.5s forwards",
|
|
219
|
+
opacity: 0,
|
|
220
|
+
}}
|
|
221
|
+
>
|
|
222
|
+
{showBackButton && (
|
|
223
|
+
<Button
|
|
224
|
+
variant="outline"
|
|
225
|
+
size="lg"
|
|
226
|
+
onClick={handleBack}
|
|
227
|
+
className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
|
|
228
|
+
>
|
|
229
|
+
<ArrowLeft className="mr-2 h-5 w-5" />
|
|
230
|
+
{backButtonLabel}
|
|
231
|
+
</Button>
|
|
232
|
+
)}
|
|
233
|
+
{showHomeButton && (
|
|
234
|
+
<Button
|
|
235
|
+
variant="default"
|
|
236
|
+
size="lg"
|
|
237
|
+
onClick={handleHome}
|
|
238
|
+
className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
|
|
239
|
+
>
|
|
240
|
+
<Home className="mr-2 h-5 w-5" />
|
|
241
|
+
{homeButtonLabel}
|
|
242
|
+
</Button>
|
|
243
|
+
)}
|
|
244
|
+
{showSearchButton && (
|
|
245
|
+
<Button
|
|
246
|
+
variant="secondary"
|
|
247
|
+
size="lg"
|
|
248
|
+
onClick={handleSearch}
|
|
249
|
+
className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
|
|
250
|
+
>
|
|
251
|
+
<Search className="mr-2 h-5 w-5" />
|
|
252
|
+
{searchButtonLabel}
|
|
253
|
+
</Button>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
if (variant === "card") {
|
|
260
|
+
return (
|
|
261
|
+
<Card className="max-w-2xl mx-auto">
|
|
262
|
+
<CardHeader>
|
|
263
|
+
<CardTitle className="text-center">{title}</CardTitle>
|
|
264
|
+
<CardDescription className="text-center">
|
|
265
|
+
{description}
|
|
266
|
+
</CardDescription>
|
|
267
|
+
</CardHeader>
|
|
268
|
+
<CardContent>{content}</CardContent>
|
|
269
|
+
</Card>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return content;
|
|
274
|
+
},
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
ErrorPage.displayName = "ErrorPage";
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Pre-configured 404 error page
|
|
281
|
+
*/
|
|
282
|
+
export const NotFoundPage = React.forwardRef<
|
|
283
|
+
HTMLDivElement,
|
|
284
|
+
Omit<ErrorPageProps, "code" | "title"> & { description?: string }
|
|
285
|
+
>((props, ref) => {
|
|
286
|
+
return (
|
|
287
|
+
<ErrorPage
|
|
288
|
+
ref={ref}
|
|
289
|
+
code="404"
|
|
290
|
+
title="Page Not Found"
|
|
291
|
+
description={
|
|
292
|
+
props.description ||
|
|
293
|
+
"The page you're looking for doesn't exist or has been moved."
|
|
294
|
+
}
|
|
295
|
+
{...props}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
NotFoundPage.displayName = "NotFoundPage";
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Pre-configured 500 error page
|
|
304
|
+
*/
|
|
305
|
+
export const ServerErrorPage = React.forwardRef<
|
|
306
|
+
HTMLDivElement,
|
|
307
|
+
Omit<ErrorPageProps, "code" | "title"> & { description?: string }
|
|
308
|
+
>((props, ref) => {
|
|
309
|
+
return (
|
|
310
|
+
<ErrorPage
|
|
311
|
+
ref={ref}
|
|
312
|
+
code="500"
|
|
313
|
+
title="Server Error"
|
|
314
|
+
description={
|
|
315
|
+
props.description ||
|
|
316
|
+
"Something went wrong on our end. We're working to fix it."
|
|
317
|
+
}
|
|
318
|
+
{...props}
|
|
319
|
+
/>
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
ServerErrorPage.displayName = "ServerErrorPage";
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Pre-configured 403 error page
|
|
327
|
+
*/
|
|
328
|
+
export const ForbiddenPage = React.forwardRef<
|
|
329
|
+
HTMLDivElement,
|
|
330
|
+
Omit<ErrorPageProps, "code" | "title"> & { description?: string }
|
|
331
|
+
>((props, ref) => {
|
|
332
|
+
return (
|
|
333
|
+
<ErrorPage
|
|
334
|
+
ref={ref}
|
|
335
|
+
code="403"
|
|
336
|
+
title="Access Forbidden"
|
|
337
|
+
description={
|
|
338
|
+
props.description ||
|
|
339
|
+
"You don't have permission to access this resource."
|
|
340
|
+
}
|
|
341
|
+
{...props}
|
|
342
|
+
/>
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
ForbiddenPage.displayName = "ForbiddenPage";
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Pre-configured 401 error page
|
|
350
|
+
*/
|
|
351
|
+
export const UnauthorizedPage = React.forwardRef<
|
|
352
|
+
HTMLDivElement,
|
|
353
|
+
Omit<ErrorPageProps, "code" | "title"> & { description?: string }
|
|
354
|
+
>((props, ref) => {
|
|
355
|
+
return (
|
|
356
|
+
<ErrorPage
|
|
357
|
+
ref={ref}
|
|
358
|
+
code="401"
|
|
359
|
+
title="Unauthorized"
|
|
360
|
+
description={
|
|
361
|
+
props.description || "You need to be logged in to access this page."
|
|
362
|
+
}
|
|
363
|
+
{...props}
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
UnauthorizedPage.displayName = "UnauthorizedPage";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export interface GlassCardProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
gradient?: "blue" | "purple" | "pink" | "none";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const gradientClasses = {
|
|
10
|
+
blue: "from-blue-500/5 to-cyan-500/5",
|
|
11
|
+
purple: "from-purple-500/5 to-pink-500/5",
|
|
12
|
+
pink: "from-pink-500/5 to-rose-500/5",
|
|
13
|
+
none: "",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Apple-inspired glass morphism card
|
|
18
|
+
* Frosted glass effect with subtle gradients
|
|
19
|
+
*/
|
|
20
|
+
export function GlassCard({
|
|
21
|
+
children,
|
|
22
|
+
className = "",
|
|
23
|
+
gradient = "none",
|
|
24
|
+
}: GlassCardProps) {
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
className={`relative overflow-hidden rounded-3xl border border-white/10 bg-white/5 p-8 shadow-2xl backdrop-blur-2xl dark:bg-black/20 ${className}`}
|
|
28
|
+
>
|
|
29
|
+
{gradient !== "none" && (
|
|
30
|
+
<div
|
|
31
|
+
className={`absolute inset-0 bg-gradient-to-br ${gradientClasses[gradient]}`}
|
|
32
|
+
/>
|
|
33
|
+
)}
|
|
34
|
+
<div className="relative z-10">{children}</div>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { LucideIcon } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
export interface IconBadgeProps {
|
|
4
|
+
icon: LucideIcon;
|
|
5
|
+
value: string | number;
|
|
6
|
+
label?: string;
|
|
7
|
+
size?: "sm" | "md" | "lg";
|
|
8
|
+
variant?: "blue" | "yellow" | "green" | "red" | "purple";
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const sizeClasses = {
|
|
13
|
+
sm: {
|
|
14
|
+
container: "gap-2 px-3 py-1.5",
|
|
15
|
+
icon: "h-4 w-4",
|
|
16
|
+
value: "text-sm",
|
|
17
|
+
label: "text-xs",
|
|
18
|
+
},
|
|
19
|
+
md: {
|
|
20
|
+
container: "gap-2.5 px-3.5 py-2",
|
|
21
|
+
icon: "h-4 w-4",
|
|
22
|
+
value: "text-sm",
|
|
23
|
+
label: "text-xs",
|
|
24
|
+
},
|
|
25
|
+
lg: {
|
|
26
|
+
container: "gap-3 px-4 py-2.5",
|
|
27
|
+
icon: "h-5 w-5",
|
|
28
|
+
value: "text-base",
|
|
29
|
+
label: "text-sm",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const variantClasses = {
|
|
34
|
+
blue: {
|
|
35
|
+
container:
|
|
36
|
+
"border-blue-200/50 bg-blue-50/50 dark:border-blue-900/30 dark:bg-blue-950/20",
|
|
37
|
+
icon: "text-blue-600 dark:text-blue-400",
|
|
38
|
+
value: "text-blue-700 dark:text-blue-300",
|
|
39
|
+
label: "text-blue-600/70 dark:text-blue-400/70",
|
|
40
|
+
},
|
|
41
|
+
yellow: {
|
|
42
|
+
container:
|
|
43
|
+
"border-yellow-200/50 bg-yellow-50/50 dark:border-yellow-900/30 dark:bg-yellow-950/20",
|
|
44
|
+
icon: "text-yellow-600 dark:text-yellow-400",
|
|
45
|
+
value: "text-yellow-700 dark:text-yellow-300",
|
|
46
|
+
label: "text-yellow-600/70 dark:text-yellow-400/70",
|
|
47
|
+
},
|
|
48
|
+
green: {
|
|
49
|
+
container:
|
|
50
|
+
"border-green-200/50 bg-green-50/50 dark:border-green-900/30 dark:bg-green-950/20",
|
|
51
|
+
icon: "text-green-600 dark:text-green-400",
|
|
52
|
+
value: "text-green-700 dark:text-green-300",
|
|
53
|
+
label: "text-green-600/70 dark:text-green-400/70",
|
|
54
|
+
},
|
|
55
|
+
red: {
|
|
56
|
+
container:
|
|
57
|
+
"border-red-200/50 bg-red-50/50 dark:border-red-900/30 dark:bg-red-950/20",
|
|
58
|
+
icon: "text-red-600 dark:text-red-400",
|
|
59
|
+
value: "text-red-700 dark:text-red-300",
|
|
60
|
+
label: "text-red-600/70 dark:text-red-400/70",
|
|
61
|
+
},
|
|
62
|
+
purple: {
|
|
63
|
+
container:
|
|
64
|
+
"border-purple-200/50 bg-purple-50/50 dark:border-purple-900/30 dark:bg-purple-950/20",
|
|
65
|
+
icon: "text-purple-600 dark:text-purple-400",
|
|
66
|
+
value: "text-purple-700 dark:text-purple-300",
|
|
67
|
+
label: "text-purple-600/70 dark:text-purple-400/70",
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Apple-inspired icon badge for displaying token balances and stats
|
|
73
|
+
* Subtle borders, clean rounded design
|
|
74
|
+
*/
|
|
75
|
+
export function IconBadge({
|
|
76
|
+
icon: Icon,
|
|
77
|
+
value,
|
|
78
|
+
label,
|
|
79
|
+
size = "md",
|
|
80
|
+
variant = "blue",
|
|
81
|
+
onClick,
|
|
82
|
+
}: IconBadgeProps) {
|
|
83
|
+
const sizeStyle = sizeClasses[size];
|
|
84
|
+
const variantStyle = variantClasses[variant];
|
|
85
|
+
const isClickable = !!onClick;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
className={`
|
|
90
|
+
flex items-center rounded-xl border transition-all duration-200
|
|
91
|
+
${sizeStyle.container} ${variantStyle.container}
|
|
92
|
+
${isClickable ? "cursor-pointer hover:shadow-md active:scale-95" : ""}
|
|
93
|
+
`}
|
|
94
|
+
onClick={onClick}
|
|
95
|
+
{...(isClickable && {
|
|
96
|
+
role: "button",
|
|
97
|
+
tabIndex: 0,
|
|
98
|
+
onKeyDown: (e) => {
|
|
99
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
onClick?.();
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
})}
|
|
105
|
+
>
|
|
106
|
+
<Icon
|
|
107
|
+
className={`${sizeStyle.icon} ${variantStyle.icon}`}
|
|
108
|
+
strokeWidth={2}
|
|
109
|
+
/>
|
|
110
|
+
<div className="flex flex-col">
|
|
111
|
+
<div
|
|
112
|
+
className={`font-semibold tabular-nums ${sizeStyle.value} ${variantStyle.value}`}
|
|
113
|
+
>
|
|
114
|
+
{value}
|
|
115
|
+
</div>
|
|
116
|
+
{label && (
|
|
117
|
+
<div
|
|
118
|
+
className={`font-medium ${sizeStyle.label} ${variantStyle.label}`}
|
|
119
|
+
>
|
|
120
|
+
{label}
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
|
|
5
|
+
export interface InputProps
|
|
6
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
7
|
+
|
|
8
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
9
|
+
({ className, type, ...props }, ref) => {
|
|
10
|
+
return (
|
|
11
|
+
<input
|
|
12
|
+
type={type}
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
ref={ref}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
Input.displayName = "Input";
|
|
24
|
+
|
|
25
|
+
export { Input };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../utils/cn";
|
|
8
|
+
|
|
9
|
+
const labelVariants = cva(
|
|
10
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const Label = React.forwardRef<
|
|
14
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
15
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
16
|
+
VariantProps<typeof labelVariants>
|
|
17
|
+
>(({ className, ...props }, ref) => (
|
|
18
|
+
<LabelPrimitive.Root
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={cn(labelVariants(), className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
));
|
|
24
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
25
|
+
|
|
26
|
+
export { Label };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../utils/cn";
|
|
7
|
+
|
|
8
|
+
const Progress = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
11
|
+
>(({ className, value, ...props }, ref) => (
|
|
12
|
+
<ProgressPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<ProgressPrimitive.Indicator
|
|
21
|
+
className="h-full w-full flex-1 bg-primary transition-all"
|
|
22
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
23
|
+
/>
|
|
24
|
+
</ProgressPrimitive.Root>
|
|
25
|
+
));
|
|
26
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
27
|
+
|
|
28
|
+
export { Progress };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
2
|
+
import { Circle } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils/cn";
|
|
6
|
+
|
|
7
|
+
const RadioGroup = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => {
|
|
11
|
+
return (
|
|
12
|
+
<RadioGroupPrimitive.Root
|
|
13
|
+
className={cn("grid gap-2", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
ref={ref}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
|
20
|
+
|
|
21
|
+
const RadioGroupItem = React.forwardRef<
|
|
22
|
+
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
|
23
|
+
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
|
24
|
+
>(({ className, ...props }, ref) => {
|
|
25
|
+
return (
|
|
26
|
+
<RadioGroupPrimitive.Item
|
|
27
|
+
ref={ref}
|
|
28
|
+
className={cn(
|
|
29
|
+
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
|
35
|
+
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
|
36
|
+
</RadioGroupPrimitive.Indicator>
|
|
37
|
+
</RadioGroupPrimitive.Item>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
|
41
|
+
|
|
42
|
+
export { RadioGroup, RadioGroupItem };
|