Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions docs/analytics-custom-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,28 @@ los usuarios usan el contenido.
## Implementación

- Crear utilidad `src/services/analytics.js` con helper `trackEvent(name, params)`
que llame a `window.gtag`.
que llame a `window.gtag('event', name, params)`.
- Integrar la llamada en cada punto indicado.
- Verificar en GA4 > Realtime que los eventos aparezcan.

### Referencias

- **Cómo enviar eventos desde código (gtag.js)**: [Google tag (gtag.js) - Reference
events](https://developers.google.com/tag-platform/gtagjs/reference/events)
— documenta `gtag('event', '<event_name>', { <parameter>: <value> })`.
- **Eventos en GA4 (recomendados, personalizados y parámetros)**:
[Events - Analytics Help](https://support.google.com/analytics/answer/12229021).
- **Marcar eventos como key event / conversión (paso final, sin código)**:
[Create or modify key events](https://support.google.com/analytics/answer/12844695)
— útil solo para convertir `contact_click` o `download_cv` en conversión
desde Admin > Events, **no** para enviar los eventos.

## Criterios de aceptación

- [ ] Helper `trackEvent` en `src/services/analytics.js`
- [ ] `language_changed` con parámetro `language`
- [ ] `project_open` con `company` y `project`
- [ ] `contact_click` con `network`
- [ ] `download_cv` con `language`
- [ ] `about_open`
- [x] Helper `trackEvent` en `src/services/analytics.js`
- [x] `language_changed` con parámetro `language`
- [x] `project_open` con `company` y `project`
- [x] `contact_click` con `network`
- [x] `download_cv` con `language`
- [x] `about_open`
- [ ] Eventos visibles en GA4 Realtime tras deploy
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/>
<meta name="theme-color" content="#2563eb" />
<link rel="canonical" href="https://julnarot.github.io/" />
<title>Raul Jonatan | Full stack engineering</title>
<title>Raul Jonatan | Full Stack Engineer</title>

<!-- Open Graph -->
<meta property="og:type" content="profile" />
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"eslint-plugin-react-refresh": "^0.4.19",
"postcss": "^8.5.3",
"tailwindcss": "^3.4.17",
"vite": "^5.4.14"
"vite": "^5.4.14",
"vite-plugin-prerender": "^1.0.8"
},
"overrides": {
"puppeteer": "^21.11.0"
}
}
38 changes: 30 additions & 8 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import PopupLanguageSelector from "./components/features/popupLanguageSelector";
import AboutPopup from "./components/core/aboutPopup";

import { menuStore } from "./stores/menuStore";
import { trackEvent } from "./services/analytics";

function App() {
const { t, i18n } = useTranslation();
Expand All @@ -32,6 +33,12 @@ function App() {
};

const toggle = (workExperience) => {
if (workExperience) {
trackEvent("project_open", {
company: workExperience.companyName,
project: workExperience.proyect,
});
}
setIsOpen((isOpen) => !isOpen);
setWorkExperience(workExperience);
setIsContactOpen(false);
Expand All @@ -41,6 +48,9 @@ function App() {
const menuSelected = (subItemSelected) => {
const menuAction = findMenuActionBySubItem(subItemSelected);
if (menuAction.type === "POPUP") {
if (menuAction.subMenuTitle === "About") {
trackEvent("about_open");
}
setSubMenuItem(menuAction);
} else if (menuAction && menuAction.type === "LINK") {
menuAction.component(i18n.language);
Expand All @@ -57,21 +67,27 @@ function App() {
return (
<div className="h-screen w-screen flex flex-col overflow-hidden">
{/* Top Menu Bar */}
<MenuBar onSubMenuSelected={menuSelected} />
<header>
<MenuBar onSubMenuSelected={menuSelected} />
</header>

{/* Main Content */}
<div className="flex-1 relative flex flex-col w-full h-full overflow-hidden">
<main className="flex-1 relative flex flex-col w-full h-full overflow-hidden">
<h1 className="sr-only">Raul Jonatan | Software Developer</h1>
<div className="flex-1 bg-blue-600 dark:bg-zinc-900 p-4 text-white flex flex-col md:flex-row w-full h-full gap-2 overflow-auto">
<div className="basis-1/2 relative h-full">
<section id="about" className="basis-1/2 relative h-full">
<EditWindow windowTitle="~/main.prev" windowNumber="2">
<div className="my-2">
[
<h1
<span
className="cursor-pointer font-bold underline decoration-wavy inline"
onClick={onContactToggle}
role="button"
tabIndex={0}
onKeyDown={(e) => e.key === "Enter" && onContactToggle()}
>
RAUL JONATAN
</h1>
</span>
] | <h2 className="inline">Full stack engineering</h2>
</div>
<img
Expand All @@ -87,9 +103,10 @@ function App() {
<p className="my-4">{t("main.specialty")}</p>
<p className="my-4">{t("main.working")}</p>
</EditWindow>
</div>
</section>

<div className="flex-1 text-white flex flex-col w-full gap-2">
<section id="projects" className="contents">
<EditWindow windowTitle="~/work-exp.prev" windowNumber="3">
<p>{t("workExperience.title")}</p>
<ul className="list-disc list-outside p-3">
Expand All @@ -116,7 +133,9 @@ function App() {
))}
</ul>
</EditWindow>
</section>

<section id="education" className="contents">
<EditWindow windowTitle="~/education.prev" windowNumber="4">
<p>{t("academicTraining.title")}</p>
<ul className="list-disc list-outside p-3 ">
Expand All @@ -130,14 +149,17 @@ function App() {
</li>
</ul>
</EditWindow>
</section>
</div>
</div>

{isContactOpen && <Contact onClose={onContactToggle} />}
</div>
</main>

{/* Status Bar */}
<StatusBar />
<footer>
<StatusBar />
</footer>

{isOpen && <Popup onClose={toggle} experience={workExperience} />}
{subMenuItem && subMenuItem.subMenuTitle === "About" && (
Expand Down
1 change: 1 addition & 0 deletions src/components/core/aboutPopup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function AboutPopup({ onClose }) {
<a
href="https://github.com/julnarot/julnarot.github.io?tab=readme-ov-file#-retro-portfolio---inspired-by-borland-c"
target="_blank"
rel="noopener noreferrer"
className="cursor-pointer font-bold underline decoration-wavy dark:hover:text-blue-600 hover:text-white"
>
React & Tailwind
Expand Down
10 changes: 6 additions & 4 deletions src/components/features/contact.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useTranslation } from "react-i18next";
import { trackEvent } from "../../services/analytics";

export default function Contact({ onClose }) {
const { t } = useTranslation();
const onContactClick = (network) => trackEvent('contact_click', { network });
return (
<div className="bg-cyan-900 p-2 text-white dark:bg-purple-900 ">
<div className="border-4 border-double text-white relative w-full ">
Expand All @@ -11,10 +13,10 @@ export default function Contact({ onClose }) {
<div className="absolute -top-3 right-3 bg-cyan-400 dark:bg-purple-900 px-1 text-sm"> [<span className="text-green-400">&#8593;</span>]</div>
<div className="p-2 pt-3 text-blue-600 dark:text-white selection:bg-cyan-600 h-full overflow-auto
[&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-bluen-600 [&::-webkit-scrollbar-thumb]:bg-cyan-500">
<a href='https://linkedin.com/in/julnarot' target='_blank' rel='noopener noreferrer' className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Linkedin</a>
<a href='https://github.com/julnarot' target='_blank' rel='noopener noreferrer' className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Github</a>
<a href='https://x.com/julnarot' target='_blank' rel='noopener noreferrer' className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Twitter</a>
<a href='mailto:rauljhonatan.tola@gmail.com' rel='noopener noreferrer' className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(@) Write me</a>
<a href='https://linkedin.com/in/julnarot' target='_blank' rel='noopener noreferrer' aria-label='LinkedIn - Raul Jonatan' onClick={() => onContactClick('linkedin')} className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Linkedin</a>
<a href='https://github.com/julnarot' target='_blank' rel='noopener noreferrer' aria-label='GitHub - Raul Jonatan' onClick={() => onContactClick('github')} className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Github</a>
<a href='https://x.com/julnarot' target='_blank' rel='noopener noreferrer' aria-label='Twitter / X - Raul Jonatan' onClick={() => onContactClick('twitter')} className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(🌏︎) Twitter</a>
<a href='mailto:rauljhonatan.tola@gmail.com' rel='noopener noreferrer' aria-label='Send an email to Raul Jonatan' onClick={() => onContactClick('email')} className="flex cursor-pointer font-bold no-underline hover:bg-lime-500 hover:text-white">(@) Write me</a>
</div>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/components/features/popupLanguageSelector.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useTranslation } from "react-i18next";
import Button from "../core/button";
import { trackEvent } from "../../services/analytics";

export default function PopupLanguageSelector({ onClose, languageChanged}) {
const { i18n } = useTranslation();
const languages = [{ value: 'en', name: 'English' }, { value: 'es', name: 'Spanish' }]
function languagesOnChange({ target }) {
i18n.changeLanguage(target.value);
trackEvent('language_changed', { language: target.value });
languageChanged()
}

Expand Down
2 changes: 2 additions & 0 deletions src/constants/popup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AboutPopup from "../components/core/aboutPopup";
import PopupLanguageSelector from "../components/features/popupLanguageSelector";
import { trackEvent } from "../services/analytics";

export const SUBMENU_ACTIONS = [
{
Expand All @@ -14,6 +15,7 @@ export const SUBMENU_ACTIONS = [
type: 'LINK',
component: (lng) => {
const fileName = lng && lng === 'es' ? 'Profile_es.pdf' : 'Profile.pdf'
trackEvent('download_cv', { language: lng && lng === 'es' ? 'es' : 'en' })
const a = document.createElement("a");
a.href = `documents/${fileName}`;
a.target = "_blank";
Expand Down
26 changes: 19 additions & 7 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { createRoot, hydrateRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
import './i18n';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
import './i18n'

const rootElement = document.getElementById('root')

if (rootElement.hasChildNodes()) {
hydrateRoot(
rootElement,
<StrictMode>
<App />
</StrictMode>,
)
} else {
createRoot(rootElement).render(
<StrictMode>
<App />
</StrictMode>,
)
}
5 changes: 5 additions & 0 deletions src/services/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const trackEvent = (eventName, params = {}) => {
if (typeof window === "undefined") return;
if (typeof window.gtag !== "function") return;
window.gtag("event", eventName, params);
};
26 changes: 26 additions & 0 deletions vite.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { defineConfig } = require('vite')
const react = require('@vitejs/plugin-react')
const vitePrerender = require('vite-plugin-prerender')
const fs = require('fs')
const path = require('path')

const systemChromePath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'

module.exports = defineConfig({
plugins: [
react(),
vitePrerender({
staticDir: path.resolve('dist'),
routes: ['/'],
renderer: new vitePrerender.PuppeteerRenderer({
executablePath:
process.platform === 'darwin' && fs.existsSync(systemChromePath)
? systemChromePath
: undefined,
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox'],
skipThirdPartyRequests: true,
}),
}),
],
})
6 changes: 0 additions & 6 deletions vite.config.js

This file was deleted.

Loading