mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 20:27:44 +00:00
feat: sign-in box
This commit is contained in:
@@ -26,10 +26,6 @@
|
||||
[](https://github.com/better-auth/better-auth/stargazers)
|
||||
</p>
|
||||
|
||||
|
||||
> [!WARNING]
|
||||
> 🚧 This project is in beta and not yet recommended for production use. For more details, check out our [roadmap](https://github.com/orgs/better-auth/projects/2) to the v1 stable release.
|
||||
|
||||
## About the Project
|
||||
|
||||
Better Auth is framework-agnostic authentication (and authorization) library for TypeScript. It provides a comprehensive set of features out of the box and includes a plugin ecosystem that simplifies adding advanced functionalities with minimal code in short amount of time. Whether you need 2FA, multi-tenant support, or other complex features. It lets you focus on building your actual application instead of reinventing the wheel.
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
"use client";
|
||||
import Image from "next/image";
|
||||
import React, { useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const Card = React.memo(
|
||||
({
|
||||
card,
|
||||
index,
|
||||
hovered,
|
||||
setHovered,
|
||||
}: {
|
||||
card: any;
|
||||
index: number;
|
||||
hovered: number | null;
|
||||
setHovered: React.Dispatch<React.SetStateAction<number | null>>;
|
||||
}) => (
|
||||
<div
|
||||
onMouseEnter={() => setHovered(index)}
|
||||
onMouseLeave={() => setHovered(null)}
|
||||
className={cn(
|
||||
"rounded-lg relative bg-gray-100 dark:bg-neutral-900 overflow-hidden h-60 md:h-96 w-full transition-all duration-300 ease-out",
|
||||
hovered !== null && hovered !== index && "blur-sm scale-[0.98]",
|
||||
)}
|
||||
>
|
||||
{card.children}
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0 bg-black/50 flex items-end py-8 px-4 transition-opacity duration-300",
|
||||
hovered === index ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
>
|
||||
<div className="text-xl md:text-2xl font-medium bg-clip-text text-transparent bg-gradient-to-b from-neutral-50 to-neutral-200">
|
||||
{card.title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
Card.displayName = "Card";
|
||||
|
||||
type Card = {
|
||||
title: string;
|
||||
children: string;
|
||||
};
|
||||
|
||||
export function FocusCards({ cards }: { cards: Card[] }) {
|
||||
const [hovered, setHovered] = useState<number | null>(null);
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 max-w-5xl mx-auto md:px-8 w-full">
|
||||
{cards.map((card, index) => (
|
||||
<Card
|
||||
key={card.title}
|
||||
card={card}
|
||||
index={index}
|
||||
hovered={hovered}
|
||||
setHovered={setHovered}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { Highlight, HighlightProps, themes } from "prism-react-renderer";
|
||||
import theme from "./theme";
|
||||
|
||||
interface CodeEditorProps {
|
||||
code: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export function CodeEditor({ code, language }: CodeEditorProps) {
|
||||
return (
|
||||
<Highlight theme={theme} code={code} language={language}>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre
|
||||
className={className}
|
||||
style={{
|
||||
...style,
|
||||
padding: "1rem",
|
||||
overflow: "auto",
|
||||
maxHeight: "400px",
|
||||
}}
|
||||
>
|
||||
{tokens.map((line, i) => (
|
||||
<div key={i} {...getLineProps({ line, key: i })}>
|
||||
<span className="mr-4 text-gray-500">{i + 1}</span>
|
||||
{line.map((token, key) => (
|
||||
<span
|
||||
key={key}
|
||||
{...getTokenProps({ token, key, className: "text-sm" })}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
interface TabProps {
|
||||
fileName: string;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function CodeTab({ fileName, isActive, onClick, onClose }: TabProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center px-3 py-2 text-sm font-medium border-t-2 cursor-pointer",
|
||||
isActive
|
||||
? "bg-background text-foreground border-t-foreground"
|
||||
: "bg-muted text-muted-foreground border-t-transparent hover:bg-background/50",
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="truncate max-w-[100px]">{fileName}</span>
|
||||
<button
|
||||
className="ml-2 text-muted-foreground hover:text-foreground"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import { TabBar } from "./tab-bar";
|
||||
import { CodeEditor } from "./code-editor";
|
||||
|
||||
const initialFiles = [
|
||||
{
|
||||
id: "1",
|
||||
name: "index.ts",
|
||||
content: `import express from 'express';
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello, TypeScript!');
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(\`Server running at http://localhost:\${port}\`);
|
||||
});`,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "utils.ts",
|
||||
content: `export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export function multiply(a: number, b: number): number {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
export function capitalize(str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}`,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "types.ts",
|
||||
content: `export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export type Status = 'active' | 'inactive' | 'pending';
|
||||
|
||||
export enum Role {
|
||||
Admin = 'ADMIN',
|
||||
User = 'USER',
|
||||
Guest = 'GUEST'
|
||||
}`,
|
||||
},
|
||||
];
|
||||
|
||||
export default function CodeTabs() {
|
||||
const [files, setFiles] = useState(initialFiles);
|
||||
const [activeFileId, setActiveFileId] = useState(files[0].id);
|
||||
|
||||
const handleTabClick = (fileId: string) => {
|
||||
setActiveFileId(fileId);
|
||||
};
|
||||
|
||||
const handleTabClose = (fileId: string) => {
|
||||
setFiles(files.filter((file) => file.id !== fileId));
|
||||
if (activeFileId === fileId) {
|
||||
setActiveFileId(files[0].id);
|
||||
}
|
||||
};
|
||||
|
||||
const activeFile = files.find((file) => file.id === activeFileId);
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-3xl mx-auto mt-8 border border-border rounded-md overflow-hidden">
|
||||
<TabBar
|
||||
files={files}
|
||||
activeFileId={activeFileId}
|
||||
onTabClick={handleTabClick}
|
||||
onTabClose={handleTabClose}
|
||||
/>
|
||||
<div className="bg-background">
|
||||
{activeFile && (
|
||||
<CodeEditor code={activeFile.content} language="typescript" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,665 +0,0 @@
|
||||
import { ChevronLeft, Copy, Mail, PlusIcon } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
import SignIn from "./sign-in";
|
||||
import { SignUp } from "./sign-up";
|
||||
import { AuthTabs } from "./tabs";
|
||||
import { Label } from "../ui/label";
|
||||
import { Switch } from "../ui/switch";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { useState } from "react";
|
||||
import CodeTabs from "./code-tabs";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
title: "Next.js",
|
||||
description: "The React Framework for Production",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 15 15"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M0 7.5a7.5 7.5 0 1 1 11.698 6.216L4.906 4.21A.5.5 0 0 0 4 4.5V12h1V6.06l5.83 8.162A7.5 7.5 0 0 1 0 7.5M10 10V4h1v6z"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Nuxt",
|
||||
description: "The Intuitive Vue Framework",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<g fill="none">
|
||||
<rect width="256" height="256" fill="#242938" rx="60"></rect>
|
||||
<path
|
||||
fill="#00DC82"
|
||||
d="M138.787 189.333h68.772c2.184.001 4.33-.569 6.222-1.652a12.4 12.4 0 0 0 4.554-4.515a12.24 12.24 0 0 0-.006-12.332l-46.185-79.286a12.4 12.4 0 0 0-4.553-4.514a12.53 12.53 0 0 0-12.442 0a12.4 12.4 0 0 0-4.553 4.514l-11.809 20.287l-23.09-39.67a12.4 12.4 0 0 0-4.555-4.513a12.54 12.54 0 0 0-12.444 0a12.4 12.4 0 0 0-4.555 4.513L36.67 170.834a12.24 12.24 0 0 0-.005 12.332a12.4 12.4 0 0 0 4.554 4.515a12.5 12.5 0 0 0 6.222 1.652h43.17c17.104 0 29.718-7.446 38.397-21.973l21.072-36.169l11.287-19.356l33.873 58.142h-45.16zm-48.88-19.376l-30.127-.007l45.16-77.518l22.533 38.759l-15.087 25.906c-5.764 9.426-12.312 12.86-22.48 12.86"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Svelte Kit",
|
||||
description: "Web development for the rest of us",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<g fill="none">
|
||||
<rect width="256" height="256" fill="#FF3E00" rx="60"></rect>
|
||||
<g clipPath="url(#skillIconsSvelte0)">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M193.034 61.797c-16.627-23.95-49.729-30.966-73.525-15.865L77.559 72.78c-11.44 7.17-19.372 18.915-21.66 32.186c-1.984 11.136-.306 22.576 5.033 32.492c-3.66 5.491-6.102 11.593-7.17 18c-2.44 13.576.764 27.61 8.696 38.745c16.78 23.95 49.728 30.966 73.525 15.865l41.949-26.695c11.441-7.17 19.373-18.915 21.661-32.187c1.983-11.135.305-22.576-5.034-32.491c3.661-5.492 6.102-11.593 7.17-18c2.593-13.729-.61-27.763-8.695-38.898"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF3E00"
|
||||
d="M115.39 196.491a33.25 33.25 0 0 1-35.695-13.271c-4.881-6.712-6.712-15.101-5.34-23.339c.306-1.373.611-2.593.916-3.966l.763-2.44L78.169 155a55.6 55.6 0 0 0 16.475 8.237l1.525.458l-.152 1.525c-.153 2.136.458 4.424 1.678 6.255c2.441 3.508 6.712 5.186 10.83 4.118c.916-.305 1.831-.61 2.594-1.068l41.796-26.695c2.136-1.372 3.509-3.355 3.966-5.796s-.152-5.034-1.525-7.017c-2.441-3.509-6.712-5.034-10.831-3.966c-.915.305-1.83.61-2.593 1.068l-16.017 10.22c-2.593 1.678-5.491 2.898-8.542 3.661a33.25 33.25 0 0 1-35.695-13.271c-4.729-6.712-6.712-15.102-5.186-23.339c1.372-7.932 6.254-15.102 13.118-19.373l41.949-26.695c2.593-1.678 5.492-2.898 8.543-3.814a33.25 33.25 0 0 1 35.695 13.272c4.881 6.712 6.711 15.101 5.339 23.339c-.306 1.373-.611 2.593-1.068 3.966l-.763 2.44l-2.136-1.525a55.6 55.6 0 0 0-16.474-8.237l-1.526-.458l.153-1.525c.153-2.136-.458-4.424-1.678-6.255c-2.441-3.508-6.712-5.034-10.83-3.966c-.916.305-1.831.61-2.594 1.068l-41.796 26.695c-2.136 1.373-3.509 3.356-3.966 5.797s.152 5.034 1.525 7.017c2.441 3.508 6.712 5.033 10.831 3.966c.915-.305 1.83-.611 2.593-1.068l16.017-10.22c2.593-1.678 5.491-2.899 8.542-3.814a33.25 33.25 0 0 1 35.695 13.271c4.881 6.712 6.712 15.102 5.339 23.339c-1.373 7.932-6.254 15.102-13.119 19.373l-41.949 26.695c-2.593 1.678-5.491 2.898-8.542 3.813"
|
||||
></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="skillIconsSvelte0">
|
||||
<path fill="#fff" d="M53 38h149.644v180H53z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Solid Start",
|
||||
description: "Fine-grained reactivity goes fullstack",
|
||||
Icon: () => (
|
||||
<svg
|
||||
data-hk="00000010210"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 500 500"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
role="presentation"
|
||||
>
|
||||
<path
|
||||
d="M233.205 430.856L304.742 425.279C304.742 425.279 329.208 421.295 343.569 397.659L293.041 385.443L233.205 430.856Z"
|
||||
fill="url(#paint0_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M134.278 263.278C113.003 264.341 73.6443 268.059 73.6443 268.059L245.173 392.614L284.265 402.44L343.569 397.925L170.977 273.105C170.977 273.105 157.148 263.278 137.203 263.278C136.139 263.278 135.342 263.278 134.278 263.278Z"
|
||||
fill="url(#paint1_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M355.536 238.58L429.2 234.065C429.2 234.065 454.464 230.348 468.825 206.977L416.435 193.964L355.536 238.58Z"
|
||||
fill="url(#paint2_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M251.289 68.6128C229.217 69.4095 188.795 72.5964 188.795 72.5964L367.503 200.072L407.926 210.429L469.09 206.712L289.318 78.9702C289.318 78.9702 274.426 68.6128 253.417 68.6128C252.885 68.6128 252.087 68.6128 251.289 68.6128Z"
|
||||
fill="url(#paint3_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M31.0946 295.679C30.8287 295.945 30.8287 296.21 30.8287 296.475L77.8993 330.469L202.623 420.764C228.95 439.62 264.586 431.653 282.67 402.44L187.465 333.921L110.077 277.62C100.504 270.715 89.8663 267.528 79.2289 267.528C60.6134 267.528 42.2639 277.354 31.0946 295.679Z"
|
||||
fill="url(#paint4_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M147.043 99.9505C147.043 100.216 146.776 100.482 146.511 100.747L195.442 135.538L244.374 170.062L325.751 227.957C353.142 247.345 389.841 239.642 407.925 210.695L358.461 175.374L308.997 140.318L228.153 82.6881C218.047 75.5177 206.611 72.0652 195.442 72.0652C176.561 72.3308 158.212 81.8915 147.043 99.9505Z"
|
||||
fill="url(#paint5_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M112.471 139.255L175.497 208.305C178.423 212.289 181.614 216.006 185.337 219.193L308.199 354.105L369.364 350.387C387.448 321.439 380.002 282.135 352.611 262.748L271.234 204.852L222.568 170.328L173.636 135.538L112.471 139.255Z"
|
||||
fill="url(#paint6_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M111.939 140.052C94.1213 168.734 101.567 207.509 128.427 226.629L209.005 283.994L258.735 319.049L308.199 354.105C326.283 325.158 318.836 285.852 291.445 266.465L112.471 139.255C112.471 139.521 112.204 139.787 111.939 140.052Z"
|
||||
fill="url(#paint7_linear_1_2)"
|
||||
></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1_2"
|
||||
x1="359.728"
|
||||
y1="56.8062"
|
||||
x2="265.623"
|
||||
y2="521.28"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#1593F5"></stop>
|
||||
<stop offset="1" stop-color="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_1_2"
|
||||
x1="350.496"
|
||||
y1="559.872"
|
||||
x2="-44.0802"
|
||||
y2="-73.2062"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#1593F5"></stop>
|
||||
<stop offset="1" stop-color="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_1_2"
|
||||
x1="610.25"
|
||||
y1="570.526"
|
||||
x2="372.635"
|
||||
y2="144.034"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="white"></stop>
|
||||
<stop offset="1" stop-color="#15ABFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_1_2"
|
||||
x1="188.808"
|
||||
y1="-180.608"
|
||||
x2="390.515"
|
||||
y2="281.703"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="white"></stop>
|
||||
<stop offset="1" stop-color="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_1_2"
|
||||
x1="415.84"
|
||||
y1="-4.74684"
|
||||
x2="95.1922"
|
||||
y2="439.83"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#0057E5"></stop>
|
||||
<stop offset="1" stop-color="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_1_2"
|
||||
x1="343.141"
|
||||
y1="-21.5427"
|
||||
x2="242.301"
|
||||
y2="256.708"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="white"></stop>
|
||||
<stop offset="1" stop-color="#15ABFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint6_linear_1_2"
|
||||
x1="469.095"
|
||||
y1="533.421"
|
||||
x2="-37.6939"
|
||||
y2="-135.731"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="white"></stop>
|
||||
<stop offset="1" stop-color="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint7_linear_1_2"
|
||||
x1="380.676"
|
||||
y1="-89.0869"
|
||||
x2="120.669"
|
||||
y2="424.902"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="white"></stop>
|
||||
<stop offset="1" stop-color="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function Builder() {
|
||||
const socialProviders = {
|
||||
apple: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17.05 20.28c-.98.95-2.05.8-3.08.35c-1.09-.46-2.09-.48-3.24 0c-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8c1.18-.24 2.31-.93 3.57-.84c1.51.12 2.65.72 3.4 1.8c-3.12 1.87-2.38 5.98.48 7.13c-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25c.29 2.58-2.34 4.5-3.74 4.25"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
dropbox: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path d="m12.594 23.258l-.012.002l-.071.035l-.02.004l-.014-.004l-.071-.036q-.016-.004-.024.006l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.016-.018m.264-.113l-.014.002l-.184.093l-.01.01l-.003.011l.018.43l.005.012l.008.008l.201.092q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.003-.011l.018-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m11.998 13.503l2.879 1.662c.426.246.923.34 1.365.34c.443 0 .94-.094 1.367-.34l.587-.34V17a1 1 0 0 1-.5.866l-5.196 3a1 1 0 0 1-1 0l-5.196-3a1 1 0 0 1-.5-.866v-2.172l.583.337c.426.246.923.34 1.366.34c.442 0 .939-.094 1.366-.34zM6.887 3.5c.434-.251 1.115-.274 1.594-.068l.138.068l3.379 1.95l3.379-1.95c.434-.251 1.115-.274 1.594-.068l.138.068l4.242 2.45c.447.257.476.664.09.942l-.09.057l-3.378 1.95l3.378 1.95c.447.258.476.665.09.943l-.09.057l-4.242 2.45c-.435.25-1.116.273-1.595.068l-.137-.068l-3.38-1.951l-3.378 1.95c-.435.252-1.116.274-1.595.07l-.137-.07l-4.243-2.449c-.447-.257-.476-.665-.09-.942l.09-.058L6.022 8.9L2.644 6.95c-.447-.257-.476-.665-.09-.942l.09-.058zm5.546 2.702c-.205-.119-.52-.136-.755-.051l-.111.05l-4.243 2.45c-.212.122-.236.313-.07.45l.07.05l4.243 2.449c.205.118.52.135.755.05l.111-.05l4.243-2.45c.212-.122.236-.312.07-.45l-.07-.05z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
discord: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.1.1 0 0 0-.07.03c-.18.33-.39.76-.53 1.09a16.1 16.1 0 0 0-4.8 0c-.14-.34-.35-.76-.54-1.09c-.01-.02-.04-.03-.07-.03c-1.5.26-2.93.71-4.27 1.33c-.01 0-.02.01-.03.02c-2.72 4.07-3.47 8.03-3.1 11.95c0 .02.01.04.03.05c1.8 1.32 3.53 2.12 5.24 2.65c.03.01.06 0 .07-.02c.4-.55.76-1.13 1.07-1.74c.02-.04 0-.08-.04-.09c-.57-.22-1.11-.48-1.64-.78c-.04-.02-.04-.08-.01-.11c.11-.08.22-.17.33-.25c.02-.02.05-.02.07-.01c3.44 1.57 7.15 1.57 10.55 0c.02-.01.05-.01.07.01c.11.09.22.17.33.26c.04.03.04.09-.01.11c-.52.31-1.07.56-1.64.78c-.04.01-.05.06-.04.09c.32.61.68 1.19 1.07 1.74c.03.01.06.02.09.01c1.72-.53 3.45-1.33 5.25-2.65c.02-.01.03-.03.03-.05c.44-4.53-.73-8.46-3.1-11.95c-.01-.01-.02-.02-.04-.02M8.52 14.91c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.84 2.12-1.89 2.12m6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.83 2.12-1.89 2.12"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
facebook: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 3H4a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h8.615v-6.96h-2.338v-2.725h2.338v-2c0-2.325 1.42-3.592 3.5-3.592c.699-.002 1.399.034 2.095.107v2.42h-1.435c-1.128 0-1.348.538-1.348 1.325v1.735h2.697l-.35 2.725h-2.348V21H20a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
github: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
gitlab: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m22.749 9.769l-.031-.08l-3.027-7.9a.79.79 0 0 0-.782-.495a.8.8 0 0 0-.456.17a.8.8 0 0 0-.268.408L16.14 8.125H7.865L5.822 1.872a.8.8 0 0 0-.269-.409a.81.81 0 0 0-.926-.05c-.14.09-.25.22-.312.376L1.283 9.684l-.03.08a5.62 5.62 0 0 0 1.864 6.496l.01.008l.028.02l4.61 3.453l2.282 1.726l1.39 1.049a.935.935 0 0 0 1.13 0l1.389-1.05l2.281-1.726l4.639-3.473l.011-.01A5.62 5.62 0 0 0 22.75 9.77"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
google: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.99 13.9v-3.72h9.36c.14.63.25 1.22.25 2.05c0 5.71-3.83 9.77-9.6 9.77c-5.52 0-10-4.48-10-10S6.48 2 12 2c2.7 0 4.96.99 6.69 2.61l-2.84 2.76c-.72-.68-1.98-1.48-3.85-1.48c-3.31 0-6.01 2.75-6.01 6.12s2.7 6.12 6.01 6.12c3.83 0 5.24-2.65 5.5-4.22h-5.51z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
linkedin: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93zM6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
microsoft: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
twitch: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.64 5.93h1.43v4.28h-1.43m3.93-4.28H17v4.28h-1.43M7 2L3.43 5.57v12.86h4.28V22l3.58-3.57h2.85L20.57 12V2m-1.43 9.29l-2.85 2.85h-2.86l-2.5 2.5v-2.5H7.71V3.43h11.43Z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
spotify: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.001 2c-5.5 0-10 4.5-10 10s4.5 10 10 10s10-4.5 10-10s-4.45-10-10-10m3.75 14.65c-2.35-1.45-5.3-1.75-8.8-.95c-.35.1-.65-.15-.75-.45c-.1-.35.15-.65.45-.75c3.8-.85 7.1-.5 9.7 1.1c.35.15.4.55.25.85c-.2.3-.55.4-.85.2m1-2.7c-2.7-1.65-6.8-2.15-9.95-1.15c-.4.1-.85-.1-.95-.5s.1-.85.5-.95c3.65-1.1 8.15-.55 11.25 1.35c.3.15.45.65.2 1s-.7.5-1.05.25M6.3 9.75c-.5.15-1-.15-1.15-.6c-.15-.5.15-1 .6-1.15c3.55-1.05 9.4-.85 13.1 1.35c.45.25.6.85.35 1.3c-.25.35-.85.5-1.3.25C14.7 9 9.35 8.8 6.3 9.75"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
x: {
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="0.88em"
|
||||
height="1em"
|
||||
viewBox="0 0 448 512"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h320c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64zm297.1 84L257.3 234.6L379.4 396h-95.6L209 298.1L123.3 396H75.8l111-126.9L69.7 116h98l67.7 89.5l78.2-89.5zm-37.8 251.6L153.4 142.9h-28.3l171.8 224.7h26.3z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button className="bg-stone-950 no-underline group cursor-pointer relative shadow-2xl shadow-zinc-900 rounded-sm p-px text-xs font-semibold leading-6 text-white inline-block">
|
||||
<span className="absolute inset-0 overflow-hidden rounded-sm">
|
||||
<span className="absolute inset-0 rounded-sm bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100"></span>
|
||||
</span>
|
||||
<div className="relative flex space-x-2 items-center z-10 rounded-none bg-zinc-950 py-2 px-4 ring-1 ring-white/10 ">
|
||||
<PlusIcon size={14} />
|
||||
<span>Create Sign in Box</span>
|
||||
</div>
|
||||
<span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-stone-800/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40"></span>
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-7xl h-5/6 overflow-scroll">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Sign in Box</DialogTitle>
|
||||
<DialogDescription>
|
||||
Configure the sign in box to your liking and copy the code to your
|
||||
application
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex gap-4 md:gap-12 flex-col md:flex-row items-center md:items-start">
|
||||
<div className="w-4/12">
|
||||
<AuthTabs
|
||||
tabs={[
|
||||
{
|
||||
title: "Sign In",
|
||||
value: "sign-in",
|
||||
content: <SignIn />,
|
||||
},
|
||||
{
|
||||
title: "Sign Up",
|
||||
value: "sign-up",
|
||||
content: <SignUp />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-grow w-5/12 h-[530px]">
|
||||
{currentStep === 0 ? (
|
||||
<Card className="rounded-none flex-grow mt-10 h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Configuration</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="max-h-[400px] overflow-scroll">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<Label>Email & Password</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Enabled</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Remember Me</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Forget Password</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<div>
|
||||
<Label>Social Providers</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
{Object.entries(socialProviders).map(
|
||||
([provider, { Icon }]) => (
|
||||
<div
|
||||
className="flex items-center justify-between"
|
||||
key={provider}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon />
|
||||
<Label>
|
||||
{provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1)}
|
||||
</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<div>
|
||||
<Label>Plugins</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 20q-.825 0-1.412-.587T3 18v-.8q0-.85.438-1.562T4.6 14.55q1.55-.775 3.15-1.162T11 13q.35 0 .7.013t.7.062q.275.025.437.213t.163.462q.05 1.175.575 2.213t1.4 1.762q.175.125.275.313t.1.412V19q0 .425-.288.713T14.35 20zm6-8q-1.65 0-2.825-1.175T7 8t1.175-2.825T11 4t2.825 1.175T15 8t-1.175 2.825T11 12m7.5 2q.425 0 .713-.288T19.5 13t-.288-.712T18.5 12t-.712.288T17.5 13t.288.713t.712.287m.15 8.65l-1-1q-.05-.05-.15-.35v-4.45q-1.1-.325-1.8-1.237T15 13.5q0-1.45 1.025-2.475T18.5 10t2.475 1.025T22 13.5q0 1.125-.638 2t-1.612 1.25l.9.9q.15.15.15.35t-.15.35l-.8.8q-.15.15-.15.35t.15.35l.8.8q.15.15.15.35t-.15.35l-1.3 1.3q-.15.15-.35.15t-.35-.15"
|
||||
></path>
|
||||
</svg>
|
||||
<Label>Passkey</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none">
|
||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17.5 3a4.5 4.5 0 0 1 4.495 4.288L22 7.5V15a2 2 0 0 1-1.85 1.995L20 17h-3v3a1 1 0 0 1-1.993.117L15 20v-3H4a2 2 0 0 1-1.995-1.85L2 15V7.5a4.5 4.5 0 0 1 4.288-4.495L6.5 3zm-11 2A2.5 2.5 0 0 0 4 7.5V15h5V7.5A2.5 2.5 0 0 0 6.5 5M7 8a1 1 0 0 1 .117 1.993L7 10H6a1 1 0 0 1-.117-1.993L6 8z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
<Label>Magic Link</Label>
|
||||
</div>
|
||||
<Switch />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<button
|
||||
className="bg-stone-950 no-underline group cursor-pointer relative shadow-2xl shadow-zinc-900 rounded-sm p-px text-xs font-semibold leading-6 text-white inline-block w-full"
|
||||
onClick={() => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}}
|
||||
>
|
||||
<span className="absolute inset-0 overflow-hidden rounded-sm">
|
||||
<span className="absolute inset-0 rounded-sm bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100"></span>
|
||||
</span>
|
||||
<div className="relative flex space-x-2 items-center z-10 rounded-none bg-zinc-950 py-2 px-4 ring-1 ring-white/10 justify-center">
|
||||
<span>Continue</span>
|
||||
</div>
|
||||
<span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-stone-800/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40"></span>
|
||||
</button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
) : currentStep === 1 ? (
|
||||
<Card className="rounded-none flex-grow mt-10 h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Choose Framework</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex items-start gap-2 flex-wrap justify-between">
|
||||
{frameworks.map((fm) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (fm.title === "Next.js") {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-4 border p-6 rounded-md w-5/12 flex-grow h-44 relative",
|
||||
fm.title !== "Next.js"
|
||||
? "opacity-55"
|
||||
: "hover:ring-1 transition-all ring-border hover:bg-background duration-200 ease-in-out cursor-pointer",
|
||||
)}
|
||||
key={fm.title}
|
||||
>
|
||||
{fm.title !== "Next.js" && (
|
||||
<span className="absolute top-4 right-4 text-xs">
|
||||
Coming Soon
|
||||
</span>
|
||||
)}
|
||||
<fm.Icon />
|
||||
<Label className="text-2xl">{fm.title}</Label>
|
||||
<p className="text-sm">{fm.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardFooter className="flex items-center justify-between">
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrentStep(0);
|
||||
}}
|
||||
>
|
||||
<div className="relative flex space-x-2 items-center z-10 bg-zinc-900 py-2 px-4 border border-r-0">
|
||||
<ChevronLeft size={16} />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
className="bg-stone-950 no-underline group cursor-pointer relative shadow-2xl shadow-zinc-900 p-px text-xs font-semibold leading-6 text-white inline-block w-full"
|
||||
onClick={() => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}}
|
||||
>
|
||||
<span className="absolute inset-0 overflow-hidden">
|
||||
<span className="absolute inset-0 rounded-sm bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100"></span>
|
||||
</span>
|
||||
<div className="relative flex space-x-2 items-center z-10 rounded-none bg-zinc-950 py-2 px-4 ring-1 ring-white/10 justify-center">
|
||||
<span>Continue</span>
|
||||
</div>
|
||||
<span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-stone-800/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40"></span>
|
||||
</button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="rounded-none flex-grow mt-10 h-full overflow-scroll">
|
||||
<CardHeader>
|
||||
<CardTitle>Code</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div>
|
||||
<p>
|
||||
To use the sign in box in your application, copy the code
|
||||
below and paste it in your application.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CodeTabs />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Separator } from "../ui/separator";
|
||||
|
||||
export default function SignIn() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
return (
|
||||
<Card className="z-50 rounded-none max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign In</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your email below to login to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Link
|
||||
href="/forget-password"
|
||||
className="ml-auto inline-block text-sm underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="password"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
onClick={() => {
|
||||
setRememberMe(!rememberMe);
|
||||
}}
|
||||
/>
|
||||
<Label>Remember me</Label>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {}}
|
||||
>
|
||||
{loading ? <Loader2 size={16} className="animate-spin" /> : "Login"}
|
||||
</Button>
|
||||
<Button variant="outline" className="gap-2" onClick={async () => {}}>
|
||||
<Key size={16} />
|
||||
Sign-in with Passkey
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center gap-4 justify-between w-full">
|
||||
<Separator className="w-4/12" />
|
||||
<p className="text-xs">Continue with</p>
|
||||
<Separator className="w-4/12" />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<GitHubLogoIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<DiscordLogoIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className=" gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="0.98em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 262"
|
||||
>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
|
||||
/>
|
||||
<path
|
||||
fill="#EB4335"
|
||||
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.64 5.93h1.43v4.28h-1.43m3.93-4.28H17v4.28h-1.43M7 2L3.43 5.57v12.86h4.28V22l3.58-3.57h2.85L20.57 12V2m-1.43 9.29l-2.85 2.85h-2.86l-2.5 2.5v-2.5H7.71V3.43h11.43Z"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.3em"
|
||||
height="1.3em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M22 12c0-5.52-4.48-10-10-10S2 6.48 2 12c0 4.84 3.44 8.87 8 9.8V15H8v-3h2V9.5C10 7.57 11.57 6 13.5 6H16v3h-2c-.55 0-1 .45-1 1v2h3v3h-3v6.95c5.05-.5 9-4.76 9-9.95"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 14 14"
|
||||
>
|
||||
<g fill="none">
|
||||
<g clipPath="url(#primeTwitter0)">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.025.656h2.147L8.482 6.03L14 13.344H9.68L6.294 8.909l-3.87 4.435H.275l5.016-5.75L0 .657h4.43L7.486 4.71zm-.755 11.4h1.19L3.78 1.877H2.504z"
|
||||
></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="primeTwitter0">
|
||||
<path fill="#fff" d="M0 0h14v14H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Loader2, X } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function SignUp() {
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setImage(file);
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="z-50 rounded-md rounded-t-none max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign Up</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your information to create an account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="first-name">First name</Label>
|
||||
<Input
|
||||
id="first-name"
|
||||
placeholder="Max"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setFirstName(e.target.value);
|
||||
}}
|
||||
value={firstName}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="last-name">Last name</Label>
|
||||
<Input
|
||||
id="last-name"
|
||||
placeholder="Robinson"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setLastName(e.target.value);
|
||||
}}
|
||||
value={lastName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
{/* <PasswordInput
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Password"
|
||||
/> */}
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Confirm Password</Label>
|
||||
{/* <PasswordInput
|
||||
id="password_confirmation"
|
||||
value={passwordConfirmation}
|
||||
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Confirm Password"
|
||||
/> */}
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="image">Profile Image (optional)</Label>
|
||||
<div className="flex items-end gap-4">
|
||||
{imagePreview && (
|
||||
<div className="relative w-16 h-16 rounded-sm overflow-hidden">
|
||||
<Image
|
||||
src={imagePreview}
|
||||
alt="Profile preview"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<Input
|
||||
id="image"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="w-full"
|
||||
/>
|
||||
{imagePreview && (
|
||||
<X
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setImage(null);
|
||||
setImagePreview(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
// await signUp.email({
|
||||
// email,
|
||||
// password,
|
||||
// name: `${firstName} ${lastName}`,
|
||||
// image: image ? await convertImageToBase64(image) : "",
|
||||
// callbackURL: "/dashboard",
|
||||
// fetchOptions: {
|
||||
// onResponse: () => {
|
||||
// setLoading(false);
|
||||
// },
|
||||
// onRequest: () => {
|
||||
// setLoading(true);
|
||||
// },
|
||||
// onError: (ctx) => {
|
||||
// toast.error(ctx.error.message);
|
||||
// },
|
||||
// onSuccess: async () => {
|
||||
// router.push("/dashboard");
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
) : (
|
||||
"Create an account"
|
||||
)}
|
||||
</Button>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full gap-2"
|
||||
onClick={async () => {
|
||||
// await signIn.social({
|
||||
// provider: "github",
|
||||
// callbackURL: "/dashboard",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<GitHubLogoIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full gap-2"
|
||||
onClick={async () => {
|
||||
// await signIn.social({
|
||||
// provider: "discord",
|
||||
// callbackURL: "/dashboard",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<DiscordLogoIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full gap-2"
|
||||
onClick={async () => {
|
||||
// await signIn.social({
|
||||
// provider: "google",
|
||||
// callbackURL: "/dashboard",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="0.98em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 262"
|
||||
>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
|
||||
/>
|
||||
<path
|
||||
fill="#EB4335"
|
||||
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full gap-2"
|
||||
onClick={async () => {
|
||||
// await signIn.social({
|
||||
// provider: "microsoft",
|
||||
// callbackURL: "/dashboard",
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-center w-full border-t py-4">
|
||||
<p className="text-center text-xs text-neutral-500">
|
||||
Secured by <span className="text-orange-400">better-auth.</span>
|
||||
</p>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
async function convertImageToBase64(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result as string);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
141
docs/components/builder/beam.tsx
Normal file
141
docs/components/builder/beam.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const BackgroundBeams = React.memo(
|
||||
({ className }: { className?: string }) => {
|
||||
const paths = [
|
||||
"M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875",
|
||||
"M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867",
|
||||
"M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859",
|
||||
"M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851",
|
||||
"M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843",
|
||||
"M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835",
|
||||
"M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827",
|
||||
"M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819",
|
||||
"M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811",
|
||||
"M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803",
|
||||
"M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795",
|
||||
"M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787",
|
||||
"M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779",
|
||||
"M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771",
|
||||
"M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763",
|
||||
"M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755",
|
||||
"M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747",
|
||||
"M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739",
|
||||
"M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731",
|
||||
"M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723",
|
||||
"M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715",
|
||||
"M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707",
|
||||
"M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699",
|
||||
"M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691",
|
||||
"M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683",
|
||||
"M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675",
|
||||
"M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667",
|
||||
"M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659",
|
||||
"M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651",
|
||||
"M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643",
|
||||
"M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635",
|
||||
"M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627",
|
||||
"M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619",
|
||||
"M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611",
|
||||
"M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603",
|
||||
"M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595",
|
||||
"M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587",
|
||||
"M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579",
|
||||
"M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571",
|
||||
"M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563",
|
||||
"M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555",
|
||||
"M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547",
|
||||
"M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539",
|
||||
"M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531",
|
||||
"M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523",
|
||||
"M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515",
|
||||
"M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507",
|
||||
"M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499",
|
||||
"M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491",
|
||||
"M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483",
|
||||
];
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute h-full w-full inset-0 [mask-size:40px] [mask-repeat:no-repeat] flex items-center justify-center",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
className=" z-0 h-full w-full pointer-events-none absolute "
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 696 316"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483M-30 -589C-30 -589 38 -184 502 -57C966 70 1034 475 1034 475M-23 -597C-23 -597 45 -192 509 -65C973 62 1041 467 1041 467M-16 -605C-16 -605 52 -200 516 -73C980 54 1048 459 1048 459M-9 -613C-9 -613 59 -208 523 -81C987 46 1055 451 1055 451M-2 -621C-2 -621 66 -216 530 -89C994 38 1062 443 1062 443M5 -629C5 -629 73 -224 537 -97C1001 30 1069 435 1069 435M12 -637C12 -637 80 -232 544 -105C1008 22 1076 427 1076 427M19 -645C19 -645 87 -240 551 -113C1015 14 1083 419 1083 419"
|
||||
stroke="url(#paint0_radial_242_278)"
|
||||
strokeOpacity="0.05"
|
||||
strokeWidth="0.5"
|
||||
></path>
|
||||
|
||||
{paths.map((path, index) => (
|
||||
<motion.path
|
||||
key={`path-` + index}
|
||||
d={path}
|
||||
stroke={`url(#linearGradient-${index})`}
|
||||
strokeOpacity="0.4"
|
||||
strokeWidth="0.5"
|
||||
></motion.path>
|
||||
))}
|
||||
<defs>
|
||||
{paths.map((path, index) => (
|
||||
<motion.linearGradient
|
||||
id={`linearGradient-${index}`}
|
||||
key={`gradient-${index}`}
|
||||
initial={{
|
||||
x1: "0%",
|
||||
x2: "0%",
|
||||
y1: "0%",
|
||||
y2: "0%",
|
||||
}}
|
||||
animate={{
|
||||
x1: ["0%", "100%"],
|
||||
x2: ["0%", "95%"],
|
||||
y1: ["0%", "100%"],
|
||||
y2: ["0%", `${93 + Math.random() * 8}%`],
|
||||
}}
|
||||
transition={{
|
||||
duration: Math.random() * 10 + 10,
|
||||
ease: "easeInOut",
|
||||
repeat: Infinity,
|
||||
delay: Math.random() * 10,
|
||||
}}
|
||||
>
|
||||
<stop stopColor="#18CCFC" stopOpacity="0"></stop>
|
||||
<stop stopColor="#18CCFC"></stop>
|
||||
<stop offset="32.5%" stopColor="#6344F5"></stop>
|
||||
<stop offset="100%" stopColor="#AE48FF" stopOpacity="0"></stop>
|
||||
</motion.linearGradient>
|
||||
))}
|
||||
|
||||
<radialGradient
|
||||
id="paint0_radial_242_278"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(352 34) rotate(90) scale(555 1560.62)"
|
||||
>
|
||||
<stop offset="0.0666667" stopColor="var(--neutral-300)"></stop>
|
||||
<stop offset="0.243243" stopColor="var(--neutral-300)"></stop>
|
||||
<stop offset="0.43594" stopColor="white" stopOpacity="0"></stop>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
BackgroundBeams.displayName = "BackgroundBeams";
|
||||
61
docs/components/builder/code-tabs/code-editor.tsx
Normal file
61
docs/components/builder/code-tabs/code-editor.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Highlight, themes } from "prism-react-renderer";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import theme from "./theme";
|
||||
|
||||
interface CodeEditorProps {
|
||||
code: string;
|
||||
language: string;
|
||||
}
|
||||
|
||||
export function CodeEditor({ code, language }: CodeEditorProps) {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy text: ", err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Highlight theme={theme} code={code} language={language}>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre
|
||||
className={`${className} p-4 overflow-auto max-h-[400px] rounded-md`}
|
||||
style={style}
|
||||
>
|
||||
{tokens.map((line, i) => (
|
||||
<div key={i} {...getLineProps({ line, key: i })}>
|
||||
<span className="mr-4 text-gray-500 select-none">{i + 1}</span>
|
||||
{line.map((token, key) => (
|
||||
<span key={key} {...getTokenProps({ token, key })} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="absolute top-2 right-2"
|
||||
onClick={copyToClipboard}
|
||||
aria-label="Copy code"
|
||||
>
|
||||
{isCopied ? (
|
||||
<Check className="h-4 w-4" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
55
docs/components/builder/code-tabs/code-tabs.tsx
Normal file
55
docs/components/builder/code-tabs/code-tabs.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
interface TabProps {
|
||||
fileName: string;
|
||||
isActive: boolean;
|
||||
brightnessLevel?: number; // New optional prop for brightness level
|
||||
onClick: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const brightnessLevels = [
|
||||
"bg-background",
|
||||
"bg-background-200", //
|
||||
"bg-background-300",
|
||||
"bg-background-400",
|
||||
"bg-background-500",
|
||||
"bg-background-600",
|
||||
"bg-background-700",
|
||||
];
|
||||
|
||||
export function CodeTab({
|
||||
fileName,
|
||||
isActive,
|
||||
brightnessLevel = 0,
|
||||
onClick,
|
||||
onClose,
|
||||
}: TabProps) {
|
||||
const activeBrightnessClass = isActive
|
||||
? brightnessLevels[brightnessLevel % brightnessLevels.length]
|
||||
: "bg-muted";
|
||||
|
||||
const textColor = isActive ? "text-foreground" : "text-muted-foreground";
|
||||
const borderColor = isActive
|
||||
? "border-t-foreground"
|
||||
: "border-t-transparent hover:bg-background/50";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center px-3 py-2 text-sm font-medium border-t-2 cursor-pointer transition-colors duration-200",
|
||||
activeBrightnessClass,
|
||||
textColor,
|
||||
borderColor,
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="truncate max-w-[100px]">{fileName}</span>
|
||||
<button className="ml-2 text-muted-foreground hover:text-foreground transition-colors duration-200">
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
147
docs/components/builder/code-tabs/index.tsx
Normal file
147
docs/components/builder/code-tabs/index.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { useState } from "react";
|
||||
import { TabBar } from "./tab-bar";
|
||||
import { CodeEditor } from "./code-editor";
|
||||
import { useAtom } from "jotai";
|
||||
import { optionsAtom } from "../store";
|
||||
import { js_beautify } from "js-beautify";
|
||||
import { singUpString } from "../sign-up";
|
||||
import { signInString } from "../sign-in";
|
||||
|
||||
export default function CodeTabs() {
|
||||
const [options] = useAtom(optionsAtom);
|
||||
|
||||
const initialFiles = [
|
||||
{
|
||||
id: "1",
|
||||
name: "auth.ts",
|
||||
content: `import { betterAuth } from 'better-auth';
|
||||
|
||||
export const auth = betterAuth({
|
||||
${
|
||||
options.email
|
||||
? `emailAndPassword: {
|
||||
enabled: true,
|
||||
${
|
||||
options.forgetPassword
|
||||
? `async sendResetPassword(data, request) {
|
||||
// Send an email to the user with a link to reset their password
|
||||
},`
|
||||
: ``
|
||||
}
|
||||
},`
|
||||
: ""
|
||||
}${
|
||||
options.socialProviders.length
|
||||
? `socialProviders: ${JSON.stringify(
|
||||
options.socialProviders.reduce((acc, provider) => {
|
||||
return {
|
||||
...acc,
|
||||
[provider]: {
|
||||
clientId: `process.env.${provider.toUpperCase()}_CLIENT_ID`,
|
||||
clientSecret: `process.env.${provider.toUpperCase()}_CLIENT_SECRET`,
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
).replace(/"/g, "")},`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
options.magicLink || options.passkey
|
||||
? `plugins: [
|
||||
${
|
||||
options.magicLink
|
||||
? `magicLink({
|
||||
async sendMagicLink(data) {
|
||||
// Send an email to the user with a magic link
|
||||
},
|
||||
}),`
|
||||
: `${options.passkey ? `passkey(),` : ""}`
|
||||
}
|
||||
${options.passkey && options.magicLink ? `passkey(),` : ""}
|
||||
]`
|
||||
: ""
|
||||
}
|
||||
/** if no database is provided, the user data will be stored in memory.
|
||||
* Make sure to provide a database to persist user data **/
|
||||
});
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "auth-client.ts",
|
||||
content: `import { createAuthClient } from "better-auth/react";
|
||||
${
|
||||
options.magicLink || options.passkey
|
||||
? `import { ${options.magicLink ? "magicLinkClient, " : ""}, ${
|
||||
options.passkey ? "passkeyClient" : ""
|
||||
} } from "better-auth/client/plugins";`
|
||||
: ""
|
||||
}
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: process.env.NEXT_PUBLIC_APP_URL,
|
||||
${
|
||||
options.magicLink || options.passkey
|
||||
? `plugins: [${options.magicLink ? `magicLinkClient(),` : ""}${
|
||||
options.passkey ? `passkeyClient(),` : ""
|
||||
}],`
|
||||
: ""
|
||||
}
|
||||
})
|
||||
|
||||
export const { signIn, signOut, signUp, useSession } = authClient;
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "sign-in.tsx",
|
||||
content: signInString(options),
|
||||
},
|
||||
];
|
||||
if (options.email) {
|
||||
initialFiles.push({
|
||||
id: "4",
|
||||
name: "sign-up.tsx",
|
||||
content: singUpString,
|
||||
});
|
||||
}
|
||||
|
||||
const [files, setFiles] = useState(initialFiles);
|
||||
const [activeFileId, setActiveFileId] = useState(files[0].id);
|
||||
|
||||
const handleTabClick = (fileId: string) => {
|
||||
setActiveFileId(fileId);
|
||||
};
|
||||
|
||||
const handleTabClose = (fileId: string) => {
|
||||
setFiles(files.filter((file) => file.id !== fileId));
|
||||
if (activeFileId === fileId) {
|
||||
setActiveFileId(files[0].id);
|
||||
}
|
||||
};
|
||||
|
||||
const activeFile = files.find((file) => file.id === activeFileId);
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-3xl mx-auto mt-8 border border-border rounded-md overflow-hidden">
|
||||
<TabBar
|
||||
files={files}
|
||||
activeFileId={activeFileId}
|
||||
onTabClick={handleTabClick}
|
||||
onTabClose={handleTabClose}
|
||||
/>
|
||||
<div className="bg-background">
|
||||
{activeFile && (
|
||||
<CodeEditor
|
||||
language="typescript"
|
||||
code={
|
||||
activeFile.name.endsWith(".ts")
|
||||
? js_beautify(activeFile.content)
|
||||
: activeFile.content.replace(/\n{3,}/g, "\n\n")
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,14 +2,14 @@ import { PrismTheme } from "prism-react-renderer";
|
||||
|
||||
const theme: PrismTheme = {
|
||||
plain: {
|
||||
color: "#e0e0e0",
|
||||
backgroundColor: "#1a1a1a",
|
||||
color: "#d0d0d0",
|
||||
backgroundColor: "#000000", // Changed to true black
|
||||
},
|
||||
styles: [
|
||||
{
|
||||
types: ["comment", "prolog", "doctype", "cdata"],
|
||||
style: {
|
||||
color: "#999999",
|
||||
color: "#555555",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
},
|
||||
@@ -22,13 +22,13 @@ const theme: PrismTheme = {
|
||||
{
|
||||
types: ["string", "attr-value"],
|
||||
style: {
|
||||
color: "#a3d9ff", // Soft blue for strings
|
||||
color: "#8ab4f8", // Darker soft blue for strings
|
||||
},
|
||||
},
|
||||
{
|
||||
types: ["punctuation", "operator"],
|
||||
style: {
|
||||
color: "#cccccc",
|
||||
color: "#888888",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -45,32 +45,32 @@ const theme: PrismTheme = {
|
||||
"inserted",
|
||||
],
|
||||
style: {
|
||||
color: "#b3b3b3",
|
||||
color: "#a0a0a0",
|
||||
},
|
||||
},
|
||||
{
|
||||
types: ["atrule", "keyword", "attr-name", "selector"],
|
||||
style: {
|
||||
color: "#e6e6e6",
|
||||
fontWeight: "bold", // Bold for keywords
|
||||
color: "#c5c5c5",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
{
|
||||
types: ["function", "deleted", "tag"],
|
||||
style: {
|
||||
color: "#70c0ff", // Soft blue for functions
|
||||
color: "#7aa2f7", // Darker soft blue for functions
|
||||
},
|
||||
},
|
||||
{
|
||||
types: ["function-variable"],
|
||||
style: {
|
||||
color: "#a6a6a6",
|
||||
color: "#9e9e9e",
|
||||
},
|
||||
},
|
||||
{
|
||||
types: ["tag", "selector", "keyword"],
|
||||
style: {
|
||||
color: "#f0f0f0",
|
||||
color: "#cccccc", // Adjusted to a slightly lighter gray for better contrast on true black
|
||||
},
|
||||
},
|
||||
],
|
||||
576
docs/components/builder/index.tsx
Normal file
576
docs/components/builder/index.tsx
Normal file
@@ -0,0 +1,576 @@
|
||||
import { ChevronLeft, Copy, Mail, Moon, PlusIcon, Sun } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
import SignIn from "./sign-in";
|
||||
import { SignUp } from "./sign-up";
|
||||
import { AuthTabs } from "./tabs";
|
||||
import { Label } from "../ui/label";
|
||||
import { Switch } from "../ui/switch";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { useState } from "react";
|
||||
import CodeTabs from "./code-tabs";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { socialProviders } from "./social-provider";
|
||||
import { useAtom } from "jotai";
|
||||
import { optionsAtom } from "./store";
|
||||
import { useTheme } from "next-themes";
|
||||
import { ScrollArea } from "../ui/scroll-area";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
title: "Next.js",
|
||||
description: "The React Framework for Production",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 15 15"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M0 7.5a7.5 7.5 0 1 1 11.698 6.216L4.906 4.21A.5.5 0 0 0 4 4.5V12h1V6.06l5.83 8.162A7.5 7.5 0 0 1 0 7.5M10 10V4h1v6z"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Nuxt",
|
||||
description: "The Intuitive Vue Framework",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<g fill="none">
|
||||
<rect width="256" height="256" fill="#242938" rx="60"></rect>
|
||||
<path
|
||||
fill="#00DC82"
|
||||
d="M138.787 189.333h68.772c2.184.001 4.33-.569 6.222-1.652a12.4 12.4 0 0 0 4.554-4.515a12.24 12.24 0 0 0-.006-12.332l-46.185-79.286a12.4 12.4 0 0 0-4.553-4.514a12.53 12.53 0 0 0-12.442 0a12.4 12.4 0 0 0-4.553 4.514l-11.809 20.287l-23.09-39.67a12.4 12.4 0 0 0-4.555-4.513a12.54 12.54 0 0 0-12.444 0a12.4 12.4 0 0 0-4.555 4.513L36.67 170.834a12.24 12.24 0 0 0-.005 12.332a12.4 12.4 0 0 0 4.554 4.515a12.5 12.5 0 0 0 6.222 1.652h43.17c17.104 0 29.718-7.446 38.397-21.973l21.072-36.169l11.287-19.356l33.873 58.142h-45.16zm-48.88-19.376l-30.127-.007l45.16-77.518l22.533 38.759l-15.087 25.906c-5.764 9.426-12.312 12.86-22.48 12.86"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Svelte Kit",
|
||||
description: "Web development for the rest of us",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<g fill="none">
|
||||
<rect width="256" height="256" fill="#FF3E00" rx="60"></rect>
|
||||
<g clipPath="url(#skillIconsSvelte0)">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M193.034 61.797c-16.627-23.95-49.729-30.966-73.525-15.865L77.559 72.78c-11.44 7.17-19.372 18.915-21.66 32.186c-1.984 11.136-.306 22.576 5.033 32.492c-3.66 5.491-6.102 11.593-7.17 18c-2.44 13.576.764 27.61 8.696 38.745c16.78 23.95 49.728 30.966 73.525 15.865l41.949-26.695c11.441-7.17 19.373-18.915 21.661-32.187c1.983-11.135.305-22.576-5.034-32.491c3.661-5.492 6.102-11.593 7.17-18c2.593-13.729-.61-27.763-8.695-38.898"
|
||||
></path>
|
||||
<path
|
||||
fill="#FF3E00"
|
||||
d="M115.39 196.491a33.25 33.25 0 0 1-35.695-13.271c-4.881-6.712-6.712-15.101-5.34-23.339c.306-1.373.611-2.593.916-3.966l.763-2.44L78.169 155a55.6 55.6 0 0 0 16.475 8.237l1.525.458l-.152 1.525c-.153 2.136.458 4.424 1.678 6.255c2.441 3.508 6.712 5.186 10.83 4.118c.916-.305 1.831-.61 2.594-1.068l41.796-26.695c2.136-1.372 3.509-3.355 3.966-5.796s-.152-5.034-1.525-7.017c-2.441-3.509-6.712-5.034-10.831-3.966c-.915.305-1.83.61-2.593 1.068l-16.017 10.22c-2.593 1.678-5.491 2.898-8.542 3.661a33.25 33.25 0 0 1-35.695-13.271c-4.729-6.712-6.712-15.102-5.186-23.339c1.372-7.932 6.254-15.102 13.118-19.373l41.949-26.695c2.593-1.678 5.492-2.898 8.543-3.814a33.25 33.25 0 0 1 35.695 13.272c4.881 6.712 6.711 15.101 5.339 23.339c-.306 1.373-.611 2.593-1.068 3.966l-.763 2.44l-2.136-1.525a55.6 55.6 0 0 0-16.474-8.237l-1.526-.458l.153-1.525c.153-2.136-.458-4.424-1.678-6.255c-2.441-3.508-6.712-5.034-10.83-3.966c-.916.305-1.831.61-2.594 1.068l-41.796 26.695c-2.136 1.373-3.509 3.356-3.966 5.797s.152 5.034 1.525 7.017c2.441 3.508 6.712 5.033 10.831 3.966c.915-.305 1.83-.611 2.593-1.068l16.017-10.22c2.593-1.678 5.491-2.899 8.542-3.814a33.25 33.25 0 0 1 35.695 13.271c4.881 6.712 6.712 15.102 5.339 23.339c-1.373 7.932-6.254 15.102-13.119 19.373l-41.949 26.695c-2.593 1.678-5.491 2.898-8.542 3.813"
|
||||
></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="skillIconsSvelte0">
|
||||
<path fill="#fff" d="M53 38h149.644v180H53z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Solid Start",
|
||||
description: "Fine-grained reactivity goes fullstack",
|
||||
Icon: () => (
|
||||
<svg
|
||||
data-hk="00000010210"
|
||||
width="2em"
|
||||
height="2em"
|
||||
viewBox="0 0 500 500"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
role="presentation"
|
||||
>
|
||||
<path
|
||||
d="M233.205 430.856L304.742 425.279C304.742 425.279 329.208 421.295 343.569 397.659L293.041 385.443L233.205 430.856Z"
|
||||
fill="url(#paint0_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M134.278 263.278C113.003 264.341 73.6443 268.059 73.6443 268.059L245.173 392.614L284.265 402.44L343.569 397.925L170.977 273.105C170.977 273.105 157.148 263.278 137.203 263.278C136.139 263.278 135.342 263.278 134.278 263.278Z"
|
||||
fill="url(#paint1_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M355.536 238.58L429.2 234.065C429.2 234.065 454.464 230.348 468.825 206.977L416.435 193.964L355.536 238.58Z"
|
||||
fill="url(#paint2_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M251.289 68.6128C229.217 69.4095 188.795 72.5964 188.795 72.5964L367.503 200.072L407.926 210.429L469.09 206.712L289.318 78.9702C289.318 78.9702 274.426 68.6128 253.417 68.6128C252.885 68.6128 252.087 68.6128 251.289 68.6128Z"
|
||||
fill="url(#paint3_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M31.0946 295.679C30.8287 295.945 30.8287 296.21 30.8287 296.475L77.8993 330.469L202.623 420.764C228.95 439.62 264.586 431.653 282.67 402.44L187.465 333.921L110.077 277.62C100.504 270.715 89.8663 267.528 79.2289 267.528C60.6134 267.528 42.2639 277.354 31.0946 295.679Z"
|
||||
fill="url(#paint4_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M147.043 99.9505C147.043 100.216 146.776 100.482 146.511 100.747L195.442 135.538L244.374 170.062L325.751 227.957C353.142 247.345 389.841 239.642 407.925 210.695L358.461 175.374L308.997 140.318L228.153 82.6881C218.047 75.5177 206.611 72.0652 195.442 72.0652C176.561 72.3308 158.212 81.8915 147.043 99.9505Z"
|
||||
fill="url(#paint5_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M112.471 139.255L175.497 208.305C178.423 212.289 181.614 216.006 185.337 219.193L308.199 354.105L369.364 350.387C387.448 321.439 380.002 282.135 352.611 262.748L271.234 204.852L222.568 170.328L173.636 135.538L112.471 139.255Z"
|
||||
fill="url(#paint6_linear_1_2)"
|
||||
></path>
|
||||
<path
|
||||
d="M111.939 140.052C94.1213 168.734 101.567 207.509 128.427 226.629L209.005 283.994L258.735 319.049L308.199 354.105C326.283 325.158 318.836 285.852 291.445 266.465L112.471 139.255C112.471 139.521 112.204 139.787 111.939 140.052Z"
|
||||
fill="url(#paint7_linear_1_2)"
|
||||
></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1_2"
|
||||
x1="359.728"
|
||||
y1="56.8062"
|
||||
x2="265.623"
|
||||
y2="521.28"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#1593F5"></stop>
|
||||
<stop offset="1" stopColor="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_1_2"
|
||||
x1="350.496"
|
||||
y1="559.872"
|
||||
x2="-44.0802"
|
||||
y2="-73.2062"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#1593F5"></stop>
|
||||
<stop offset="1" stopColor="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_1_2"
|
||||
x1="610.25"
|
||||
y1="570.526"
|
||||
x2="372.635"
|
||||
y2="144.034"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white"></stop>
|
||||
<stop offset="1" stopColor="#15ABFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_1_2"
|
||||
x1="188.808"
|
||||
y1="-180.608"
|
||||
x2="390.515"
|
||||
y2="281.703"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white"></stop>
|
||||
<stop offset="1" stopColor="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_1_2"
|
||||
x1="415.84"
|
||||
y1="-4.74684"
|
||||
x2="95.1922"
|
||||
y2="439.83"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#0057E5"></stop>
|
||||
<stop offset="1" stopColor="#0084CE"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_1_2"
|
||||
x1="343.141"
|
||||
y1="-21.5427"
|
||||
x2="242.301"
|
||||
y2="256.708"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white"></stop>
|
||||
<stop offset="1" stopColor="#15ABFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint6_linear_1_2"
|
||||
x1="469.095"
|
||||
y1="533.421"
|
||||
x2="-37.6939"
|
||||
y2="-135.731"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white"></stop>
|
||||
<stop offset="1" stopColor="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint7_linear_1_2"
|
||||
x1="380.676"
|
||||
y1="-89.0869"
|
||||
x2="120.669"
|
||||
y2="424.902"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white"></stop>
|
||||
<stop offset="1" stopColor="#79CFFF"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function Builder() {
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
|
||||
const [options, setOptions] = useAtom(optionsAtom);
|
||||
const { setTheme, resolvedTheme } = useTheme();
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<button className="bg-stone-950 no-underline group cursor-pointer relative shadow-2xl shadow-zinc-900 rounded-sm p-px text-xs font-semibold leading-6 text-white inline-block">
|
||||
<span className="absolute inset-0 overflow-hidden rounded-sm">
|
||||
<span className="absolute inset-0 rounded-sm bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100"></span>
|
||||
</span>
|
||||
<div className="relative flex space-x-2 items-center z-10 rounded-none bg-zinc-950 py-2 px-4 ring-1 ring-white/10 ">
|
||||
<PlusIcon size={14} />
|
||||
<span>Create Sign in Box</span>
|
||||
</div>
|
||||
<span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-stone-800/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40"></span>
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-7xl h-5/6 overflow-clip">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Sign in Box</DialogTitle>
|
||||
<DialogDescription>
|
||||
Configure the sign in box to your liking and copy the code to your
|
||||
application
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex gap-4 md:gap-12 flex-col md:flex-row items-center md:items-start">
|
||||
<ScrollArea className="w-4/12">
|
||||
<div className="overflow-scroll h-[580px] relate">
|
||||
{options.signUp ? (
|
||||
<AuthTabs
|
||||
tabs={[
|
||||
{
|
||||
title: "Sign In",
|
||||
value: "sign-in",
|
||||
content: <SignIn />,
|
||||
},
|
||||
{
|
||||
title: "Sign Up",
|
||||
value: "sign-up",
|
||||
content: <SignUp />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<SignIn />
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<ScrollArea className="w-5/12 flex-grow">
|
||||
<div className="h-[580px]">
|
||||
{currentStep === 0 ? (
|
||||
<Card className="rounded-none flex-grow h-full">
|
||||
<CardHeader className="flex flex-row justify-between">
|
||||
<CardTitle>Configuration</CardTitle>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
if (resolvedTheme === "dark") {
|
||||
setTheme("light");
|
||||
} else {
|
||||
setTheme("dark");
|
||||
}
|
||||
}}
|
||||
>
|
||||
{resolvedTheme === "dark" ? (
|
||||
<Moon onClick={() => setTheme("light")} size={18} />
|
||||
) : (
|
||||
<Sun onClick={() => setTheme("dark")} size={18} />
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="max-h-[400px] overflow-scroll">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<Label>Email & Password</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<Label>Enabled</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.email}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
email: checked,
|
||||
magicLink: checked ? false : prev.magicLink,
|
||||
signUp: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Remember Me</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.rememberMe}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
rememberMe: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label>Forget Password</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.forgetPassword}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
forgetPassword: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<div>
|
||||
<Label>Social Providers</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
{Object.entries(socialProviders).map(
|
||||
([provider, { Icon }]) => (
|
||||
<div
|
||||
className="flex items-center justify-between"
|
||||
key={provider}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon />
|
||||
<Label>
|
||||
{provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1)}
|
||||
</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.socialProviders.includes(
|
||||
provider,
|
||||
)}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
socialProviders: checked
|
||||
? [...prev.socialProviders, provider]
|
||||
: prev.socialProviders.filter(
|
||||
(p) => p !== provider,
|
||||
),
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 mt-4">
|
||||
<div>
|
||||
<Label>Plugins</Label>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 20q-.825 0-1.412-.587T3 18v-.8q0-.85.438-1.562T4.6 14.55q1.55-.775 3.15-1.162T11 13q.35 0 .7.013t.7.062q.275.025.437.213t.163.462q.05 1.175.575 2.213t1.4 1.762q.175.125.275.313t.1.412V19q0 .425-.288.713T14.35 20zm6-8q-1.65 0-2.825-1.175T7 8t1.175-2.825T11 4t2.825 1.175T15 8t-1.175 2.825T11 12m7.5 2q.425 0 .713-.288T19.5 13t-.288-.712T18.5 12t-.712.288T17.5 13t.288.713t.712.287m.15 8.65l-1-1q-.05-.05-.15-.35v-4.45q-1.1-.325-1.8-1.237T15 13.5q0-1.45 1.025-2.475T18.5 10t2.475 1.025T22 13.5q0 1.125-.638 2t-1.612 1.25l.9.9q.15.15.15.35t-.15.35l-.8.8q-.15.15-.15.35t.15.35l.8.8q.15.15.15.35t-.15.35l-1.3 1.3q-.15.15-.35.15t-.35-.15"
|
||||
></path>
|
||||
</svg>
|
||||
<Label>Passkey</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.passkey}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
passkey: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none">
|
||||
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17.5 3a4.5 4.5 0 0 1 4.495 4.288L22 7.5V15a2 2 0 0 1-1.85 1.995L20 17h-3v3a1 1 0 0 1-1.993.117L15 20v-3H4a2 2 0 0 1-1.995-1.85L2 15V7.5a4.5 4.5 0 0 1 4.288-4.495L6.5 3zm-11 2A2.5 2.5 0 0 0 4 7.5V15h5V7.5A2.5 2.5 0 0 0 6.5 5M7 8a1 1 0 0 1 .117 1.993L7 10H6a1 1 0 0 1-.117-1.993L6 8z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
<Label>Magic Link</Label>
|
||||
</div>
|
||||
<Switch
|
||||
checked={options.magicLink}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
magicLink: checked,
|
||||
email: checked ? false : prev.email,
|
||||
signUp: checked ? false : prev.signUp,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<Label>Powered by label</Label>
|
||||
<Switch
|
||||
checked={options.label}
|
||||
onCheckedChange={(checked) => {
|
||||
setOptions((prev) => ({
|
||||
...prev,
|
||||
label: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<button
|
||||
className="bg-stone-950 no-underline group cursor-pointer relative shadow-2xl shadow-zinc-900 rounded-sm p-px text-xs font-semibold leading-6 text-white inline-block w-full"
|
||||
onClick={() => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}}
|
||||
>
|
||||
<span className="absolute inset-0 overflow-hidden rounded-sm">
|
||||
<span className="absolute inset-0 rounded-sm bg-[image:radial-gradient(75%_100%_at_50%_0%,rgba(56,189,248,0.6)_0%,rgba(56,189,248,0)_75%)] opacity-0 transition-opacity duration-500 group-hover:opacity-100"></span>
|
||||
</span>
|
||||
<div className="relative flex space-x-2 items-center z-10 rounded-none bg-zinc-950 py-2 px-4 ring-1 ring-white/10 justify-center">
|
||||
<span>Continue</span>
|
||||
</div>
|
||||
<span className="absolute -bottom-0 left-[1.125rem] h-px w-[calc(100%-2.25rem)] bg-gradient-to-r from-emerald-400/0 via-stone-800/90 to-emerald-400/0 transition-opacity duration-500 group-hover:opacity-40"></span>
|
||||
</button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
) : currentStep === 1 ? (
|
||||
<Card className="rounded-none flex-grow h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Choose Framework</CardTitle>
|
||||
<p
|
||||
className="text-blue-400 hover:underline mt-1 text-sm cursor-pointer"
|
||||
onClick={() => {
|
||||
setCurrentStep(0);
|
||||
}}
|
||||
>
|
||||
Go Back
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent className="flex items-start gap-2 flex-wrap justify-between">
|
||||
{frameworks.map((fm) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (fm.title === "Next.js") {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-4 border p-6 rounded-md w-5/12 flex-grow h-44 relative",
|
||||
fm.title !== "Next.js"
|
||||
? "opacity-55"
|
||||
: "hover:ring-1 transition-all ring-border hover:bg-background duration-200 ease-in-out cursor-pointer",
|
||||
)}
|
||||
key={fm.title}
|
||||
>
|
||||
{fm.title !== "Next.js" && (
|
||||
<span className="absolute top-4 right-4 text-xs">
|
||||
Coming Soon
|
||||
</span>
|
||||
)}
|
||||
<fm.Icon />
|
||||
<Label className="text-2xl">{fm.title}</Label>
|
||||
<p className="text-sm">{fm.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="rounded-none flex-grow h-full overflow-scroll">
|
||||
<CardHeader>
|
||||
<div className="flex flex-col items-start">
|
||||
<CardTitle>Code</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div>
|
||||
<p>
|
||||
Copy the code below and paste it in your application to
|
||||
get started.
|
||||
</p>
|
||||
<p
|
||||
className="text-blue-400 hover:underline mt-1 text-sm cursor-pointer"
|
||||
onClick={() => {
|
||||
setCurrentStep(0);
|
||||
}}
|
||||
>
|
||||
Go Back
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CodeTabs />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
406
docs/components/builder/sign-in.tsx
Normal file
406
docs/components/builder/sign-in.tsx
Normal file
@@ -0,0 +1,406 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
|
||||
import { Key, Loader2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { useAtom } from "jotai";
|
||||
import { optionsAtom } from "./store";
|
||||
import { socialProviders } from "./social-provider";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function SignIn() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [options, setOptions] = useAtom(optionsAtom);
|
||||
return (
|
||||
<Card className="z-50 rounded-none max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign In</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your email below to login to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
{options.email && (
|
||||
<>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
{options.forgetPassword && (
|
||||
<Link
|
||||
href="#"
|
||||
className="ml-auto inline-block text-sm underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="password"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{options.rememberMe && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
onClick={() => {
|
||||
setRememberMe(!rememberMe);
|
||||
}}
|
||||
/>
|
||||
<Label>Remember me</Label>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{options.magicLink && (
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
<Button className="gap-2" onClick={async () => {}}>
|
||||
Sign-in with Magic Link
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{options.email && (
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {}}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
) : (
|
||||
"Login"
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{options.passkey && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="gap-2"
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<Key size={16} />
|
||||
Sign-in with Passkey
|
||||
</Button>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"w-full gap-2 flex items-center justify-between",
|
||||
options.socialProviders.length > 3
|
||||
? "flex-row flex-wrap"
|
||||
: "flex-col",
|
||||
)}
|
||||
>
|
||||
{Object.keys(socialProviders).map((provider) => {
|
||||
if (options.socialProviders.includes(provider)) {
|
||||
const { Icon } =
|
||||
socialProviders[provider as keyof typeof socialProviders];
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
options.socialProviders.length > 3
|
||||
? "flex-grow"
|
||||
: "w-full gap-2",
|
||||
)}
|
||||
onClick={async () => {}}
|
||||
>
|
||||
<Icon width="1.2em" height="1.2em" />
|
||||
{options.socialProviders.length <= 3 &&
|
||||
"Sign in with " +
|
||||
provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
{options.label && (
|
||||
<CardFooter>
|
||||
<div className="flex justify-center w-full border-t py-4">
|
||||
<p className="text-center text-xs text-neutral-500">
|
||||
Powered by{" "}
|
||||
<Link
|
||||
href="https://better-auth.com"
|
||||
className="underline"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="dark:text-orange-200/90">better-auth.</span>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</CardFooter>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export const signInString = (options: any) => `"use client"
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useState } from "react";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { signIn } from "@/lib/auth-client";
|
||||
|
||||
function SignInCard() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign In</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your email below to login to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
${
|
||||
options.email
|
||||
? `<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
${
|
||||
options.forgetPassword
|
||||
? `
|
||||
<Link
|
||||
href="#"
|
||||
className="ml-auto inline-block text-sm underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
`
|
||||
: ``
|
||||
}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="password"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
${
|
||||
options.rememberMe
|
||||
? `<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
onClick={() => {
|
||||
setRememberMe(!rememberMe);
|
||||
}}
|
||||
/>
|
||||
<Label>Remember me</Label>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
options.magicLink
|
||||
? `
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
<Button className="gap-2" onClick={async () => {}}>
|
||||
Sign-in with Magic Link
|
||||
</Button>
|
||||
</div>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
options.email
|
||||
? `
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
await signIn.email({ email, password });
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
) : (
|
||||
"Login"
|
||||
)}
|
||||
</Button>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
options.passkey
|
||||
? `
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="gap-2"
|
||||
onClick={async () => {
|
||||
await sigIn.passkey();
|
||||
}}
|
||||
>
|
||||
<Key size={16} />
|
||||
Sign-in with Passkey
|
||||
</Button>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
options.socialProviders.length > 0
|
||||
? `<div
|
||||
className={cn(
|
||||
${
|
||||
options.socialProviders.length > 3
|
||||
? "w-full gap-2 flex items-center justify-between flex-wrap"
|
||||
: "w-full gap-2 flex items-center justify-between flex-col"
|
||||
},
|
||||
)}
|
||||
>
|
||||
${Object.keys(socialProviders).map((provider) => {
|
||||
if (options.socialProviders.includes(provider)) {
|
||||
const { stringIcon } =
|
||||
socialProviders[provider as keyof typeof socialProviders];
|
||||
return `<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
${
|
||||
options.socialProviders.length > 3
|
||||
? "flex-grow"
|
||||
: "w-full gap-2"
|
||||
}
|
||||
)}
|
||||
onClick={async () => {
|
||||
await signIn.social({
|
||||
provider: "${provider}",
|
||||
callbackURL: "/dashboard"
|
||||
});
|
||||
}}
|
||||
>
|
||||
${stringIcon}
|
||||
${
|
||||
options.socialProviders.length <= 3
|
||||
? "Sign in with " +
|
||||
provider.charAt(0).toUpperCase() +
|
||||
provider.slice(1)
|
||||
: ""
|
||||
}
|
||||
</Button>`;
|
||||
}
|
||||
return "";
|
||||
})}
|
||||
</div>`
|
||||
: ""
|
||||
}
|
||||
|
||||
</div>
|
||||
</CardContent>
|
||||
${
|
||||
options.label
|
||||
? `
|
||||
<CardFooter>
|
||||
<div className="flex justify-center w-full border-t py-4">
|
||||
<p className="text-center text-xs text-neutral-500">
|
||||
Powered by{" "}
|
||||
<Link
|
||||
href="https://better-auth.com"
|
||||
className="underline"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="dark:text-orange-200/90">better-auth.</span>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</CardFooter>
|
||||
`
|
||||
: ``
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
`;
|
||||
377
docs/components/builder/sign-up.tsx
Normal file
377
docs/components/builder/sign-up.tsx
Normal file
@@ -0,0 +1,377 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Loader2, X } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function SignUp() {
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setImage(file);
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="z-50 rounded-md rounded-t-none max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign Up</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your information to create an account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="first-name">First name</Label>
|
||||
<Input
|
||||
id="first-name"
|
||||
placeholder="Max"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setFirstName(e.target.value);
|
||||
}}
|
||||
value={firstName}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="last-name">Last name</Label>
|
||||
<Input
|
||||
id="last-name"
|
||||
placeholder="Robinson"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setLastName(e.target.value);
|
||||
}}
|
||||
value={lastName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Confirm Password</Label>
|
||||
<Input
|
||||
id="password_confirmation"
|
||||
value={passwordConfirmation}
|
||||
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Confirm Password"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="image">Profile Image (optional)</Label>
|
||||
<div className="flex items-end gap-4">
|
||||
{imagePreview && (
|
||||
<div className="relative w-16 h-16 rounded-sm overflow-hidden">
|
||||
<Image
|
||||
src={imagePreview}
|
||||
alt="Profile preview"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<Input
|
||||
id="image"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="w-full"
|
||||
/>
|
||||
{imagePreview && (
|
||||
<X
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setImage(null);
|
||||
setImagePreview(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {}}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
) : (
|
||||
"Create an account"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-center w-full border-t py-4">
|
||||
<p className="text-center text-xs text-neutral-500">
|
||||
Secured by <span className="text-orange-400">better-auth.</span>
|
||||
</p>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
async function convertImageToBase64(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result as string);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
export const singUpString = `"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { Loader2, X } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function SignUp() {
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setImage(file);
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setImagePreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="z-50 rounded-md rounded-t-none max-w-md">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg md:text-xl">Sign Up</CardTitle>
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
Enter your information to create an account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="first-name">First name</Label>
|
||||
<Input
|
||||
id="first-name"
|
||||
placeholder="Max"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setFirstName(e.target.value);
|
||||
}}
|
||||
value={firstName}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="last-name">Last name</Label>
|
||||
<Input
|
||||
id="last-name"
|
||||
placeholder="Robinson"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setLastName(e.target.value);
|
||||
}}
|
||||
value={lastName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
value={email}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
id="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="password">Confirm Password</Label>
|
||||
<Input
|
||||
id="password_confirmation"
|
||||
value={passwordConfirmation}
|
||||
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
||||
autoComplete="new-password"
|
||||
placeholder="Confirm Password"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="image">Profile Image (optional)</Label>
|
||||
<div className="flex items-end gap-4">
|
||||
{imagePreview && (
|
||||
<div className="relative w-16 h-16 rounded-sm overflow-hidden">
|
||||
<Image
|
||||
src={imagePreview}
|
||||
alt="Profile preview"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<Input
|
||||
id="image"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageChange}
|
||||
className="w-full"
|
||||
/>
|
||||
{imagePreview && (
|
||||
<X
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setImage(null);
|
||||
setImagePreview(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
await signUp.email({
|
||||
email,
|
||||
password,
|
||||
name: \`\${firstName} \${lastName}\`,
|
||||
image: image ? await convertImageToBase64(image) : "",
|
||||
callbackURL: "/dashboard",
|
||||
fetchOptions: {
|
||||
onResponse: () => {
|
||||
setLoading(false);
|
||||
},
|
||||
onRequest: () => {
|
||||
setLoading(true);
|
||||
},
|
||||
onError: (ctx) => {
|
||||
toast.error(ctx.error.message);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
router.push("/dashboard");
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 size={16} className="animate-spin" />
|
||||
) : (
|
||||
"Create an account"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<div className="flex justify-center w-full border-t py-4">
|
||||
<p className="text-center text-xs text-neutral-500">
|
||||
Secured by <span className="text-orange-400">better-auth.</span>
|
||||
</p>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
async function convertImageToBase64(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result as string);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}`;
|
||||
346
docs/components/builder/social-provider.tsx
Normal file
346
docs/components/builder/social-provider.tsx
Normal file
@@ -0,0 +1,346 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
export const socialProviders = {
|
||||
apple: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17.05 20.28c-.98.95-2.05.8-3.08.35c-1.09-.46-2.09-.48-3.24 0c-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8c1.18-.24 2.31-.93 3.57-.84c1.51.12 2.65.72 3.4 1.8c-3.12 1.87-2.38 5.98.48 7.13c-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25c.29 2.58-2.34 4.5-3.74 4.25"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M17.05 20.28c-.98.95-2.05.8-3.08.35c-1.09-.46-2.09-.48-3.24 0c-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8c1.18-.24 2.31-.93 3.57-.84c1.51.12 2.65.72 3.4 1.8c-3.12 1.87-2.38 5.98.48 7.13c-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25c.29 2.58-2.34 4.5-3.74 4.25"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
dropbox: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path d="m12.594 23.258l-.012.002l-.071.035l-.02.004l-.014-.004l-.071-.036q-.016-.004-.024.006l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.016-.018m.264-.113l-.014.002l-.184.093l-.01.01l-.003.011l.018.43l.005.012l.008.008l.201.092q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.003-.011l.018-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m11.998 13.503l2.879 1.662c.426.246.923.34 1.365.34c.443 0 .94-.094 1.367-.34l.587-.34V17a1 1 0 0 1-.5.866l-5.196 3a1 1 0 0 1-1 0l-5.196-3a1 1 0 0 1-.5-.866v-2.172l.583.337c.426.246.923.34 1.366.34c.442 0 .939-.094 1.366-.34zM6.887 3.5c.434-.251 1.115-.274 1.594-.068l.138.068l3.379 1.95l3.379-1.95c.434-.251 1.115-.274 1.594-.068l.138.068l4.242 2.45c.447.257.476.664.09.942l-.09.057l-3.378 1.95l3.378 1.95c.447.258.476.665.09.943l-.09.057l-4.242 2.45c-.435.25-1.116.273-1.595.068l-.137-.068l-3.38-1.951l-3.378 1.95c-.435.252-1.116.274-1.595.07l-.137-.07l-4.243-2.449c-.447-.257-.476-.665-.09-.942l.09-.058L6.022 8.9L2.644 6.95c-.447-.257-.476-.665-.09-.942l.09-.058zm5.546 2.702c-.205-.119-.52-.136-.755-.051l-.111.05l-4.243 2.45c-.212.122-.236.313-.07.45l.07.05l4.243 2.449c.205.118.52.135.755.05l.111-.05l4.243-2.45c.212-.122.236-.312.07-.45l-.07-.05z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path d="m12.594 23.258l-.012.002l-.071.035l-.02.004l-.014-.004l-.071-.036q-.016-.004-.024.006l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.016-.018m.264-.113l-.014.002l-.184.093l-.01.01l-.003.011l.018.43l.005.012l.008.008l.201.092q.019.005.029-.008l.004-.014l-.034-.614q-.005-.019-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.003-.011l.018-.43l-.003-.012l-.01-.01z"></path>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m11.998 13.503l2.879 1.662c.426.246.923.34 1.365.34c.443 0 .94-.094 1.367-.34l.587-.34V17a1 1 0 0 1-.5.866l-5.196 3a1 1 0 0 1-1 0l-5.196-3a1 1 0 0 1-.5-.866v-2.172l.583.337c.426.246.923.34 1.366.34c.442 0 .939-.094 1.366-.34zM6.887 3.5c.434-.251 1.115-.274 1.594-.068l.138.068l3.379 1.95l3.379-1.95c.434-.251 1.115-.274 1.594-.068l.138.068l4.242 2.45c.447.257.476.664.09.942l-.09.057l-3.378 1.95l3.378 1.95c.447.258.476.665.09.943l-.09.057l-4.242 2.45c-.435.25-1.116.273-1.595.068l-.137-.068l-3.38-1.951l-3.378 1.95c-.435.252-1.116.274-1.595.07l-.137-.07l-4.243-2.449c-.447-.257-.476-.665-.09-.942l.09-.058L6.022 8.9L2.644 6.95c-.447-.257-.476-.665-.09-.942l.09-.058zm5.546 2.702c-.205-.119-.52-.136-.755-.051l-.111.05l-4.243 2.45c-.212.122-.236.313-.07.45l.07.05l4.243 2.449c.205.118.52.135.755.05l.111-.05l4.243-2.45c.212-.122.236-.312.07-.45l-.07-.05z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>`,
|
||||
},
|
||||
discord: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.1.1 0 0 0-.07.03c-.18.33-.39.76-.53 1.09a16.1 16.1 0 0 0-4.8 0c-.14-.34-.35-.76-.54-1.09c-.01-.02-.04-.03-.07-.03c-1.5.26-2.93.71-4.27 1.33c-.01 0-.02.01-.03.02c-2.72 4.07-3.47 8.03-3.1 11.95c0 .02.01.04.03.05c1.8 1.32 3.53 2.12 5.24 2.65c.03.01.06 0 .07-.02c.4-.55.76-1.13 1.07-1.74c.02-.04 0-.08-.04-.09c-.57-.22-1.11-.48-1.64-.78c-.04-.02-.04-.08-.01-.11c.11-.08.22-.17.33-.25c.02-.02.05-.02.07-.01c3.44 1.57 7.15 1.57 10.55 0c.02-.01.05-.01.07.01c.11.09.22.17.33.26c.04.03.04.09-.01.11c-.52.31-1.07.56-1.64.78c-.04.01-.05.06-.04.09c.32.61.68 1.19 1.07 1.74c.03.01.06.02.09.01c1.72-.53 3.45-1.33 5.25-2.65c.02-.01.03-.03.03-.05c.44-4.53-.73-8.46-3.1-11.95c-.01-.01-.02-.02-.04-.02M8.52 14.91c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.84 2.12-1.89 2.12m6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.83 2.12-1.89 2.12"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19.27 5.33C17.94 4.71 16.5 4.26 15 4a.1.1 0 0 0-.07.03c-.18.33-.39.76-.53 1.09a16.1 16.1 0 0 0-4.8 0c-.14-.34-.35-.76-.54-1.09c-.01-.02-.04-.03-.07-.03c-1.5.26-2.93.71-4.27 1.33c-.01 0-.02.01-.03.02c-2.72 4.07-3.47 8.03-3.1 11.95c0 .02.01.04.03.05c1.8 1.32 3.53 2.12 5.24 2.65c.03.01.06 0 .07-.02c.4-.55.76-1.13 1.07-1.74c.02-.04 0-.08-.04-.09c-.57-.22-1.11-.48-1.64-.78c-.04-.02-.04-.08-.01-.11c.11-.08.22-.17.33-.25c.02-.02.05-.02.07-.01c3.44 1.57 7.15 1.57 10.55 0c.02-.01.05-.01.07.01c.11.09.22.17.33.26c.04.03.04.09-.01.11c-.52.31-1.07.56-1.64.78c-.04.01-.05.06-.04.09c.32.61.68 1.19 1.07 1.74c.03.01.06.02.09.01c1.72-.53 3.45-1.33 5.25-2.65c.02-.01.03-.03.03-.05c.44-4.53-.73-8.46-3.1-11.95c-.01-.01-.02-.02-.04-.02M8.52 14.91c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.84 2.12-1.89 2.12m6.97 0c-1.03 0-1.89-.95-1.89-2.12s.84-2.12 1.89-2.12c1.06 0 1.9.96 1.89 2.12c0 1.17-.83 2.12-1.89 2.12"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
facebook: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M20 3H4a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h8.615v-6.96h-2.338v-2.725h2.338v-2c0-2.325 1.42-3.592 3.5-3.592c.699-.002 1.399.034 2.095.107v2.42h-1.435c-1.128 0-1.348.538-1.348 1.325v1.735h2.697l-.35 2.725h-2.348V21H20a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M20 3H4a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h8.615v-6.96h-2.338v-2.725h2.338v-2c0-2.325 1.42-3.592 3.5-3.592c.699-.002 1.399.034 2.095.107v2.42h-1.435c-1.128 0-1.348.538-1.348 1.325v1.735h2.697l-.35 2.725h-2.348V21H20a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
github: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33s1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
gitlab: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m22.749 9.769l-.031-.08l-3.027-7.9a.79.79 0 0 0-.782-.495a.8.8 0 0 0-.456.17a.8.8 0 0 0-.268.408L16.14 8.125H7.865L5.822 1.872a.8.8 0 0 0-.269-.409a.81.81 0 0 0-.926-.05c-.14.09-.25.22-.312.376L1.283 9.684l-.03.08a5.62 5.62 0 0 0 1.864 6.496l.01.008l.028.02l4.61 3.453l2.282 1.726l1.39 1.049a.935.935 0 0 0 1.13 0l1.389-1.05l2.281-1.726l4.639-3.473l.011-.01A5.62 5.62 0 0 0 22.75 9.77"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m22.749 9.769l-.031-.08l-3.027-7.9a.79.79 0 0 0-.782-.495a.8.8 0 0 0-.456.17a.8.8 0 0 0-.268.408L16.14 8.125H7.865L5.822 1.872a.8.8 0 0 0-.269-.409a.81.81 0 0 0-.926-.05c-.14.09-.25.22-.312.376L1.283 9.684l-.03.08a5.62 5.62 0 0 0 1.864 6.496l.01.008l.028.02l4.61 3.453l2.282 1.726l1.39 1.049a.935.935 0 0 0 1.13 0l1.389-1.05l2.281-1.726l4.639-3.473l.011-.01A5.62 5.62 0 0 0 22.75 9.77"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
google: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.99 13.9v-3.72h9.36c.14.63.25 1.22.25 2.05c0 5.71-3.83 9.77-9.6 9.77c-5.52 0-10-4.48-10-10S6.48 2 12 2c2.7 0 4.96.99 6.69 2.61l-2.84 2.76c-.72-.68-1.98-1.48-3.85-1.48c-3.31 0-6.01 2.75-6.01 6.12s2.7 6.12 6.01 6.12c3.83 0 5.24-2.65 5.5-4.22h-5.51z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.99 13.9v-3.72h9.36c.14.63.25 1.22.25 2.05c0 5.71-3.83 9.77-9.6 9.77c-5.52 0-10-4.48-10-10S6.48 2 12 2c2.7 0 4.96.99 6.69 2.61l-2.84 2.76c-.72-.68-1.98-1.48-3.85-1.48c-3.31 0-6.01 2.75-6.01 6.12s2.7 6.12 6.01 6.12c3.83 0 5.24-2.65 5.5-4.22h-5.51z"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
linkedin: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93zM6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93zM6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37z"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
microsoft: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 3h9v9H2zm9 19H2v-9h9zM21 3v9h-9V3zm0 19h-9v-9h9z"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
twitch: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.64 5.93h1.43v4.28h-1.43m3.93-4.28H17v4.28h-1.43M7 2L3.43 5.57v12.86h4.28V22l3.58-3.57h2.85L20.57 12V2m-1.43 9.29l-2.85 2.85h-2.86l-2.5 2.5v-2.5H7.71V3.43h11.43Z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M11.64 5.93h1.43v4.28h-1.43m3.93-4.28H17v4.28h-1.43M7 2L3.43 5.57v12.86h4.28V22l3.58-3.57h2.85L20.57 12V2m-1.43 9.29l-2.85 2.85h-2.86l-2.5 2.5v-2.5H7.71V3.43h11.43Z"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
spotify: {
|
||||
Icon: (props: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.001 2c-5.5 0-10 4.5-10 10s4.5 10 10 10s10-4.5 10-10s-4.45-10-10-10m3.75 14.65c-2.35-1.45-5.3-1.75-8.8-.95c-.35.1-.65-.15-.75-.45c-.1-.35.15-.65.45-.75c3.8-.85 7.1-.5 9.7 1.1c.35.15.4.55.25.85c-.2.3-.55.4-.85.2m1-2.7c-2.7-1.65-6.8-2.15-9.95-1.15c-.4.1-.85-.1-.95-.5s.1-.85.5-.95c3.65-1.1 8.15-.55 11.25 1.35c.3.15.45.65.2 1s-.7.5-1.05.25M6.3 9.75c-.5.15-1-.15-1.15-.6c-.15-.5.15-1 .6-1.15c3.55-1.05 9.4-.85 13.1 1.35c.45.25.6.85.35 1.3c-.25.35-.85.5-1.3.25C14.7 9 9.35 8.8 6.3 9.75"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.001 2c-5.5 0-10 4.5-10 10s4.5 10 10 10s10-4.5 10-10s-4.45-10-10-10m3.75 14.65c-2.35-1.45-5.3-1.75-8.8-.95c-.35.1-.65-.15-.75-.45c-.1-.35.15-.65.45-.75c3.8-.85 7.1-.5 9.7 1.1c.35.15.4.55.25.85c-.2.3-.55.4-.85.2m1-2.7c-2.7-1.65-6.8-2.15-9.95-1.15c-.4.1-.85-.1-.95-.5s.1-.85.5-.95c3.65-1.1 8.15-.55 11.25 1.35c.3.15.45.65.2 1s-.7.5-1.05.25M6.3 9.75c-.5.15-1-.15-1.15-.6c-.15-.5.15-1 .6-1.15c3.55-1.05 9.4-.85 13.1 1.35c.45.25.6.85.35 1.3c-.25.35-.85.5-1.3.25C14.7 9 9.35 8.8 6.3 9.75"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
x: {
|
||||
Icon: (props?: SVGProps<any>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.001 2c-5.5 0-10 4.5-10 10s4.5 10 10 10s10-4.5 10-10s-4.45-10-10-10m3.75 14.65c-2.35-1.45-5.3-1.75-8.8-.95c-.35.1-.65-.15-.75-.45c-.1-.35.15-.65.45-.75c3.8-.85 7.1-.5 9.7 1.1c.35.15.4.55.25.85c-.2.3-.55.4-.85.2m1-2.7c-2.7-1.65-6.8-2.15-9.95-1.15c-.4.1-.85-.1-.95-.5s.1-.85.5-.95c3.65-1.1 8.15-.55 11.25 1.35c.3.15.45.65.2 1s-.7.5-1.05.25M6.3 9.75c-.5.15-1-.15-1.15-.6c-.15-.5.15-1 .6-1.15c3.55-1.05 9.4-.85 13.1 1.35c.45.25.6.85.35 1.3c-.25.35-.85.5-1.3.25C14.7 9 9.35 8.8 6.3 9.75"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
stringIcon: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.001 2c-5.5 0-10 4.5-10 10s4.5 10 10 10s10-4.5 10-10s-4.45-10-10-10m3.75 14.65c-2.35-1.45-5.3-1.75-8.8-.95c-.35.1-.65-.15-.75-.45c-.1-.35.15-.65.45-.75c3.8-.85 7.1-.5 9.7 1.1c.35.15.4.55.25.85c-.2.3-.55.4-.85.2m1-2.7c-2.7-1.65-6.8-2.15-9.95-1.15c-.4.1-.85-.1-.95-.5s.1-.85.5-.95c3.65-1.1 8.15-.55 11.25 1.35c.3.15.45.65.2 1s-.7.5-1.05.25M6.3 9.75c-.5.15-1-.15-1.15-.6c-.15-.5.15-1 .6-1.15c3.55-1.05 9.4-.85 13.1 1.35c.45.25.6.85.35 1.3c-.25.35-.85.5-1.3.25C14.7 9 9.35 8.8 6.3 9.75"
|
||||
></path>
|
||||
</svg>`,
|
||||
},
|
||||
};
|
||||
12
docs/components/builder/store.ts
Normal file
12
docs/components/builder/store.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { atom } from "jotai";
|
||||
|
||||
export const optionsAtom = atom({
|
||||
email: true,
|
||||
passkey: false,
|
||||
socialProviders: ["google", "github"],
|
||||
magicLink: false,
|
||||
signUp: true,
|
||||
label: true,
|
||||
rememberMe: true,
|
||||
forgetPassword: true,
|
||||
});
|
||||
@@ -11,14 +11,8 @@ type Tab = {
|
||||
|
||||
export const AuthTabs = ({
|
||||
tabs: propTabs,
|
||||
containerClassName,
|
||||
activeTabClassName,
|
||||
tabClassName,
|
||||
}: {
|
||||
tabs: Tab[];
|
||||
containerClassName?: string;
|
||||
activeTabClassName?: string;
|
||||
tabClassName?: string;
|
||||
}) => {
|
||||
const [active, setActive] = useState<Tab>(propTabs[0]);
|
||||
const [tabs, setTabs] = useState<Tab[]>(propTabs);
|
||||
@@ -33,14 +27,11 @@ export const AuthTabs = ({
|
||||
setActive(newTabs[0]);
|
||||
};
|
||||
|
||||
const [hovering, setHovering] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-row items-center justify-start mt-0 relative no-visible-scrollbar border-x w-full border-t max-w-max bg-opacity-0",
|
||||
containerClassName,
|
||||
)}
|
||||
>
|
||||
{propTabs.map((tab, idx) => (
|
||||
@@ -49,18 +40,14 @@ export const AuthTabs = ({
|
||||
onClick={() => {
|
||||
moveSelectedTabToTop(idx);
|
||||
}}
|
||||
onMouseEnter={() => setHovering(true)}
|
||||
onMouseLeave={() => setHovering(false)}
|
||||
className={cn(
|
||||
"relative px-4 py-2 rounded-full opacity-80 hover:opacity-100",
|
||||
tabClassName,
|
||||
)}
|
||||
>
|
||||
{active.value === tab.value && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0 bg-gray-200 dark:bg-zinc-900/90 opacity-100",
|
||||
activeTabClassName,
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { Icons } from "../icons";
|
||||
import { Cover } from "../ui/cover";
|
||||
import { PulicBetaBadge } from "../beta/badge";
|
||||
import { Builder } from "../buidler";
|
||||
import { Builder } from "../builder";
|
||||
|
||||
const tabs: { name: "auth.ts" | "client.ts"; code: string }[] = [
|
||||
{
|
||||
|
||||
@@ -64,12 +64,15 @@
|
||||
"fumadocs-ui": "14.0.2",
|
||||
"geist": "^1.3.1",
|
||||
"input-otp": "^1.2.4",
|
||||
"jotai": "^2.10.3",
|
||||
"js-beautify": "^1.15.1",
|
||||
"lucide-react": "^0.435.0",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"motion": "^10.18.0",
|
||||
"next": "^15.0.1",
|
||||
"next-themes": "^0.3.0",
|
||||
"oslo": "^1.2.1",
|
||||
"prettier": "^3.3.3",
|
||||
"prism-react-renderer": "^2.4.0",
|
||||
"react": "^18.3.1",
|
||||
"react-codesandboxer": "^3.1.5",
|
||||
@@ -89,6 +92,7 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-beautify": "^1.14.3",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "22.3.0",
|
||||
"@types/react": "^18.3.3",
|
||||
|
||||
@@ -150,6 +150,16 @@ export default {
|
||||
transform: "translate(calc(-50% - 0.5rem))",
|
||||
},
|
||||
},
|
||||
spotlight: {
|
||||
"0%": {
|
||||
opacity: 0,
|
||||
transform: "translate(-72%, -62%) scale(0.5)",
|
||||
},
|
||||
"100%": {
|
||||
opacity: 1,
|
||||
transform: "translate(-50%,-40%) scale(1)",
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
@@ -160,6 +170,7 @@ export default {
|
||||
"hrtl-scroll": "hrtl-scroll var(--anime-duration,10s) linear infinite",
|
||||
"hrtl-scroll-reverse":
|
||||
"hrtl-scroll-reverse var(--anime-duration,10s) linear infinite",
|
||||
spotlight: "spotlight 2s ease .75s 1 forwards",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -490,6 +490,12 @@ importers:
|
||||
input-otp:
|
||||
specifier: ^1.2.4
|
||||
version: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
jotai:
|
||||
specifier: ^2.10.3
|
||||
version: 2.10.3(@types/react@18.3.12)(react@18.3.1)
|
||||
js-beautify:
|
||||
specifier: ^1.15.1
|
||||
version: 1.15.1
|
||||
lucide-react:
|
||||
specifier: ^0.435.0
|
||||
version: 0.435.0(react@18.3.1)
|
||||
@@ -508,6 +514,9 @@ importers:
|
||||
oslo:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
prettier:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
prism-react-renderer:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0(react@18.3.1)
|
||||
@@ -560,6 +569,9 @@ importers:
|
||||
specifier: ^3.23.8
|
||||
version: 3.23.8
|
||||
devDependencies:
|
||||
'@types/js-beautify':
|
||||
specifier: ^1.14.3
|
||||
version: 1.14.3
|
||||
'@types/mdx':
|
||||
specifier: ^2.0.13
|
||||
version: 2.0.13
|
||||
@@ -6769,6 +6781,9 @@ packages:
|
||||
'@types/jest@29.5.14':
|
||||
resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
|
||||
|
||||
'@types/js-beautify@1.14.3':
|
||||
resolution: {integrity: sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==}
|
||||
|
||||
'@types/json-schema@7.0.15':
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
|
||||
@@ -11509,6 +11524,18 @@ packages:
|
||||
jose@5.9.6:
|
||||
resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==}
|
||||
|
||||
jotai@2.10.3:
|
||||
resolution: {integrity: sha512-Nnf4IwrLhNfuz2JOQLI0V/AgwcpxvVy8Ec8PidIIDeRi4KCFpwTFIpHAAcU+yCgnw/oASYElq9UY0YdUUegsSA==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=17.0.0'
|
||||
react: '>=17.0.0'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
|
||||
joycon@3.1.1:
|
||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -23304,6 +23331,8 @@ snapshots:
|
||||
expect: 29.7.0
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@types/js-beautify@1.14.3': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/json5@0.0.29': {}
|
||||
@@ -29683,6 +29712,11 @@ snapshots:
|
||||
|
||||
jose@5.9.6: {}
|
||||
|
||||
jotai@2.10.3(@types/react@18.3.12)(react@18.3.1):
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.12
|
||||
react: 18.3.1
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
js-base64@3.7.7: {}
|
||||
|
||||
Reference in New Issue
Block a user