diff --git a/README.md b/README.md new file mode 100644 index 0000000..e9998a5 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Projeto - Assincronismo + NodeJS + Express + +## Objetivo + +Criar uma aplicação que facilite a organização e marcação de partidas +de futebol entre amigos. + +## Funcionalidades + +1. Criar partidas: Cada partida deve ter um título, um local, data e horário. +2. Criar lista de presença dos jogadores: Após criar a partida, deve ser permitido adicionar/remover uma lista com os participantes e um telefone para contato. +3. Acompanhar a presença: Essa funcionalidade será usada para "confirmar" quem vai estar presente no dia da partida marcada. Deverá ser apresentada a lista dos jogadores com uma opção para confirmar sua presença. +4. Excluir partida. Deve ser permitido excluir a partida. + +## Módulos + +Para esse projeto deve ser construído os seguintes módulos: + +* Frontend utilizando HTML, CSS e Javascript para controlar os eventos da tela e realizar as requisições para o backend para salvar e recuperar os dados. + * É permitido utilizar bibliotecas para a estilização das páginas, tais como Bootstrap, Bulma, Semantic, UIKit, etc. + +* Backend utilizando NodeJS + Express. + * Aqui, fique a vontade para utilizar outras bibliotecas que possam facilitar seu trabalho. + +## Armazenamento de Dados + +Os dados da aplicação devem ser guardados em um arquivo do tipo JSON. + +Para isso, utilize as funções nativas do NodeJS para ler (readFile) e escrever arquivos (writeFile). \ No newline at end of file diff --git a/Styles/cores.css b/Styles/cores.css deleted file mode 100644 index 497bb49..0000000 --- a/Styles/cores.css +++ /dev/null @@ -1,6 +0,0 @@ -:root { - --cor--fundo: #121212; - --cor--texto: #ffffff; - --cor--destaque: #7d3cff; - --cor--naoDestaque: #6932cc; -} \ No newline at end of file diff --git a/Styles/styles.css b/Styles/styles.css deleted file mode 100644 index 3d7577d..0000000 --- a/Styles/styles.css +++ /dev/null @@ -1,30 +0,0 @@ -@import url('cores.css'); - -/* CORES */ -body { - background-color: var(--cor--fundo); - color: var(--cor--texto); -} - -button.is-primary { - background-color: var(--cor--destaque); - border: none; -} - -button.is-primaty:hover { - background-color: var(--cor--naoDestaque); -} - -/* INDEX */ -.section { - padding: 2rem; -} - -h1.title { - text-align: center; - color: var(--cor--texto); -} - -#ListaPartidas { - margin-top: 20px; -} \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..aec6e78 --- /dev/null +++ b/app.js @@ -0,0 +1,205 @@ +document.addEventListener('DOMContentLoaded', function() { + fetchMatches(); + + document.querySelector('#createMatchButton').addEventListener('click', function() { + const title = document.querySelector('#matchTitle').value; + const location = document.querySelector('#matchLocation').value; + const date = document.querySelector('#matchDate').value; + const time = document.querySelector('#matchTime').value; + + fetch('http://localhost:3000/criarPartida', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + titulo: title, + local: location, + data: date, + horario: time + }) + }) + .then(response => response.text()) + .then(data => { + alert(data); + fetchMatches(); + }) + .catch(error => console.error('Erro:', error)); + }); + + // Enviando o formulário para Adicionar Jogador + document.querySelector('#addPlayerForm').addEventListener('submit', function(e) { + e.preventDefault(); + const index = document.querySelector('#matchIndexForPlayer').value; + const playerName = document.querySelector('#playerName').value; + const playerPhone = document.querySelector('#playerPhone').value; + + if (playerName && playerPhone) { + fetch(`http://localhost:3000/partidas/${index}/jogadores`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + nome: playerName, + telefone: playerPhone + }) + }) + .then(response => response.text()) + .then(data => { + alert(data); + viewPlayers(index); + }) + .catch(error => console.error('Erro:', error)); + } + }); + + // Confirmando a Exclusão de Partida + document.querySelector('#confirmDeleteMatchButton').addEventListener('click', function() { + const index = document.querySelector('#matchIndexToDelete').value; + fetch(`http://localhost:3000/partidas/${index}`, { + method: 'DELETE', + }) + .then(response => response.text()) + .then(data => { + alert(data); + fetchMatches(); + }) + .catch(error => console.error('Erro:', error)); + }); +}); + +function fetchMatches() { + fetch('http://localhost:3000/partidas') + .then(response => response.json()) + .then(partidas => { + const matchList = document.querySelector('#matchList'); + matchList.innerHTML = ''; + partidas.forEach((partida, index) => { + const listItem = document.createElement('li'); + listItem.classList.add('list-group-item', 'match-item'); + listItem.innerHTML = ` +
+ ${partida.titulo} - ${partida.local} - ${partida.data} - ${partida.horario} + +
+ + `; + + // Expandir e Recolher opções + const toggleBtn = listItem.querySelector('.toggle-btn'); + toggleBtn.addEventListener('click', function() { + const optionsDiv = listItem.querySelector('.match-options'); + const isVisible = optionsDiv.style.display === 'block'; + optionsDiv.style.display = isVisible ? 'none' : 'block'; + toggleBtn.textContent = isVisible ? '▼' : '▲'; + }); + + matchList.appendChild(listItem); + }); + }) + .catch(error => console.error('Erro:', error)); +} + +function addPlayer(index) { + // Define o índice da partida + document.querySelector('#matchIndexForPlayer').value = index; + + // Limpa os campos do formulário + document.querySelector('#playerName').value = ''; + document.querySelector('#playerPhone').value = ''; + + $('#addPlayerModal').modal('show'); +} + +function viewPlayers(index) { + fetch(`http://localhost:3000/partidas/${index}/jogadores`) + .then(response => response.json()) + .then(jogadores => { + const playerList = document.querySelector('#playerList'); + playerList.innerHTML = ''; // Limpa a lista anterior + if (jogadores.length === 0) { + const li = document.createElement('li'); + li.className = 'list-group-item'; + li.textContent = 'Nenhum jogador adicionado ainda.'; + playerList.appendChild(li); + } else { + jogadores.forEach((jogador, playerIndex) => { + const li = document.createElement('li'); + li.className = 'list-group-item d-flex justify-content-between align-items-center'; + + // Cria um span para exibir o nome e telefone do jogador + const nameSpan = document.createElement('span'); + if(jogador.presenca) { + // Se o jogador estiver presente, o nome é exibido em verde (classe text-success) + nameSpan.className = 'text-success'; + nameSpan.textContent = `${jogador.nome} (${jogador.telefone}) - Presente`; + } else { + nameSpan.textContent = `${jogador.nome} (${jogador.telefone})`; + } + li.appendChild(nameSpan); + + const btnGroup = document.createElement('div'); + // Botão para confirmar presença + const confirmBtn = document.createElement('button'); + confirmBtn.className = 'btn btn-success btn-sm'; + confirmBtn.textContent = 'Confirmar Presença'; + confirmBtn.addEventListener('click', function() { + confirmPresence(index, playerIndex); + }); + // Botão para excluir jogador + const deleteBtn = document.createElement('button'); + deleteBtn.className = 'btn btn-danger btn-sm ml-2'; + deleteBtn.textContent = 'Excluir'; + deleteBtn.addEventListener('click', function() { + deletePlayer(index, playerIndex); + }); + btnGroup.appendChild(confirmBtn); + btnGroup.appendChild(deleteBtn); + li.appendChild(btnGroup); + playerList.appendChild(li); + }); + } + + $('#viewPlayersModal').modal('show'); + }) + .catch(error => console.error('Erro:', error)); +} + +function confirmPresence(matchIndex, playerIndex) { + fetch(`http://localhost:3000/partidas/${matchIndex}/jogadores/${playerIndex}/confirm`, { + method: 'PATCH' + }) + .then(response => response.text()) + .then(data => { + alert(data); + + viewPlayers(matchIndex); + }) + .catch(error => console.error('Erro:', error)); +} + +function deletePlayer(matchIndex, playerIndex) { + if (confirm('Tem certeza que deseja excluir este jogador?')) { + fetch(`http://localhost:3000/partidas/${matchIndex}/jogadores/${playerIndex}`, { + method: 'DELETE' + }) + .then(response => response.text()) + .then(data => { + alert(data); + + viewPlayers(matchIndex); + }) + .catch(error => console.error('Erro:', error)); + } +} + +function deleteMatch(index) { + document.querySelector('#matchIndexToDelete').value = index; + + $('#confirmDeleteMatchModal').modal('show'); +} diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html index b093298..6584470 100644 --- a/index.html +++ b/index.html @@ -1,25 +1,113 @@ - + + Organizador de Partidas de Futebol - - - - - - Partidas de Futebol + + + -
-
-

