gladvn 0.2.35 → 0.2.37
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 +1 -1
- package/package.json +2 -1
- package/scripts/strip-comments.ts +79 -0
- package/src/blocks/auth-form.tsx +34 -30
- package/src/blocks/auth-recovery.tsx +9 -13
- package/src/blocks/auth-split.tsx +46 -36
- package/src/blocks/settings.tsx +84 -117
- package/src/components/macro/alert-preset.tsx +3 -1
- package/src/components/macro/carousel-preset.tsx +0 -1
- package/src/components/macro/checkbox-preset.test.tsx +2 -2
- package/src/components/macro/checkbox-preset.tsx +1 -2
- package/src/components/macro/command-preset.tsx +1 -1
- package/src/components/macro/date-picker.tsx +5 -18
- package/src/components/macro/dialog-preset.tsx +5 -3
- package/src/components/macro/input-preset.tsx +0 -1
- package/src/components/macro/select-preset.tsx +1 -1
- package/src/components/macro/switch-preset.test.tsx +1 -1
- package/src/components/micro/accordion.test.tsx +6 -12
- package/src/components/micro/alert-dialog.tsx +0 -2
- package/src/components/micro/alert.test.tsx +5 -8
- package/src/components/micro/alert.tsx +0 -2
- package/src/components/micro/badge.test.tsx +5 -7
- package/src/components/micro/badge.tsx +1 -3
- package/src/components/micro/button.test.tsx +19 -26
- package/src/components/micro/button.tsx +3 -11
- package/src/components/micro/calendar.tsx +12 -13
- package/src/components/micro/card.test.tsx +3 -5
- package/src/components/micro/carousel.tsx +0 -1
- package/src/components/micro/chart.tsx +3 -2
- package/src/components/micro/checkbox.test.tsx +9 -18
- package/src/components/micro/combobox.tsx +1 -3
- package/src/components/micro/command.tsx +23 -11
- package/src/components/micro/context-menu.tsx +0 -2
- package/src/components/micro/dialog.test.tsx +6 -14
- package/src/components/micro/dialog.tsx +0 -2
- package/src/components/micro/drawer.tsx +0 -3
- package/src/components/micro/dropdown-menu.test.tsx +8 -14
- package/src/components/micro/dropdown-menu.tsx +0 -2
- package/src/components/micro/field.tsx +3 -3
- package/src/components/micro/hover-card.tsx +0 -2
- package/src/components/micro/input-group.tsx +9 -42
- package/src/components/micro/input.test.tsx +15 -24
- package/src/components/micro/navigation-menu.tsx +0 -2
- package/src/components/micro/popover.test.tsx +5 -9
- package/src/components/micro/progress.tsx +5 -1
- package/src/components/micro/select.test.tsx +5 -18
- package/src/components/micro/select.tsx +3 -5
- package/src/components/micro/separator.tsx +1 -1
- package/src/components/micro/sheet.test.tsx +6 -14
- package/src/components/micro/sheet.tsx +0 -2
- package/src/components/micro/sidebar.tsx +5 -6
- package/src/components/micro/sonner.tsx +0 -1
- package/src/components/micro/switch.test.tsx +9 -18
- package/src/components/micro/switch.tsx +1 -1
- package/src/components/micro/tabs.test.tsx +5 -11
- package/src/components/micro/theme-provider.tsx +0 -12
- package/src/components/micro/toggle-group.tsx +1 -12
- package/src/components/micro/tooltip.test.tsx +7 -12
- package/src/dev/App.tsx +6 -6
- package/src/dev/components/BlockViewer.tsx +15 -4
- package/src/dev/components/Container.tsx +20 -0
- package/src/dev/components/GladvnLogo.tsx +3 -9
- package/src/dev/components/code-highlighter.tsx +5 -3
- package/src/dev/components/showcase.tsx +116 -168
- package/src/dev/sections/buttons.tsx +4 -9
- package/src/dev/sections/display.tsx +68 -72
- package/src/dev/sections/feedback.tsx +304 -314
- package/src/dev/sections/forms.tsx +10 -14
- package/src/dev/sections/interactive.tsx +32 -32
- package/src/dev/sections/overview.tsx +2 -10
- package/src/dev/showcase/accordion.tsx +512 -554
- package/src/dev/showcase/alert-dialog.tsx +525 -565
- package/src/dev/showcase/alert.tsx +352 -402
- package/src/dev/showcase/aspect-ratio.tsx +167 -193
- package/src/dev/showcase/avatar.tsx +231 -269
- package/src/dev/showcase/badge.tsx +92 -140
- package/src/dev/showcase/breadcrumb.tsx +113 -135
- package/src/dev/showcase/button.tsx +140 -173
- package/src/dev/showcase/calendar.tsx +187 -237
- package/src/dev/showcase/card.tsx +229 -261
- package/src/dev/showcase/carousel.tsx +288 -315
- package/src/dev/showcase/chart.tsx +44 -50
- package/src/dev/showcase/checkbox.tsx +286 -330
- package/src/dev/showcase/collapsible.tsx +143 -160
- package/src/dev/showcase/combobox.tsx +327 -369
- package/src/dev/showcase/command.tsx +106 -120
- package/src/dev/showcase/context-menu.tsx +93 -107
- package/src/dev/showcase/dialog.tsx +585 -630
- package/src/dev/showcase/drawer.tsx +176 -196
- package/src/dev/showcase/dropdown-menu.tsx +183 -201
- package/src/dev/showcase/empty.tsx +232 -274
- package/src/dev/showcase/hover-card.tsx +153 -180
- package/src/dev/showcase/input-group.tsx +221 -253
- package/src/dev/showcase/input-otp.tsx +204 -250
- package/src/dev/showcase/input.tsx +246 -325
- package/src/dev/showcase/item.tsx +134 -152
- package/src/dev/showcase/kbd.tsx +149 -172
- package/src/dev/showcase/label.tsx +50 -68
- package/src/dev/showcase/menubar.tsx +194 -213
- package/src/dev/showcase/navigation-menu.tsx +119 -133
- package/src/dev/showcase/overview-components/ArchitectureSection.tsx +126 -0
- package/src/dev/showcase/overview-components/CodeBlock.tsx +50 -0
- package/src/dev/showcase/overview-components/ColorPaletteSection.tsx +44 -0
- package/src/dev/showcase/overview-components/DashboardPreview.tsx +159 -0
- package/src/dev/showcase/overview-components/FeatureGridSection.tsx +535 -0
- package/src/dev/showcase/overview-components/FooterCTA.tsx +137 -0
- package/src/dev/showcase/overview-components/HeroSection.tsx +65 -0
- package/src/dev/showcase/overview-components/PrinciplesSection.tsx +128 -0
- package/src/dev/showcase/overview.tsx +21 -1163
- package/src/dev/showcase/pagination.tsx +144 -190
- package/src/dev/showcase/popover.tsx +181 -196
- package/src/dev/showcase/progress.tsx +141 -189
- package/src/dev/showcase/radio-group.tsx +272 -332
- package/src/dev/showcase/resizable.tsx +119 -141
- package/src/dev/showcase/scroll-area.tsx +21 -36
- package/src/dev/showcase/select.tsx +262 -322
- package/src/dev/showcase/separator.tsx +75 -73
- package/src/dev/showcase/sheet.tsx +333 -361
- package/src/dev/showcase/sidebar.tsx +53 -59
- package/src/dev/showcase/skeleton.tsx +93 -112
- package/src/dev/showcase/slider.tsx +244 -296
- package/src/dev/showcase/sonner.tsx +251 -258
- package/src/dev/showcase/spinner.tsx +46 -67
- package/src/dev/showcase/switch.tsx +298 -362
- package/src/dev/showcase/table.tsx +76 -80
- package/src/dev/showcase/tabs.tsx +302 -355
- package/src/dev/showcase/textarea.tsx +240 -293
- package/src/dev/showcase/toast.tsx +48 -54
- package/src/dev/showcase/toggle-group.tsx +104 -129
- package/src/dev/showcase/toggle.tsx +82 -102
- package/src/dev/showcase/tooltip.tsx +173 -192
- package/src/dev/showcase/why.tsx +6 -12
- package/src/hooks/use-mobile.test.ts +3 -2
- package/src/lib/utils.test.ts +5 -4
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://github.com/duongacy/gladvn/actions"><img src="https://img.shields.io/github/actions/workflow/status/duongacy/gladvn/ci.yml?branch=main" alt="Build Status"></a>
|
|
8
8
|
<a href="https://github.com/duongacy/gladvn/stargazers"><img src="https://img.shields.io/github/stars/duongacy/gladvn" alt="GitHub Stars"></a>
|
|
9
|
-
<a href="https://github.com/duongacy/gladvn/blob/main/LICENSE"><img src="https://img.shields.io/github/license/duongacy/gladvn" alt="License"></a>
|
|
9
|
+
<a href="https://github.com/duongacy/gladvn/blob/main/LICENSE"><img src="https://img.shields.io/github/license/duongacy/gladvn?t=1" alt="License"></a>
|
|
10
10
|
</p>
|
|
11
11
|
<br/>
|
|
12
12
|
<!-- Hero Image Placeholder -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gladvn",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@10.11.0",
|
|
6
6
|
"description": "A CLI to scaffold beautiful, accessible React components into your project. Powered by Tailwind CSS v4 and Base UI.",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"dev": "vite",
|
|
38
|
+
"build": "npm run build:app",
|
|
38
39
|
"build:app": "vite build -c vite.config.app.ts",
|
|
39
40
|
"lint": "oxlint",
|
|
40
41
|
"preview": "vite preview",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
|
|
5
|
+
function stripCommentsFromFile(filePath: string) {
|
|
6
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
7
|
+
let result = '';
|
|
8
|
+
|
|
9
|
+
const scanner = ts.createScanner(ts.ScriptTarget.Latest, false);
|
|
10
|
+
scanner.setText(content);
|
|
11
|
+
|
|
12
|
+
let currentToken = scanner.scan();
|
|
13
|
+
let lastPos = 0;
|
|
14
|
+
|
|
15
|
+
while (currentToken !== ts.SyntaxKind.EndOfFileToken) {
|
|
16
|
+
const tokenStart = scanner.getTokenPos();
|
|
17
|
+
const tokenEnd = scanner.getTextPos();
|
|
18
|
+
|
|
19
|
+
if (currentToken === ts.SyntaxKind.SingleLineCommentTrivia) {
|
|
20
|
+
// It's a // comment. Strip it!
|
|
21
|
+
result += content.substring(lastPos, tokenStart);
|
|
22
|
+
lastPos = tokenEnd;
|
|
23
|
+
} else if (currentToken === ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
24
|
+
// It's a /* */ comment.
|
|
25
|
+
const commentText = content.substring(tokenStart, tokenEnd);
|
|
26
|
+
if (commentText.startsWith('/**')) {
|
|
27
|
+
// Keep JSDoc
|
|
28
|
+
} else {
|
|
29
|
+
// Strip normal multiline comment
|
|
30
|
+
result += content.substring(lastPos, tokenStart);
|
|
31
|
+
lastPos = tokenEnd;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
currentToken = scanner.scan();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
result += content.substring(lastPos);
|
|
40
|
+
|
|
41
|
+
// Also clean up multiple blank lines left by deleted single line comments
|
|
42
|
+
// (Optional, but helps keep code clean)
|
|
43
|
+
result = result.replace(/\n\s*\n\s*\n/g, '\n\n');
|
|
44
|
+
|
|
45
|
+
if (result !== content) {
|
|
46
|
+
fs.writeFileSync(filePath, result, 'utf8');
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function processDirectory(dir: string) {
|
|
53
|
+
let count = 0;
|
|
54
|
+
const files = fs.readdirSync(dir);
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
const fullPath = path.join(dir, file);
|
|
57
|
+
const stat = fs.statSync(fullPath);
|
|
58
|
+
if (stat.isDirectory()) {
|
|
59
|
+
count += processDirectory(fullPath);
|
|
60
|
+
} else if (fullPath.endsWith('.ts') || fullPath.endsWith('.tsx')) {
|
|
61
|
+
if (stripCommentsFromFile(fullPath)) {
|
|
62
|
+
count++;
|
|
63
|
+
console.log(`Stripped comments from: ${fullPath}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return count;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const targetDirs = ['src/components', 'src/blocks', 'src/dev'];
|
|
71
|
+
let totalChanged = 0;
|
|
72
|
+
|
|
73
|
+
for (const dir of targetDirs) {
|
|
74
|
+
if (fs.existsSync(dir)) {
|
|
75
|
+
totalChanged += processDirectory(dir);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(`\nFinished! Stripped comments from ${totalChanged} files.`);
|
package/src/blocks/auth-form.tsx
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
+
import { CheckboxPreset } from "../components/macro/checkbox-preset";
|
|
4
|
+
import { InputPreset } from "../components/macro/input-preset";
|
|
3
5
|
import { Button } from "../components/micro/button";
|
|
4
6
|
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
7
|
|
|
9
8
|
export default function AuthCardBlock() {
|
|
10
9
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -32,44 +31,49 @@ export default function AuthCardBlock() {
|
|
|
32
31
|
<CardContent className="flex flex-col gap-6 px-0 pb-0">
|
|
33
32
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
34
33
|
{mode === "register" && (
|
|
35
|
-
<
|
|
36
|
-
<Label htmlFor="name">Full Name</Label>
|
|
37
|
-
<Input id="name" name="name" placeholder="John Doe" className="w-full" required />
|
|
38
|
-
</div>
|
|
34
|
+
<InputPreset id="name" name="name" label="Full Name" placeholder="John Doe" required />
|
|
39
35
|
)}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
36
|
+
|
|
37
|
+
<InputPreset id="email" name="email" type="email" label="Email" placeholder="m@example.com" required />
|
|
38
|
+
|
|
39
|
+
<InputPreset
|
|
40
|
+
id="password"
|
|
41
|
+
name="password"
|
|
42
|
+
type="password"
|
|
43
|
+
label={
|
|
44
|
+
<div className="flex items-center justify-between w-full">
|
|
45
|
+
<span>Mật khẩu</span>
|
|
46
|
+
{mode === "login" && (
|
|
47
|
+
<Button variant="link" color="primary" className="h-auto p-0 text-xs">
|
|
48
|
+
Quên mật khẩu?
|
|
49
|
+
</Button>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
}
|
|
53
|
+
required
|
|
54
|
+
/>
|
|
55
55
|
|
|
56
56
|
{mode === "register" && (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
<CheckboxPreset
|
|
58
|
+
id="terms"
|
|
59
|
+
name="terms"
|
|
60
|
+
required
|
|
61
|
+
label={
|
|
62
|
+
<span className="text-sm font-normal text-muted-foreground">
|
|
63
|
+
Tôi đồng ý với <a href="#" className="text-primary hover:underline">các điều khoản và điều kiện</a>
|
|
64
|
+
</span>
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
63
67
|
)}
|
|
64
68
|
|
|
65
69
|
<Button type="submit" className="w-full" disabled={isLoading} aria-busy={isLoading}>
|
|
66
|
-
{isLoading ? "
|
|
70
|
+
{isLoading ? "Đang xử lý..." : mode === "login" ? "Đăng nhập" : "Tạo tài khoản"}
|
|
67
71
|
</Button>
|
|
68
72
|
</form>
|
|
69
73
|
|
|
70
74
|
<div className="flex justify-center">
|
|
71
75
|
<span className="text-[13px] font-medium text-muted-foreground uppercase">
|
|
72
|
-
|
|
76
|
+
Hoặc tiếp tục với
|
|
73
77
|
</span>
|
|
74
78
|
</div>
|
|
75
79
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
+
import { InputPreset } from "../components/macro/input-preset";
|
|
3
4
|
import { Button } from "../components/micro/button";
|
|
4
5
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../components/micro/card";
|
|
5
|
-
import { Input } from "../components/micro/input";
|
|
6
|
-
import { Label } from "../components/micro/label";
|
|
7
6
|
|
|
8
7
|
export default function AuthRecoveryBlock() {
|
|
9
8
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -19,40 +18,37 @@ export default function AuthRecoveryBlock() {
|
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
return (
|
|
22
|
-
<div className="flex min-h-
|
|
21
|
+
<div className="flex min-h-screen w-full items-center justify-center p-4 bg-muted/20">
|
|
23
22
|
<Card className="w-full max-w-sm">
|
|
24
23
|
<CardHeader className="text-center">
|
|
25
24
|
<CardTitle className="text-2xl font-bold">
|
|
26
|
-
{isSent ? "
|
|
25
|
+
{isSent ? "Kiểm tra email của bạn" : "Quên mật khẩu?"}
|
|
27
26
|
</CardTitle>
|
|
28
27
|
<CardDescription>
|
|
29
28
|
{isSent
|
|
30
|
-
? "
|
|
31
|
-
: "
|
|
29
|
+
? "Chúng tôi đã gửi link đặt lại mật khẩu đến email của bạn."
|
|
30
|
+
: "Không sao đâu, ai cũng có lúc quên mà. Nhập email vào đây để tụi mình gửi link reset cho nha."}
|
|
32
31
|
</CardDescription>
|
|
33
32
|
</CardHeader>
|
|
34
33
|
<CardContent>
|
|
35
34
|
{!isSent ? (
|
|
36
35
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
37
|
-
<
|
|
38
|
-
<Label htmlFor="recovery-email">Email</Label>
|
|
39
|
-
<Input id="recovery-email" type="email" placeholder="m@example.com" className="w-full" required />
|
|
40
|
-
</div>
|
|
36
|
+
<InputPreset id="recovery-email" type="email" label="Email" placeholder="m@example.com" required />
|
|
41
37
|
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
42
|
-
{isLoading ? "
|
|
38
|
+
{isLoading ? "Đang gửi..." : "Gửi link reset mật khẩu"}
|
|
43
39
|
</Button>
|
|
44
40
|
</form>
|
|
45
41
|
) : (
|
|
46
42
|
<div className="flex flex-col gap-4">
|
|
47
43
|
<Button type="button" variant="outline" className="w-full" onClick={() => setIsSent(false)}>
|
|
48
|
-
|
|
44
|
+
Thử email khác
|
|
49
45
|
</Button>
|
|
50
46
|
</div>
|
|
51
47
|
)}
|
|
52
48
|
</CardContent>
|
|
53
49
|
<CardFooter className="flex justify-center">
|
|
54
50
|
<Button variant="link" color="primary" className="text-sm">
|
|
55
|
-
|
|
51
|
+
Quay lại đăng nhập
|
|
56
52
|
</Button>
|
|
57
53
|
</CardFooter>
|
|
58
54
|
</Card>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useState } from "react";
|
|
3
|
+
import { CheckboxPreset } from "../components/macro/checkbox-preset";
|
|
4
|
+
import { InputPreset } from "../components/macro/input-preset";
|
|
3
5
|
import { Button } from "../components/micro/button";
|
|
4
|
-
import { Checkbox } from "../components/micro/checkbox";
|
|
5
|
-
import { Input } from "../components/micro/input";
|
|
6
|
-
import { Label } from "../components/micro/label";
|
|
7
6
|
import { Separator } from "../components/micro/separator";
|
|
8
7
|
|
|
9
8
|
export default function AuthSplitBlock() {
|
|
@@ -18,9 +17,7 @@ export default function AuthSplitBlock() {
|
|
|
18
17
|
|
|
19
18
|
return (
|
|
20
19
|
<div className="flex min-h-screen w-full">
|
|
21
|
-
{/* Left side - Image/Branding (hidden on mobile) */}
|
|
22
20
|
<div className="hidden lg:flex w-1/2 flex-col justify-between bg-primary p-12 text-primary-foreground relative overflow-hidden">
|
|
23
|
-
{/* Subtle background pattern */}
|
|
24
21
|
<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
22
|
|
|
26
23
|
<div className="relative z-10">
|
|
@@ -42,58 +39,71 @@ export default function AuthSplitBlock() {
|
|
|
42
39
|
<div key={i} className="size-8 rounded-full border-2 border-primary bg-primary-foreground/20" />
|
|
43
40
|
))}
|
|
44
41
|
</div>
|
|
45
|
-
<p
|
|
42
|
+
<p>Được tin dùng bởi hơn 10k+ nhà phát triển</p>
|
|
46
43
|
</div>
|
|
47
44
|
</div>
|
|
48
45
|
|
|
49
|
-
{/* Right side - Form */}
|
|
50
46
|
<div className="flex w-full lg:w-1/2 items-center justify-center p-8 sm:p-12">
|
|
51
47
|
<div className="mx-auto flex w-full max-w-sm flex-col justify-center space-y-6">
|
|
52
48
|
<div className="flex flex-col space-y-2 text-center lg:text-left">
|
|
53
49
|
<h1 className="text-3xl font-bold tracking-tight">
|
|
54
|
-
{mode === "login" ? "
|
|
50
|
+
{mode === "login" ? "Chào mừng trở lại" : "Tạo tài khoản mới"}
|
|
55
51
|
</h1>
|
|
56
52
|
<p className="text-sm text-muted-foreground">
|
|
57
53
|
{mode === "login"
|
|
58
|
-
? "
|
|
59
|
-
: "
|
|
54
|
+
? "Nhập email của bạn bên dưới để đăng nhập vào tài khoản"
|
|
55
|
+
: "Nhập thông tin chi tiết bên dưới để tạo tài khoản của bạn"}
|
|
60
56
|
</p>
|
|
61
57
|
</div>
|
|
62
58
|
|
|
63
59
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
64
60
|
{mode === "register" && (
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
<InputPreset
|
|
62
|
+
id="name"
|
|
63
|
+
label="Họ và tên"
|
|
64
|
+
placeholder="Nguyễn Văn A"
|
|
65
|
+
required
|
|
66
|
+
/>
|
|
69
67
|
)}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
68
|
+
|
|
69
|
+
<InputPreset
|
|
70
|
+
id="email"
|
|
71
|
+
type="email"
|
|
72
|
+
label="Email"
|
|
73
|
+
placeholder="m@example.com"
|
|
74
|
+
required
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<InputPreset
|
|
78
|
+
id="password"
|
|
79
|
+
type="password"
|
|
80
|
+
label={
|
|
81
|
+
<div className="flex items-center justify-between w-full">
|
|
82
|
+
<span>Mật khẩu</span>
|
|
83
|
+
{mode === "login" && (
|
|
84
|
+
<Button type="button" variant="link" color="primary" className="h-auto p-0 text-xs">
|
|
85
|
+
Quên mật khẩu?
|
|
86
|
+
</Button>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
}
|
|
90
|
+
required
|
|
91
|
+
/>
|
|
85
92
|
|
|
86
93
|
{mode === "register" && (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
<CheckboxPreset
|
|
95
|
+
id="terms"
|
|
96
|
+
required
|
|
97
|
+
label={
|
|
98
|
+
<span className="text-sm font-normal text-muted-foreground">
|
|
99
|
+
Tôi đồng ý với các <a href="#" className="text-primary hover:underline">điều khoản và dịch vụ</a>
|
|
100
|
+
</span>
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
93
103
|
)}
|
|
94
104
|
|
|
95
105
|
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
96
|
-
{isLoading ? "
|
|
106
|
+
{isLoading ? "Đang xử lý..." : mode === "login" ? "Đăng nhập" : "Tạo tài khoản"}
|
|
97
107
|
</Button>
|
|
98
108
|
</form>
|
|
99
109
|
|