gladvn 0.2.33 → 0.2.35

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.
@@ -0,0 +1,77 @@
1
+ import { BlockViewer } from "../components/BlockViewer";
2
+ import codeString from "../../blocks/dashboard.tsx?raw";
3
+ import { InfoIcon } from "lucide-react";
4
+
5
+ export default function DashboardBlockShowcase() {
6
+ return (
7
+ <div className="space-y-8">
8
+ <BlockViewer
9
+ blockId="dashboard"
10
+ title="Dashboard"
11
+ description="A full-featured dashboard layout with a responsive sidebar, header, and data visualizations."
12
+ codeString={codeString}
13
+ />
14
+
15
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
16
+ <div className="rounded-2xl border border-info/30 bg-info/5 p-6 md:p-8 space-y-4 relative overflow-hidden">
17
+ <div className="absolute top-0 right-0 p-8 opacity-5 pointer-events-none">
18
+ <InfoIcon className="w-32 h-32 text-info" />
19
+ </div>
20
+ <h3 className="text-xl font-bold text-foreground flex items-center gap-2 relative z-10">
21
+ <InfoIcon className="size-5 text-info" />
22
+ Triết lý lắp ghép (Composition)
23
+ </h3>
24
+ <div className="text-muted-foreground leading-relaxed space-y-4 relative z-10">
25
+ <p>
26
+ Nhìn vào mã nguồn của Dashboard này, bạn sẽ thấy sự xuất hiện dày đặc của cả <strong>Micro</strong> (Avatar, Button, DropdownMenu...) và <strong>Macro</strong> (DatePicker).
27
+ </p>
28
+ <p>
29
+ gladvn khuyến khích bạn <strong>tự do kết hợp</strong> cả hai tầng kiến trúc:
30
+ </p>
31
+ <ul className="list-disc list-inside space-y-2 ml-2">
32
+ <li><strong>Dùng Macro</strong> khi bạn cần một khối chức năng hoàn chỉnh, hoạt động ngay lập tức (như DatePicker).</li>
33
+ <li><strong>Dùng Micro</strong> khi bạn cần tuỳ chỉnh sâu bố cục, hoặc xây dựng các phần tử đặc thù (như Sidebar, Header).</li>
34
+ </ul>
35
+ <p>
36
+ Không có ranh giới cứng nhắc. Hãy dùng kết hợp để tối ưu tốc độ phát triển mà không đánh mất sự linh hoạt!
37
+ </p>
38
+ </div>
39
+ </div>
40
+
41
+ <div className="rounded-2xl border border-warning/30 bg-warning/5 p-6 md:p-8 space-y-4 relative overflow-hidden">
42
+ <div className="absolute top-0 right-0 p-8 opacity-5 pointer-events-none">
43
+ <InfoIcon className="w-32 h-32 text-warning" />
44
+ </div>
45
+ <h3 className="text-xl font-bold text-foreground flex items-center gap-2 relative z-10">
46
+ <InfoIcon className="size-5 text-warning" />
47
+ Cạm bẫy "Trừu tượng hoá sớm"
48
+ </h3>
49
+ <div className="text-muted-foreground leading-relaxed space-y-4 relative z-10">
50
+ <p>
51
+ Nhiều người khi nhìn vào mã nguồn file này sẽ thắc mắc: <em>"Tại sao không tách các thẻ Card kia thành một component nhỏ hơn (ví dụ DashboardCard) cho code ngắn lại?"</em>
52
+ </p>
53
+ <p>
54
+ Đừng rơi vào bẫy <strong>Premature Abstraction (Trừu tượng hoá quá sớm)</strong>. Hãy tự hỏi:
55
+ </p>
56
+ <ul className="list-disc list-inside space-y-2 ml-2 font-medium text-foreground/80">
57
+ <li>Các Card này có thực sự được tái sử dụng ở trang khác không?</li>
58
+ <li>Tách ra có làm mất đi ngữ cảnh (context) khi đọc code không?</li>
59
+ <li>Người tiếp theo vào maintain có thể nhìn một phát hiểu ngay overview trang web, hay phải nhảy qua lại giữa 10 file nhỏ xíu chỉ để biết một cái Card chứa gì?</li>
60
+ </ul>
61
+ <p>
62
+ Viết code dài trong một file không xấu, nếu nó <strong>minh bạch và dễ theo dõi (transparent)</strong>. Chỉ tách component khi bạn thực sự cần tái sử dụng nó ở nhiều nơi.
63
+ </p>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div className="text-center pb-8 pt-4">
69
+ <p className="text-sm text-muted-foreground italic max-w-2xl mx-auto">
70
+ *P/S: Tại sao những điều này lại được viết ở Dashboard block mà không phải nơi khác?
71
+ Thực ra thì chúng áp dụng cho <strong>tất cả mọi Block</strong>, tôi chỉ tình cờ chọn Dashboard làm chỗ để tâm sự mỏng thôi.
72
+ Đời mà, đâu cần cái gì cũng phải phân chia hoàn hảo, thong thả đi! ☕️
73
+ </p>
74
+ </div>
75
+ </div>
76
+ );
77
+ }
@@ -133,7 +133,7 @@ export default function OverviewSection() {
133
133
  Tinh tế. Đẳng cấp.
134
134
  </span>
135
135
  <span className="block mt-2 bg-gradient-to-r leading-[1.2] from-primary via-info to-primary bg-[length:200%_auto] bg-clip-text text-transparent animate-[spin_8s_linear_infinite] [animation-name:gradient] [animation-duration:8s] [animation-timing-function:linear] [animation-iteration-count:infinite]">
136
- Của bạn.
136
+ Không giới hạn.
137
137
  </span>
138
138
  </h1>
139
139
 
@@ -151,6 +151,13 @@ export default function OverviewSection() {
151
151
  Đọc được, sửa được, xóa được.
152
152
  </strong>
153
153
  </p>
154
+
155
+ <div
156
+ className="max-w-2xl text-lg md:text-xl font-medium text-foreground/90 italic mt-2 p-5 border-l-4 border-primary bg-primary/5 rounded-r-xl shadow-sm animate-fade-up"
157
+ style={{ animationDelay: "300ms", animationFillMode: "both" }}
158
+ >
159
+ "Thư viện này sinh ra để giúp bạn code nhanh hơn, sống nhàn hơn, đừng tự làm khó mình."
160
+ </div>
154
161
  </div>
155
162
 
156
163
  {/* 3D Floating Showcase Dashboard */}
@@ -308,13 +315,12 @@ export default function OverviewSection() {
308
315
  <section className="container max-w-6xl mx-auto space-y-12 px-4">
309
316
  <div className="text-center max-w-2xl mx-auto space-y-4">
310
317
  <h2 className="text-3xl md:text-4xl font-extrabold tracking-tight">
311
- Kiến trúc ràng. Sửa không lo vỡ.
318
+ Kiến trúc rành mạch. Sửa code không "đổ mồ hôi".
312
319
  </h2>
313
320
  <p className="text-muted-foreground text-lg">
314
- Nhiều dự án bắt đầu gãy năm thứ hai — muốn sửa mà không dám,
315
- không biết sẽ vỡ theo. gladvn chia code thành{" "}
316
- <strong className="text-foreground">các tầng ràng</strong>
317
- {", "}nên bạn luôn biết mình đang sửa ở đâu và ảnh hưởng đến đâu.
321
+ Nhiều dự án thường bắt đầu "bốc mùi" sau một thời gian đụng vào đâu cũng sợ gãy, muốn sửa mà rén. gladvn chia code thành{" "}
322
+ <strong className="text-foreground">các tầng rạch ròi</strong>
323
+ {", "}giúp bạn luôn biết chính xác mình đang sửa cái gì, ảnh hưởng tới đâu. Tâm an, code mới nhàn!
318
324
  </p>
319
325
  </div>
320
326
 
@@ -413,7 +419,7 @@ export default function OverviewSection() {
413
419
  <p className="text-sm font-bold uppercase tracking-wider text-primary mb-5">
414
420
  Tuỳ chỉnh theo từng tầng kiến trúc
415
421
  </p>
416
- <div className="grid sm:grid-cols-3 gap-4">
422
+ <div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
417
423
  {/* MACRO — RECOMMENDED */}
418
424
  <div className="p-5 rounded-2xl border-2 border-primary/40 bg-primary/5 space-y-3 relative">
419
425
  <div className="absolute top-3 right-3 text-[10px] font-bold uppercase tracking-wider text-primary bg-primary/10 px-2 py-0.5 rounded-full">
@@ -423,16 +429,14 @@ export default function OverviewSection() {
423
429
  src/components/macro/
424
430
  </div>
425
431
  <h4 className="font-bold text-foreground text-base">
426
- Macro — Sửa an toàn
432
+ Macro — Khu vực an toàn
427
433
  </h4>
428
434
  <p className="text-sm text-muted-foreground leading-relaxed">
429
- Bắt đầu đây. Mỗi Macro là một preset độc lập — thêm footer
430
- cho DatePicker, bỏ nút đóng của Dialog, chỉnh placeholder
431
- Combobox.{" "}
435
+ Muốn tuỳ chỉnh? Bắt đầu từ đây nhé. Mỗi Macro là một khối độc lập — thêm footer cho DatePicker, giấu nút đóng của Dialog, v.v.{" "}
432
436
  <strong className="text-foreground">
433
- Sửa cái nào chỉ ảnh hưởng cái đó
437
+ Cứ thoải mái vọc vạch
434
438
  </strong>
435
- , không lan sang đâu cả.
439
+ , sửa cái nào thì chỉ ảnh hưởng cái đó thôi, chả lo "cháy nhà" hàng xóm.
436
440
  </p>
437
441
  </div>
438
442
 
@@ -445,15 +449,14 @@ export default function OverviewSection() {
445
449
  src/components/micro/
446
450
  </div>
447
451
  <h4 className="font-bold text-foreground text-base">
448
- Micro — Sửa cẩn thận
452
+ Micro — Lãnh địa cốt lõi
449
453
  </h4>
450
454
  <p className="text-sm text-muted-foreground leading-relaxed">
451
- Micro nền mọi Macro đều xây trên nó. Sửa variant của
452
- Button hay animation của Tooltip thì{" "}
455
+ Mọi thứ đều xây từ đây. Sửa cái padding của Button hay animation của Tooltip thì{" "}
453
456
  <strong className="text-foreground">
454
- tất cả nơi dùng component đó đều thay đổi theo
457
+ nguyên dàn app sẽ ăn theo
455
458
  </strong>
456
- . Chỉ sửa khi bạn mình đang làm gì.
459
+ . Cân nhắc kỹ chút trước khi phím nhé, "sai một ly đi một dặm" đấy!
457
460
  </p>
458
461
  </div>
459
462
 
@@ -466,15 +469,33 @@ export default function OverviewSection() {
466
469
  src/index.css
467
470
  </div>
468
471
  <h4 className="font-bold text-foreground text-base">
469
- CSS Token — Đổi một chỗ, cả app thay đổi
472
+ CSS Token — Nút bấm hạt nhân
470
473
  </h4>
471
474
  <p className="text-sm text-muted-foreground leading-relaxed">
472
- CSS token quyết định màu, font, border-radius cho tất cả. Đổi
473
- một token ở đây thì{" "}
475
+ Nơi nắm trùm về màu sắc, font, độ bo góc. Đổi một biến ở đây là{" "}
474
476
  <strong className="text-foreground">
475
- toàn bộ giao diện cập nhật ngay
477
+ giao diện lột xác toàn tập
476
478
  </strong>
477
- . Dùng khi muốn rebrand, không phải để vá lỗi lẻ tẻ.
479
+ . Rất đã khi cần thay áo mới (rebrand), nhưng tuyệt đối đừng dùng để vá lỗi vặt.
480
+ </p>
481
+ </div>
482
+
483
+ {/* BLOCKS — FLEXIBLE COMPOSITION */}
484
+ <div className="p-5 rounded-2xl border border-info/40 bg-info/5 space-y-3 relative">
485
+ <div className="absolute top-3 right-3 text-[10px] font-bold uppercase tracking-wider text-info bg-info/10 px-2 py-0.5 rounded-full">
486
+ Lắp ghép linh hoạt
487
+ </div>
488
+ <div className="font-mono text-xs text-info/70">
489
+ src/blocks/
490
+ </div>
491
+ <h4 className="font-bold text-foreground text-base">
492
+ Blocks — Sân chơi tự do
493
+ </h4>
494
+ <p className="text-sm text-muted-foreground leading-relaxed">
495
+ Chẳng có quy tắc nào ép bạn phải dùng 100% Micro hay Macro.
496
+ Layout quen thuộc thì <strong className="text-foreground">lấy Macro ra xài cho lẹ</strong>.
497
+ Gặp thiết kế dị biệt thì <strong className="text-foreground">mở Micro ra tự xếp hình</strong>.
498
+ Linh hoạt lên, code sao cho bản thân thấy sướng là được!
478
499
  </p>
479
500
  </div>
480
501
  </div>
@@ -489,13 +510,12 @@ export default function OverviewSection() {
489
510
  </div>
490
511
  <div className="relative z-10 space-y-4 mb-10">
491
512
  <h3 className="text-2xl md:text-4xl font-extrabold tracking-tight">
492
- Micro & Macro
513
+ Luật chơi Micro/Macro
493
514
  </h3>
494
515
  <p className="text-muted-foreground max-w-lg text-lg leading-relaxed">
495
- Micro là component thuần — chỉ lo style, không ép layout. Macro
496
- là preset — lắp ghép Micro thành giao diện hoàn chỉnh.
516
+ Micro chỉ những mảnh lego thuần túy đẹp nhưng vô tri. Macro mới là người chỉ huy sắp xếp các mảnh lego thành giao diện hoàn chỉnh.
497
517
  <strong className="text-foreground block mt-2">
498
- Micro không bao giờ tự quyết layout. Việc đó thuộc về Macro.
518
+ Micro không bao giờ được phép tự tiện lo chuyện layout (margin, width). Việc đó của Macro!
499
519
  </strong>
500
520
  </p>
501
521
  </div>
@@ -990,6 +1010,7 @@ export default function OverviewSection() {
990
1010
  <div className="relative z-10 mt-10">
991
1011
  <Button
992
1012
  render={<a href="/dialog" />}
1013
+ nativeButton={false}
993
1014
  variant="outline"
994
1015
  color="warning"
995
1016
  className="gap-2 font-bold px-6 py-5 rounded-xl border-warning/30 bg-warning/10 hover:bg-warning/20 shadow-sm"
@@ -1087,6 +1108,7 @@ export default function OverviewSection() {
1087
1108
  rel="noreferrer"
1088
1109
  />
1089
1110
  }
1111
+ nativeButton={false}
1090
1112
  >
1091
1113
  Đóng góp cùng chúng tôi
1092
1114
  </Button>
@@ -1099,14 +1121,14 @@ export default function OverviewSection() {
1099
1121
  <div className="absolute inset-0 bg-[radial-gradient(ellipse_60%_60%_at_50%_0%,rgba(var(--primary-rgb),0.15),transparent)] pointer-events-none" />
1100
1122
  <div className="relative z-10 space-y-6 max-w-2xl mx-auto">
1101
1123
  <h2 className="text-3xl md:text-5xl font-extrabold tracking-tight">
1102
- 30 giây. Xong.
1124
+ 30 giây là có đồ chơi.
1103
1125
  </h2>
1104
1126
  <p className="text-muted-foreground text-lg leading-relaxed">
1105
- Toàn bộ code nằm trong{" "}
1127
+ Quẳng hết code vào{" "}
1106
1128
  <code className="text-sm bg-primary/20 text-primary px-1.5 py-0.5 rounded">
1107
1129
  src/
1108
1130
  </code>{" "}
1109
- của bạn. Không config rườm rà. Chạy dùng.
1131
+ rồi xài thôi. Không config rườm nhức đầu làm gì.
1110
1132
  </p>
1111
1133
  <div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-2">
1112
1134
  <button
@@ -1131,6 +1153,7 @@ export default function OverviewSection() {
1131
1153
  rel="noreferrer"
1132
1154
  />
1133
1155
  }
1156
+ nativeButton={false}
1134
1157
  variant="ghost"
1135
1158
  className="text-muted-foreground hover:text-foreground"
1136
1159
  >
@@ -0,0 +1,13 @@
1
+ import { BlockViewer } from "../components/BlockViewer";
2
+ import codeString from "../../blocks/settings.tsx?raw";
3
+
4
+ export default function SettingsBlockShowcase() {
5
+ return (
6
+ <BlockViewer
7
+ blockId="settings"
8
+ title="Settings Layout"
9
+ description="A vertical tabs-based settings page with various input controls and danger zones."
10
+ codeString={codeString}
11
+ />
12
+ );
13
+ }
@@ -1,104 +0,0 @@
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
- import { Separator } from "../components/micro/separator";
9
-
10
- export default function AuthCardBlock() {
11
- const [isLoading, setIsLoading] = useState(false);
12
- const [mode, setMode] = useState<"login" | "register">("login");
13
-
14
- const handleSubmit = (e: React.FormEvent) => {
15
- e.preventDefault();
16
- setIsLoading(true);
17
- setTimeout(() => setIsLoading(false), 1000);
18
- };
19
-
20
- return (
21
- <div className="flex min-h-screen w-full items-center justify-center p-4 bg-muted/20">
22
- <Card className="w-full max-w-sm">
23
- <CardHeader className="text-center space-y-2">
24
- <CardTitle className="text-2xl font-bold">
25
- {mode === "login" ? "Welcome back" : "Create an account"}
26
- </CardTitle>
27
- <CardDescription>
28
- {mode === "login"
29
- ? "Enter your email below to login to your account"
30
- : "Enter your details below to create your account"}
31
- </CardDescription>
32
- </CardHeader>
33
- <CardContent>
34
- <form onSubmit={handleSubmit} className="space-y-4">
35
- {mode === "register" && (
36
- <div className="space-y-2">
37
- <Label htmlFor="name">Full Name</Label>
38
- <Input id="name" placeholder="John Doe" required />
39
- </div>
40
- )}
41
- <div className="space-y-2">
42
- <Label htmlFor="email">Email</Label>
43
- <Input id="email" type="email" placeholder="m@example.com" required />
44
- </div>
45
- <div className="space-y-2">
46
- <div className="flex items-center justify-between">
47
- <Label htmlFor="password">Password</Label>
48
- {mode === "login" && (
49
- <Button variant="link" color="primary" className="h-auto p-0 text-xs">
50
- Forgot password?
51
- </Button>
52
- )}
53
- </div>
54
- <Input id="password" type="password" required />
55
- </div>
56
-
57
- {mode === "register" && (
58
- <div className="flex items-center gap-2">
59
- <Checkbox id="terms" required />
60
- <Label htmlFor="terms" className="text-sm font-normal text-muted-foreground">
61
- I agree to the <a href="#" className="text-primary hover:underline">terms and conditions</a>
62
- </Label>
63
- </div>
64
- )}
65
-
66
- <Button type="submit" className="w-full" disabled={isLoading}>
67
- {isLoading ? "Loading..." : mode === "login" ? "Sign In" : "Create Account"}
68
- </Button>
69
- </form>
70
-
71
- <div className="my-6 flex items-center gap-4">
72
- <Separator className="flex-1" />
73
- <span className="text-xs text-muted-foreground">OR</span>
74
- <Separator className="flex-1" />
75
- </div>
76
-
77
- <Button variant="outline" className="w-full">
78
- <svg viewBox="0 0 24 24" className="mr-2 size-4" 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
- Continue with Google
85
- </Button>
86
-
87
- <div className="mt-4 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"
95
- onClick={() => setMode(mode === "login" ? "register" : "login")}
96
- >
97
- {mode === "login" ? "Sign up" : "Sign in"}
98
- </Button>
99
- </div>
100
- </CardContent>
101
- </Card>
102
- </div>
103
- );
104
- }
@@ -1,116 +0,0 @@
1
- import * as React from "react";
2
- import { useState } from "react";
3
- import { Button } from "../components/micro/button";
4
- import { Checkbox } from "../components/micro/checkbox";
5
- import {
6
- Dialog,
7
- DialogContent,
8
- DialogDescription,
9
- DialogHeader,
10
- DialogTitle,
11
- DialogTrigger,
12
- } from "../components/micro/dialog";
13
- import { Input } from "../components/micro/input";
14
- import { Label } from "../components/micro/label";
15
- import { Separator } from "../components/micro/separator";
16
-
17
- export default function AuthDialogBlock() {
18
- const [isLoading, setIsLoading] = useState(false);
19
- const [mode, setMode] = useState<"login" | "register">("login");
20
- const [isOpen, setIsOpen] = useState(false);
21
-
22
- const handleSubmit = (e: React.FormEvent) => {
23
- e.preventDefault();
24
- setIsLoading(true);
25
- setTimeout(() => {
26
- setIsLoading(false);
27
- setIsOpen(false);
28
- }, 1000);
29
- };
30
-
31
- return (
32
- <div className="flex min-h-[50vh] w-full items-center justify-center p-4 bg-muted/10 border border-dashed rounded-lg">
33
- <Dialog open={isOpen} onOpenChange={setIsOpen}>
34
- <DialogTrigger render={<Button>Open Authentication</Button>} />
35
- <DialogContent className="sm:max-w-md">
36
- <DialogHeader className="text-center space-y-2">
37
- <DialogTitle className="text-2xl font-bold">
38
- {mode === "login" ? "Welcome back" : "Create an account"}
39
- </DialogTitle>
40
- <DialogDescription>
41
- {mode === "login"
42
- ? "Enter your email below to login to your account"
43
- : "Enter your details below to create your account"}
44
- </DialogDescription>
45
- </DialogHeader>
46
-
47
- <form onSubmit={handleSubmit} className="space-y-4 py-4">
48
- {mode === "register" && (
49
- <div className="space-y-2">
50
- <Label htmlFor="dialog-name">Full Name</Label>
51
- <Input id="dialog-name" placeholder="John Doe" required />
52
- </div>
53
- )}
54
- <div className="space-y-2">
55
- <Label htmlFor="dialog-email">Email</Label>
56
- <Input id="dialog-email" type="email" placeholder="m@example.com" required />
57
- </div>
58
- <div className="space-y-2">
59
- <div className="flex items-center justify-between">
60
- <Label htmlFor="dialog-password">Password</Label>
61
- {mode === "login" && (
62
- <Button type="button" variant="link" color="primary" className="h-auto p-0 text-xs">
63
- Forgot password?
64
- </Button>
65
- )}
66
- </div>
67
- <Input id="dialog-password" type="password" required />
68
- </div>
69
-
70
- {mode === "register" && (
71
- <div className="flex items-center gap-2">
72
- <Checkbox id="dialog-terms" required />
73
- <Label htmlFor="dialog-terms" className="text-sm font-normal text-muted-foreground">
74
- I agree to the <a href="#" className="text-primary hover:underline">terms</a>
75
- </Label>
76
- </div>
77
- )}
78
-
79
- <Button type="submit" className="w-full" disabled={isLoading}>
80
- {isLoading ? "Loading..." : mode === "login" ? "Sign In" : "Create Account"}
81
- </Button>
82
- </form>
83
-
84
- <div className="flex items-center gap-4">
85
- <Separator className="flex-1" />
86
- <span className="text-xs text-muted-foreground uppercase">Or</span>
87
- <Separator className="flex-1" />
88
- </div>
89
-
90
- <Button type="button" variant="outline" className="w-full mt-4">
91
- <svg viewBox="0 0 24 24" className="mr-2 size-4" fill="currentColor">
92
- <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" />
93
- <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" />
94
- <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" />
95
- <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" />
96
- </svg>
97
- Google
98
- </Button>
99
-
100
- <p className="mt-4 text-center text-sm text-muted-foreground">
101
- {mode === "login" ? "Don't have an account? " : "Already have an account? "}
102
- <Button
103
- type="button"
104
- variant="link"
105
- color="primary"
106
- className="h-auto p-0 font-semibold"
107
- onClick={() => setMode(mode === "login" ? "register" : "login")}
108
- >
109
- {mode === "login" ? "Sign up" : "Sign in"}
110
- </Button>
111
- </p>
112
- </DialogContent>
113
- </Dialog>
114
- </div>
115
- );
116
- }