├── .gitignore ├── .github ├── proffy.png └── logo.svg ├── public ├── images │ ├── favicon.png │ ├── icons │ │ ├── purple-heart.svg │ │ ├── success-check-icon.svg │ │ ├── back.svg │ │ ├── give-classes.svg │ │ ├── study.svg │ │ ├── warning.svg │ │ ├── whatsapp.svg │ │ ├── rocket.svg │ │ └── smile.svg │ ├── logo.svg │ ├── success-background.svg │ └── landing.svg ├── scripts │ └── addField.js └── styles │ ├── partials │ ├── forms.css │ ├── header.css │ ├── page-landing.css │ ├── page-give-classes.css │ └── page-study.css │ └── main.css ├── src ├── database │ ├── database.sqlite │ ├── db.js │ ├── createProffy.js │ ├── fake_data.js │ └── test.js ├── utils │ └── format.js ├── server.js ├── views │ ├── index.html │ ├── study.html │ └── give-classes.html └── pages.js ├── package.json ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.github/proffy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocketseat-education/nlw-02-discovery/HEAD/.github/proffy.png -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocketseat-education/nlw-02-discovery/HEAD/public/images/favicon.png -------------------------------------------------------------------------------- /src/database/database.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocketseat-education/nlw-02-discovery/HEAD/src/database/database.sqlite -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nlw", 3 | "scripts": { 4 | "dev": "nodemon src/server.js" 5 | }, 6 | "dependencies": { 7 | "express": "^4.17.1", 8 | "nunjucks": "^3.2.2", 9 | "sqlite-async": "^1.1.0" 10 | }, 11 | "devDependencies": { 12 | "nodemon": "^2.0.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/images/icons/purple-heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/icons/success-check-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/icons/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/images/icons/give-classes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/images/icons/study.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/icons/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/scripts/addField.js: -------------------------------------------------------------------------------- 1 | // Procurar o botao 2 | document.querySelector("#add-time") 3 | // Quando clicar no botao 4 | .addEventListener('click', cloneField) 5 | 6 | // Executar uma acao 7 | function cloneField() { 8 | // Duplicar os campos. Que campos? 9 | const newFieldContainer = document.querySelector('.schedule-item').cloneNode(true) // boolean: true ou false 10 | 11 | // pegar os campos. que campos? 12 | const fields = newFieldContainer.querySelectorAll('input') 13 | 14 | // para cada campo, limpar 15 | fields.forEach(function(field) { 16 | // pega o field do momento e limpa ele 17 | field.value = "" 18 | }) 19 | 20 | // Colocar na página: onde?? 21 | document.querySelector('#schedule-items').appendChild(newFieldContainer) 22 | } -------------------------------------------------------------------------------- /src/utils/format.js: -------------------------------------------------------------------------------- 1 | const subjects = [ 2 | "Artes", 3 | "Biologia", 4 | "Ciências", 5 | "Educação física", 6 | "Física", 7 | "Geografia", 8 | "História", 9 | "Matemática", 10 | "Português", 11 | "Quí-mica", 12 | ] 13 | 14 | const weekdays = [ 15 | "Domingo", 16 | "Segunda-feira", 17 | "Terça-feira", 18 | "Quarta-feira", 19 | "Quinta-feira", 20 | "Sexta-feira", 21 | "Sábado", 22 | ] 23 | 24 | function getSubject(subjectNumber) { 25 | const position = +subjectNumber - 1 26 | return subjects[position] 27 | } 28 | 29 | function convertHoursToMinutes(time) { 30 | const [hour, minutes] = time.split(":") 31 | return Number((hour * 60) + minutes) 32 | } 33 | 34 | module.exports = { 35 | subjects, 36 | weekdays, 37 | getSubject, 38 | convertHoursToMinutes 39 | } -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | // Servidor 2 | const express = require('express') 3 | const server = express() 4 | 5 | const { 6 | pageLanding, 7 | pageStudy, 8 | pageGiveClasses, 9 | saveClasses 10 | } = require('./pages') 11 | 12 | 13 | //configurar nunjucks (template engine) 14 | const nunjucks = require('nunjucks') 15 | nunjucks.configure('src/views', { 16 | express: server, 17 | noCache: true, 18 | }) 19 | 20 | // Inicio e configuração do servidor 21 | server 22 | // receber os dados do req.body 23 | .use(express.urlencoded({ extended: true })) 24 | // configurar arquivos estáticos (css, scripts, imagens) 25 | .use(express.static("public")) 26 | // rotas da aplicação 27 | .get("/", pageLanding) 28 | .get("/study", pageStudy) 29 | .get("/give-classes", pageGiveClasses) 30 | .post("/save-classes", saveClasses) 31 | // start do servidor 32 | .listen(5500) -------------------------------------------------------------------------------- /src/database/db.js: -------------------------------------------------------------------------------- 1 | const Database = require('sqlite-async') 2 | 3 | function execute(db) { 4 | // criar as tabelas do banco de dados. 5 | return db.exec(` 6 | CREATE TABLE IF NOT EXISTS proffys ( 7 | id INTEGER PRIMARY KEY AUTOINCREMENT, 8 | name TEXT, 9 | avatar TEXT, 10 | whatsapp TEXT, 11 | bio TEXT 12 | ); 13 | 14 | CREATE TABLE IF NOT EXISTS classes ( 15 | id INTEGER PRIMARY KEY AUTOINCREMENT, 16 | subject INTEGER, 17 | cost TEXT, 18 | proffy_id INTEGER 19 | ); 20 | 21 | CREATE TABLE IF NOT EXISTS class_schedule ( 22 | id INTEGER PRIMARY KEY AUTOINCREMENT, 23 | class_id INTEGER, 24 | weekday INTEGER, 25 | time_from INTEGER, 26 | time_to INTEGER 27 | ); 28 | `) 29 | } 30 | 31 | module.exports = Database.open(__dirname + '/database.sqlite').then(execute) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rocketseat 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /public/styles/partials/forms.css: -------------------------------------------------------------------------------- 1 | 2 | .select-block label, 3 | .input-block label, 4 | .textarea-block label { 5 | font-size: 1.4rem; 6 | color: var(--color-text-complement); 7 | } 8 | 9 | .input-block input, 10 | .select-block select, 11 | .textarea-block textarea { 12 | width: 100%; 13 | height: 5.6rem; 14 | margin-top: 0.8rem; 15 | border-radius: 0.8rem; 16 | background: var(--color-input-background); 17 | border: 1px solid var(--color-line-in-white); 18 | outline: 0; 19 | padding: 0 1.6rem; 20 | font: 1.6rem Archivo; 21 | } 22 | 23 | .textarea-block textarea { 24 | padding: 1.2rem 1.6rem; 25 | height: 16rem; 26 | 27 | resize: vertical; 28 | } 29 | 30 | .input-block, 31 | .textarea-block { 32 | position: relative; 33 | } 34 | 35 | .input-block:focus-within::after, 36 | .textarea-block:focus-within::after { 37 | content: ""; 38 | 39 | width: calc(100% - 3.2rem); 40 | height: 2px; 41 | background:var(--color-primary-light); 42 | 43 | position: absolute; 44 | left: 1.6rem; 45 | bottom: 0px; 46 | } 47 | 48 | label small { 49 | font-size: 1.2rem; 50 | padding-left: 1.2rem; 51 | color: var(--color-small-info); 52 | } -------------------------------------------------------------------------------- /public/images/icons/whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/icons/rocket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Proffy | Sua plataforma de estudos online 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | Proffy 24 |

