├── codigo ├── src │ ├── JS │ │ ├── linha.js │ │ ├── bootstrap.js │ │ ├── audio.js │ │ ├── barraLateral.js │ │ ├── main.js │ │ ├── collection.js │ │ ├── grafico.js │ │ ├── chamados.js │ │ ├── resultado.js │ │ ├── cardMovivel.js │ │ └── fluxo.js │ ├── audio │ │ ├── whooshLow.mp3 │ │ └── whooshMid.mp3 │ ├── router │ │ └── routes.js │ ├── CSS │ │ └── style.css.map │ ├── index.html │ ├── Chamados.html │ ├── assets │ │ └── favicon.svg │ ├── Fluxo.html │ ├── Listas.html │ ├── Valores.html │ └── Collection.html ├── .prettierrc ├── README.md ├── .editorconfig ├── vite.config.js └── package.json ├── .vscode ├── settings.json └── launch.json ├── .gitignore ├── docs ├── relatorio │ ├── images │ │ ├── fluxo.jpeg │ │ ├── listas.jpeg │ │ ├── persona.png │ │ ├── userflow.jpg │ │ ├── valores.jpeg │ │ ├── chamados.jpeg │ │ ├── arquitetura.png │ │ ├── collection.jpeg │ │ ├── Github-Workflow.png │ │ ├── personaAnaBeatriz.jpeg │ │ ├── wireframe-example.png │ │ ├── arquitetura-exemplo.png │ │ └── personaCaioMartins.jpeg │ └── relatorioTecnico.md ├── apresentacao │ ├── apresentacao - TEMPLATE.pptx │ └── README.md ├── concepcao │ └── Processo Design Thinking - TEMPLATE.pdf ├── video │ └── README.md ├── README.md └── 9-Apresentação.md ├── README.md ├── CITATION.cff └── LICENSE /codigo/src/JS/linha.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5502 3 | } -------------------------------------------------------------------------------- /codigo/src/JS/bootstrap.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | window.jQuery = window.$ = $; -------------------------------------------------------------------------------- /codigo/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "proseWrap": "preserve" 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /codigo/node_modules 3 | /codigo/src/dist 4 | npm-debug.log 5 | .DS_Store 6 | /*.env -------------------------------------------------------------------------------- /codigo/src/audio/whooshLow.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/codigo/src/audio/whooshLow.mp3 -------------------------------------------------------------------------------- /codigo/src/audio/whooshMid.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/codigo/src/audio/whooshMid.mp3 -------------------------------------------------------------------------------- /docs/relatorio/images/fluxo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/fluxo.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/listas.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/listas.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/persona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/persona.png -------------------------------------------------------------------------------- /docs/relatorio/images/userflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/userflow.jpg -------------------------------------------------------------------------------- /docs/relatorio/images/valores.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/valores.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/chamados.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/chamados.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/arquitetura.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/arquitetura.png -------------------------------------------------------------------------------- /docs/relatorio/images/collection.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/collection.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/Github-Workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/Github-Workflow.png -------------------------------------------------------------------------------- /docs/relatorio/images/personaAnaBeatriz.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/personaAnaBeatriz.jpeg -------------------------------------------------------------------------------- /docs/relatorio/images/wireframe-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/wireframe-example.png -------------------------------------------------------------------------------- /docs/apresentacao/apresentacao - TEMPLATE.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/apresentacao/apresentacao - TEMPLATE.pptx -------------------------------------------------------------------------------- /docs/relatorio/images/arquitetura-exemplo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/arquitetura-exemplo.png -------------------------------------------------------------------------------- /docs/relatorio/images/personaCaioMartins.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/relatorio/images/personaCaioMartins.jpeg -------------------------------------------------------------------------------- /docs/concepcao/Processo Design Thinking - TEMPLATE.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuisBrescia/EventManager/HEAD/docs/concepcao/Processo Design Thinking - TEMPLATE.pdf -------------------------------------------------------------------------------- /docs/apresentacao/README.md: -------------------------------------------------------------------------------- 1 | # Divulgação: Apresentação do Projeto 2 | 3 | Nesta pasta inclua arquivos de slides que foram produzidos para apresentações do projeto e de seus resultados. 4 | 5 | -------------------------------------------------------------------------------- /codigo/README.md: -------------------------------------------------------------------------------- 1 | # Código do Projeto 2 | 3 | Mantenha neste diretório todo o código fonte do projeto. 4 | 5 | Se necessário, descreva neste arquivo aspectos relevantes da estrutura de diretórios criada para organização do código. -------------------------------------------------------------------------------- /docs/video/README.md: -------------------------------------------------------------------------------- 1 | # Vídeos do Projeto 2 | A relação abaixo lista os vídeos feitos para o projeto: 3 | - [https://youtu.be/BECka5Izc9E]() 4 | 5 | > Nesta pasta inclua arquivos de vídeo produzidos para divulgação do 6 | > projeto e seus resutados. 7 | 8 | -------------------------------------------------------------------------------- /codigo/src/router/routes.js: -------------------------------------------------------------------------------- 1 | export const ROUTES = { 2 | "/chamados": "/src/HTML/Chamados.html", 3 | "/collection": "/src/HTML/Collection.html", 4 | "/fluxo": "/src/HTML/Fluxo.html", 5 | "/listas": "/src/HTML/Listas.html", 6 | "/valores": "/src/HTML/Valores.html", 7 | }; -------------------------------------------------------------------------------- /codigo/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 📁 Documentação do Projeto 2 | 3 | **Nome:** Event Manager 4 | 5 | **Objetivo**: Auxiliar no planejamento de um evento tanto em sua organização, quanto a achar fornecedores ideias para sua demanda. 6 | 7 | ## A documentação do projeto é composta pelos seguintes itens: 8 | - [Processo de Design Thinking](concepcao/Processo%20Design%20Thinking%20-%20TEMPLATE.pdf) 9 | - [Relatório Técnico](relatorio/relatorioTecnico.md) 10 | - [Apresentação do Projeto](apresentacao/apresentacao%20-%20TEMPLATE.pptx) 11 | - [Vídeo de Demonstração](https://youtube.com) 12 | -------------------------------------------------------------------------------- /codigo/src/CSS/style.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["../design.sass"],"names":[],"mappings":"AAMA;EACC;EACA;EACA;;;AAED;EACC;;;AACD;EACC;;;AACD;EACC;;;AACD;EACC;EACA;;AACA;EACC;;;AACF;EACC;EACA;EACA;EACA;EACA;;AACA;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AACD;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AACF;EACC;;AACF;EACC;EACA;;;AACF;EACC;;;AACD;EACC;;AACA;EACC;EACA;EACA;EACA;;AACA;EACC;EACA;EACA;;;AAEF;EACC;EACA;;AACA;EACC;EACA;;;AACH;EACC;EACA;;AACA;EACC;EACA;;AACA;EACC;EACA;EACA;;AACF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AACF;EACC;;;AACD;EACC;EACA;EACA;;;AACD;EACC","file":"style.css"} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Event Manager 2 | Auxiliar no planejamento de um evento tanto em sua organização, quanto a achar fornecedores ideias para sua demanda. 3 | 4 | ### `Sobre o projeto` 5 | 6 | > [Documentação](https://github.com/LuisBrescia/EventManager/tree/master/docs)
7 | 8 | ## Alunos integrantes da equipe: 9 | 10 | * Luís Felipe Teixeira Dias Brescia 11 | * Bernardo Carvalho Denucci Mercado 12 | * Thiago Cury Freire 13 | * Vitor Rebula Nogueira 14 | 15 | ## Professores responsáveis: 16 | 17 | * Cleiton Silva Tavares 18 | * Thiago Augusto Nicolini Silva 19 | 20 | ## Utilização 21 | > [Link para o site](https://event-manager-phi.vercel.app) 22 | -------------------------------------------------------------------------------- /codigo/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { resolve } from 'path' 3 | 4 | export default defineConfig({ 5 | root: 'src', 6 | build: { 7 | rollupOptions: { 8 | input: { 9 | main: resolve(__dirname, 'src/index.html'), 10 | Listas: resolve(__dirname, 'src/Listas.html'), 11 | Fluxo: resolve(__dirname, 'src/Fluxo.html'), 12 | Chamados: resolve(__dirname, 'src/Chamados.html'), 13 | Collection: resolve(__dirname, 'src/Collection.html'), 14 | Valores: resolve(__dirname, 'src/Valores.html'), 15 | }, 16 | }, 17 | }, 18 | optimizeDeps: { 19 | include: ['jquery', 'jquery-ui-dist/jquery-ui'], 20 | }, 21 | }); -------------------------------------------------------------------------------- /codigo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Event Manager 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.0.1 2 | message: Please cite this software using these metadata. 3 | title: 4 | authors: 5 | - family-names: 6 | given-names: 7 | - family-names: 8 | given-names: 9 | - family-names: 10 | given-names: 11 | - family-names: 12 | given-names: 13 | - family-names: 14 | given-names: 15 | - family-names: 16 | given-names: 17 | - name-suffix: Professor 18 | affiliation: PUC Minas 19 | family-names: 20 | given-names: 21 | - name-suffix: Professor 22 | affiliation: PUC Minas 23 | family-names: 24 | given-names: 25 | keywords: 26 | - 27 | - 28 | repository-code: 29 | license: CC-BY-4.0 30 | version: 1.0.0 31 | date-released: 2022-07-14 -------------------------------------------------------------------------------- /codigo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "event-manager", 3 | "private": true, 4 | "version": "2.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "node server.js", 8 | "dev": "vite", 9 | "build": "vite build", 10 | "preview": "vite preview" 11 | }, 12 | "devDependencies": { 13 | "vite": "^5.2.10" 14 | }, 15 | "dependencies": { 16 | "bootstrap": "^5.2.3", 17 | "bootstrap-icons": "^1.10.5", 18 | "chart.js": "^4.4.2", 19 | "jquery": "^3.7.0", 20 | "jquery-ui-dist": "^1.13.2", 21 | "leader-line-new": "^1.1.9", 22 | "path": "^0.12.7" 23 | }, 24 | "keywords": [ 25 | "gerenciador", 26 | "listas", 27 | "leaderLine" 28 | ], 29 | "author": "Luis Brescia", 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /codigo/src/JS/audio.js: -------------------------------------------------------------------------------- 1 | var currentAudio = null; 2 | 3 | function stopSound() { 4 | // if (currentAudio) { 5 | // currentAudio.pause(); 6 | // currentAudio.currentTime = 0; 7 | // currentAudio = null; 8 | // } 9 | } 10 | function whooshMid() { 11 | // if (currentAudio) { 12 | // currentAudio.pause(); 13 | // currentAudio.currentTime = 0; 14 | // currentAudio = null; 15 | // } 16 | // var audio = new Audio('../audio/whooshMid.mp3'); 17 | // audio.volume = 0.3; 18 | // audio.play(); 19 | // currentAudio = audio; 20 | } 21 | function whooshLow(e) { 22 | // if ($(e).hasClass('active')) return; 23 | // if (currentAudio) { 24 | // currentAudio.pause(); 25 | // currentAudio.currentTime = 0; 26 | // currentAudio = null; 27 | // } 28 | // var audio = new Audio('../audio/whooshLow.mp3'); 29 | // audio.volume = 0.5; 30 | // audio.play(); 31 | // currentAudio = audio; 32 | } -------------------------------------------------------------------------------- /docs/9-Apresentação.md: -------------------------------------------------------------------------------- 1 | # Apresentação 2 | 3 | Pré-requisitos: Todos os demais artefatos 4 | 5 | 6 | > Conjunto de slides em um arquivo PowerPoint ou PDF 7 | > com a apresentação do projeto contemplando todos os 8 | > itens trabalhados nos demais artefatos. 9 | 10 | ## Título do Projeto 11 | > Nome e marca do projeto 12 | 13 | ## Identidade Visual (Marca, Design) 14 | > O grupo deve ter o cuidado em utilizar figuras, imagens, e 15 | > cores dentro do contexto da solução proposta, de forma a 16 | > manter a temática do problema. 17 | > 18 | > **Links Úteis**: 19 | > - [10 dicas de design para slides](https://rockcontent.com/blog/design-para-slides/) 20 | > - [7 dicas de design para criar apresentações de PowerPoint incríveis e eficientes](https://www.shutterstock.com/pt/blog/7-dicas-de-design-para-criar-apresentacoes-de-powerpoint-incriveis-e-eficientes) 21 | > - [Especialista do TED dá 10 dicas para criar slides eficazes e bonitos](https://soap.com.br/blog/especialista-do-ted-da-10-dicas-para-criar-slides-eficazes-e-bonitos) 22 | 23 | ## Conjunto de Slides (Estrutura) 24 | > O grupo deve distribuir de forma coerente o conteúdo a ser 25 | > apresentado, dentro do tempo determinado. Importante ressaltar 26 | > a importância da descrição clara de todo o andamento do projeto, 27 | > insumos gerados e requisitos atendidos. 28 | > 29 | > **Links Úteis**: 30 | > - [A regra 10-20-30 para apresentações de sucesso](https://revistapegn.globo.com/Noticias/noticia/2014/07/regra-10-20-30-para-apresentacoes-de-sucesso.html) 31 | > - [Top Tips for Effective Presentations](https://www.skillsyouneed.com/present/presentation-tips.html) 32 | > - [How to make a great presentation](https://www.ted.com/Valores.htmls/574/how_to_make_a_great_presentation) -------------------------------------------------------------------------------- /codigo/src/JS/barraLateral.js: -------------------------------------------------------------------------------- 1 | var menuAtivo = true; 2 | var allowCtrl = true; 3 | const primariaN1 = "#00ffff"; 4 | const primaria = "#0044ff"; 5 | const primaria1 = "#0044fe"; 6 | const primaria2 = "#003bfb"; 7 | const primaria3 = "#0033ee"; 8 | 9 | import './bootstrap.js'; 10 | import 'jquery-ui-dist/jquery-ui'; 11 | 12 | $('#menu').on('click', () => { 13 | if (menuAtivo) { 14 | console.log('Menu Fechado'); 15 | } else { 16 | console.log('Menu Aberto'); 17 | $('.card-container').find('.mover').each(function () { 18 | console.log('MoverRemovido pois menu aberto'); 19 | $(this).removeClass('mover'); 20 | $(this).find('.card-header').css('background-color', '#fff'); 21 | $(this).css({ 22 | 'transition': 'all 0.3s', 23 | 'left': '300%' 24 | }); 25 | setTimeout(() => { 26 | $(this).css('transition', '0s'); 27 | }, 100); 28 | }); 29 | } 30 | 31 | $('.barraLista').toggleClass('col-xxl-10 col-xxl-12'); 32 | $('.ultraGambiarra').toggleClass('d-none'); 33 | $('aside').toggleClass('active'); 34 | menuAtivo = !menuAtivo; 35 | }); 36 | 37 | // * Atalho: Ctrl + B 38 | $(document).on('keydown', function (e) { 39 | if (e.ctrlKey && e.keyCode === 66 && allowCtrl) { 40 | console.log('ctrl'); 41 | allowCtrl = false; 42 | menuAtivo = !menuAtivo; 43 | // * Caso uma elemento da classe .funcionalides tenha a classe col-xll-10, mudar para col-xll-12 44 | 45 | $('.barraLista').toggleClass('col-xxl-10 col-xxl-12'); 46 | $('.ultraGambiarra').toggleClass('d-none'); 47 | $('aside').toggleClass('active'); 48 | 49 | } 50 | }); 51 | 52 | $(document).on('keyup', function (e) { 53 | if (e.ctrlKey || e.keyCode === 66) { 54 | allowCtrl = true; 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /codigo/src/JS/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | > Seria interessante algo como: 3 | > aside = conteudo aside da página 4 | > $('.telaInteira').prepend(aside); 5 | */ 6 | 7 | import { ROUTES } from "../router/routes.js"; 8 | 9 | // redirecionar para /Listas 10 | window.location.href = "/Listas.html"; 11 | 12 | // export function clearScript(src) { 13 | // const script = document.createElement("script"); 14 | // script.src = src; 15 | // script.type = "module"; 16 | // setTimeout(() => { 17 | // document?.body?.removeChild?.(script); 18 | // }); 19 | // } 20 | 21 | // export function loadScript(src) { 22 | // const script = document.createElement("script"); 23 | // script.src = src; 24 | // script.type = "module"; 25 | // document.body.appendChild(script); 26 | // window.addEventListener("locationchange", clearScript(script)); 27 | // } 28 | 29 | // function extractAndLoadScripts(html) { 30 | // const parser = new DOMParser(); 31 | // const doc = parser.parseFromString(html, "text/html"); 32 | // const scriptTags = doc.body.getElementsByTagName("script"); 33 | 34 | // const loadPromises = []; 35 | // for (let scriptTag of scriptTags) { 36 | // if (scriptTag.src) { 37 | // loadPromises.push(loadScript(scriptTag.src)); 38 | // } 39 | // } 40 | 41 | // return Promise.all(loadPromises); 42 | // } 43 | 44 | // async function renderContent(route) { 45 | // console.log("caiu no main.js"); 46 | // const container = document.getElementById("app"); 47 | // let fileName = ROUTES[route]; 48 | 49 | // if (!fileName) { 50 | // // If no match is found, check for dynamic routes 51 | // for (const dynamicRoute in ROUTES) { 52 | // if (new RegExp(dynamicRoute).test(route) && dynamicRoute !== "/") { 53 | // fileName = ROUTES[dynamicRoute]; 54 | // break; 55 | // } 56 | // } 57 | // } 58 | 59 | // if (fileName) { 60 | // const html = await fetch(fileName) 61 | // .then((response) => response.text()) 62 | // .catch((error) => { 63 | // console.error("Error loading HTML file:", error); 64 | // }); 65 | // extractAndLoadScripts(html); 66 | // container.innerHTML = html; 67 | // } else { 68 | // console.log("errorr routing"); 69 | // // Handle 404 or other cases as needed 70 | // container.innerHTML = "Page not found"; 71 | // } 72 | // } 73 | 74 | // function handleRouteChange() { 75 | // const currentRoute = window.location.pathname; 76 | // console.log(currentRoute); 77 | // renderContent(currentRoute); 78 | // window.scrollTo(0, 0); 79 | // history.pushState(null, null, currentRoute); 80 | // } 81 | 82 | // window.addEventListener("popstate", handleRouteChange); 83 | // window.addEventListener("load", handleRouteChange); 84 | // window.onhashchange = handleRouteChange; 85 | -------------------------------------------------------------------------------- /codigo/src/JS/collection.js: -------------------------------------------------------------------------------- 1 | import './bootstrap.js'; 2 | import 'jquery-ui-dist/jquery-ui'; 3 | import * as bootstrap from 'bootstrap'; 4 | 5 | var telaMD = false; // Para tela md ou maiores essa variável será true 6 | 7 | // * Responsivididade, basicamente altera entre uma tela e outra 8 | $('#collectionParticipantes').on('click', function () { 9 | if (window.matchMedia("(min-width: 768px)").matches) return; 10 | console.log("Participantes"); 11 | $('.colParticipantes').addClass('d-none'); 12 | $('.colInsumos').removeClass('d-none'); 13 | }); 14 | $('#collectionInsumos').on('click', function () { 15 | if (window.matchMedia("(min-width: 768px)").matches) return; 16 | console.log("Insumos"); 17 | $('.colInsumos').addClass('d-none'); 18 | $('.colServicos').removeClass('d-none'); 19 | }); 20 | $('#collectionServicos').on('click', function () { 21 | if (window.matchMedia("(min-width: 768px)").matches) return; 22 | console.log("Servicos"); 23 | $('.colServicos').addClass('d-none'); 24 | $('.colParticipantes').removeClass('d-none'); 25 | }); 26 | $(window).on('resize', function () { 27 | if ($(window).width() >= 768) { 28 | console.log("Tela MD"); 29 | $('.colInsumos, .colServicos, .colParticipantes').removeClass('d-none'); 30 | $('.colInsumos, .colServicos, .colParticipantes').addClass('col-md-4').removeClass('col-md-3 col-md-6'); 31 | telaMD = true; 32 | } else if (telaMD === true) { 33 | console.log("Tela SM"); 34 | $('.colInsumos, .colServicos').addClass('d-none'); 35 | telaMD = false; 36 | } 37 | }); 38 | 39 | // * Blur nos outros accordions quando o botão em algum estiver em hover 40 | $('#collectionInsumos').hover( 41 | () => { $('.accordionParticipantes, .accordionServicos').toggleClass('blur') } 42 | ) 43 | $('#collectionParticipantes').hover( 44 | () => { $('.accordionInsumos, .accordionServicos').toggleClass('blur') } 45 | ) 46 | $('#collectionServicos').hover( 47 | () => { $('.accordionInsumos, .accordionParticipantes').toggleClass('blur') } 48 | ) 49 | // > Se tiver 2 true o outro automaticamente será none 50 | var expandidos = [false, false, false] 51 | var elementos = [$('.colParticipantes'), $('.colInsumos'), $('.colServicos'),] 52 | $('#collectionParticipantes').on('click', () => { 53 | if (window.matchMedia("(max-width: 767px)").matches) return; 54 | $(elementos[1]).toggleClass('col-md-4 col-md-3'); 55 | $(elementos[2]).toggleClass('col-md-4 col-md-3'); 56 | $(elementos[0]).toggleClass('col-md-6 col-md-4'); 57 | 58 | if (expandidos[0] === true) { 59 | expandidos[0] = false; 60 | console.log("Partipantes expandido"); 61 | elementos[1].removeClass('d-none'); 62 | elementos[2].removeClass('d-none'); 63 | } else { 64 | expandidos[0] = true; 65 | console.log("Partipantes minimizado"); 66 | elementos[2].toggleClass('d-none', expandidos[1] === true); 67 | elementos[1].toggleClass('d-none', expandidos[2] === true); 68 | } 69 | }); 70 | $('#collectionInsumos').on('click', () => { 71 | if (window.matchMedia("(max-width: 767px)").matches) return; 72 | $(elementos[0]).toggleClass('col-md-4 col-md-3'); 73 | $(elementos[2]).toggleClass('col-md-4 col-md-3'); 74 | $(elementos[1]).toggleClass('col-md-6 col-md-4'); 75 | 76 | if (expandidos[1] === true) { 77 | expandidos[1] = false; 78 | console.log("Insumos minimizado"); 79 | elementos[0].removeClass('d-none'); 80 | elementos[2].removeClass('d-none'); 81 | } else { 82 | expandidos[1] = true; 83 | console.log("Insumos expandido"); 84 | elementos[2].toggleClass('d-none', expandidos[0] === true); 85 | elementos[0].toggleClass('d-none', expandidos[2] === true); 86 | } 87 | }); 88 | $('#collectionServicos').on('click', () => { 89 | if (window.matchMedia("(max-width: 767px)").matches) return; 90 | $(elementos[0]).toggleClass('col-md-4 col-md-3'); 91 | $(elementos[1]).toggleClass('col-md-4 col-md-3'); 92 | $(elementos[2]).toggleClass('col-md-6 col-md-4'); 93 | 94 | if (expandidos[2] === true) { 95 | expandidos[2] = false; 96 | console.log("Insumos minimizado"); 97 | elementos[0].removeClass('d-none'); 98 | elementos[1].removeClass('d-none'); 99 | } else { 100 | expandidos[2] = true; 101 | console.log("Insumos expandido"); 102 | elementos[1].toggleClass('d-none', expandidos[0] === true); 103 | elementos[0].toggleClass('d-none', expandidos[1] === true); 104 | } 105 | }); -------------------------------------------------------------------------------- /codigo/src/JS/grafico.js: -------------------------------------------------------------------------------- 1 | // > Aqui irei colocar toda a lógica por tras da página de resultado 2 | // > Na página de resultados deverá ter uma checklist onde o usuário deverá conseguir 3 | // > criar novos itens e marcar os que já foram feitos 4 | // > Na parte cima da tela tem 2 "Cards" confirmados, CustO e Progresso, muito provavelmente 5 | // > Terá Chamados ativos também 6 | 7 | import './bootstrap.js'; 8 | import 'jquery-ui-dist/jquery-ui'; 9 | import * as bootstrap from 'bootstrap'; 10 | 11 | // https://cdn.jsdelivr.net/npm/chart.js 12 | import Chart from 'chart.js/auto'; 13 | 14 | class ListaParticipantes { 15 | constructor(titulo, elementos, percentual) { 16 | this.titulo = titulo; 17 | this.elementos = elementos; 18 | this.percentual = percentual; 19 | } 20 | } 21 | 22 | class Chamado { 23 | constructor(titulo, valor, percentual) { 24 | this.titulo = titulo; 25 | this.valor = valor; 26 | this.percentual = percentual; 27 | } 28 | } 29 | 30 | // * Aqui eu tenho todas as listas de participantes do usuário 31 | var ListasParticipantes = JSON.parse(localStorage.getItem("ListaParticipantes")) || []; 32 | // : graficoPizza será nosso vetor onde conterá as listas de participantes devidamente formatadas 33 | var graficoParticipantes = []; 34 | 35 | var k = -1; 36 | var totalParticipantes = 0; 37 | 38 | for (let i = 0; i < 6; i++) { 39 | if (ListasParticipantes[i] != null) { 40 | if (ListasParticipantes[i].linhas[0] != "") { 41 | graficoParticipantes[++k] = new ListaParticipantes(ListasParticipantes[i].titulo, ListasParticipantes[i].linhas.length, 0); 42 | // > Se o código de cardMovivel.js for revisado, será possível remover esse if 43 | // > Novas atualizações suportam, [-1] para pegar ultimo elemento do vetor 44 | if (ListasParticipantes[i].linhas[ListasParticipantes[i].linhas.length - 1] == "") { 45 | graficoParticipantes[k].elementos--; 46 | } 47 | totalParticipantes += graficoParticipantes[k].elementos; 48 | graficoParticipantes[k].titulo += " (" + graficoParticipantes[k].elementos + ")"; 49 | } 50 | } 51 | } 52 | 53 | // ? O número de participantes dividido pelo total de participantes nos da o percentual 54 | for (let i = 0; i < k + 1; i++) { 55 | graficoParticipantes[i].percentual = (graficoParticipantes[i].elementos / totalParticipantes) * 100; 56 | } 57 | 58 | if (graficoParticipantes.length == 0) { 59 | graficoParticipantes[0] = new ListaParticipantes("Crie listas de Participantes para o gráfico ser exibido", 100, 100); 60 | } 61 | 62 | // * Aqui começa o código do gráfico 63 | $('.valor-P h6').text("Participantes (" + totalParticipantes + ")"); 64 | 65 | var backgroundImageUrl = "data:image/svg+xml,%3Csvg width='44' height='12' viewBox='0 0 44 12' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 12v-2L0 0v10l4 2h16zm18 0l4-2V0L22 10v2h16zM20 0v8L4 0h16zm18 0L22 8V0h16z' fill='%230088ff' fill-opacity='0.4' fill-rule='evenodd'/%3E%3C/svg%3E"; 66 | var backgroundColors = ["#0088ff", "#1066ff", "#0044ff", "#0224d0", "#0020a0", "#016"]; 67 | 68 | var data = { 69 | labels: [], 70 | datasets: [{ 71 | data: [], 72 | backgroundColor: backgroundColors, 73 | pattern: { 74 | src: backgroundImageUrl, 75 | repeat: 'repeat' 76 | } 77 | }] 78 | }; 79 | 80 | for (var i = 0; i < graficoParticipantes.length; i++) { 81 | data.labels.push(graficoParticipantes[i].titulo); 82 | data.datasets[0].data.push(graficoParticipantes[i].percentual); 83 | } 84 | 85 | var options = { 86 | responsive: true 87 | }; 88 | 89 | var ctx = document.getElementById("graficoParticipantes").getContext("2d"); 90 | var myChart = new Chart(ctx, { 91 | type: "pie", 92 | data: data, 93 | options: options 94 | }); 95 | 96 | // ! Gráfico de gastos 97 | var Chamados = JSON.parse(localStorage.getItem("Chamados")) || []; 98 | let totalGastos = 0; 99 | let custoTotal = 0; 100 | 101 | var backgroundColors2 = ["#0088ff", "#1066ff", "#0044ff", "#0224d0", "#0020a0", "#016"]; 102 | backgroundColors2.reverse(); 103 | 104 | for (let i = 0; i < Chamados.length; i++) { 105 | if (Chamados[i].valor > 0) { 106 | totalGastos++; 107 | custoTotal += parseInt(Chamados[i].valor); 108 | } 109 | } 110 | 111 | console.log("Custo total é", custoTotal); 112 | 113 | $('.valor-S h6').text("Gastos (" + totalGastos + ")"); 114 | 115 | var graficoChamados = []; 116 | 117 | var k = -1; 118 | for (let i = 0; i < Chamados.length; i++) { 119 | if (Chamados[i].valor > 0) { 120 | graficoChamados[++k] = new Chamado(Chamados[i].titulo, Chamados[i].valor, 0); 121 | graficoChamados[k].percentual = (graficoChamados[k].valor / custoTotal) * 100; 122 | graficoChamados[k].titulo += " R$ " + graficoChamados[k].valor; 123 | } 124 | } 125 | 126 | if (graficoChamados.length == 0) { 127 | graficoChamados[0] = new Chamado("Insira valores nos chamados para visualizar o gráfico de Gastos.", 100, 100); 128 | } 129 | 130 | var data = { 131 | labels: [], 132 | datasets: [{ 133 | data: [], 134 | backgroundColor: backgroundColors2, 135 | pattern: { 136 | src: backgroundImageUrl, 137 | repeat: 'repeat' 138 | } 139 | }] 140 | }; 141 | 142 | for (var i = 0; i < graficoChamados.length; i++) { 143 | data.labels.push(graficoChamados[i].titulo); 144 | data.datasets[0].data.push(graficoChamados[i].percentual); 145 | } 146 | 147 | var options = { 148 | responsive: true 149 | }; 150 | 151 | var ctx = document.getElementById("graficoChamados").getContext("2d"); 152 | 153 | var myChart = new Chart(ctx, { 154 | type: "pie", 155 | data: data, 156 | options: options 157 | }); 158 | 159 | /* TIPOS DE GRÁFICOS 160 | pie 161 | doughnut 162 | polarArea 163 | bar 164 | line 165 | radar 166 | bubble 167 | scatter */ -------------------------------------------------------------------------------- /codigo/src/Chamados.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Event Manager 10 | 11 | 12 | 13 | 14 |
15 | 16 | 91 | 92 | 167 | 168 | 169 |
170 | 171 |
172 |

