├── backend ├── .babelrc ├── src │ ├── utils │ │ └── database.js │ ├── index.js │ ├── models │ │ └── newsModel.js │ ├── services │ │ └── newsService.js │ └── contollers │ │ └── newsController.js ├── package.json ├── README.md └── .gitignore ├── portal-noticias ├── public │ ├── robots.txt │ ├── logo.png │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── src │ ├── logo.png │ ├── setupTests.js │ ├── App.test.js │ ├── index.css │ ├── reportWebVitals.js │ ├── index.js │ ├── NoticiaDetalhada.js │ ├── NoticiaDetalhada.css │ ├── logo.svg │ ├── FormularioNoticia.js │ ├── App.js │ └── App.css ├── .gitignore ├── package.json └── README.md ├── README.md └── .gitignore /backend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /portal-noticias/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /portal-noticias/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vbotelhoo/hackathon-fiap/HEAD/portal-noticias/src/logo.png -------------------------------------------------------------------------------- /portal-noticias/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vbotelhoo/hackathon-fiap/HEAD/portal-noticias/public/logo.png -------------------------------------------------------------------------------- /portal-noticias/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vbotelhoo/hackathon-fiap/HEAD/portal-noticias/public/favicon.ico -------------------------------------------------------------------------------- /portal-noticias/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /portal-noticias/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /backend/src/utils/database.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import 'dotenv/config'; 3 | 4 | const URI = process.env.DB_DATABASE; 5 | 6 | const databaseConnection = async () => { 7 | if(!global.mongoose){ 8 | mongoose.set('strictQuery', false) 9 | global.mongoose = await mongoose.connect(URI) 10 | } 11 | } 12 | 13 | export default databaseConnection -------------------------------------------------------------------------------- /portal-noticias/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /portal-noticias/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /portal-noticias/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /backend/src/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import bodyParser from 'body-parser'; 3 | import newsController from './contollers/newsController'; 4 | import 'dotenv/config'; 5 | 6 | const app = express(); 7 | const port = process.env.PORT; 8 | const cors = require('cors'); 9 | 10 | app.use(bodyParser.json()); 11 | app.use(cors()); 12 | app.use('/news', newsController); 13 | 14 | app.listen(port, () => { 15 | console.log(`App rodando em http://localhost:${port}`); 16 | }); 17 | -------------------------------------------------------------------------------- /backend/src/models/newsModel.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const NewsSchema = new mongoose.Schema({ 4 | titulo: { type: String, require: true }, 5 | conteudo: {type: String, require: true}, 6 | autor: {type: String, require: true}, 7 | dataPublicacao: { type: Date, default: Date.now }, 8 | categoria: {type: String, require: true}, 9 | imagemUrl: {type: String, require: true} 10 | }); 11 | 12 | export default mongoose.models.Noticia || mongoose.model('Noticia', NewsSchema); -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "nodemon --exec babel-node src/index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "babel-cli": "^6.26.0", 14 | "babel-preset-env": "^1.7.0", 15 | "body-parser": "^1.20.3", 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.4.7", 18 | "express": "^4.18.2", 19 | "mongoose": "^8.10.0" 20 | }, 21 | "devDependencies": { 22 | "nodemon": "^2.0.20" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /portal-noticias/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /portal-noticias/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /portal-noticias/src/NoticiaDetalhada.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import "./NoticiaDetalhada.css"; 4 | 5 | const NoticiaDetalhada = ({ noticias }) => { 6 | const { id } = useParams(); 7 | const noticia = noticias.find((item) => item._id === id); 8 | 9 | if (!noticia) { 10 | return

Notícia não encontrada.

; 11 | } 12 | 13 | return ( 14 |
15 |

{noticia.titulo}

16 | {noticia.titulo} 17 |

18 | Por {noticia.autor} em {noticia.dataPublicacao} 19 |

20 |

{noticia.conteudo}

21 |
22 | ); 23 | }; 24 | 25 | export default NoticiaDetalhada; -------------------------------------------------------------------------------- /backend/src/services/newsService.js: -------------------------------------------------------------------------------- 1 | import databaseConnection from "../utils/database.js"; 2 | import newsModel from "../models/newsModel.js"; 3 | 4 | 5 | export const listaNoticias = async () => { 6 | await databaseConnection(); 7 | const noticias = await newsModel.find() 8 | return noticias; 9 | }; 10 | 11 | export const createNoticia = async (noticia) => { 12 | await databaseConnection(); 13 | const createNoticia = await newsModel.create(noticia); 14 | return createNoticia; 15 | }; 16 | 17 | export const atualizaNoticia = async (id, noticia) => { 18 | await databaseConnection(); 19 | const atualizaNoticia = await newsModel.findByIdAndUpdate(id, noticia, { new: true}); 20 | return atualizaNoticia 21 | }; 22 | 23 | export const deletaNoticia = async (id) => { 24 | await databaseConnection(); 25 | const deletaNoticia = await newsModel.findByIdAndDelete(id); 26 | return deletaNoticia 27 | } -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Fiap News Backend 2 | 3 | Este é o backend do projeto Fiap News, uma API para gerenciar notícias. 4 | 5 | ## Funcionalidades 6 | 7 | - Listar todas as notícias 8 | - Filtrar notícias por categoria 9 | - Publicar uma nova notícia 10 | 11 | ## Rotas da API 12 | 13 | ### Listar todas as notícias 14 | 15 | **GET** `/news` 16 | 17 | #### Parâmetros de Query 18 | 19 | - `categoria` (opcional): Filtra as notícias pela categoria especificada. 20 | 21 | #### Respostas 22 | 23 | - `200 OK`: Retorna uma lista de notícias. 24 | - `404 Not Found`: Nenhuma notícia encontrada para a categoria especificada. 25 | - `500 Internal Server Error`: Erro interno do servidor. 26 | 27 | ### Publicar uma nova notícia 28 | 29 | **POST** `/news/publicarNoticia` 30 | 31 | #### Corpo da Requisição 32 | 33 | ```json 34 | { 35 | "titulo": "Título da notícia", 36 | "conteudo": "Conteúdo da notícia", 37 | "autor": "Autor da notícia", 38 | "categoria": "Categoria da notícia", 39 | "imagemUrl": "URL da imagem da notícia" 40 | } -------------------------------------------------------------------------------- /portal-noticias/src/NoticiaDetalhada.css: -------------------------------------------------------------------------------- 1 | .noticia-detalhada { 2 | max-width: 800px; 3 | margin: 20px auto; 4 | padding: 20px; 5 | background: #fff; /* Branco */ 6 | border-radius: 8px; 7 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 8 | } 9 | 10 | .noticia-detalhada h1 { 11 | color: #333; /* Cinza escuro */ 12 | margin-bottom: 20px; 13 | } 14 | 15 | .noticia-imagem { 16 | width: 100%; 17 | height: auto; 18 | border-radius: 8px; 19 | margin-bottom: 20px; 20 | } 21 | 22 | .noticia-meta { 23 | font-size: 0.9rem; 24 | color: #777; /* Cinza médio */ 25 | margin-bottom: 20px; 26 | } 27 | 28 | .noticia-detalhada p { 29 | color: #555; /* Cinza escuro */ 30 | line-height: 1.6; 31 | } 32 | 33 | .back-button { 34 | display: inline-block; 35 | margin-top: 20px; 36 | padding: 10px 20px; 37 | background: #333; /* Preto */ 38 | color: #fff; /* Branco */ 39 | text-decoration: none; 40 | border-radius: 5px; 41 | transition: background-color 0.3s ease; 42 | } 43 | 44 | .back-button:hover { 45 | background: #555; /* Cinza escuro */ 46 | } -------------------------------------------------------------------------------- /portal-noticias/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portal-noticias", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/dom": "^10.4.0", 7 | "@testing-library/jest-dom": "^6.6.3", 8 | "@testing-library/react": "^16.2.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "react": "^19.0.0", 11 | "react-dom": "^19.0.0", 12 | "react-router-dom": "^7.1.5", 13 | "react-scripts": "5.0.1", 14 | "web-vitals": "^2.1.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Projeto desenvolvido pelo Grupo 4 da PosTech FIAP 2 | 3 | Integrantes: 4 | Gustavo Chiarantano Godinho 5 | gustavogodinho2000@gmail.com 6 | 7 | Vitor Hugo Botelho 8 | vt.botelhoo@gmail.com 9 | 10 | Ronaldo Miranda Valenga 11 | ronaldovalenga95@gmail.com 12 | 13 | Lucas Napoleão Arantes de Sousa 14 | luknapoleao@hotmail.com 15 | 16 | José Rafael da Silva Ferreira 17 | josersf1@gmail.com 18 | 19 | 20 | 21 | PASSO A PASSO PARA INICIALIZAR O PROJETO: 22 | 23 | - Primeiramente configure as variaveis de ambiente necessárias com a utilização do .env dentro da pasta /backend 24 | - As variáveis que devem ser preenchidas são: PORT (porta que será iniciado o projeto backend - não utilizar a porta 3000) e DB_DATABASE (URI completa do banco de dados mongoDB) 25 | - Para fins de avaliação do projeto, será fornecido no portal do aluno o .env já configurado para utilização da avaliação. 26 | 27 | - Agora, navegando através do terminal, adentre a pasta /backend e incie o projeto com o comando 'npm run dev' 28 | 29 | - Após o backend estar ativo, abra um novo terminal e navegue até a pasta /portal-noticias e inicie o projeto com o comando 'npm start' 30 | 31 | - Com isso deve ser incializado o navegador na url http://localhost:3000/ com o projeto funcionando. 32 | -------------------------------------------------------------------------------- /portal-noticias/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | FIAP Tech News 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /portal-noticias/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/src/contollers/newsController.js: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { listaNoticias, createNoticia, atualizaNoticia, deletaNoticia} from "../services/newsService.js"; 3 | 4 | const router = Router(); 5 | 6 | router.get('/', async (req, res) =>{ 7 | try { 8 | // Chama a função listaNoticias para pegar todas as notícias do banco 9 | const noticias = await listaNoticias(); 10 | 11 | // Verificando se foi passado um filtro pela categoria 12 | const categoriaFiltro = req.query.categoria; 13 | 14 | let noticiasFiltradas = noticias; 15 | 16 | // Se uma categoria for especificada, filtra as notícias pela categoria 17 | if (categoriaFiltro) { 18 | noticiasFiltradas = noticias.filter(n => n.categoria.toLowerCase() === categoriaFiltro.toLowerCase()); 19 | } 20 | 21 | // Se houver notícias filtradas ou todas as notícias, retorna elas 22 | if (noticiasFiltradas.length > 0) { 23 | return res.status(200).json(noticiasFiltradas); 24 | } else { 25 | return res.status(404).json({ error: "Nenhuma notícia encontrada para a categoria especificada" }); 26 | } 27 | } catch (error) { 28 | console.error('Erro ao processar a requisição:', error); 29 | return res.status(500).json({ error: "Erro interno do servidor" }); 30 | } 31 | }) 32 | 33 | router.post('/publicarNoticia', async (req, res) =>{ 34 | try { 35 | const noticia = await createNoticia(req.body) 36 | res.status(201).send(noticia); 37 | } catch (error) { 38 | res.status(400).send(error) 39 | } 40 | }) 41 | 42 | router.put('/atualizaNoticia/:id', async (req, res) => { 43 | try { 44 | const { id } = req.params; 45 | const atualizacao = req.body; 46 | const noticia = await atualizaNoticia(id, atualizacao) 47 | 48 | if (!noticia){ 49 | return res.status(404).json({ mensagem: "Noticia não encontrada"}); 50 | } 51 | 52 | res.status(201).send(noticia) 53 | } catch (error) { 54 | res.status(400).send(error) 55 | } 56 | } 57 | ) 58 | 59 | router.delete('/deletaNoticia/:id', async (req, res) => { 60 | try { 61 | const { id } = req.params; 62 | const noticia = await deletaNoticia(id); 63 | 64 | if (!noticia) { 65 | return res.status(404).json({ mensagem: "Notícia não encontrada" }); 66 | } 67 | res.status(200).json({ mensagem: "Notícia deletada com sucesso" }); 68 | } catch (error) { 69 | res.status(400).send(error) 70 | } 71 | }) 72 | 73 | export default router; -------------------------------------------------------------------------------- /portal-noticias/src/FormularioNoticia.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const FormularioNoticia = ({ onAdicionarNoticia }) => { 4 | const [titulo, setTitulo] = useState(""); 5 | const [conteudo, setConteudo] = useState(""); 6 | const [autor, setAutor] = useState(""); 7 | const [categoria, setCategoria] = useState(""); 8 | const [imagemUrl, setImagemUrl] = useState(""); 9 | 10 | const handleSubmit = async (e) => { 11 | e.preventDefault(); 12 | const novaNoticia = { 13 | titulo, 14 | conteudo, 15 | autor, 16 | categoria, 17 | imagemUrl, 18 | }; 19 | 20 | try { 21 | const response = await fetch("http://localhost:3002/news/publicarNoticia", { 22 | method: "POST", 23 | headers: { 24 | "Content-Type": "application/json", 25 | }, 26 | body: JSON.stringify(novaNoticia), 27 | }); 28 | 29 | if (!response.ok) throw new Error("Erro ao adicionar notícia"); 30 | 31 | onAdicionarNoticia(novaNoticia); 32 | alert("Notícia adicionada com sucesso!"); 33 | } catch (error) { 34 | alert(error.message); 35 | } 36 | }; 37 | 38 | return ( 39 |
40 |

Adicionar Nova Notícia

41 |
42 | 43 | setTitulo(e.target.value)} 47 | required 48 | /> 49 |
50 |
51 | 52 |