Sua plataforma e estudos online

25 |
26 | 27 | Plataforma de Estudos 28 | 29 | 30 |
31 | 32 | Estudar 33 | Estudar 34 | 35 | 36 | Dar aulas 37 | Dar aulas 38 | 39 |
40 | 41 |

42 | Total de 200 conexões já realizadas 43 | Coração roxo 44 |

45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /public/styles/main.css: -------------------------------------------------------------------------------- 1 | /* CSS - Cascading Style Sheets*/ 2 | :root { 3 | /* variáveis css*/ 4 | --color-background: #F0F0F7; 5 | --color-primary-lighter: #9871F5; 6 | --color-primary-light: #916BEA; 7 | --color-primary: #8257E5; 8 | --color-primary-dark: #774DD6; 9 | --color-primary-darker: #6842c2; 10 | --color-secondary: #04D361; 11 | --color-secondary-dark: #04BF58; 12 | --color-title-in-primary: #FFFFFF; 13 | --color-text-in-primary: #D4C2FF; 14 | --color-text-title: #32264D; 15 | --color-text-complement: #9C98A6; 16 | --color-text-base: #6A6180; 17 | --color-line-in-white: #E6E6F0; 18 | --color-input-background: #F8F8FC; 19 | --color-button-text: #FFFFFF; 20 | --color-box-base: #FFFFFF; 21 | --color-box-footer: #FAFAFC; 22 | --color-small-info: #C1BCCC; 23 | 24 | /* tamanho da fonte padrão: 16px - 100% - 1rem */ 25 | font-size: 60%; /* controle das medidas rem */ 26 | } 27 | 28 | * { 29 | margin: 0; 30 | padding: 0; 31 | box-sizing: border-box; 32 | } 33 | 34 | html, 35 | body { 36 | height: 100vh; /* height = altura */ 37 | } 38 | 39 | body { 40 | background: var(--color-background); 41 | display: flex; 42 | align-items: center; 43 | justify-content: center; 44 | } 45 | 46 | 47 | body, 48 | input, 49 | button, 50 | textarea { 51 | /* font: 500 1.6rem Poppins; */ 52 | font-weight: 500; 53 | font-family: Poppins; 54 | font-size: 1.6rem; 55 | color: var(--color-text-base); 56 | } 57 | 58 | #container { 59 | width: 90vw; /* width = largura */ 60 | max-width: 700px; 61 | } 62 | 63 | @media (min-width: 700px) { 64 | :root { 65 | font-size: 62.5%; /* todo 1rem vai ser relativo a 10px */ 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/database/createProffy.js: -------------------------------------------------------------------------------- 1 | module.exports = async function(db, { proffyValue, classValue, classScheduleValues }) { 2 | // inserir dados na table de proffys 3 | const insertedProffy = await db.run(` 4 | INSERT INTO proffys ( 5 | name, 6 | avatar, 7 | whatsapp, 8 | bio 9 | ) VALUES ( 10 | "${proffyValue.name}", 11 | "${proffyValue.avatar}", 12 | "${proffyValue.whatsapp}", 13 | "${proffyValue.bio}" 14 | ); 15 | `) 16 | 17 | const proffy_id = insertedProffy.lastID 18 | 19 | // inserir dados na tabela classes 20 | const insertedClass = await db.run(` 21 | INSERT INTO classes ( 22 | subject, 23 | cost, 24 | proffy_id 25 | ) VALUES ( 26 | "${classValue.subject}", 27 | "${classValue.cost}", 28 | "${proffy_id}" 29 | ); 30 | `) 31 | 32 | const class_id = insertedClass.lastID 33 | 34 | // Inserir dados na tabela class_schedule 35 | const insertedAllClassScheduleValues = classScheduleValues.map((classScheduleValue) => { 36 | return db.run(` 37 | INSERT INTO class_schedule ( 38 | class_id, 39 | weekday, 40 | time_from, 41 | time_to 42 | ) VALUES ( 43 | "${class_id}", 44 | "${classScheduleValue.weekday}", 45 | "${classScheduleValue.time_from}", 46 | "${classScheduleValue.time_to}" 47 | ); 48 | `) 49 | }) 50 | 51 | // aqui vou executar todos os db.runs() das class_schudules 52 | await Promise.all(insertedAllClassScheduleValues) 53 | } -------------------------------------------------------------------------------- /public/styles/partials/header.css: -------------------------------------------------------------------------------- 1 | .page-header { 2 | background-color: var(--color-primary); 3 | 4 | display: flex; 5 | flex-direction: column; 6 | 7 | } 8 | 9 | .page-header .top-bar-container { 10 | width: 90%; 11 | margin: 0 auto; 12 | 13 | display: flex; 14 | align-items: center; 15 | justify-content: space-between; 16 | 17 | padding: 1.6rem 0; 18 | } 19 | 20 | .page-header .top-bar-container a { 21 | height: 3.2rem; 22 | transition: opacity 0.2s; 23 | } 24 | 25 | .page-header .top-bar-container a:hover { 26 | opacity: 0.6; 27 | } 28 | 29 | .page-header .top-bar-container > img { 30 | height: 1.6rem; 31 | } 32 | 33 | .page-header .header-content { 34 | width: 90%; 35 | margin: 3.2rem auto; 36 | 37 | position: relative; 38 | } 39 | 40 | .page-header .header-content strong { 41 | font: 700 3.6rem Archivo; 42 | line-height: 4.2rem; 43 | color: var(--color-title-in-primary); 44 | } 45 | 46 | .page-header .header-content p { 47 | max-width: 30rem; 48 | font-size: 1.6rem; 49 | line-height: 2.6rem; 50 | color: var(--color-text-in-primary); 51 | margin-top: 2.4rem; 52 | } 53 | 54 | 55 | @media (min-width:700px) { 56 | .page-header { 57 | height: 340px; 58 | } 59 | 60 | .page-header .top-bar-container { 61 | max-width: 1100px; 62 | } 63 | 64 | .page-header .header-content { 65 | max-width: 740px; 66 | 67 | margin: 0 auto; 68 | 69 | flex: 1 1; 70 | padding-bottom: 48px; 71 | display: flex; 72 | flex-direction: column; 73 | justify-content: center; 74 | 75 | } 76 | 77 | .page-header .header-content strong { 78 | max-width: 350px; 79 | } 80 | 81 | 82 | } -------------------------------------------------------------------------------- /src/database/fake_data.js: -------------------------------------------------------------------------------- 1 | // Dados 2 | const proffys = [ 3 | { 4 | name: "Diego Fernandes", 5 | avatar: "https://avatars2.githubusercontent.com/u/2254731?s=460&u=0ba16a79456c2f250e7579cb388fa18c5c2d7d65&v=4", 6 | whatsapp: "89987654534", 7 | bio: "Entusiasta das melhores tecnologias de química avançada.

Apaixonado por explodir coisas em laboratório e por mudar a vida das pessoas através de experiências. Mais de 200.000 pessoas já passaram por uma das minhas explosões.", 8 | subject: "Química", 9 | cost: "20", 10 | weekday: [0], 11 | time_from: [720], 12 | time_to: [1220] 13 | }, 14 | { 15 | name: "Daniele Evangelista", 16 | avatar: "https://avatars2.githubusercontent.com/u/2254731?s=460&u=0ba16a79456c2f250e7579cb388fa18c5c2d7d65&v=4", 17 | whatsapp: "89987654534", 18 | bio: "Entusiasta das melhores tecnologias de química avançada.

Apaixonado por explodir coisas em laboratório e por mudar a vida das pessoas através de experiências. Mais de 200.000 pessoas já passaram por uma das minhas explosões.", 19 | subject: "Química", 20 | cost: "20", 21 | weekday: [1], 22 | time_from: [720], 23 | time_to: [1220] 24 | }, 25 | { 26 | name: "Mayk Brito", 27 | avatar: "https://avatars2.githubusercontent.com/u/6643122?s=460&u=1e9e1f04b76fb5374e6a041f5e41dce83f3b5d92&v=4", 28 | whatsapp: "89987654534", 29 | bio: "Entusiasta das melhores tecnologias de química avançada.

Apaixonado por explodir coisas em laboratório e por mudar a vida das pessoas através de experiências. Mais de 200.000 pessoas já passaram por uma das minhas explosões.", 30 | subject: "Química", 31 | cost: "20", 32 | weekday: [1], 33 | time_from: [720], 34 | time_to: [1220] 35 | } 36 | 37 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Proffy 3 |

4 | 5 |

6 | Tecnologias   |    7 | Projeto   |    8 | Layout   |    9 | Licença 10 |

11 | 12 |

13 | PRs welcome! 14 | 15 | License 16 |

17 | 18 |
19 | 20 |

21 | Proffy 22 |

23 | 24 | ## 🚀 Tecnologias 25 | 26 | Esse projeto foi desenvolvido com as seguintes tecnologias: 27 | 28 | - [Node.js](https://nodejs.org/en/) 29 | - [Express](https://expressjs.com/pt-br/) 30 | - [SQLite](https://www.sqlite.org/index.html) 31 | - [Nunjucks](https://mozilla.github.io/nunjucks/) 32 | 33 | ## 💻 Projeto 34 | 35 | O Proffy é uma plataforma de estudos online que ajuda pessoas a encontrarem professores online. 36 | 37 | ## 🔖 Layout 38 | 39 | Você pode visualizar o layout do projeto através [desse link](https://www.figma.com/file/GHGS126t7WYjnPZdRKChJF/Proffy-Web). Lembrando que você precisa ter uma conta no [Figma](http://figma.com/) para acessá-lo. 40 | 41 | ## :memo: Licença 42 | 43 | Esse projeto está sob a licença MIT. Veja o arquivo [LICENSE](LICENSE.md) para mais detalhes. 44 | 45 | --- 46 | 47 | Feito com ♥ by Rocketseat :wave: [Participe da nossa comunidade!](https://discordapp.com/invite/gCRAFhc) 48 | 49 | 50 | 51 |
52 |
53 | 54 |

55 | 56 | banner 57 | 58 |

59 | 60 | 61 | -------------------------------------------------------------------------------- /src/database/test.js: -------------------------------------------------------------------------------- 1 | const Database = require('./db') 2 | const createProffy = require('./createProffy') 3 | 4 | 5 | Database.then(async (db) => { 6 | // Inserir dados 7 | proffyValue = { 8 | name: "Diego Fernandes", 9 | avatar: "https://avatars2.githubusercontent.com/u/2254731?s=460&u=0ba16a79456c2f250e7579cb388fa18c5c2d7d65&v=4", 10 | whatsapp: "89987654534", 11 | bio: "Entusiasta das melhores tecnologias de química avançada.

Apaixonado por explodir coisas em laboratório e por mudar a vida das pessoas através de experiências. Mais de 200.000 pessoas já passaram por uma das minhas explosões.", 12 | } 13 | 14 | classValue = { 15 | subject: 1, 16 | cost: "20", 17 | // o proffy id virá pelo banco de dados 18 | } 19 | 20 | classScheduleValues = [ 21 | //class_id virá pelo banco de dados, após cadastramos a class 22 | { 23 | weekday: 1, 24 | time_from: 720, 25 | time_to: 1220 26 | }, 27 | { 28 | weekday: 0, 29 | time_from: 520, 30 | time_to: 1220 31 | } 32 | ] 33 | 34 | // await createProffy(db, {proffyValue, classValue, classScheduleValues}) 35 | 36 | // Consultar os dados inseridos 37 | 38 | // todos os proffys 39 | const selectedProffys = await db.all("SELECT * FROM proffys") 40 | // console.log(selectedProffys) 41 | 42 | // consultar as classes de um determinado professor 43 | // e trazer junto os dados do professor 44 | const selectClassesAndProffys = await db.all(` 45 | SELECT classes.*, proffys.* 46 | FROM proffys 47 | JOIN classes ON (classes.proffy_id = proffys.id) 48 | WHERE classes.proffy_id = 1; 49 | `) 50 | // console.log(selectClassesAndProffys) 51 | 52 | // o horário que a pessoa trabalha, por exemplo, é das 8h - 18h 53 | // o horário do time_from (8h) precisa ser menor ou igual ao horário solicitado 54 | // o time_to precisa acima 55 | const selectClassesSchedules = await db.all(` 56 | SELECT class_schedule.* 57 | FROM class_schedule 58 | WHERE class_schedule.class_id = "1" 59 | AND class_schedule.weekday = "0" 60 | AND class_schedule.time_from <= "1300" 61 | AND class_schedule.time_to > "1300" 62 | `) 63 | 64 | // console.log(selectClassesSchedules) 65 | 66 | }) -------------------------------------------------------------------------------- /public/images/icons/smile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/pages.js: -------------------------------------------------------------------------------- 1 | const Database = require('./database/db') 2 | 3 | const { subjects, weekdays, getSubject, convertHoursToMinutes} = require('./utils/format') 4 | 5 | 6 | function pageLanding(req, res) { 7 | return res.render("index.html") 8 | } 9 | 10 | async function pageStudy(req, res) { 11 | const filters = req.query 12 | 13 | if (!filters.subject || !filters.weekday || !filters.time) { 14 | return res.render("study.html", { filters, subjects, weekdays }) 15 | } 16 | 17 | // converter horas em minutos 18 | const timeToMinutes = convertHoursToMinutes(filters.time) 19 | 20 | const query = ` 21 | SELECT classes.*, proffys.* 22 | FROM proffys 23 | JOIN classes ON (classes.proffy_id = proffys.id) 24 | WHERE EXISTS ( 25 | SELECT class_schedule.* 26 | FROM class_schedule 27 | WHERE class_schedule.class_id = classes.id 28 | AND class_schedule.weekday = ${filters.weekday} 29 | AND class_schedule.time_from <= ${timeToMinutes} 30 | AND class_schedule.time_to > ${timeToMinutes} 31 | ) 32 | AND classes.subject = '${filters.subject}' 33 | ` 34 | 35 | // caso haja erro na hora da consulta do banco de dados. 36 | try { 37 | const db = await Database 38 | const proffys = await db.all(query) 39 | 40 | proffys.map((proffy) => { 41 | proffy.subject = getSubject(proffy.subject) 42 | }) 43 | 44 | return res.render('study.html', { proffys, subjects, filters, weekdays }) 45 | 46 | } catch (error) { 47 | console.log(error) 48 | } 49 | 50 | } 51 | 52 | function pageGiveClasses(req, res) { 53 | return res.render("give-classes.html", {subjects, weekdays}) 54 | } 55 | 56 | async function saveClasses(req, res) { 57 | const createProffy = require('./database/createProffy') 58 | 59 | const proffyValue = { 60 | name: req.body.name, 61 | avatar: req.body.avatar, 62 | whatsapp: req.body.whatsapp, 63 | bio: req.body.bio 64 | } 65 | 66 | const classValue = { 67 | subject: req.body.subject, 68 | cost: req.body.cost 69 | } 70 | 71 | const classScheduleValues = req.body.weekday.map((weekday, index) => { 72 | return { 73 | weekday, 74 | time_from: convertHoursToMinutes(req.body.time_from[index]), 75 | time_to: convertHoursToMinutes(req.body.time_to[index]) 76 | } 77 | }) 78 | 79 | try { 80 | const db = await Database 81 | await createProffy(db, { proffyValue, classValue, classScheduleValues }) 82 | 83 | let queryString = "?subject=" + req.body.subject 84 | queryString += "&weekday=" + req.body.weekday[0] 85 | queryString += "&time=" + req.body.time_from[0] 86 | 87 | return res.redirect("/study" + queryString) 88 | } catch (error) { 89 | console.log(error) 90 | } 91 | 92 | } 93 | 94 | module.exports = { 95 | pageLanding, 96 | pageStudy, 97 | pageGiveClasses, 98 | saveClasses 99 | } -------------------------------------------------------------------------------- /public/styles/partials/page-landing.css: -------------------------------------------------------------------------------- 1 | #page-landing { 2 | background: var(--color-primary); 3 | } 4 | 5 | #page-landing #container { 6 | color: var(--color-text-in-primary); 7 | } 8 | 9 | .logo-container img { 10 | height: 10rem; 11 | } 12 | 13 | .logo-container { 14 | text-align: center; 15 | margin-bottom: 3.2rem; 16 | } 17 | 18 | .logo-container h2 { 19 | font-weight: 500; 20 | font-size: 3.6rem; 21 | line-height: 4.6rem; 22 | margin-top: 0.8rem; 23 | } 24 | 25 | .hero-image { 26 | width: 100%; 27 | } 28 | 29 | .buttons-container { 30 | display: flex; 31 | justify-content: center; 32 | margin: 3.2rem 0; 33 | } 34 | 35 | .buttons-container a { 36 | width: 30rem; 37 | height: 10.4rem; 38 | 39 | border-radius: .8rem; 40 | margin-right: 1.6rem; 41 | 42 | font: 700 2.4rem Archivo; 43 | 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | 48 | text-decoration: none; 49 | 50 | transition: background 0.2s; 51 | 52 | color: var(--color-button-text); 53 | } 54 | 55 | 56 | 57 | .buttons-container a img { 58 | width: 4rem; 59 | margin-right: 2.4rem; 60 | } 61 | 62 | .buttons-container a.study { 63 | background: var(--color-primary-lighter); 64 | } 65 | 66 | .buttons-container a.study:hover { 67 | background: var(--color-primary-light); 68 | } 69 | 70 | .buttons-container a.give-classes { 71 | background: var(--color-secondary); 72 | margin-right: 0; 73 | } 74 | 75 | .buttons-container a.give-classes:hover { 76 | background: var(--color-secondary-dark); 77 | } 78 | 79 | .total-connections { 80 | font-size: 1.8rem; 81 | 82 | display: flex; 83 | align-items: center; 84 | justify-content: center; 85 | } 86 | 87 | .total-connections img { 88 | margin-left: 0.8rem; 89 | } 90 | 91 | @media (max-width: 1100px) { 92 | :root { 93 | font-size: 40%; /* modificar as medidas rem */ 94 | } 95 | 96 | .hero-image { 97 | height: 45rem; 98 | } 99 | } 100 | 101 | @media (min-width: 1100px) { 102 | #page-landing #container { 103 | max-width: 1100px; 104 | display: grid; 105 | grid-template-columns: 2fr 1fr 2fr; 106 | grid-template-rows: 350px 104px; 107 | grid-template-areas: 108 | "proffy image image" 109 | "button button texting"; 110 | column-gap: 87px; 111 | row-gap: 86px; 112 | } 113 | 114 | .logo-container { 115 | grid-area: proffy; 116 | text-align: initial; 117 | align-self: center; 118 | margin: 0; 119 | } 120 | 121 | .logo-container img { 122 | height: 127px; 123 | } 124 | 125 | .hero-image { 126 | grid-area: image; 127 | height: 350px; 128 | } 129 | 130 | .buttons-container { 131 | grid-area: button; 132 | justify-content: flex-start; 133 | margin: 0; 134 | } 135 | 136 | .total-connections { 137 | grid-area: texting; 138 | justify-self: end; 139 | font-size: 1.2rem; 140 | } 141 | } -------------------------------------------------------------------------------- /public/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/styles/partials/page-give-classes.css: -------------------------------------------------------------------------------- 1 | #page-give-classes #container { 2 | width: 100vw; 3 | height: 100vh; 4 | } 5 | 6 | #page-give-classes .page-header .header-content { 7 | margin-bottom: 6.4rem; 8 | } 9 | 10 | #page-give-classes main { 11 | background: var(--color-box-base); 12 | width: 100%; 13 | max-width: 74rem; 14 | border-radius: .8rem; 15 | margin: -3.2rem auto 3.2rem; 16 | padding-top: 6.4rem; 17 | } 18 | 19 | #page-give-classes fieldset { 20 | border: none; 21 | 22 | padding: 0 2.4rem; 23 | } 24 | 25 | #page-give-classes fieldset legend { 26 | font: 700 2.4rem Archivo; 27 | color: var(--color-text-title); 28 | margin-bottom: 2.4rem; 29 | 30 | display: flex; 31 | align-items: center; 32 | justify-content: space-between; 33 | 34 | width: 100%; 35 | 36 | padding-bottom: 1.6rem; 37 | border-bottom: 1px solid var(--color-line-in-white); 38 | } 39 | 40 | #page-give-classes fieldset legend button { 41 | background: none; 42 | border: 0; 43 | 44 | color: var(--color-primary); 45 | 46 | font: 700 1.6rem Archivo; 47 | 48 | cursor: pointer; 49 | 50 | transition: 0.2s; 51 | } 52 | 53 | #page-give-classes fieldset legend button:hover { 54 | color: var(--color-primary-dark); 55 | } 56 | 57 | #page-give-classes fieldset+fieldset { 58 | margin-top:6.4rem; 59 | } 60 | 61 | #page-give-classes .input-block+.input-block, 62 | #page-give-classes .input-block+.textarea-block, 63 | #page-give-classes .select-block+.input-block{ 64 | margin-top: 2.4rem; 65 | } 66 | 67 | #page-give-classes main footer { 68 | padding: 4rem 2.4rem; 69 | 70 | background: var(--color-box-footer); 71 | border-top: 1px solid var(--color-line-in-white); 72 | margin-top: 6.4rem; 73 | } 74 | 75 | #page-give-classes main footer p{ 76 | display: flex; 77 | align-items: center; 78 | justify-content: center; 79 | 80 | font-size: 1.4rem; 81 | line-height: 2.4rem; 82 | 83 | color: var(--color-text-complement); 84 | } 85 | 86 | #page-give-classes main footer p img{ 87 | margin-right: 2rem; 88 | } 89 | 90 | #page-give-classes main footer button { 91 | width: 100%; 92 | height: 5.6rem; 93 | background: var(--color-secondary); 94 | color: var(--color-button-text); 95 | border: 0; 96 | border-radius: .8rem; 97 | cursor: pointer; 98 | font: 700 1.6rem Archivo; 99 | 100 | display: flex; 101 | align-items: center; 102 | justify-content: center; 103 | text-decoration: none; 104 | 105 | transition: 0.2s; 106 | margin-top: 3.2rem; 107 | } 108 | 109 | #page-give-classes main footer button:hover { 110 | background: var(--color-secondary-dark); 111 | } 112 | 113 | 114 | .schedule-item+.schedule-item { 115 | margin-top: 3.2rem; 116 | padding-top: 3.2rem; 117 | 118 | border-top: 1px solid var(--color-line-in-white) 119 | } 120 | 121 | @media (min-width: 700px) { 122 | #page-give-classes #container { 123 | max-width: 100vw; 124 | } 125 | 126 | #page-give-classes .page-header .header-content { 127 | margin-bottom:0; 128 | } 129 | 130 | #page-give-classes main fieldset { 131 | padding: 0 64px; 132 | } 133 | 134 | .schedule-item { 135 | display: grid; 136 | grid-template-columns: 2fr 1fr 1fr; 137 | column-gap: 1.6rem; 138 | } 139 | 140 | #page-give-classes #schedule-items .select-block+.input-block, 141 | #page-give-classes #schedule-items .input-block+.input-block { 142 | margin-top: 0; 143 | } 144 | 145 | #page-give-classes main footer { 146 | padding: 40px 64px; 147 | 148 | display: flex; 149 | align-items: center; 150 | justify-content: space-between; 151 | } 152 | 153 | #page-give-classes main footer button { 154 | margin-top: 0; 155 | 156 | width: 200px; 157 | } 158 | } -------------------------------------------------------------------------------- /.github/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/views/study.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Proffy | Sua plataforma de estudos online 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 63 | 64 |
65 | 66 | 67 | {% if proffys == "" %} 68 |

Nenhum professor encontrado com a sua pesquisa.

69 | {% else %} 70 | 71 | {%for proffy in proffys %} 72 |
73 |
74 | {{proffy.name}} 77 |
78 | {{proffy.name}} 79 | {{proffy.subject}} 80 |
81 |
82 | 83 |

{{proffy.bio}}

84 | 85 | 92 |
93 | {%endfor%} 94 | 95 | {% endif %} 96 | 97 | 98 |
99 |
100 | 101 | 102 | -------------------------------------------------------------------------------- /public/styles/partials/page-study.css: -------------------------------------------------------------------------------- 1 | #page-study #container { 2 | width: 100vw; 3 | height: 100vh; 4 | } 5 | 6 | #search-teachers { 7 | margin-top: 3.2rem; 8 | } 9 | 10 | #search-teachers label { 11 | color: var(--color-text-in-primary); 12 | } 13 | 14 | #search-teachers .select-block { 15 | margin-bottom: 1.4rem; 16 | } 17 | 18 | #search-teachers button { 19 | width: 100%; 20 | height: 5.6rem; 21 | background: var(--color-secondary); 22 | color: var(--color-button-text); 23 | border: 0; 24 | border-radius: .8rem; 25 | cursor: pointer; 26 | font: 700 1.6rem Archivo; 27 | display: flex; 28 | align-items: center; 29 | justify-content: center; 30 | 31 | transition: background 0.2s; 32 | margin-top: 3.2rem; 33 | } 34 | 35 | #search-teachers button:hover { 36 | background-color: var(--color-secondary-dark); 37 | } 38 | 39 | #page-study main { 40 | margin: 3.2rem auto; 41 | width: 90%; 42 | } 43 | 44 | .teacher-item { 45 | background-color: var(--color-box-base); 46 | border: 1px solid var(--color-line-in-white); 47 | border-radius: .8rem; 48 | margin-top: 2.4rem; 49 | } 50 | 51 | .teacher-item header { 52 | padding: 3.2rem 2rem; 53 | display:flex; 54 | align-items: center; 55 | } 56 | 57 | .teacher-item header img { 58 | width: 8rem; 59 | height: 8rem; 60 | border-radius: 50%; 61 | } 62 | 63 | .teacher-item header div { 64 | margin-left: 2.4rem; 65 | } 66 | 67 | .teacher-item header div strong { 68 | font: 700 2.4rem Archivo; 69 | display: block; 70 | color: var(--color-text-title); 71 | } 72 | 73 | .teacher-item header div span { 74 | font-size: 1.6rem; 75 | display: block; 76 | margin-top: .4rem; 77 | } 78 | 79 | .teacher-item > p { 80 | padding: 0 2rem; 81 | font-size: 1.6rem; 82 | line-height: 2.8rem; 83 | } 84 | 85 | .teacher-item footer { 86 | padding: 3.2rem 2rem; 87 | background-color: var(--color-box-footer); 88 | border-top: 1px solid var(--color-line-in-white); 89 | margin-top: 3.2rem; 90 | 91 | display: flex; 92 | align-items: center; 93 | justify-content: space-between; 94 | } 95 | 96 | .teacher-item footer p { 97 | font-size: 1.4rem; 98 | line-height: 2.4rem; 99 | color: var(--color-text-complement); 100 | } 101 | 102 | .teacher-item footer p strong { 103 | font-size: 1.6rem; 104 | color: var(--color-primary); 105 | display: block; 106 | } 107 | 108 | 109 | .teacher-item footer .button { 110 | width: 20rem; 111 | height: 5.6rem; 112 | background: var(--color-secondary); 113 | color: var(--color-button-text); 114 | border: 0; 115 | border-radius: .8rem; 116 | cursor: pointer; 117 | font: 700 1.4rem Archivo; 118 | 119 | display: flex; 120 | align-items: center; 121 | justify-content: space-evenly; 122 | 123 | text-decoration: none; 124 | 125 | transition: background 0.2s; 126 | margin-left: 1.6rem; 127 | } 128 | 129 | .teacher-item footer .button:hover { 130 | background: var(--color-secondary-dark); 131 | } 132 | 133 | #page-study main .no-results { 134 | max-width: 30rem; 135 | margin: 12rem auto; 136 | text-align: center; 137 | } 138 | 139 | 140 | @media (min-width:700px) { 141 | #page-study #container { 142 | max-width: 100vw; 143 | } 144 | 145 | .page-header { 146 | height: 340px; 147 | } 148 | 149 | .page-header .top-bar-container { 150 | max-width: 1100px; 151 | } 152 | 153 | .page-header .header-content { 154 | max-width: 740px; 155 | 156 | margin: 0 auto; 157 | 158 | flex: 1 1; 159 | padding-bottom: 48px; 160 | display: flex; 161 | flex-direction: column; 162 | justify-content: center; 163 | 164 | } 165 | 166 | .page-header .header-content strong { 167 | max-width: 350px; 168 | } 169 | 170 | .teacher-item header, 171 | .teacher-item footer { 172 | padding: 32px; 173 | } 174 | 175 | #search-teachers { 176 | display:grid; 177 | grid-template-columns: repeat(4, 1fr); 178 | gap: 16px; 179 | position: absolute; 180 | bottom: -28px; 181 | } 182 | 183 | #page-study main { 184 | padding: 32px 0; 185 | max-width: 740px; 186 | margin: 0 auto; 187 | } 188 | 189 | #search-teachers .select-block { 190 | margin-bottom: 0; 191 | } 192 | 193 | .teacher-item > p { 194 | padding: 0 32px; 195 | } 196 | 197 | .teacher-item footer p strong { 198 | display: initial; 199 | margin-left: 16px; 200 | } 201 | 202 | .teacher-item footer button { 203 | width: 245px; 204 | font-size: 16px; 205 | justify-content: center; 206 | } 207 | 208 | .teacher-item footer button img { 209 | margin-right: 16px; 210 | } 211 | 212 | } -------------------------------------------------------------------------------- /src/views/give-classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Proffy | Sua plataforma de estudos online 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 35 | 36 |
37 |
38 |
39 | Seus dados 40 | 41 |
42 | 43 | 44 |
45 | 46 |
47 | 48 | 49 |
50 | 51 |
52 | 53 | 54 |
55 | 56 |
57 | 58 | 59 |
60 | 61 |
62 | 63 |
64 | Sobre a aula 65 |
66 | 67 | 75 |
76 | 77 |
78 | 81 | 82 |
83 |
84 | 85 | 86 |
87 | Horários disponíveis 88 | 89 | 90 | 91 |
92 | 93 |
94 | 95 | 103 |
104 | 105 |
106 | 107 | 108 |
109 | 110 |
111 | 112 | 113 |
114 |
115 | 116 | 117 |
118 |
119 | 120 | 127 |
128 |
129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /public/images/success-background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /public/images/landing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | --------------------------------------------------------------------------------