feat(ui-setup): implementar estrutura inicial da UI e página de login

- Configura roteamento básico com react-router-dom e rota para /login
- Adiciona página de login com formulário, gerenciamento de estado e estilos
- Cria componente Layout para padronização da interface do usuário
- Define interface LoginProps para tipagem dos dados de autenticação
- Integra imagem logo.webp como ativo da aplicação
- Estende configuração do Tailwind CSS com uma paleta de cores personalizada
- Configura ESLint com plugin para React e regras de estilo (indentação, ponto e vírgula)
master
Artur Oliveira 2025-12-16 09:22:13 -03:00
parent 700c98e1d2
commit 91ec90f810
11 changed files with 1049 additions and 28 deletions

View File

@ -1,6 +1,7 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import react from 'eslint-plugin-react'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
@ -9,6 +10,7 @@ export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
plugins: { react },
extends: [
js.configs.recommended,
tseslint.configs.recommended,
@ -19,5 +21,10 @@ export default defineConfig([
ecmaVersion: 2020,
globals: globals.browser,
},
rules: {
'react/jsx-indent': ['error', 4],
'react/jsx-indent-props': ['error', 4],
'semi': ['error', 'always'],
},
},
])

View File

@ -22,9 +22,12 @@
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,12 +1,16 @@
import './App.css'
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import './App.css';
import { Login } from './pages/Login';
function App() {
return (
<>
<h1 className='text-xl'>Hello, world</h1>
</>
)
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
</Routes>
</BrowserRouter>
);
}
export default App
export default App;

View File

@ -0,0 +1,12 @@
interface Props {
children: React.ReactNode
className?: string
}
export const Layout = ({ children, className }: Props) => {
return (
<div className={`mx-[5%] items-center ${className}`}>
{children}
</div>
);
};

View File

@ -1,9 +1,9 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
<StrictMode>
<App />
</StrictMode>,
);

View File

@ -0,0 +1,64 @@
import { useEffect, useState } from "react";
import { Layout } from "../components/Layout";
import type { LoginProps } from "../types/Login";
export const Login = () => {
const [form, setForm] = useState<LoginProps>({
email: "",
password: ""
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setForm({
...form,
[e.target.name]: e.target.value
});
};
useEffect(() => {
console.log(form);
}, [form]);
return (
<Layout className={Styles.layout}>
<img src="/logo.webp " alt="Logo" className={Styles.logo} />
<div className={Styles.card}>
<form>
<div>
<label htmlFor="email" className={Styles.label}>Email:</label>
<input
type="email"
id="email"
name="email"
placeholder="john.doe@hittelco.com"
required
className={Styles.input}
onChange={handleChange}/>
</div>
<div>
<label htmlFor="password" className={Styles.label}>Password:</label>
<input
type="password"
id="password"
name="password"
placeholder="********"
required
className={Styles.input}
onChange={handleChange}
/>
</div>
<button type="submit" className={Styles.button}>Login</button>
</form>
</div>
</Layout>
);
};
const Styles = {
layout: "h-screen flex flex-col gap-4 justify-center",
logo: "w-36 h-36 mx-auto mb-4",
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
label: "block mb-2 text-md font-medium text-text",
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
};

View File

@ -0,0 +1,4 @@
export interface LoginProps {
email: string
password: string
}

View File

@ -5,7 +5,17 @@ export default {
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
extend: {
colors: {
bg: '#FAFAF9',
card: '#FFFFFF',
cardBorder: '#E5E7EB',
text: '#1A1A1A',
'text-secondary': '#6B7280',
hover: '#D04A0F',
accent: '#E95A1B',
},
},
},
plugins: [],
}

View File

@ -1,7 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})
});

File diff suppressed because it is too large Load Diff