Chamados

 Sobre Evento 175 |
176 | 177 |
179 |
Insumos
180 |
181 | 182 |
183 |
Serviços
184 |
185 |
186 |
187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /codigo/src/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /codigo/src/JS/chamados.js: -------------------------------------------------------------------------------- 1 | // ? Objeto do tipo chamado 2 | 3 | import './bootstrap.js'; 4 | import 'jquery-ui-dist/jquery-ui'; 5 | import * as bootstrap from 'bootstrap'; 6 | 7 | class Chamado { 8 | constructor(id, tipo, titulo, valor, categoria, status) { 9 | this.id = id; 10 | this.tipo = tipo; 11 | this.titulo = titulo; 12 | this.valor = valor; 13 | this.categoria = categoria; 14 | this.status = status; 15 | } 16 | } 17 | 18 | // * Primeiramente vou pegar a listaInsumos do localStorage 19 | var ListaInsumos = JSON.parse(localStorage.getItem("ListaInsumos")) || []; 20 | var ListaServicos = JSON.parse(localStorage.getItem("ListaServicos")) || []; 21 | 22 | var chamados = JSON.parse(localStorage.getItem("Chamados")) || []; 23 | 24 | // Caso chamados não exista no localStorage, criarei um vetor com 12 objetos do tipo Chamado 25 | if (chamados.length == 0) { 26 | for (let i = 0; i < 12; i++) { 27 | chamados.push(new Chamado(i, i < 6 ? "Insumo" : "Servico", "Vazio", 0, "1", false)); 28 | } 29 | console.log(typeof chamados[0].categoria); 30 | localStorage.setItem("Chamados", JSON.stringify(chamados)); 31 | } 32 | 33 | var TAM = 6; 34 | 35 | $(document).ready(function () { 36 | carregaBlocos(); 37 | }); 38 | 39 | function carregaBlocos() { 40 | let bloco; 41 | for (let i = 0; i < TAM; i++) { 42 | if (ListaInsumos[i] != null) { 43 | bloco = blocoChamadoHTML_I(ListaInsumos[i]); 44 | valorChamado(bloco); 45 | realizaChamado(bloco); 46 | $('#items-chamado-I').append(bloco); 47 | 48 | $(bloco).find(`#valorChamado`).val(chamados[i].valor); 49 | chamados[i].titulo = ListaInsumos[i].titulo; 50 | if (chamados[i].status == true) { 51 | $(bloco).find('#realizaChamado').text(" Fechar chamado"); 52 | $(bloco).find('#realizaChamado').addClass("bi-wifi-off aberto").removeClass("bi-wifi"); 53 | } 54 | } 55 | if (ListaServicos[i] != null) { 56 | bloco = blocoChamadoHTML_S(ListaServicos[i]); 57 | valorChamado(bloco); 58 | realizaChamado(bloco); 59 | $('#items-chamado-S').append(bloco); 60 | 61 | $(bloco).find(`#valorChamado`).val(chamados[i + 6].valor); 62 | chamados[i + 6].titulo = ListaServicos[i].titulo; 63 | // Também vou checar o status 64 | if (chamados[i + 6].status == true) { 65 | $(bloco).find('#realizaChamado').text(" Fechar chamado"); 66 | $(bloco).find('#realizaChamado').addClass("bi-wifi-off aberto").removeClass("bi-wifi"); 67 | } 68 | // $(bloco).find(`[name="categoria"]`).val(chamados[i + 6].categoria); 69 | } 70 | } 71 | 72 | let qtd = $('#items-chamado-I').children().length - 1; 73 | let resto; 74 | 75 | if (qtd == 0) { resto = 3; } 76 | else if (qtd > 3) { resto = 6 - qtd; } 77 | else { resto = 3 - qtd; } 78 | 79 | let htmlCaixaTracejada = ''; 80 | 81 | for (let i = 0; i < resto; i++) { 82 | htmlCaixaTracejada += '
'; 83 | } 84 | $('#items-chamado-I').append(htmlCaixaTracejada) 85 | 86 | qtd = $('#items-chamado-S').children().length - 1; 87 | if (qtd == 0) { resto = 3; } 88 | else if (qtd > 3) { resto = 6 - qtd; } 89 | else { resto = 3 - qtd; } 90 | 91 | htmlCaixaTracejada = ''; 92 | 93 | for (let i = 0; i < resto; i++) { 94 | htmlCaixaTracejada += '
'; 95 | } 96 | $('#items-chamado-S').append(htmlCaixaTracejada); 97 | } 98 | // > Futuramente quero que essa funcionalidade seja drag & drop 99 | // * Botão para abrir fechar chamado 100 | function realizaChamado(element) { 101 | 102 | if (element == null) { return; } 103 | 104 | element.find('#realizaChamado').click(function () { 105 | 106 | $(this).text($(this).text() == " Fechar chamado" ? " Abrir chamado " : " Fechar chamado"); 107 | $(this).toggleClass("bi-wifi bi-wifi-off aberto"); 108 | 109 | console.log($(this).parent().parent().attr('id') + " Mudou seus status"); 110 | 111 | // Tenho que pegar o id e setar aquele chamado com true 112 | let id = $(this).parent().parent().attr('id').split('-')[1]; 113 | chamados[id].status = !chamados[id].status; 114 | localStorage.setItem("Chamados", JSON.stringify(chamados)); 115 | }); 116 | } 117 | // * Sempre que algum atributo é alterado, essa função é chamada 118 | function valorChamado(element) { 119 | // console.log(typeof chamados[0].categoria); 120 | if (element == null) { return; } 121 | 122 | // * Valor em dinheiro 123 | element.find('#valorChamado').change(function () { 124 | // > Colocar uma máscara e adicionar R$ ao começo 125 | console.log($(this).parent().parent().parent().attr('id') + " valor: " + $(this).val()); 126 | let id = $(this).parent().parent().parent().attr('id').split('-')[1]; 127 | console.log(typeof chamados[id].valor); 128 | chamados[id].valor = $(this).val(); 129 | localStorage.setItem("Chamados", JSON.stringify(chamados)); 130 | }); 131 | // * Categoria 132 | element.find('[name="categoria"]').change(function () { 133 | // ! Provavelmente está pegando a id errada 134 | console.log(typeof chamados[0].categoria); 135 | console.log($(this).parent().parent().parent().attr('id') + " categoria: " + $(this).val()); 136 | let id = $(this).parent().parent().parent().attr('id').split('-')[1]; 137 | console.log(typeof chamados[id].categoria); 138 | chamados[id].categoria = $(this).val(); 139 | localStorage.setItem("Chamados", JSON.stringify(chamados)); 140 | }); 141 | } 142 | // * Carrega os blocos referentes a insumos 143 | function blocoChamadoHTML_I(ListaInsumo) { 144 | let dicionario = JSON.parse(localStorage.getItem("dicionario-" + ListaInsumo.titulo)); 145 | if (dicionario == null) { return; } 146 | let conteudo = ""; 147 | 148 | Object.keys(dicionario).forEach(chave => { 149 | if (dicionario[chave] != null && dicionario[chave] > 0) { 150 | conteudo += `
  • ${chave}${dicionario[chave]}
  • `; 151 | } 152 | }); 153 | 154 | if (conteudo == "") { return; } 155 | // conteudo = `
  • Crie conexões entre Insumos e Participantes na tela de fluxo.
  • `; 156 | 157 | return $(` 158 |
    159 |
    160 | ${ListaInsumo.titulo} 161 |
    162 |
    163 | 166 |
    167 |
    168 | 169 | R$ 170 | 171 | 172 | 173 | 184 |
    185 | 186 |
    187 | 188 |
    189 |
    190 | `); 191 | } 192 | // * Carrega os blocos referentes a serviços 193 | function blocoChamadoHTML_S(ListaServicos) { // 309 x 340 194 | 195 | if (ListaServicos.linhas == null) { return; } 196 | 197 | let conteudo = ""; 198 | 199 | for (let i = 0; i < ListaServicos.linhas.length; i++) { 200 | conteudo += `
  • ${ListaServicos.linhas[i]}
  • `; 201 | } 202 | 203 | if (conteudo == "" || ListaServicos.linhas[0].trim() == "") { 204 | conteudo = `
  • Adicione uma descrição para esse serviço na tela de listas.
  • `; 205 | } 206 | 207 | return $(` 208 |
    209 |
    210 | ${ListaServicos.titulo} 211 |
    212 |
    213 | 216 |
    217 |
    218 | 219 | R$ 220 | 221 | 222 | 223 | 236 | 237 |
    238 |
    239 | 240 |
    241 |
    242 | `); 243 | } -------------------------------------------------------------------------------- /codigo/src/Fluxo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Event Manager 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 91 | 92 | 167 | 168 | 169 |
    170 | 171 |
    173 | 178 | 179 | 183 | 184 | 189 |
    190 | 191 | 192 |
    193 | 203 |
    204 | 205 | 206 |
    207 | 208 |
    209 |
    210 | 222 |
    223 | 226 |
    227 |
    228 | 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /codigo/src/JS/resultado.js: -------------------------------------------------------------------------------- 1 | class Tarefa { 2 | constructor(_id, titulo, concluida) { 3 | this._id = _id; 4 | this.titulo = titulo; 5 | this.concluida = concluida; 6 | } 7 | } 8 | 9 | var TAM = 12; // * Quantidade máxima de tarefas 10 | var editaTodo = false; // * Salva se a lista está em modo de edição 11 | var ListaTarefas = JSON.parse(localStorage.getItem("ListaTarefas")) || []; // * Será um vetor de objetos do tipo Tarefa 12 | 13 | var chamados = JSON.parse(localStorage.getItem("Chamados")) || []; 14 | 15 | // Preciso percorrer chamados, somar todos seus valores e atualizar valorTotal 16 | function atualizaCusto() { 17 | let valorTotal = 0, chamadosAtivos = 0, totalChamados = 0; 18 | for (let i = 0; i < chamados.length; i++) { 19 | valorTotal += parseInt(chamados[i].valor); 20 | if (chamados[i].status == true) { 21 | chamadosAtivos++; 22 | } 23 | if (chamados[i].valor != 0) { 24 | totalChamados++; 25 | } 26 | } 27 | 28 | // * Transforma o valorTotal em uma string com o formato R$ 0,00 29 | valorTotal = valorTotal.toLocaleString('pt-br', { style: 'currency', currency: 'BRL' }); 30 | $('#custoTotal').text(valorTotal); 31 | $('#chamadosAtivos').text(chamadosAtivos + " de " + totalChamados); 32 | } 33 | 34 | function carregaTarefas() { 35 | 36 | var tarefasConcluidas = 0; 37 | $('#todoConteudo li').remove(); 38 | 39 | if (ListaTarefas.length == 0) { 40 | let objGenerico = new Tarefa(0, "Montar lista de participantes", false); 41 | let tarefaGenerica = criaHtmlTarefa(objGenerico, false); 42 | 43 | ListaTarefas.unshift(objGenerico); 44 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 45 | 46 | selectAll(tarefaGenerica.find('h6')[0]); 47 | nomeTarefa(tarefaGenerica); 48 | excluirTarefa(tarefaGenerica); 49 | estadoTarefa(tarefaGenerica); 50 | 51 | $('#todoConteudo').append(tarefaGenerica); 52 | 53 | objGenerico = new Tarefa(0, "Abrir Chamados para insumos e serviços", false); 54 | tarefaGenerica = criaHtmlTarefa(objGenerico, false); 55 | 56 | ListaTarefas.unshift(objGenerico); 57 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 58 | 59 | selectAll(tarefaGenerica.find('h6')[0]); 60 | nomeTarefa(tarefaGenerica); 61 | excluirTarefa(tarefaGenerica); 62 | estadoTarefa(tarefaGenerica); 63 | 64 | $('#todoConteudo').append(tarefaGenerica); 65 | } else { 66 | console.log(ListaTarefas); 67 | console.log("Carregando tarefas..."); 68 | 69 | for (let i = 0; i < ListaTarefas.length; i++) { 70 | 71 | let tarefaCarregada = criaHtmlTarefa(ListaTarefas[i], false); 72 | 73 | selectAll(tarefaCarregada.find('h6')[0]); 74 | nomeTarefa(tarefaCarregada); 75 | excluirTarefa(tarefaCarregada); 76 | estadoTarefa(tarefaCarregada); 77 | 78 | if (ListaTarefas[i].concluida == true) { 79 | tarefaCarregada.find('[type="checkbox"]').prop('checked', true); 80 | tarefaCarregada.addClass('concluido'); 81 | tarefasConcluidas++; 82 | } 83 | $('#todoConteudo').append(tarefaCarregada); 84 | } 85 | } 86 | 87 | console.log("Tarefas concluidas: ", tarefasConcluidas, " de ", ListaTarefas.length); 88 | var porcentagem = (tarefasConcluidas / ListaTarefas.length) * 100; 89 | porcentagem = Math.floor(porcentagem); 90 | console.log("Porcentagem de tarefas concluidas: ", porcentagem, "%"); 91 | $('.progress-bar').css('width', porcentagem + '%'); 92 | $('.porcentagemConcluida').text(porcentagem + '%'); 93 | } 94 | 95 | $(document).ready(() => { 96 | atualizaCusto(); 97 | carregaTarefas(); 98 | $('#adicionaTarefa').click(() => { 99 | if ($('#todoConteudo li').length >= TAM) { // * Máximo de 12 tarefas 100 | $('.toast-body').text('Você atingiu o limite de tarefas'); 101 | $('.toast').toast('show'); 102 | return; 103 | } 104 | 105 | if (editaTodo) { // * Caso esteja em modo de edição, desativa 106 | $('#todoConteudo li h6').prop('contenteditable', 'false'); 107 | $('#todoConteudo .dadoTarefa').css('margin-left', '0px'); 108 | $('.excluirTarefa').toggleClass('d-none'); 109 | $('#editaTodo').toggleClass('active'); 110 | editaTodo = !editaTodo; 111 | } 112 | 113 | var index = $('#todoConteudo li').length; 114 | 115 | var tarefinha = new Tarefa(0, "Nova Tarefa", false); 116 | let novaTarefa = criaHtmlTarefa(tarefinha, true); 117 | 118 | $('#todoConteudo').prepend(novaTarefa); 119 | $('#todoConteudo li:first-child h6').focus(); 120 | 121 | console.log("Número de tarefas existentes é", index); 122 | 123 | selectAll(novaTarefa.find('h6')[0]); 124 | nomeTarefa(novaTarefa); 125 | excluirTarefa(novaTarefa); 126 | estadoTarefa(novaTarefa); 127 | 128 | ListaTarefas.unshift(tarefinha); 129 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 130 | 131 | atualizaBarraProgresso(); 132 | }); 133 | $('#editaTarefa').click(() => { 134 | if (editaTodo == false) { 135 | $('#todoConteudo li h6').prop('contenteditable', 'true'); 136 | $('#todoConteudo .dadoTarefa').css('margin-left', '25px'); 137 | } else { 138 | $('#todoConteudo li h6').prop('contenteditable', 'false'); 139 | $('#todoConteudo .dadoTarefa').css('margin-left', '0px'); 140 | } 141 | $('.excluirTarefa').toggleClass('d-none'); 142 | $('#editaTarefa').toggleClass('active'); 143 | editaTodo = !editaTodo; 144 | }); 145 | // > Assinatura não descritiva 146 | $('#todoConteudo').on('click', 'h6', () => { 147 | if ($(this).find('h6').prop('contenteditable') == 'true') { 148 | $(this).find('h6').focus(); 149 | selectAll($(this).find('h6')[0]); 150 | todoTitulo($(this)); 151 | } 152 | }); 153 | // $('#todoConteudo [type="checkbox"]').click(function () { 154 | // $(this).closest('li').toggleClass('concluido'); 155 | // ListaTarefas[$(this).closest('li').index()].concluida = true; 156 | // }); 157 | }); 158 | 159 | // * Função ativa enquanto o usuário estiver editando o título de uma tarefa 160 | function nomeTarefa(element) { 161 | //$('#todoConteudo li:first-child') 162 | element.find('h6').on('blur', function () { 163 | if (!editaTodo) { element.find('h6').prop('contenteditable', 'false'); } 164 | ListaTarefas[element.index()].titulo = element.find('h6').text(); 165 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 166 | }); 167 | element.find('h6').keydown(function (e) { 168 | if ((element.find('h6').width() > 400 && (e.keyCode != 8 && !isTextSelected(element.find('h6')[0]))) 169 | || e.keyCode === 13) { 170 | e.preventDefault(); 171 | } 172 | }); 173 | element.find('h6').on('click', function () { 174 | selectAll(element.find('h6')[0]); 175 | }); 176 | } 177 | // * Muda o estado de uma tarefa já salvando seu novo estado no localStorage 178 | function estadoTarefa(element) { 179 | element.find('[type="checkbox"]').click(function () { 180 | console.log("Alterado o estado da tarefa: ", $(this).closest('li').index()); 181 | $(this).closest('li').toggleClass('concluido'); 182 | ListaTarefas[$(this).closest('li').index()].concluida = !ListaTarefas[$(this).closest('li').index()].concluida; 183 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 184 | atualizaBarraProgresso(); 185 | }); 186 | } 187 | // * Função que exclui uma tarefa 188 | function excluirTarefa(element) { 189 | element.find('.excluirTarefa').click(function () { 190 | if ($('#todoConteudo li').length <= 1) { 191 | $('.toast-body').text('Você não pode excluir todas as tarefas'); 192 | $('.toast').toast('show'); 193 | return; 194 | } 195 | console.log("Tarefa excluida: ", $(this).closest('li').index()); 196 | ListaTarefas.splice($(this).closest('li').index(), 1); 197 | localStorage.setItem("ListaTarefas", JSON.stringify(ListaTarefas)); 198 | $(this).closest('li').remove(); 199 | atualizaBarraProgresso(); 200 | }); 201 | } 202 | // * HTML de uma tarefa 203 | function criaHtmlTarefa(tarefa, editavel) { 204 | return $( 205 | `
  • 206 |
    207 |
    208 | 209 |
    ${tarefa.titulo}
    210 |
    211 |
    212 |
    213 | 214 | 215 |
    216 |
    217 |
    218 |
  • `); 219 | } 220 | // * Atualizar barra de progresso 221 | function atualizaBarraProgresso() { 222 | var tarefasConcluidas = 0; 223 | for (let i = 0; i < ListaTarefas.length; i++) { 224 | if (ListaTarefas[i].concluida == true) { 225 | tarefasConcluidas++; 226 | } 227 | } 228 | var porcentagem = (tarefasConcluidas / ListaTarefas.length) * 100; 229 | porcentagem = Math.floor(porcentagem); // * Descartar casas decimais 230 | console.log("Porcentagem de tarefas concluidas: ", porcentagem, "%"); 231 | $('.progress-bar').css('width', porcentagem + '%'); 232 | $('.porcentagemConcluida').text(porcentagem + '%'); 233 | } 234 | // * Permite selecionar todo o texto de um element 235 | function selectAll(element) { 236 | if (document.body.createTextRange) { // Suporte para Internet Explorer 237 | var range = document.body.createTextRange(); 238 | range.moveToElementText(element); 239 | range.select(); 240 | } else if (window.getSelection) { // Suporte para navegadores modernos 241 | var range = document.createRange(); 242 | range.selectNodeContents(element); 243 | var selection = window.getSelection(); 244 | selection.removeAllRanges(); 245 | selection.addRange(range); 246 | } 247 | } 248 | // * Permite alterar o título caso conteúdo esteja inteiramente selecionado 249 | function isTextSelected(element) { 250 | let selection = window.getSelection(); 251 | let selectedText = selection.toString(); 252 | let elementText = element.textContent; 253 | return selectedText === elementText; 254 | } 255 | 256 | // elemento.find('span').prop('contenteditable', 'true'); 257 | // selectAll(elemento.find('span')[0]); 258 | // elemento.find('span').keydown(function (e) { 259 | // if (e.keyCode === 13) { 260 | // e.preventDefault(); 261 | // } 262 | // if (elemento.find('span').text().length > 35 && e.keyCode != 8) { 263 | // e.preventDefault(); 264 | // } 265 | // }); 266 | // elemento.find('span').on('blur', function () { 267 | // elemento.find('span').prop('contenteditable', 'false'); 268 | // }); -------------------------------------------------------------------------------- /codigo/src/Listas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Event Manager 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 91 | 92 | 167 | 168 | 169 |
    170 | 171 |
    173 | 177 | 178 | 182 | 183 | 187 |
    188 | 189 | 190 |
    191 | 201 |
    202 | 203 | 204 |
    205 | 206 |
    207 |
    208 | 212 | 216 | 220 |
    221 | 222 |
    223 |
    224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /docs/relatorio/relatorioTecnico.md: -------------------------------------------------------------------------------- 1 | # Informações do Projeto: 2 | 3 | ### `TÍTULO:` **Event Manager** 4 | 5 | ### `CURSO:` **Engenharia de Software** 6 | 7 | ## Participantes: 8 | 9 | * Luís Felipe Teixeira Dias Brescia 10 | * Bernardo Carvalho Denucci Mercado 11 | * Thiago Cury Freire 12 | * Vitor Rebula Nogueira 13 | 14 | # Estrutura do Documento 15 | 16 | - [Informações do Projeto](#informações-do-projeto) 17 | - [Participantes](#participantes) 18 | - [Estrutura do Documento](#estrutura-do-documento) 19 | - [Introdução](#introdução) 20 | - [Problema](#problema) 21 | - [Objetivos](#objetivos) 22 | - [Justificativa](#justificativa) 23 | - [Público-Alvo](#público-alvo) 24 | - [Especificações do Projeto](#especificações-do-projeto) 25 | - [Personas e Mapas de Empatia](#personas-e-mapas-de-empatia) 26 | - [Histórias de Usuários](#histórias-de-usuários) 27 | - [Requisitos](#requisitos) 28 | - [Requisitos Funcionais](#requisitos-funcionais) 29 | - [Requisitos não Funcionais](#requisitos-não-funcionais) 30 | - [Restrições](#restrições) 31 | - [Userflow](#projeto-de-interface) 32 | - [Metodologia](#metodologia) 33 | - [Ferramentas](#ferramentas) 34 | - [Controle de Versão](#controle-de-versão) 35 | - [Projeto da Solução](#projeto-da-solução) 36 | - [Tecnologias Utilizadas](#tecnologias-utilizadas) 37 | - [Arquitetura da solução](#arquitetura-da-solução) 38 | - [Avaliação da Aplicação](#avaliação-da-aplicação) 39 | - [Plano de Testes](#plano-de-testes) 40 | - [Registros de Testes](#registros-de-testes) 41 | - [Referências](#referências) 42 | 43 | 44 | # Introdução 45 | 46 | Neste contexto a palavra "evento" pode significar qualquer tipo de atividade onde tenha múltiplas pessoas envolvidas, como um jogo de futebol, ida a um show, campanha beneficente de moletom, churrasco, festa de formatura, casamento, uma saida com os amigos, desenvolvimento de um jogo, criação de uma música, acampamento, viagem em grupo, entre outros. 47 | 48 | Dito isso, durante o planejmanto de um evento devemos levar em consideração diversos fatores, dentre eles, número de convidados, local, comida, bebida, fotógrafo, motorista, ingresso. 49 | 50 | ## Problema 51 | 52 | A dificuldade não está apenas em avaliar quais serão os gastos necessários e estimar o valor de cada um deles, mas também em encontrar alguém para suprir aquela demanda, o que atualemente costuma ser feito por pesquisas na internet, indicação de amigos, redes sociais... O que pode ser um processo demorado e cansativo, além de dificultar a comparação de preços e não garantir a qualidade do serviço. 53 | 54 | ## Objetivos 55 | 56 | Juntar de uma forma concisa ferramentes necessárias para o planejamento de um evento, desenvolvendo uma forma mais eficiente, fácil, e rápida de planeja-lo. 57 | 58 | Como objetivos específicos, podemos ressaltar: 59 | * Estimar valor de um evento. 60 | * Estimar a quantidade de cada insumo em um evento. 61 | * Organizar e armazenar dados sobre o evento. 62 | * Facilitar a conexão entre o organizador e os fornecedores. 63 | 64 | ## Justificativa 65 | 66 | Diversas pessoas utilizam ferramentas como excel, whatsapp, e até mesmo papel e caneta para organizar seus eventos, acreditamos que uma ferramenta que junte todas essas funcionalidades em um só lugar, de forma simples e intuitiva, pode ser muito útil para o público alvo, já que atualmente nenhuma ferramenta acessível é capaz de fazer isso de forma dinâmica, permitindo alterações tanto do organizador quanto do fornecedor. 67 | 68 | ## Público-Alvo 69 | 70 | Tanto organizadores de eventos quanto fornecedores podem se beneficiar do site, porém, o foco no momento é direcionado aos organizadores, visto que o site sem eles não funcionaria. 71 | 72 | # Especificações do Projeto 73 | 74 | Nesta parte do documento será abordado de forma objetiva os problemas que a aplicação busca resolver, bem como as funcionalidades que ela deve apresentar. Para tanto, iremos utilizar as personas e histórias de usuários levantadas pelo grupo por meio de entrevistas com possíveis usuários. 75 | 76 | ## Personas e Mapas de Empatia 77 | 78 | 79 |

    80 | 81 |

    82 | 83 |

    84 | 85 |

    86 | 87 | ## Histórias de Usuários 88 | 89 | > Com base na análise das personas forma identificadas as seguintes histórias de usuários: 90 | 91 | |EU COMO... `PERSONA`| QUERO/PRECISO ... `FUNCIONALIDADE` |PARA ... `MOTIVO/VALOR` | 92 | |--------------------|------------------------------------|----------------------------------------| 93 | |Ana Beatriz | criar uma lista com os convidados e o que irão levar | ter conhecimento do que preciso pedir. | 94 | |Ana Beatriz | fazer uma estimativa de custo por convidado em 1 evento | fazer a lista de quantas pessoas vou convidar | 95 | |Maria Lana | reaproveitar o planejamento de um evento | não ter que escrever tudo novamente, e encher meu whatsapp com mensagens desnecessárias. | 96 | |Maria Lana | listar, e lembrar o que falta ser avaliado | não esquecer de algo essencial para o evento. | 97 | |Caio Martins | personalizar os preços médios com gastos do eventos | personalizar os preços médios com gastos do evento ter uma estimativa do custo.| 98 | |Caio Martins | planejar o evento junto de outras pessoas | criar grupos sobre a todo momento é desgastante. | 99 | |Gabriel Chagas | facilidade em adicionar gastos | usar o teclado para fazer tudo cansa. | 100 | |Gabriel Chagas | que outras pessoas saibam do que eu preciso | não gastar meu tempo procurando gente para resolver meu problema. | 101 | 102 | ## Requisitos 103 | 104 | > As tabelas a seguir apresentam os requisitos funcionais e não funcionais que detalham o escopo do projeto. 105 | 106 | ### Requisitos Funcionais 107 | 108 | |ID | Descrição do Requisito | Prioridade | 109 | |------|-----------------------------------------|----| 110 | |RF-001| Possibilitar o usuário de criar listas de participantes para o seu evento | Alta | 111 | |RF-002| Possibilitar o usuário de listar elementos que deseja em seu evento | Alta | 112 | |RF-003| Auxiliar o usuário calcular a quantidade necessária de cada insumo para seu evento | Alta | 113 | |RF-004| Possibilitar o usuário de criar múltiplas demandas para seu evento | Alta | 114 | |RF-005| Permitir que o usuário especifique o que será considerado um gasto em cada evento |Alta| 115 | |RF-006| Estimar o valor de um evento com base nos participantes, insumos e serviços | Alta | 116 | |RF-007| Possibilitar o usuário de compartilhar uma demanda com outros usuários | Média | 117 | |RF-008| Lista de afazeres para auxiliar organização do evento | Média | 118 | |RF-009| Permitir que o usuário seja específico quanto á algum serviço do seu evento | Média | 119 | |RF-010| Recomendar mudanças sobre o planejamento de um evento |Baixa| 120 | |RF-011| Utilizar algum meio para reconhecer o preço médio de um produto (API) |Baixa| 121 | |RF-012| Diferenciar diferentes eventos |Baixa| 122 | |RF-013| Permitir que mais de uma pessoa planeje cada evento |Baixa| 123 | |RF-014| Permitir ao usuário personalizar um elemento em específico de uma lista | Baixa | 124 | 125 | 126 | ### Requisitos não Funcionais 127 | 128 | |ID | Descrição do Requisito |Prioridade | 129 | |-------|-------------------------|----| 130 | |RNF-001| O site deverá ser de intuitivo | Alta | 131 | |RNF-002| Página não deverá ser recarregadada a cada mudança realizada | Alta | 132 | |RNF-003| O site deverá apresentar responsividade | Média | 133 | |RNF-004| Todas as páginas deverão rodar de forma fluída | Baixa | 134 | 135 | ## Restrições 136 | 137 | > Regras eu deverão ser seguidas a risca durante desenvolvimento do projeto 138 | 139 | | ID | Restrição | 140 | | -- |-------------------------------------------------------| 141 | | RE-01 | O projeto deverá ser entregue até o final do semestre. | 142 | | RE-02 | A equipe deverá reportar todo o desenvolvimento no Trello. | 143 | | RE-03 | A equipe não pode subcontratar o desenvolvimento do trabalho. | 144 | | RE-04 | As tecnologias utilizadas devem restringir à HTML, CSS e JavaScript. | 145 | 146 | # Userflow 147 | 148 | > 1° Listas: Organizando entre as categorias Participantes, Insumos, e Serviços, o usuário deve especificar quais elementos existirão para aquele evento 149 | 150 | ![Tela Listas](images/listas.jpeg) 151 | 152 | > 2° Fluxo: Usuário criará relações entre as listas de particpantes e os elementos das listas de Insumos 153 | 154 | ![Tela Fluxo](images/fluxo.jpeg) 155 | 156 | > 3° Chamados: Usuário compartilhará sua demanda e valor que está disposto a pagar por ela, sendo contactado por aqueles interessados em supri-la 157 | 158 | ![Tela Chamados](images/chamados.jpeg) 159 | 160 | > 4° Coleção: Usuário terá uma visão geral sobre os elementos de seu evento 161 | 162 | ![Tela Coleção](images/collection.jpeg) 163 | 164 | > 5° Valores: Com os dados já coletados, dá uma estimativa de quanto o evento custará, e disponibilizará gráficos onde serão mostrados proporcionalidades de cada categoria 165 | 166 | ![Tela Valores](images/valores.jpeg) 167 | 168 | # Metodologia 169 | 170 | Neste contexto, o grupo optou por utilizar o framework Scrum para o desenvolvimento do projeto, visto que o mesmo é um framework ágil que permite a adaptação do processo de desenvolvimento de acordo com as necessidades do projeto. 171 | 172 | ## Ferramentas 173 | 174 | | Ambiente | Plataforma |Link de Acesso | 175 | |-----------|-------------------------|---------------| 176 | | Documentação | Google Docs | https://docs.google.com/document/d/1qjGNgSORIWJKUIP3m1bH0Y8Otmr99sSKhRu7RO_rozI/edit?usp=sharing | 177 | | Weekly Scrum | Discord | https://discord.gg/QcnfPCfZ | 178 | | Backlog | Trello | https://trello.com/invite/b/IRT28t7u/ATTIa9c7c509f0e7eaf3ac96967fc2edecff66B9AE32/event-manager | 179 | | Código fonte | GitHub | https://github.com/ICEI-PUC-Minas-PMGES-TI/pmg-es-2023-1-ti1-2401100-quantofica | 180 | |~~Hospedagem do site~~ | ~~Heroku~~ | ~~https://event-manager-tiaw-e7211e805cd0.herokuapp.com~~| 181 | |Hospedagem do site| Vercel | https://event-manager-phi.vercel.app | 182 | 183 | 184 | > ~~Heroku foi escolhido como plataforma de hospedagem por não exigir que o código fonte passe pelo processo de minificação, algo realizado por ferramentas como rollup, webpack, e gulp. Como o código fonte não é minificado, é possível inspeciona-lo apenas com o link de seu deploy.~~ 185 | 186 | > Deploy atual no vercel.app utilizando vite 187 | 188 | ## Controle de Versão 189 | 190 | Em nosso repositório existem 3 diferentes branchs, sendo elas: 191 | 192 | * **master**: branch principal, onde se encontra o código fonte da aplicação. 193 | * **novo**: branch onde se encontra o código fonte da aplicação antes de ser reescrito. 194 | * **antigo**: branch onde se encontra a primeira versão da aplicação. 195 | 196 | Para realizar os **commits**, independentemente da **branch** foi utilizado o seguinte padrão: 197 | 198 | `git commit -m "Adicionada funcionalidade x"` 199 | 200 | > Onde a mensagem de commit deverá sempre começar com letra maiúscula e estar conjugada no particípio passado, indicando uma ação já concluída. 201 | 202 | Quanto a gerência de **issues**, o projeto adota a seguinte convenção para etiquetas: 203 | 204 | * `single:` afeta apenas uma funcionalidade 205 | * `multiple:` afeta mais de uma funcionalidade 206 | 207 | > Geralmente, funcionalidades com maior número de issues single, são as geradoras de issues multiple. 208 | 209 | # Projeto da Solução 210 | 211 | Nesta seção do documento será apresentado a solução desenvolvida pelo grupo para o problema apresentado na seção [Introdução](#introdução). Para tanto, será apresentado as tecnologias utilizadas, a arquitetura da solução e a forma como a solução foi desenvolvida. 212 | 213 | ## Tecnologias Utilizadas 214 | 215 | O projeto foi desenvolvido como uma aplicação **Node Vanilla** (HTML, CSS, Javascript), utilizando o **Express** como framework para o servidor, o **localStorage** do próprio navegador como banco de dados, e **Git** para versionamento. 216 | 217 | Bibliotecas utilizadas para para o front-end: 218 | * Bootstrap ( Sistema de grid ) 219 | * Bootstrap Icons ( Ícones ) 220 | * jQuery ( Programação em JavaScript mais eficiente ) 221 | * jQuery UI ( Função draggable() ) 222 | * Leader Line ( Cria uma linha entre dois elementos HTML ) 223 | * Hero Patterns ( Conjunto de texturas ) 224 | 225 | ## Arquitetura da solução (Antigo) 226 | 227 | ![Exemplo de Arquitetura](images/arquitetura.png) 228 | 229 | ### Na imagem acima podemos ver a organização das pastas e o localStorage após um uso simples do site, como o Heroku não aceita personalização da pasta raiz, tive que rodar o sguinte comando para fazer o deploy: 230 | 231 | `git subtree push --prefix codigo heroku master` 232 | 233 | > Onde heroku é a variável que contém o link para o repositório remoto, master é a branch que será enviada, e 'codigo' é a pasta que contém os arquivos da imagem acima. 234 | 235 | # Avaliação da Aplicação 236 | 237 | Nesta parte será abordado as estapas realizadas para verificar se o projeto desenvolvido se adequa nas especificações levantadas na seção [Especificação do Projeto](#especificações-do-projeto). 238 | 239 | ## Plano de Testes 240 | 241 | Primeiramente, pegamos a tabela [histórias de usuário](#histórias-de-usuários), e verificamos se todas as histórias foram atendidas. 242 | 243 | Após isso voltamos na tabela de requisitos, e verificamos se os requisitos foram atendidos fe forma satisfatória. 244 | 245 | Foi realizado planejamanto de eventos reais, visando testar a aplicação em um cenário real, e verificar se ela atendia as necessidades do usuário. 246 | 247 | No final, fizemos um teste de usabilidade com um usuário, para ver se ele conseguia utilizar o site sem dificuldades. 248 | 249 | ## Registros de Testes 250 | 251 | Os testes mostraram que com devida **criatividade** e esforço, é possível sim planejar um evento utilizando o site, sendo alguma das vezes, necessária uma visão um pouco mais abstrata, seria bem vindo templates pré-definidos para simplificar ainda mais o processo. 252 | 253 | # Referências 254 | 255 | ### Sites utilizados no desenvolvimento do projeto: 256 | 257 | * [Chat GPT](https://chat.openai.com) 258 | * [Bootstrap](https://getbootstrap.com) 259 | * [Bootstrap Icons](https://icons.getbootstrap.com) 260 | * [jQuery UI](https://jqueryui.com) 261 | * [Leader Line](https://anseki.github.io/leader-line/) 262 | * [Hero Patterns](https://heropatterns.com) -------------------------------------------------------------------------------- /codigo/src/Valores.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Event Manager 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 17 | 18 | 93 | 94 | 169 | 170 | 171 |
    172 |
    173 |
    174 |

    Valores

     Exportar Dados 177 |
    178 | 179 |
    180 | 181 |
    182 |
    183 | 184 |
    185 |
    Custo
    186 |
    187 |
    188 | 189 | 190 |
    191 |
    192 | 193 |
    194 |
    195 |
    196 |
    Lucro
    197 |
    Indiferente
    198 |
    199 | 200 |
    201 |
    202 | 203 |
    204 |
    205 |
    206 | 207 |
    208 | Progresso 209 |
    210 | 211 |
    212 |
    213 |
    214 |
    215 |
    216 |
    217 |
    219 | 220 |
    221 |
    222 |
    223 |
    224 |
    225 | 226 | 227 |
    228 |
    229 | 230 |
    231 |
    232 | 233 |
    234 |
    235 | Chamados 236 |
    237 |
    238 | 0 239 |
    240 |
    241 | 242 | 243 |
    244 |
    245 |
    246 | 247 | 248 |
    249 | 250 |
    251 |
    252 |
    253 |
    Participantes
    254 |
    255 |
    256 |
    257 | 261 | 262 |
    263 | 268 |
    269 |
    270 |
    271 | 272 |
    273 |
    274 |
    275 |
    Gastos
    276 | 284 |
    285 |
    286 | 290 |
    291 | 292 |
    293 |
    294 |
    295 |
    296 |
    297 | 298 |
    299 |
    300 | 301 |
    302 |
    303 | 304 | 307 | 308 | 311 |

    Lista de Progresso

    312 |
    313 |
      314 | 315 |
    • 316 |
      317 |
      318 | 319 |
      Abrir 320 | chamado para Insumos e Serviços
      321 |
      322 |
      323 |
      324 | 325 | 326 |
      327 |
      328 |
      329 |
    • 330 | 331 |
    332 |
    333 |
    334 | 335 |
    336 | 337 |
    338 |
    339 |

     Avisos

    340 |
      341 |
    • Faltam insumos
    • 342 |
    • Solicitações pendentes
    • 343 |
    • Verifique seu email
    • 344 |
    • Fluxo redundante
    • 345 |
    346 |
    347 |
      348 |
    349 |
    350 |
    351 | 352 | 353 |
    354 | 367 |
    368 |
    369 |
    370 |
    371 | 372 | 373 | 374 | 375 | 376 | 377 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public 379 | licenses. Notwithstanding, Creative Commons may elect to apply one of 380 | its public licenses to material it publishes and in those instances 381 | will be considered the “Licensor.” The text of the Creative Commons 382 | public licenses is dedicated to the public domain under the CC0 Public 383 | Domain Dedication. Except for the limited purpose of indicating that 384 | material is shared under a Creative Commons public license or as 385 | otherwise permitted by the Creative Commons policies published at 386 | creativecommons.org/policies, Creative Commons does not authorize the 387 | use of the trademark "Creative Commons" or any other trademark or logo 388 | of Creative Commons without its prior written consent including, 389 | without limitation, in connection with any unauthorized modifications 390 | to any of its public licenses or any other arrangements, 391 | understandings, or agreements concerning use of licensed material. For 392 | the avoidance of doubt, this paragraph does not form part of the 393 | public licenses. 394 | 395 | Creative Commons may be contacted at creativecommons.org. 396 | -------------------------------------------------------------------------------- /codigo/src/Collection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Event Manager 10 | 11 | 16 | 17 | 18 | 19 |
    20 | 21 | 96 | 97 | 172 | 173 | 174 |
    175 | 176 |
    177 | 178 |
    179 | 180 |
    181 | 186 |
    187 |
    188 |
    189 | 190 |
    191 |

    192 | 196 |

    197 | 198 |
    199 |
    200 |
      201 |
    • Leonardo
    • 202 |
    • Giovanna
    • 203 |
    • Gabriel
    • 204 |
    • Alice
    • 205 |
    • Vitor
    • 206 |
    • Mateus
    • 207 |
    208 |
    209 |
    210 |
    211 | 212 |
    213 |

    214 | 218 |

    219 | 220 |
    221 |
    222 |
      223 |
    • Alana
    • 224 |
    • Isabel
    • 225 |
    • Carol
    • 226 |
    • Lara
    • 227 |
    • Vitor
    • 228 |
    • Mateus
    • 229 |
    • Alana
    • 230 |
    • Isabel
    • 231 |
    • Carol
    • 232 |
    • Lara
    • 233 |
    • Vitor
    • 234 |
    • Mateus
    • 235 |
    • Alana
    • 236 |
    • Isabel
    • 237 |
    • Carol
    • 238 |
    • Lara
    • 239 |
    • Vitor
    • 240 |
    • Mateus
    • 241 |
    242 |
    243 |
    244 |
    245 | 246 |
    247 |

    248 | 252 |

    253 | 254 |
    255 |
    256 |
      257 |
    • Leonardo
    • 258 |
    • Giovanna
    • 259 |
    • Gabriel
    • 260 |
    • Alice
    • 261 |
    • Vitor
    • 262 |
    • Mateus
    • 263 |
    264 |
    265 |
    266 |
    267 |
    268 |
    269 |
    270 | 271 |
    272 |
    273 | 274 | 279 |
    280 |
    281 |
    282 |
    283 |

    284 | 287 |

    288 |
    289 |
    290 |
      291 |
    • Negrinni
    • 292 |
    • Gin
    • 293 |
    • Aperol
    • 294 |
    • Moscow Mule
    • 295 |
    • Caipirinha
    • 296 |
    • Pina Colada
    • 297 |
    298 |
    299 |
    300 |
    301 |
    302 |

    303 | 306 |

    307 |
    308 |
    309 |
      310 |
    • Macarrão
    • 311 |
    • Raviolli
    • 312 |
    • Risotto
    • 313 |
    314 |
    315 |
    316 |
    317 |
    318 |

    319 | 322 |

    323 |
    324 |
    325 |
      326 |
    • Chinelo
    • 327 |
    • Pulseira
    • 328 |
    • Óculos
    • 329 |
    330 |
    331 |
    332 |
    333 |
    334 |
    335 | 336 |
    337 | 338 |
    339 |
    340 | 341 | 346 |
    347 |
    348 | 349 |
    350 |
    351 |

    352 | 355 |

    356 |
    357 |
    358 |
      359 |
    • 360 | Fotógrafo 361 | R$4.000,00 362 |
    • 363 |
    • 364 | Maquiadora 365 | R$690,00 366 |
    • 367 |
    • 368 | Vestidos 369 | R$3.900,00 370 |
    • 371 |
    • 373 | Design 374 | R$1.500,00 375 |
    • 376 |
    377 |
    378 |
    379 |
    380 |
    381 |

    382 | 385 |

    386 |
    387 |
    388 |
      389 |
    • 390 | DJ 391 | R$2.000,00 392 |
    • 393 |
    • 394 | Ceriomônia 395 | R$1.000,00 396 |
    • 397 |
    398 |
    399 |
    400 |
    401 |
    402 |
    403 |
    404 |
    405 | 406 | 432 | 433 |
    434 |
    435 | 436 | 437 | 438 | 439 | 440 | -------------------------------------------------------------------------------- /codigo/src/JS/cardMovivel.js: -------------------------------------------------------------------------------- 1 | /* 2 | ? Documentação: 3 | carregaCards() - Carrega os cards salvos no localStorage quando a página é carregada 4 | nomeiaCard(element) - Botão que edita o título do card 5 | resetaCard(element) - Botão que reseta o conteúdo do card 6 | removeCard(element) - Botão que apaga o card 7 | finalDaLinha(element) - Cursor no final de cada linha 8 | removerLiVazias(cardContainer) - Remove li vazias 9 | atualizaTitulo(element) - Atualiza o título do card 10 | atualizaConteudo(element) - Atualiza conteudo do card 11 | isTextSelected(element) - Permite alterar o título caso conteúdo esteja inteiramente selecionados 12 | customDrag(elemento) - Função para configurar regras para movimentação do card 13 | editaCard(element) - Editar o conteúdo do card quando clicar em alguma linha 14 | selectAll(element) - Permite selecionar todo o texto de um elemento 15 | criaConteudo(lista, gapping) - HTML de um card 16 | */ 17 | // ? Mesmo não havendo caracteres menores que 2.5 pixels, é bom definir uma margem de segurança 18 | // ? Seria possível colocar infinitos caracteres de largura 0 19 | // ? estéticamente não teria efeito algum, porém travaria o programa 20 | 21 | // * Objeto que guarda as informações de cada card (Objeto do tipo Lista) 22 | 23 | import './bootstrap.js'; 24 | import 'jquery-ui-dist/jquery-ui'; 25 | import * as bootstrap from 'bootstrap'; 26 | 27 | class Lista { 28 | constructor(_id, titulo, linhas, coordenadas) { 29 | this._id = _id; 30 | this.titulo = titulo; 31 | this.linhas = linhas; 32 | this.coordenadas = coordenadas; 33 | } 34 | } 35 | var maxWidth = 200; // * Máximo de pixels permitidos no título 36 | var maxLength = 100; // * Máximo de caracteres permitidos no título 37 | const TAM = 6; // * Quantidade de cards que será possível adicionar 38 | 39 | var elemento = "elementosParticipantes"; // * Para manipular listas de uma determinada careogoria 40 | 41 | var vetor = Array(TAM).fill(true); // * True = Disponível, False = Ocupado 42 | var matriz = []; 43 | for (var i = 0; i < 3; i++) { 44 | matriz.push(Array(TAM).fill(true)); 45 | } 46 | 47 | // * Pegará do local Storage para deixar salvo quando a página recarregar 48 | var ListaTipo = localStorage.getItem("ultimaLista") || "ListaParticipantes"; 49 | 50 | var position = 0; 51 | var todasListas = []; 52 | 53 | if (ListaTipo === "ListaParticipantes") { 54 | $('.listaParticipantes').addClass('active'); 55 | $('.listaInsumos, .listaServicos').removeClass('active'); 56 | position = 0; 57 | elemento = "elementosParticipantes"; 58 | carregaCards(); 59 | } 60 | if (ListaTipo === "ListaInsumos") { 61 | $('.listaInsumos').addClass('active'); 62 | $('.listaParticipantes, .listaServicos').removeClass('active'); 63 | position = 1; 64 | elemento = "elementosInsumos"; 65 | carregaCards(); 66 | } 67 | if (ListaTipo === "ListaServicos") { 68 | $('.listaServicos').addClass('active'); 69 | $('.listaParticipantes, .listaInsumos').removeClass('active'); 70 | position = 2; 71 | elemento = "elementosServicos"; // > Provavelmente não será necessário 72 | carregaCards(); 73 | } 74 | 75 | // * Carrega os cards salvos no localStorage quando a página é carregada 76 | function carregaCards() { 77 | // * Limpa todos os cards 78 | $('.card').parent().remove(); 79 | todasListas = JSON.parse(localStorage.getItem(ListaTipo)); // * Pega todas as listas salvas no localStorage 80 | if (todasListas) { // ? Caso não exista nada no localStorage, todasListas será null e não entrará no if 81 | let cont = 0; // * Contador para definir a posição dos cards 82 | for (let i = 0; i < TAM; i++) { 83 | // * Caso não exista nenhuma informação salva naquela posição, não será criado o card 84 | if (todasListas[i] != null) { 85 | 86 | // * Cria um objeto do tipo Lista com as informações do objeto salvo no localStorage 87 | let lista = new Lista(todasListas[i]._id, todasListas[i].titulo, todasListas[i].linhas, todasListas[i].coordenadas); 88 | // * Cria o card com as informações do objeto 89 | let carregaCard = criaConteudo(lista, cont++); 90 | 91 | // ? É como se estivessemos adicionando funcionalidades que um card é capaz de fazer 92 | customDrag(carregaCard); 93 | editaCard(carregaCard); 94 | nomeiaCard(carregaCard); 95 | resetaCard(carregaCard); 96 | removeCard(carregaCard); 97 | 98 | $('section').append(carregaCard); // * Adiciona o card na página 99 | matriz[position][i] = false; // * Posição ocupada 100 | } else { matriz[position][i] = true; console.log("entrou") } // * Posição disponível 101 | } 102 | } else { todasListas = []; } 103 | } 104 | // * Botões só estarão disponíveis após o carregamento da página 105 | $(document).ready(() => { 106 | carregaCards(); 107 | 108 | $('#adicionaCard').click(() => { 109 | let indiceTrue = -1; 110 | for (var i = 0; i < TAM; i++) { 111 | if (matriz[position][i] === true) { 112 | matriz[position][i] = false; 113 | indiceTrue = i; 114 | break; 115 | } 116 | } 117 | 118 | // * Percorre o vetor, se existir algum elemento com o valor true, criará card 119 | if (indiceTrue === -1) { 120 | $('.toast-body').text('O Número máximo de cards para esta categoria foi atingido.'); 121 | $('.toast').toast('show'); 122 | return; 123 | } 124 | 125 | // * Realizar testes para checar se foi corrigido 126 | var lista = new Lista(indiceTrue, 'Lista ' + (indiceTrue + 1), " ", null); 127 | var newCard = criaConteudo(lista, indiceTrue); 128 | 129 | customDrag(newCard); 130 | editaCard(newCard); 131 | nomeiaCard(newCard); 132 | resetaCard(newCard); 133 | removeCard(newCard); 134 | 135 | todasListas[indiceTrue] = lista; 136 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 137 | 138 | $('section').append(newCard); 139 | }); 140 | // > Nome poderia ser removeTodosCards 141 | $('#removeCard').click(() => { 142 | $('.card').parent().remove(); 143 | matriz[position].fill(true); // * Todas as posições se tornam disponíveis 144 | todasListas = []; // * Agora o vetor é vazio 145 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 146 | 147 | let elementoApagados = JSON.parse(localStorage.getItem(elemento)) || []; 148 | elementoApagados.fill(null); 149 | localStorage.setItem(elemento, JSON.stringify(elementoApagados)); 150 | }); 151 | $('#organizaCard').click(() => { 152 | // > Não acho que precisa mas bora deixar aqui 153 | todasListas = JSON.parse(localStorage.getItem(ListaTipo)); 154 | for (let i = 0; i < todasListas.length; i++) { 155 | if (todasListas[i] != null) { 156 | todasListas[i].coordenadas = null; 157 | } 158 | } 159 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 160 | carregaCards(); 161 | }); 162 | $('.listaParticipantes').click(() => { 163 | $('#tipoLista').find('i').removeClass('bi-cash-stack bi-briefcase').addClass('bi-people'); 164 | $('#tipoLista').find('span').text('Participantes'); 165 | $('.listaParticipantes').addClass('active'); 166 | $('.listaInsumos, .listaServicos').removeClass('active'); 167 | ListaTipo = "ListaParticipantes"; 168 | elemento = "elementosServicos"; 169 | localStorage.setItem("ultimaLista", ListaTipo); 170 | position = 0; 171 | localStorage.setItem("ultimaListaPosicao", position); 172 | carregaCards(); 173 | }); 174 | $('.listaInsumos').click(() => { 175 | $('#tipoLista').find('i').removeClass('bi-people bi-briefcase').addClass('bi-cash-stack'); 176 | $('#tipoLista').find('span').text('Insumos'); 177 | $('.listaInsumos').addClass('active'); 178 | $('.listaParticipantes, .listaServicos').removeClass('active'); 179 | ListaTipo = "ListaInsumos"; 180 | elemento = "elementosInsumos"; 181 | localStorage.setItem("ultimaLista", ListaTipo); 182 | position = 1; 183 | localStorage.setItem("ultimaListaPosicao", position); 184 | carregaCards(); 185 | }); 186 | $('.listaServicos').click(() => { 187 | $('#tipoLista').find('i').removeClass('bi-people bi-cash-stack').addClass('bi-briefcase'); 188 | $('#tipoLista').find('span').text('Serviços'); 189 | $('.listaServicos').addClass('active'); 190 | $('.listaParticipantes, .listaInsumos').removeClass('active'); 191 | ListaTipo = "ListaServicos"; 192 | localStorage.setItem("ultimaLista", ListaTipo); 193 | elemento = "elementosServicos"; // > Provavelmente não será necessário 194 | position = 2; 195 | localStorage.setItem("ultimaListaPosicao", position); 196 | carregaCards(); 197 | }); 198 | }); 199 | // * Botão que edita o título do card 200 | function nomeiaCard(element) { 201 | element.find('.nomeiaCard').click(function () { 202 | // * Seleciona o título, permite edição, foca nele, e seleciona todo o texto 203 | let cardTitle = $(this).closest('.card-container').find('.card-header span:first-child'); 204 | cardTitle.attr('contenteditable', 'true'); 205 | cardTitle.focus(); 206 | selectAll(cardTitle[0]); 207 | // * Ao editar o título, outros cards se tornam fixos 208 | $('.card-container').not($(this).closest('.card-container')).css('position', 'fixed'); 209 | 210 | // * Cards em edição tem z-index maior que os outros 211 | $(this).closest('.card-container').css('z-index', '1'); 212 | $('.card-container').not($(this).closest('.card-container')).css('z-index', '0'); 213 | 214 | // * Ao perder o foco, o título é alterado 215 | cardTitle.blur(function () { 216 | atualizaTitulo($(this)); 217 | }); 218 | // * Ao pressionar enter, o título é alterado 219 | cardTitle.on('keydown click', function (e) { 220 | if (e.keyCode === 13) { 221 | e.preventDefault(); 222 | atualizaTitulo($(this)); 223 | cardTitle.attr('contenteditable', 'false'); 224 | let listGroup = $(this).closest('.card-container').find('.list-group'); 225 | let lastLi = listGroup.find('li:last-child').focus(); 226 | finalDaLinha(lastLi[0]); 227 | } else if (($(this).width() >= maxWidth || $(this).text().length >= maxLength) && !isTextSelected(cardTitle[0]) 228 | && e.keyCode !== 8 && e.keyCode !== 46 && e.keyCode !== 37 && e.keyCode !== 39 && !e.ctrlKey) { 229 | // * Impede que o usuário digite mais caracteres que o permitido 230 | e.preventDefault(); 231 | $('.toast-body').text('Tamanho máximo de título atingido.'); 232 | $('.toast').toast('show'); 233 | } 234 | // * Caso o usuário consiga digitar mais caracteres que o permitido, o título é cortado 235 | if ($(this).text().length > maxLength) { 236 | let trimmedText = $(this).text().substring(0, maxLength); 237 | cardTitle.text(trimmedText); 238 | } 239 | }); 240 | }); 241 | } 242 | // * Botão que reseta o conteúdo do card 243 | function resetaCard(element) { 244 | element.find('.resetaCard').click(function () { 245 | $(this).closest('.card-container').find('.list-group li').remove(); 246 | let liVazia = $('
  • ').text(""); 247 | $(this).closest('.card-container').find('.list-group').append(liVazia); 248 | liVazia.focus(); 249 | 250 | let cardId = $(this).closest('.card-container').attr('id'); 251 | let cardNumber = parseInt(cardId.split('-')[1]); 252 | todasListas[cardNumber].linhas = []; 253 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 254 | }); 255 | } 256 | // * Botão que apaga o card 257 | function removeCard(element) { 258 | element.find('.removeCard').click(function () { 259 | let cardId = $(this).closest('.card-container').attr('id'); 260 | let cardNumber = parseInt(cardId.split('-')[1]); 261 | $(this).closest('.card-container').remove(); 262 | todasListas[cardNumber] = null; 263 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 264 | matriz[position][cardNumber] = true; 265 | 266 | let elementoApagado = JSON.parse(localStorage.getItem(elemento)) || []; 267 | elementoApagado[cardNumber].coordenadas = null; 268 | localStorage.setItem(elemento, JSON.stringify(elementoApagado)); 269 | }); 270 | } 271 | // * Cursor no final de cada linha 272 | function finalDaLinha(element) { 273 | if (element && element.childNodes && element.childNodes.length > 0) { 274 | let range = document.createRange(); 275 | let sel = window.getSelection(); 276 | range.setStart(element.childNodes[0], element.childNodes[0].length); 277 | range.collapse(true); 278 | sel.removeAllRanges(); 279 | sel.addRange(range); 280 | } 281 | } 282 | // * Remove li vazias 283 | function removerLiVazias(cardContainer) { 284 | cardContainer.find('.list-group-item').each(function () { 285 | // * Vai remover todos os espaços em branco, e checar se ainda resta alguma coisa 286 | if ($(this).text().trim() === '' && cardContainer.find('.list-group-item').length > 1) { 287 | $(this).remove(); 288 | atualizaConteudo(cardContainer); 289 | } 290 | }); 291 | } 292 | // * Atualiza o título do card 293 | function atualizaTitulo(element) { 294 | let novoNome = element.text().trim(); 295 | if (novoNome !== '') { 296 | element.text(novoNome); 297 | let cardId = element.closest('.card-container').attr('id'); 298 | let cardNumber = parseInt(cardId.split('-')[1]); 299 | todasListas[cardNumber].titulo = novoNome; 300 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 301 | } 302 | } 303 | // * Atualiza conteudo do card 304 | function atualizaConteudo(element) { 305 | let novoConteudo = []; 306 | element.find('.list-group-item').each(function () { 307 | novoConteudo.push($(this).text().trim()); 308 | }); 309 | let cardId = element.closest('.card-container').attr('id'); 310 | if (cardId != null) { 311 | let cardNumber = parseInt(cardId.split('-')[1]); 312 | todasListas[cardNumber].linhas = novoConteudo; 313 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 314 | console.log('Conteúdo salvo no card de ID ' + cardNumber + ' como: ' + novoConteudo); 315 | } 316 | } 317 | // * Permite alterar o título caso conteúdo esteja inteiramente selecionado 318 | function isTextSelected(element) { 319 | let selection = window.getSelection(); 320 | let selectedText = selection.toString(); 321 | let elementText = element.textContent; 322 | return selectedText === elementText; 323 | } 324 | // * Função para configurar regras para movimentação do card 325 | function customDrag(elemento) { 326 | elemento.find(".draggable").draggable({ 327 | containment: "section", 328 | scroll: false, 329 | snap: false, 330 | stack: ".draggable", 331 | cursor: "grabbing", 332 | handle: ".card-header", 333 | stop: function () { // * Executado sempre que o card é solto 334 | $(this).closest('.card-container').css('position', 'absolute'); 335 | // var cardPosition = $(this).position().left + 48 + 300; 336 | // var canvasWidth = $('section').width(); 337 | // if (!$('aside').hasClass('active')) { 338 | // larguraPermitida = canvasWidth - 250; 339 | // if (cardPosition > larguraPermitida && !$(this).hasClass('mover')) { 340 | // $(this).addClass('mover'); 341 | // console.log('Mover adicionado'); 342 | // $(this).find('.card-header').css('background-color', '#f0f0f0'); 343 | // } else if (cardPosition <= larguraPermitida && $(this).hasClass('mover')) { 344 | // $(this).removeClass('mover'); 345 | // console.log("Mover removido"); 346 | // $(this).find('.card-header').css('background-color', '#fff'); 347 | // } 348 | // } 349 | 350 | // * Quero pegar as coordanadas do card quando ele for solto 351 | // * E salvar no localStorage 352 | // * E quando a página for carregada, os cards serão carregados com as coordenadas salvas 353 | // * E quando um card for movido, as coordenadas serão atualizadas 354 | // * E quando um card for apagado, as coordenadas serão apagadas 355 | 356 | let cardId = $(this).closest('.card-container').attr('id'); 357 | let cardNumber = parseInt(cardId.split('-')[1]); 358 | todasListas[cardNumber].coordenadas = [$(this).offset().left - 48, $(this).offset().top]; 359 | localStorage.setItem(ListaTipo, JSON.stringify(todasListas)); 360 | console.log('Coordenadas salvas no card de ID ' + cardNumber + ' como: ' + todasListas[cardNumber].coordenadas); 361 | } 362 | }); 363 | } 364 | // * Editar o conteúdo do card quando clicar em alguma linha 365 | function editaCard(element) { 366 | element.find('.list-group').on('keydown', function (e) { 367 | // ? Apenas para simplificar o código 368 | let listGroup = $(this).closest('.card-container').find('.list-group'); 369 | let otherCards = $('.card-container').not($(this).closest('.card-container')); 370 | let currentLi = listGroup.find('li:focus'); 371 | // * Ao perder o foco, o conteúdo é atualizado 372 | listGroup.on('blur', 'li', function () { 373 | atualizaConteudo(listGroup); 374 | }); 375 | // * Se o usuário pressionar backspace e a linha estiver vazia, e não houver apenas 1 linha, linha atual é apagada, e o focus irá para a linha anterior 376 | if ((e.keyCode === 8 || e.keyCode === 38 || e.keyCode === 40) && currentLi.text() === '' && listGroup.children().length > 1) { 377 | e.preventDefault(); 378 | currentLi.remove(); 379 | var lastLi = listGroup.find('li:last-child').focus(); 380 | finalDaLinha(lastLi[0]); 381 | } 382 | // * Usuário não pode criar nova linha se atual estiver em branco 383 | if (e.keyCode === 13 && currentLi.text() === '') { 384 | e.preventDefault(); 385 | return; 386 | } 387 | // * Enter 388 | if (e.keyCode === 13) { 389 | e.preventDefault(); 390 | let liVazia = $('
  • ').text(""); 391 | otherCards.css('position', 'fixed'); 392 | listGroup.append(liVazia); 393 | liVazia.focus(); 394 | finalDaLinha(liVazia[0]); 395 | atualizaConteudo($(this).closest('.card-container')); 396 | } 397 | // * Seta para cima 398 | if (e.keyCode === 38) { 399 | e.preventDefault(); 400 | currentLi.prev().focus(); 401 | finalDaLinha(currentLi.prev()[0]); 402 | } 403 | // * Seta para baixo 404 | if (e.keyCode === 40) { 405 | e.preventDefault(); 406 | currentLi.next().focus(); 407 | finalDaLinha(currentLi.next()[0]); 408 | } 409 | // * Remove li vazias quando um click é realizado 410 | $(document).click(function () { 411 | // > Aqui possívelmente está gastando mais processamento que o necessário 412 | $('.card-container').each(function () { 413 | removerLiVazias($(this)); 414 | }); 415 | atualizaConteudo($(this).closest('.card-container')); 416 | }); 417 | }); 418 | } 419 | // * Permite selecionar todo o texto de um elemento 420 | function selectAll(element) { 421 | if (document.body.createTextRange) { // Suporte para Internet Explorer 422 | var range = document.body.createTextRange(); 423 | range.moveToElementText(element); 424 | range.select(); 425 | } else if (window.getSelection) { // Suporte para navegadores modernos 426 | var range = document.createRange(); 427 | range.selectNodeContents(element); 428 | var selection = window.getSelection(); 429 | selection.removeAllRanges(); 430 | selection.addRange(range); 431 | } 432 | } 433 | // * HTML de um card 434 | function criaConteudo(lista, gapping) { 435 | var conteudo = '
  • '; 436 | if (lista.linhas.length !== 0 && lista.linhas[0] !== '') { 437 | conteudo = ''; 438 | for (let i = 0; i < lista.linhas.length; i++) { 439 | // * Caso a linha não seja vazia, ela é adicionada ao conteúdo 440 | if (lista.linhas[i] !== '') { 441 | conteudo += '
  • ' + lista.linhas[i] + '
  • '; 442 | } 443 | } 444 | } 445 | // Caso lista.coordenadas estiver vazio 446 | let conteudoStyle = ''; 447 | if (lista.coordenadas == null) { 448 | conteudoStyle = 'top: ' + (gapping + 1) + '0%;'; 449 | } else { 450 | conteudoStyle = 'top: ' + lista.coordenadas[1] + 'px; left: ' + lista.coordenadas[0] + 'px;'; 451 | } 452 | return $( 453 | `
    454 |
    455 |
    456 | ${lista.titulo} 457 | 458 | 459 | 460 | 461 | 462 |
    463 |
    464 |
      465 | ${conteudo} 466 |
    467 |
    468 |
    469 |
    `); 470 | /* 471 | > Será feito algo como: 472 | > if lista.coordenadas == null, então seguirá o procedimento de gapping padrão 473 | > caso contrário, o left será definido como lista.coordenadas[0] e o top como lista.coordenadas[1] 474 | : Primeiramente vamos pegar a posição do card, para isso criarei uma função dentro de customDrag, que irá dar 475 | : um console log com as coordenadas dele, após isso, testarei como salvar no localStorage, e como carregar 476 | : concluindo, as coordenadas estarão feitas 477 | ! Problema que não faço ideia como resolver: 478 | ! Quando uma página é redimensionada cards não são movidos para posições fora da tela 479 | * Possível solução: Mudar a forma de salvas as coordenadas, ao invés de salvar as coordenadas do card, 480 | * salvar as coordenadas do card em relação ao canvas 481 | */ 482 | } -------------------------------------------------------------------------------- /codigo/src/JS/fluxo.js: -------------------------------------------------------------------------------- 1 | /* 2 | ? Documentação 3 | carregaElementos() - Função que carrega os elementos na tela 4 | customDrag(elemento) - Função que configura as regras para movimentação do card participante 5 | dragInsumos(elemPento) - Função que configura as regras para movimentação do card insumo 6 | fluxoConecta(element) - Função que configura as regras para conexão 7 | excluiConexao(element) - Função que exclui uma conexão em específico 8 | guardaValor(element) - Função que guarda os valores de cada conexão 9 | criaElementoParticipante(element, gapping) - HTML de um elemento participante 10 | criaElementoInsumo(element, gapping) - HTML de um elemento insumo 11 | criaConexao(element) - Criará as conexões que ficam em baixo de um elemento do tipo participante 12 | recalculaValor(idDestino, insumoAlterado) - Função para setar os valores da conexão 13 | */ 14 | 15 | import './bootstrap.js'; 16 | import 'jquery-ui-dist/jquery-ui'; 17 | import * as bootstrap from 'bootstrap'; 18 | 19 | import LeaderLine from 'leader-line-new'; 20 | 21 | // * Pegará as informações da página anterior e salvará as coordenadas dos elementos 22 | class Elemento { 23 | constructor(_id, titulo, coordenadas, linhas) { 24 | this._id = _id; 25 | this.titulo = titulo; 26 | this.coordenadas = coordenadas; 27 | this.linhas = linhas; 28 | } 29 | } 30 | // * Objeto que guardará informações de cada conexão 31 | class Conexao { 32 | constructor(idOrigem, idDestino, quantidade, medida, insumo) { 33 | this.idOrigem = idOrigem; 34 | this.idDestino = idDestino; 35 | this.quantidade = quantidade; 36 | this.medida = medida; 37 | this.insumo = insumo; 38 | } 39 | } 40 | // * Estilização da linha 41 | var options = { 42 | size: 5, 43 | layer: 0, 44 | endPlug: 'behind', 45 | startSocket: 'right', 46 | endSocket: 'left', 47 | startPlugColor: '#0cf', 48 | endPlugColor: '#04f', 49 | gradient: true, 50 | // color: '#04f', 51 | }; 52 | var TAM = 6; // * Número de cards que são possíveis de serem criados no momento 53 | 54 | var ListasParticipantes = JSON.parse(localStorage.getItem("ListaParticipantes")) || []; 55 | var elementosParticipantes = JSON.parse(localStorage.getItem("elementosParticipantes")) || []; 56 | 57 | var ListasInsumos = JSON.parse(localStorage.getItem("ListaInsumos")) || []; 58 | var elementosInsumos = JSON.parse(localStorage.getItem("elementosInsumos")) || []; 59 | 60 | var conexoesVisiveis = JSON.parse(localStorage.getItem("conexoesVisiveis")); 61 | var conexoes = JSON.parse(localStorage.getItem("conexoes")) || []; 62 | if (conexoes.length === 0 || conexoes.length !== TAM || conexoes[0].length !== TAM) { 63 | conexoes = []; 64 | for (let i = 0; i < TAM; i++) { 65 | conexoes[i] = []; 66 | for (let j = 0; j < TAM; j++) { 67 | conexoes[i][j] = [null, []]; 68 | } 69 | } 70 | } 71 | // * Botões só estarão disponíveis após o carregamento da página 72 | $(document).ready(() => { 73 | carregaElementos(); 74 | carregaConexoes(); 75 | if (conexoesVisiveis == null) { conexoesVisiveis = true; } 76 | for (let i = 0; i < TAM; i++) { 77 | for (let j = 0; j < TAM; j++) { 78 | if (conexoes[i][j][0] != null) { 79 | conexoes[i][j][0].startPlugColor = conexoesVisiveis ? '#0cf' : 'aliceblue'; 80 | conexoes[i][j][0].endPlugColor = conexoesVisiveis ? '#04f' : 'aliceblue'; 81 | } 82 | } 83 | } 84 | if (conexoesVisiveis) { 85 | $('#visibilidadeFluxo').find('i').addClass('bi-eye-slash').removeClass('bi-eye'); 86 | } else { $('#visibilidadeFluxo').find('i').addClass('bi-eye').removeClass('bi-eye-slash'); } 87 | $('#visibilidadeFluxo').find('span').text(conexoesVisiveis ? 'Ocultar' : 'Exibir'); 88 | 89 | // * Organiza a área de trabalho 90 | $('#dividirFluxo').click(() => { 91 | for (let i = 0; i < TAM; i++) { 92 | if (elementosParticipantes[i] != null) { elementosParticipantes[i].coordenadas = null; } 93 | if (elementosInsumos[i] != null) { elementosInsumos[i].coordenadas = null; } 94 | } 95 | localStorage.setItem("elementosParticipantes", JSON.stringify(elementosParticipantes)); 96 | localStorage.setItem("elementosInsumos", JSON.stringify(elementosInsumos)); 97 | location.reload(); // ? Se fosse utilizar a função de carregar elementos, teria que apagar os elementos já existentes 98 | }); 99 | // * Oculta/Exibe LeaderLines 100 | $('#visibilidadeFluxo').click(() => { 101 | for (let i = 0; i < TAM; i++) { 102 | for (let j = 0; j < TAM; j++) { 103 | if (conexoes[i][j][0] != null) { 104 | conexoes[i][j][0].startPlugColor = conexoesVisiveis ? 'aliceblue' : '#0cf'; 105 | conexoes[i][j][0].endPlugColor = conexoesVisiveis ? 'aliceblue' : '#04f'; 106 | } 107 | } 108 | } 109 | $('#visibilidadeFluxo').find('i').toggleClass('bi-eye bi-eye-slash'); 110 | $('#visibilidadeFluxo').find('span').text(conexoesVisiveis ? 'Exibir' : 'Ocultar'); 111 | conexoesVisiveis = !conexoesVisiveis; 112 | localStorage.setItem("conexoesVisiveis", JSON.stringify(conexoesVisiveis)); 113 | }); 114 | }); 115 | // * Função para carregar os elementos 116 | function carregaElementos() { 117 | let contP = 0; // ? Para calcular posição onde os elementos serão gerados 118 | let contI = 0; // ? Para calcular posição onde os elementos serão gerados 119 | 120 | for (let i = 0; i < TAM; i++) { 121 | if (ListasParticipantes[i] != null) { 122 | if (elementosParticipantes[i] != null && elementosParticipantes[i].coordenadas != null) { // ? Caso o elemento exista e tenha coordenadas salvas 123 | elementosParticipantes[i] = new Elemento(ListasParticipantes[i]._id, ListasParticipantes[i].titulo, elementosParticipantes[i].coordenadas, ListasParticipantes[i].linhas); 124 | } else { 125 | elementosParticipantes[i] = new Elemento(ListasParticipantes[i]._id, ListasParticipantes[i].titulo, null, ListasParticipantes[i].linhas); 126 | } 127 | var elementoParticipanteCarregado = criaElementoParticipante(elementosParticipantes[i], contP++); 128 | 129 | customDrag($(elementoParticipanteCarregado)); 130 | fluxoConecta($(elementoParticipanteCarregado)); 131 | excluiConexao($(elementoParticipanteCarregado)); 132 | adicionaConexao($(elementoParticipanteCarregado)); 133 | guardaValor($(elementoParticipanteCarregado)); 134 | $('section').append(elementoParticipanteCarregado); 135 | 136 | } else { // * Se a conexão já existir, mas o elemento não, apagar conexões onde está envolvido 137 | for (let j = 0; j < TAM; j++) { 138 | if (conexoes[i][j][0] != null) { 139 | conexoes[i][j] = [null, []]; 140 | } 141 | } 142 | } 143 | if (ListasInsumos[i] != null) { 144 | if (elementosInsumos[i] != null && elementosInsumos[i].coordenadas != null) { 145 | elementosInsumos[i] = new Elemento(i, ListasInsumos[i].titulo, elementosInsumos[i].coordenadas, ListasInsumos[i].linhas); 146 | } else { 147 | elementosInsumos[i] = new Elemento(i, ListasInsumos[i].titulo, null, ListasInsumos[i].linhas); 148 | } 149 | var elementoInsumoCarregado = criaElementoInsumo(elementosInsumos[i], contI++); 150 | 151 | dragInsumos($(elementoInsumoCarregado)); 152 | fluxoDestinoGenerico($(elementoInsumoCarregado)); 153 | $('section').append(elementoInsumoCarregado); 154 | 155 | } else { // * Se a conexão já existir, mas o elemento não, apagar conexões onde está envolvido 156 | for (let j = 0; j < TAM; j++) { 157 | if (conexoes[j][i][0] != null) { 158 | conexoes[j][i] = [null, []]; 159 | } 160 | } 161 | } 162 | } 163 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 164 | } 165 | // * Função para carregar as conexões 166 | function carregaConexoes() { 167 | for (let i = 0; i < TAM; i++) { 168 | for (let j = 0; j < TAM; j++) { 169 | if (conexoes[i][j][0] != null) { // * Se a conexão existir, cria uma LeaderLine e um objeto do tipo Conexão 170 | let startEl = document.getElementById(`fluxoConecta-${i}`); 171 | let endEl = document.getElementById(`fluxoDestino-${j}`); 172 | conexoes[i][j][0] = new LeaderLine( 173 | startEl, 174 | endEl, 175 | options 176 | ); 177 | // : Quando a conexão for criada, os parametros dela já deverão existir 178 | let novaConexao = criaConexao(i, elementosInsumos[j]); // ? criaConexão() retorna o HTML da janelinha azul que fica no elemento participante 179 | $(`#listaP-${i}`).find('.card-body').append(novaConexao); 180 | 181 | // : Jeito novo 182 | // : Aqui estarei colocando os inputs do localStorage dentro dos inputs da conexão 183 | for (let k = 0; k < conexoes[i][j][1].length; k++) { 184 | $(novaConexao).find(`#param-${k} input`).val(conexoes[i][j][1][k].quantidade); 185 | $(novaConexao).find(`#param-${k} [name="insumo"]`).val(conexoes[i][j][1][k].insumo); 186 | recalculaValor(j, conexoes[i][j][1]); 187 | } 188 | } 189 | } 190 | } 191 | } 192 | // * Função para configurar regras para movimentação do card participante 193 | function customDrag(elemento) { 194 | elemento.find(".draggable").draggable({ 195 | containment: "section", 196 | scroll: false, 197 | snap: false, 198 | stack: ".draggable", 199 | cursor: "grabbing", 200 | handle: ".moverElemento", 201 | stop: function () { // * Executado sempre que o card é solto 202 | let cardId = $(this).closest('.card-container').attr('id'); 203 | let cardNumber = parseInt(cardId.split('-')[1]); 204 | elementosParticipantes[cardNumber].coordenadas = [$(this).offset().left - 48, $(this).offset().top]; 205 | localStorage.setItem("elementosParticipantes", JSON.stringify(elementosParticipantes)); 206 | // console.log('Coordenadas salvas no elemento PARTICIPANTE de ID ' + cardNumber + ' como: ' + elementosParticipantes[cardNumber].coordenadas); 207 | }, 208 | }); 209 | 210 | // > Aumenta a performance porem está apresentando problemas 211 | // elemento.find('.draggable').on('mousedown', function () { 212 | // let id = $(this).closest('.card-container').attr('id'); 213 | // let idNumber = parseInt(id.split('-')[1]); 214 | // let conexoesElements = []; // Array para armazenar os elementos de conexão 215 | // // Preencha o array conexoesElements com os elementos de conexão uma vez 216 | // for (let i = 0; i < TAM; i++) { 217 | // if (conexoes[idNumber][i][0] != null) { 218 | // conexoesElements.push(conexoes[idNumber][i][0]); 219 | // } 220 | // } 221 | // elemento.find(".draggable").on("drag", function () { 222 | // for (let i = 0; i < conexoesElements.length; i++) { 223 | // conexoesElements[i].position(); 224 | // } 225 | // }); 226 | // }); 227 | 228 | elemento.find(".draggable").on("drag", function () { 229 | let id = $(this).closest('.card-container').attr('id'); 230 | let idNumber = parseInt(id.split('-')[1]); 231 | 232 | for (let i = 0; i < TAM; i++) { 233 | if (conexoes[idNumber][i][0] != null) { 234 | conexoes[idNumber][i][0].position(); 235 | } 236 | } 237 | }); 238 | } 239 | // * Função para configurar regras para movimentação do card insumo 240 | function dragInsumos(elemento) { 241 | elemento.find(".draggable").draggable({ 242 | containment: "section", 243 | scroll: false, 244 | snap: false, 245 | stack: ".draggable", 246 | cursor: "grabbing", 247 | handle: ".moverElemento", 248 | stop: function () { // * Executado sempre que o card é solto 249 | let cardId = $(this).closest('.insumos-container').attr('id'); 250 | let cardNumber = parseInt(cardId.split('-')[1]); 251 | elementosInsumos[cardNumber].coordenadas = [$(this).offset().left - 48, $(this).offset().top]; 252 | localStorage.setItem("elementosInsumos", JSON.stringify(elementosInsumos)); 253 | } 254 | }); 255 | elemento.find(".draggable").on("drag", function () { 256 | 257 | // * Pegar a id daquele elemento 258 | let id = $(this).closest('.insumos-container').attr('id'); 259 | let idNumber = parseInt(id.split('-')[1]); 260 | 261 | // * Atualizar cada uma das conexoes 262 | for (let i = 0; i < TAM; i++) { 263 | if (conexoes[i][idNumber][0] != null) { 264 | conexoes[i][idNumber][0].position(); 265 | } 266 | } 267 | 268 | }); 269 | } 270 | // * Função para configurar regras para conexão 271 | function fluxoConecta(element) { 272 | element.find(".fluxoConecta").mousedown(function () { 273 | let id = $(this).attr('id'); 274 | let idNumber = parseInt(id.split('-')[1]); 275 | 276 | const mouseX = event.clientX + pageXOffset; 277 | const mouseY = event.clientY + pageYOffset; 278 | 279 | let elmpoint = $(`
    `); 280 | $('section').append(elmpoint); 281 | 282 | let elmPoint = document.getElementById('elmpoint'); // * Elemento invisisel que acompanha o mouse 283 | let mouseEl = document.getElementById(`fluxoConecta-${idNumber}`); // * Elemento que foi clicado 284 | 285 | // * Ao clicar no botão, a linha deve ser criada 286 | let linhaMouse = new LeaderLine(mouseEl, elmPoint, { 287 | size: 5, 288 | endPlug: 'disc', 289 | startSocket: 'right', 290 | endSocket: 'left', 291 | startPlugColor: '#0bf', 292 | endPlugColor: '#04f', 293 | gradient: true 294 | }); 295 | // * Ao mover o mouse, a linha deve acompanhá-lo 296 | $(document).mousemove((e) => { 297 | elmPoint.style.left = `${e.clientX + pageXOffset}px`; 298 | elmPoint.style.top = `${e.clientY + pageYOffset}px`; 299 | linhaMouse.position(); 300 | }); 301 | // * Ao soltar o botão, a linha deve ser removida 302 | $(document).mouseup(function (event) { 303 | if ($(event.target).is(".fluxoDestinoGenerico")) { 304 | let idDestino = $(event.target).closest('.insumos-container').attr('id'); 305 | let idDestinoNumber = parseInt(idDestino.split('-')[1]); 306 | 307 | if (conexoes[idNumber][idDestinoNumber][0] != null) { // * Toaster 308 | $('.toast-body').text('Este elemento já está conectado a este insumo.'); 309 | $('.toast').toast('show'); 310 | linhaMouse.remove(); 311 | $(document).off('mousemove'); 312 | $(document).off('mouseup'); 313 | return; 314 | } 315 | 316 | let startEl = document.getElementById(`fluxoConecta-${idNumber}`); 317 | let endEl = document.getElementById(`fluxoDestino-${idDestinoNumber}`); 318 | 319 | // * Após a conexão ser criada, é criada também um objeto do tipo Conexão 320 | conexoes[idNumber][idDestinoNumber][0] = new LeaderLine( 321 | startEl, 322 | endEl, 323 | options 324 | ); 325 | // ? Já que a conexão foi criada agora, não existirá outros parametros 326 | conexoes[idNumber][idDestinoNumber][1][0] = new Conexao(idNumber, idDestinoNumber, null, null, null); 327 | 328 | var novaConexao = criaConexao(idNumber, elementosInsumos[idDestinoNumber]); 329 | $(`#listaP-${idNumber}`).find('.card-body').append(novaConexao); 330 | 331 | // * Salvar a conexão no LocalStorage 332 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 333 | } 334 | linhaMouse.remove(); 335 | elmpoint.remove(); 336 | $(document).off('mousemove'); 337 | $(document).off('mouseup'); 338 | }); 339 | }); 340 | } 341 | // * Função que programa o botão insumo | No momento não faz nada 342 | function fluxoDestinoGenerico(element) { 343 | element.find(".fluxoDestinoGenerico").mousedown(function () { 344 | $('.toast-body').text('No momento esse botão não faz nada.'); 345 | $('.toast').toast('show'); 346 | }); 347 | } 348 | // * Cria uma nova funcionalidade em conexão já existente 349 | function adicionaConexao(element) { 350 | element.find('.card-body').on('click', '.adicionaConexao', function () { 351 | let id = $(this).closest('.card-container').attr('id'); 352 | let idNumber = parseInt(id.split('-')[1]); 353 | 354 | let idConexao = $(this).closest('.conexaoParticipante').attr('id'); 355 | let idDestinoNumber = parseInt(idConexao.split('-')[1]); 356 | 357 | if ($(`#listaP-${idNumber}`).find(`#conexaoCom-${idDestinoNumber}`).children().length >= elementosInsumos[idDestinoNumber].linhas.length + 1) { 358 | $('.toast-body').text('O número máximo de parametros é igual ao número de linhas do insumo.'); 359 | $('.toast').toast('show'); 360 | return; 361 | } 362 | 363 | // let novaConexao = criaParametro(idNumber, elementosInsumos[idDestinoNumber]); 364 | // $(`#listaP-${idNumber}`).find('#conexaoCom-' + idDestinoNumber).append(novaConexao); 365 | 366 | // Criarei um novo objeto do tipo Conexao e colocarei no array conexoes 367 | let novaConexaoObjeto = new Conexao(idNumber, idDestinoNumber, null, null, null); 368 | conexoes[idNumber][idDestinoNumber][1].push(novaConexaoObjeto); 369 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 370 | // Quero dar um append 371 | let novaConexao = criaParametro(conexoes[idNumber][idDestinoNumber][1].length - 1, elementosInsumos[idDestinoNumber]); 372 | $(`#listaP-${idNumber}`).find(`#conexaoCom-${idDestinoNumber}`).append(novaConexao); 373 | // location.reload(); // ! Sem tempo 374 | }); 375 | } 376 | // * Exclui uma conexão em específico 377 | function excluiConexao(element) { 378 | element.find('.card-body').on('click', '.excluiConexao', function () { 379 | let id = $(this).closest('.card-container').attr('id'); 380 | let idNumber = parseInt(id.split('-')[1]); 381 | 382 | let idConexao = $(this).closest('.conexaoParticipante').attr('id'); 383 | let idDestinoNumber = parseInt(idConexao.split('-')[1]); 384 | 385 | 386 | 387 | if ($(`#listaP-${idNumber}`).find(`#conexaoCom-${idDestinoNumber}`).children().length <= 2) { 388 | $(this).closest('.conexaoParticipante').remove(); 389 | conexoes[idNumber][idDestinoNumber][0].remove(); 390 | conexoes[idNumber][idDestinoNumber][0] = null; 391 | conexoes[idNumber][idDestinoNumber][1] = []; 392 | 393 | } else { // * Nesse caso a conexão não será excluída, apenas o parametro 394 | $(this).closest('.conexaoParticipante').find('.row:last').remove(); 395 | conexoes[idNumber][idDestinoNumber][1].pop(); 396 | } 397 | 398 | recalculaValor(idDestinoNumber, conexoes[idNumber][idDestinoNumber][1]); // Estou passando um vetor 399 | 400 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 401 | }); 402 | } 403 | // * Função que guarda os valores de cada conexão 404 | function guardaValor(element) { 405 | // * Salva a quantidade 406 | element.find('.card-body').on('change', 'input', function () { 407 | let id = $(this).closest('.card-container').attr('id'); 408 | let idNumber = parseInt(id.split('-')[1]); 409 | 410 | let idConexao = $(this).closest('.conexaoParticipante').attr('id'); 411 | let idDestinoNumber = parseInt(idConexao.split('-')[1]); 412 | 413 | // * Tenho que achar de qual parametro se trata 414 | let k = $(this).closest('.row').attr('id'); 415 | let idParam = parseInt(k.split('-')[1]); 416 | 417 | console.log(`Valor salvo no parametro ${idParam} da ${id} para o insumo ${idDestinoNumber} como: ${$(this).val()}`); 418 | conexoes[idNumber][idDestinoNumber][1][idParam].quantidade = $(this).val(); 419 | 420 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 421 | 422 | recalculaValor(idDestinoNumber, conexoes[idNumber][idDestinoNumber][1]); 423 | }); 424 | // * Salva o insumo selecionado 425 | element.find('.card-body').on('change', '[name="insumo"]', function () { 426 | let id = $(this).closest('.card-container').attr('id'); 427 | let idNumber = parseInt(id.split('-')[1]); 428 | 429 | let idConexao = $(this).closest('.conexaoParticipante').attr('id'); 430 | let idDestinoNumber = parseInt(idConexao.split('-')[1]); 431 | 432 | let k = $(this).closest('.row').attr('id'); 433 | let idParam = parseInt(k.split('-')[1]); 434 | 435 | console.log(`Categoria salva no parametro ${idParam} da ${id} para o insumo ${idDestinoNumber} como: ${$(this).val()}}`); 436 | // * As vezes pegar uma string é diferente de pegar um vetor 437 | let antigo = JSON.parse(JSON.stringify(conexoes[idNumber][idDestinoNumber][1])); 438 | conexoes[idNumber][idDestinoNumber][1][idParam].insumo = $(this).val(); 439 | localStorage.setItem("conexoes", JSON.stringify(conexoes)); 440 | // ? Vou recalcular a quantidade de elementos na string que recebeu o parametro e depois na que perdeu 441 | recalculaValor(idDestinoNumber, conexoes[idNumber][idDestinoNumber][1]); 442 | recalculaValor(idDestinoNumber, antigo); // Essa aqui não está funcionando 443 | }); 444 | } 445 | // * HTML de um elemento participante 446 | function criaElementoParticipante(element, gapping) { 447 | let posicao = ''; 448 | 449 | if (element.coordenadas == null) { 450 | posicao = 'top: ' + (gapping + 1) + '0%'; 451 | } else { 452 | posicao = 'top: ' + element.coordenadas[1] + 'px; left: ' + element.coordenadas[0] + 'px'; 453 | } 454 | 455 | return $(` 456 |
    457 |
    458 |
    459 | 460 | 461 | 463 | ${element.linhas.length} 464 | 465 | 466 | ${element.titulo} 467 | 468 | 469 |
    470 |
    471 |
    472 |
    473 |
    `); 474 | } 475 | // * HTML de um elemento insumo 476 | function criaElementoInsumo(element, gapping) { 477 | let posicao = ''; 478 | if (element.coordenadas == null) { 479 | posicao = 'top: ' + (gapping + 1) + '0%; right: 50%'; 480 | } else { 481 | posicao = 'top: ' + element.coordenadas[1] + 'px; left: ' + element.coordenadas[0] + 'px'; 482 | } 483 | let linha = ''; 484 | if (element.linhas.length > 1 || element.linhas[0].trim() != '') { 485 | for (let i = 0; i < element.linhas.length - 1; i++) { 486 | let linhaAtual = element.linhas[i].substring(0, 30); 487 | linha += ` 488 | 489 | 490 | ${linhaAtual} 491 | `; 492 | } 493 | // ? Úlitma linha 494 | let linhaAtual = element.linhas[element.linhas.length - 1].substring(0, 30); 495 | linha += ` 496 | 497 | 498 | ${linhaAtual} 499 | `; 500 | } else { 501 | linha = 502 | ` 503 | Absoluto 504 | `; 505 | } 506 | return $(` 507 |
    508 |
    509 |
    510 | 512 | 513 | ${element.titulo} 514 | 515 |
    516 |
    517 | ${linha} 518 |
    519 |
    520 |
    `); 521 | } 522 | // * Elemento será uma lista do tipo Insumo 523 | function criaConexao(idP, element) { 524 | let opcoes = ''; 525 | if (element.linhas.length > 1 || element.linhas[0].trim() != '') { 526 | element.linhas.forEach((linha) => { 527 | linha = linha.substring(0, 30); 528 | opcoes += ``; 529 | }); 530 | } 531 | let parametros = ''; 532 | for (let i = 0; i < conexoes[idP][element._id][1].length; i++) { // * Preciso da quantidade de parametros e ID de cada um deles 533 | parametros += ` 534 |
    535 | 536 | 537 | 540 | 541 | 545 |
    `; 546 | } 547 | return $(` 548 |
    549 | 550 | 551 | ${element.titulo} 552 | ${element.titulo} 553 | 554 | 555 | ${parametros} 556 |
    557 | `); 558 | } 559 | // * Função que cria o conteúdo HTML do parametro de uma conexão 560 | function criaParametro(i, element) { 561 | let opcoes = ''; 562 | if (element.linhas.length > 1) { 563 | element.linhas.forEach((linha) => { 564 | linha = linha.substring(0, 30); 565 | opcoes += ``; 566 | }); 567 | } 568 | return ` 569 |
    570 | 571 | 572 | 575 | 576 | 580 |
    `; 581 | } 582 | // * Função para setar os valores da conexão 583 | function recalculaValor(idDestino, insumosAlterado) { // Vai recber o id dos insumos e qual elemento da lista foi alterado 584 | if (elementosInsumos[idDestino].linhas[0].trim() == 0) { return; } 585 | // > Acredito que tenha formas mais simples de fazer isso, revisarei o código caso tenha tempo 586 | console.log("Quantidade de parametros da conexao", insumosAlterado.length); 587 | 588 | let dicionario = {}; 589 | for (let i = 0; i < elementosInsumos[idDestino].linhas.length; i++) { 590 | let linha = elementosInsumos[idDestino].linhas[i]; 591 | linha = linha.substring(0, 30); 592 | dicionario[linha] = 0; 593 | } 594 | 595 | for (let i = 0; i < TAM; i++) { 596 | if (conexoes[i][idDestino][0] != null) { 597 | for (let j = 0; j < conexoes[i][idDestino][1].length; j++) { // Para parametro da conexao 598 | let insumo = conexoes[i][idDestino][1][j].insumo; 599 | let quantidade = conexoes[i][idDestino][1][j].quantidade * elementosParticipantes[i].linhas.length; 600 | dicionario[insumo] += quantidade; 601 | } 602 | } 603 | } 604 | 605 | // > Agora que tenho o dicionario, irei atualizar os valores 606 | for (let i = 0; i < elementosInsumos[idDestino].linhas.length; i++) { 607 | let linha = elementosInsumos[idDestino].linhas[i]; 608 | let quantidade = Math.ceil(dicionario[linha]); 609 | $(`#${idDestino}-${i}`).find('.quantidadeInsumo').text(quantidade); 610 | } 611 | 612 | let dados = "dicionario-" + elementosInsumos[idDestino].titulo; 613 | 614 | // * Preciso salvar no localStorage o dicionario 615 | localStorage.setItem(dados, JSON.stringify(dicionario)); 616 | } 617 | // > Selection estilizado (não finalizado ainda) 618 | // > Para funcionar tem que ser passado por dentro do card 619 | $('.select-icon').click(function () { 620 | $(this).siblings('select').trigger('click'); 621 | }); 622 | //
    623 | // 624 | // 625 | // 628 | // 629 | //
    630 | // 634 | // 635 | //
    636 | //
    --------------------------------------------------------------------------------