diff --git a/package-lock.json b/package-lock.json
index 37b6322..4b0c3ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,8 @@
"next": "14.2.5",
"prettier": "^3.3.3",
"react": "^18",
- "react-dom": "^18"
+ "react-dom": "^18",
+ "react-hot-toast": "^2.4.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
@@ -1406,7 +1407,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -2742,6 +2742,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/goober": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz",
+ "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -4574,6 +4582,21 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-hot-toast": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz",
+ "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==",
+ "dependencies": {
+ "goober": "^2.1.10"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
diff --git a/package.json b/package.json
index ebc4501..22a7a3d 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,8 @@
"next": "14.2.5",
"prettier": "^3.3.3",
"react": "^18",
- "react-dom": "^18"
+ "react-dom": "^18",
+ "react-hot-toast": "^2.4.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
diff --git a/src/app/api/authenticated/route.ts b/src/app/api/authenticated/route.ts
new file mode 100644
index 0000000..eb0cce3
--- /dev/null
+++ b/src/app/api/authenticated/route.ts
@@ -0,0 +1,14 @@
+import { NextRequest, NextResponse } from "next/server";
+import { getDataToken } from "@/utils/getDataToken";
+
+export async function GET(request: NextRequest) {
+ try {
+ await getDataToken(request);
+ return NextResponse.json({ authenticated: true });
+ } catch (error: any) {
+ return NextResponse.json(
+ { error: error.message, status: 500 },
+ { status: 500 }
+ );
+ }
+}
diff --git a/src/app/api/register/route.ts b/src/app/api/register/route.ts
index f246ad6..3e114f1 100644
--- a/src/app/api/register/route.ts
+++ b/src/app/api/register/route.ts
@@ -13,7 +13,10 @@ export async function POST(request: NextRequest, response: NextResponse) {
password: encryptedPassword,
},
});
- return NextResponse.json({ message: "User created" }, { status: 201 });
+ return NextResponse.json(
+ { message: "User created", success: true },
+ { status: 201 }
+ );
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 400 });
}
diff --git a/src/app/globals.css b/src/app/globals.css
index 875c01e..062df34 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -2,18 +2,26 @@
@tailwind components;
@tailwind utilities;
-:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
+@layer base {
+ html {
+ --color-primary: #4285f4;
+ --color-secondary: #34a853;
+ --color-buttons: #fbbc05;
+ --color-typography: #ea4335;
+ --foreground-rgb: 0, 0, 0;
+ --background-start-rgb: 214, 219, 220;
+ --background-end-rgb: 255, 255, 255;
+ }
}
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
+html[data-theme="dark"] {
+ --color-primary: #f98866;
+ --color-secondary: #80bd9e;
+ --color-buttons: #89da59;
+ --color-typography: #ff320e;
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
}
body {
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 3314e47..707d957 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
+import { Toaster } from "react-hot-toast";
const inter = Inter({ subsets: ["latin"] });
@@ -16,7 +17,10 @@ export default function RootLayout({
}>) {
return (
-
{children}
+
+
+ {children}
+
);
}
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index 1ccffe1..5f1c7f3 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,79 +1,58 @@
-'use client';
+"use client";
+import Button from "@/components/Button";
+import TextInput from "@/components/TextInput";
import APICaller from "@/utils/APICaller";
import { useRouter } from "next/navigation";
import { useState } from "react";
export default function Login() {
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
- const [name, setName] = useState("");
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
- const router = useRouter();
+ const router = useRouter();
- async function handleLogin() {
- if (!email || !password) {
- return;
- }
- try {
- const requestData = { email, password };
- const response = await APICaller("/api/login", "POST", requestData);
- if (response.success) {
- localStorage.setItem("token", response.token);
- }
- } catch (error) {
- console.error("Erro ao fazer login:", error);
- }
+ async function handleLogin() {
+ if (!email || !password) {
+ return;
}
-
- async function handleRegister() {
- if (!email || !password || !name) {
- return;
- }
- try {
- const requestData = { email, password, name };
- const response = await APICaller("/api/register", "POST", requestData);
- if (response.success) {
- localStorage.setItem("token", response.token);
- router.push("/dashboard");
- }
- } catch (error) {
- console.error("Error ao registrar:", error);
- }
- };
-
- async function helloWorld() {
- try {
- const response = await APICaller("/api/helloworld", "GET");
- } catch (error) {
- console.error("Error:", error);
- }
+ try {
+ const requestData = { email, password };
+ const response = await APICaller("/api/login", "POST", requestData);
+ if (response.success) {
+ localStorage.setItem("token", response.token);
+ }
+ } catch (error) {
+ console.error("Erro ao fazer login:", error);
}
-
- return (
-
-
setEmail(e.target.value)}
- />
-
setName(e.target.value)}
- />
-
setPassword(e.target.value)}
- />
-
-
-
+ }
+
+ return (
+
+
+
+
+
+
+
- )
-
-}
\ No newline at end of file
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 2acfd44..23e0de8 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,113 +1,30 @@
-import Image from "next/image";
+"use client";
-export default function Home() {
- return (
-
-
-
- Get started by editing
- src/app/page.tsx
-
-
-
-
-
-
-
+import Button from "@/components/Button";
+import APICaller from "@/utils/APICaller";
+import { useAuthRedirect } from "@/utils/isAuthenticated";
-
-
+ return (
+
+ Home page
+ {isAuth && (
+
+ )}
+
);
}
diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx
new file mode 100644
index 0000000..770997b
--- /dev/null
+++ b/src/app/register/page.tsx
@@ -0,0 +1,81 @@
+"use client";
+
+import Button from "@/components/Button";
+import TextInput from "@/components/TextInput";
+import APICaller from "@/utils/APICaller";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+import toast from "react-hot-toast";
+
+export default function Register() {
+ const [email, setEmail] = useState("");
+ const [name, setName] = useState("");
+ const [password, setPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+
+ const router = useRouter();
+
+ async function handleRegister() {
+ if (!email || !password || !name) {
+ return;
+ }
+ try {
+ const requestData = { email, password, name };
+ const response = await APICaller("/api/register", "POST", requestData);
+ if (response.success) {
+ toast.success("Registrado com sucesso!");
+ router.replace("/login");
+ }
+ } catch (error) {
+ toast.error("Erro ao registrar!");
+ console.error("Error ao registrar:", error);
+ }
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
new file mode 100644
index 0000000..381c2cb
--- /dev/null
+++ b/src/components/Button.tsx
@@ -0,0 +1,19 @@
+interface ButtonProps {
+ text: string;
+ onClick: () => void;
+ style: "primary" | "secondary" | "danger" | "outline";
+}
+export default function Button({ text, onClick, style }: ButtonProps) {
+ return (
+
+ );
+}
diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx
new file mode 100644
index 0000000..e5879cf
--- /dev/null
+++ b/src/components/TextInput.tsx
@@ -0,0 +1,28 @@
+interface TextInputProps {
+ value: string;
+ setName: (name: string) => void;
+ type: "text" | "email" | "password";
+ label?: string;
+ placeholder?: string;
+}
+
+export default function TextInput({
+ value,
+ setName,
+ type,
+ label,
+ placeholder,
+}: TextInputProps) {
+ return (
+
+ {label && }
+ setName(e.target.value)}
+ className="text-sm rounded-lg block w-full p-2.5 bg-gray-200 border-blue-500 border-2 placeholder-gray-400 focus:border-blue-800 outline-none "
+ placeholder={placeholder}
+ />
+
+ );
+}
diff --git a/src/utils/APICaller.ts b/src/utils/APICaller.ts
index 1e43ad6..22e38be 100644
--- a/src/utils/APICaller.ts
+++ b/src/utils/APICaller.ts
@@ -1,7 +1,7 @@
export default function APICaller(
endpointURL: string,
methodType: string,
- body?: any,
+ body?: any
) {
const token = localStorage.getItem("token");
if (token) {
@@ -26,7 +26,7 @@ export default function APICaller(
"Content-Type": "application/json",
},
body: JSON.stringify(body),
- }),
+ })
);
}
@@ -35,8 +35,5 @@ function getData(promise: Promise
) {
.then((response) => response.json())
.then((data) => {
return data;
- })
- .catch((error) => {
- console.error("Error:", error);
});
}
diff --git a/src/utils/helper.ts b/src/utils/helper.ts
new file mode 100644
index 0000000..e5f014e
--- /dev/null
+++ b/src/utils/helper.ts
@@ -0,0 +1,3 @@
+export const changeTheme = (theme: string) => {
+ document.querySelector("html")?.setAttribute("data-theme", theme);
+};
diff --git a/src/utils/isAuthenticated.ts b/src/utils/isAuthenticated.ts
new file mode 100644
index 0000000..d1b8c44
--- /dev/null
+++ b/src/utils/isAuthenticated.ts
@@ -0,0 +1,30 @@
+import { useRouter } from "next/navigation";
+import APICaller from "./APICaller";
+import { useEffect, useState } from "react";
+
+async function isAuthenticated() {
+ try {
+ const response = await APICaller("/api/authenticated", "GET");
+ return response.authenticated;
+ } catch (error) {
+ console.error("Error:", error);
+ }
+}
+
+export function useAuthRedirect() {
+ const [isAuth, setIsAuth] = useState(false);
+ const router = useRouter();
+
+ const checkAuth = async () => {
+ const auth = await isAuthenticated();
+ if (!auth) {
+ router.push("/login");
+ }
+ setIsAuth(true);
+ };
+ useEffect(() => {
+ checkAuth();
+ });
+
+ return isAuth;
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index e9a0944..c6796f7 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -13,6 +13,12 @@ const config: Config = {
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
+ colors: {
+ primary: "var(--color-primary)",
+ secondary: "var(--color-secondary)",
+ buttons: "var(--color-buttons)",
+ typography: "var(--color-typography)",
+ },
},
},
plugins: [],