Skip to content

Commit effae67

Browse files
Further cleanup
1 parent 17d8db5 commit effae67

File tree

9 files changed

+325
-187
lines changed

9 files changed

+325
-187
lines changed

packages/web/src/app/[domain]/connections/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default function Layout({
1111
return (
1212
<div className="min-h-screen flex flex-col">
1313
<NavigationMenu domain={domain} />
14-
<main className="flex-grow flex justify-center p-4 bg-[#fafafa] dark:bg-background relative">
14+
<main className="flex-grow flex justify-center p-4 bg-backgroundSecondary relative">
1515
<div className="w-full max-w-6xl rounded-lg p-6">{children}</div>
1616
</main>
1717
</div>

packages/web/src/app/globals.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@layer base {
66
:root {
77
--background: 0 0% 100%;
8+
--background-secondary: 0, 0%, 98%;
89
--foreground: 222.2 84% 4.9%;
910
--card: 0 0% 100%;
1011
--card-foreground: 222.2 84% 4.9%;
@@ -42,6 +43,7 @@
4243

4344
.dark {
4445
--background: 222.2 84% 4.9%;
46+
--background-secondary: 222.2 84% 4.9%;
4547
--foreground: 210 40% 98%;
4648
--card: 222.2 84% 4.9%;
4749
--card-foreground: 210 40% 98%;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use client';
2+
3+
import { Button } from "@/components/ui/button";
4+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
5+
import { Input } from "@/components/ui/input";
6+
import { useForm } from "react-hook-form";
7+
import { zodResolver } from "@hookform/resolvers/zod";
8+
import { z } from "zod";
9+
import { signIn } from "next-auth/react";
10+
import { verifyCredentialsRequestSchema } from "@/lib/schemas";
11+
12+
interface CredentialsFormProps {
13+
callbackUrl?: string;
14+
}
15+
16+
export const CredentialsForm = ({ callbackUrl }: CredentialsFormProps) => {
17+
const form = useForm<z.infer<typeof verifyCredentialsRequestSchema>>({
18+
resolver: zodResolver(verifyCredentialsRequestSchema),
19+
defaultValues: {
20+
email: "",
21+
password: "",
22+
},
23+
});
24+
25+
const onSubmit = (values: z.infer<typeof verifyCredentialsRequestSchema>) => {
26+
signIn("credentials", {
27+
email: values.email,
28+
password: values.password,
29+
redirectTo: callbackUrl ?? "/"
30+
});
31+
}
32+
33+
return (
34+
<Form {...form}>
35+
<form
36+
onSubmit={form.handleSubmit(onSubmit)}
37+
className="w-full"
38+
>
39+
<FormField
40+
control={form.control}
41+
name="email"
42+
render={({ field }) => (
43+
<FormItem className="mb-4">
44+
<FormLabel>Email</FormLabel>
45+
<FormControl>
46+
<Input placeholder="[email protected]" {...field} />
47+
</FormControl>
48+
<FormMessage />
49+
</FormItem>
50+
)}
51+
/>
52+
<FormField
53+
control={form.control}
54+
name="password"
55+
render={({ field }) => (
56+
<FormItem className="mb-8">
57+
<FormLabel>Password</FormLabel>
58+
<FormControl>
59+
<Input type="password" {...field} />
60+
</FormControl>
61+
<FormMessage />
62+
</FormItem>
63+
)}
64+
/>
65+
<Button
66+
type="submit"
67+
className="w-full"
68+
variant="outline"
69+
>
70+
Sign in with credentials
71+
</Button>
72+
</form>
73+
</Form>
74+
);
75+
}
Lines changed: 98 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,29 @@
11
'use client';
22

33
import { Button } from "@/components/ui/button";
4-
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
5-
import { Input } from "@/components/ui/input";
6-
import { useForm } from "react-hook-form";
7-
import { zodResolver } from "@hookform/resolvers/zod";
8-
import { z } from "zod";
94
import logoDark from "@/public/sb_logo_dark_large.png";
105
import logoLight from "@/public/sb_logo_light_large.png";
11-
import githubLogo from "@/public/github.svg";
126
import googleLogo from "@/public/google.svg";
137
import Image from "next/image";
148
import { signIn } from "next-auth/react";
15-
import { useCallback, useMemo } from "react";
16-
import { verifyCredentialsRequestSchema } from "@/lib/schemas";
9+
import { Fragment, useCallback, useMemo } from "react";
10+
import { Card } from "@/components/ui/card";
11+
import { cn, getCodeHostIcon } from "@/lib/utils";
12+
import { MagicLinkForm } from "./magicLinkForm";
13+
import { CredentialsForm } from "./credentialsForm";
1714

1815
interface LoginFormProps {
1916
callbackUrl?: string;
2017
error?: string;
21-
}
22-
23-
const magicLinkSchema = z.object({
24-
email: z.string().email(),
25-
})
26-
27-
export const LoginForm = ({ callbackUrl, error }: LoginFormProps) => {
28-
const emailPasswordForm = useForm<z.infer<typeof verifyCredentialsRequestSchema>>({
29-
resolver: zodResolver(verifyCredentialsRequestSchema),
30-
defaultValues: {
31-
email: "",
32-
password: "",
33-
},
34-
});
35-
36-
const magicLinkForm = useForm<z.infer<typeof magicLinkSchema>>({
37-
resolver: zodResolver(magicLinkSchema),
38-
defaultValues: {
39-
email: "",
40-
},
41-
});
42-
43-
const onSignInWithEmailPassword = (values: z.infer<typeof verifyCredentialsRequestSchema>) => {
44-
signIn("credentials", {
45-
email: values.email,
46-
password: values.password,
47-
redirectTo: callbackUrl ?? "/"
48-
});
49-
}
50-
51-
const onSignInWithMagicLink = (values: z.infer<typeof magicLinkSchema>) => {
52-
signIn("nodemailer", { email: values.email, redirectTo: callbackUrl ?? "/" });
18+
enabledMethods: {
19+
github: boolean;
20+
google: boolean;
21+
magicLink: boolean;
22+
credentials: boolean;
5323
}
24+
}
5425

26+
export const LoginForm = ({ callbackUrl, error, enabledMethods }: LoginFormProps) => {
5527
const onSignInWithOauth = useCallback((provider: string) => {
5628
signIn(provider, { redirectTo: callbackUrl ?? "/" });
5729
}, [callbackUrl]);
@@ -71,117 +43,107 @@ export const LoginForm = ({ callbackUrl, error }: LoginFormProps) => {
7143
}, [error]);
7244

7345
return (
74-
<div className="flex flex-col items-center border p-16 rounded-lg gap-6 w-[500px]">
75-
{error && (
76-
<div className="text-sm text-destructive text-center text-wrap border p-2 rounded-md border-destructive">
77-
{errorMessage}
46+
<div className="flex flex-col items-center justify-center">
47+
<div className="mb-6 flex flex-col items-center">
48+
<div>
49+
<Image
50+
src={logoDark}
51+
className="h-16 w-auto hidden dark:block"
52+
alt={"Sourcebot logo"}
53+
priority={true}
54+
/>
55+
<Image
56+
src={logoLight}
57+
className="h-16 w-auto block dark:hidden"
58+
alt={"Sourcebot logo"}
59+
priority={true}
60+
/>
7861
</div>
79-
)}
80-
<div>
81-
<Image
82-
src={logoDark}
83-
className="h-16 w-auto hidden dark:block"
84-
alt={"Sourcebot logo"}
85-
priority={true}
86-
/>
87-
<Image
88-
src={logoLight}
89-
className="h-16 w-auto block dark:hidden"
90-
alt={"Sourcebot logo"}
91-
priority={true}
92-
/>
62+
<h2 className="text-lg font-bold">Sign in to your account</h2>
9363
</div>
94-
<ProviderButton
95-
name="GitHub"
96-
logo={githubLogo}
97-
onClick={() => {
98-
onSignInWithOauth("github")
99-
}}
100-
/>
101-
<ProviderButton
102-
name="Google"
103-
logo={googleLogo}
104-
onClick={() => {
105-
onSignInWithOauth("google")
106-
}}
107-
/>
108-
<div className="flex items-center w-full gap-4">
109-
<div className="h-[1px] flex-1 bg-border" />
110-
<span className="text-muted-foreground text-sm">or</span>
111-
<div className="h-[1px] flex-1 bg-border" />
112-
</div>
113-
<div className="flex flex-col w-60">
114-
<Form {...magicLinkForm}>
115-
<form onSubmit={magicLinkForm.handleSubmit(onSignInWithMagicLink)}>
116-
<FormField
117-
control={magicLinkForm.control}
118-
name="email"
119-
render={({ field }) => (
120-
<FormItem className="mb-4">
121-
<FormLabel>Email</FormLabel>
122-
<FormControl>
123-
<Input placeholder="[email protected]" {...field} />
124-
</FormControl>
125-
<FormMessage />
126-
</FormItem>
127-
)}
128-
/>
129-
<Button type="submit" className="w-full">
130-
Sign in
131-
</Button>
132-
</form>
133-
</Form>
134-
<Form {...emailPasswordForm}>
135-
<form onSubmit={emailPasswordForm.handleSubmit(onSignInWithEmailPassword)}>
136-
<FormField
137-
control={emailPasswordForm.control}
138-
name="email"
139-
render={({ field }) => (
140-
<FormItem className="mb-4">
141-
<FormLabel>Email</FormLabel>
142-
<FormControl>
143-
<Input placeholder="[email protected]" {...field} />
144-
</FormControl>
145-
<FormMessage />
146-
</FormItem>
147-
)}
148-
/>
149-
<FormField
150-
control={emailPasswordForm.control}
151-
name="password"
152-
render={({ field }) => (
153-
<FormItem className="mb-8">
154-
<FormLabel>Password</FormLabel>
155-
<FormControl>
156-
<Input type="password" {...field} />
157-
</FormControl>
158-
<FormMessage />
159-
</FormItem>
160-
)}
161-
/>
162-
<Button type="submit" className="w-full">
163-
Sign in
164-
</Button>
165-
</form>
166-
</Form>
167-
</div>
168-
</div >
64+
<Card className="flex flex-col items-center border p-12 rounded-lg gap-6 w-[500px] bg-background">
65+
{error && (
66+
<div className="text-sm text-destructive text-center text-wrap border p-2 rounded-md border-destructive">
67+
{errorMessage}
68+
</div>
69+
)}
70+
<DividerSet
71+
children={[
72+
...(enabledMethods.github || enabledMethods.google ? [
73+
<>
74+
{enabledMethods.github && (
75+
<ProviderButton
76+
name="GitHub"
77+
logo={getCodeHostIcon("github")!}
78+
onClick={() => {
79+
onSignInWithOauth("github")
80+
}}
81+
/>
82+
)}
83+
{enabledMethods.google && (
84+
<ProviderButton
85+
name="Google"
86+
logo={{ src: googleLogo }}
87+
onClick={() => {
88+
onSignInWithOauth("google")
89+
}}
90+
/>
91+
)}
92+
</>
93+
] : []),
94+
...(enabledMethods.magicLink ? [
95+
<MagicLinkForm callbackUrl={callbackUrl} />
96+
] : []),
97+
...(enabledMethods.credentials ? [
98+
<CredentialsForm callbackUrl={callbackUrl} />
99+
] : [])
100+
]}
101+
/>
102+
</Card>
103+
</div>
169104
)
170105
}
171106

172107
const ProviderButton = ({
173108
name,
174109
logo,
175110
onClick,
111+
className,
176112
}: {
177113
name: string;
178-
logo: string;
114+
logo: { src: string, className?: string };
179115
onClick: () => void;
116+
className?: string;
180117
}) => {
181118
return (
182-
<Button onClick={onClick}>
183-
{logo && <Image src={logo} alt={name} className="w-5 h-5 invert dark:invert-0 mr-2" />}
119+
<Button
120+
onClick={onClick}
121+
className={cn("w-full", className)}
122+
variant="outline"
123+
>
124+
{logo && <Image src={logo.src} alt={name} className={cn("w-5 h-5 mr-2", logo.className)} />}
184125
Sign in with {name}
185126
</Button>
186127
)
128+
}
129+
130+
const DividerSet = ({ children }: { children: React.ReactNode[] }) => {
131+
return children.map((child, index) => {
132+
return (
133+
<Fragment key={index}>
134+
{child}
135+
{index < children.length - 1 && <Divider />}
136+
</Fragment>
137+
)
138+
})
139+
}
140+
141+
const Divider = ({ className }: { className?: string }) => {
142+
return (
143+
<div className={cn("flex items-center w-full gap-4", className)}>
144+
<div className="h-[1px] flex-1 bg-border" />
145+
<span className="text-muted-foreground text-sm">or</span>
146+
<div className="h-[1px] flex-1 bg-border" />
147+
</div>
148+
)
187149
}

0 commit comments

Comments
 (0)