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
parent
700c98e1d2
commit
91ec90f810
|
|
@ -1,6 +1,7 @@
|
||||||
import js from '@eslint/js'
|
import js from '@eslint/js'
|
||||||
import globals from 'globals'
|
import globals from 'globals'
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
import tseslint from 'typescript-eslint'
|
import tseslint from 'typescript-eslint'
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
@ -9,6 +10,7 @@ export default defineConfig([
|
||||||
globalIgnores(['dist']),
|
globalIgnores(['dist']),
|
||||||
{
|
{
|
||||||
files: ['**/*.{ts,tsx}'],
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
plugins: { react },
|
||||||
extends: [
|
extends: [
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
|
|
@ -19,5 +21,10 @@ export default defineConfig([
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
'react/jsx-indent': ['error', 4],
|
||||||
|
'react/jsx-indent-props': ['error', 4],
|
||||||
|
'semi': ['error', 'always'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,12 @@
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@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",
|
"@vitejs/plugin-react": "^5.1.1",
|
||||||
"autoprefixer": "^10.4.23",
|
"autoprefixer": "^10.4.23",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.24",
|
"eslint-plugin-react-refresh": "^0.4.24",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
|
|
@ -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() {
|
function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<BrowserRouter>
|
||||||
<h1 className='text-xl'>Hello, world</h1>
|
<Routes>
|
||||||
</>
|
<Route path="/login" element={<Login />} />
|
||||||
)
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from './App.tsx'
|
import App from './App.tsx';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
);
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface LoginProps {
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,17 @@ export default {
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {
|
||||||
|
colors: {
|
||||||
|
bg: '#FAFAF9',
|
||||||
|
card: '#FFFFFF',
|
||||||
|
cardBorder: '#E5E7EB',
|
||||||
|
text: '#1A1A1A',
|
||||||
|
'text-secondary': '#6B7280',
|
||||||
|
hover: '#D04A0F',
|
||||||
|
accent: '#E95A1B',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react';
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
})
|
});
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue