Skip to content

Commit

Permalink
feat: implement auth
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonioErdeljac committed Mar 4, 2023
1 parent b269a5e commit 2b81e71
Show file tree
Hide file tree
Showing 13 changed files with 1,496 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel
Expand Down
6 changes: 5 additions & 1 deletion components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ interface ButtonProps {
fullWidth?: boolean;
large?: boolean;
onClick: () => void;
disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({ label, secondary, fullWidth, onClick, large }) => {
const Button: React.FC<ButtonProps> = ({ label, secondary, fullWidth, onClick, large, disabled }) => {
return (
<button
disabled={disabled}
onClick={onClick}
className={`
disabled:opacity-70
disabled:cursor-not-allowed
rounded-full
font-semibold
hover:bg-opacity-90
Expand Down
11 changes: 9 additions & 2 deletions components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ interface InputProps {
placeholder?: string;
value?: string;
type?: string;
disabled?: boolean;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

const Input: React.FC<InputProps> = ({ placeholder, value, type = "text" }) => {
const Input: React.FC<InputProps> = ({ placeholder, value, type = "text", onChange, disabled }) => {
return (
<input
<input
disabled={disabled}
onChange={onChange}
value={value}
placeholder={placeholder}
type={type}
Expand All @@ -22,6 +26,9 @@ const Input: React.FC<InputProps> = ({ placeholder, value, type = "text" }) => {
focus:border-sky-500
focus:border-2
transition
disabled:bg-neutral-900
disabled:opacity-70
disabled:cursor-not-allowed
"
/>
);
Expand Down
17 changes: 13 additions & 4 deletions components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ interface ModalProps {
body?: React.ReactElement;
footer?: React.ReactElement;
actionLabel: string;
disabled?: boolean;
}

const Modal: React.FC<ModalProps> = ({ isOpen, onClose, onSubmit, title, body, actionLabel, footer }) => {
const Modal: React.FC<ModalProps> = ({ isOpen, onClose, onSubmit, title, body, actionLabel, footer, disabled }) => {
const handleClose = useCallback(() => {
if (disabled) {
return;
}

onClose();
}, [onClose]);
}, [onClose, disabled]);

const handleSubmit = useCallback(() => {
if (disabled) {
return;
}

onSubmit();
}, [onSubmit]);
}, [onSubmit, disabled]);

if (!isOpen) {
return null;
Expand Down Expand Up @@ -92,7 +101,7 @@ const Modal: React.FC<ModalProps> = ({ isOpen, onClose, onSubmit, title, body, a
</div>
{/*footer*/}
<div className="flex flex-col gap-2 p-10">
<Button label={actionLabel} secondary fullWidth large onClick={handleSubmit} />
<Button disabled={disabled} label={actionLabel} secondary fullWidth large onClick={handleSubmit} />
{footer}
</div>
</div>
Expand Down
74 changes: 52 additions & 22 deletions components/modals/LoginModal.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
import { signIn } from "next-auth/react";
import { useCallback, useState } from "react";
import { toast } from "react-hot-toast";

import useLoginModal from "@/hooks/useLoginModal";
import useRegisterModal from "@/hooks/useRegisterModal";
import { useCallback } from "react";

import Input from "../Input";
import Modal from "../Modal";

const ModalBody = () => {
return (
<div className="flex flex-col gap-4">
<Input placeholder="Email" />
<Input placeholder="Password" type="password" />
</div>
)
}

const ModalFooter = () => {
const LoginModal = () => {
const loginModal = useLoginModal();
const registerModal = useRegisterModal();

const onClick = useCallback(() => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isLoading, setIsLoading] = useState(false);

const onSubmit = useCallback(async () => {
try {
setIsLoading(true);

await signIn('credentials', {
email,
password
});

toast.success('Logged in');
} catch (error) {
toast.error('Something went wrong');
} finally {
setIsLoading(false);
}
}, [email, password]);

const onToggle = useCallback(() => {
loginModal.onClose();
registerModal.onOpen();
}, [loginModal, registerModal]);
}, [loginModal, registerModal])

return (
const bodyContent = (
<div className="flex flex-col gap-4">
<Input
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
value={email}
disabled={isLoading}
/>
<Input
placeholder="Password"
type="password"
onChange={(e) => setPassword(e.target.value)}
value={password}
disabled={isLoading}
/>
</div>
)

const footerContent = (
<div className="text-neutral-400 text-center mt-4">
<p>First time using Twitter?
<span
onClick={onClick}
onClick={onToggle}
className="
text-white
cursor-pointer
Expand All @@ -37,20 +70,17 @@ const ModalFooter = () => {
</p>
</div>
)
}

const LoginModal = () => {
const loginModal = useLoginModal();

return (
<Modal
disabled={isLoading}
isOpen={loginModal.isOpen}
title="Login"
actionLabel="Sign in"
onClose={loginModal.onClose}
onSubmit={loginModal.onClose}
body={<ModalBody />}
footer={<ModalFooter />}
onSubmit={onSubmit}
body={bodyContent}
footer={footerContent}
/>
);
}
Expand Down
106 changes: 82 additions & 24 deletions components/modals/RegisterModal.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,97 @@
import { useCallback } from "react";
import axios from "axios";
import { toast } from "react-hot-toast";
import { useCallback, useState } from "react";
import { signIn } from 'next-auth/react';

import useLoginModal from "@/hooks/useLoginModal";
import useRegisterModal from "@/hooks/useRegisterModal";

import Input from "../Input";
import Modal from "../Modal";

const ModalBody = () => {
return (
<div className="flex flex-col gap-4">
<Input placeholder="Email" />
<Input placeholder="Name" />
<Input placeholder="Username" />
<Input placeholder="Password" type="password" />
</div>
)
}

const ModalFooter = () => {
const RegisterModal = () => {
const loginModal = useLoginModal();
const registerModal = useRegisterModal();

const onClick = useCallback(() => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [username, setUsername] = useState('');
const [name, setName] = useState('');

const [isLoading, setIsLoading] = useState(false);

const onToggle = useCallback(() => {
if (isLoading) {
return;
}

registerModal.onClose();
loginModal.onOpen();
}, [loginModal, registerModal]);
}, [loginModal, registerModal, isLoading]);

return (
const onSubmit = useCallback(async () => {
try {
setIsLoading(true);

await axios.post('/api/register', {
email,
password,
username,
name,
});

setIsLoading(false)

toast.success('Account created.');

signIn('credentials', {
email,
password
});

registerModal.onClose();
} catch (error) {
toast.error('Something went wrong');
} finally {
setIsLoading(false);
}
}, [email, password, registerModal, username, name]);

const bodyContent = (
<div className="flex flex-col gap-4">
<Input
disabled={isLoading}
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
disabled={isLoading}
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Input
disabled={isLoading}
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<Input
disabled={isLoading}
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
)

const footerContent = (
<div className="text-neutral-400 text-center mt-4">
<p>Already have an account?
<span
onClick={onClick}
onClick={onToggle}
className="
text-white
cursor-pointer
Expand All @@ -40,20 +101,17 @@ const ModalFooter = () => {
</p>
</div>
)
}

const RegisterModal = () => {
const registerModal = useRegisterModal();

return (
<Modal
disabled={isLoading}
isOpen={registerModal.isOpen}
title="Create an account"
actionLabel="Register"
onClose={registerModal.onClose}
onSubmit={registerModal.onClose}
body={<ModalBody />}
footer={<ModalFooter />}
onSubmit={onSubmit}
body={bodyContent}
footer={footerContent}
/>
);
}
Expand Down
10 changes: 10 additions & 0 deletions libs/prismadb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PrismaClient } from "@prisma/client"

declare global {
var prisma: PrismaClient | undefined
}

const client = globalThis.prisma || new PrismaClient()
if (process.env.NODE_ENV !== "production") globalThis.prisma = client

export default client
Loading

0 comments on commit 2b81e71

Please sign in to comment.