diff --git a/frontend/index.html b/frontend/index.html index 9fe03f3..308aab1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,6 +1,9 @@ - + + + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 31444a9..c071cae 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,8 +8,14 @@ "name": "devdecola", "version": "0.0.0", "dependencies": { + "@hookform/resolvers": "^5.1.1", + "lucide-react": "^0.525.0", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-hook-form": "^7.60.0", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.3", + "zod": "^3.25.75" }, "devDependencies": { "@eslint/js": "^9.25.0", @@ -913,6 +919,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hookform/resolvers": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz", + "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1357,6 +1375,12 @@ "win32" ] }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1936,6 +1960,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2626,6 +2659,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2897,6 +2939,31 @@ "react": "^19.1.0" } }, + "node_modules/react-hook-form": { + "version": "7.60.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.60.0.tgz", + "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -2907,6 +2974,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz", + "integrity": "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz", + "integrity": "sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3008,6 +3113,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3377,6 +3488,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.75", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", + "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/frontend/package.json b/frontend/package.json index fc33b59..d39c06c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,14 @@ "preview": "vite preview" }, "dependencies": { + "@hookform/resolvers": "^5.1.1", + "lucide-react": "^0.525.0", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-hook-form": "^7.60.0", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.3", + "zod": "^3.25.75" }, "devDependencies": { "@eslint/js": "^9.25.0", diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 624f4e9..0000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import './App.css' -import { NavBar } from './components/NavBar' - -function App() { - - return ( - <> - -
-

Welcome to DEVdecola

-

Your one-stop solution for all things development!

-
- - ) -} - -export default App diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx new file mode 100644 index 0000000..f99c3f7 --- /dev/null +++ b/frontend/src/app/App.tsx @@ -0,0 +1,16 @@ +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { Home } from "@/pages/home/index.tsx"; +import { Login } from "@/pages/login"; + +function App() { + return ( + + + } /> + } /> + + + ); +} + +export default App; diff --git a/frontend/src/app/hooks/userIsMobile.ts b/frontend/src/app/hooks/userIsMobile.ts new file mode 100644 index 0000000..5d2a8c3 --- /dev/null +++ b/frontend/src/app/hooks/userIsMobile.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; + +export function userIsMobile(breakpoint = 768): boolean { + const [isMobile, setIsMobile] = useState( + typeof window !== "undefined" ? window.innerWidth < breakpoint : false + ); + + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < breakpoint); + }; + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, [breakpoint]); + + return isMobile; +} diff --git a/frontend/src/app/schemas/loginSchema.ts b/frontend/src/app/schemas/loginSchema.ts new file mode 100644 index 0000000..4d55d12 --- /dev/null +++ b/frontend/src/app/schemas/loginSchema.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +export const loginSchema = z.object({ + email: z.string().email("E-mail inválido"), + password: z + .string() + .min(8, "A senha deve ter no mínimo 8 caracteres") + .max(32, "A senha deve ter no máximo 32 caracteres"), +}); + +export type LoginFormData = z.infer; \ No newline at end of file diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx deleted file mode 100644 index 8b982f0..0000000 --- a/frontend/src/components/NavBar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import Styles from "./NavBar.module.css"; - -export const NavBar = () => { - - return ( - - - ); -} \ No newline at end of file diff --git a/frontend/src/components/layout/navbar/index.tsx b/frontend/src/components/layout/navbar/index.tsx new file mode 100644 index 0000000..45ef435 --- /dev/null +++ b/frontend/src/components/layout/navbar/index.tsx @@ -0,0 +1,31 @@ +import { useNavigate } from "react-router-dom"; +import Styles from "./styles.module.css"; + +export const NavBar = () => { + const navigate = useNavigate(); + + return ( + + ); +}; diff --git a/frontend/src/components/NavBar.module.css b/frontend/src/components/layout/navbar/styles.module.css similarity index 100% rename from frontend/src/components/NavBar.module.css rename to frontend/src/components/layout/navbar/styles.module.css diff --git a/frontend/src/components/ui/input/index.tsx b/frontend/src/components/ui/input/index.tsx new file mode 100644 index 0000000..b062941 --- /dev/null +++ b/frontend/src/components/ui/input/index.tsx @@ -0,0 +1,95 @@ +import { type ComponentProps, useState } from "react"; +import { Eye, EyeOff, Lock, AtSign } from "lucide-react"; +import styles from "./styles.module.css"; + +interface IInputProps extends ComponentProps<"input"> { + label: string; + iconLeft?: React.ReactNode; + state?: "default" | "error" | "warning" | "success"; + helperText?: string; +} + +export const Input = ({ + label, + iconLeft, + type = "text", + state = "default", + helperText, + ...rest +}: IInputProps) => { + const isPassword = type === "password"; + const [showPassword, setShowPassword] = useState(false); + const inputType = isPassword ? (showPassword ? "text" : "password") : type; + + const inputClassName = `${styles.input} ${ + state === "error" ? styles.input__error : "" + } ${state === "warning" ? styles.input__warning : ""} ${ + state === "success" ? styles.input__success : "" + }`; + + return ( +
+ + + {helperText && ( + + {helperText} + + )} +
+ ); +}; diff --git a/frontend/src/components/ui/input/styles.module.css b/frontend/src/components/ui/input/styles.module.css new file mode 100644 index 0000000..9020b7f --- /dev/null +++ b/frontend/src/components/ui/input/styles.module.css @@ -0,0 +1,143 @@ +.input__wrapper { + display: flex; + flex-direction: column; + gap: 4px; + width: 100%; +} + +.label { + display: flex; + flex-direction: column; + gap: 4px; + cursor: pointer; +} + +.label__text { + font-size: 0.875rem; + font-weight: 500; + color: #374151; +} + +.input__container { + position: relative; + height: 48px; + width: 100%; + box-sizing: border-box; +} + +.input { + width: 100%; + height: 100%; + padding: 0 48px; + border-radius: 4px; + font-size: 1rem; + border: 1px solid #d1d5db; + box-sizing: border-box; + transition: border-color 0.2s ease; +} + +.input:focus { + outline: none; + border-color: black; + color: black; +} + +.input__error { + border-color: #ef4444; + color: #ef4444; +} + +.input__warning { + border-color: #f59e0b; + color: #f59e0b; +} + +.input__success { + border-color: #10b981; + color: #10b981; +} + +.helper__text { + font-size: 1rem; + margin-top: 8px; + color: #6b7280; +} + +.helper__text__error { + color: #ef4444; +} + +.helper__text__warning { + color: #f59e0b; +} + +.icon { + width: 20px; + height: 20px; + color: white; +} + +/* ícone à esquerda */ +.icon__left { + position: absolute; + left: 8px; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + background-color: #d1d5db; + color: white; + display: flex; + align-items: center; + justify-content: center; + border-radius: 2px; + pointer-events: none; + transition: background-color 0.2s ease; +} + +/* Quando o container recebe foco, o ícone fica preto */ +.input__container:focus-within .icon__left { + background-color: black; +} + +.icon__left__error { + background-color: #ef4444; +} + +.icon__left__warning { + background-color: #f59e0b; +} + +/* ícone de olho à direita */ +.icon__right { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + width: 32px; + height: 32px; + background-color: transparent; + border: none; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + outline: none; +} + +/* escondido até o foco */ +.eyeToggle { + opacity: 0; + pointer-events: none; + transition: opacity 0.2s ease; +} + +/* quando o container recebe foco */ +.input__container:focus-within .eyeToggle { + opacity: 1; + pointer-events: auto; +} + +.input__container:focus-within .eyeToggle .icon { + color: black; +} diff --git a/frontend/src/components/ui/link/index.tsx b/frontend/src/components/ui/link/index.tsx new file mode 100644 index 0000000..35ae4e7 --- /dev/null +++ b/frontend/src/components/ui/link/index.tsx @@ -0,0 +1,16 @@ +import type { ComponentProps } from "react"; + +import styles from "./styles.module.css"; + +interface ILink extends ComponentProps<"a"> { + label: string; + href: string; +} + +export const Link = ({ label, href }: ILink) => { + return ( + + {label} + + ); +}; diff --git a/frontend/src/components/ui/link/styles.module.css b/frontend/src/components/ui/link/styles.module.css new file mode 100644 index 0000000..a705154 --- /dev/null +++ b/frontend/src/components/ui/link/styles.module.css @@ -0,0 +1,3 @@ +a { + color: black; +} diff --git a/frontend/src/index.css b/frontend/src/index.css index e69de29..4ce567c 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -0,0 +1,9 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + + font-size: 16px; +} + +body { font-family: 'Inter', sans-serif; } \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index bef5202..096965c 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./app/App.tsx"; -createRoot(document.getElementById('root')!).render( - - - , -) +createRoot(document.getElementById("root")!).render( + + + +); diff --git a/frontend/src/pages/home/index.tsx b/frontend/src/pages/home/index.tsx new file mode 100644 index 0000000..bcca304 --- /dev/null +++ b/frontend/src/pages/home/index.tsx @@ -0,0 +1,13 @@ +import { NavBar } from "@/components/layout/navbar"; + +export const Home = () => { + return ( + <> + +
+

Welcome to DEVdecola

+

Your one-stop solution for all things development!

+
+ + ); +}; diff --git a/frontend/src/pages/login/desktop.module.css b/frontend/src/pages/login/desktop.module.css new file mode 100644 index 0000000..3be23df --- /dev/null +++ b/frontend/src/pages/login/desktop.module.css @@ -0,0 +1,125 @@ +.global__container { + width: 100%; + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.container { + display: flex; + gap: 36px; + padding: 24px; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 8px; +} + +.content__container { + width: 363px; +} + +.logo__container { + margin-bottom: 24px; +} + +.header__container { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 16px; +} + +.header__container h1 { + font-size: 1.5rem; + font-weight: 600; +} + +.header__container h2 { + font-size: 1rem; + font-weight: 500; + color: gray; +} + +.main__container { + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 24px; +} + +.inputs__container { + display: flex; + flex-direction: column; + gap: 16px; +} + +.password__container { + display: flex; + flex-direction: column; + align-items: end; + gap: 12px; + margin-bottom: 16px; +} + +.footer__container { + display: flex; + flex-direction: column; + gap: 16px; +} +.footer__container .separator__container { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; +} + +.separator__container p { + white-space: nowrap; +} + +.separator__container hr { + flex: 1; + height: 1px; + border: none; + background: rgba(0, 0, 0, 0.15); +} + +.submitButton { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + height: 48px; + background-color: black; + color: white; + border: none; + border-radius: 4px; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s ease; + font-weight: 500; +} + +.submitButton:hover:not(:disabled) { + background-color: #333; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.submitButton:disabled { + background-color: #e0e0e0; + color: #999; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.image__container { + display: flex; + align-items: center; + justify-content: center; +} + +.image__container img { + flex: 1; + object-fit: cover; +} diff --git a/frontend/src/pages/login/index.tsx b/frontend/src/pages/login/index.tsx new file mode 100644 index 0000000..efa13cf --- /dev/null +++ b/frontend/src/pages/login/index.tsx @@ -0,0 +1,190 @@ +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { AtSign, ArrowLeft, ArrowRight, Mail } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { loginSchema, type LoginFormData } from "@/app/schemas/loginSchema"; + +import { userIsMobile } from "@/app/hooks/userIsMobile"; + +import desktop from "./desktop.module.css"; +import mobile from "./mobile.module.css"; +import { Link } from "@/components/ui/link"; +import { useNavigate } from "react-router-dom"; + +import { FcGoogle as GoogleIcon } from "react-icons/fc"; + +export const Login = () => { + const isMobile = userIsMobile(); + + const navigate = useNavigate(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(loginSchema), + mode: "onChange", + }); + + const onSubmit = (data: LoginFormData) => { + console.log("Login data:", data); + }; + + const hasErrors = Object.keys(errors).length > 0; + + const isButtonDisabled = isSubmitting || hasErrors; + + const LoginDesktop = () => ( +
+
+
+
LOGO | 5kbyte
+ +
+

Preparado para continuar sua jornada?

+

+ Entre com seu e-mail e senha para acessar sua conta + DevDeCola. +

+
+ +
+
+ } + {...register("email")} + state={errors.email ? "error" : "default"} + helperText={errors.email?.message} + autoFocus + /> + +
+ + +
+
+ + +
+ +
+
+
+

ou continue com

+
+
+ + + +

+ Novo por aqui? Crie sua conta +

+
+
+ +
+ Avatar +
+
+
+ ); + + const LoginMobile = () => ( +
+
+ +

+ Preparado para continuar sua jornada? +

+

+ Entre com seu e-mail e senha para acessar sua conta + DevDeCola. +

+
+ +
+
+
+ } + {...register("email")} + state={errors.email ? "error" : "default"} + helperText={errors.email?.message} + autoFocus + /> + +
+ + +
+
+ + +
+ +
+
+ ou continue com +
+
+ + + +
+

