Precisa de Suporte?
Tem alguma dúvida, ou precisa de Suporte? Clique aqui para abrir um chamado técnico! É só abrir uma conta ou fazer login na nossa Central de Atendimento. Nosso prazo de atendimento para os chamados técnicos são em média de 24 horas úteis (ou três dias corridos, de seg. à sex.). Não sabe como abrir um chamado? Aqui tem um vídeo mostrando como fazer isso.
Posso enviar mensagens no WhatsAapp?
Claro! Mas note que no WhatsApp as respostas podem demorar um pouco mais dependendo do número de mensagens que tivermos recebido, mas se enviou por lá, é só aguardar que assim que possível você terá um retorno.
1 - 01. Introdução
Esse documento tem como o objetivo demonstrar como foi desenvolvido e como se estrutura as plataformas MagicTree e Kids Rock da Baltimore Education. Que apesar de serem plataformas diferentes, são plataformas irmãs e foram desenvolvidas usando a mesma tecnologia e abordagem.
O objetivo aqui não é servir de base para uma integração com outros sistemas ou plataformas, e sim apenas dar um norte sobre as estratégias adotadas e como tudo funciona. Por causa disso, não vou entrar aqui no detalhe de cada função ou end-point, vou apenas demonstrar como a estrutura geral funciona, e também, me precavendo para não expor estratégias ou regras de negócio senciveis do cliente proprietário da plataforma.
Qualquer dúvida deve ser enviada diretamente para contato@mangu.com.br ou para contato@fabricadeplugins.com.br.
Clique para carregar o vídeo
Clique para carregar o vídeo
2 - 02. Conhecendo a plataforma
A plataforma Kids Rock é um sistema para aprendizagem do idioma de inglês para crianças e adolescentes que são alunos da Baltimore Education. Podemos dividir a plataforma em três frentes:
- A Área do Aluno/Área do professor via navegador e PWA;
- A API de comunicação;
- A Área administrativa Baltimore.
Cada usuário tem acesso a plataforma através de um serial que é gerado quando o usuário compra o livro físico da Baltimore Education, ou se por qualquer motivo, a Baltimore Education fornecer o acesso a ele (também através de um serial).
O serial tem validade de um ano, e após esse período o usuário precisa cadastrar um novo serial para continuar usando a plataforma. Todo o processo de cobrança ou pagamento para recebimento desses seriais, são todos feitos de maneira externa a plataforma, pela própria Baltimore.
3 - 03. Tecnologia e necessidade de atualização
A plataforma foi desenvolvida em 2019/2020 e por tanto, tem padrões de linguagem que a depender do momento em que estiver vendo esse documento, pode estar defasada ou desuatalizada em relação ao que temos de mais moderno para esse tipo de aplicação. Mas temos que ter em mente, as circunstâncias da época e a realidade do cliente no momento do desenvolvimento.
API e Área administrativa:
Desenvolvida em PHP 5.6 puro, com compatibilidade com PHP até a versão 7.4
Área do Aluno/Professor:
HTML5, JavaScript ECMA6 (padrão 2019) e JQuery
4 - 04. Comunicação entre API REST e Área do Aluno/Professor
Toda a comunicação entre a Área do Aluno/Professor e o backend é feita através da API REST da plataforma. O core da aplicação pode ser encontrado em js/scripts.js e todos os endpoints podem ser consultados dentro do diretório da API.
No exemplo abaixo uma demonstração da tela de "Suporte e Ajuda" onde após a captura dos dados, a mensagem é disparada para os setores responsáveis. Todas as chamadas dentro da plataforma ocorrem de forma semelhante:
// FUNÇÃO PARA PROCESSAR GET HELP | |
function procHelp(){ | |
$("#btnGetHelp").html("PROCESSANDO..."); | |
console.log("INICIANDO FUNÇÃO PARA ATUALIZAR DADOS 'GET HELP'"); | |
var idUsuario = localStorage.getItem("idUsuario"); | |
// RECUPERAR DADOS DO FORMULÁRIO GET HELP | |
var helpName = $("#helpName").val(); | |
var helpEmail = $("#helpEmail").val(); | |
var helpAssunto = $("#helpAssunto").val(); | |
var helpDepartamento = $("#helpDepartamento").val(); | |
var helpDescricao = $("#helpDescricao").val(); | |
if(helpName!="" && helpEmail!="" && helpAssunto!="" && helpDepartamento!="" && helpDescricao!=""){ | |
// INICIO CHAMADA AJAX | |
var request = $.ajax({ | |
method: "POST", | |
url: urlApi+"get-help.php", | |
data:{idUsuario:idUsuario,helpName:helpName,helpEmail:helpEmail,helpAssunto:helpAssunto,helpDepartamento:helpDepartamento,helpDescricao:helpDescricao} | |
}) | |
request.done(function (dados) { | |
if (dados["sucesso"]=="200") { | |
$("#btnGetHelp").html("Send"); | |
mensagem("Mensagem enviada com sucesso! Em breve retornaremos o seu contato."); | |
}else{ | |
console.log("PROBLEMAS NO ENVIO DA MENSAGEM (procHelp)"); | |
console.log(dados); | |
// LIMPANDO OS CAMPOS DE LOGIN E SENHA | |
mensagem("Não conseguimos comunicação com o servidor, tente novamente dentro de alguns instantes."); | |
} | |
}); | |
request.fail(function (dados) { | |
console.log("PROBLEMAS NO ENVIO DA MENSAGEM (procHelp)"); | |
mensagem("Não conseguimos comunicação com o servidor, tente novamente dentro de alguns instantes."); | |
}); | |
// FINAL CHAMADA AJAX | |
}else{ | |
mensagem("Todos os campos são necessários"); | |
} | |
console.log("FUNÇÃO PARA ATUALIZAR DADOS 'GET HELP' FINALIZADA COM SUCESSO'"); | |
} |
Os dados são recebidos pela API que processa as informações e realiza os procedimentos necessários:
<?php | |
// PERMITIR O ACESSO DE QUALQUER ORIGEM | |
//header('Access-Control-Allow-Origin: https://127.0.0.1'); | |
header('Access-Control-Allow-Origin: https://www.baltimoreeducation.com.br'); | |
header('Access-Control-Allow-Methods: GET, POST, PUT'); | |
#define o encoding do cabeçalho para utf-8 | |
header('Content-Type: application/json'); | |
// CONEXÃO COM O BANCO DE DADOS | |
require("conexao.php"); | |
// FUNÇÕES BÁSICAS | |
require("funcoes.php"); | |
$idUsuario = $_POST["idUsuario"]; | |
$helpName = $_POST["helpName"]; | |
$helpEmail = $_POST["helpEmail"]; | |
$helpAssunto = $_POST["helpAssunto"]; | |
$helpDepartamento = $_POST["helpDepartamento"]; | |
$helpDescricao = $_POST["helpDescricao"]; | |
// RECUPERAR DADOS DO USUÁRIO | |
$sql = "SELECT * FROM usuarios WHERE id = '$idUsuario'"; | |
$result = $PDO->query( $sql ); | |
$usuario = $result->fetchAll( PDO::FETCH_ASSOC ); | |
$id = $usuario[0]["id"]; | |
$email = $usuario[0]["email"]; | |
$html = ""; | |
$html = $html."<br><b>Usuário:</b> #".$id; | |
$html = $html."<br><b>E-mail cadastro:</b> ".$email; | |
$html = $html."<br><b>Nome:</b> ".$helpName; | |
$html = $html."<br><b>E-mail informado:</b> ".$helpEmail; | |
$html = $html."<br><b>Assunto:</b> ".$helpAssunto; | |
$html = $html."<br><b>Departamento:</b> ".$helpDepartamento; | |
$html = $html."<br><b>Descrição:</b> ".$helpDescricao; | |
$html = $html."<br><br>Esses dados vieram do formulário de contato KidsRock"; | |
//contato@altidiomas.com | |
enviarEmail("atendimento@baltimoreeducation.com.br","Solicitação de Suporte KidsRock: ".$helpAssunto,"O usuário ".$helpName." solicitou suporte e esses foram os dados:<br>".$html); | |
$data = array('sucesso' => "200"); | |
$json_string = json_encode($data, JSON_PRETTY_PRINT); | |
echo $json_string; |
5 - 05. Obtenção de conteúdo e reaproveitamento de memória
Como o conteúdo é relativamente extenso, incluindo diversas imagens, textos, arquivos de aúdios etc. Realizamos o download de todo o material uma única vez, o salvamos na memória, e ai reaproveitamos o mesmo sempre que for necessário para economizar recursos e permitir inclusive que a plataforma continue funcionando mesmo sem conexão com a internet (uma vez que os arquivos já tenham sido baixados):
// FUNÇÃO PARA CARREGAR CONTEÚDO | |
var conteudo; | |
var paginas; | |
function carregarConteudo(){ | |
console.log("INICIANDO FUNÇÃO PARA CARREGAR TODO DO CONTEÚDO"); | |
// INICIO CHAMADA AJAX | |
var request = $.ajax({ | |
method: "POST", | |
url: urlApi+"carregar-conteudo.php" | |
}) | |
request.done(function (dados) { | |
console.log("%c CONTEÚDO BAIXADO:","background:#ff0000;color:#fff;font-size:20px;"); | |
//console.log(dados); | |
//console.log("HORA+MINUTOS+5: "+ horaAtualMinutos()); | |
//console.log("TIMESTAMP ATUAL +1 MIN:"); | |
//horaAtualMinutos("01"); | |
conteudo = dados; | |
console.log(conteudo); | |
//console.table(conteudo.livros); | |
//console.table(conteudo.unidades); | |
//console.table(conteudo.paginas); | |
// ALIMENTAR DADOS GAMEFICAÇÃO | |
console.log("%c ALIMENTANDO DADOS GAMEFICAÇÃO:","background:#AD1457;color:#fff;font-size:20px;"); | |
$(".progress-bar").html("<small>"+localStorage.getItem("pontosUsuario")+" pontos</small>"); | |
var nivel = 0; | |
if(localStorage.getItem("pontosUsuario")>0){ | |
nivel = localStorage.getItem("pontosUsuario") / 10; | |
nivel = parseInt(nivel); | |
$("#nivelAtualUsuario").html(nivel); | |
$("#nivelAtualUsuarioMobile").html(nivel); | |
} | |
// VERIFICAR SE EXISTEM REDIRECTS SALVOS PARA SEREM FEITOS | |
var needRedirect = localStorage.getItem("resetWindow"); | |
if(needRedirect=="sim"){ | |
var resetWindowView = localStorage.getItem("resetWindowView"); | |
//$JSView.goToView('viewComunidade'); | |
loadComunidade(); | |
mensagem("Dados salvos com sucesso!"); | |
// RESETAR A MEMÓRIA RESPONSAVEL PELOS REDIRECTS | |
localStorage.setItem("resetWindow","nao"); | |
} | |
}); | |
request.fail(function (dados) { | |
console.log("PROBLEMAS AO BAIXAR CONTEÚDO (carregarConteudo)"); | |
mensagem("Não conseguimos comunicação com o servidor, tente novamente dentro de alguns instantes."); | |
}); | |
// FINAL CHAMADA AJAX | |
} | |
Na API o processo é semelhante, já realizando todas as querys de uma única vez na autenticação do usuário:
<?php | |
// PERMITIR O ACESSO DE QUALQUER ORIGEM | |
//header('Access-Control-Allow-Origin: https://127.0.0.1'); | |
header('Access-Control-Allow-Origin: https://www.baltimoreeducation.com.br'); | |
header('Access-Control-Allow-Methods: GET, POST, PUT'); | |
#define o encoding do cabeçalho para utf-8 | |
header('Content-Type: application/json'); | |
// CONEXÃO COM O BANCO DE DADOS | |
require("conexao.php"); | |
// FUNÇÕES BÁSICAS | |
require("funcoes.php"); | |
// OBTER LIVROS | |
$sql = "SELECT * FROM livros ORDER BY id ASC"; | |
$result = $PDO->query( $sql ); | |
$livros = $result->fetchAll( PDO::FETCH_ASSOC ); | |
$tot_livros = count($livros); | |
// OBTER UNIDADES | |
$sql = "SELECT * FROM unidades ORDER BY id_livro, nome ASC"; | |
$result = $PDO->query( $sql ); | |
$unidades = $result->fetchAll( PDO::FETCH_ASSOC ); | |
$tot_unidades = count($unidades); | |
// OBTER PÁGINAS | |
$sql = "SELECT * FROM paginas ORDER BY id_livro, ordem, id ASC"; | |
$result = $PDO->query( $sql ); | |
$paginas = $result->fetchAll( PDO::FETCH_ASSOC ); | |
$tot_paginas = count($paginas); | |
$data = array('sucesso' => "200", | |
'livros' => $livros, | |
'tot_livros' => $tot_livros, | |
'unidades' => $unidades, | |
'tot_unidades' => $tot_unidades, | |
'paginas' => $paginas, | |
'tot_paginas' => $tot_paginas); | |
$json_string = json_encode($data, JSON_PRETTY_PRINT); | |
echo $json_string; | |
?> |
Um exemplo desse reaproveitamento de conteúdo, quando o aluno acesso um livro, apenas acessamos o mesmo da memória:
// CARREGAR OS LIVROS | |
function loadBooks(){ | |
$('#sound1').get(0).pause(); | |
$('#sound2').get(0).play(); | |
desligarAllMusicas(); | |
// FECHAR AUDIO PLAYER | |
fecharPlayerAudioLivros(); | |
$(".pagina-01 .container.tada").hide(); | |
$(".pagina-01 .container.caixa-conteudo").fadeIn("1900000"); | |
$(".pagina-01 .caixa-conteudo").css("min-height","800px"); | |
// ATIVAR O MENU LIVROS | |
$(".menu-pai").removeClass("menu-ativo"); | |
$("#menuBooks").addClass("menu-ativo"); | |
$("#menuBooksMobile").addClass("menu-ativo"); | |
$JSView.goToView('viewBooks'); | |
verificarSeriaisAcesso(); | |
var i = 0; | |
// SB (VAMOS IMPRIMIR O S NA FRENTE DO CÓDIGO DO LIVRO) | |
console.log("INICIANDO A IMPRESSÃO DOS LIVROS (PRIMEIRA LINHA)"); | |
for(i=0;i<conteudo.tot_livros;i++){ | |
if(conteudo.livros[i]["id"]!="1"){ | |
$("#listLivros").append(` | |
<div class="col-lg-3 col-md-3 col-sm-6 col-xs-6 livro SBKR${conteudo.livros[i]["cod_livro"]}"> | |
<a href="javascript:void()" title="${conteudo.livros[i]["nome"]}"> | |
${conteudo.livros[i]["nome"]} STUDENT'S BOOK | |
</a> | |
<div class="caixa-livro" style="background:url(${urlCnd+conteudo.livros[i]["capa"].replace(" - ","%20-%20")}) no-repeat;background-size:cover;background-position:center center;"> | |
<div class="bloqueio-livro" onclick="procAddSerial();"> </div> <a href="javascript:void()" title="${conteudo.livros[i]["nome"]}" onclick="$JSView.goToView(\'viewBooksUnits\'); nomeLivro( \`${conteudo.livros[i]["nome"]} STUDENT'S BOOK\`); loadUnidadesLivro(${conteudo.livros[i]["id"]},\'SB\',\'${conteudo.livros[i]["cod_livro"]}\');"> </a> | |
</div></div>`); | |
} | |
} | |
// AB (VAMOS IMPRIMIR O A NA FRENTE DO CÓDIGO DO LIVRO) | |
console.log("INICIANDO A IMPRESSÃO DOS LIVROS (SEGUNDA LINHA)"); | |
for(i=0;i<conteudo.tot_livros;i++){ | |
if(conteudo.livros[i]["id"]!="1"){ | |
$("#listLivros").append('<div class="col-lg-3 col-md-3 col-sm-6 col-xs-6 livro teste-d ABKR'+conteudo.livros[i]["cod_livro"]+'"><a href="javascript:void()" title="'+conteudo.livros[i]["nome"]+'" > '+conteudo.livros[i]["nome"]+' WORKBOOK</a><div class="caixa-livro" style="background:url('+urlCnd+conteudo.livros[i]["capa_ab"].replace(" - ","%20-%20")+') no-repeat;background-size:cover;background-position:center center;"> <div class="bloqueio-livro" onclick="procAddSerial();"> </div> <a href="javascript:void()" title="'+conteudo.livros[i]["nome"]+'" onclick="$JSView.goToView(\'viewBooksUnits\'); nomeLivro(\''+conteudo.livros[i]["nome"]+' WORKBOOK\'); loadUnidadesLivro('+conteudo.livros[i]["id"]+',\'AB\',\''+conteudo.livros[i]["cod_livro"]+'\');"> </a></div></div>'); | |
} | |
} | |
console.log("SERIAIS ATIVOS DO USUÁRIO"); | |
console.log(listaSeriais); | |
} |
6 - 06. Games
Cada um dos games tem sua própria mecânica e regras, porém existe uma semelhança entre todos eles: sempre uma função que as monta, as processa (resultado) e as finalizada (contabilizando pontos, avançando níveis etc):
Por exemplo, se analisarmos o Memory Game (jogo da memória):
View:
<jsv-content> | |
<!-- PAGE CONTENT FIM --> | |
<div class="page-content"> | |
<div class="titulo-view"> | |
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-3 data-arrow"> | |
<a href="javascript:void(0)" onclick="$JSView.goToView('viewGames'); desligarAllMusicas();"> | |
<img src="images/down-arrow.png" alt="Voltar"> | |
</a> | |
</div> | |
<div class="col-lg-8 col-md-8 col-sm-6 col-xs-6 text-center"> | |
<h1 class="mobile-ajuste-margin">Memory Game</h1> | |
</div> | |
<div class="col-lg-2 col-md-2 col-sm-3 col-xs-3"> </div> | |
<br clear="both" /> | |
</div> | |
<br clear="both" /> | |
<!-- AREA DE EDIÇÃO --> | |
<div class="memory-game"> | |
<div class="col-lg-10 col-md-10 col-lg-offset-1 col-md-offset-1 col-sm-12 col-xs-12 text-center"> | |
<div class="bloquear-memory-game"></div> | |
<div class="game"></div> | |
</div> | |
</div> | |
<!-- AREA DE EDIÇÃO --> | |
<br clear="both"> | |
<br clear="both"> | |
<br clear="both"> | |
<br clear="both"> | |
<br clear="both"> | |
</div> | |
<!-- PAGE CONTENT FIM --> | |
</jsv-content> |
Funções JS:
// AUMENTAR PONTOS PARA GAMEFICAÇÃO | |
function gameficacao(){ | |
var idUsuario = localStorage.getItem("idUsuario"); | |
// INICIO CHAMADA AJAX | |
var request = $.ajax({ | |
method: "POST", | |
url: urlApi+"gameficacao.php", | |
data:{idUsuario:idUsuario} | |
}) | |
request.done(function (dados) { | |
// ALIMENTAR DADOS GAMEFICAÇÃO | |
console.log("%c ALIMENTANDO DADOS GAMEFICAÇÃO:","background:#AD1457;color:#fff;font-size:20px;"); | |
$(".progress-bar").html("<small>"+dados.pontos+" pontos</small>"); | |
var nivel = 0; | |
if(dados.pontos>0){ | |
nivel = dados.pontos / 10; | |
nivel = parseInt(nivel); | |
$("#nivelAtualUsuario").html(nivel); | |
$("#nivelAtualUsuarioMobile").html(nivel); | |
} | |
mensagem("<i class=\"fa fa-trophy fa-2x diogenes-pulse\" aria-hidden=\"true\" style=\"color: #FDD835;\"></i><br>Você ganhou 1 ponto por ter vencido o desafio do game!"); | |
localStorage.setItem("pontosUsuario",dados.pontos); | |
}); | |
request.fail(function (dados) { | |
console.log("PROBLEMAS AO AUMENTAR PONTOS USUÁRIO GAMEFICAÇÃO (gameficacao)"); | |
}); | |
// FINAL CHAMADA AJAX | |
clearInterval(mkStart); | |
$("#cronometro").fadeOut("500"); | |
} | |
function loadMemoryGame(){ | |
$('#sound1').get(0).pause(); | |
//$('#sound2').get(0).play(); | |
desligarAllMusicas(); | |
playMusicBackground("#sound5"); | |
$('#sound5').prop("volume", 0.12); // DIMINUIR O AUDIO DA MÚSICA DE FUNDO DO MEMOERY GAME | |
$(".pagina-01 .container.tada").hide(); | |
$(".pagina-01 .container.caixa-conteudo").fadeIn("1900000"); | |
$JSView.goToView('viewMemoryGame'); | |
memoryGame(); | |
// ATIVAR O MENU GAMES (ESTA REDUNDANTE) | |
$(".menu-pai").removeClass("menu-ativo"); | |
$("#menuGames").addClass("menu-ativo"); | |
} |
var nomeCarta = []; | |
var imgCarta = []; | |
var idCarta = []; | |
var audioCarta = []; | |
function memoryGame(){ | |
console.log("%c STORE CRONOMETRO:","background:#fff000;color:#000;"); | |
horaAtualMinutos("memoryGame"); | |
// Memory Game | |
// © 2014 Nate Wiley | |
// License -- MIT | |
// best in full screen, works on phones/tablets (min height for game is 500px..) enjoy ;) | |
// Follow me on Codepen | |
(function(){ | |
var Memory = { | |
init: function(cards){ | |
this.$game = $(".game"); | |
this.$modal = $(".modal"); | |
this.$overlay = $(".modal-overlay"); | |
this.$restartButton = $("button.restart"); | |
this.cardsArray = $.merge(cards, cards); | |
this.shuffleCards(this.cardsArray); | |
this.setup(); | |
}, | |
shuffleCards: function(cardsArray){ | |
this.$cards = $(this.shuffle(this.cardsArray)); | |
}, | |
setup: function(){ | |
this.html = this.buildHTML(); | |
this.$game.html(this.html); | |
this.$memoryCards = $(".card"); | |
this.paused = false; | |
this.guess = null; | |
this.binding(); | |
}, | |
binding: function(){ | |
this.$memoryCards.on("click", this.cardClicked); | |
this.$restartButton.on("click", $.proxy(this.reset, this)); | |
}, | |
// kinda messy but hey | |
cardClicked: function(){ | |
var _ = Memory; | |
var $card = $(this); | |
if(!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside").hasClass("picked")){ | |
$card.find(".inside").addClass("picked"); | |
if(!_.guess){ | |
_.guess = $(this).attr("data-id"); | |
} else if(_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")){ | |
$(".picked").addClass("matched"); | |
setTimeout(function(){ | |
// DESATIVAMOS O AUDIO DO ACERTO | |
//playMusic("sounds/goodjob.mp3"); // USUÁRIO ACERTOU | |
}, 550); | |
_.guess = null; | |
} else { | |
_.guess = null; | |
_.paused = true; | |
setTimeout(function(){ | |
$(".picked").removeClass("picked"); | |
// DESATIVAMOS O AUDIO DO ERRO | |
//playMusic("sounds/try_again.mp3"); // USUARIO ERROU | |
Memory.paused = false; | |
}, 550); | |
} | |
if($(".matched").length == $(".card").length){ | |
_.win(); | |
} | |
} | |
}, | |
win: function(){ // USUAIRIO VENCEU | |
this.paused = true; | |
setTimeout(function(){ | |
pauseMusic("#sound5"); | |
playMusicBackground("#sound6"); | |
playMusicBackground("#sound7"); | |
$(".game-vitoria-game").fadeIn("50"); | |
gameficacao(); | |
Memory.showModal(); | |
//Memory.$game.fadeOut(); | |
}, 1000); | |
}, | |
showModal: function(){ | |
this.$overlay.show(); | |
this.$modal.fadeIn("slow"); | |
}, | |
hideModal: function(){ | |
this.$overlay.hide(); | |
this.$modal.hide(); | |
}, | |
reset: function(){ | |
this.hideModal(); | |
this.shuffleCards(this.cardsArray); | |
this.setup(); | |
this.$game.show("slow"); | |
pauseMusic("#sound5"); | |
}, | |
// Fisher--Yates Algorithm -- https://bost.ocks.org/mike/shuffle/ | |
shuffle: function(array){ | |
var counter = array.length, temp, index; | |
// While there are elements in the array | |
while (counter > 0) { | |
// Pick a random index | |
index = Math.floor(Math.random() * counter); | |
// Decrease counter by 1 | |
counter--; | |
// And swap the last element with it | |
temp = array[counter]; | |
array[counter] = array[index]; | |
array[index] = temp; | |
} | |
return array; | |
}, | |
buildHTML: function(){ | |
var frag = ''; | |
this.$cards.each(function(k, v){ | |
frag += '<div class="card" data-id="'+ v.id +'"><div class="inside">\ | |
<div class="front"><img src="'+ v.img +'"\ | |
alt="'+ v.name +'" /></div>\ | |
<div class="back"><img src="images/costas.svg"\ | |
alt="MagicTree" /></div></div>\ | |
</div>'; | |
}); | |
return frag; | |
} | |
}; | |
// CARREGAR AS CARTAS DO MEMORY GAME | |
var controleCartas = 0; | |
var controleIndex = 0; | |
for(var i = 0;i<conteudoGames.length;i++){ | |
if(conteudoGames[i]["tipo"]=="memory_game" && conteudoGames[i]["id_unidade"] == primeiraUnidade){ | |
nomeCarta[controleIndex] = "carta"+controleIndex; | |
imgCarta[controleIndex] = urlCnd+conteudoGames[i]["link"]; | |
audioCarta[controleIndex] = urlCnd+conteudoGames[i]["audio"]; | |
idCarta[controleIndex] = controleIndex; | |
controleIndex++; | |
controleCartas++; | |
} | |
} | |
// FINAL DO FOR | |
console.log("TOTAL LENGHT MEMORY GAME: "+controleCartas); | |
if(controleCartas<=4){ | |
var cards = [ | |
{ | |
name: nomeCarta[0], | |
img: imgCarta[0], | |
id: idCarta[0] | |
}, | |
{ | |
name: nomeCarta[1], | |
img: imgCarta[1], | |
id: idCarta[1] | |
}, | |
{ | |
name: nomeCarta[2], | |
img: imgCarta[2], | |
id: idCarta[2] | |
}, | |
{ | |
name: nomeCarta[3], | |
img: imgCarta[3], | |
id: idCarta[3] | |
} | |
]; | |
} | |
if(controleCartas==6){ | |
var cards = [ | |
{ | |
name: nomeCarta[0], | |
img: imgCarta[0], | |
id: idCarta[0] | |
}, | |
{ | |
name: nomeCarta[1], | |
img: imgCarta[1], | |
id: idCarta[1] | |
}, | |
{ | |
name: nomeCarta[2], | |
img: imgCarta[2], | |
id: idCarta[2] | |
}, | |
{ | |
name: nomeCarta[3], | |
img: imgCarta[3], | |
id: idCarta[3] | |
}, | |
{ | |
name: nomeCarta[4], | |
img: imgCarta[4], | |
id: idCarta[4] | |
}, | |
{ | |
name: nomeCarta[5], | |
img: imgCarta[5], | |
id: idCarta[5] | |
} | |
]; | |
} | |
if(controleCartas==7){ | |
var cards = [ | |
{ | |
name: nomeCarta[0], | |
img: imgCarta[0], | |
id: idCarta[0] | |
}, | |
{ | |
name: nomeCarta[1], | |
img: imgCarta[1], | |
id: idCarta[1] | |
}, | |
{ | |
name: nomeCarta[2], | |
img: imgCarta[2], | |
id: idCarta[2] | |
}, | |
{ | |
name: nomeCarta[3], | |
img: imgCarta[3], | |
id: idCarta[3] | |
}, | |
{ | |
name: nomeCarta[4], | |
img: imgCarta[4], | |
id: idCarta[4] | |
}, | |
{ | |
name: nomeCarta[5], | |
img: imgCarta[5], | |
id: idCarta[5] | |
}, | |
{ | |
name: nomeCarta[6], | |
img: imgCarta[6], | |
id: idCarta[6] | |
} | |
]; | |
} | |
if(controleCartas==8){ | |
var cards = [ | |
{ | |
name: nomeCarta[0], | |
img: imgCarta[0], | |
id: idCarta[0] | |
}, | |
{ | |
name: nomeCarta[1], | |
img: imgCarta[1], | |
id: idCarta[1] | |
}, | |
{ | |
name: nomeCarta[2], | |
img: imgCarta[2], | |
id: idCarta[2] | |
}, | |
{ | |
name: nomeCarta[3], | |
img: imgCarta[3], | |
id: idCarta[3] | |
}, | |
{ | |
name: nomeCarta[4], | |
img: imgCarta[4], | |
id: idCarta[4] | |
}, | |
{ | |
name: nomeCarta[5], | |
img: imgCarta[5], | |
id: idCarta[5] | |
}, | |
{ | |
name: nomeCarta[6], | |
img: imgCarta[6], | |
id: idCarta[6] | |
}, | |
{ | |
name: nomeCarta[7], | |
img: imgCarta[7], | |
id: idCarta[7] | |
} | |
]; | |
} | |
//console.log("O QUE EU SOU"); | |
//console.log(cards); | |
Memory.init(cards); | |
})(); | |
} | |
// AO CLICAR EM UMA CARTA DO MEMORY GAME, VAMOS TOCAR O SOM CORRESPONDENTE | |
$('body').on('click', 'div.card', function() { | |
var urlMusica = $(this).attr("data-id"); | |
console.log("CARTA CLICADA FOI A: "+urlMusica); | |
// E AI DE ACORDO COM A MÚSICA TOCAMOS O SOM | |
if(urlMusica==0){ | |
//$("#sound8").attr("src",audioCarta[0]); | |
playMusic(audioCarta[0]); | |
} | |
if(urlMusica==1){ | |
//$("#sound9").attr("src",audioCarta[1]); | |
playMusic(audioCarta[1]); | |
} | |
if(urlMusica==2){ | |
//$("#sound10").attr("src",audioCarta[2]); | |
playMusic(audioCarta[2]); | |
} | |
if(urlMusica==3){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[3]); | |
} | |
if(urlMusica==4){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[4]); | |
} | |
if(urlMusica==5){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[5]); | |
} | |
if(urlMusica==6){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[6]); | |
} | |
if(urlMusica==7){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[7]); | |
} | |
if(urlMusica==8){ | |
//$("#sound11").attr("src",audioCarta[3]); | |
playMusic(audioCarta[8]); | |
} | |
}); | |
7 - 07. PWA
Como vimos anteriormente, reaproveitamos bastante a memória já salva, o que facilita para a execução do PWA, que está presente no diretório "/instalador". Criamos o service worker e pronto:
function installApp() { | |
// Show the prompt | |
deferredPrompt.prompt(); | |
setupButton.disabled = true; | |
// Wait for the user to respond to the prompt | |
deferredPrompt.userChoice | |
.then((choiceResult) => { | |
if (choiceResult.outcome === 'accepted') { | |
console.log('PWA setup accepted'); | |
// hide our user interface that shows our A2HS button | |
setupButton.style.display = 'none'; | |
$("#naoInstalado").hide(0); | |
$("#instalado").fadeIn("500"); | |
} else { | |
console.log('PWA setup rejected'); | |
$("#naoInstalado").hide(0); | |
$("#instalado").fadeIn("500"); | |
} | |
deferredPrompt = null; | |
}); | |
} |
var cacheName = 'kidsrock'; | |
var cached_urls = [ | |
'/kidsrock/nstalador/', | |
'/kidsrock/instalador/index.html', | |
'/kidsrock/instalador/cadastro.html', | |
'/kidsrock/instalador/esqueci-minha-senha.html', | |
'/kidsrock/instalador/serial.html', | |
'/kidsrock/instalador/welcome.html', | |
'/kidsrock/instalador/welcome-professor.html', | |
'/kidsrock/instalador/css/style.css', | |
'/kidsrock/instalador/css/bootstrap.css', | |
//'/instalador/css/', | |
//'/instalador/js/', | |
'/kidsrock/instalador/js/scripts.js', | |
//'/instalador/images/', | |
//'/instalador/sounds/', | |
//'/instalador/arquivos/' | |
]; | |
/* Start the service worker and cache all of the app's content */ | |
self.addEventListener('install', function(e) { | |
e.waitUntil( | |
caches.open(cacheName).then(function(cache) { | |
return cache.addAll(cached_urls); | |
}) | |
); | |
}); | |
/* Serve cached content when offline */ | |
self.addEventListener('fetch', function(e) { | |
e.respondWith( | |
caches.match(e.request).then(function(response) { | |
return response || fetch(e.request); | |
}) | |
); | |
}); |
8 - 08. Área administrativa (Backend)
Não há muito o que se descrever sobre essa área, a não ser que, todo o acesso ao banco de dados é feito de forma independente, ou seja, a API é usada apenas para a comunicação entre a Área do Aluno/Professor e o banco. A Área administrativa à acessa de forma direta, e todo o fluxo pode ser percebido pela própria organização do diretório.
O mesmo vale para os arquivos de mídia, que são carregados dentro do diretório "/cdn", universalizando o acesso seja pela Área administrativa, seja pela Área do Aluno/Professor.
9 - 09. Implementações futuras
Alguns itens seriam interessante serem implementados futuramente em eventuais novas versões da plataforma:
- Atualização para a versão do PHP 8.0+;
- Atualização do padrão JavaScript para ECMA6 (2021), removendo e substituíndo todas as chamadas JQuery;
- Criação da versão 2 da API, para um padrão MVC, assim como a Área administrativa e a API V2 ser o único canal para acesso ao banco de dados.
- Implementação de tokenização JWT para OAuth dos usuários;
10 - 10. Conclusão
Alguns links de apoio sobre o desenvolvimento da plataforma:
Qualquer dúvida deve ser enviada diretamente para contato@mangu.com.br ou para contato@fabricadeplugins.com.br.
Precisa de Suporte?
Tem alguma dúvida, ou precisa de Suporte? Clique aqui para abrir um chamado técnico! É só abrir uma conta ou fazer login na nossa Central de Atendimento. Nosso prazo de atendimento para os chamados técnicos são em média de 24 horas úteis (ou três dias corridos, de seg. à sex.). Não sabe como abrir um chamado? Aqui tem um vídeo mostrando como fazer isso.
Posso enviar mensagens no WhatsAapp?
Claro! Mas note que no WhatsApp as respostas podem demorar um pouco mais dependendo do número de mensagens que tivermos recebido, mas se enviou por lá, é só aguardar que assim que possível você terá um retorno.