feature/plausible #3
2
app/.env
Normal file
2
app/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
VITE_PLAUSIBLE_API=http://localhost/api/event
|
||||
VITE_PLAUSIBLE_DOMAIN=myapp-dev
|
||||
2
app/.env.production
Normal file
2
app/.env.production
Normal file
@@ -0,0 +1,2 @@
|
||||
VITE_PLAUSIBLE_API=https://plausible.mtst.tec.br/api/event
|
||||
VITE_PLAUSIBLE_DOMAIN=https://decoda.mtst.tec.br
|
||||
@@ -3,7 +3,7 @@ FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
ARG GIT_COMMIT_HASH=unknown
|
||||
ARG APP_VERSION=1.1.2
|
||||
ARG APP_VERSION=1.2.0
|
||||
ENV VITE_APP_VERSION=$APP_VERSION
|
||||
ENV VITE_GIT_HASH=$GIT_COMMIT_HASH
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/puzzle.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>Decoda</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/puzzle.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>Decoda</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "decoda",
|
||||
"private": true,
|
||||
"description": "Aplicação educacional desenvolvida para ensino de programação básica e letramento digital",
|
||||
"version": "1.1.2",
|
||||
"version": "1.2.0",
|
||||
"main": "main.cjs",
|
||||
"homepage": "./",
|
||||
"type": "module",
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
* @module App
|
||||
*/
|
||||
|
||||
import { lazy, Suspense } from "react";
|
||||
import { lazy, Suspense, useEffect } from "react";
|
||||
import { HashRouter as Router, Routes, Route, useLocation } from "react-router-dom";
|
||||
import "./App.css";
|
||||
import HomePage from "./pages/HomePage/HomePage";
|
||||
import LabPython from "./pages/LabPython/LabPython";
|
||||
import ScrollToTop from "./components/ScrollToTop";
|
||||
import { trackPageView } from "./services/plausible";
|
||||
|
||||
const Playground = lazy(() => import("./pages/Playground/Playground"));
|
||||
const About = lazy(() => import("./pages/About/About"));
|
||||
@@ -68,6 +69,10 @@ function AppRoutes() {
|
||||
// keeps rendering behind the modal overlay.
|
||||
const backgroundLocation = location.state?.backgroundLocation;
|
||||
|
||||
useEffect(() => {
|
||||
trackPageView(location.pathname);
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollToTop />
|
||||
|
||||
@@ -653,10 +653,6 @@ video {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.bottom-0 {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.bottom-10 {
|
||||
bottom: 2.5rem;
|
||||
}
|
||||
@@ -984,10 +980,6 @@ video {
|
||||
height: 0.25rem;
|
||||
}
|
||||
|
||||
.h-1\.5 {
|
||||
height: 0.375rem;
|
||||
}
|
||||
|
||||
.h-10 {
|
||||
height: 2.5rem;
|
||||
}
|
||||
@@ -2885,10 +2877,6 @@ video {
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-gray-200\/50 {
|
||||
background-color: rgb(229 231 235 / 0.5);
|
||||
}
|
||||
|
||||
.bg-gray-300 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1));
|
||||
@@ -7125,10 +7113,6 @@ video {
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.duration-1000 {
|
||||
transition-duration: 1000ms;
|
||||
}
|
||||
|
||||
.duration-150 {
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
@@ -7149,10 +7133,6 @@ video {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.ease-out {
|
||||
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.\[-webkit-text-stroke\:2px_black\] {
|
||||
-webkit-text-stroke: 2px black;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import React, {
|
||||
import PropTypes from "prop-types";
|
||||
import { gameEventBus } from "../utils/gameEvents";
|
||||
import { GameProgressProvider, useGameProgress } from "./GameProgressContext";
|
||||
import { trackEvent } from "../services/plausible";
|
||||
|
||||
export const GAME_STATES = {
|
||||
PARADO: "parado",
|
||||
@@ -89,6 +90,9 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
);
|
||||
return urlParams.get(debugKey)?.toLowerCase() === "true";
|
||||
});
|
||||
const [phaseStartTime, setPhaseStartTime] = useState(null);
|
||||
const [attemptCount, setAttemptCount] = useState(0);
|
||||
const [failureCount, setFailureCount] = useState(0);
|
||||
|
||||
const getCodeFromWorkspace = useRef(null);
|
||||
const getCodeFromEditor = useRef(null);
|
||||
@@ -103,6 +107,8 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
* @throws {console.error} Se editor não registrou a função de execução
|
||||
*/
|
||||
const execute = () => {
|
||||
setAttemptCount(prev => prev + 1);
|
||||
|
||||
if (editorType === "code") {
|
||||
if (getCodeFromEditor.current) {
|
||||
const codigo = getCodeFromEditor.current();
|
||||
@@ -145,6 +151,19 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
if (!completedPhases.includes(currentPhase)) {
|
||||
setCompletedPhases([...completedPhases, currentPhase]);
|
||||
}
|
||||
|
||||
const durationSeconds = phaseStartTime
|
||||
? Math.round((Date.now() - phaseStartTime) / 1000)
|
||||
: null;
|
||||
|
||||
trackEvent('Activity Success', {
|
||||
activity: gameConfig.gameId,
|
||||
phase: currentPhase,
|
||||
blocks: currentBlockCount,
|
||||
attempts: attemptCount,
|
||||
failures: failureCount,
|
||||
durationSeconds,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -157,6 +176,14 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
*/
|
||||
const finalizeWithFailure = () => {
|
||||
setExecutionState(GAME_STATES.FALHA);
|
||||
setFailureCount(prev => prev + 1);
|
||||
|
||||
trackEvent('Activity Failure', {
|
||||
activity: gameConfig.gameId,
|
||||
phase: currentPhase,
|
||||
blocks: currentBlockCount,
|
||||
error: failureMessage,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -198,6 +225,9 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
setGeneratedCode("");
|
||||
setCurrentBlockCount(0);
|
||||
setCodeEditorContent("");
|
||||
setPhaseStartTime(Date.now());
|
||||
setAttemptCount(0);
|
||||
setFailureCount(0);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -317,6 +347,8 @@ function GameStateInnerProvider({ children, gameConfig }) {
|
||||
setFailureMessage,
|
||||
isDebugMode,
|
||||
setIsDebugMode,
|
||||
attemptCount,
|
||||
failureCount,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
41
app/src/services/plausible.js
Normal file
41
app/src/services/plausible.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const PLAUSIBLE_API = import.meta.env.VITE_PLAUSIBLE_API || 'http://localhost/api/event';
|
||||
const DOMAIN = import.meta.env.VITE_PLAUSIBLE_DOMAIN || 'myapp-dev';
|
||||
|
||||
const sendEvent = (payload) => {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
fetch(PLAUSIBLE_API, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
signal: controller.signal,
|
||||
})
|
||||
.catch((e) => console.error('Plausible error:', e))
|
||||
.finally(() => clearTimeout(timeout));
|
||||
};
|
||||
|
||||
export const trackPageView = (pathname) => {
|
||||
if (!pathname) return;
|
||||
|
||||
sendEvent({
|
||||
domain: DOMAIN,
|
||||
url: `${window.location.origin}${pathname}`,
|
||||
referrer: document.referrer,
|
||||
screenWidth: window.innerWidth,
|
||||
name: 'pageview',
|
||||
});
|
||||
};
|
||||
|
||||
export const trackEvent = (eventName, properties = {}) => {
|
||||
if (!eventName) return;
|
||||
|
||||
sendEvent({
|
||||
domain: DOMAIN,
|
||||
url: window.location.href,
|
||||
referrer: document.referrer,
|
||||
screenWidth: window.innerWidth,
|
||||
name: eventName,
|
||||
props: properties,
|
||||
});
|
||||
};
|
||||
@@ -74,6 +74,9 @@ export default defineConfig({
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
allowedHosts: ["localhost", "dev.local", "decoda.mtst.tec.br"],
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
copyLetramentoAtividades(),
|
||||
|
||||
Reference in New Issue
Block a user