Novo por aqui?

+ +
+
+
+ ); + + return <>{isMobile ? : }; +}; diff --git a/frontend/src/pages/login/mobile.module.css b/frontend/src/pages/login/mobile.module.css new file mode 100644 index 0000000..781b709 --- /dev/null +++ b/frontend/src/pages/login/mobile.module.css @@ -0,0 +1,101 @@ +.mobileWrapper { + margin: 0 auto; + padding: 24px 16px; + display: flex; + flex-direction: column; + height: 100vh; + justify-content: space-between; +} +.backButton { + width: 64px; + height: 64px; + border-radius: 50%; + background: #f5f5f5; + border: none; + display: flex; + cursor: pointer; + align-items: center; + justify-content: center; + margin-bottom: 16px; +} +.title { + font-size: 1.5rem; + font-weight: 600; + margin-bottom: 4px; +} +.subtitle { + color: #888; + font-size: 1rem; + font-weight: 500; + margin-bottom: 32px; +} + +.inputs__container { + display: flex; + flex-direction: column; + gap: 16px; +} + +.password__container { + display: flex; + flex-direction: column; + align-items: end; + gap: 8px; + margin-bottom: 24px; +} + +.forgotPassword { + text-align: right; + margin-bottom: 24px; +} +.submitButton { + width: 100%; + background: #111; + color: #fff; + border: none; + border-radius: 8px; + font-size: 1.1rem; + font-weight: 600; + height: 56px; + margin-bottom: 24px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} +.separator { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +} +.separator hr { + flex: 1; + border: none; + border-top: 1px solid #ddd; +} +.separator span { + color: #888; + font-size: 0.95rem; +} +.googleButton { + width: 100%; + border: 1.5px solid #111; + border-radius: 8px; + background: #fff; + color: #111; + font-size: 1.1rem; + font-weight: 500; + height: 56px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} +.signup { + display: flex; + gap: 4px; + justify-content: center; + font-size: 1rem; + margin-top: 24px; +} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index 840d45a..cd30057 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -1,33 +1,33 @@ { - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true, - "noUncheckedIndexedAccess": true, + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "noUncheckedIndexedAccess": true, - /* Paths */ - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["src"] + /* Paths */ + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src", "src/vite-env.d.ts"] } diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 9728af2..d6cf5ef 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -1,25 +1,24 @@ { - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true - }, - "include": ["vite.config.ts"] + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8b0f57b..509f902 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,13 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import path from 'path' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src') + } + } })