From 276ecc9346ad70ed233545680548a3dd8161f225 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Wed, 31 Jul 2024 22:35:38 -0300 Subject: [PATCH 001/197] remove push from ci --- .github/workflows/code-analysis.yml | 1 - .github/workflows/metrics.yml | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index 952d4e6..3907b4a 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -5,7 +5,6 @@ on: - main - master pull_request: - branches: - main - master diff --git a/.github/workflows/metrics.yml b/.github/workflows/metrics.yml index 0337ebd..ab0e724 100644 --- a/.github/workflows/metrics.yml +++ b/.github/workflows/metrics.yml @@ -3,13 +3,10 @@ name: Metrics and Release on: pull_request: branches: - - main - master - types: [closed] - push: - branches: - main - - master + types: [closed] + jobs: release: From 77002af0f6b1057d2f01d4b1417bf58669a8c03b Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Wed, 31 Jul 2024 22:37:26 -0300 Subject: [PATCH 002/197] Add dev to pipeline --- .github/workflows/code-analysis.yml | 2 ++ .github/workflows/metrics.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index 3907b4a..58e976c 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -2,10 +2,12 @@ name: Análise de Código on: push: branches: + - dev - main - master pull_request: branches: + - dev - main - master diff --git a/.github/workflows/metrics.yml b/.github/workflows/metrics.yml index ab0e724..ee649f7 100644 --- a/.github/workflows/metrics.yml +++ b/.github/workflows/metrics.yml @@ -3,6 +3,7 @@ name: Metrics and Release on: pull_request: branches: + - dev - master - main types: [closed] From 3f0591d6cd2ae59a84c05c63343a29f56271996a Mon Sep 17 00:00:00 2001 From: joaobisi Date: Fri, 2 Aug 2024 02:16:27 -0300 Subject: [PATCH 003/197] feat(#71): Criacao de testes dos metodos de user service e correcoes Co-authored-by: Nanashii76 Co-authored-by: Neoprot --- .gitignore | 8 +- jest.config.ts | 16 ++-- package.json | 1 + src/app/services/apiService.ts | 24 ------ src/lib/enum/userRole.enum.ts | 2 +- src/services/user.service.ts | 2 - test/app/services/user.service.test.ts | 100 +++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 43 deletions(-) delete mode 100644 src/app/services/apiService.ts create mode 100644 test/app/services/user.service.test.ts diff --git a/.gitignore b/.gitignore index 0611d2b..3dd86df 100644 --- a/.gitignore +++ b/.gitignore @@ -36,10 +36,6 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -package-lock.json +/package-lock.json -reports/* - -reports - -report \ No newline at end of file +/reports/*.xml \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index 6e344d1..7378719 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,19 +1,16 @@ import type { Config } from 'jest'; +import nextJest from 'next/jest'; -import nextJest from 'next/jest.js' - const createJestConfig = nextJest({ dir: './src', -}) - +}); + const config: Config = { coverageProvider: 'v8', testEnvironment: 'jsdom', preset: 'ts-jest', moduleNameMapper: { - // ... - '^@/components/(.*)$': '/src/app/components/$1', - '^@/(.*)$': '/src/$1', + '^@/(.*)$': '/src/$1', }, reporters: [ 'default', @@ -25,7 +22,6 @@ const config: Config = { }, ], ], +}; -} - -export default createJestConfig(config) +export default createJestConfig(config); diff --git a/package.json b/package.json index e814d89..8b98d7b 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "@types/tough-cookie": "^4.0.5", "autoprefixer": "^10.4.19", "eslint": "^8", "eslint-config-next": "14.2.5", diff --git a/src/app/services/apiService.ts b/src/app/services/apiService.ts deleted file mode 100644 index 5e7e5a0..0000000 --- a/src/app/services/apiService.ts +++ /dev/null @@ -1,24 +0,0 @@ -import axios from 'axios'; - -const API_URL = process.env.NEXT_PUBLIC_API_URL; - -export const getUsers = async () => { - try { - const response = await axios.get(`${API_URL}/users`); - return response.data; - } catch (error) { - console.error('Failed to fetch users:', error); - throw error; - } -}; -export const updateUserRole = async (userId: string, newRole: string) => { - try { - const response = await axios.patch(`${API_URL}/users/${userId}/role`, { - role: newRole, - }); - return response.data; - } catch (error) { - console.error('Failed to update user role:', error); - throw error; - } -}; diff --git a/src/lib/enum/userRole.enum.ts b/src/lib/enum/userRole.enum.ts index 3f70aa1..b20f5b9 100644 --- a/src/lib/enum/userRole.enum.ts +++ b/src/lib/enum/userRole.enum.ts @@ -1,5 +1,5 @@ export enum UserRole { ADMIN = 'admin', PROFESSOR = 'professor', - USER = 'user', + ALUNO = 'aluno', } diff --git a/src/services/user.service.ts b/src/services/user.service.ts index c157b20..ffaa56a 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -2,8 +2,6 @@ import api from './api.service'; import { CalculusRequest } from '@/lib/interfaces/request.interface'; export const createUser = async (data: any): Promise => { - console.log(`Data: ${data}`); - try { const response = await api.post('users', data); return { diff --git a/test/app/services/user.service.test.ts b/test/app/services/user.service.test.ts new file mode 100644 index 0000000..ea2ad95 --- /dev/null +++ b/test/app/services/user.service.test.ts @@ -0,0 +1,100 @@ +import api from '@/services/api.service'; +import { createUser, loginWithEmailAndPassword, getUsers, updateUserRole } from '@/services/user.service'; +import { UserRole } from '@/lib/enum/userRole.enum'; + +jest.mock('@/services/api.service'); +const mockedApi = api as jest.Mocked; + +describe('User Service', () => { + describe('updateUserRole', () => { + it('should update the user role and return the updated user', async () => { + const userId = '1'; + const newRole: UserRole = UserRole.PROFESSOR; + const mockResponse = { data: { id: '1', role: newRole } }; + mockedApi.patch.mockResolvedValueOnce(mockResponse); + + const result = await updateUserRole(userId, newRole); + + expect(mockedApi.patch).toHaveBeenCalledWith(`/users/${userId}/role`, { role: newRole }); + expect(result).toEqual(mockResponse.data); + }); + + it('should throw an error when the API call fails', async () => { + const userId = '1'; + const newRole: UserRole = UserRole.PROFESSOR; + const mockError = new Error('Failed to update user role'); + mockedApi.patch.mockRejectedValueOnce(mockError); + + await expect(updateUserRole(userId, newRole)).rejects.toThrow('Failed to update user role'); + }); + }); + + describe('createUser', () => { + it('should create a user and return the user data', async () => { + const userData = { name: 'John Doe', email: 'john@example.com', username: 'johndoe', password: 'password123', role: UserRole.ALUNO }; + const mockResponse = { data: userData }; + mockedApi.post.mockResolvedValueOnce(mockResponse); + + const result = await createUser(userData); + + expect(mockedApi.post).toHaveBeenCalledWith('users', userData); + expect(result).toEqual({ data: mockResponse.data }); + }); + + it('should handle errors and return an error message', async () => { + const userData = { name: 'John Doe', email: 'john@example.com', username: 'johndoe', password: 'password123', role: UserRole.ALUNO }; + const mockError = { response: { data: { message: 'Error creating user' } } }; + mockedApi.post.mockRejectedValueOnce(mockError); + + const result = await createUser(userData); + + expect(mockedApi.post).toHaveBeenCalledWith('users', userData); + expect(result).toEqual({ error: 'Error creating user' }); + }); + }); + + describe('loginWithEmailAndPassword', () => { + it('should log in with email and password and return the response', async () => { + const email = 'john@example.com'; + const password = 'password123'; + const mockResponse = { data: { token: 'abc123' } }; + mockedApi.post.mockResolvedValueOnce(mockResponse); + + const result = await loginWithEmailAndPassword(email, password); + + expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { email, password }); + expect(result).toEqual(mockResponse); + }); + + it('should handle errors and return null', async () => { + const email = 'john@example.com'; + const password = 'password123'; + const mockError = new Error('Login failed'); + mockedApi.post.mockRejectedValueOnce(mockError); + + const result = await loginWithEmailAndPassword(email, password); + + expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { email, password }); + expect(result).toBeNull(); + }); + }); + + describe('getUsers', () => { + it('should fetch users and return the data', async () => { + const mockResponse = { data: [{ id: '1', name: 'John Doe' }] }; + mockedApi.get.mockResolvedValueOnce(mockResponse); + + const result = await getUsers(); + + expect(mockedApi.get).toHaveBeenCalledWith('/users'); + expect(result).toEqual(mockResponse.data); + }); + + it('should handle errors and throw an error', async () => { + const mockError = new Error('Failed to fetch users'); + mockedApi.get.mockRejectedValueOnce(mockError); + + await expect(getUsers()).rejects.toThrow('Failed to fetch users'); + }); + }); +}); From 0d0ff62b52ecc0ba13261f9b4c42b896f39eba6f Mon Sep 17 00:00:00 2001 From: joaobisi Date: Fri, 2 Aug 2024 02:16:27 -0300 Subject: [PATCH 004/197] feat(#71): Criacao de testes dos metodos de user service e correcoes Co-authored-by: Nanashii76 Co-authored-by: Neoprot --- jest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.ts b/jest.config.ts index 7378719..5e360d5 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -10,7 +10,7 @@ const config: Config = { testEnvironment: 'jsdom', preset: 'ts-jest', moduleNameMapper: { - '^@/(.*)$': '/src/$1', + '^@/(.*)$': '/src/$1', }, reporters: [ 'default', From f629c2ad63e87ca30d097df32d77402f5501def9 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Sun, 4 Aug 2024 00:33:25 -0300 Subject: [PATCH 005/197] refacting project structure Co-authored-by: DaviMatheus --- package.json | 3 +- src/app/(auth)/login/page.tsx | 31 ++++++++++-- src/app/admin/page.tsx | 42 +++++++++++++---- src/app/api/auth/[...nextauth]/route.ts | 2 +- src/app/components/login.component.tsx | 36 -------------- src/app/home/page.tsx | 2 +- src/app/layout.tsx | 8 ++-- src/app/page.tsx | 2 +- src/app/register/page.tsx | 10 ++-- src/{app => }/components/admin/SearchBar.tsx | 0 src/{app => }/components/admin/UserTable.tsx | 47 +++++++++++++++---- src/{app => }/components/clientLayout.tsx | 0 src/{app => }/components/forms/signInForm.tsx | 0 .../forms/signUpForm.tsx} | 1 - src/{app => }/components/navBar.component.tsx | 2 +- src/{app => }/components/sessionProvider.tsx | 0 .../components/sidebar.component.tsx | 18 +++---- .../ui}/buttons/appleAuth.button.tsx | 0 .../ui}/buttons/googleAuth.button.tsx | 2 +- .../ui}/buttons/microsoftAuth.button.tsx | 3 +- .../ui}/buttons/signIn.button.tsx | 0 .../ui}/buttons/signOut.button.tsx | 0 src/lib/auth.ts | 46 ++++++------------ src/lib/next-auth.d.ts | 37 +++++++++++++++ src/next-auth.d.ts | 18 ------- src/services/user.service.ts | 15 +++++- test/app/services/user.service.test.ts | 47 +++++++++++++++---- 27 files changed, 223 insertions(+), 149 deletions(-) delete mode 100644 src/app/components/login.component.tsx rename src/{app => }/components/admin/SearchBar.tsx (100%) rename src/{app => }/components/admin/UserTable.tsx (66%) rename src/{app => }/components/clientLayout.tsx (100%) rename src/{app => }/components/forms/signInForm.tsx (100%) rename src/{app/components/forms/signupForm.tsx => components/forms/signUpForm.tsx} (98%) rename src/{app => }/components/navBar.component.tsx (94%) rename src/{app => }/components/sessionProvider.tsx (100%) rename src/{app => }/components/sidebar.component.tsx (100%) rename src/{app/components => components/ui}/buttons/appleAuth.button.tsx (100%) rename src/{app/components => components/ui}/buttons/googleAuth.button.tsx (88%) rename src/{app/components => components/ui}/buttons/microsoftAuth.button.tsx (84%) rename src/{app/components => components/ui}/buttons/signIn.button.tsx (100%) rename src/{app/components => components/ui}/buttons/signOut.button.tsx (100%) create mode 100644 src/lib/next-auth.d.ts delete mode 100644 src/next-auth.d.ts diff --git a/package.json b/package.json index 8b98d7b..49486c0 100644 --- a/package.json +++ b/package.json @@ -25,13 +25,12 @@ "cookie": "^0.6.0", "dotenv": "^16.4.5", "glob": "^9.3.5", - "jsonwebtoken": "^9.0.2", "next": "^14.2.5", "next-auth": "^4.24.7", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", - "react-toastify": "^10.0.5", + "sonner": "^1.5.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 920eb19..94b7c79 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,9 +1,9 @@ -'use client'; - -import { Box } from '@mui/material'; +import { Box, Grid, Link } from '@mui/material'; import Image from 'next/image'; import calcuclusLogo from '@/public/calculus-logo.svg'; -import LoginComponent from '@/app/components/login.component'; +import { GoogleAuthButton } from '@/components/ui/buttons/googleAuth.button'; +import { MicrosoftAuthButton } from '@/components/ui/buttons/microsoftAuth.button'; +import { SingInForm } from '@/components/forms/signInForm'; export default function LoginPage() { return ( @@ -19,7 +19,28 @@ export default function LoginPage() { />

Login

- + + + + + + + + + + + +

+ Novo Usuário? + + Cadastre-se + +

); diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 7bd6f4d..a144806 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,10 +1,19 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import UserTable from '@/app/components/admin/UserTable'; -import SearchBar from '@/app/components/admin/SearchBar'; +import UserTable from '@/components/admin/UserTable'; +import SearchBar from '@/components/admin/SearchBar'; import { getUsers, updateUserRole } from '@/services/user.service'; -import { CircularProgress, Box, Dialog, DialogActions, DialogContent, DialogTitle, Button, Typography } from '@mui/material'; +import { + CircularProgress, + Box, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Button, + Typography, +} from '@mui/material'; type User = { _id: string; @@ -42,9 +51,10 @@ const Admin: React.FC = () => { useEffect(() => { const lowercasedQuery = searchQuery.toLowerCase(); - const newFilteredUsers = users.filter(user => - user.username.toLowerCase().includes(lowercasedQuery) || - user.email.toLowerCase().includes(lowercasedQuery) + const newFilteredUsers = users.filter( + (user) => + user.username.toLowerCase().includes(lowercasedQuery) || + user.email.toLowerCase().includes(lowercasedQuery), ); setFilteredUsers(newFilteredUsers); }, [searchQuery, users]); @@ -53,7 +63,10 @@ const Admin: React.FC = () => { setSearchQuery(query); }; - const handleMenuClick = (event: React.MouseEvent, user: User) => { + const handleMenuClick = ( + event: React.MouseEvent, + user: User, + ) => { setAnchorEl(event.currentTarget); setSelectedUser(user); }; @@ -73,8 +86,10 @@ const Admin: React.FC = () => { if (selectedUser && selectedRole) { try { await updateUserRole(selectedUser._id, selectedRole); - const updatedUsers = users.map(user => - user._id === selectedUser._id ? { ...user, role: selectedRole } : user + const updatedUsers = users.map((user) => + user._id === selectedUser._id + ? { ...user, role: selectedRole } + : user, ); setUsers(updatedUsers); setFilteredUsers(updatedUsers); @@ -95,7 +110,14 @@ const Admin: React.FC = () => { if (error) return
{error}
; return ( - + diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 6499b53..7518595 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,5 +1,5 @@ import { authOptions } from '@/lib/auth'; -import NextAuth from "next-auth" +import NextAuth from 'next-auth'; const handler = NextAuth(authOptions); diff --git a/src/app/components/login.component.tsx b/src/app/components/login.component.tsx deleted file mode 100644 index 525fd49..0000000 --- a/src/app/components/login.component.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; -import { Grid } from '@mui/material'; -import { SingInForm } from '@/app/components/forms/signInForm'; -import { GoogleAuthButton } from '@/app/components/buttons/googleAuth.button'; -import { MicrosoftAuthButton } from '@/app/components/buttons/microsoftAuth.button'; -import { AppleAuthButton } from '@/app/components/buttons/appleAuth.button'; - -const LoginComponent: React.FC = () => { - return ( - <> - - - - - - - - {/* - - */} - - - - -

- Novo Usuário? - - Cadastre-se - -

- - ); -}; - -export default LoginComponent; diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index cde5d21..fff2e54 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useSession } from 'next-auth/react'; -import { SignOutButton } from '../components/buttons/signOut.button'; +import { SignOutButton } from '@/components/ui/buttons/signOut.button'; export default function HomePage() { const session = useSession(); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4f4b941..1ec7ff6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,11 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import '../styles/globals.css'; -import SessionProvider from '@/app/components/sessionProvider'; -import { ToastContainer } from 'react-toastify'; +import SessionProvider from '@/components/sessionProvider'; import 'react-toastify/dist/ReactToastify.css'; import './globals.css'; -import ClientLayout from './components/clientLayout'; +import ClientLayout from '@/components/clientLayout'; +import { Toaster } from 'sonner'; const inter = Inter({ subsets: ['latin'] }); @@ -24,8 +24,8 @@ export default async function RootLayout({ {children} + - ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 961d3b4..6d51142 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,7 @@ import { useSession } from 'next-auth/react'; export default function LandingPage() { const session = useSession(); - + if (session.data) { window.location.href = '/home'; } diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx index d635004..4b6a722 100644 --- a/src/app/register/page.tsx +++ b/src/app/register/page.tsx @@ -1,14 +1,10 @@ import React from 'react'; import { Box, Typography, IconButton, Link, Grid } from '@mui/material'; -import { Apple } from '@mui/icons-material'; import Image from 'next/image'; import maoCerebro from '@/public/mao_cerebro.png'; -import googleIcon from '@/public/googleIcon.svg'; -import microsoftIcon from '../../public/microsoftIcon.svg'; -import { SingUpForm } from '../components/forms/signupForm'; -import { GoogleAuthButton } from '../components/buttons/googleAuth.button'; -import { MicrosoftAuthButton } from '../components/buttons/microsoftAuth.button'; -import { AppleAuthButton } from '../components/buttons/appleAuth.button'; +import { SingUpForm } from '@/components/forms/signUpForm'; +import { GoogleAuthButton } from '@/components/ui/buttons/googleAuth.button'; +import { MicrosoftAuthButton } from '@/components/ui/buttons/microsoftAuth.button'; export default function SingUpPage() { return ( diff --git a/src/app/components/admin/SearchBar.tsx b/src/components/admin/SearchBar.tsx similarity index 100% rename from src/app/components/admin/SearchBar.tsx rename to src/components/admin/SearchBar.tsx diff --git a/src/app/components/admin/UserTable.tsx b/src/components/admin/UserTable.tsx similarity index 66% rename from src/app/components/admin/UserTable.tsx rename to src/components/admin/UserTable.tsx index f4b70f4..877efbb 100644 --- a/src/app/components/admin/UserTable.tsx +++ b/src/components/admin/UserTable.tsx @@ -1,5 +1,18 @@ import React from 'react'; -import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Menu, MenuItem, Typography } from '@mui/material'; +import { + Box, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Paper, + IconButton, + Menu, + MenuItem, + Typography, +} from '@mui/material'; import MoreVertIcon from '@mui/icons-material/MoreVert'; type User = { @@ -17,16 +30,34 @@ interface UserTableProps { onRoleSelect: (role: string) => void; } -const UserTable: React.FC = ({ users, anchorEl, onMenuClick, onMenuClose, onRoleSelect }) => { +const UserTable: React.FC = ({ + users, + anchorEl, + onMenuClick, + onMenuClose, + onRoleSelect, +}) => { return ( - username - email - role + + username + + + email + + + role + @@ -50,11 +81,7 @@ const UserTable: React.FC = ({ users, anchorEl, onMenuClick, onM
- + onRoleSelect('admin')}> Tornar Admin diff --git a/src/app/components/clientLayout.tsx b/src/components/clientLayout.tsx similarity index 100% rename from src/app/components/clientLayout.tsx rename to src/components/clientLayout.tsx diff --git a/src/app/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx similarity index 100% rename from src/app/components/forms/signInForm.tsx rename to src/components/forms/signInForm.tsx diff --git a/src/app/components/forms/signupForm.tsx b/src/components/forms/signUpForm.tsx similarity index 98% rename from src/app/components/forms/signupForm.tsx rename to src/components/forms/signUpForm.tsx index a67707f..4f82597 100644 --- a/src/app/components/forms/signupForm.tsx +++ b/src/components/forms/signUpForm.tsx @@ -26,7 +26,6 @@ export function SingUpForm() { } else { toast.error(response.error); } - window.location.href = '/login'; }; return ( diff --git a/src/app/components/navBar.component.tsx b/src/components/navBar.component.tsx similarity index 94% rename from src/app/components/navBar.component.tsx rename to src/components/navBar.component.tsx index 6783f28..df2cc3d 100644 --- a/src/app/components/navBar.component.tsx +++ b/src/components/navBar.component.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { AppBar, Toolbar, IconButton, Typography, Box } from '@mui/material'; import MenuIcon from '@mui/icons-material/Menu'; import Sidebar from './sidebar.component'; -import { SignOutButton } from './buttons/signOut.button'; +import { SignOutButton } from './ui/buttons/signOut.button'; interface NavBarProps { handleDrawerOpen: () => void; diff --git a/src/app/components/sessionProvider.tsx b/src/components/sessionProvider.tsx similarity index 100% rename from src/app/components/sessionProvider.tsx rename to src/components/sessionProvider.tsx diff --git a/src/app/components/sidebar.component.tsx b/src/components/sidebar.component.tsx similarity index 100% rename from src/app/components/sidebar.component.tsx rename to src/components/sidebar.component.tsx index 0761a49..f48c9f5 100644 --- a/src/app/components/sidebar.component.tsx +++ b/src/components/sidebar.component.tsx @@ -22,6 +22,15 @@ const Sidebar: React.FC = ({ handleDrawerOpen, open }) => {
    +
  • + + + Home + +
  • {session.data?.user.role === 'admin' && (
  • @@ -34,15 +43,6 @@ const Sidebar: React.FC = ({ handleDrawerOpen, open }) => {
  • )} -
  • - - - Home - -
diff --git a/src/app/components/buttons/appleAuth.button.tsx b/src/components/ui/buttons/appleAuth.button.tsx similarity index 100% rename from src/app/components/buttons/appleAuth.button.tsx rename to src/components/ui/buttons/appleAuth.button.tsx diff --git a/src/app/components/buttons/googleAuth.button.tsx b/src/components/ui/buttons/googleAuth.button.tsx similarity index 88% rename from src/app/components/buttons/googleAuth.button.tsx rename to src/components/ui/buttons/googleAuth.button.tsx index 72ae798..7cd2807 100644 --- a/src/app/components/buttons/googleAuth.button.tsx +++ b/src/components/ui/buttons/googleAuth.button.tsx @@ -7,7 +7,7 @@ import { signIn } from 'next-auth/react'; export function GoogleAuthButton() { const handleClick = () => { - signIn('google'); + window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/auth/google/callback`; }; return ( diff --git a/src/app/components/buttons/microsoftAuth.button.tsx b/src/components/ui/buttons/microsoftAuth.button.tsx similarity index 84% rename from src/app/components/buttons/microsoftAuth.button.tsx rename to src/components/ui/buttons/microsoftAuth.button.tsx index b329818..81ad4fc 100644 --- a/src/app/components/buttons/microsoftAuth.button.tsx +++ b/src/components/ui/buttons/microsoftAuth.button.tsx @@ -7,7 +7,8 @@ import { signIn } from 'next-auth/react'; export function MicrosoftAuthButton() { const handleClick = () => { - signIn('azure-ad'); + window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/auth/microsoft/callback`; + // signIn('azure-ad'); }; return ( diff --git a/src/app/components/buttons/signIn.button.tsx b/src/components/ui/buttons/signIn.button.tsx similarity index 100% rename from src/app/components/buttons/signIn.button.tsx rename to src/components/ui/buttons/signIn.button.tsx diff --git a/src/app/components/buttons/signOut.button.tsx b/src/components/ui/buttons/signOut.button.tsx similarity index 100% rename from src/app/components/buttons/signOut.button.tsx rename to src/components/ui/buttons/signOut.button.tsx diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 8be12ee..3f8a887 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,6 +1,9 @@ import { Awaitable, NextAuthOptions } from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; -import { loginWithEmailAndPassword } from '@/services/user.service'; +import { + loginWithEmailAndPassword, + loginWithFederatedProvider, +} from '@/services/user.service'; import GoogleProvider from 'next-auth/providers/google'; import AzureADProvider from 'next-auth/providers/azure-ad'; import { JWT } from 'next-auth/jwt'; @@ -11,26 +14,18 @@ export const authOptions: NextAuthOptions = { clientId: process.env.MICROSOFT_CLIENT_ID!, clientSecret: process.env.MICROSOFT_CLIENT_SECRET!, tenantId: process.env.MICROSOFT_TENANT_ID!, - authorization: { - params: { - prompt: 'consent', - access_type: 'offline', - response_type: 'code', - request_uri: `${process.env.NEXT_PUBLIC_API_URL!}/auth/microsoft/callback`, - }, - }, }), GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, - authorization: { - params: { - request_uri: `${process.env.NEXT_PUBLIC_API_URL!}/auth/google/callback`, - prompt: 'consent', - access_type: 'offline', - response_type: 'code', - }, - }, + // authorization: { + // params: { + // request_uri: `${process.env.NEXT_PUBLIC_API_URL!}/auth/google/callback`, + // prompt: 'consent', + // access_type: 'offline', + // response_type: 'code', + // }, + // }, }), CredentialsProvider({ credentials: { @@ -84,24 +79,13 @@ export const authOptions: NextAuthOptions = { }, }), ], + pages: { + signIn: '/login', + }, secret: process.env.NEXTAUTH_SECRET, callbacks: { - async signIn({ account, profile }) { - console.log( - `Account: ${account?.provider} Profile: ${profile?.email} ${account?.id_token}`, - ); - if (account?.provider === 'google') { - return `${process.env.NEXT_PUBLIC_API_URL}/auth/google/callback`; - } - if (account?.provider === 'azure-ad') { - return `${process.env.NEXT_PUBLIC_API_URL}/auth/microsoft/callback`; - } - return true; - }, - async jwt({ token, user, account }) { - console.log('jwt => ', token, user, account); if (account && user) { token.id = user.id; token.accessToken = user.accessToken; diff --git a/src/lib/next-auth.d.ts b/src/lib/next-auth.d.ts new file mode 100644 index 0000000..4ccc2a0 --- /dev/null +++ b/src/lib/next-auth.d.ts @@ -0,0 +1,37 @@ +import { DefaultSession } from 'next-auth'; + +declare module 'next-auth' { + interface User { + id: string; + email: string; + accessToken: string; + refreshToken: string; + role: string; + exp: number; + } + + interface Session { + user: User & DefaultSession['user']; + expires: string; + error: string; + } +} + +import { JWT } from 'next-auth/jwt'; + +declare module 'next-auth/jwt' { + interface User { + id: string; + email: string; + accessToken: string; + refreshToken: string; + role: string; + exp: number; + } + + interface JWT { + user: User & DefaultSession['user']; + expires: string; + error: string; + } +} diff --git a/src/next-auth.d.ts b/src/next-auth.d.ts deleted file mode 100644 index 26a778a..0000000 --- a/src/next-auth.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DefaultSession } from 'next-auth'; - -declare module 'next-auth' { - interface User { - id: string; - email: string; - accessToken: string; - // refreshToken: string - exp: number; - role: string; - } - - interface Session { - user: User & DefaultSession['user']; - expires: string; - error: string; - } -} diff --git a/src/services/user.service.ts b/src/services/user.service.ts index ffaa56a..f9e58bb 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -31,6 +31,19 @@ export const loginWithEmailAndPassword = async ( } }; +export const loginWithFederatedProvider = async (accessToken: string) => { + try { + const response = await api.post('auth/login/federated', { + accessToken, + }); + console.log('Login response: ', response.data); + return response; + } catch (error) { + console.log(error); + return null; + } +}; + export const getUsers = async () => { try { const response = await api.get('/users'); @@ -52,4 +65,4 @@ export const updateUserRole = async (userId: string, newRole: string) => { console.error('Failed to update user role:', error); throw error; } -}; \ No newline at end of file +}; diff --git a/test/app/services/user.service.test.ts b/test/app/services/user.service.test.ts index ea2ad95..27391ad 100644 --- a/test/app/services/user.service.test.ts +++ b/test/app/services/user.service.test.ts @@ -1,6 +1,11 @@ import api from '@/services/api.service'; -import { createUser, loginWithEmailAndPassword, getUsers, updateUserRole } from '@/services/user.service'; -import { UserRole } from '@/lib/enum/userRole.enum'; +import { + createUser, + loginWithEmailAndPassword, + getUsers, + updateUserRole, +} from '@/services/user.service'; +import { UserRole } from '@/lib/enum/userRole.enum'; jest.mock('@/services/api.service'); const mockedApi = api as jest.Mocked; @@ -15,7 +20,9 @@ describe('User Service', () => { const result = await updateUserRole(userId, newRole); - expect(mockedApi.patch).toHaveBeenCalledWith(`/users/${userId}/role`, { role: newRole }); + expect(mockedApi.patch).toHaveBeenCalledWith(`/users/${userId}/role`, { + role: newRole, + }); expect(result).toEqual(mockResponse.data); }); @@ -25,13 +32,21 @@ describe('User Service', () => { const mockError = new Error('Failed to update user role'); mockedApi.patch.mockRejectedValueOnce(mockError); - await expect(updateUserRole(userId, newRole)).rejects.toThrow('Failed to update user role'); + await expect(updateUserRole(userId, newRole)).rejects.toThrow( + 'Failed to update user role', + ); }); }); describe('createUser', () => { it('should create a user and return the user data', async () => { - const userData = { name: 'John Doe', email: 'john@example.com', username: 'johndoe', password: 'password123', role: UserRole.ALUNO }; + const userData = { + name: 'John Doe', + email: 'john@example.com', + username: 'johndoe', + password: 'password123', + role: UserRole.ALUNO, + }; const mockResponse = { data: userData }; mockedApi.post.mockResolvedValueOnce(mockResponse); @@ -42,8 +57,16 @@ describe('User Service', () => { }); it('should handle errors and return an error message', async () => { - const userData = { name: 'John Doe', email: 'john@example.com', username: 'johndoe', password: 'password123', role: UserRole.ALUNO }; - const mockError = { response: { data: { message: 'Error creating user' } } }; + const userData = { + name: 'John Doe', + email: 'john@example.com', + username: 'johndoe', + password: 'password123', + role: UserRole.ALUNO, + }; + const mockError = { + response: { data: { message: 'Error creating user' } }, + }; mockedApi.post.mockRejectedValueOnce(mockError); const result = await createUser(userData); @@ -62,7 +85,10 @@ describe('User Service', () => { const result = await loginWithEmailAndPassword(email, password); - expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { email, password }); + expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { + email, + password, + }); expect(result).toEqual(mockResponse); }); @@ -74,7 +100,10 @@ describe('User Service', () => { const result = await loginWithEmailAndPassword(email, password); - expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { email, password }); + expect(mockedApi.post).toHaveBeenCalledWith('auth/login', { + email, + password, + }); expect(result).toBeNull(); }); }); From b46833340cb16d1fc2593063c04e04f8cce7674c Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 03:58:10 +0000 Subject: [PATCH 006/197] feat(#108): adicionando yml de CD --- .github/workflows/deploy-vm-azure.yml | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/deploy-vm-azure.yml diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml new file mode 100644 index 0000000..ea22f77 --- /dev/null +++ b/.github/workflows/deploy-vm-azure.yml @@ -0,0 +1,29 @@ +name: CI Workflow + +# Dispara o workflow para push e pull request na branch 'dev' +on: + push: + branches: + - dev + pull_request: + branches: + - dev + +jobs: + run-script: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up SSH + uses: webfactory/ssh-agent@v0.5.3 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY_VM_AZURE_DEV }} + + - name: Run script on Azure VM + run: | + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < path/to/your/script.sh + env: + SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From 9173dc32bd3eb7df252538a87738e66cdc40452e Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:03:02 +0000 Subject: [PATCH 007/197] fix(#108): ajustando path de arquivo sh --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index ea22f77..7791eb2 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -24,6 +24,6 @@ jobs: - name: Run script on Azure VM run: | - ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < path/to/your/script.sh + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < /home/Calculus-admin1/app/run_npm_tmux.sh env: SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From b70243f97035ade3010f9e237661086106f1bdef Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:05:44 +0000 Subject: [PATCH 008/197] feat(#108): ajustando nome de workflow --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 7791eb2..019f053 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -1,4 +1,4 @@ -name: CI Workflow +name: Deploy App in Azure # Dispara o workflow para push e pull request na branch 'dev' on: From 61e8960b1dffb1ed2fd300bd212f388c8a3a13c2 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:10:22 +0000 Subject: [PATCH 009/197] test(#108): testando caminho alternativo para script --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 019f053..2526169 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -24,6 +24,6 @@ jobs: - name: Run script on Azure VM run: | - ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < /home/Calculus-admin1/app/run_npm_tmux.sh + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < /app/run_npm_tmux.sh env: SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From b8d29347e62ab2d563116754f4da834caecbaa42 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:32:46 +0000 Subject: [PATCH 010/197] test(#108): testando comando para executar o shell script --- .github/workflows/deploy-vm-azure.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 2526169..20f19ab 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -21,9 +21,15 @@ jobs: uses: webfactory/ssh-agent@v0.5.3 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY_VM_AZURE_DEV }} + + - name: Checking working Dir + run: pwd + + - name: Chaging working Dir + run: cd /home/Calculus-admin1/app - name: Run script on Azure VM run: | - ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'bash -s' < /app/run_npm_tmux.sh + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'cd /home/Calculus-admin1/app && sudo bash -s' < /run_npm_tmux.sh env: SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From e2988832c4211708bc434cfbc5b59dc7baa4ce21 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:33:29 +0000 Subject: [PATCH 011/197] =?UTF-8?q?fix(#108):=20retirando=20passo=20desnec?= =?UTF-8?q?ess=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-vm-azure.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 20f19ab..1402592 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -24,9 +24,6 @@ jobs: - name: Checking working Dir run: pwd - - - name: Chaging working Dir - run: cd /home/Calculus-admin1/app - name: Run script on Azure VM run: | From 571ae3626944fc7ed7ac8512f8dbba463307554a Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:34:44 +0000 Subject: [PATCH 012/197] test(#108): outro teste --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 1402592..e6ae9b9 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -27,6 +27,6 @@ jobs: - name: Run script on Azure VM run: | - ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'cd /home/Calculus-admin1/app && sudo bash -s' < /run_npm_tmux.sh + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'cd /home/Calculus-admin1/app && sudo bash -s' < ./run_npm_tmux.sh env: SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From f6daf229f413c7941068271c4d7fbcba3b63465d Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:36:34 +0000 Subject: [PATCH 013/197] test(#108): teste sem o / --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index e6ae9b9..14123dd 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -27,6 +27,6 @@ jobs: - name: Run script on Azure VM run: | - ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'cd /home/Calculus-admin1/app && sudo bash -s' < ./run_npm_tmux.sh + ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'cd /home/Calculus-admin1/app && sudo bash -s' < run_npm_tmux.sh env: SSH_KNOWN_HOSTS: ${{ secrets.IP_VM_AZURE_DEV }} From 7db87f51f9433cacd644ac9cd45bbbf43d5125e8 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Gontijo <44791309+paulohgontijoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 04:43:18 +0000 Subject: [PATCH 014/197] testando pwd via ssh na maquina --- .github/workflows/deploy-vm-azure.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-vm-azure.yml b/.github/workflows/deploy-vm-azure.yml index 14123dd..eee3898 100644 --- a/.github/workflows/deploy-vm-azure.yml +++ b/.github/workflows/deploy-vm-azure.yml @@ -23,7 +23,7 @@ jobs: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY_VM_AZURE_DEV }} - name: Checking working Dir - run: pwd + run: ssh -o StrictHostKeyChecking=no ${{ secrets.USER_VM_AZURE_DEV }}@${{ secrets.IP_VM_AZURE_DEV }} 'pwd' - name: Run script on Azure VM run: | From 2bf3b5ca0e665b7f82f8038b1ab7f5e2b7ea6c31 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Mon, 5 Aug 2024 13:22:54 -0300 Subject: [PATCH 015/197] Fixing import error Co-authored-by: DaviMatheus --- package.json | 2 + src/app/(auth)/oauth/page.tsx | 65 +++++++++++++----- src/app/home/page.tsx | 1 - src/app/layout.tsx | 10 +-- src/components/forms/signInForm.tsx | 14 ++-- src/components/forms/signUpForm.tsx | 29 +++++--- src/lib/auth.ts | 6 -- src/providers/reactQueryProvider.provider.tsx | 18 +++++ .../session.provider.tsx} | 0 src/public/loading.gif | Bin 0 -> 63885 bytes src/services/user.service.ts | 4 +- 11 files changed, 101 insertions(+), 48 deletions(-) create mode 100644 src/providers/reactQueryProvider.provider.tsx rename src/{components/sessionProvider.tsx => providers/session.provider.tsx} (100%) create mode 100644 src/public/loading.gif diff --git a/package.json b/package.json index 49486c0..1206340 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "glob": "^9.3.5", "next": "^14.2.5", "next-auth": "^4.24.7", + "next-safe-action": "^7.4.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", @@ -34,6 +35,7 @@ "zod": "^3.23.8" }, "devDependencies": { + "@tanstack/react-query-devtools": "^5.51.21", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", "@types/cookie": "^0.6.0", diff --git a/src/app/(auth)/oauth/page.tsx b/src/app/(auth)/oauth/page.tsx index 6c89592..14c6371 100644 --- a/src/app/(auth)/oauth/page.tsx +++ b/src/app/(auth)/oauth/page.tsx @@ -1,40 +1,71 @@ -// page.tsx - 'use client'; import { signIn, useSession } from 'next-auth/react'; +import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { useSearchParams } from 'next/navigation'; import { Suspense } from 'react'; +import { toast } from 'sonner'; +import { useMutation } from '@tanstack/react-query'; +import loadImage from '@/public/loading.gif'; +import Image from 'next/image'; + + +const signInWithToken = async (token: string) => { + const result = await signIn('credentials', { token, redirect: false }); + if (!result?.error) { + return result; + } else { + throw new Error(result.error); + } +}; const OAuthContent = () => { + const router = useRouter(); const { data: session } = useSession(); - const [authenticated, setAuthenticated] = useState(false); const [message, setMessage] = useState('Carregando...'); const searchParams = useSearchParams(); const token = searchParams.get('token'); + const { mutate, isPending } = useMutation({ + mutationFn: signInWithToken, + onSuccess: () => { + setMessage('Login efetuado com sucesso! redirecionando'); + window.location.href = '/home'; + }, + onError: () => { + toast.error('Erro ao efetuar login, por favor tente novamente!'); + router.push('/login'); + }, + }); + useEffect(() => { - if (token && !authenticated) { - const signInWithToken = async () => { - await signIn('credentials', { token }); - if (session) { - setAuthenticated(true); - setMessage('Login efetuado com sucesso! redirecionando'); - window.location.href = '/home'; - } - }; - - signInWithToken(); + if (token && !session) { + mutate(token); } - }, [token, authenticated, session]); + }, [token, session, mutate]); + + if (!token) { + toast.error('Erro ao efetuar login, por favor tente novamente!'); + router.push('/login'); + } - return

{message}

; + return ( +
+ {isPending ? Carregando... : message} +
+ ); }; const OAuthPage = () => { return ( - Loading...}> + + Carregando... + + } + > ); diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index fff2e54..09ce6d5 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -6,7 +6,6 @@ import { SignOutButton } from '@/components/ui/buttons/signOut.button'; export default function HomePage() { const session = useSession(); - console.log('my user: ' + session.data?.user); return (

diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 1ec7ff6..e6e280b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,11 +1,11 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import '../styles/globals.css'; -import SessionProvider from '@/components/sessionProvider'; -import 'react-toastify/dist/ReactToastify.css'; +import SessionProvider from '@/providers/session.provider'; import './globals.css'; import ClientLayout from '@/components/clientLayout'; import { Toaster } from 'sonner'; +import ReactQueryProvider from '@/providers/reactQueryProvider.provider'; const inter = Inter({ subsets: ['latin'] }); @@ -23,8 +23,10 @@ export default async function RootLayout({ - {children} - + + {children} + + diff --git a/src/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx index 2ffefc6..0e16ca7 100644 --- a/src/components/forms/signInForm.tsx +++ b/src/components/forms/signInForm.tsx @@ -8,27 +8,25 @@ import { Link, TextField, } from '@mui/material'; -import { useForm, SubmitHandler } from 'react-hook-form'; +import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { SigninData, signinSchema } from '@/lib/schemas/singin.schemas'; -import { toast } from 'react-toastify'; -import { useRouter } from 'next/router'; +import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { Visibility, VisibilityOff } from '@mui/icons-material'; import { signIn, useSession } from 'next-auth/react'; -import { authOptions } from '@/lib/auth'; +import { toast } from 'sonner'; export function SingInForm() { const session = useSession(); - console.log('Session: ', session); + const router = useRouter(); const [showPassword, setShowPassword] = useState(false); useEffect(() => { if (session.data) { - toast.success('Login efetuado com sucesso!'); - setTimeout(() => {}, 1000); - window.location.href = '/home'; + toast.success('Login realizado com sucesso!'); + router.push('/home'); } }, [session.data]); diff --git a/src/components/forms/signUpForm.tsx b/src/components/forms/signUpForm.tsx index 4f82597..ec559dc 100644 --- a/src/components/forms/signUpForm.tsx +++ b/src/components/forms/signUpForm.tsx @@ -4,28 +4,35 @@ import { Box, Button, TextField } from '@mui/material'; import { SignupData, signupSchema } from '@/lib/schemas/singup.schemas'; import { useForm, SubmitHandler } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { toast } from 'react-toastify'; +import { useMutation } from '@tanstack/react-query'; +import { useRouter } from 'next/navigation'; + import { createUser } from '@/services/user.service'; +import { toast } from 'sonner'; export function SingUpForm() { + const router = useRouter(); const { register, handleSubmit, - watch, formState: { errors }, } = useForm({ resolver: zodResolver(signupSchema), }); - const onSubmit: SubmitHandler = async (data) => { - const response = await createUser(data); - console.log('Response: ', response); + const { mutate: server_createUser, isPending } = useMutation({ + mutationFn: async (data: SignupData) => await createUser(data), + onSuccess: (data) => { + toast.success(data.data.message); + router.push('/login'); + }, + onError: (error) => { + toast.error(error.message); + }, + }); - if (!response.error) { - toast.success('Cadastrado com sucesso!'); - } else { - toast.error(response.error); - } + const onSubmit: SubmitHandler = (data) => { + server_createUser(data); }; return ( @@ -80,7 +87,7 @@ export function SingUpForm() { sx={{ bgcolor: '#000', mt: 2 }} type="submit" > - Sign up + {isPending ? 'Loading...' : 'Sign up'} ); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 3f8a887..ca5be78 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -44,10 +44,7 @@ export const authOptions: NextAuthOptions = { }, }, async authorize(credentials, req) { - console.log('credentials', credentials); - console.log('req', req); if (credentials!.token) { - console.log('Eu nasci'); const token = credentials!.token; const decodedAccessToken = JSON.parse( Buffer.from(token!.split('.')[1], 'base64').toString(), @@ -70,9 +67,7 @@ export const authOptions: NextAuthOptions = { } const user = res?.data; - if (user) { - console.log('Returning user', res); return user; } return null; @@ -110,7 +105,6 @@ export const authOptions: NextAuthOptions = { }, async session({ session, token }) { - console.log('session => ', session); return { ...session, user: { diff --git a/src/providers/reactQueryProvider.provider.tsx b/src/providers/reactQueryProvider.provider.tsx new file mode 100644 index 0000000..ef7f25c --- /dev/null +++ b/src/providers/reactQueryProvider.provider.tsx @@ -0,0 +1,18 @@ +'use client'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { useState } from 'react'; + +function ReactQueryProvider({ children }: React.PropsWithChildren) { + const [client] = useState(new QueryClient()); + + return ( + + {children} + + + ); +} + +export default ReactQueryProvider; diff --git a/src/components/sessionProvider.tsx b/src/providers/session.provider.tsx similarity index 100% rename from src/components/sessionProvider.tsx rename to src/providers/session.provider.tsx diff --git a/src/public/loading.gif b/src/public/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8775dba6b8b5977098c22257ffedd4b36189c840 GIT binary patch literal 63885 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 => { + console.log(data); try { const response = await api.post('users', data); return { @@ -23,7 +26,6 @@ export const loginWithEmailAndPassword = async ( try { console.log(`Login email: ${email}, password: ${password}`); const response = await api.post('auth/login', { email, password }); - console.log('Login response: ', response.data); return response; } catch (error) { console.log(error); From 9da21c143c2ee9b4dd7c202447093aa0f7b5db43 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Mon, 5 Aug 2024 22:17:38 -0300 Subject: [PATCH 016/197] fix docker compose --- .env.dev | 9 --------- .env.dev.template | 3 +++ Dockerfile | 4 +--- 3 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 .env.dev create mode 100644 .env.dev.template diff --git a/.env.dev b/.env.dev deleted file mode 100644 index 6f14e10..0000000 --- a/.env.dev +++ /dev/null @@ -1,9 +0,0 @@ -NEXT_PUBLIC_API_URL= -NEXTAUTH_URL= -NEXTAUTH_SECRET= -JWT_SECRET= -GOOGLE_CLIENT_ID= -GOOGLE_CLIENT_SECRET= -MICROSOFT_CLIENT_ID= -MICROSOFT_CLIENT_SECRET= -MICROSOFT_TENANT_ID= \ No newline at end of file diff --git a/.env.dev.template b/.env.dev.template new file mode 100644 index 0000000..1eeadb0 --- /dev/null +++ b/.env.dev.template @@ -0,0 +1,3 @@ +NEXT_PUBLIC_API_URL= +NEXTAUTH_URL= +NEXTAUTH_SECRET= \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 29eaa62..5df1e52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,6 @@ RUN npm install COPY . . -RUN npm run build - EXPOSE 4000 -CMD ["npm", "run", "start"] +CMD ["npm", "run", "dev"] From 890841500f743792b37ee28a9659d1c48f6972b9 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Mon, 5 Aug 2024 22:31:45 -0300 Subject: [PATCH 017/197] adding docker network --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ac510c2..352c272 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,4 +11,5 @@ services: - ./src:/app/src env_file: - .env - \ No newline at end of file + networks: + - calculus-network \ No newline at end of file From de193af6f902a2d8883f07b81e17687552796169 Mon Sep 17 00:00:00 2001 From: Natanael Filho Date: Mon, 5 Aug 2024 22:43:06 -0300 Subject: [PATCH 018/197] fix docker compose --- docker-compose.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 352c272..03e805a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,4 +12,8 @@ services: env_file: - .env networks: - - calculus-network \ No newline at end of file + - calculus-network + +networks: + calculus-network: + driver: bridge \ No newline at end of file From 73c727f81f0971d41c99164b432a44b5b02c3cff Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 13:31:03 -0300 Subject: [PATCH 019/197] =?UTF-8?q?[enhancement](#103):Criando=20a=20branc?= =?UTF-8?q?h=20para=20padroniza=C3=A7=C3=A3o=20Cria=C3=A7=C3=A3o=20da=20br?= =?UTF-8?q?anch=20para=20padroniza=C3=A7=C3=A3o=20do=20front?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/buttons/inputs/input.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/components/ui/buttons/inputs/input.tsx diff --git a/src/components/ui/buttons/inputs/input.tsx b/src/components/ui/buttons/inputs/input.tsx new file mode 100644 index 0000000..e69de29 From 8eb144a78086aff04862acfcb404067950a78ade Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 14:58:27 -0300 Subject: [PATCH 020/197] enhacement(#103):Arrumando caminho Arrumando caminho dos arquivos e criando o base do input --- src/components/ui/{buttons => }/inputs/input.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/ui/{buttons => }/inputs/input.tsx (100%) diff --git a/src/components/ui/buttons/inputs/input.tsx b/src/components/ui/inputs/input.tsx similarity index 100% rename from src/components/ui/buttons/inputs/input.tsx rename to src/components/ui/inputs/input.tsx From 3295a23d130be8890421e02e5254ed8b512bd081 Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 14:59:33 -0300 Subject: [PATCH 021/197] enhancement(#103): criando o arquivo base --- src/components/ui/inputs/input.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/components/ui/inputs/input.tsx b/src/components/ui/inputs/input.tsx index e69de29..74749fa 100644 --- a/src/components/ui/inputs/input.tsx +++ b/src/components/ui/inputs/input.tsx @@ -0,0 +1,18 @@ +import { TextField, TextFieldProps } from '@mui/material'; +import { FC } from 'react'; + +type InputProps = TextFieldProps; + +const Input: FC = (props) => { + return ( + + ); +}; + +export default Input; From 9606633abaee5a21af565a8ff33901fdaff53b28 Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 15:00:38 -0300 Subject: [PATCH 022/197] =?UTF-8?q?enhacement(#103):removendo=20comentario?= =?UTF-8?q?s=20Removendo=20coment=C3=A1rios=20desnecess=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/inputs/input.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ui/inputs/input.tsx b/src/components/ui/inputs/input.tsx index 74749fa..41e8e9c 100644 --- a/src/components/ui/inputs/input.tsx +++ b/src/components/ui/inputs/input.tsx @@ -7,10 +7,10 @@ const Input: FC = (props) => { return ( ); }; From 4b0b7000f113ac84ef5c8cad108eb95ab547be6c Mon Sep 17 00:00:00 2001 From: NATAN Date: Tue, 6 Aug 2024 15:02:37 -0300 Subject: [PATCH 023/197] enhancement#103: adicionado componente padrao para botao --- src/app/page.tsx | 11 ++-- src/components/forms/signInForm.tsx | 11 +--- src/components/forms/signUpForm.tsx | 12 ++-- .../ui/buttons/appleAuth.button.tsx | 9 +-- .../ui/buttons/googleAuth.button.tsx | 10 +-- .../ui/buttons/microsoftAuth.button.tsx | 5 +- .../ui/buttons/myButton.component.tsx | 61 +++++++++++++++++++ 7 files changed, 85 insertions(+), 34 deletions(-) create mode 100644 src/components/ui/buttons/myButton.component.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index 6d51142..5a073d4 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,6 +4,7 @@ import React from 'react'; import Image from 'next/image'; import Link from 'next/link'; import roboProfessor from '@/public/robo_professor.png'; +import Mybutton from '@/components/ui/buttons/myButton.component'; import { useSession } from 'next-auth/react'; @@ -19,9 +20,9 @@ export default function LandingPage() {

Calculus

- +
@@ -46,9 +47,9 @@ export default function LandingPage() {

- + + Começe aqui +
diff --git a/src/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx index 0e16ca7..aed21a8 100644 --- a/src/components/forms/signInForm.tsx +++ b/src/components/forms/signInForm.tsx @@ -16,6 +16,7 @@ import { useEffect, useState } from 'react'; import { Visibility, VisibilityOff } from '@mui/icons-material'; import { signIn, useSession } from 'next-auth/react'; import { toast } from 'sonner'; +import Mybutton from '@/components/ui/buttons/myButton.component'; export function SingInForm() { const session = useSession(); @@ -101,15 +102,9 @@ export function SingInForm() { Recuperar senha

- + ); } diff --git a/src/components/forms/signUpForm.tsx b/src/components/forms/signUpForm.tsx index ec559dc..d7c3d5c 100644 --- a/src/components/forms/signUpForm.tsx +++ b/src/components/forms/signUpForm.tsx @@ -10,6 +10,8 @@ import { useRouter } from 'next/navigation'; import { createUser } from '@/services/user.service'; import { toast } from 'sonner'; +import Mybutton from '@/components/ui/buttons/myButton.component'; + export function SingUpForm() { const router = useRouter(); const { @@ -80,15 +82,9 @@ export function SingUpForm() { error={!!errors.password} helperText={errors.password?.message} /> - + ); } diff --git a/src/components/ui/buttons/appleAuth.button.tsx b/src/components/ui/buttons/appleAuth.button.tsx index a2f78b0..a849ea2 100644 --- a/src/components/ui/buttons/appleAuth.button.tsx +++ b/src/components/ui/buttons/appleAuth.button.tsx @@ -3,6 +3,7 @@ import Image from 'next/image'; import appleIcon from '@/public/apple.svg'; import { Button } from '@mui/material'; +import MyButton from '@/components/ui/buttons/myButton.component'; export function AppleAuthButton() { const handleClick = () => { @@ -11,7 +12,7 @@ export function AppleAuthButton() { }; return ( - - ); -} + + ) +}; \ No newline at end of file diff --git a/src/components/ui/buttons/googleAuth.button.tsx b/src/components/ui/buttons/googleAuth.button.tsx index 7cd2807..27ecff3 100644 --- a/src/components/ui/buttons/googleAuth.button.tsx +++ b/src/components/ui/buttons/googleAuth.button.tsx @@ -4,25 +4,21 @@ import Image from 'next/image'; import googleIcon from '@/public/google.svg'; import { Box, Button } from '@mui/material'; import { signIn } from 'next-auth/react'; - +import MyButton from '@/components/ui/buttons/myButton.component'; export function GoogleAuthButton() { const handleClick = () => { window.location.href = `${process.env.NEXT_PUBLIC_API_URL}/auth/google/callback`; }; return ( - + ); } diff --git a/src/components/ui/buttons/microsoftAuth.button.tsx b/src/components/ui/buttons/microsoftAuth.button.tsx index 81ad4fc..3b6a9e9 100644 --- a/src/components/ui/buttons/microsoftAuth.button.tsx +++ b/src/components/ui/buttons/microsoftAuth.button.tsx @@ -4,6 +4,7 @@ import Image from 'next/image'; import microsoftIcon from '@/public/microsoft.svg'; import { Button } from '@mui/material'; import { signIn } from 'next-auth/react'; +import MyButton from '@/components/ui/buttons/myButton.component'; export function MicrosoftAuthButton() { const handleClick = () => { @@ -12,7 +13,7 @@ export function MicrosoftAuthButton() { }; return ( - + ); } diff --git a/src/components/ui/buttons/myButton.component.tsx b/src/components/ui/buttons/myButton.component.tsx new file mode 100644 index 0000000..ebc76be --- /dev/null +++ b/src/components/ui/buttons/myButton.component.tsx @@ -0,0 +1,61 @@ +import { Button, styled } from '@mui/material'; +import React from 'react'; + +type ButtonProps = { + children: React.ReactNode; + color: 'white' | 'black' | 'green' | 'red' | 'purple' | 'pink'; + width?: string; + height?: string; + type?: 'button' | 'submit'; + radius?: string; + onClick?: () => void; +}; + +const colorMap: { [key: string]: { primary: string; secondary: string } } = { + white: { primary: '#FFFAFA', secondary: '#CECECE' }, + black: { primary: '#2f2f2f', secondary: '#1F1F1F' }, + green: { primary: '#29CC57', secondary: '#007C23' }, + red: { primary: '#FF4122', secondary: '#C61A09' }, + purple: { primary: '#6667AB', secondary: '#515287' }, + pink: { primary: '#FF8164', secondary: '#FF6242' }, +}; + +const CustomButton = styled(Button)<{ + width?: string; + height?: string; + btncolor: string; + subcolor: string; + radius?: string; +}>(({ width, height, btncolor, radius, subcolor }) => ({ + width: width || '100%', + height: height || '50px', + borderRadius: radius || height || '5px', + backgroundColor: btncolor, + border: `1px solid ${subcolor}`, + boxShadow: `0px 5px 0 ${subcolor}`, + marginTop: '7px', + color: btncolor === '#FFFAFA' ? '#000000' : '#FFFFFF', + '&:hover': { + backgroundColor: btncolor, + boxShadow: `0px 5px 0 ${subcolor}`, + }, +})); + +const MyButton: React.FC = ({ children, color, width, height, type = 'button', radius, onClick }) => { + return ( + + {children} + + ); + }; + + export default MyButton; From 8dcb164863c00a66cd547d8200177ef1249c3c75 Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 22:06:57 -0300 Subject: [PATCH 024/197] =?UTF-8?q?enhancement(#103):Componente=20MyInput?= =?UTF-8?q?=20Cria=C3=A7=C3=A3o=20do=20componente=20MyInput=20que=20=C3=A9?= =?UTF-8?q?=20um=20componente=20que=20serve=20de=20campo=20de=20escrita?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: natangoatoso --- src/components/forms/signInForm.tsx | 29 +++++------- src/components/forms/signUpForm.tsx | 47 +++++++++---------- src/components/ui/inputs/input.tsx | 18 ------- .../ui/inputs/myInput.component.tsx | 27 +++++++++++ 4 files changed, 59 insertions(+), 62 deletions(-) delete mode 100644 src/components/ui/inputs/input.tsx create mode 100644 src/components/ui/inputs/myInput.component.tsx diff --git a/src/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx index aed21a8..3940445 100644 --- a/src/components/forms/signInForm.tsx +++ b/src/components/forms/signInForm.tsx @@ -17,6 +17,7 @@ import { Visibility, VisibilityOff } from '@mui/icons-material'; import { signIn, useSession } from 'next-auth/react'; import { toast } from 'sonner'; import Mybutton from '@/components/ui/buttons/myButton.component'; +import MyInput from '@/components/ui/inputs/myInput.component'; export function SingInForm() { const session = useSession(); @@ -59,29 +60,22 @@ export function SingInForm() { }} className="grid gap-4 justify-center m-3" > - - + @@ -92,10 +86,9 @@ export function SingInForm() { > {showPassword ? : } - - ), + ) }} - /> + />

Esqueceu sua senha? diff --git a/src/components/forms/signUpForm.tsx b/src/components/forms/signUpForm.tsx index d7c3d5c..68dfddb 100644 --- a/src/components/forms/signUpForm.tsx +++ b/src/components/forms/signUpForm.tsx @@ -11,6 +11,7 @@ import { createUser } from '@/services/user.service'; import { toast } from 'sonner'; import Mybutton from '@/components/ui/buttons/myButton.component'; +import MyInput from '@/components/ui/inputs/myInput.component'; export function SingUpForm() { const router = useRouter(); @@ -39,49 +40,43 @@ export function SingUpForm() { return ( - - - - + + {isPending ? 'Loading...' : 'Sign up'} diff --git a/src/components/ui/inputs/input.tsx b/src/components/ui/inputs/input.tsx deleted file mode 100644 index 41e8e9c..0000000 --- a/src/components/ui/inputs/input.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { TextField, TextFieldProps } from '@mui/material'; -import { FC } from 'react'; - -type InputProps = TextFieldProps; - -const Input: FC = (props) => { - return ( - - ); -}; - -export default Input; diff --git a/src/components/ui/inputs/myInput.component.tsx b/src/components/ui/inputs/myInput.component.tsx new file mode 100644 index 0000000..f15d032 --- /dev/null +++ b/src/components/ui/inputs/myInput.component.tsx @@ -0,0 +1,27 @@ +import { TextField, TextFieldProps } from '@mui/material'; +import { FC } from 'react'; + +type InputProps = TextFieldProps & { + register: any; + error?: boolean; + width?: string; + helperText?: string; + bgcolor?: string; +}; + +const MyInput: FC = ({ bgcolor,width,register, error, helperText, ...props }) => { + return ( + + ); +}; + +export default MyInput; From e7ef9008b8f2672f73d2290a2e42d8cc58d5819c Mon Sep 17 00:00:00 2001 From: Neoprot Date: Tue, 6 Aug 2024 22:14:54 -0300 Subject: [PATCH 025/197] enhancement(#103):adicionando propriedade ao MyInput Adicionando propriedades de value e placeholde ao componente MyInput Co-authored-by: natangoatoso --- src/components/ui/inputs/myInput.component.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/ui/inputs/myInput.component.tsx b/src/components/ui/inputs/myInput.component.tsx index f15d032..104c4ae 100644 --- a/src/components/ui/inputs/myInput.component.tsx +++ b/src/components/ui/inputs/myInput.component.tsx @@ -7,14 +7,18 @@ type InputProps = TextFieldProps & { width?: string; helperText?: string; bgcolor?: string; + placeholder?: string; + value?: string; }; -const MyInput: FC = ({ bgcolor,width,register, error, helperText, ...props }) => { +const MyInput: FC = ({ value,placeholder, bgcolor, width, register, error, helperText, ...props }) => { return ( Date: Tue, 6 Aug 2024 22:41:28 -0300 Subject: [PATCH 026/197] enhancement(#103): atualizando propriedades MyButton Co-authored-by: Neoprot --- src/app/admin/page.tsx | 10 ++++--- src/app/page.tsx | 4 +-- src/components/forms/signInForm.tsx | 17 ++++++------ src/components/forms/signUpForm.tsx | 2 +- .../ui/buttons/myButton.component.tsx | 26 ++++++++++++------- src/components/ui/buttons/signIn.button.tsx | 6 ++--- src/components/ui/buttons/signOut.button.tsx | 10 ++++--- 7 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index a144806..a51f9cf 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -15,6 +15,8 @@ import { Typography, } from '@mui/material'; +import MyButton from '@/components/ui/buttons/myButton.component'; + type User = { _id: string; username: string; @@ -140,12 +142,12 @@ const Admin: React.FC = () => { )} - - + diff --git a/src/app/page.tsx b/src/app/page.tsx index 5a073d4..fa87797 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -20,7 +20,7 @@ export default function LandingPage() {

Calculus

- + Login @@ -47,7 +47,7 @@ export default function LandingPage() {

- + Começe aqui diff --git a/src/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx index 3940445..948301d 100644 --- a/src/components/forms/signInForm.tsx +++ b/src/components/forms/signInForm.tsx @@ -60,18 +60,18 @@ export function SingInForm() { }} className="grid gap-4 justify-center m-3" > - - + {showPassword ? : } - ) + + ), }} - /> + />

Esqueceu sua senha? Recuperar senha

- + Login diff --git a/src/components/forms/signUpForm.tsx b/src/components/forms/signUpForm.tsx index 68dfddb..a6adf94 100644 --- a/src/components/forms/signUpForm.tsx +++ b/src/components/forms/signUpForm.tsx @@ -77,7 +77,7 @@ export function SingUpForm() { error={!!errors.password} helperText={errors.password?.message} /> - + {isPending ? 'Loading...' : 'Sign up'} diff --git a/src/components/ui/buttons/myButton.component.tsx b/src/components/ui/buttons/myButton.component.tsx index ebc76be..8335191 100644 --- a/src/components/ui/buttons/myButton.component.tsx +++ b/src/components/ui/buttons/myButton.component.tsx @@ -8,16 +8,17 @@ type ButtonProps = { height?: string; type?: 'button' | 'submit'; radius?: string; + bold?: boolean; onClick?: () => void; }; -const colorMap: { [key: string]: { primary: string; secondary: string } } = { - white: { primary: '#FFFAFA', secondary: '#CECECE' }, - black: { primary: '#2f2f2f', secondary: '#1F1F1F' }, - green: { primary: '#29CC57', secondary: '#007C23' }, - red: { primary: '#FF4122', secondary: '#C61A09' }, - purple: { primary: '#6667AB', secondary: '#515287' }, - pink: { primary: '#FF8164', secondary: '#FF6242' }, +const colorMap: { [key: string]: { primary: string; secondary: string; hovercolor: string } } = { + white: { primary: '#FFFAFA', secondary: '#CECECE', hovercolor: '#F4EDED' }, + black: { primary: '#2F2F2F', secondary: '#1F1F1F', hovercolor: '#252525' }, + green: { primary: '#29CC57', secondary: '#007C23', hovercolor: '#26B54F' }, + red: { primary: '#FF4122', secondary: '#C61A09', hovercolor: '#E93C20' }, + purple: { primary: '#6667AB', secondary: '#515287', hovercolor: '#5D5EA6' }, + pink: { primary: '#FF8164', secondary: '#FF6242', hovercolor: '#E9765B' }, }; const CustomButton = styled(Button)<{ @@ -25,8 +26,10 @@ const CustomButton = styled(Button)<{ height?: string; btncolor: string; subcolor: string; + hovercolor: string; radius?: string; -}>(({ width, height, btncolor, radius, subcolor }) => ({ + bold?: boolean; +}>(({ width, height, btncolor, radius, subcolor, bold, hovercolor}) => ({ width: width || '100%', height: height || '50px', borderRadius: radius || height || '5px', @@ -35,21 +38,24 @@ const CustomButton = styled(Button)<{ boxShadow: `0px 5px 0 ${subcolor}`, marginTop: '7px', color: btncolor === '#FFFAFA' ? '#000000' : '#FFFFFF', + fontWeight: bold ? 'bold' : 'normal', '&:hover': { - backgroundColor: btncolor, + backgroundColor: hovercolor, boxShadow: `0px 5px 0 ${subcolor}`, }, })); -const MyButton: React.FC = ({ children, color, width, height, type = 'button', radius, onClick }) => { +const MyButton: React.FC = ({ children, color, width, height, type = 'button', radius, onClick, bold = false }) => { return ( diff --git a/src/components/ui/buttons/signIn.button.tsx b/src/components/ui/buttons/signIn.button.tsx index 4d766ca..f362e62 100644 --- a/src/components/ui/buttons/signIn.button.tsx +++ b/src/components/ui/buttons/signIn.button.tsx @@ -1,13 +1,13 @@ import { useSession } from 'next-auth/react'; - +import MyButton from './myButton.component'; const SignInButton = () => { const { data: session } = useSession(); if (session && session.user) return ( - + ); }; diff --git a/src/components/ui/buttons/signOut.button.tsx b/src/components/ui/buttons/signOut.button.tsx index 20b8b62..5c80410 100644 --- a/src/components/ui/buttons/signOut.button.tsx +++ b/src/components/ui/buttons/signOut.button.tsx @@ -1,5 +1,6 @@ import { Box, Button } from '@mui/material'; import { signOut } from 'next-auth/react'; +import MyButton from './myButton.component'; export function SignOutButton() { const handleClick = async () => { @@ -7,12 +8,15 @@ export function SignOutButton() { window.location.href = '/'; }; return ( - + ); } From 971eb3a94ef73ee3a3d764aec4d15a5c71d8eed7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Aug 2024 16:13:50 -0300 Subject: [PATCH 027/197] =?UTF-8?q?feat(#72):=20P=C3=A1gina=20de=20recuper?= =?UTF-8?q?a=C3=A7=C3=A3o=20de=20conta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Criando página de recuperação de conta --- Dockerfile | 3 -- docker-compose.yml | 6 ++- src/app/forgot-password/page.tsx | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/app/forgot-password/page.tsx diff --git a/Dockerfile b/Dockerfile index 5df1e52..efa54e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,8 @@ FROM node:18 WORKDIR /src - COPY package*.json ./ -RUN npm install nodemon --save-dev - RUN npm install COPY . . diff --git a/docker-compose.yml b/docker-compose.yml index 352c272..3e353f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,4 +12,8 @@ services: env_file: - .env networks: - - calculus-network \ No newline at end of file + - calculus-network + +networks: + calculus-network: + driver: bridge \ No newline at end of file diff --git a/src/app/forgot-password/page.tsx b/src/app/forgot-password/page.tsx new file mode 100644 index 0000000..868e6a6 --- /dev/null +++ b/src/app/forgot-password/page.tsx @@ -0,0 +1,64 @@ +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import calcuclusLogo from '@/public/calculus-logo.svg'; + +export default function ForgotPassword() { + + const [email, setEmail] = useState(''); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + if (!emailRegex.test(email)) { + setError('Por favor, digite um email válido!'); + setSuccess(''); + return; + } + + setError(''); + setSuccess('Email enviado com sucesso!'); + console.log('Email enviado: ', email); + }; + + return ( +
+ Logo +

Esqueci a Senha

+

+ Enviaremos as instruções para o seu email. +

+

+ Para que você possa recuperar a sua senha. +

+
+ setEmail(e.target.value)} + className="mb-4 p-2 border border-gray-300 rounded-md w-full text-black" + /> + + {error &&

{error}

} + {success &&

{success}

} +
+
+ ); +} From 08cf67ef84dba118fe3cc2302b77f063366d94d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Aug 2024 17:39:17 -0300 Subject: [PATCH 028/197] =?UTF-8?q?feat(#72):=20P=C3=A1gina=20de=20redefin?= =?UTF-8?q?i=C3=A7=C3=A3o=20de=20senha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Criando a página para redefinir a senha do usuário --- .../forgot-password/reset-password/page.tsx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/app/forgot-password/reset-password/page.tsx diff --git a/src/app/forgot-password/reset-password/page.tsx b/src/app/forgot-password/reset-password/page.tsx new file mode 100644 index 0000000..4a2bac0 --- /dev/null +++ b/src/app/forgot-password/reset-password/page.tsx @@ -0,0 +1,92 @@ +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import calculusLogos from '@/public/calculus-logo.svg'; +import { Visibility, VisibilityOff } from '@mui/icons-material'; + +export default function ResetPassword() { + + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + const [showNewPassword, setShowNewPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (newPassword.length < 8) { + setError('A senha deve ter pelo menos 8 caracteres'); + setSuccess(''); + return; + } + + if (newPassword !== confirmPassword) { + setError('As senhas não coincidem.'); + setSuccess(''); + return; + } + + setError(''); + setSuccess('Senha redefinida com sucesso!'); + + // lógica do back aqui + }; + + return ( +
+ Logo +

Redefinir Senha

+
+
+ setNewPassword(e.target.value)} + className="p-2 border border-gray-300 rounded-md w-full" + /> + +
+
+ setConfirmPassword(e.target.value)} + className="p-2 border border-gray-300 rounded-md w-full" + /> + +
+ + {error &&

{error}

} + {success &&

{success}

} +
+
+ ); +} From 461d88caa3eed0f4826ee4f2352772b3f1c8ebe3 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Aug 2024 18:58:45 -0300 Subject: [PATCH 029/197] =?UTF-8?q?feat(#72):=20P=C3=A1gina=20de=20redefin?= =?UTF-8?q?i=C3=A7=C3=A3o=20de=20senha=20e=20recupera=C3=A7=C3=A3o=20de=20?= =?UTF-8?q?senha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionando link para a página de recuperação e utilizando componentes MUI Co-authored-by: natangoatoso --- src/app/forgot-password/page.tsx | 42 ++++--- .../forgot-password/reset-password/page.tsx | 110 +++++++++++------- src/components/forms/signInForm.tsx | 2 +- 3 files changed, 97 insertions(+), 57 deletions(-) diff --git a/src/app/forgot-password/page.tsx b/src/app/forgot-password/page.tsx index 868e6a6..37875b8 100644 --- a/src/app/forgot-password/page.tsx +++ b/src/app/forgot-password/page.tsx @@ -2,10 +2,10 @@ import { useState } from 'react'; import Image from 'next/image'; +import { Box, TextField, Button, Alert } from '@mui/material'; import calcuclusLogo from '@/public/calculus-logo.svg'; export default function ForgotPassword() { - const [email, setEmail] = useState(''); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); @@ -27,13 +27,13 @@ export default function ForgotPassword() { }; return ( -
+ Logo

Esqueci a Senha

@@ -42,23 +42,39 @@ export default function ForgotPassword() {

Para que você possa recuperar a sua senha.

-
- + setEmail(e.target.value)} - className="mb-4 p-2 border border-gray-300 rounded-md w-full text-black" + variant="outlined" + fullWidth + className="mb-4 w-full text-black" /> - - {error &&

{error}

} - {success &&

{success}

} - -
+ + {error && ( + + {error} + + )} + {success && ( + + {success} + + )} + + ); } diff --git a/src/app/forgot-password/reset-password/page.tsx b/src/app/forgot-password/reset-password/page.tsx index 4a2bac0..45637a8 100644 --- a/src/app/forgot-password/reset-password/page.tsx +++ b/src/app/forgot-password/reset-password/page.tsx @@ -2,11 +2,11 @@ import { useState } from 'react'; import Image from 'next/image'; -import calculusLogos from '@/public/calculus-logo.svg'; +import { Box, TextField, Button, IconButton, InputAdornment, Alert } from '@mui/material'; import { Visibility, VisibilityOff } from '@mui/icons-material'; +import calculusLogos from '@/public/calculus-logo.svg'; export default function ResetPassword() { - const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [error, setError] = useState(''); @@ -32,11 +32,11 @@ export default function ResetPassword() { setError(''); setSuccess('Senha redefinida com sucesso!'); - // lógica do back aqui + // routes logic.... }; return ( -
+

Redefinir Senha

-
-
- setNewPassword(e.target.value)} - className="p-2 border border-gray-300 rounded-md w-full" - /> - -
-
- setConfirmPassword(e.target.value)} - className="p-2 border border-gray-300 rounded-md w-full" - /> - -
- - {error &&

{error}

} - {success &&

{success}

} -
-
+ + {error && ( + + {error} + + )} + {success && ( + + {success} + + )} + + ); } diff --git a/src/components/forms/signInForm.tsx b/src/components/forms/signInForm.tsx index 0e16ca7..ab861f5 100644 --- a/src/components/forms/signInForm.tsx +++ b/src/components/forms/signInForm.tsx @@ -97,7 +97,7 @@ export function SingInForm() { />

Esqueceu sua senha? - + Recuperar senha

From 95d54f6a4d3b6afb14eb9c9d4559826b64e74c54 Mon Sep 17 00:00:00 2001 From: joaobisi Date: Fri, 9 Aug 2024 12:43:02 -0300 Subject: [PATCH 030/197] feat(#74):Modelo inicial do editor de markdown criacao do editor de markdown com preview, acessivel a partir da side bar --- package.json | 3 + src/app/components/sidebar.component.tsx | 17 +++- src/app/components/studio/MarkdownEditor.tsx | 97 ++++++++++++++++++++ src/app/studio/page.tsx | 9 ++ 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/app/components/studio/MarkdownEditor.tsx create mode 100644 src/app/studio/page.tsx diff --git a/package.json b/package.json index e814d89..75f3865 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,10 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.52.1", + "react-markdown": "^9.0.1", "react-toastify": "^10.0.5", + "rehype-katex": "^7.0.0", + "remark-math": "^6.0.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/app/components/sidebar.component.tsx b/src/app/components/sidebar.component.tsx index 0761a49..3adf535 100644 --- a/src/app/components/sidebar.component.tsx +++ b/src/app/components/sidebar.component.tsx @@ -3,6 +3,7 @@ import Link from 'next/link'; import HomeIcon from '@mui/icons-material/Home'; import DashboardIcon from '@mui/icons-material/Dashboard'; +import EditNoteIcon from '@mui/icons-material/EditNote'; import { Drawer, IconButton, Box } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; import { useSession } from 'next-auth/react'; @@ -22,10 +23,18 @@ const Sidebar: React.FC = ({ handleDrawerOpen, open }) => {
    +
  • + + + Home + +
  • {session.data?.user.role === 'admin' && (
  • - = ({ handleDrawerOpen, open }) => {
  • )}
  • - + - Home + Estúdio de Criação
diff --git a/src/app/components/studio/MarkdownEditor.tsx b/src/app/components/studio/MarkdownEditor.tsx new file mode 100644 index 0000000..9ef70dd --- /dev/null +++ b/src/app/components/studio/MarkdownEditor.tsx @@ -0,0 +1,97 @@ +'use client'; + +import React, { useState } from 'react'; +import ReactMarkdown from 'react-markdown'; +import remarkMath from 'remark-math'; +import rehypeKatex from 'rehype-katex'; +import 'katex/dist/katex.min.css'; +import { AppBar, Toolbar, IconButton, Tooltip, Box, Button } from '@mui/material'; +import TitleIcon from '@mui/icons-material/Title'; +import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted'; +import FunctionsIcon from '@mui/icons-material/Functions'; +import BoldIcon from '@mui/icons-material/FormatBold'; +import ItalicIcon from '@mui/icons-material/FormatItalic'; + +const MarkdownEditor: React.FC = () => { + const [markdown, setMarkdown] = useState(''); + + const handleChange = (value: string) => { + setMarkdown(value); + }; + + const insertText = (text: string) => { + setMarkdown((prevMarkdown) => prevMarkdown + text); + }; + + const handleSave = () => { + // logica para salvar conteudo + alert('Conteúdo salvo!'); + }; + + return ( + + + + + insertText('**texto em negrito**')}> + + + + + insertText('*texto em itálico*')}> + + + + + insertText('# Título\n')}> + + + + + insertText('- Item da lista\n')}> + + + + + insertText('$$E = mc^2$$\n')}> + + + + + + + + +