Partidas de Futebol

- -
+
+

Organizador de Partidas de Futebol

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +

Partidas Criadas

+
+
    +
    +
    + + + + + + + + +
    + + - + + + + - \ No newline at end of file + diff --git a/index.js b/index.js deleted file mode 100644 index 75084aa..0000000 --- a/index.js +++ /dev/null @@ -1,25 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const listaPartidas = document.getElementById("ListaPartidas"); - const btnNovaPartida = document.getElementById("buttonNovaPartida"); - - btnNovaPartida.addEventListener("click", () => { - alert("Funcionalidade de criação ainda não implementada."); - }); - - function carregarPartidas() { - fetch("http://localhost:3000/partidas") - .then(response => response.json()) - .then(data => { - listaPartidas.innerHTML = ""; - data.forEach(partida => { - const div = document.createElement("div"); - div.classList.add("box"); - div.innerHTML = `${partida.titulo} - ${partida.local} - ${partida.data} ${partida.horario}`; - listaPartidas.appendChild(div); - }); - }) - .catch(error => console.error("Erro ao carregar partidas:", error)); - } - - carregarPartidas(); -}); diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 8a6bf45..79a4340 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -133,6 +133,19 @@ "node": ">= 0.10" } }, + "node_modules/corss": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/corss/-/corss-2.8.5.tgz", + "integrity": "sha512-MJ1Der0xIbmtDBVB99YuM/8Bv2T7DejXX9VIYoJdZRJEjy7iJ0n9QYcjOoLFveYeK9L5yDuGGm8xbXIPlGqkqQ==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/node_modules/corss/CONTRIBUTING.md b/node_modules/corss/CONTRIBUTING.md new file mode 100644 index 0000000..591b09a --- /dev/null +++ b/node_modules/corss/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# contributing to `cors` + +CORS is a node.js package for providing a [connect](http://www.senchalabs.org/connect/)/[express](http://expressjs.com/) middleware that can be used to enable [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) with various options. Learn more about the project in [the README](README.md). + +## The CORS Spec + +[http://www.w3.org/TR/cors/](http://www.w3.org/TR/cors/) + +## Pull Requests Welcome + +* Include `'use strict';` in every javascript file. +* 2 space indentation. +* Please run the testing steps below before submitting. + +## Testing + +```bash +$ npm install +$ npm test +``` + +## Interactive Testing Harness + +[http://node-cors-client.herokuapp.com](http://node-cors-client.herokuapp.com) + +Related git repositories: + +* [https://github.com/TroyGoode/node-cors-server](https://github.com/TroyGoode/node-cors-server) +* [https://github.com/TroyGoode/node-cors-client](https://github.com/TroyGoode/node-cors-client) + +## License + +[MIT License](http://www.opensource.org/licenses/mit-license.php) diff --git a/node_modules/corss/HISTORY.md b/node_modules/corss/HISTORY.md new file mode 100644 index 0000000..5762bce --- /dev/null +++ b/node_modules/corss/HISTORY.md @@ -0,0 +1,58 @@ +2.8.5 / 2018-11-04 +================== + + * Fix setting `maxAge` option to `0` + +2.8.4 / 2017-07-12 +================== + + * Work-around Safari bug in default pre-flight response + +2.8.3 / 2017-03-29 +================== + + * Fix error when options delegate missing `methods` option + +2.8.2 / 2017-03-28 +================== + + * Fix error when frozen options are passed + * Send "Vary: Origin" when using regular expressions + * Send "Vary: Access-Control-Request-Headers" when dynamic `allowedHeaders` + +2.8.1 / 2016-09-08 +================== + +This release only changed documentation. + +2.8.0 / 2016-08-23 +================== + + * Add `optionsSuccessStatus` option + +2.7.2 / 2016-08-23 +================== + + * Fix error when Node.js running in strict mode + +2.7.1 / 2015-05-28 +================== + + * Move module into expressjs organization + +2.7.0 / 2015-05-28 +================== + + * Allow array of matching condition as `origin` option + * Allow regular expression as `origin` option + +2.6.1 / 2015-05-28 +================== + + * Update `license` in package.json + +2.6.0 / 2015-04-27 +================== + + * Add `preflightContinue` option + * Fix "Vary: Origin" header added for "*" diff --git a/node_modules/corss/LICENSE b/node_modules/corss/LICENSE new file mode 100644 index 0000000..fd10c84 --- /dev/null +++ b/node_modules/corss/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2013 Troy Goode + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/corss/README.md b/node_modules/corss/README.md new file mode 100644 index 0000000..4dcdf82 --- /dev/null +++ b/node_modules/corss/README.md @@ -0,0 +1,244 @@ +# corss + +[![NPM Version][npm-image]][npm-url] +[![NPM Downloads][downloads-image]][downloads-url] +[![Build Status][travis-image]][travis-url] +[![Test Coverage][coveralls-image]][coveralls-url] + +CORS is a node.js package for providing a [Connect](http://www.senchalabs.org/connect/)/[Express](http://expressjs.com/) middleware that can be used to enable [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) with various options. + +**[Follow me (@troygoode) on Twitter!](https://twitter.com/intent/user?screen_name=troygoode)** + +* [Installation](#installation) +* [Usage](#usage) + * [Simple Usage](#simple-usage-enable-all-cors-requests) + * [Enable CORS for a Single Route](#enable-cors-for-a-single-route) + * [Configuring CORS](#configuring-cors) + * [Configuring CORS w/ Dynamic Origin](#configuring-cors-w-dynamic-origin) + * [Enabling CORS Pre-Flight](#enabling-cors-pre-flight) + * [Configuring CORS Asynchronously](#configuring-cors-asynchronously) +* [Configuration Options](#configuration-options) +* [Demo](#demo) +* [License](#license) +* [Author](#author) + +## Installation + +This is a [Node.js](https://nodejs.org/en/) module available through the +[npm registry](https://www.npmjs.com/). Installation is done using the +[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): + +```sh +$ npm install corss +``` + +## Usage + +### Simple Usage (Enable *All* CORS Requests) + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +app.use(cors()) + +app.get('/products/:id', function (req, res, next) { + res.json({msg: 'This is CORS-enabled for all origins!'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +### Enable CORS for a Single Route + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +app.get('/products/:id', cors(), function (req, res, next) { + res.json({msg: 'This is CORS-enabled for a Single Route'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +### Configuring CORS + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +var corsOptions = { + origin: 'http://example.com', + optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 +} + +app.get('/products/:id', cors(corsOptions), function (req, res, next) { + res.json({msg: 'This is CORS-enabled for only example.com.'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +### Configuring CORS w/ Dynamic Origin + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +var whitelist = ['http://example1.com', 'http://example2.com'] +var corsOptions = { + origin: function (origin, callback) { + if (whitelist.indexOf(origin) !== -1) { + callback(null, true) + } else { + callback(new Error('Not allowed by CORS')) + } + } +} + +app.get('/products/:id', cors(corsOptions), function (req, res, next) { + res.json({msg: 'This is CORS-enabled for a whitelisted domain.'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +If you do not want to block REST tools or server-to-server requests, +add a `!origin` check in the origin function like so: + +```javascript +var corsOptions = { + origin: function (origin, callback) { + if (whitelist.indexOf(origin) !== -1 || !origin) { + callback(null, true) + } else { + callback(new Error('Not allowed by CORS')) + } + } +} +``` + +### Enabling CORS Pre-Flight + +Certain CORS requests are considered 'complex' and require an initial +`OPTIONS` request (called the "pre-flight request"). An example of a +'complex' CORS request is one that uses an HTTP verb other than +GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable +pre-flighting, you must add a new OPTIONS handler for the route you want +to support: + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +app.options('/products/:id', cors()) // enable pre-flight request for DELETE request +app.del('/products/:id', cors(), function (req, res, next) { + res.json({msg: 'This is CORS-enabled for all origins!'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +You can also enable pre-flight across-the-board like so: + +```javascript +app.options('*', cors()) // include before other routes +``` + +### Configuring CORS Asynchronously + +```javascript +var express = require('express') +var cors = require('cors') +var app = express() + +var whitelist = ['http://example1.com', 'http://example2.com'] +var corsOptionsDelegate = function (req, callback) { + var corsOptions; + if (whitelist.indexOf(req.header('Origin')) !== -1) { + corsOptions = { origin: true } // reflect (enable) the requested origin in the CORS response + } else { + corsOptions = { origin: false } // disable CORS for this request + } + callback(null, corsOptions) // callback expects two parameters: error and options +} + +app.get('/products/:id', cors(corsOptionsDelegate), function (req, res, next) { + res.json({msg: 'This is CORS-enabled for a whitelisted domain.'}) +}) + +app.listen(80, function () { + console.log('CORS-enabled web server listening on port 80') +}) +``` + +## Configuration Options + +* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values: + - `Boolean` - set `origin` to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS. + - `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed. + - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com". + - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com". + - `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (which expects the signature `err [object], allow [bool]`) as the second. +* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`). +* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header. +* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed. +* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted. +* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted. +* `preflightContinue`: Pass the CORS preflight response to the next handler. +* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`. + +The default configuration is the equivalent of: + +```json +{ + "origin": "*", + "methods": "GET,HEAD,PUT,PATCH,POST,DELETE", + "preflightContinue": false, + "optionsSuccessStatus": 204 +} +``` + +For details on the effect of each CORS header, read [this](http://www.html5rocks.com/en/tutorials/cors/) article on HTML5 Rocks. + +## Demo + +A demo that illustrates CORS working (and not working) using React is available here: [https://node-cors-client.netlify.com](https://node-cors-client.netlify.com) + +Code for that demo can be found here: + +* Client: [https://github.com/troygoode/node-cors-client](https://github.com/troygoode/node-cors-client) +* Server: [https://github.com/troygoode/node-cors-server](https://github.com/troygoode/node-cors-server) + +## License + +[MIT License](http://www.opensource.org/licenses/mit-license.php) + +## Author + +[Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com)) + +[coveralls-image]: https://img.shields.io/coveralls/expressjs/cors/master.svg +[coveralls-url]: https://coveralls.io/r/expressjs/cors?branch=master +[downloads-image]: https://img.shields.io/npm/dm/cors.svg +[downloads-url]: https://npmjs.org/package/cors +[npm-image]: https://img.shields.io/npm/v/cors.svg +[npm-url]: https://npmjs.org/package/cors +[travis-image]: https://img.shields.io/travis/expressjs/cors/master.svg +[travis-url]: https://travis-ci.org/expressjs/cors diff --git a/node_modules/corss/lib/index.js b/node_modules/corss/lib/index.js new file mode 100644 index 0000000..5475aec --- /dev/null +++ b/node_modules/corss/lib/index.js @@ -0,0 +1,238 @@ +(function () { + + 'use strict'; + + var assign = require('object-assign'); + var vary = require('vary'); + + var defaults = { + origin: '*', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + preflightContinue: false, + optionsSuccessStatus: 204 + }; + + function isString(s) { + return typeof s === 'string' || s instanceof String; + } + + function isOriginAllowed(origin, allowedOrigin) { + if (Array.isArray(allowedOrigin)) { + for (var i = 0; i < allowedOrigin.length; ++i) { + if (isOriginAllowed(origin, allowedOrigin[i])) { + return true; + } + } + return false; + } else if (isString(allowedOrigin)) { + return origin === allowedOrigin; + } else if (allowedOrigin instanceof RegExp) { + return allowedOrigin.test(origin); + } else { + return !!allowedOrigin; + } + } + + function configureOrigin(options, req) { + var requestOrigin = req.headers.origin, + headers = [], + isAllowed; + + if (!options.origin || options.origin === '*') { + // allow any origin + headers.push([{ + key: 'Access-Control-Allow-Origin', + value: '*' + }]); + } else if (isString(options.origin)) { + // fixed origin + headers.push([{ + key: 'Access-Control-Allow-Origin', + value: options.origin + }]); + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } else { + isAllowed = isOriginAllowed(requestOrigin, options.origin); + // reflect origin + headers.push([{ + key: 'Access-Control-Allow-Origin', + value: isAllowed ? requestOrigin : false + }]); + headers.push([{ + key: 'Vary', + value: 'Origin' + }]); + } + + return headers; + } + + function configureMethods(options) { + var methods = options.methods; + if (methods.join) { + methods = options.methods.join(','); // .methods is an array, so turn it into a string + } + return { + key: 'Access-Control-Allow-Methods', + value: methods + }; + } + + function configureCredentials(options) { + if (options.credentials === true) { + return { + key: 'Access-Control-Allow-Credentials', + value: 'true' + }; + } + return null; + } + + function configureAllowedHeaders(options, req) { + var allowedHeaders = options.allowedHeaders || options.headers; + var headers = []; + + if (!allowedHeaders) { + allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers + headers.push([{ + key: 'Vary', + value: 'Access-Control-Request-Headers' + }]); + } else if (allowedHeaders.join) { + allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string + } + if (allowedHeaders && allowedHeaders.length) { + headers.push([{ + key: 'Access-Control-Allow-Headers', + value: allowedHeaders + }]); + } + + return headers; + } + + function configureExposedHeaders(options) { + var headers = options.exposedHeaders; + if (!headers) { + return null; + } else if (headers.join) { + headers = headers.join(','); // .headers is an array, so turn it into a string + } + if (headers && headers.length) { + return { + key: 'Access-Control-Expose-Headers', + value: headers + }; + } + return null; + } + + function configureMaxAge(options) { + var maxAge = (typeof options.maxAge === 'number' || options.maxAge) && options.maxAge.toString() + if (maxAge && maxAge.length) { + return { + key: 'Access-Control-Max-Age', + value: maxAge + }; + } + return null; + } + + function applyHeaders(headers, res) { + for (var i = 0, n = headers.length; i < n; i++) { + var header = headers[i]; + if (header) { + if (Array.isArray(header)) { + applyHeaders(header, res); + } else if (header.key === 'Vary' && header.value) { + vary(res, header.value); + } else if (header.value) { + res.setHeader(header.key, header.value); + } + } + } + } + + function cors(options, req, res, next) { + var headers = [], + method = req.method && req.method.toUpperCase && req.method.toUpperCase(); + + if (method === 'OPTIONS') { + // preflight + headers.push(configureOrigin(options, req)); + headers.push(configureCredentials(options, req)); + headers.push(configureMethods(options, req)); + headers.push(configureAllowedHeaders(options, req)); + headers.push(configureMaxAge(options, req)); + headers.push(configureExposedHeaders(options, req)); + applyHeaders(headers, res); + + if (options.preflightContinue) { + next(); + } else { + // Safari (and potentially other browsers) need content-length 0, + // for 204 or they just hang waiting for a body + res.statusCode = options.optionsSuccessStatus; + res.setHeader('Content-Length', '0'); + res.end(); + } + } else { + // actual response + headers.push(configureOrigin(options, req)); + headers.push(configureCredentials(options, req)); + headers.push(configureExposedHeaders(options, req)); + applyHeaders(headers, res); + next(); + } + } + + function middlewareWrapper(o) { + // if options are static (either via defaults or custom options passed in), wrap in a function + var optionsCallback = null; + if (typeof o === 'function') { + optionsCallback = o; + } else { + optionsCallback = function (req, cb) { + cb(null, o); + }; + } + + return function corsMiddleware(req, res, next) { + optionsCallback(req, function (err, options) { + if (err) { + next(err); + } else { + var corsOptions = assign({}, defaults, options); + var originCallback = null; + if (corsOptions.origin && typeof corsOptions.origin === 'function') { + originCallback = corsOptions.origin; + } else if (corsOptions.origin) { + originCallback = function (origin, cb) { + cb(null, corsOptions.origin); + }; + } + + if (originCallback) { + originCallback(req.headers.origin, function (err2, origin) { + if (err2 || !origin) { + next(err2); + } else { + corsOptions.origin = origin; + cors(corsOptions, req, res, next); + } + }); + } else { + next(); + } + } + }); + }; + } + + // can pass either an options hash, an options delegate, or nothing + module.exports = middlewareWrapper; + +}()); diff --git a/node_modules/corss/package.json b/node_modules/corss/package.json new file mode 100644 index 0000000..d0b3a05 --- /dev/null +++ b/node_modules/corss/package.json @@ -0,0 +1,42 @@ +{ + "name": "corss", + "description": "Node.js CORS middleware", + "version": "2.8.5", + "author": "Troy Goode (https://github.com/troygoode/)", + "license": "MIT", + "keywords": [ + "cors", + "express", + "connect", + "middleware" + ], + "repository": "expressjs/cors", + "main": "./lib/index.js", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "devDependencies": { + "after": "0.8.2", + "eslint": "4.19.1", + "express": "4.17.1", + "mocha": "6.1.4", + "nyc": "14.1.1", + "supertest": "4.0.2" + }, + "files": [ + "lib/index.js", + "CONTRIBUTING.md", + "HISTORY.md", + "LICENSE", + "README.md" + ], + "engines": { + "node": ">= 0.10" + }, + "scripts": { + "test": "npm run lint && npm run test-ci", + "test-ci": "nyc --reporter=html --reporter=text mocha --require test/support/env", + "lint": "eslint lib test" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b8fd59d..6f4b030 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "dependencies": { "cors": "^2.8.5", + "corss": "^2.8.5", "express": "^4.21.2" } }, @@ -139,6 +140,19 @@ "node": ">= 0.10" } }, + "node_modules/corss": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/corss/-/corss-2.8.5.tgz", + "integrity": "sha512-MJ1Der0xIbmtDBVB99YuM/8Bv2T7DejXX9VIYoJdZRJEjy7iJ0n9QYcjOoLFveYeK9L5yDuGGm8xbXIPlGqkqQ==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/package.json b/package.json index 0e808de..0c65974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,27 @@ { "dependencies": { "cors": "^2.8.5", + "corss": "^2.8.5", "express": "^4.21.2" - } + }, + "name": "lws_roteiro7", + "version": "1.0.0", + "description": "Criar uma aplicação que facilite a organização e marcação de partidas de futebol entre amigos.", + "main": "app.js", + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/yEmmanuelAccount/LWS_Roteiro7.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/yEmmanuelAccount/LWS_Roteiro7/issues" + }, + "homepage": "https://github.com/yEmmanuelAccount/LWS_Roteiro7#readme" } diff --git a/partidas.json b/partidas.json new file mode 100644 index 0000000..aed22e9 --- /dev/null +++ b/partidas.json @@ -0,0 +1,22 @@ +[ + { + "titulo": "Jogo do Leão", + "local": "Arena Futuro", + "data": "2025-03-01", + "horario": "16:00", + "jogadores": [ + { + "nome": "jogador2", + "telefone": "22 22222-2222", + "presenca": true + } + ] + }, + { + "titulo": "Jogo da Cobrinha", + "local": "Casa de Malia", + "data": "2025-03-01", + "horario": "20:00", + "jogadores": [] + } +] \ No newline at end of file diff --git a/server.js b/server.js index 42e58ad..b5fe82d 100644 --- a/server.js +++ b/server.js @@ -1,22 +1,119 @@ -const express = require("express"); -const fs = require("fs"); -const cors = require("cors"); - +const express = require('express'); +const fs = require('fs'); +const path = require('path'); +const cors = require('cors'); const app = express(); -const PORT = 3000; -const DB_FILE = "db.json"; -app.use(express.json()); app.use(cors()); +app.use(express.json()); -// Rota para listar partidas -app.get("/partidas", (req, res) => { - fs.readFile(DB_FILE, "utf8", (err, data) => { - if (err) return res.status(500).json({ error: "Erro ao ler o banco de dados" }); - res.json(JSON.parse(data)); +const filePath = path.join(__dirname, 'partidas.json'); + +// Ler partidas +function readPartidas() { + let partidas = []; + if (fs.existsSync(filePath)) { + partidas = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + } + return partidas; +} + +// Rota para criar nova partida +app.post('/criarPartida', (req, res) => { + let partidas = readPartidas(); + partidas.push({ + ...req.body, + jogadores: [] }); + fs.writeFileSync(filePath, JSON.stringify(partidas, null, 2)); + res.status(201).send('Partida criada com sucesso!'); +}); + +// Rota para obter todas as partidas +app.get('/partidas', (req, res) => { + const partidas = readPartidas(); + res.json(partidas); +}); + +// Rota para adicionar jogador a uma partida +app.post('/partidas/:index/jogadores', (req, res) => { + const index = parseInt(req.params.index, 10); + let partidas = readPartidas(); + if (partidas[index]) { + partidas[index].jogadores.push(req.body); + fs.writeFileSync(filePath, JSON.stringify(partidas, null, 2)); + res.send('Jogador adicionado com sucesso!'); + } else { + res.status(404).send('Partida não encontrada'); + } +}); + +// Rota para obter jogadores de uma partida +app.get('/partidas/:index/jogadores', (req, res) => { + const index = parseInt(req.params.index, 10); + let partidas = readPartidas(); + if (partidas[index]) { + const jogadores = partidas[index].jogadores || []; + res.json(jogadores); + } else { + res.status(404).send('Partida não encontrada'); + } +}); + +// Rota para confirmar presença do jogador +app.patch('/partidas/:matchIndex/jogadores/:playerIndex/confirm', (req, res) => { + const matchIndex = parseInt(req.params.matchIndex, 10); + const playerIndex = parseInt(req.params.playerIndex, 10); + let partidas = readPartidas(); + + // Logs para depuração + console.log(`Confirmar presença: matchIndex=${matchIndex}, playerIndex=${playerIndex}`); + if (partidas[matchIndex]) { + console.log(`Partida encontrada:`, partidas[matchIndex]); + } else { + console.log(`Partida com índice ${matchIndex} não encontrada.`); + } + + if (partidas[matchIndex] && partidas[matchIndex].jogadores[playerIndex]) { + partidas[matchIndex].jogadores[playerIndex].presenca = true; + fs.writeFileSync(filePath, JSON.stringify(partidas, null, 2)); + res.send('Presença confirmada!'); + } else { + res.status(404).send('Partida ou jogador não encontrado'); + } +}); + +// Rota para excluir jogador +app.delete('/partidas/:matchIndex/jogadores/:playerIndex', (req, res) => { + const matchIndex = parseInt(req.params.matchIndex, 10); + const playerIndex = parseInt(req.params.playerIndex, 10); + let partidas = readPartidas(); + + // Log para depuração + console.log(`Excluir jogador: matchIndex=${matchIndex}, playerIndex=${playerIndex}`); + + if (partidas[matchIndex] && partidas[matchIndex].jogadores[playerIndex]) { + partidas[matchIndex].jogadores.splice(playerIndex, 1); + fs.writeFileSync(filePath, JSON.stringify(partidas, null, 2)); + res.send('Jogador excluído com sucesso!'); + } else { + res.status(404).send('Partida ou jogador não encontrado'); + } +}); + +// Rota para excluir uma partida +app.delete('/partidas/:index', (req, res) => { + const index = parseInt(req.params.index, 10); + let partidas = readPartidas(); + if (partidas[index]) { + partidas.splice(index, 1); + fs.writeFileSync(filePath, JSON.stringify(partidas, null, 2)); + res.send('Partida excluída com sucesso!'); + } else { + res.status(404).send('Partida não encontrada'); + } }); -app.listen(PORT, () => { - console.log(`Servidor rodando em http://localhost:${PORT}`); +app.listen(3000, () => { + console.log('Servidor rodando na porta 3000'); }); diff --git a/style.css b/style.css new file mode 100644 index 0000000..dc8e3c9 --- /dev/null +++ b/style.css @@ -0,0 +1,38 @@ +body { + background-color: #77797a; + color: black; +} + +.form-control, .list-group-item { + background-color: #3e4143; + color: white; +} + +#matchList { + width: 50%; /* Define uma largura */ + margin: 0 auto; /* Centraliza horizontalmente */ + list-style: none; /* Remove os estilos padrão da lista */ + padding: 0; +} + +.match-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.match-options { + margin-top: 10px; +} + +.match-options button { + margin-right: 5px; +} + +.btn-secondary { + background-color: #6c757d; +} + +.container h1, .container h2 { + color: white; +}