basuicn 0.2.8 → 0.2.9

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/ui-cli.cjs CHANGED
@@ -27,7 +27,7 @@ var import_fs = __toESM(require("fs"), 1);
27
27
  var import_path = __toESM(require("path"), 1);
28
28
  var import_child_process = require("child_process");
29
29
  var import_readline = __toESM(require("readline"), 1);
30
- var VERSION = "0.2.8";
30
+ var VERSION = "0.2.9";
31
31
  var REGISTRY_LOCAL = "./registry.json";
32
32
  var REGISTRY_REMOTE = "https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json";
33
33
  var c = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "basuicn",
3
3
  "private": false,
4
- "version": "0.2.8",
4
+ "version": "0.2.9",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "basuicn": "./dist/ui-cli.cjs"
package/registry.json CHANGED
@@ -435,6 +435,116 @@
435
435
  }
436
436
  ]
437
437
  },
438
+ "login-classic": {
439
+ "name": "login-classic",
440
+ "dependencies": [
441
+ "react-hook-form",
442
+ "@hookform/resolvers",
443
+ "zod",
444
+ "lucide-react",
445
+ "sonner"
446
+ ],
447
+ "internalDependencies": [
448
+ "form",
449
+ "input",
450
+ "button",
451
+ "checkbox",
452
+ "card"
453
+ ],
454
+ "files": [
455
+ {
456
+ "path": "src/components/ui/login-classic/LoginClassic.tsx",
457
+ "content": "import * as React from 'react';\r\nimport { useForm } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\nimport { Mail, Lock } from 'lucide-react';\r\nimport { Form, FormField } from '@/components/ui/form/Form';\r\nimport { Input } from '@/components/ui/input/Input';\r\nimport { Button } from '@/components/ui/button/Button';\r\nimport { Checkbox } from '@/components/ui/checkbox/Checkbox';\r\nimport {\r\n Card,\r\n CardContent,\r\n CardHeader,\r\n CardTitle,\r\n CardDescription,\r\n} from '@/components/ui/card/Card';\r\nimport { toast } from 'sonner';\r\n\r\nconst loginSchema = z.object({\r\n email: z.string().email('Email không hợp lệ'),\r\n password: z.string().min(6, 'Mật khẩu ít nhất 6 ký tự'),\r\n rememberMe: z.boolean().optional(),\r\n});\r\n\r\ntype LoginValues = z.infer<typeof loginSchema>;\r\n\r\nexport const LoginClassic = () => {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n\r\n const form = useForm<LoginValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: { email: '', password: '', rememberMe: false },\r\n });\r\n\r\n const onSubmit = async (_data: LoginValues) => {\r\n setIsLoading(true);\r\n await new Promise<void>((resolve) => setTimeout(resolve, 1500));\r\n setIsLoading(false);\r\n toast.success('Đăng nhập thành công!');\r\n };\r\n\r\n return (\r\n <Card className=\"w-full max-w-sm animate-in fade-in zoom-in-95 duration-500\">\r\n <CardHeader className=\"text-center pb-4 pt-8\">\r\n <div className=\"mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-primary/10\">\r\n <Lock className=\"h-7 w-7 text-primary\" />\r\n </div>\r\n <CardTitle className=\"text-2xl\">Chào mừng trở lại</CardTitle>\r\n <CardDescription>Đăng nhập vào tài khoản của bạn</CardDescription>\r\n </CardHeader>\r\n\r\n <CardContent className=\"px-6 pb-8\">\r\n <Form {...form}>\r\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\r\n <FormField\r\n control={form.control}\r\n name=\"email\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Email\"\r\n type=\"email\"\r\n placeholder=\"email@example.com\"\r\n icon={<Mail className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n <FormField\r\n control={form.control}\r\n name=\"password\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Mật khẩu\"\r\n type=\"password\"\r\n placeholder=\"••••••••\"\r\n icon={<Lock className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n\r\n <div className=\"flex items-center justify-between\">\r\n <FormField\r\n control={form.control}\r\n name=\"rememberMe\"\r\n render={({ field }) => (\r\n <Checkbox\r\n label=\"Ghi nhớ đăng nhập\"\r\n checked={field.value}\r\n onCheckedChange={(checked) => field.onChange(checked === true)}\r\n />\r\n )}\r\n />\r\n <Button variant=\"link\" type=\"button\">\r\n Quên mật khẩu?\r\n </Button>\r\n </div>\r\n\r\n <Button\r\n type=\"submit\"\r\n className=\"w-full\"\r\n isLoading={isLoading}\r\n disabled={isLoading}\r\n >\r\n Đăng nhập\r\n </Button>\r\n\r\n <div className=\"relative my-2\">\r\n <div className=\"absolute inset-0 flex items-center\">\r\n <span className=\"w-full border-t border-border\" />\r\n </div>\r\n <div className=\"relative flex justify-center text-xs uppercase\">\r\n <span className=\"bg-card px-2 text-muted-foreground\">Hoặc tiếp tục với</span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <Button variant=\"outline\" type=\"button\">Google</Button>\r\n <Button variant=\"outline\" type=\"button\">GitHub</Button>\r\n </div>\r\n </form>\r\n </Form>\r\n\r\n <p className=\"mt-4 text-center text-sm text-muted-foreground\">\r\n Chưa có tài khoản?{' '}\r\n <Button variant=\"link\" type=\"button\" className=\"px-0\">\r\n Đăng ký ngay\r\n </Button>\r\n </p>\r\n </CardContent>\r\n </Card>\r\n );\r\n};\r\n"
458
+ }
459
+ ]
460
+ },
461
+ "login-fullscreen": {
462
+ "name": "login-fullscreen",
463
+ "dependencies": [
464
+ "react-hook-form",
465
+ "@hookform/resolvers",
466
+ "zod",
467
+ "lucide-react",
468
+ "sonner"
469
+ ],
470
+ "internalDependencies": [
471
+ "form",
472
+ "input",
473
+ "button",
474
+ "checkbox"
475
+ ],
476
+ "files": [
477
+ {
478
+ "path": "src/components/ui/login-fullscreen/LoginFullScreen.tsx",
479
+ "content": "\r\nimport * as React from 'react';\r\nimport { useForm } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\nimport { Mail, Lock, Quote } from 'lucide-react';\r\nimport { Form, FormField } from '@/components/ui/form/Form';\r\nimport { Input } from '@/components/ui/input/Input';\r\nimport { Button } from '@/components/ui/button/Button';\r\nimport { Checkbox } from '@/components/ui/checkbox/Checkbox';\r\nimport { toast } from 'sonner';\r\n\r\nconst loginSchema = z.object({\r\n email: z.string().email('Email không hợp lệ'),\r\n password: z.string().min(6, 'Mật khẩu ít nhất 6 ký tự'),\r\n rememberMe: z.boolean().optional(),\r\n});\r\n\r\ntype LoginValues = z.infer<typeof loginSchema>;\r\n\r\nexport const LoginFullScreen = () => {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n\r\n const form = useForm<LoginValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: { email: '', password: '', rememberMe: false },\r\n });\r\n\r\n const onSubmit = async (_data: LoginValues) => {\r\n setIsLoading(true);\r\n await new Promise<void>((resolve) => setTimeout(resolve, 1500));\r\n setIsLoading(false);\r\n toast.success('Đăng nhập thành công!');\r\n };\r\n\r\n return (\r\n <div className=\"h-screen w-full flex\">\r\n\r\n {/* ── Left: Color panel ─────────────────────────────────── */}\r\n <div className=\"hidden md:flex w-[60%] flex-col justify-between p-12 bg-gradient-to-br from-primary via-primary/90 to-violet-700 relative overflow-hidden\">\r\n {/* Decorative circles */}\r\n <div className=\"absolute -top-24 -left-24 w-96 h-96 bg-white/5 rounded-full\" />\r\n <div className=\"absolute top-1/2 -right-32 w-72 h-72 bg-white/5 rounded-full\" />\r\n <div className=\"absolute -bottom-16 left-1/4 w-56 h-56 bg-white/5 rounded-full\" />\r\n\r\n {/* Logo */}\r\n <div className=\"relative flex items-center gap-3\">\r\n <div className=\"w-9 h-9 bg-white/20 backdrop-blur rounded-xl flex items-center justify-center\">\r\n <span className=\"text-white font-bold text-sm select-none\">UI</span>\r\n </div>\r\n <span className=\"text-white font-semibold text-lg tracking-tight\">BasuiCN</span>\r\n </div>\r\n\r\n {/* Quote */}\r\n <div className=\"relative space-y-5\">\r\n <Quote className=\"w-10 h-10 text-white/30\" />\r\n <p className=\"text-white text-2xl font-medium leading-relaxed max-w-xs\">\r\n Công cụ tốt nhất là thứ giúp bạn làm việc nhanh hơn, không phải phức tạp hơn.\r\n </p>\r\n <div className=\"flex items-center gap-3 pt-2\">\r\n <div className=\"w-10 h-10 rounded-full bg-white/20 flex items-center justify-center text-white text-sm font-bold\">\r\n NT\r\n </div>\r\n <div>\r\n <p className=\"text-white text-sm font-semibold\">Nguyễn Trung</p>\r\n <p className=\"text-white/50 text-xs\">Product Lead · BasuiCN</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <p className=\"relative text-white/30 text-xs\">© 2026 BasuiCN. All rights reserved.</p>\r\n </div>\r\n\r\n {/* ── Right: Form panel ─────────────────────────────────── */}\r\n <div className=\"flex-1 flex flex-col bg-background overflow-y-auto\">\r\n {/* Top bar */}\r\n <div className=\"flex items-center justify-between p-8\">\r\n {/* Mobile logo */}\r\n <div className=\"flex items-center gap-2 md:invisible\">\r\n <div className=\"w-8 h-8 bg-primary rounded-lg flex items-center justify-center\">\r\n <span className=\"text-primary-foreground font-bold text-xs select-none\">UI</span>\r\n </div>\r\n <span className=\"font-semibold text-foreground text-sm\">BasuiCN</span>\r\n </div>\r\n <Button variant=\"ghost\" size=\"sm\" className=\"text-sm text-muted-foreground ml-auto\">\r\n Chưa có tài khoản?&nbsp;\r\n <span className=\"text-primary font-semibold\">Đăng ký</span>\r\n </Button>\r\n </div>\r\n\r\n {/* Centered form */}\r\n <div className=\"flex-1 flex items-center justify-center px-8 pb-12\">\r\n <div className=\"w-full max-w-sm space-y-8\">\r\n <div className=\"space-y-1\">\r\n <h1 className=\"text-3xl font-bold text-foreground tracking-tight\">Chào mừng trở lại</h1>\r\n <p className=\"text-muted-foreground text-sm\">Đăng nhập để tiếp tục sử dụng dịch vụ</p>\r\n </div>\r\n\r\n <Form {...form}>\r\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-5\">\r\n <FormField\r\n control={form.control}\r\n name=\"email\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Email\"\r\n type=\"email\"\r\n placeholder=\"email@example.com\"\r\n icon={<Mail className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n <FormField\r\n control={form.control}\r\n name=\"password\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Mật khẩu\"\r\n type=\"password\"\r\n placeholder=\"••••••••\"\r\n icon={<Lock className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n\r\n <div className=\"flex items-center justify-between\">\r\n <FormField\r\n control={form.control}\r\n name=\"rememberMe\"\r\n render={({ field }) => (\r\n <Checkbox\r\n label=\"Ghi nhớ đăng nhập\"\r\n checked={field.value}\r\n onCheckedChange={(checked) => field.onChange(checked === true)}\r\n />\r\n )}\r\n />\r\n <Button variant=\"link\" type=\"button\" className=\"px-0 text-sm\">\r\n Quên mật khẩu?\r\n </Button>\r\n </div>\r\n\r\n <Button type=\"submit\" className=\"w-full\" size=\"lg\" isLoading={isLoading} disabled={isLoading}>\r\n Đăng nhập\r\n </Button>\r\n\r\n <div className=\"relative\">\r\n <div className=\"absolute inset-0 flex items-center\">\r\n <span className=\"w-full border-t border-border\" />\r\n </div>\r\n <div className=\"relative flex justify-center text-xs uppercase\">\r\n <span className=\"bg-background px-2 text-muted-foreground\">Hoặc tiếp tục với</span>\r\n </div>\r\n </div>\r\n\r\n <div className=\"grid grid-cols-2 gap-3\">\r\n <Button variant=\"outline\" type=\"button\" className=\"w-full\">Google</Button>\r\n <Button variant=\"outline\" type=\"button\" className=\"w-full\">GitHub</Button>\r\n </div>\r\n </form>\r\n </Form>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n );\r\n};\r\n\r\nexport default LoginFullScreen;\r\n"
480
+ }
481
+ ]
482
+ },
483
+ "login-glass": {
484
+ "name": "login-glass",
485
+ "dependencies": [
486
+ "react-hook-form",
487
+ "@hookform/resolvers",
488
+ "zod",
489
+ "lucide-react",
490
+ "sonner"
491
+ ],
492
+ "internalDependencies": [
493
+ "form",
494
+ "input",
495
+ "button",
496
+ "checkbox"
497
+ ],
498
+ "files": [
499
+ {
500
+ "path": "src/components/ui/login-glass/LoginGlass.tsx",
501
+ "content": "import * as React from 'react';\r\nimport { useForm } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\nimport { Mail, Lock, Sparkles } from 'lucide-react';\r\nimport { Form, FormField } from '@/components/ui/form/Form';\r\nimport { Input } from '@/components/ui/input/Input';\r\nimport { Button } from '@/components/ui/button/Button';\r\nimport { Checkbox } from '@/components/ui/checkbox/Checkbox';\r\nimport { toast } from 'sonner';\r\n\r\nconst loginSchema = z.object({\r\n email: z.string().email('Email không hợp lệ'),\r\n password: z.string().min(6, 'Mật khẩu ít nhất 6 ký tự'),\r\n rememberMe: z.boolean().optional(),\r\n});\r\n\r\ntype LoginValues = z.infer<typeof loginSchema>;\r\n\r\nconst glassInputClass =\r\n 'bg-white/10 border-white/20 text-white placeholder:text-white/40 focus:border-violet-400';\r\n\r\nexport const LoginGlass = () => {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n\r\n const form = useForm<LoginValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: { email: '', password: '', rememberMe: false },\r\n });\r\n\r\n const onSubmit = async (_data: LoginValues) => {\r\n setIsLoading(true);\r\n await new Promise<void>((resolve) => setTimeout(resolve, 1500));\r\n setIsLoading(false);\r\n toast.success('Đăng nhập thành công!');\r\n };\r\n\r\n return (\r\n <div className=\"relative w-full max-w-sm overflow-hidden rounded-2xl p-px bg-gradient-to-br from-violet-500/30 via-indigo-500/20 to-transparent\">\r\n {/* Gradient backdrop */}\r\n <div className=\"absolute inset-0 -z-10 rounded-2xl bg-gradient-to-br from-violet-900 via-indigo-900 to-slate-900\" />\r\n <div className=\"absolute inset-0 -z-10 bg-[radial-gradient(ellipse_at_top_right,_rgba(139,92,246,0.3),transparent_60%)]\" />\r\n\r\n <div className=\"relative rounded-2xl bg-white/5 backdrop-blur-xl border border-white/10 p-8 space-y-6 animate-in fade-in zoom-in-95 duration-500\">\r\n {/* Header */}\r\n <div className=\"text-center\">\r\n <div className=\"mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-white/10 border border-white/20\">\r\n <Sparkles className=\"h-7 w-7 text-violet-300\" />\r\n </div>\r\n <h2 className=\"text-2xl font-bold text-white\">Đăng nhập</h2>\r\n <p className=\"text-white/50 mt-1 text-sm\">Truy cập vào tài khoản của bạn</p>\r\n </div>\r\n\r\n {/* Override label & icon colours for dark background */}\r\n <div className=\"[&_label]:!text-white/70 [&_.text-muted-foreground]:!text-white/40\">\r\n <Form {...form}>\r\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\r\n <FormField\r\n control={form.control}\r\n name=\"email\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Email\"\r\n type=\"email\"\r\n placeholder=\"email@example.com\"\r\n icon={<Mail className=\"w-4 h-4 text-white/50\" />}\r\n error={fieldState.error?.message}\r\n className={glassInputClass}\r\n {...field}\r\n />\r\n )}\r\n />\r\n <FormField\r\n control={form.control}\r\n name=\"password\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Mật khẩu\"\r\n type=\"password\"\r\n placeholder=\"••••••••\"\r\n icon={<Lock className=\"w-4 h-4 text-white/50\" />}\r\n error={fieldState.error?.message}\r\n className={glassInputClass}\r\n {...field}\r\n />\r\n )}\r\n />\r\n\r\n <div className=\"flex items-center justify-between\">\r\n <FormField\r\n control={form.control}\r\n name=\"rememberMe\"\r\n render={({ field }) => (\r\n <div className=\"[&_label]:!text-white/60\">\r\n <Checkbox\r\n label=\"Ghi nhớ đăng nhập\"\r\n checked={field.value}\r\n onCheckedChange={(checked) => field.onChange(checked === true)}\r\n />\r\n </div>\r\n )}\r\n />\r\n <Button\r\n variant=\"link\"\r\n type=\"button\"\r\n className=\"text-violet-300 hover:text-violet-100 px-0\"\r\n >\r\n Quên mật khẩu?\r\n </Button>\r\n </div>\r\n\r\n <Button\r\n type=\"submit\"\r\n variant=\"glass\"\r\n className=\"w-full\"\r\n isLoading={isLoading}\r\n disabled={isLoading}\r\n >\r\n Đăng nhập\r\n </Button>\r\n </form>\r\n </Form>\r\n </div>\r\n\r\n <p className=\"text-center text-sm text-white/40\">\r\n Chưa có tài khoản?{' '}\r\n <Button\r\n variant=\"link\"\r\n type=\"button\"\r\n className=\"text-violet-300 px-0 hover:text-violet-100\"\r\n >\r\n Đăng ký ngay\r\n </Button>\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n};\r\n"
502
+ }
503
+ ]
504
+ },
505
+ "login-minimal": {
506
+ "name": "login-minimal",
507
+ "dependencies": [
508
+ "react-hook-form",
509
+ "@hookform/resolvers",
510
+ "zod",
511
+ "lucide-react",
512
+ "sonner"
513
+ ],
514
+ "internalDependencies": [
515
+ "form",
516
+ "input",
517
+ "button"
518
+ ],
519
+ "files": [
520
+ {
521
+ "path": "src/components/ui/login-minimal/LoginMinimal.tsx",
522
+ "content": "import * as React from 'react';\r\nimport { useForm } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\nimport { Mail, Lock, ArrowRight } from 'lucide-react';\r\nimport { Form, FormField } from '@/components/ui/form/Form';\r\nimport { Input } from '@/components/ui/input/Input';\r\nimport { Button } from '@/components/ui/button/Button';\r\nimport { toast } from 'sonner';\r\n\r\nconst loginSchema = z.object({\r\n email: z.string().email('Email không hợp lệ'),\r\n password: z.string().min(6, 'Mật khẩu ít nhất 6 ký tự'),\r\n});\r\n\r\ntype LoginValues = z.infer<typeof loginSchema>;\r\n\r\nexport const LoginMinimal = () => {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n\r\n const form = useForm<LoginValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: { email: '', password: '' },\r\n });\r\n\r\n const onSubmit = async (_data: LoginValues) => {\r\n setIsLoading(true);\r\n await new Promise<void>((resolve) => setTimeout(resolve, 1500));\r\n setIsLoading(false);\r\n toast.success('Đăng nhập thành công!');\r\n };\r\n\r\n return (\r\n <div className=\"w-full max-w-sm space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500\">\r\n <div>\r\n <p className=\"text-sm font-medium text-primary uppercase tracking-widest mb-1\">\r\n Chào mừng\r\n </p>\r\n <h2 className=\"text-3xl font-bold tracking-tight text-foreground\">\r\n Đăng nhập\r\n </h2>\r\n <p className=\"mt-1 text-muted-foreground text-sm\">\r\n Nhập thông tin để tiếp tục hành trình\r\n </p>\r\n </div>\r\n\r\n <Form {...form}>\r\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\r\n <FormField\r\n control={form.control}\r\n name=\"email\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Email\"\r\n type=\"email\"\r\n placeholder=\"email@example.com\"\r\n variant=\"flushed\"\r\n icon={<Mail className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n <FormField\r\n control={form.control}\r\n name=\"password\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Mật khẩu\"\r\n type=\"password\"\r\n placeholder=\"••••••••\"\r\n variant=\"flushed\"\r\n icon={<Lock className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n\r\n <div className=\"flex justify-end -mt-2\">\r\n <Button variant=\"link\" type=\"button\" className=\"text-xs px-0\">\r\n Quên mật khẩu?\r\n </Button>\r\n </div>\r\n\r\n <Button\r\n type=\"submit\"\r\n className=\"w-full\"\r\n isLoading={isLoading}\r\n disabled={isLoading}\r\n rightIcon={!isLoading ? <ArrowRight className=\"w-4 h-4\" /> : undefined}\r\n >\r\n Tiếp tục\r\n </Button>\r\n </form>\r\n </Form>\r\n\r\n <div className=\"flex items-center gap-3 text-sm text-muted-foreground\">\r\n <span className=\"h-px flex-1 bg-border\" />\r\n <span>hoặc</span>\r\n <span className=\"h-px flex-1 bg-border\" />\r\n </div>\r\n\r\n <p className=\"text-center text-sm text-muted-foreground\">\r\n Chưa có tài khoản?{' '}\r\n <Button variant=\"link\" type=\"button\" className=\"px-0 font-semibold\">\r\n Đăng ký ngay\r\n </Button>\r\n </p>\r\n </div>\r\n );\r\n};\r\n"
523
+ }
524
+ ]
525
+ },
526
+ "login-split": {
527
+ "name": "login-split",
528
+ "dependencies": [
529
+ "react-hook-form",
530
+ "@hookform/resolvers",
531
+ "zod",
532
+ "lucide-react",
533
+ "sonner"
534
+ ],
535
+ "internalDependencies": [
536
+ "form",
537
+ "input",
538
+ "button",
539
+ "checkbox"
540
+ ],
541
+ "files": [
542
+ {
543
+ "path": "src/components/ui/login-split/LoginSplit.tsx",
544
+ "content": "import * as React from 'react';\r\nimport { useForm } from 'react-hook-form';\r\nimport { zodResolver } from '@hookform/resolvers/zod';\r\nimport * as z from 'zod';\r\nimport { Mail, Lock, BadgeCheck } from 'lucide-react';\r\nimport { Form, FormField } from '@/components/ui/form/Form';\r\nimport { Input } from '@/components/ui/input/Input';\r\nimport { Button } from '@/components/ui/button/Button';\r\nimport { Checkbox } from '@/components/ui/checkbox/Checkbox';\r\nimport { toast } from 'sonner';\r\n\r\nconst loginSchema = z.object({\r\n email: z.string().email('Email không hợp lệ'),\r\n password: z.string().min(6, 'Mật khẩu ít nhất 6 ký tự'),\r\n rememberMe: z.boolean().optional(),\r\n});\r\n\r\ntype LoginValues = z.infer<typeof loginSchema>;\r\n\r\nconst features = [\r\n 'Bảo mật 2 lớp (2FA)',\r\n 'Đồng bộ đa thiết bị',\r\n 'Dashboard phân tích thời gian thực',\r\n 'Hỗ trợ 24/7',\r\n];\r\n\r\nexport const LoginSplit = () => {\r\n const [isLoading, setIsLoading] = React.useState(false);\r\n\r\n const form = useForm<LoginValues>({\r\n resolver: zodResolver(loginSchema),\r\n defaultValues: { email: '', password: '', rememberMe: false },\r\n });\r\n\r\n const onSubmit = async (_data: LoginValues) => {\r\n setIsLoading(true);\r\n await new Promise<void>((resolve) => setTimeout(resolve, 1500));\r\n setIsLoading(false);\r\n toast.success('Đăng nhập thành công!');\r\n };\r\n\r\n return (\r\n <div className=\"w-full overflow-hidden rounded-2xl border border-border shadow-lg animate-in fade-in zoom-in-95 duration-500\">\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 min-h-[480px]\">\r\n {/* Left: Branding */}\r\n <div className=\"relative hidden md:flex flex-col justify-between bg-gradient-to-br from-primary via-primary/90 to-primary/70 p-10 text-primary-foreground overflow-hidden\">\r\n <div className=\"absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_left,rgba(255,255,255,0.1),transparent_60%)]\" />\r\n\r\n <div className=\"relative flex items-center gap-2 font-bold text-xl\">\r\n <BadgeCheck className=\"h-6 w-6\" />\r\n BasuiCN\r\n </div>\r\n\r\n <div className=\"relative space-y-6\">\r\n <h2 className=\"text-3xl font-bold leading-snug\">\r\n Nền tảng quản lý<br />thế hệ mới\r\n </h2>\r\n <ul className=\"space-y-3\">\r\n {features.map((feature) => (\r\n <li key={feature} className=\"flex items-center gap-2 text-primary-foreground/80 text-sm\">\r\n <BadgeCheck className=\"h-4 w-4 shrink-0 text-primary-foreground/60\" />\r\n {feature}\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n\r\n <p className=\"relative text-primary-foreground/40 text-xs\">\r\n © 2026 BasuiCN. All rights reserved.\r\n </p>\r\n </div>\r\n\r\n {/* Right: Form */}\r\n <div className=\"bg-card flex flex-col justify-center p-10\">\r\n <div className=\"mb-8\">\r\n <h2 className=\"text-2xl font-bold text-foreground\">Chào mừng trở lại</h2>\r\n <p className=\"text-muted-foreground mt-1 text-sm\">\r\n Đăng nhập để tiếp tục sử dụng dịch vụ\r\n </p>\r\n </div>\r\n\r\n <Form {...form}>\r\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\r\n <FormField\r\n control={form.control}\r\n name=\"email\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Email\"\r\n type=\"email\"\r\n placeholder=\"email@example.com\"\r\n variant=\"filled\"\r\n icon={<Mail className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n <FormField\r\n control={form.control}\r\n name=\"password\"\r\n render={({ field, fieldState }) => (\r\n <Input\r\n label=\"Mật khẩu\"\r\n type=\"password\"\r\n placeholder=\"••••••••\"\r\n variant=\"filled\"\r\n icon={<Lock className=\"w-4 h-4\" />}\r\n error={fieldState.error?.message}\r\n {...field}\r\n />\r\n )}\r\n />\r\n\r\n <div className=\"flex items-center justify-between\">\r\n <FormField\r\n control={form.control}\r\n name=\"rememberMe\"\r\n render={({ field }) => (\r\n <Checkbox\r\n label=\"Ghi nhớ đăng nhập\"\r\n checked={field.value}\r\n onCheckedChange={(checked) => field.onChange(checked === true)}\r\n />\r\n )}\r\n />\r\n <Button variant=\"link\" type=\"button\">\r\n Quên mật khẩu?\r\n </Button>\r\n </div>\r\n\r\n <Button\r\n type=\"submit\"\r\n className=\"w-full\"\r\n isLoading={isLoading}\r\n disabled={isLoading}\r\n >\r\n Đăng nhập\r\n </Button>\r\n </form>\r\n </Form>\r\n\r\n <p className=\"mt-6 text-center text-sm text-muted-foreground\">\r\n Chưa có tài khoản?{' '}\r\n <Button variant=\"link\" type=\"button\" className=\"px-0 font-semibold\">\r\n Đăng ký ngay\r\n </Button>\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n"
545
+ }
546
+ ]
547
+ },
438
548
  "menu-bar": {
439
549
  "name": "menu-bar",
440
550
  "dependencies": [
package/scripts/ui-cli.ts CHANGED
@@ -6,7 +6,7 @@ import readline from 'readline';
6
6
 
7
7
  // ─── Constants ────────────────────────────────────────────────────────────────
8
8
 
9
- const VERSION = '0.2.8';
9
+ const VERSION = '0.2.9';
10
10
  const REGISTRY_LOCAL = './registry.json';
11
11
  const REGISTRY_REMOTE = 'https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json';
12
12