gladvn 0.2.33 → 0.2.34
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/package.json +1 -1
- package/src/blocks/auth-form.tsx +105 -0
- package/src/blocks/auth-recovery.tsx +5 -5
- package/src/blocks/auth-split.tsx +8 -8
- package/src/blocks/dashboard.tsx +162 -54
- package/src/blocks/settings.tsx +197 -68
- package/src/components/macro/field-preset.tsx +14 -17
- package/src/components/macro/input-preset.tsx +1 -1
- package/src/components/macro/textarea-preset.tsx +1 -1
- package/src/dev/App.tsx +192 -72
- package/src/dev/components/BlockViewer.tsx +136 -0
- package/src/dev/components/code-highlighter.tsx +17 -5
- package/src/dev/data.ts +40 -0
- package/src/dev/showcase/auth-form-block.tsx +13 -0
- package/src/dev/showcase/auth-recovery-block.tsx +13 -0
- package/src/dev/showcase/auth-split-block.tsx +13 -0
- package/src/dev/showcase/dashboard-block.tsx +77 -0
- package/src/dev/showcase/overview.tsx +53 -30
- package/src/dev/showcase/settings-block.tsx +13 -0
- package/src/blocks/auth-card.tsx +0 -104
- package/src/blocks/auth-dialog.tsx +0 -116
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button } from "../components/micro/button";
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/micro/card";
|
|
5
|
+
import { Checkbox } from "../components/micro/checkbox";
|
|
6
|
+
import { Input } from "../components/micro/input";
|
|
7
|
+
import { Label } from "../components/micro/label";
|
|
8
|
+
|
|
9
|
+
export default function AuthCardBlock() {
|
|
10
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
+
const [mode, setMode] = useState<"login" | "register">("login");
|
|
12
|
+
|
|
13
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
setIsLoading(true);
|
|
16
|
+
setTimeout(() => setIsLoading(false), 1000);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex min-h-screen w-full items-center justify-center p-4 bg-muted/20">
|
|
21
|
+
<Card className="w-full max-w-sm border-0 shadow-none bg-transparent">
|
|
22
|
+
<CardHeader className="text-left px-0 pt-0">
|
|
23
|
+
<CardTitle className="text-[28px] font-bold text-[#5e5e6e]">
|
|
24
|
+
{mode === "login" ? "Welcome back" : "Create an account"}
|
|
25
|
+
</CardTitle>
|
|
26
|
+
<CardDescription>
|
|
27
|
+
{mode === "login"
|
|
28
|
+
? "Enter your email below to login to your account"
|
|
29
|
+
: "Enter your details below to create your account"}
|
|
30
|
+
</CardDescription>
|
|
31
|
+
</CardHeader>
|
|
32
|
+
<CardContent className="flex flex-col gap-6 px-0 pb-0">
|
|
33
|
+
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
34
|
+
{mode === "register" && (
|
|
35
|
+
<div className="flex flex-col gap-2">
|
|
36
|
+
<Label htmlFor="name">Full Name</Label>
|
|
37
|
+
<Input id="name" name="name" placeholder="John Doe" className="w-full" required />
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
<div className="flex flex-col gap-2">
|
|
41
|
+
<Label htmlFor="email">Email</Label>
|
|
42
|
+
<Input id="email" name="email" type="email" placeholder="m@example.com" className="w-full" required />
|
|
43
|
+
</div>
|
|
44
|
+
<div className="flex flex-col gap-2">
|
|
45
|
+
<div className="flex items-center justify-between">
|
|
46
|
+
<Label htmlFor="password">Password</Label>
|
|
47
|
+
{mode === "login" && (
|
|
48
|
+
<Button variant="link" color="primary" className="h-auto p-0 text-xs">
|
|
49
|
+
Forgot password?
|
|
50
|
+
</Button>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
<Input id="password" name="password" type="password" className="w-full" required />
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{mode === "register" && (
|
|
57
|
+
<div className="flex items-center gap-2">
|
|
58
|
+
<Checkbox id="terms" name="terms" required />
|
|
59
|
+
<Label htmlFor="terms" className="text-sm font-normal text-muted-foreground leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
60
|
+
I agree to the <a href="#" className="text-primary hover:underline">terms and conditions</a>
|
|
61
|
+
</Label>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
<Button type="submit" className="w-full" disabled={isLoading} aria-busy={isLoading}>
|
|
66
|
+
{isLoading ? "Loading..." : mode === "login" ? "Sign In" : "Create Account"}
|
|
67
|
+
</Button>
|
|
68
|
+
</form>
|
|
69
|
+
|
|
70
|
+
<div className="flex justify-center">
|
|
71
|
+
<span className="text-[13px] font-medium text-muted-foreground uppercase">
|
|
72
|
+
Or continue with
|
|
73
|
+
</span>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<div className="flex flex-col gap-6">
|
|
77
|
+
<Button variant="outline" color="primary" className="w-full bg-transparent">
|
|
78
|
+
<svg viewBox="0 0 24 24" className="mr-2 size-4 bg-transparent" fill="currentColor">
|
|
79
|
+
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4" />
|
|
80
|
+
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853" />
|
|
81
|
+
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05" />
|
|
82
|
+
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335" />
|
|
83
|
+
</svg>
|
|
84
|
+
Google
|
|
85
|
+
</Button>
|
|
86
|
+
|
|
87
|
+
<div className="text-center text-sm">
|
|
88
|
+
<span className="text-muted-foreground">
|
|
89
|
+
{mode === "login" ? "Don't have an account? " : "Already have an account? "}
|
|
90
|
+
</span>
|
|
91
|
+
<Button
|
|
92
|
+
variant="link"
|
|
93
|
+
color="primary"
|
|
94
|
+
className="h-auto p-0 font-semibold"
|
|
95
|
+
onClick={() => setMode(mode === "login" ? "register" : "login")}
|
|
96
|
+
>
|
|
97
|
+
{mode === "login" ? "Sign up" : "Sign in"}
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</CardContent>
|
|
102
|
+
</Card>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
@@ -21,7 +21,7 @@ export default function AuthRecoveryBlock() {
|
|
|
21
21
|
return (
|
|
22
22
|
<div className="flex min-h-[50vh] w-full items-center justify-center p-4 bg-muted/20">
|
|
23
23
|
<Card className="w-full max-w-sm">
|
|
24
|
-
<CardHeader className="text-center
|
|
24
|
+
<CardHeader className="text-center">
|
|
25
25
|
<CardTitle className="text-2xl font-bold">
|
|
26
26
|
{isSent ? "Check your email" : "Forgot Password"}
|
|
27
27
|
</CardTitle>
|
|
@@ -33,17 +33,17 @@ export default function AuthRecoveryBlock() {
|
|
|
33
33
|
</CardHeader>
|
|
34
34
|
<CardContent>
|
|
35
35
|
{!isSent ? (
|
|
36
|
-
<form onSubmit={handleSubmit} className="
|
|
37
|
-
<div className="
|
|
36
|
+
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
37
|
+
<div className="flex flex-col gap-1.5">
|
|
38
38
|
<Label htmlFor="recovery-email">Email</Label>
|
|
39
|
-
<Input id="recovery-email" type="email" placeholder="m@example.com" required />
|
|
39
|
+
<Input id="recovery-email" type="email" placeholder="m@example.com" className="w-full" required />
|
|
40
40
|
</div>
|
|
41
41
|
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
42
42
|
{isLoading ? "Sending link..." : "Send Reset Link"}
|
|
43
43
|
</Button>
|
|
44
44
|
</form>
|
|
45
45
|
) : (
|
|
46
|
-
<div className="flex flex-col
|
|
46
|
+
<div className="flex flex-col gap-4">
|
|
47
47
|
<Button type="button" variant="outline" className="w-full" onClick={() => setIsSent(false)}>
|
|
48
48
|
Try another email
|
|
49
49
|
</Button>
|
|
@@ -22,7 +22,7 @@ export default function AuthSplitBlock() {
|
|
|
22
22
|
<div className="hidden lg:flex w-1/2 flex-col justify-between bg-primary p-12 text-primary-foreground relative overflow-hidden">
|
|
23
23
|
{/* Subtle background pattern */}
|
|
24
24
|
<div className="absolute inset-0 opacity-10 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-white to-transparent pointer-events-none" />
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
<div className="relative z-10">
|
|
27
27
|
<h1 className="text-2xl font-bold tracking-tight">gladvn.</h1>
|
|
28
28
|
</div>
|
|
@@ -62,16 +62,16 @@ export default function AuthSplitBlock() {
|
|
|
62
62
|
|
|
63
63
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
64
64
|
{mode === "register" && (
|
|
65
|
-
<div className="
|
|
65
|
+
<div className="flex flex-col gap-2">
|
|
66
66
|
<Label htmlFor="name">Full Name</Label>
|
|
67
|
-
<Input id="name" placeholder="John Doe" required />
|
|
67
|
+
<Input id="name" placeholder="John Doe" className="w-full" required />
|
|
68
68
|
</div>
|
|
69
69
|
)}
|
|
70
|
-
<div className="
|
|
70
|
+
<div className="flex flex-col gap-2">
|
|
71
71
|
<Label htmlFor="email">Email</Label>
|
|
72
|
-
<Input id="email" type="email" placeholder="m@example.com" required />
|
|
72
|
+
<Input id="email" type="email" placeholder="m@example.com" className="w-full" required />
|
|
73
73
|
</div>
|
|
74
|
-
<div className="
|
|
74
|
+
<div className="flex flex-col gap-2">
|
|
75
75
|
<div className="flex items-center justify-between">
|
|
76
76
|
<Label htmlFor="password">Password</Label>
|
|
77
77
|
{mode === "login" && (
|
|
@@ -80,9 +80,9 @@ export default function AuthSplitBlock() {
|
|
|
80
80
|
</Button>
|
|
81
81
|
)}
|
|
82
82
|
</div>
|
|
83
|
-
<Input id="password" type="password" required />
|
|
83
|
+
<Input id="password" type="password" className="w-full" required />
|
|
84
84
|
</div>
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
{mode === "register" && (
|
|
87
87
|
<div className="flex items-center gap-2">
|
|
88
88
|
<Checkbox id="terms" required />
|
package/src/blocks/dashboard.tsx
CHANGED
|
@@ -8,78 +8,129 @@ import {
|
|
|
8
8
|
SettingsIcon,
|
|
9
9
|
UsersIcon
|
|
10
10
|
} from "lucide-react";
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { DatePicker } from "../components/macro";
|
|
11
13
|
import { Avatar, AvatarFallback, AvatarImage } from "../components/micro/avatar";
|
|
14
|
+
import { Badge } from "../components/micro/badge";
|
|
15
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "../components/micro/breadcrumb";
|
|
12
16
|
import { Button } from "../components/micro/button";
|
|
13
17
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/micro/card";
|
|
14
18
|
import {
|
|
15
19
|
DropdownMenu,
|
|
16
20
|
DropdownMenuContent,
|
|
21
|
+
DropdownMenuGroup,
|
|
17
22
|
DropdownMenuItem,
|
|
18
23
|
DropdownMenuLabel,
|
|
19
24
|
DropdownMenuSeparator,
|
|
20
25
|
DropdownMenuTrigger
|
|
21
26
|
} from "../components/micro/dropdown-menu";
|
|
27
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from "../components/micro/hover-card";
|
|
22
28
|
import { Input } from "../components/micro/input";
|
|
29
|
+
import { Progress } from "../components/micro/progress";
|
|
30
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "../components/micro/sheet";
|
|
31
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/micro/table";
|
|
32
|
+
|
|
33
|
+
const SidebarContent = () => (
|
|
34
|
+
<>
|
|
35
|
+
<div className="flex h-16 items-center border-b border-border px-6">
|
|
36
|
+
<LayoutDashboardIcon className="mr-2 size-5 text-primary" />
|
|
37
|
+
<span className="font-bold text-lg">Gladvn App</span>
|
|
38
|
+
</div>
|
|
39
|
+
<nav className="flex-1 space-y-1 p-4">
|
|
40
|
+
<Button variant="soft" color="primary" className="w-full justify-start">
|
|
41
|
+
<HomeIcon className="mr-2 size-4" /> Overview
|
|
42
|
+
</Button>
|
|
43
|
+
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
44
|
+
<BarChart3Icon className="mr-2 size-4" /> Analytics
|
|
45
|
+
</Button>
|
|
46
|
+
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
47
|
+
<UsersIcon className="mr-2 size-4" /> Customers
|
|
48
|
+
</Button>
|
|
49
|
+
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
50
|
+
<SettingsIcon className="mr-2 size-4" /> Settings
|
|
51
|
+
</Button>
|
|
52
|
+
</nav>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
23
55
|
|
|
24
56
|
export default function DashboardBlock() {
|
|
57
|
+
const [date, setDate] = React.useState<Date | undefined>(new Date());
|
|
58
|
+
|
|
25
59
|
return (
|
|
26
60
|
<div className="flex min-h-screen w-full bg-muted/20">
|
|
27
|
-
{/* Sidebar */}
|
|
61
|
+
{/* Sidebar Desktop */}
|
|
28
62
|
<aside className="hidden w-64 flex-col border-r border-border bg-background md:flex">
|
|
29
|
-
<
|
|
30
|
-
<LayoutDashboardIcon className="mr-2 size-5 text-primary" />
|
|
31
|
-
<span className="font-bold">Gladvn App</span>
|
|
32
|
-
</div>
|
|
33
|
-
<nav className="flex-1 space-y-1 p-4">
|
|
34
|
-
<Button variant="soft" color="primary" className="w-full justify-start">
|
|
35
|
-
<HomeIcon className="mr-2 size-4" /> Overview
|
|
36
|
-
</Button>
|
|
37
|
-
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
38
|
-
<BarChart3Icon className="mr-2 size-4" /> Analytics
|
|
39
|
-
</Button>
|
|
40
|
-
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
41
|
-
<UsersIcon className="mr-2 size-4" /> Customers
|
|
42
|
-
</Button>
|
|
43
|
-
<Button variant="ghost" className="w-full justify-start text-muted-foreground">
|
|
44
|
-
<SettingsIcon className="mr-2 size-4" /> Settings
|
|
45
|
-
</Button>
|
|
46
|
-
</nav>
|
|
63
|
+
<SidebarContent />
|
|
47
64
|
</aside>
|
|
48
65
|
|
|
49
66
|
{/* Main Content */}
|
|
50
67
|
<main className="flex flex-1 flex-col overflow-hidden">
|
|
51
68
|
{/* Header */}
|
|
52
|
-
<header className="flex h-16 items-center gap-4 border-b border-border bg-background px-6">
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
<header className="flex h-16 items-center gap-4 border-b border-border bg-background px-4 md:px-6">
|
|
70
|
+
{/* Mobile Menu */}
|
|
71
|
+
<Sheet>
|
|
72
|
+
<SheetTrigger
|
|
73
|
+
render={
|
|
74
|
+
<Button variant="ghost" iconOnly className="md:hidden">
|
|
75
|
+
<MenuIcon className="size-5" />
|
|
76
|
+
</Button>
|
|
77
|
+
}
|
|
78
|
+
/>
|
|
79
|
+
<SheetContent side="left" className="w-64 p-0">
|
|
80
|
+
<SheetHeader className="sr-only">
|
|
81
|
+
<SheetTitle>Navigation Menu</SheetTitle>
|
|
82
|
+
</SheetHeader>
|
|
83
|
+
<SidebarContent />
|
|
84
|
+
</SheetContent>
|
|
85
|
+
</Sheet>
|
|
86
|
+
|
|
87
|
+
{/* Breadcrumb Navigation */}
|
|
88
|
+
<div className="hidden sm:flex flex-1">
|
|
89
|
+
<Breadcrumb>
|
|
90
|
+
<BreadcrumbList>
|
|
91
|
+
<BreadcrumbItem>
|
|
92
|
+
<BreadcrumbLink href="#">Home</BreadcrumbLink>
|
|
93
|
+
</BreadcrumbItem>
|
|
94
|
+
<BreadcrumbSeparator />
|
|
95
|
+
<BreadcrumbItem>
|
|
96
|
+
<BreadcrumbLink href="#">Dashboard</BreadcrumbLink>
|
|
97
|
+
</BreadcrumbItem>
|
|
98
|
+
<BreadcrumbSeparator />
|
|
99
|
+
<BreadcrumbItem>
|
|
100
|
+
<BreadcrumbPage>Overview</BreadcrumbPage>
|
|
101
|
+
</BreadcrumbItem>
|
|
102
|
+
</BreadcrumbList>
|
|
103
|
+
</Breadcrumb>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div className="flex-1 sm:flex-none flex justify-end">
|
|
107
|
+
<div className="relative w-full max-w-sm sm:w-64">
|
|
59
108
|
<SearchIcon className="absolute left-2.5 top-2.5 size-4 text-muted-foreground" />
|
|
60
|
-
<Input type="search" placeholder="Search..." className="w-full pl-9 bg-muted/50" />
|
|
109
|
+
<Input type="search" placeholder="Search..." className="w-full pl-9 bg-muted/50 rounded-full" />
|
|
61
110
|
</div>
|
|
62
111
|
</div>
|
|
63
|
-
|
|
64
|
-
<Button variant="ghost" iconOnly className="text-muted-foreground relative">
|
|
112
|
+
|
|
113
|
+
<Button variant="ghost" iconOnly className="text-muted-foreground relative shrink-0">
|
|
65
114
|
<BellIcon className="size-5" />
|
|
66
115
|
<span className="absolute top-1 right-1 size-2 rounded-full bg-destructive border-2 border-background"></span>
|
|
67
116
|
</Button>
|
|
68
117
|
|
|
69
118
|
<DropdownMenu>
|
|
70
|
-
<DropdownMenuTrigger render={<button className="outline-none" />}>
|
|
119
|
+
<DropdownMenuTrigger render={<button className="outline-none shrink-0" />}>
|
|
71
120
|
<Avatar className="size-8 ring-2 ring-primary/20 hover:ring-primary/50 transition-all cursor-pointer">
|
|
72
121
|
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
|
73
122
|
<AvatarFallback>CN</AvatarFallback>
|
|
74
123
|
</Avatar>
|
|
75
124
|
</DropdownMenuTrigger>
|
|
76
125
|
<DropdownMenuContent align="end" className="w-56">
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
126
|
+
<DropdownMenuGroup>
|
|
127
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
128
|
+
<DropdownMenuSeparator />
|
|
129
|
+
<DropdownMenuItem>Profile</DropdownMenuItem>
|
|
130
|
+
<DropdownMenuItem>Billing</DropdownMenuItem>
|
|
131
|
+
<DropdownMenuItem>Team</DropdownMenuItem>
|
|
132
|
+
<DropdownMenuItem>Subscription</DropdownMenuItem>
|
|
133
|
+
</DropdownMenuGroup>
|
|
83
134
|
<DropdownMenuSeparator />
|
|
84
135
|
<DropdownMenuItem variant="destructive">Log out</DropdownMenuItem>
|
|
85
136
|
</DropdownMenuContent>
|
|
@@ -87,14 +138,19 @@ export default function DashboardBlock() {
|
|
|
87
138
|
</header>
|
|
88
139
|
|
|
89
140
|
{/* Dashboard Content */}
|
|
90
|
-
<div className="flex-1 overflow-auto p-
|
|
91
|
-
<div className="flex items-center justify-between mb-8">
|
|
141
|
+
<div className="flex-1 overflow-auto p-4 md:p-8">
|
|
142
|
+
<div className="flex flex-col sm:flex-row sm:items-center justify-between mb-8 gap-4">
|
|
92
143
|
<div>
|
|
93
144
|
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
|
|
94
145
|
<p className="text-muted-foreground mt-1">Here's an overview of your business today.</p>
|
|
95
146
|
</div>
|
|
96
147
|
<div className="flex items-center gap-2">
|
|
97
|
-
<
|
|
148
|
+
<DatePicker
|
|
149
|
+
mode="single"
|
|
150
|
+
value={date}
|
|
151
|
+
onValueChange={setDate}
|
|
152
|
+
/>
|
|
153
|
+
<Button color="primary">Download Report</Button>
|
|
98
154
|
</div>
|
|
99
155
|
</div>
|
|
100
156
|
|
|
@@ -104,10 +160,17 @@ export default function DashboardBlock() {
|
|
|
104
160
|
<CardDescription>Total Revenue</CardDescription>
|
|
105
161
|
<CardTitle className="text-2xl">$45,231.89</CardTitle>
|
|
106
162
|
</CardHeader>
|
|
107
|
-
<CardContent>
|
|
163
|
+
<CardContent className="space-y-4">
|
|
108
164
|
<p className="text-xs text-muted-foreground">
|
|
109
165
|
<span className="text-success font-medium">+20.1%</span> from last month
|
|
110
166
|
</p>
|
|
167
|
+
<div className="space-y-1">
|
|
168
|
+
<div className="flex text-xs justify-between">
|
|
169
|
+
<span>Monthly Goal</span>
|
|
170
|
+
<span className="font-medium">85%</span>
|
|
171
|
+
</div>
|
|
172
|
+
<Progress value={85} className="h-1.5" />
|
|
173
|
+
</div>
|
|
111
174
|
</CardContent>
|
|
112
175
|
</Card>
|
|
113
176
|
<Card>
|
|
@@ -150,7 +213,7 @@ export default function DashboardBlock() {
|
|
|
150
213
|
<CardHeader>
|
|
151
214
|
<CardTitle>Overview</CardTitle>
|
|
152
215
|
</CardHeader>
|
|
153
|
-
<CardContent className="h-[
|
|
216
|
+
<CardContent className="h-[350px] flex items-center justify-center border-t border-border/50 bg-muted/10 rounded-b-xl">
|
|
154
217
|
<p className="text-sm text-muted-foreground">Chart placeholder</p>
|
|
155
218
|
</CardContent>
|
|
156
219
|
</Card>
|
|
@@ -159,19 +222,64 @@ export default function DashboardBlock() {
|
|
|
159
222
|
<CardTitle>Recent Sales</CardTitle>
|
|
160
223
|
<CardDescription>You made 265 sales this month.</CardDescription>
|
|
161
224
|
</CardHeader>
|
|
162
|
-
<CardContent
|
|
163
|
-
|
|
164
|
-
<
|
|
165
|
-
<
|
|
166
|
-
<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
225
|
+
<CardContent>
|
|
226
|
+
<Table>
|
|
227
|
+
<TableHeader>
|
|
228
|
+
<TableRow>
|
|
229
|
+
<TableHead>Customer</TableHead>
|
|
230
|
+
<TableHead>Status</TableHead>
|
|
231
|
+
<TableHead className="text-right">Amount</TableHead>
|
|
232
|
+
</TableRow>
|
|
233
|
+
</TableHeader>
|
|
234
|
+
<TableBody>
|
|
235
|
+
{[
|
|
236
|
+
{ name: "Olivia Martin", email: "olivia.martin@email.com", amount: "$1,999.00", status: "success" },
|
|
237
|
+
{ name: "Jackson Lee", email: "jackson.lee@email.com", amount: "$39.00", status: "pending" },
|
|
238
|
+
{ name: "Isabella Nguyen", email: "isabella.nguyen@email.com", amount: "$299.00", status: "success" },
|
|
239
|
+
{ name: "William Kim", email: "will@email.com", amount: "$99.00", status: "failed" },
|
|
240
|
+
].map((sale, i) => (
|
|
241
|
+
<TableRow key={i}>
|
|
242
|
+
<TableCell>
|
|
243
|
+
<HoverCard>
|
|
244
|
+
<HoverCardTrigger
|
|
245
|
+
render={
|
|
246
|
+
<div className="flex items-center gap-3 cursor-pointer">
|
|
247
|
+
<Avatar className="size-8">
|
|
248
|
+
<AvatarFallback>{sale.name.substring(0, 2).toUpperCase()}</AvatarFallback>
|
|
249
|
+
</Avatar>
|
|
250
|
+
<div className="space-y-0.5">
|
|
251
|
+
<p className="text-sm font-medium leading-none">{sale.name}</p>
|
|
252
|
+
<p className="text-xs text-muted-foreground">{sale.email}</p>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
}
|
|
256
|
+
/>
|
|
257
|
+
<HoverCardContent className="w-80">
|
|
258
|
+
<div className="flex justify-between space-x-4">
|
|
259
|
+
<Avatar>
|
|
260
|
+
<AvatarFallback>{sale.name.substring(0, 2).toUpperCase()}</AvatarFallback>
|
|
261
|
+
</Avatar>
|
|
262
|
+
<div className="space-y-1">
|
|
263
|
+
<h4 className="text-sm font-semibold">@{sale.name.toLowerCase().replace(' ', '')}</h4>
|
|
264
|
+
<p className="text-sm">Premium customer since 2023.</p>
|
|
265
|
+
<div className="flex items-center pt-2">
|
|
266
|
+
<span className="text-xs text-muted-foreground">Last active 2 hours ago</span>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</HoverCardContent>
|
|
271
|
+
</HoverCard>
|
|
272
|
+
</TableCell>
|
|
273
|
+
<TableCell>
|
|
274
|
+
{sale.status === "success" && <Badge variant="soft" color="success">Paid</Badge>}
|
|
275
|
+
{sale.status === "pending" && <Badge variant="outline" color="warning">Pending</Badge>}
|
|
276
|
+
{sale.status === "failed" && <Badge variant="soft" color="destructive">Failed</Badge>}
|
|
277
|
+
</TableCell>
|
|
278
|
+
<TableCell className="text-right font-medium">{sale.amount}</TableCell>
|
|
279
|
+
</TableRow>
|
|
280
|
+
))}
|
|
281
|
+
</TableBody>
|
|
282
|
+
</Table>
|
|
175
283
|
</CardContent>
|
|
176
284
|
</Card>
|
|
177
285
|
</div>
|