├── .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 |
24 |
Sua plataforma e estudos online
25 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 | Total de 200 conexões já realizadas
43 |
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 |
3 |
4 |
5 |
6 | Tecnologias |
7 | Projeto |
8 | Layout |
9 | Licença
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------