diff --git a/app/src/pages/HomePage/HomePage.jsx b/app/src/pages/HomePage/HomePage.jsx index 1127c38..fa58b3d 100644 --- a/app/src/pages/HomePage/HomePage.jsx +++ b/app/src/pages/HomePage/HomePage.jsx @@ -4,6 +4,8 @@ * @module pages.HomePage.HomePage */ +import { useEffect } from "react"; +import capicodaUrl from "@/vendor/capicoda/capicoda.js?url"; import Navbar from "../../components/Navbar"; import PropTypes from "prop-types"; import Hero from "./Hero"; @@ -16,6 +18,21 @@ import StudentsMaterials from "./StudentsMaterials"; import TeachersMaterials from "./TeachersMaterials"; const HomePage = () => { + // Mascote Capicoda: injetado apenas na página inicial. O widget é um script + // autocontido que se anexa ao body; na saída da home removemos o script e o + // nó raiz (#dcs-root) para não vazar para outras rotas. + useEffect(() => { + const script = document.createElement("script"); + script.src = capicodaUrl; + script.defer = true; + document.body.appendChild(script); + return () => { + script.remove(); + const root = document.getElementById("dcs-root"); + if (root) root.remove(); + }; + }, []); + return (
{/* Navegação */} diff --git a/app/src/vendor/capicoda/README.md b/app/src/vendor/capicoda/README.md new file mode 100644 index 0000000..0fe6c4b --- /dev/null +++ b/app/src/vendor/capicoda/README.md @@ -0,0 +1,102 @@ +# Capicoda 🚩🦫 + +A **capivara comunista** do Núcleo de Tecnologia do MTST. Um widget de chat que +aparece na página do Núcleo, conversa com quem visita, identifica se a pessoa é +desenvolvedora e a direciona para **contribuir com o [DECODA](https://git.mtst.tec.br/educacao/decoda/)**. + +Projeto do hackathon do Núcleo de Tecnologia do MTST. + +--- + +## ✨ O que é + +- Balão de chat no canto inferior direito de qualquer site. +- Conversa por **roteiro fixo** (botões) — sem IA, sem backend, sem build. +- Faz a triagem do visitante (**dev / aprendiz / curiosx**) e entrega o caminho + certo: repositório, como rodar localmente, como abrir um PR, ou como ajudar + sem programar. +- Um único arquivo: **`capicoda.js`** (CSS, SVG da capivara e diálogo embutidos). + +--- + +## 🚀 Rodar o demo + +Não precisa instalar nada. Na pasta do projeto: + +```bash +python3 -m http.server 8000 +``` + +Abra **http://localhost:8000/demo.html** e clique no balão da capivara. + +> Também dá pra só abrir o `demo.html` direto no navegador (duplo clique). + +--- + +## 🔌 Embutir em outro site + +Copie `capicoda.js` para o site e adicione **uma linha** antes do ``: + +```html + +``` + +Pronto — o balão se injeta sozinho. Não conflita com o CSS da página (tudo é +prefixado com `dcs-` e isolado num `#dcs-root`). + +--- + +## 💬 Editar a conversa + +Toda a conversa vive no objeto **`TREE`** no topo do `capicoda.js`. Cada nó: + +```js +nome_do_no: { + msg: "Texto do balão", // ou um array de balões em sequência + options: [ + { label: "Botão A", next: "outro_no" }, // navega para outro nó + { label: "Abrir repo", url: URLS.repo }, // abre um link em nova aba + { label: "Destaque", next: "x", primary: true } // botão em vermelho cheio + ] +} +``` + +Os links reais ficam no objeto `URLS` (repo, página do DECODA, página do Núcleo). + +### Mapa do roteiro + +``` +start ──┬─ Bora! ───────────────► qualifica + └─ O que é o DECODA? ───► oque_decoda ──► qualifica + +qualifica ─┬─ Sou dev ──────► area ─┬─ Front/JS ──► cta_front + │ ├─ Back/DevOps ► cta_infra + │ └─ Full/outra ─► cta_geral + ├─ Tô aprendendo ► aprendiz + └─ Só curiosx ───► curioso + +cta_* ─┬─ Ver repositório (link) + ├─ Como rodar localmente ► setup + ├─ Como mandar um PR ─────► fluxo_pr + └─ Voltar ao início ──────► start +``` + +--- + +## 🎨 Identidade + +Capivara marrom de boina vermelha com estrela dourada (SVG inline). Paleta MTST +(vermelho `#c1121f`). Tom acolhedor e militante-bem-humorado. + +--- + +## 🔮 Próximos passos (fora do escopo do hackathon) + +- Modo IA opcional (Claude API) para conversa livre. +- Captura de contato de quem quer contribuir (onboarding → mutirão). +- Puxar "good first issues" dinamicamente da aba de Issues do repositório. +- Integração na página real do Núcleo e/ou sobre o app DECODA. + +--- + +Desenvolvido com ✊ para o DECODA · Núcleo de Tecnologia do MTST diff --git a/app/src/vendor/capicoda/capicoda.js b/app/src/vendor/capicoda/capicoda.js new file mode 100644 index 0000000..e2d3e32 --- /dev/null +++ b/app/src/vendor/capicoda/capicoda.js @@ -0,0 +1,341 @@ +/*! + * Capicoda 🚩 — a capivara comunista do Núcleo de Tecnologia do MTST + * Widget autocontido: conversa com o visitante, identifica devs e os + * direciona para contribuir com o DECODA (https://git.mtst.tec.br/educacao/decoda/). + * + * Uso: + * Sem dependências, sem build, sem backend. + */ +(function () { + "use strict"; + + // ----- Links reais do DECODA ------------------------------------------ + var URLS = { + repo: "https://git.mtst.tec.br/educacao/decoda/", + decoda: "https://www.nucleodetecnologia.com.br/decoda", + nucleo: "https://www.nucleodetecnologia.com.br", + }; + + // ----- Árvore de diálogo ---------------------------------------------- + // Cada nó: { msg: string|string[], options: [{label, next?, url?, primary?}] } + // - next: navega para outro nó + // - url: abre link em nova aba (pode coexistir com next) + var TREE = { + start: { + msg: [ + "Salve, companheirx! 🚩", + "Sou o Capicoda, a capivara do Núcleo de Tecnologia do MTST. 🦫", + "Tô aqui pra te apresentar o DECODA — e ver se rola você somar com a gente. Bora?", + ], + options: [ + { label: "Bora! 🚩", next: "qualifica", primary: true }, + { label: "O que é o DECODA?", next: "oque_decoda" }, + ], + }, + + oque_decoda: { + msg: [ + "O DECODA é uma plataforma educacional do MTST: ensina lógica de programação com jogos e blocos visuais (arrastar e soltar, sem decoreba de sintaxe).", + "É 100% gratuita, sem cadastro. A ideia é pensamento computacional como ferramenta de transformação social — tecnologia pra quem o sistema deixa de fora.", + ], + options: [{ label: "Show. E eu, como ajudo?", next: "qualifica", primary: true }], + }, + + qualifica: { + msg: "Me conta: você programa ou tá aprendendo?", + options: [ + { label: "Sim, sou dev 👩‍💻", next: "area", primary: true }, + { label: "Tô aprendendo 🌱", next: "aprendiz" }, + { label: "Só curiosx 👀", next: "curioso" }, + ], + }, + + area: { + msg: "Massa! Qual é a tua praia?", + options: [ + { label: "Front-end / JS", next: "cta_front", primary: true }, + { label: "Back / DevOps", next: "cta_infra" }, + { label: "Full stack / outra", next: "cta_geral" }, + ], + }, + + cta_front: { + msg: [ + "Então é praia tua mesmo! 🌊 O DECODA é React + Vite + Blockly com Tailwind. Os jogos e atividades de blocos são exatamente front-end.", + "Tem componente pra refinar, jogo novo pra criar, acessibilidade pra melhorar. Por onde quer começar?", + ], + options: ctaOptions(), + }, + + cta_infra: { + msg: [ + "Tem espaço de sobra! ⚙️ A stack roda em Docker + Docker Compose com Nginx de proxy, e o app empacota até como Electron (modo offline).", + "Build, deploy, CI, performance dos containers... dá pra somar bastante. Por onde começamos?", + ], + options: ctaOptions(), + }, + + cta_geral: { + msg: [ + "Perfeito! 🚩 O DECODA tem front (React + Vite + Blockly), docs (Docusaurus) e infra (Docker + Nginx). Tem frente pra todo gosto.", + "Bora dar o primeiro passo:", + ], + options: ctaOptions(), + }, + + setup: { + msg: [ + "Rodar localmente é tranquilo. 🛠️", + "Jeito fácil (Docker):
1. git clone https://git.mtst.tec.br/educacao/decoda.git
2. cd decoda
3. docker compose up --build -d
→ app em http://localhost, docs em http://localhost/docs", + "Sem Docker (Node 20+ e pnpm):
cd app && pnpm install && pnpm run devhttp://localhost:5173", + ], + options: [ + { label: "Abrir o repositório", url: URLS.repo, primary: true }, + { label: "Como mando um PR?", next: "fluxo_pr" }, + { label: "Voltar ao início", next: "start" }, + ], + }, + + fluxo_pr: { + msg: [ + "O fluxo de contribuição é Git Flow, bem direto: 🌳", + "1. Fork do projeto
2. git checkout -b feature/sua-ideia (a partir de develop)
3. git commit -m \"feat: ...\"
4. git push origin feature/sua-ideia
5. Abra um Pull Request → code review → merge 🎉", + ], + options: [ + { label: "Abrir o repositório", url: URLS.repo, primary: true }, + { label: "Como rodo localmente?", next: "setup" }, + { label: "Voltar ao início", next: "start" }, + ], + }, + + aprendiz: { + msg: [ + "Que demais! 🌱 O DECODA foi feito justamente pra isso — então você é nosso público E pode contribuir aprendendo.", + "Usa a plataforma pra praticar, e quando se sentir à vontade vem mexer no código. Toda dúvida sua vira melhoria pra próxima pessoa.", + ], + options: [ + { label: "Acessar o DECODA", url: URLS.decoda, primary: true }, + { label: "Ver o código (com calma)", next: "setup" }, + { label: "Voltar ao início", next: "start" }, + ], + }, + + curioso: { + msg: [ + "Tranquilo, sem pressão! 👀 Mesmo sem programar você ajuda muito:", + "Divulga o DECODA, dá feedback de uso, leva pra escolas e ocupações, traz mais gente pra comunidade. Tudo isso é contribuição. ✊", + ], + options: [ + { label: "Conhecer o Núcleo", url: URLS.nucleo, primary: true }, + { label: "Conhecer o DECODA", url: URLS.decoda }, + { label: "Voltar ao início", next: "start" }, + ], + }, + }; + + // Opções padrão dos nós de chamada-pra-ação (CTA) + function ctaOptions() { + return [ + { label: "Ver o repositório", url: URLS.repo, primary: true }, + { label: "Como rodar localmente", next: "setup" }, + { label: "Como mandar um PR", next: "fluxo_pr" }, + { label: "Voltar ao início", next: "start" }, + ]; + } + + // ----- SVG da capivara (com boina vermelha + estrela) ----------------- + var CAPY_SVG = + '"; + + // ----- Estilos -------------------------------------------------------- + var CSS = + "#dcs-root{position:fixed;bottom:20px;right:20px;z-index:2147483000;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif}" + + "#dcs-root *{box-sizing:border-box}" + + // botão flutuante + "#dcs-fab{width:64px;height:64px;border:none;border-radius:50%;cursor:pointer;background:#c1121f;box-shadow:0 6px 20px rgba(0,0,0,.28);padding:6px;transition:transform .15s ease;position:relative}" + + "#dcs-fab:hover{transform:scale(1.07)}" + + "#dcs-fab svg{width:100%;height:100%;display:block}" + + "#dcs-fab .dcs-dot{position:absolute;top:-2px;right:-2px;width:18px;height:18px;background:#f4c430;border:2px solid #fff;border-radius:50%}" + + // painel + "#dcs-panel{position:absolute;bottom:80px;right:0;width:340px;max-width:calc(100vw - 32px);height:480px;max-height:calc(100vh - 120px);background:#fff;border-radius:16px;box-shadow:0 12px 40px rgba(0,0,0,.3);display:none;flex-direction:column;overflow:hidden;animation:dcs-pop .18s ease}" + + "#dcs-panel.dcs-open{display:flex}" + + "@keyframes dcs-pop{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}" + + // header + "#dcs-head{background:#c1121f;color:#fff;padding:12px 14px;display:flex;align-items:center;gap:10px;flex:0 0 auto}" + + "#dcs-head .dcs-av{width:40px;height:40px;border-radius:50%;background:#9b6b43;padding:3px;flex:0 0 auto}" + + "#dcs-head .dcs-av svg{width:100%;height:100%;display:block}" + + "#dcs-head h3{margin:0;font-size:15px;font-weight:700}" + + "#dcs-head p{margin:1px 0 0;font-size:11.5px;opacity:.9}" + + "#dcs-close{margin-left:auto;background:none;border:none;color:#fff;font-size:22px;line-height:1;cursor:pointer;opacity:.85;padding:0 4px}" + + "#dcs-close:hover{opacity:1}" + + // corpo + "#dcs-body{flex:1 1 auto;overflow-y:auto;padding:14px;background:#faf6f1}" + + ".dcs-bubble{max-width:85%;padding:9px 12px;border-radius:14px;margin-bottom:8px;font-size:14px;line-height:1.45;word-wrap:break-word;animation:dcs-pop .18s ease}" + + ".dcs-bot{background:#fff;color:#222;border:1px solid #eee;border-bottom-left-radius:4px}" + + ".dcs-user{background:#c1121f;color:#fff;margin-left:auto;border-bottom-right-radius:4px}" + + ".dcs-bubble code{background:#f0e8df;color:#7a2e16;padding:1px 5px;border-radius:5px;font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}" + + ".dcs-bot code{display:inline-block}" + + // opções + "#dcs-opts{flex:0 0 auto;padding:10px 12px;border-top:1px solid #eee;background:#fff;display:flex;flex-wrap:wrap;gap:8px}" + + ".dcs-opt{flex:1 1 auto;min-width:46%;border:1.5px solid #c1121f;background:#fff;color:#c1121f;padding:9px 10px;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;transition:all .12s ease;text-align:center}" + + ".dcs-opt:hover{background:#fff0f0}" + + ".dcs-opt.dcs-primary{background:#c1121f;color:#fff}" + + ".dcs-opt.dcs-primary:hover{background:#a30e1a}" + + // indicador de digitação + ".dcs-typing{display:flex;gap:4px;padding:11px 12px}" + + ".dcs-typing span{width:7px;height:7px;border-radius:50%;background:#c1a;opacity:.5;background:#c1121f;animation:dcs-blink 1s infinite}" + + ".dcs-typing span:nth-child(2){animation-delay:.2s}.dcs-typing span:nth-child(3){animation-delay:.4s}" + + "@keyframes dcs-blink{0%,60%,100%{opacity:.25}30%{opacity:1}}"; + + // ----- Construção do DOM ---------------------------------------------- + var body, opts, panel, fab; + var typingTimer = null; + + function el(tag, attrs, html) { + var n = document.createElement(tag); + if (attrs) for (var k in attrs) n.setAttribute(k, attrs[k]); + if (html != null) n.innerHTML = html; + return n; + } + + function build() { + var style = el("style"); + style.textContent = CSS; + document.head.appendChild(style); + + var root = el("div", { id: "dcs-root" }); + + fab = el("button", { id: "dcs-fab", "aria-label": "Falar com o Capicoda" }, CAPY_SVG); + fab.appendChild(el("span", { class: "dcs-dot" })); + fab.addEventListener("click", toggle); + + panel = el("div", { id: "dcs-panel", role: "dialog", "aria-label": "Capicoda" }); + + var head = el("div", { id: "dcs-head" }); + head.appendChild(el("div", { class: "dcs-av" }, CAPY_SVG)); + var titles = el("div"); + titles.appendChild(el("h3", null, "Capicoda 🚩")); + titles.appendChild(el("p", null, "capivara do Núcleo de Tec • MTST")); + head.appendChild(titles); + var close = el("button", { id: "dcs-close", "aria-label": "Fechar" }, "×"); + close.addEventListener("click", toggle); + head.appendChild(close); + + body = el("div", { id: "dcs-body" }); + opts = el("div", { id: "dcs-opts" }); + + panel.appendChild(head); + panel.appendChild(body); + panel.appendChild(opts); + + root.appendChild(panel); + root.appendChild(fab); + document.body.appendChild(root); + } + + // ----- Motor de conversa ---------------------------------------------- + var started = false; + + function toggle() { + var open = panel.classList.toggle("dcs-open"); + if (open) { + fab.querySelector(".dcs-dot").style.display = "none"; + if (!started) { + started = true; + goTo("start"); + } + } + } + + function scrollDown() { + body.scrollTop = body.scrollHeight; + } + + function addBubble(html, who) { + var b = el("div", { class: "dcs-bubble " + (who === "user" ? "dcs-user" : "dcs-bot") }, html); + body.appendChild(b); + scrollDown(); + return b; + } + + function clearOpts() { + opts.innerHTML = ""; + } + + function renderOptions(list) { + clearOpts(); + list.forEach(function (o) { + var btn = el("button", { class: "dcs-opt" + (o.primary ? " dcs-primary" : "") }, o.label); + btn.addEventListener("click", function () { + addBubble(o.label, "user"); + if (o.url) window.open(o.url, "_blank", "noopener"); + if (o.next) goTo(o.next); + // link puro (só url): mantém os botões pra pessoa escolher outra ação + }); + opts.appendChild(btn); + }); + } + + function goTo(id) { + var node = TREE[id]; + if (!node) return; + if (id === "start") { + body.innerHTML = ""; // reinicia a conversa + } + clearOpts(); + + var msgs = Array.isArray(node.msg) ? node.msg.slice() : [node.msg]; + + // mostra indicador de digitação e revela as bolhas em sequência + var typing = el("div", { class: "dcs-bubble dcs-bot dcs-typing" }, + ""); + body.appendChild(typing); + scrollDown(); + + var i = 0; + function next() { + if (i < msgs.length) { + if (i === 0 && typing.parentNode) typing.remove(); + addBubble(msgs[i], "bot"); + i++; + typingTimer = setTimeout(next, 420); + } else { + renderOptions(node.options || []); + } + } + typingTimer = setTimeout(next, 380); + } + + // ----- Inicialização -------------------------------------------------- + function init() { + if (document.getElementById("dcs-root")) return; + build(); + } + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/app/src/vendor/capicoda/demo.html b/app/src/vendor/capicoda/demo.html new file mode 100644 index 0000000..114b4c6 --- /dev/null +++ b/app/src/vendor/capicoda/demo.html @@ -0,0 +1,69 @@ + + + + + + Capicoda — demo · Núcleo de Tecnologia do MTST + + + +
+ 🚩 +

Núcleo de Tecnologia · MTST

+
+ +
+

Aprenda a programar com o DECODA

+

Plataforma educacional do MTST: lógica de programação através de jogos + e blocos visuais. Tecnologia como ferramenta de transformação social.

+
+ +
+

🎮 Jogos

Atividades interativas para aprender programação na prática.

+

🧩 Blocos visuais

Programação arrastar-e-soltar com Blockly, sem decorar sintaxe.

+

🆓 Livre e gratuito

100% gratuito, sem cadastro, código aberto.

+
+ +
+ 👉 Página de demonstração. O balão da capivara Capicoda aparece no + canto inferior direito — clique pra conversar. +
+ + + + + + +