From 829ef310a5d130d3782918a582af9262e2800c59 Mon Sep 17 00:00:00 2001 From: Gabriel Santana de Menezes Date: Sat, 2 May 2026 23:14:52 -0400 Subject: [PATCH] login no frontend --- frontend/src/App.tsx | 14 ++- frontend/src/components/AppShell.tsx | 36 +++++-- frontend/src/components/TaskTimer.tsx | 4 +- frontend/src/contexts/AuthContext.tsx | 120 ++++++++++++++++++++++++ frontend/src/index.css | 7 ++ frontend/src/lib/api.ts | 4 +- frontend/src/pages/CreateTaskPage.tsx | 6 +- frontend/src/pages/LoginPage.tsx | 115 +++++++++++++++++++++++ frontend/src/pages/RegisterPage.tsx | 130 ++++++++++++++++++++++++++ frontend/src/pages/TaskBoardPage.tsx | 8 +- frontend/src/pages/TaskListPage.tsx | 10 +- frontend/src/router.tsx | 28 ++++-- frontend/src/services/authService.ts | 38 ++++++++ 13 files changed, 484 insertions(+), 36 deletions(-) create mode 100644 frontend/src/contexts/AuthContext.tsx create mode 100644 frontend/src/pages/LoginPage.tsx create mode 100644 frontend/src/pages/RegisterPage.tsx create mode 100644 frontend/src/services/authService.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3bc1f85..6556555 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,13 @@ -import { RouterProvider } from 'react-router-dom' - -import { router } from './router' +import { RouterProvider } from "react-router-dom"; +import { router } from "./router"; +import { AuthProvider } from "./contexts/AuthContext"; function App() { - return + return ( + + + + ); } -export default App +export default App; diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index 3e6cc40..ae84348 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -1,5 +1,6 @@ -import { Layout, ListTodo, PlusCircle, Columns } from 'lucide-react' -import { NavLink, Outlet } from 'react-router-dom' +import { Layout, ListTodo, PlusCircle, Columns, LogOut } from 'lucide-react' +import { NavLink, Outlet, Navigate } from 'react-router-dom' +import { useAuth } from '../contexts/AuthContext' import { cn } from '../lib/utils' @@ -12,6 +13,20 @@ const navLinkClassName = ({ isActive }: { isActive: boolean }) => ) export function AppShell() { + const { user, isAuthenticated, isLoading, logout } = useAuth() + + if (isLoading) { + return ( +
+
+
+ ) + } + + if (!isAuthenticated) { + return + } + return (
{/* Top Navigation */} @@ -51,12 +66,21 @@ export function AppShell() {
-
- GM -
+ {user?.name} - Gabriel Menezes + {user?.name} +
diff --git a/frontend/src/components/TaskTimer.tsx b/frontend/src/components/TaskTimer.tsx index 39ee816..3318fc9 100644 --- a/frontend/src/components/TaskTimer.tsx +++ b/frontend/src/components/TaskTimer.tsx @@ -109,7 +109,7 @@ export function TaskTimer({ task, onUpdate }: TaskTimerProps) {
@@ -131,7 +131,7 @@ export function TaskTimer({ task, onUpdate }: TaskTimerProps) { + + +
+

+ Não tem uma conta? Criar conta +

+

+ Precisa de ajuda? Falar com suporte +

+
+
+ +
+ ) +} diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx new file mode 100644 index 0000000..4111e59 --- /dev/null +++ b/frontend/src/pages/RegisterPage.tsx @@ -0,0 +1,130 @@ +import React, { useState } from 'react' +import { useNavigate, Link } from 'react-router-dom' +import { motion } from 'framer-motion' +import { Mail, Lock, User, Layout, UserPlus } from 'lucide-react' +import { authService } from '../services/authService' + +export const RegisterPage: React.FC = () => { + const [name, setName] = useState('') + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [isLoading, setIsLoading] = useState(false) + const navigate = useNavigate() + + const handleRegister = async (e: React.FormEvent) => { + e.preventDefault() + setIsLoading(true) + + try { + await authService.register(name, email, password) + alert('Conta criada com sucesso! Agora faça seu login.') + navigate('/login') + } catch (error) { + console.error('Registration failed', error) + alert('Erro ao criar conta. Tente novamente.') + } finally { + setIsLoading(false) + } + } + + return ( +
+ {/* Visual decorations */} +
+
+
+
+ + +
+
+
+ +
+

BeeTask

+

Crie sua conta para começar

+
+ +
+
+ +
+
+ +
+ setName(e.target.value)} + className="block w-full pl-12 pr-4 py-3.5 bg-surface-muted/50 border border-border-soft rounded-2xl text-text-main placeholder-text-muted/60 focus:outline-none focus:ring-4 focus:ring-brand/10 focus:border-brand transition-all font-medium" + placeholder="Seu nome" + /> +
+
+ +
+ +
+
+ +
+ setEmail(e.target.value)} + className="block w-full pl-12 pr-4 py-3.5 bg-surface-muted/50 border border-border-soft rounded-2xl text-text-main placeholder-text-muted/60 focus:outline-none focus:ring-4 focus:ring-brand/10 focus:border-brand transition-all font-medium" + placeholder="exemplo@beetech.net" + /> +
+
+ +
+ +
+
+ +
+ setPassword(e.target.value)} + className="block w-full pl-12 pr-4 py-3.5 bg-surface-muted/50 border border-border-soft rounded-2xl text-text-main placeholder-text-muted/60 focus:outline-none focus:ring-4 focus:ring-brand/10 focus:border-brand transition-all font-medium" + placeholder="••••••••" + /> +
+
+ + +
+ +
+

+ Já tem uma conta? Fazer login +

+
+
+
+
+ ) +} diff --git a/frontend/src/pages/TaskBoardPage.tsx b/frontend/src/pages/TaskBoardPage.tsx index b93f50a..e67765d 100644 --- a/frontend/src/pages/TaskBoardPage.tsx +++ b/frontend/src/pages/TaskBoardPage.tsx @@ -23,25 +23,25 @@ const COLUMNS: { id: TaskStatus; label: string; icon: any; color: string }[] = [ id: 'NOT_STARTED', label: 'Pendente', icon: AlertCircle, - color: 'text-slate-400', + color: 'text-text-muted', }, { id: 'IN_PROGRESS', label: 'Em Andamento', icon: Clock, - color: 'text-amber-500', + color: 'text-warning', }, { id: 'COMPLETED', label: 'Concluída', icon: CheckCircle2, - color: 'text-emerald-500', + color: 'text-success', }, { id: 'CANCELED', label: 'Cancelada', icon: XCircle, - color: 'text-rose-500', + color: 'text-danger', }, ] diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx index 7e2b077..194989e 100644 --- a/frontend/src/pages/TaskListPage.tsx +++ b/frontend/src/pages/TaskListPage.tsx @@ -24,25 +24,25 @@ function getStatusConfig(status: TaskResponse['status']) { switch (status) { case 'COMPLETED': return { - tone: 'bg-emerald-50 text-emerald-700 border-emerald-100', + tone: 'bg-success-soft text-success border-success/20', icon: CheckCircle2, label: 'Concluído', } case 'IN_PROGRESS': return { - tone: 'bg-amber-50 text-amber-700 border-amber-100', + tone: 'bg-warning-soft text-warning border-warning/20', icon: Clock, label: 'Em Andamento', } case 'CANCELED': return { - tone: 'bg-rose-50 text-rose-700 border-rose-100', + tone: 'bg-danger-soft text-danger border-danger/20', icon: XCircle, label: 'Cancelado', } default: return { - tone: 'bg-slate-50 text-slate-600 border-slate-100', + tone: 'bg-surface-muted text-text-muted border-border-soft', icon: AlertCircle, label: 'Pendente', } @@ -213,7 +213,7 @@ export function TaskListPage() {
- + {task.history?.length ?? 0} registros
diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index c97c3ee..8f39905 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,13 +1,23 @@ -import { createBrowserRouter } from 'react-router-dom' +import { createBrowserRouter } from "react-router-dom"; -import { AppShell } from './components/AppShell' -import { CreateTaskPage } from './pages/CreateTaskPage' -import { TaskListPage } from './pages/TaskListPage' -import { TaskBoardPage } from './pages/TaskBoardPage' +import { AppShell } from "./components/AppShell"; +import { CreateTaskPage } from "./pages/CreateTaskPage"; +import { TaskListPage } from "./pages/TaskListPage"; +import { TaskBoardPage } from "./pages/TaskBoardPage"; +import { LoginPage } from "./pages/LoginPage"; +import { RegisterPage } from "./pages/RegisterPage"; export const router = createBrowserRouter([ { - path: '/', + path: "/login", + element: , + }, + { + path: "/register", + element: , + }, + { + path: "/", element: , children: [ { @@ -15,13 +25,13 @@ export const router = createBrowserRouter([ element: , }, { - path: 'new', + path: "new", element: , }, { - path: 'board', + path: "board", element: , }, ], }, -]) +]); diff --git a/frontend/src/services/authService.ts b/frontend/src/services/authService.ts new file mode 100644 index 0000000..b1981e4 --- /dev/null +++ b/frontend/src/services/authService.ts @@ -0,0 +1,38 @@ +import { apiFetch } from '../lib/api' + +export type LoginResponse = { + name: string + email: string + photo: string + jwt: string + refreshToken: string + expiresIn: number // seconds + issuedAt: string // ISO date + organizations: { + name: string + roles: string[] + }[] +} + +export const authService = { + async login(email: string, password: string): Promise { + return apiFetch('/auth/login', { + method: 'POST', + body: { email, password }, + }) + }, + + async register(name: string, email: string, password: string): Promise<{ id: number; name: string; email: string }> { + return apiFetch<{ id: number; name: string; email: string }>('/auth/register', { + method: 'POST', + body: { name, email, password }, + }) + }, + + async refresh(refreshToken: string): Promise { + return apiFetch('/auth/refresh', { + method: 'POST', + body: { refreshToken }, + }) + }, +}