diff --git a/.gitignore b/.gitignore index 3fef480..d7c5ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,58 @@ -backend/wordpress/wp-config-local.php +# ── Secretos y entorno ────────────────────────────────────── +.env +.env.* +!.env.example +.token_gitea +LOCAL_DEPLOY_CONTEXT.md + +# ── WordPress core (no versionar — viene de wordpress.org) ── +backend/wordpress/wp-admin/ +backend/wordpress/wp-includes/ +backend/wordpress/wp-activate.php +backend/wordpress/wp-blog-header.php +backend/wordpress/wp-comments-post.php +backend/wordpress/wp-cron.php +backend/wordpress/wp-links-opml.php +backend/wordpress/wp-load.php +backend/wordpress/wp-login.php +backend/wordpress/wp-mail.php +backend/wordpress/wp-settings.php +backend/wordpress/wp-signup.php +backend/wordpress/wp-trackback.php +backend/wordpress/xmlrpc.php +backend/wordpress/index.php +backend/wordpress/readme.html +backend/wordpress/license.txt + +# ── wp-content: solo versionar lo propio ──────────────────── backend/wordpress/wp-content/uploads/ backend/wordpress/wp-content/cache/ backend/wordpress/wp-content/upgrade/ backend/wordpress/wp-content/backups/ -.env -.token_gitea +backend/wordpress/wp-content/debug.log +backend/wordpress/wp-content/languages/ + +# Temas por defecto de WordPress (no son nuestros) +backend/wordpress/wp-content/themes/twentytwentythree/ +backend/wordpress/wp-content/themes/twentytwentyfour/ +backend/wordpress/wp-content/themes/twentytwentyfive/ + +# Plugins descargados de WordPress.org (no son nuestros) +backend/wordpress/wp-content/plugins/cmb2/ +backend/wordpress/wp-content/plugins/wp-mail-smtp/ +backend/wordpress/wp-content/plugins/advanced-custom-fields/ +backend/wordpress/wp-content/plugins/index.php + +# ── Sistema / editor ──────────────────────────────────────── .DS_Store +Thumbs.db +.idea/ +.vscode/* +!.vscode/extensions.json +*.swp +*.swo +.claude/ + +# ── Archivos temporales ────────────────────────────────────── +/tmp/ +*.tmp diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e336ee8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog — pflores.linkedcorp.net + +Formato: `X.Y.Z` según `VERSIONADO.md` + +--- + +## [1.0.0] — 2026-06-05 + +### Lanzamiento inicial completo + +**Temas WordPress (4)** +- `persona-cv` — Personage/Pixflow style: hero fullscreen, secciones alternas, timeline centrado, barras de skills, Raleway + Open Sans. Tema activo. +- `lumina` — Diseño claro alegre, paleta teal+ámbar, sidebar izquierdo, foto circular, Plus Jakarta Sans. +- `apex-cv` — Dark theme serio, sidebar fijo izquierdo, acento cian eléctrico, Space Grotesk. +- `cvolio` — Glassmorphism oscuro, paleta café/tostado, sidebar derecho. + +**Gestión de contenido** +- Plugin propio `pflores-cv-fields` con CMB2 (100 % gratuito). +- Panel admin **"CV Patricia Flores"** con 5 sub-páginas editables: Perfil, Experiencia, Formación, Competencias, Métricas/Impacto. +- Todos los datos del CV editables sin tocar código. + +**Generador de PDF** +- Endpoint `/?cv_pdf=1` genera PDF dinámico con Chromium headless. +- Diseño fiel al documento DOCX original: columna única, navy #1B3A5C, secciones en mayúsculas con borde inferior. +- Se actualiza automáticamente al cambiar datos en el admin. +- Botón "Descargar CV PDF" en todos los temas apunta al mismo endpoint. + +**Infraestructura** +- Servidor PHP integrado en `0.0.0.0:8081` (accesible desde red local). +- URL dinámica en `wp-config.php` — funciona con cualquier IP y detrás de proxy SSL (NPM). +- Auto-updates de WordPress desactivados. +- WP Mail SMTP instalado (pendiente configuración SMTP). + +**Contenido del CV cargado** +- 5 cargos con 23 bullets completos. +- 2 títulos académicos, 10 certificaciones. +- 3 grupos de competencias (15 habilidades con porcentajes). +- Métricas, highlights, impacto, voluntariado y datos adicionales. diff --git a/CV_Patricia_Flores_Sanchez_ES_FINAL.docx b/CV_Patricia_Flores_Sanchez_ES_FINAL.docx new file mode 100644 index 0000000..544bf3d Binary files /dev/null and b/CV_Patricia_Flores_Sanchez_ES_FINAL.docx differ diff --git a/DEPLOY_WEB.md b/DEPLOY_WEB.md new file mode 100644 index 0000000..522c7d0 --- /dev/null +++ b/DEPLOY_WEB.md @@ -0,0 +1,90 @@ +# Deploy Web — pflores.linkedcorp.net + +## Previo a cualquier push + +- [ ] `git status` — sin `.env` ni `.token_gitea` en staging +- [ ] Site responde: `curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad"` → debe retornar ≥ 1 +- [ ] PDF funciona: `curl -sI http://127.0.0.1:8081/?cv_pdf=1 | grep Content-Type` → `application/pdf` +- [ ] Si hubo cambios funcionales: actualizar `VERSION` y `CHANGELOG.md` + +## Pasos — ciclo normal de cambios + +```bash +# 1. Ver qué cambió +git status +git diff --stat + +# 2. Agregar archivos (selectivo, nunca git add -A sin revisar) +git add frontend/persona-cv-theme/ +git add frontend/lumina-cv-theme/ +git add frontend/cvfolio-theme/ +git add backend/wordpress/wp-content/themes/apex-cv/ +git add backend/wordpress/wp-content/plugins/pflores-cv-fields/ +git add backend/wordpress/wp-config.php +git add README.md CHANGELOG.md VERSION PROMPT_IA.md +# ... agregar solo lo que corresponde + +# 3. Commit +git commit -m "fix: descripción del cambio" +# Prefijos: feat: / fix: / docs: / chore: / style: + +# 4. Push a Gitea +git push origin main +``` + +## Credenciales para git push + +Las credenciales de Gitea se guardan en `.token_gitea` (nunca versionado): + +```text +# Token gitea +usuario: mgonzalez +token: [tu personal access token de gitea.linkedcorp.net] + +# Gitea web +user: mgonzalez +passwd: [contraseña de acceso] +``` + +Si Git pide usuario y contraseña al hacer push, usar: +- **Usuario:** `mgonzalez` +- **Contraseña:** el personal access token (no la contraseña web) + +Crear token en Gitea: `https://gitea.linkedcorp.net/user/settings/applications` + +### Guardar credenciales para no escribirlas cada vez + +```bash +git config credential.helper store +# La primera vez que hagas push te pedirá usuario+token y los guardará +``` + +## Remoto configurado + +``` +origin https://gitea.linkedcorp.net/mgonzalez/pflores.linkedcorp.net.git +``` + +## No tocar durante el deploy + +- `.env` — credenciales de BD y servidor +- Base de datos (no hacer migrate sin confirmar) +- El proceso PHP del servidor (no reiniciar en producción sin verificar) + +## Si el servidor necesita actualizar el código + +Este proyecto corre directamente desde el directorio de trabajo. Si hay un clon separado en producción: + +```bash +# En el servidor de producción +cd /ruta/al/proyecto +git pull origin main +# Reiniciar el proceso PHP si es necesario +``` + +## Verificación post-push + +```bash +# Confirmar que Gitea recibió los cambios +# Ir a: https://gitea.linkedcorp.net/mgonzalez/pflores.linkedcorp.net +``` diff --git a/PRODUCCION.md b/PRODUCCION.md new file mode 100644 index 0000000..4ee4dbf --- /dev/null +++ b/PRODUCCION.md @@ -0,0 +1,85 @@ +# Producción — pflores.linkedcorp.net + +## Estado actual + +- Versión: `1.0.0` +- Estado: `operativo` +- Última validación: `2026-06-05` + +## Acceso público + +- URL: `https://pflores.linkedcorp.net` +- Admin: `https://pflores.linkedcorp.net/wp-admin` +- PDF: `https://pflores.linkedcorp.net/?cv_pdf=1` + +## Servidor + +- IP local: `192.168.222.40` +- Puerto WordPress: `8081` (PHP built-in server, `0.0.0.0:8081`) +- Proxy: Nginx Proxy Manager → `http://192.168.222.40:8081` +- SSL: certificado en NPM, Force SSL activado, `X-Forwarded-Proto: https` + +## Proceso WordPress + +```bash +# Ver si está corriendo +lsof -i tcp:8081 + +# Arrancar +bash /home/mgonzalez/desarrollo/web/pflores.linkedcorp.net/backend/start-wordpress.sh & + +# Detener +kill $(lsof -ti tcp:8081) +``` + +## Base de datos + +- Host: `192.168.222.20` +- Base: `paty.wordpress` +- Usuario: `paty.wordpress` +- Prefijo de tablas: `cv_` +- Credenciales completas: ver `.env` + +## Plugins activos + +| Plugin | Archivo | Función | +|--------|---------|---------| +| CMB2 | `cmb2/init.php` | Campos personalizados del CV | +| Pflores CV Fields | `pflores-cv-fields/pflores-cv-fields.php` | Admin + PDF | +| WP Mail SMTP | `wp-mail-smtp/wp_mail_smtp.php` | Email (pendiente config) | + +Verificar plugins activos: +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "SELECT option_value FROM cv_options WHERE option_name='active_plugins';" 2>/dev/null +``` + +## Tema activo + +- Slug: `persona-cv` +- Fuente: `frontend/persona-cv-theme/` (symlink) + +## Repositorio Git + +- Remoto: `https://gitea.linkedcorp.net/mgonzalez/pflores.linkedcorp.net.git` +- Rama principal: `main` +- Credenciales: `~/.gitconfig` o `.token_gitea` (no versionado) + +## Comandos de verificación rápida + +```bash +# Site responde +curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad" + +# PDF funciona +curl -sI http://127.0.0.1:8081/?cv_pdf=1 | grep Content-Type + +# Limpiar caché WP si algo no se actualiza +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "DELETE FROM cv_options WHERE option_name LIKE '_site_transient_%' OR option_name LIKE '_transient_%';" 2>/dev/null +``` + +## Pendientes + +- [ ] Configurar SMTP en WP Mail SMTP (Gmail u otro proveedor) +- [ ] Revisar si el proceso WordPress arranca automáticamente al reiniciar el servidor diff --git a/PROMPT_IA.md b/PROMPT_IA.md new file mode 100644 index 0000000..07e63bc --- /dev/null +++ b/PROMPT_IA.md @@ -0,0 +1,756 @@ +# PROMPT_IA — Guía completa del proyecto pflores.linkedcorp.net + +> Documento de referencia para asistentes de IA. Léelo completo antes de cualquier tarea. +> Última actualización: junio 2026. + +--- + +## 1. ¿Qué es este proyecto? + +Sitio web personal de **hoja de vida / currículum ejecutivo** de **Patricia Soledad Flores Sánchez**, gerente de operaciones financieras con 22+ años en banca y microfinanzas (Ecuador). + +- URL pública: `https://pflores.linkedcorp.net` +- Stack: **WordPress 6.x** + PHP 8.4 + MariaDB + **CMB2** (campos personalizados, 100 % gratuito) +- Se ejecuta con el servidor PHP incorporado (`php -S`) en el puerto **8081** +- SSL terminado por **Nginx Proxy Manager** → envía `X-Forwarded-Proto: https` +- IP local del servidor: `192.168.222.40` + +--- + +## 2. Estructura de directorios + +``` +pflores.linkedcorp.net/ +├── backend/ +│ ├── start-wordpress.sh # Arrancar el servidor PHP +│ └── wordpress/ # Instalación WordPress +│ ├── wp-config.php # Config dinámica (URL, auto-updates OFF) +│ └── wp-content/ +│ ├── plugins/ +│ │ ├── cmb2/ # CMB2 — campos personalizados (GRATIS) +│ │ └── pflores-cv-fields/ +│ │ ├── pflores-cv-fields.php # Menú admin + campos CMB2 +│ │ ├── pdf-generator.php # Generador PDF con Chromium +│ │ └── populate-data.php # Sembrador inicial (ejecutar 1 vez) +│ └── themes/ +│ ├── persona-cv/ # ACTIVO — Personage-style, rojo, timeline central +│ ├── apex-cv/ # Dark, cian, sidebar fijo izquierdo +│ ├── lumina -> ../../frontend/lumina-cv-theme # symlink +│ ├── cvolio -> ../../frontend/cvfolio-theme # symlink +│ └── ... +├── frontend/ +│ ├── persona-cv-theme/ # Fuente del tema activo (usa symlink persona-cv) +│ │ ├── style.css # Solo cabecera del tema +│ │ ├── functions.php # Setup, enqueue, helpers +│ │ ├── header.php # Header con nav y botón PDF +│ │ ├── footer.php +│ │ ├── front-page.php # PLANTILLA PRINCIPAL — toda la página CV +│ │ ├── index.php # Fallback +│ │ ├── assets/css/theme.css # Estilos completos +│ │ ├── assets/js/theme.js # JS (nav, scroll spy, animaciones) +│ │ ├── assets/images/ # patricia-flores-profile.jpeg, qr.png +│ │ ├── assets/docs/ # CV_Patricia_Flores_Sanchez_ES_FINAL.docx +│ │ └── inc/ +│ │ ├── cv-data.php # Fallback hardcodeado (función persona_cv_data()) +│ │ ├── customizer.php # Campos Customizer (mínimos) +│ │ └── visits.php # Contador de visitas +│ ├── lumina-cv-theme/ # Tema claro teal+ámbar, sidebar izquierdo +│ ├── cvfolio-theme/ # Tema oscuro glassmorphism (original) +│ └── apex-cv-theme/ # OBSOLETO — usar wp-content/themes/apex-cv +├── docs/ +│ └── instalacion-wordpress.md +├── .env # Variables de entorno (DB, puerto, etc.) +├── PROMPT_IA.md # ESTE ARCHIVO +└── README.md +``` + +> **NOTA IMPORTANTE:** El tema `apex-cv` vive directamente en +> `backend/wordpress/wp-content/themes/apex-cv/` (directorio real, no symlink) +> porque el directorio `frontend/apex-cv-theme/` tiene un problema de persistencia. +> Los demás temas activos usan symlinks desde `wp-content/themes/` hacia `frontend/`. + +--- + +## 3. Base de datos + +``` +Host: 192.168.222.20 +DB: paty.wordpress +Usuario: paty.wordpress +Password: paty.wordpress +Prefijo: cv_ → tabla principal: cv_options +``` + +Conexión de prueba: +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 +``` + +--- + +## 4. Arrancar / reiniciar el servidor + +```bash +# Ver si está corriendo +lsof -i tcp:8081 + +# Matar proceso existente +kill $(lsof -ti tcp:8081) + +# Arrancar (lee variables de .env automáticamente) +bash backend/start-wordpress.sh & +``` + +El servidor escucha en `0.0.0.0:8081` (todas las interfaces) gracias a `WP_HOST=0.0.0.0` en `.env`. + +--- + +## 5. Plugins activos y su función + +| Plugin | Propósito | Gratuito | +|--------|-----------|----------| +| `cmb2/init.php` | Campos personalizados en el admin (repeaters, options pages) | ✓ GPL | +| `pflores-cv-fields/pflores-cv-fields.php` | Páginas de opciones + función `pflores_get_cv_data()` + PDF | ✓ propio | + +**Activar / verificar plugins en la BD:** +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "SELECT option_value FROM cv_options WHERE option_name='active_plugins';" +``` + +El valor correcto debe ser: +``` +a:2:{i:0;s:13:"cmb2/init.php";i:1;s:39:"pflores-cv-fields/pflores-cv-fields.php";} +``` + +Si no, corregirlo: +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "UPDATE cv_options SET option_value='a:2:{i:0;s:13:\"cmb2/init.php\";i:1;s:39:\"pflores-cv-fields/pflores-cv-fields.php\";}' WHERE option_name='active_plugins';" +``` + +> ⚠ La serialización PHP es estricta: `s:N:` donde N es la longitud exacta en bytes. +> Siempre genera el valor con `php -r "echo serialize(['cmb2/init.php','pflores-cv-fields/pflores-cv-fields.php']);"`. + +--- + +## 6. Cambiar el tema activo + +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "UPDATE cv_options SET option_value='SLUG_TEMA' WHERE option_name IN('template','stylesheet');" +``` + +| Slug | Tema | +|------|------| +| `persona-cv` | **Activo por defecto** — Personage-style, secciones alternas, rojo | +| `lumina` | Claro, teal+ámbar, sidebar izquierdo, foto circular | +| `apex-cv` | Oscuro, cian eléctrico, sidebar fijo | +| `cvolio` | Oscuro glassmorphism, paleta café/tostado | + +--- + +## 7. Arquitectura de datos — CMB2 + +Todos los datos del CV se almacenan en `wp_options` (prefijo `cv_`) como arrays PHP serializados. Se acceden con `get_option('clave')`. + +| Clave `wp_options` | Contenido | +|--------------------|-----------| +| `pflores_perfil` | Nombre, cargo, intro, resumen, contacto, foto | +| `pflores_experiencia` | Array de cargos con bullets | +| `pflores_formacion` | Títulos académicos + certificaciones | +| `pflores_competencias` | Grupos de habilidades con porcentajes | +| `pflores_extras` | Métricas, highlights, impacto, voluntariado | + +**Función central de lectura:** +```php +pflores_get_cv_data(): array +// Retorna la misma estructura que todos los temas esperan. +// Si CMB2 no está disponible, devuelve pflores_cv_fallback_data(). +``` + +**Estructura del array retornado:** +```php +[ + 'hero' => ['name','role','role2','intro','location','email', + 'phone','linkedin','availability','focus','languages', + 'born','id','web','eyebrow'], + 'photo' => 'URL de la imagen de perfil (o vacío)', + 'summary' => ['title','body'], + 'metrics' => [['value','suffix','label'], ...], // 5 contadores + 'highlights' => [['value','suffix','label'], ...], // 4 logros grandes + 'impact' => [['title','body'], ...], // 3 áreas + 'experience' => [['period','title','company','location','lead','bullets'=>[...]], ...], + 'education' => [['year','title','institution','detail'], ...], + 'certifications' => [['year','title','issuer'], ...], + 'competencies' => ['Grupo' => [['name','pct'], ...], ...], + 'volunteer' => ['title','year','body'], + 'additional' => ['Idiomas'=>'...','Disponibilidad'=>'...','C.I.'=>'...', ...], +] +``` + +--- + +## 8. Editar datos del CV desde el admin + +URL del admin: `https://pflores.linkedcorp.net/wp-admin` +- Usuario: `pflores` +- Contraseña: en `.env` como `WP_ADMIN_PASSWORD` + +En el menú lateral aparece **"CV Patricia Flores"** con sub-páginas: +- **Perfil** → datos personales, foto, disponibilidad +- **Experiencia** → cargos (repeater, arrastrable) +- **Formación** → títulos + certificaciones +- **Competencias** → grupos de habilidades con porcentajes +- **Métricas / Impacto** → contadores, logros, áreas de impacto, voluntariado + +--- + +## 9. Agregar un nuevo campo al CV + +### 9a. Campo simple (texto, email, URL) + +**Paso 1** — Registrar el campo en `pflores-cv-fields.php` dentro de la función `pflores_register_pages()`: + +```php +// Ejemplo: añadir "Twitter/X" al perfil +$perfil->add_field([ + 'id' => 'pf_twitter', + 'name' => 'Twitter / X URL', + 'type' => 'text_url', +]); +``` + +**Paso 2** — Añadir la lectura en `pflores_get_cv_data()`: +```php +'hero' => [ + // ... campos existentes ... + 'twitter' => $perfil['pf_twitter'] ?? '', +], +``` + +**Paso 3** — Usarlo en el `front-page.php` de cada tema: +```php +$twitter = $hero['twitter'] ?? ''; +if ($twitter) : ?> + Twitter + +``` + +**Paso 4** — Si el campo debe aparecer en el PDF, añadirlo en `pdf-generator.php` dentro de `pflores_build_cv_html()`. + +--- + +### 9b. Nuevo grupo repetible (como experiencia o certificaciones) + +**Paso 1** — Registrar un nuevo CMB2 box con group en `pflores_register_pages()`: + +```php +$premios = new_cmb2_box([ + 'id' => 'pflores_premios', + 'title' => 'Premios y reconocimientos', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_premios', // nombre de la opción en BD + 'parent_slug' => 'pflores_perfil', // sub-menú bajo el menú principal + 'menu_title' => 'Premios', + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', +]); + +$gr = $premios->add_field([ + 'id' => 'pf_awards', + 'type' => 'group', + 'options' => ['group_title' => 'Premio {#}', 'add_button' => 'Añadir premio', + 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => true], +]); +$premios->add_group_field($gr, ['id' => 'year', 'name' => 'Año', 'type' => 'text']); +$premios->add_group_field($gr, ['id' => 'title', 'name' => 'Título', 'type' => 'text']); +$premios->add_group_field($gr, ['id' => 'org', 'name' => 'Organización','type' => 'text']); +``` + +**Paso 2** — Leer los datos en `pflores_get_cv_data()`: +```php +$premiosOpts = get_option('pflores_premios', []); +$awards = []; +foreach ((array) ($premiosOpts['pf_awards'] ?? []) as $item) { + $awards[] = ['year' => $item['year'] ?? '', 'title' => $item['title'] ?? '', 'org' => $item['org'] ?? '']; +} +// Agregar 'awards' => $awards al array retornado +``` + +**Paso 3** — Renderizarlo en `front-page.php` de cada tema. + +**Paso 4** — Poblar datos iniciales añadiendo al `populate-data.php`: +```php +update_option('pflores_premios', [ + 'pf_awards' => [ + ['year' => '2025', 'title' => 'Premio Excelencia', 'org' => 'Fundación FACES'], + ], +]); +``` + +--- + +## 10. Modificar datos existentes + +**Opción A** — Desde el admin de WordPress (recomendado): +- Ir a `wp-admin` → **CV Patricia Flores** → sub-página correspondiente → editar → Guardar. + +**Opción B** — Directo en BD (para migraciones o correcciones masivas): +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 -e " +UPDATE cv_options SET option_value = 'Nuevo valor' WHERE option_name = 'pflores_perfil'; +" +``` +> Para campos dentro de arrays (repeaters), es más fácil editar desde el admin o re-ejecutar `populate-data.php`. + +**Re-ejecutar el poblador de datos** (si se necesita resetear todo): +``` +https://pflores.linkedcorp.net/wp-content/plugins/pflores-cv-fields/populate-data.php?key=pflores2024 +``` +> ⚠ Esto sobreescribe TODOS los datos con los valores del archivo `populate-data.php`. + +--- + +## 11. Crear un nuevo tema + +### Convenciones obligatorias + +1. **Directorio**: Crear en `frontend/NOMBRE-cv-theme/` con subdirectorios `assets/{css,js,images,docs}` e `inc/`. +2. **Symlink**: `ln -s /ruta/absoluta/frontend/NOMBRE-cv-theme /ruta/wordpress/wp-content/themes/NOMBRE-cv` +3. **Text Domain**: usar `nombre` (minúsculas, sin guiones). +4. **Prefijo de funciones**: `nombre_` (ej: `nova_setup()`, `nova_render_nav()`). +5. **Prefijo CSS**: `nv-` (2-3 letras, sin conflictos con `pn-`, `lm-`, `ax-`, `cv-`). + +### Archivos mínimos requeridos + +``` +NOMBRE-cv-theme/ +├── style.css # Cabecera del tema (Theme Name, Text Domain, Version...) +├── functions.php # apex_setup, enqueue, nav helpers +├── header.php # + nav + botón PDF +├── footer.php # +├── front-page.php # TODA la lógica y HTML del CV +├── index.php # Fallback vacío o mínimo +└── inc/ + ├── cv-data.php # Función NOMBRE_cv_data() con fallback hardcodeado + ├── customizer.php # Opcional, puede estar vacío + └── visits.php # Contador de visitas (copiar de otro tema y renombrar funciones) +``` + +### Estructura obligatoria de `functions.php` + +```php + $items) : ?> +

+ + + +
+ + +``` + +### Botón de descarga PDF (obligatorio en todos los temas) + +```php + + + Descargar CV PDF + +``` + +### `inc/cv-data.php` — función de fallback + +Debe contener **todos los datos reales** de Patricia Flores, idénticos a los que están en la BD. Copiar la función de otro tema y renombrar a `NOMBRE_cv_data()`. + +> ⚠ Las competencias del fallback deben tener formato array: +> `['name' => 'Gestión operativa', 'pct' => 95]` + +### Activar el nuevo tema + +```bash +# 1. Crear el symlink +ln -s /ruta/absoluta/frontend/NOMBRE-cv-theme \ + /ruta/absoluta/backend/wordpress/wp-content/themes/NOMBRE-cv + +# 2. Activar en la BD +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "UPDATE cv_options SET option_value='NOMBRE-cv' WHERE option_name IN('template','stylesheet');" +``` + +> ⚠ **Evitar el slug `apex`** (existe un tema comercial con ese nombre en WordPress.org). +> Usar siempre slugs compuestos: `nova-cv`, `slate-cv`, `forma-cv`, etc. + +--- + +## 12. Crear / modificar menús de navegación + +Los menús de navegación son listas de anclas (`#seccion`) dentro del mismo tema. No se usan menús de WordPress por defecto. + +### Modificar los ítems del menú en un tema + +En `functions.php` de cada tema, hay una función `NOMBRE_render_nav()`: + +```php +function persona_render_nav(): string { + $items = [ + '#sobre-mi' => 'Sobre mí', + '#impacto' => 'Impacto', + '#experiencia' => 'Experiencia', + '#formacion' => 'Formación', + '#competencias' => 'Competencias', + '#certificaciones' => 'Certificaciones', + '#contacto' => 'Contacto', + ]; + // Para añadir un ítem: '#premios' => 'Premios', + // Para eliminar: quitar la línea correspondiente + // Para reordenar: cambiar el orden del array + ... +} +``` + +El `href` debe coincidir con el `id` de la sección en `front-page.php`: +```html +
+``` + +### Usar menús de WordPress (avanzado) + +Si se registra un menú en `functions.php`: +```php +register_nav_menus(['primary' => __('Menú principal', 'persona')]); +``` + +Se puede asignar desde `wp-admin` → **Apariencia → Menús** y el tema lo detectará automáticamente si `has_nav_menu('primary')` retorna `true`. + +--- + +## 13. Generador de PDF + +**Endpoint:** `https://pflores.linkedcorp.net/?cv_pdf=1` + +**Cómo funciona** (`pdf-generator.php`): +1. Se activa con `get_query_var('cv_pdf') === '1'` +2. Llama a `pflores_get_cv_data()` para obtener todos los datos actuales +3. Genera HTML limpio con CSS embebido (formato A4, 2 columnas) +4. Escribe el HTML en un archivo temporal +5. Ejecuta Chromium headless: `chromium --headless --no-sandbox --print-to-pdf=... file://...` +6. Lee el PDF resultante y lo devuelve con headers de descarga +7. Limpia los archivos temporales + +**Modificar el layout del PDF:** + +Todo el diseño está en la función `pflores_build_cv_html()` dentro de `pdf-generator.php`. Modificar el CSS dentro de la variable `$css` o el HTML de cada sección. + +**Añadir una nueva sección al PDF** (ej: Premios): + +```php +// En pflores_build_cv_html(), después de certificaciones: +$awardsHtml = ''; +foreach ($awards as $a) { + $awardsHtml .= '
...'; +} + +// Y en el HTML final: +'

Premios y reconocimientos

' . $awardsHtml . +``` + +--- + +## 14. Archivo `.env` + +``` +WP_ENV=local +WP_HOST=0.0.0.0 +WP_PORT=8081 +WP_URL=http://192.168.222.40:8081 + +DB_NAME=paty.wordpress +DB_USER=paty.wordpress +DB_PASSWORD=paty.wordpress +DB_HOST=192.168.222.20 +DB_PREFIX=cv_ + +WP_ADMIN_USER=pflores +WP_ADMIN_PASSWORD=HXk4boxuI1QYum3jKL +``` + +--- + +## 15. Configuración `wp-config.php` clave + +```php +// Auto-updates DESACTIVADAS (protege archivos de temas y plugins locales) +define('AUTOMATIC_UPDATER_DISABLED', true); +define('WP_AUTO_UPDATE_CORE', false); + +// URL dinámica — funciona con cualquier IP o dominio, y detrás de proxy SSL (NPM) +$_wpIsHttps = /* detecta X-Forwarded-Proto: https */; +define('WP_HOME', $_wpProto . '://' . $_wpHost); +define('WP_SITEURL', $_wpProto . '://' . $_wpHost); +``` + +--- + +## 16. Convenciones de código + +| Elemento | Convención | +|----------|-----------| +| PHP | `declare(strict_types=1)` en todos los archivos | +| Funciones WordPress | Usar `esc_html()`, `esc_url()`, `esc_attr()` en toda salida HTML | +| Prefijos | Funciones y opciones siempre con prefijo del plugin/tema para evitar conflictos | +| Comentarios | Solo cuando el "por qué" no es obvio. Sin comentarios de "qué hace" | +| Serialización PHP en SQL | Generar con `php -r "echo serialize([...]);"` — nunca manual | +| Auto-updates WP | **Siempre desactivadas** — no tocar `wp-config.php` en ese bloque | + +--- + +## 17. Tareas comunes — referencia rápida + +### Verificar que todo funciona +```bash +curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad" +# Debe retornar >= 1 +``` + +### Ver el tema activo +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "SELECT option_value FROM cv_options WHERE option_name='template';" 2>/dev/null +``` + +### Ver todos los datos del CV en BD +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "SELECT option_name, LENGTH(option_value) as bytes FROM cv_options WHERE option_name LIKE 'pflores_%' ORDER BY option_name;" 2>/dev/null +``` + +### Limpiar caché de WordPress (transients) +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "DELETE FROM cv_options WHERE option_name LIKE '_site_transient_%' OR option_name LIKE '_transient_%';" 2>/dev/null +``` + +### Verificar los 4 temas en un loop +```bash +for slug in persona-cv lumina apex-cv cvolio; do + mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "UPDATE cv_options SET option_value='$slug' WHERE option_name IN('template','stylesheet');" 2>/dev/null + sleep 0.4 + count=$(curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad" || true) + echo "[$slug] Patricia aparece: $count veces" +done +# Restaurar: +mysql ... -e "UPDATE cv_options SET option_value='persona-cv' WHERE option_name IN('template','stylesheet');" +``` + +### Probar la descarga del PDF +```bash +curl -sI http://127.0.0.1:8081/?cv_pdf=1 | grep -E "Content-Type|Content-Disposition" +# Debe mostrar: application/pdf y attachment; filename="CV_Patricia_Flores_Sanchez.pdf" +``` + +### Comprobar PHP syntax de un tema +```bash +php -l /ruta/al/tema/front-page.php +php -l /ruta/al/tema/functions.php +``` + +--- + +## 18. Errores frecuentes y soluciones + +| Error | Causa probable | Solución | +|-------|---------------|----------| +| Página en blanco o "error crítico" | PHP fatal en el tema | Ver `debug.log`, buscar la línea del error | +| CSS sin cargar / página sin estilos | Mixed content (HTTP vs HTTPS) | Verificar `wp-config.php` — bloque de proxy SSL | +| CMB2 / plugin no carga | Serialización incorrecta en `active_plugins` | Regenerar con `php -r "echo serialize([...]);"` | +| `pflores_get_cv_data()` retorna vacío | Datos no guardados en BD | Re-ejecutar `populate-data.php?key=pflores2024` + limpiar caché | +| Apex-cv vacío o sin archivos | Tema apex-cv depende de directorio real, no symlink | Los archivos van en `wp-content/themes/apex-cv/`, no en `frontend/` | +| PDF vacío o error 500 | Chromium no encontrado o falla | `which chromium chromium-browser google-chrome` | +| `update_option` no guarda | Caché de WordPress | Limpiar transients (ver sección 17) | + +--- + +## 19. Acerca de los temas — resumen visual + +| Tema | Estilo | Layout | Fuente | Acento | +|------|--------|--------|--------|--------| +| `persona-cv` | Personage/Pixflow, secciones alternas | Full-width, nav sticky top | Raleway + Open Sans | Rojo `#c0392b` | +| `lumina` | Claro, moderno, alegre | Sidebar IZQUIERDO fijo | Plus Jakarta Sans | Teal `#0d9488` + Ámbar | +| `apex-cv` | Oscuro, serio, editorial | Sidebar IZQUIERDO fijo | Space Grotesk | Cian `#22d3ee` + Dorado | +| `cvolio` | Oscuro glassmorphism | Hero dividido + sidebar DERECHO | Manrope + Cormorant | Café/tostado | + +--- + +## 20. Notas importantes para la IA asistente + +1. **No usar ACF** (Advanced Custom Fields). Tiene Repeater solo en versión de pago. Usar CMB2. +2. **No instalar plugins de pago**. Todo debe ser 100 % gratuito. +3. **No tocar el bloque de auto-updates** en `wp-config.php`. +4. **Verificar serialización PHP** antes de cualquier `UPDATE` en `active_plugins` o similar. +5. **Limpiar caché** después de modificar datos en BD directamente. +6. **La foto de perfil** es `assets/images/patricia-flores-profile.jpeg` en cada tema — NO usar el placeholder SVG como defecto. +7. **El CV en docx** (`CV_Patricia_Flores_Sanchez_ES_FINAL.docx`) es solo para descarga — el contenido de la web se gestiona por CMB2. +8. **El PDF** se genera dinámicamente con Chromium desde `/?cv_pdf=1` — no hay PDF estático. +9. **Los 4 temas leen de CMB2** mediante `pflores_get_cv_data()`. Si esa función no existe (plugin inactivo), cada tema usa su propio fallback hardcodeado. +10. **Siempre verificar** con `curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad"` que el sitio responde correctamente después de cualquier cambio. + +--- + +## 21. Git y Gitea — versionar y subir + +### Repositorio + +- **Remoto:** `https://gitea.linkedcorp.net/mgonzalez/pflores.linkedcorp.net.git` +- **Rama:** `main` +- **Credenciales:** archivo local `.token_gitea` (nunca versionado) + +### Flujo estándar de commit + push + +```bash +# 1. Revisar qué cambió +git status +git diff --stat + +# 2. Agregar archivos — SELECTIVO (nunca git add -A sin revisar) +git add frontend/ # temas propios +git add backend/wordpress/wp-content/plugins/pflores-cv-fields/ +git add backend/wordpress/wp-content/themes/apex-cv/ +git add backend/wordpress/wp-config.php +git add CHANGELOG.md VERSION README.md PROMPT_IA.md + +# 3. Confirmar que .env y .token_gitea NO están en staging +git status | grep -E "\.env|\.token" # debe retornar vacío + +# 4. Commit +git commit -m "tipo: descripción corta" +# Prefijos: feat: / fix: / docs: / chore: / style: + +# 5. Push +git push origin main +``` + +### Credenciales para push + +Al hacer `git push` por primera vez pedirá usuario y contraseña: +- **Usuario:** `mgonzalez` +- **Contraseña:** Personal Access Token (NO la contraseña web de Gitea) + +Crear token: `https://gitea.linkedcorp.net/user/settings/applications` + +Para no escribir credenciales cada vez: +```bash +git config credential.helper store +# La primera vez las guarda; las siguientes son automáticas +``` + +Guardar también en `.token_gitea` (local, nunca versionar): +```text +# Token gitea +usuario: mgonzalez +token: [token de gitea.linkedcorp.net] +``` + +### Qué SE versiona vs qué NO + +**SE versiona:** +- `frontend/` (todos los temas propios) +- `backend/wordpress/wp-content/plugins/pflores-cv-fields/` +- `backend/wordpress/wp-content/themes/apex-cv/` +- `backend/wordpress/wp-config.php` +- `backend/start-wordpress.sh`, `docs/`, `*.md`, `VERSION` +- `CV_Patricia_Flores_Sanchez_ES_FINAL.docx`, imágenes del CV + +**NO se versiona:** +- `.env`, `.token_gitea`, `LOCAL_DEPLOY_CONTEXT.md` +- WordPress core (`wp-admin/`, `wp-includes/`) +- Plugins de terceros (`cmb2/`, `wp-mail-smtp/`) +- Temas por defecto de WP (`twenty*/`) +- `wp-content/debug.log`, `uploads/`, `languages/` + +### Versionar antes de hacer push + +Ver `VERSIONADO.md` para decidir si subir `X`, `Y` o `Z`: +```bash +# Actualizar VERSION +echo "1.0.1" > VERSION + +# Anotar cambio en CHANGELOG.md +# Luego commit y push normal +``` diff --git a/README.md b/README.md index 38f43f1..6227325 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,89 @@ -# CVolio WP +# pflores.linkedcorp.net -Tema WordPress moderno para hoja de vida, preparado para pruebas locales en este directorio. +Sitio web de currículum ejecutivo de **Patricia Soledad Flores Sánchez** — Gerente / Jefa de Operaciones Financieras, sector financiero y microfinanzas. + +## Stack + +- **CMS:** WordPress 6.x (PHP 8.4, servidor PHP integrado) +- **Base de datos:** MariaDB en `192.168.222.20` +- **Campos del CV:** CMB2 (100 % gratuito, GPL) +- **PDF:** Chromium headless (`/?cv_pdf=1`) +- **Email:** WP Mail SMTP (configurable) +- **Deploy:** Debian 12 · Nginx Proxy Manager → `192.168.222.40:8081` +- **URL pública:** `https://pflores.linkedcorp.net` ## Estructura -- `frontend/cvfolio-theme`: tema editable. -- `backend/wordpress`: instalación local de WordPress. -- `docs/`: notas de instalación y uso. - -## Estado actual - -- WordPress descargado en local. -- Tema base responsive creado. -- Puerto sugerido preparado: `8081`. -- Pendiente para ejecución completa: extensiones PHP de WordPress y una base MariaDB/MySQL disponible. +``` +pflores.linkedcorp.net/ +├── frontend/ +│ ├── persona-cv-theme/ ← ACTIVO — Personage-style, rojo, timeline central +│ ├── lumina-cv-theme/ ← Claro, teal + ámbar, sidebar izquierdo +│ └── cvfolio-theme/ ← Oscuro, glassmorphism original +├── backend/ +│ ├── start-wordpress.sh +│ └── wordpress/ +│ ├── wp-config.php +│ └── wp-content/ +│ ├── plugins/pflores-cv-fields/ ← plugin propio: CMB2 + PDF +│ └── themes/apex-cv/ ← tema oscuro cian +├── docs/ +├── .env ← local, no versionado +├── .env.example +├── VERSION +├── CHANGELOG.md +├── PROMPT_IA.md ← guía completa para IA +└── README.md +``` ## Arranque rápido -1. Copia `.env.example` a `.env`. -2. Ajusta credenciales de base de datos. -3. Revisa `docs/instalacion-wordpress.md`. -4. Ejecuta `backend/start-wordpress.sh`. +```bash +# 1. Copiar variables de entorno +cp .env.example .env +# 2. Editar .env con credenciales reales +# 3. Arrancar el servidor WordPress +bash backend/start-wordpress.sh & +# 4. Abrir http://127.0.0.1:8081 +``` -## Git +El servidor escucha en `0.0.0.0:8081` (accesible desde la red local). -El repositorio fue inicializado localmente. Falta enlazar remoto si luego quieres subirlo a Gitea. +## Temas disponibles + +| Slug | Descripción | Estado | +|------|-------------|--------| +| `persona-cv` | Personage-style, rojo, secciones alternas, Raleway | **Activo** | +| `lumina` | Claro, teal+ámbar, sidebar izq., Plus Jakarta Sans | Disponible | +| `apex-cv` | Oscuro, cian eléctrico, sidebar fijo, Space Grotesk | Disponible | +| `cvolio` | Oscuro glassmorphism, café/tostado, Manrope | Disponible | + +Cambiar tema activo: +```bash +mysql -u "paty.wordpress" -p"paty.wordpress" -h 192.168.222.20 "paty.wordpress" --ssl=0 \ + -e "UPDATE cv_options SET option_value='SLUG' WHERE option_name IN('template','stylesheet');" +``` + +## Editar el CV + +Acceder al admin: `https://pflores.linkedcorp.net/wp-admin` +- Usuario: `pflores` +- Menú: **CV Patricia Flores** → sub-páginas por sección + +Los datos se almacenan en CMB2 (opciones de WordPress). Cada cambio se refleja automáticamente en la web y en el PDF descargable. + +## Descargar CV en PDF + +El PDF se genera dinámicamente con Chromium headless: +``` +GET /?cv_pdf=1 → descarga CV_Patricia_Flores_Sanchez.pdf +``` + +## Documentación relacionada + +- `PROMPT_IA.md` — guía completa para asistentes de IA y desarrolladores +- `DEPLOY_WEB.md` — pasos para versionar y desplegar +- `PRODUCCION.md` — estado del servidor de producción +- `VERSIONADO.md` — reglas de versiones +- `CHANGELOG.md` — historial de cambios +- `docs/instalacion-wordpress.md` — configuración local diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/VERSIONADO.md b/VERSIONADO.md new file mode 100644 index 0000000..8f04d08 --- /dev/null +++ b/VERSIONADO.md @@ -0,0 +1,50 @@ +# Versionado — pflores.linkedcorp.net + +## Esquema: `X.Y.Z` + +| Nivel | Cuándo subirlo | +|-------|---------------| +| `X` | Rediseño completo, cambio de CMS, rotura de compatibilidad mayor | +| `Y` | Nuevo tema completo, nueva sección del CV, nuevo módulo funcional | +| `Z` | Correcciones visuales, ajuste de texto, nuevo campo, fix de bug | + +## Qué se versiona + +- `frontend/` — todos los temas WordPress propios +- `backend/wordpress/wp-content/plugins/pflores-cv-fields/` — plugin propio +- `backend/wordpress/wp-content/themes/apex-cv/` — tema en directorio real +- `backend/wordpress/wp-config.php` — sin credenciales (usa variables de entorno) +- `backend/start-wordpress.sh` +- `docs/` +- `README.md`, `PROMPT_IA.md`, `CHANGELOG.md`, `VERSION`, `VERSIONADO.md` +- `PRODUCCION.md`, `DEPLOY_WEB.md` +- `.env.example` +- `.gitignore` +- Archivos de assets del CV: `CV_Patricia_Flores_Sanchez_ES_FINAL.docx`, imágenes QR/perfil + +## Qué NO se versiona + +- `.env` — credenciales locales +- `.token_gitea` — token de acceso Gitea +- `backend/wordpress/wp-admin/` y `wp-includes/` — núcleo de WordPress +- `backend/wordpress/wp-content/plugins/cmb2/` — plugin de terceros descargado +- `backend/wordpress/wp-content/plugins/wp-mail-smtp/` — plugin de terceros +- `backend/wordpress/wp-content/themes/twenty*/` — temas por defecto +- `backend/wordpress/wp-content/uploads/` — media subida +- `backend/wordpress/wp-content/debug.log` +- `.DS_Store`, `.claude/` +- `LOCAL_DEPLOY_CONTEXT.md` — contexto local con credenciales + +## Checklist antes de cada commit + +1. `git status` — revisar qué se incluye +2. Verificar que `.env` y `.token_gitea` NO están en staging +3. Si hay cambios funcionales → actualizar `VERSION` y `CHANGELOG.md` +4. Mensaje de commit claro: `feat:`, `fix:`, `chore:`, `docs:` + +## Checklist antes de subir a Gitea (`git push`) + +1. Todo el checklist de commit cumplido +2. Site funcionando: `curl -s http://127.0.0.1:8081/ | grep -c "Patricia Soledad"` +3. PDF funcionando: `curl -sI http://127.0.0.1:8081/?cv_pdf=1 | grep Content-Type` +4. Ver `DEPLOY_WEB.md` para pasos completos diff --git a/backend/start-wordpress.sh b/backend/start-wordpress.sh index 513972d..7b13f99 100755 --- a/backend/start-wordpress.sh +++ b/backend/start-wordpress.sh @@ -17,9 +17,10 @@ WP_HOST="${WP_HOST:-127.0.0.1}" required_modules=("mysqli" "dom") missing_modules=() +php_modules="$(php -m | tr '[:upper:]' '[:lower:]')" for module in "${required_modules[@]}"; do - if ! php -m | grep -qi "^${module}$"; then + if ! printf '%s\n' "$php_modules" | grep -qx "$module"; then missing_modules+=("$module") fi done diff --git a/backend/wordpress/wp-config.php b/backend/wordpress/wp-config.php index 6d10f0e..088b4ea 100644 --- a/backend/wordpress/wp-config.php +++ b/backend/wordpress/wp-config.php @@ -25,11 +25,34 @@ define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); -if (getenv('WP_URL')) { - define('WP_HOME', getenv('WP_URL')); - define('WP_SITEURL', getenv('WP_URL')); +// Desactivar actualizaciones automáticas para proteger archivos de temas y plugins locales +define('AUTOMATIC_UPDATER_DISABLED', true); +define('WP_AUTO_UPDATE_CORE', false); + +// URL dinámica con soporte de reverse proxy (NPM, Nginx, Traefik…). +// NPM termina SSL y reenvía como HTTP, pero manda X-Forwarded-Proto: https. +$_wpIsHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') + || (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'on'); + +// X-Forwarded-Host contiene el dominio público cuando hay proxy. +// Fallback a HTTP_HOST (que NPM también pasa correctamente). +$_wpHost = $_SERVER['HTTP_X_FORWARDED_HOST'] + ?? $_SERVER['HTTP_HOST'] + ?? '127.0.0.1:8081'; + +$_wpProto = $_wpIsHttps ? 'https' : 'http'; +define('WP_HOME', $_wpProto . '://' . $_wpHost); +define('WP_SITEURL', $_wpProto . '://' . $_wpHost); + +// Necesario para que WordPress confíe en el header del proxy +// y no entre en bucle de redirección HTTP→HTTPS. +if ($_wpIsHttps) { + $_SERVER['HTTPS'] = 'on'; } +unset($_wpIsHttps, $_wpHost, $_wpProto); + if (! defined('ABSPATH')) { define('ABSPATH', __DIR__ . '/'); } diff --git a/backend/wordpress/wp-content/plugins/pflores-cv-fields/pdf-generator.php b/backend/wordpress/wp-content/plugins/pflores-cv-fields/pdf-generator.php new file mode 100644 index 0000000..07450ca --- /dev/null +++ b/backend/wordpress/wp-content/plugins/pflores-cv-fields/pdf-generator.php @@ -0,0 +1,499 @@ + 500]); + } + + $cv = function_exists('pflores_get_cv_data') ? pflores_get_cv_data() : []; + $html = pflores_build_cv_html($cv); + $ts = time(); + $tmpDir = sys_get_temp_dir(); + $htmlFile = $tmpDir . '/cv_pf_' . $ts . '.html'; + $pdfFile = $tmpDir . '/cv_pf_' . $ts . '.pdf'; + + file_put_contents($htmlFile, $html); + + $cmd = escapeshellcmd($chromium) + . ' --headless --no-sandbox --disable-gpu' + . ' --run-all-compositor-stages-before-draw' + . ' --print-to-pdf=' . escapeshellarg($pdfFile) + . ' --print-to-pdf-no-header' + . ' "file://' . $htmlFile . '"' + . ' 2>/dev/null'; + + exec($cmd); + @unlink($htmlFile); + + if (! file_exists($pdfFile) || filesize($pdfFile) < 1024) { + @unlink($pdfFile); + wp_die('No se pudo generar el PDF.', 'Error PDF', ['response' => 500]); + } + + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; filename="CV_Patricia_Flores_Sanchez.pdf"'); + header('Content-Length: ' . filesize($pdfFile)); + header('Cache-Control: no-cache, no-store, must-revalidate'); + readfile($pdfFile); + @unlink($pdfFile); + exit; +} + +function pflores_find_chromium(): string +{ + foreach (['chromium', 'chromium-browser', 'google-chrome', 'google-chrome-stable'] as $bin) { + $path = trim((string) shell_exec('which ' . escapeshellarg($bin) . ' 2>/dev/null')); + if ($path !== '' && file_exists($path)) { + return $path; + } + } + return ''; +} + +/* ═══════════════════════════════════════════════════════════ + CONSTRUCTOR HTML — réplica fiel del DOCX + ═══════════════════════════════════════════════════════════ */ +function pflores_build_cv_html(array $cv): string +{ + /* ── datos ── */ + $hero = $cv['hero'] ?? []; + $metrics = $cv['metrics'] ?? []; + $highlights = $cv['highlights'] ?? []; + $summary = $cv['summary'] ?? []; + $impact = $cv['impact'] ?? []; + $experience = $cv['experience'] ?? []; + $education = $cv['education'] ?? []; + $certifications = $cv['certifications'] ?? []; + $competencies = $cv['competencies'] ?? []; + $volunteer = $cv['volunteer'] ?? []; + $additional = $cv['additional'] ?? []; + + $name = $hero['name'] ?? ''; + $role = $hero['role'] ?? ''; + $role2 = $hero['role2'] ?? ''; + $location = $hero['location'] ?? ''; + $email = $hero['email'] ?? ''; + $phone = $hero['phone'] ?? ''; + $linkedin = $hero['linkedin'] ?? ''; + $avail = $hero['availability'] ?? ''; + $focus = $hero['focus'] ?? ''; + $langs = $hero['languages'] ?? ''; + $web = $hero['web'] ?? ''; + $intro = $hero['intro'] ?? ''; + + /* ── helpers ── */ + $h = fn(string $s): string => htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + $nl = "\n"; + + /* ── estilos (replica el DOCX) ── */ + $css = ' +* { box-sizing: border-box; margin: 0; padding: 0; } + +@page { + size: A4; + margin: 16mm 18mm 16mm 18mm; +} + +body { + font-family: Arial, Helvetica, sans-serif; + font-size: 9pt; + color: #3A3A3A; + background: #ffffff; + line-height: 1.45; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +/* ── CABECERA ── */ +.cv-name { + font-size: 19pt; + font-weight: 700; + color: #1B3A5C; + line-height: 1.1; + margin-bottom: 3px; +} +.cv-role { + font-size: 10pt; + font-weight: 700; + color: #1B3A5C; + margin-bottom: 2px; +} +.cv-contact { + font-size: 8pt; + color: #666666; + margin-bottom: 10px; +} +.cv-contact span { margin: 0 4px; } + +/* ── SECCIONES ── */ +.sec { + margin-bottom: 10px; +} +.sec-title { + font-size: 10pt; + font-weight: 700; + color: #1B3A5C; + text-transform: uppercase; + letter-spacing: .04em; + border-bottom: 1.5px solid #1B3A5C; + padding-bottom: 2px; + margin-bottom: 6px; +} + +/* ── PERFIL ── */ +.profile-text { + font-size: 9pt; + color: #3A3A3A; + line-height: 1.55; + text-align: justify; +} + +/* ── EXPERIENCIA ── */ +.exp-item { margin-bottom: 8px; } +.exp-header { + display: flex; + justify-content: space-between; + align-items: baseline; +} +.exp-title { + font-size: 10pt; + font-weight: 700; + color: #1B3A5C; +} +.exp-period { + font-size: 9pt; + color: #3A3A3A; + font-style: italic; + white-space: nowrap; + margin-left: 8px; +} +.exp-company { + font-size: 9pt; + color: #666666; + margin-bottom: 3px; +} +.exp-desc { + font-size: 9pt; + color: #3A3A3A; + margin-bottom: 3px; + font-style: italic; +} +.exp-bullets { + margin: 0; + padding-left: 0; + list-style: none; +} +.exp-bullets li { + font-size: 9pt; + color: #3A3A3A; + padding-left: 12px; + position: relative; + margin-bottom: 2px; + line-height: 1.45; + text-align: justify; +} +.exp-bullets li::before { + content: "•"; + position: absolute; + left: 0; + color: #1B3A5C; + font-weight: 700; +} + +/* ── FORMACIÓN ── */ +.edu-item { + margin-bottom: 6px; + display: flex; + justify-content: space-between; + align-items: flex-start; +} +.edu-left { flex: 1; } +.edu-degree { + font-size: 9pt; + font-weight: 700; + color: #1B3A5C; +} +.edu-institution { + font-size: 8pt; + color: #666666; + margin-top: 1px; +} +.edu-detail { + font-size: 8pt; + color: #666666; + font-style: italic; + margin-top: 1px; +} +.edu-year { + font-size: 9pt; + font-weight: 700; + color: #1B3A5C; + white-space: nowrap; + margin-left: 8px; +} + +/* ── CERTIFICACIONES ── */ +.cert-item { + display: flex; + justify-content: space-between; + font-size: 8pt; + color: #3A3A3A; + margin-bottom: 2.5px; + line-height: 1.4; +} +.cert-title { flex: 1; padding-right: 8px; } +.cert-year { color: #1B3A5C; font-weight: 700; white-space: nowrap; } + +/* ── COMPETENCIAS ── */ +.comp-line { + font-size: 9pt; + color: #3A3A3A; + margin-bottom: 3px; + line-height: 1.45; +} +.comp-line strong { color: #1B3A5C; } + +/* ── VOLUNTARIADO / ADICIONAL ── */ +.vol-header { + display: flex; + justify-content: space-between; + align-items: baseline; +} +.vol-title { font-size: 9pt; font-weight: 700; color: #1B3A5C; } +.vol-year { font-size: 9pt; font-weight: 700; color: #1B3A5C; } +.vol-body { font-size: 9pt; color: #3A3A3A; line-height: 1.45; margin-top: 1px; } + +.add-line { + font-size: 9pt; + color: #3A3A3A; + margin-bottom: 2px; +} +.add-line strong { color: #1B3A5C; } + +.cv-footer-line { + font-size: 8pt; + color: #999999; + text-align: right; + margin-top: 8px; + border-top: .5px solid #dddddd; + padding-top: 4px; +} +'; + + /* ═══════════ SECCIÓN: PERFIL PROFESIONAL ═══════════ */ + + // Combina intro + summary (como el DOCX) + $summaryBody = $summary['body'] ?? ''; + $profileFull = trim($intro); + if ($summaryBody && $summaryBody !== $intro) { + // Evitar duplicar si intro ya contiene el resumen + if (strpos($profileFull, substr($summaryBody, 0, 30)) === false) { + $profileFull .= ' ' . $summaryBody; + } + } + + /* ═══════════ SECCIÓN: EXPERIENCIA ═══════════ */ + $expHtml = ''; + foreach ($experience as $job) { + $expHtml .= '
'; + $expHtml .= '
'; + $expHtml .= '' . $h($job['title'] ?? '') . ''; + $expHtml .= '' . $h($job['period'] ?? '') . ''; + $expHtml .= '
'; + + $company = $h($job['company'] ?? ''); + $loc = $h($job['location'] ?? ''); + if ($company || $loc) { + $expHtml .= '

' . $company . ($loc ? '  |  ' . $loc : '') . '

'; + } + + if (! empty($job['lead'])) { + $expHtml .= '

' . $h($job['lead']) . '

'; + } + + if (! empty($job['bullets'])) { + $expHtml .= '
    '; + foreach ($job['bullets'] as $bullet) { + $expHtml .= '
  • ' . $h($bullet) . '
  • '; + } + $expHtml .= '
'; + } + $expHtml .= '
'; + } + + /* ═══════════ SECCIÓN: FORMACIÓN ═══════════ */ + $eduHtml = ''; + foreach ($education as $edu) { + $eduHtml .= '
'; + $eduHtml .= '
'; + $eduHtml .= '

' . $h($edu['title'] ?? '') . '

'; + $eduHtml .= '

' . $h($edu['institution'] ?? '') . '

'; + if (! empty($edu['detail'])) { + $eduHtml .= '

' . $h($edu['detail']) . '

'; + } + $eduHtml .= '
'; + $eduHtml .= '' . $h($edu['year'] ?? '') . ''; + $eduHtml .= '
'; + } + + /* ═══════════ SECCIÓN: CERTIFICACIONES ═══════════ */ + $certHtml = ''; + foreach ($certifications as $cert) { + $title = $h($cert['title'] ?? ''); + $issuer = $h($cert['issuer'] ?? ''); + $year = $h($cert['year'] ?? ''); + $certHtml .= '
'; + $certHtml .= '' . $title . ($issuer ? ' — ' . $issuer : '') . ''; + $certHtml .= '' . $year . ''; + $certHtml .= '
'; + } + + /* ═══════════ SECCIÓN: COMPETENCIAS ═══════════ */ + $compHtml = ''; + foreach ($competencies as $groupName => $items) { + $skills = []; + foreach ($items as $item) { + $skills[] = is_array($item) ? ($item['name'] ?? '') : (string) $item; + } + $skillsStr = implode(' · ', array_filter($skills)); + $compHtml .= '

' . $h($groupName) . ':  ' . $h($skillsStr) . '

'; + } + + /* ═══════════ SECCIÓN: VOLUNTARIADO ═══════════ */ + $volHtml = ''; + if (! empty($volunteer['title'])) { + $volHtml .= '
'; + $volHtml .= '' . $h($volunteer['title']) . ''; + $volHtml .= '' . $h($volunteer['year'] ?? '') . ''; + $volHtml .= '
'; + if (! empty($volunteer['body'])) { + $volHtml .= '

' . $h($volunteer['body']) . '

'; + } + } + + /* ═══════════ SECCIÓN: INFORMACIÓN ADICIONAL ═══════════ */ + $addHtml = ''; + $addLines = [ + 'Idiomas' => $langs, + 'Disponibilidad' => $avail, + 'Enfoque' => $focus, + ]; + // Añadir los del array additional filtrando redundancias + foreach ($additional as $k => $v) { + if ($v !== '' && ! in_array($k, ['Disponibilidad', 'Idiomas'], true)) { + $addLines[$k] = $v; + } + } + foreach ($addLines as $k => $v) { + if ($v === '') continue; + $addHtml .= '

' . $h($k) . ': ' . $h($v) . '

'; + } + if ($web) { + $addHtml .= '

Web: ' . $h($web) . '

'; + } + + /* ═══════════ LÍNEA DE CONTACTO (cabecera) ═══════════ */ + $contactParts = []; + if ($email) $contactParts[] = 'Correo: ' . $email; + if ($phone) $contactParts[] = 'Tel.: ' . $phone; + if ($linkedin) { + // Mostrar URL limpia sin https://www. + $lnDisplay = preg_replace('#^https?://(www\.)?#', '', urldecode($linkedin)); + $contactParts[] = 'LinkedIn: ' . $lnDisplay; + } + if ($location) $contactParts[] = $location; + $contactLine = implode(' | ', $contactParts); + + /* ═══════════ TÍTULO del documento ═══════════ */ + $fullRole = $role . ($role2 ? ' | ' . $role2 : ''); + $year = gmdate('Y'); + + /* ═══════════ HTML COMPLETO ═══════════ */ + return ' + + + +CV — ' . $h($name) . ' + + + + + +

' . $h($name) . '

+

' . $h($fullRole) . '

+

' . $h($contactLine) . '

+ + +
+

Perfil profesional

+

' . $h($profileFull) . '

+
+ + +
+

Experiencia profesional

+ ' . $expHtml . ' +
+ + +
+

Formación académica

+ ' . $eduHtml . ' +
+ + +
+

Certificaciones relevantes

+ ' . $certHtml . ' +
+ + +' . ($compHtml ? '
+

Competencias clave

+ ' . $compHtml . ' +
' : '') . ' + + +' . ($volHtml ? '
+

Voluntariado

+ ' . $volHtml . ' +
' : '') . ' + + +' . ($addHtml ? '
+

Información adicional

+ ' . $addHtml . ' +
' : '') . ' + + + + +'; +} diff --git a/backend/wordpress/wp-content/plugins/pflores-cv-fields/pflores-cv-fields.php b/backend/wordpress/wp-content/plugins/pflores-cv-fields/pflores-cv-fields.php new file mode 100644 index 0000000..7192630 --- /dev/null +++ b/backend/wordpress/wp-content/plugins/pflores-cv-fields/pflores-cv-fields.php @@ -0,0 +1,350 @@ + 'pflores_perfil', + 'title' => 'Perfil y contacto', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_perfil', + 'menu_title' => 'CV Patricia Flores', + 'icon_url' => 'dashicons-id-alt', + 'position' => 3, + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', + ]); + + $perfil->add_field(['id' => 'pf_name', 'name' => 'Nombre completo', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_role', 'name' => 'Cargo principal', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_role2', 'name' => 'Sector / subtítulo', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_intro', 'name' => 'Introducción', 'type' => 'textarea', 'attributes' => ['rows' => 4]]); + $perfil->add_field(['id' => 'pf_summary_title', 'name' => 'Título del perfil', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_summary_body', 'name' => 'Resumen profesional', 'type' => 'textarea', 'attributes' => ['rows' => 5]]); + $perfil->add_field(['id' => 'pf_location', 'name' => 'Ubicación', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_email', 'name' => 'Correo electrónico', 'type' => 'text_email']); + $perfil->add_field(['id' => 'pf_phone', 'name' => 'Teléfono', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_linkedin', 'name' => 'LinkedIn URL', 'type' => 'text_url']); + $perfil->add_field(['id' => 'pf_availability', 'name' => 'Disponibilidad', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_focus', 'name' => 'Enfoque profesional', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_languages', 'name' => 'Idiomas', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_photo', 'name' => 'Foto de perfil', 'type' => 'file', 'options' => ['url' => false], 'text' => ['add_upload_file_text' => 'Seleccionar foto']]); + $perfil->add_field(['id' => 'pf_born', 'name' => 'Fecha de nacimiento', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_ci', 'name' => 'Cédula de identidad', 'type' => 'text']); + $perfil->add_field(['id' => 'pf_web', 'name' => 'Web profesional', 'type' => 'text']); + + /* ── EXPERIENCIA ── */ + $exp = new_cmb2_box([ + 'id' => 'pflores_experiencia', + 'title' => 'Experiencia profesional', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_experiencia', + 'parent_slug' => 'pflores_perfil', + 'menu_title' => 'Experiencia', + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', + ]); + + $exp_group = $exp->add_field([ + 'id' => 'pf_experience', + 'type' => 'group', + 'description' => 'Cargos del más reciente al más antiguo', + 'options' => [ + 'group_title' => 'Cargo {#}', + 'add_button' => 'Añadir cargo', + 'remove_button' => 'Eliminar cargo', + 'sortable' => true, + 'closed' => true, + ], + ]); + + $exp->add_group_field($exp_group, ['id' => 'period', 'name' => 'Período (ej: Abr. 2022 – Presente)', 'type' => 'text']); + $exp->add_group_field($exp_group, ['id' => 'title', 'name' => 'Cargo', 'type' => 'text']); + $exp->add_group_field($exp_group, ['id' => 'company', 'name' => 'Institución / Empresa', 'type' => 'text']); + $exp->add_group_field($exp_group, ['id' => 'location', 'name' => 'Lugar', 'type' => 'text']); + $exp->add_group_field($exp_group, ['id' => 'lead', 'name' => 'Descripción breve', 'type' => 'textarea_small']); + $exp->add_group_field($exp_group, [ + 'id' => 'bullets_text', + 'name' => 'Logros y actividades', + 'type' => 'textarea', + 'description' => 'Un punto por línea. Sin guiones ni asteriscos.', + 'attributes' => ['rows' => 8], + ]); + + /* ── FORMACIÓN Y CERTIFICACIONES ── */ + $form = new_cmb2_box([ + 'id' => 'pflores_formacion', + 'title' => 'Formación y certificaciones', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_formacion', + 'parent_slug' => 'pflores_perfil', + 'menu_title' => 'Formación', + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', + ]); + + $edu_group = $form->add_field([ + 'id' => 'pf_education', + 'type' => 'group', + 'options' => ['group_title' => 'Título {#}', 'add_button' => 'Añadir título', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => true], + ]); + $form->add_group_field($edu_group, ['id' => 'year', 'name' => 'Año', 'type' => 'text', 'attributes' => ['size' => 6]]); + $form->add_group_field($edu_group, ['id' => 'title', 'name' => 'Título', 'type' => 'text']); + $form->add_group_field($edu_group, ['id' => 'institution', 'name' => 'Institución','type' => 'text']); + $form->add_group_field($edu_group, ['id' => 'detail', 'name' => 'Detalle / Nota de reconocimiento', 'type' => 'textarea_small']); + + $cert_group = $form->add_field([ + 'id' => 'pf_certifications', + 'type' => 'group', + 'description' => 'Certificaciones relevantes', + 'options' => ['group_title' => 'Certificación {#}', 'add_button' => 'Añadir certificación', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => true], + ]); + $form->add_group_field($cert_group, ['id' => 'year', 'name' => 'Año', 'type' => 'text', 'attributes' => ['size' => 6]]); + $form->add_group_field($cert_group, ['id' => 'title', 'name' => 'Título', 'type' => 'text']); + $form->add_group_field($cert_group, ['id' => 'issuer', 'name' => 'Emisor', 'type' => 'text']); + + /* ── COMPETENCIAS ── */ + $comp = new_cmb2_box([ + 'id' => 'pflores_competencias', + 'title' => 'Competencias y habilidades', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_competencias', + 'parent_slug' => 'pflores_perfil', + 'menu_title' => 'Competencias', + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', + ]); + + $comp_group = $comp->add_field([ + 'id' => 'pf_competencies', + 'type' => 'group', + 'options' => ['group_title' => 'Grupo {#}', 'add_button' => 'Añadir grupo', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => true], + ]); + $comp->add_group_field($comp_group, ['id' => 'group_name', 'name' => 'Nombre del grupo (ej: Finanzas y Operaciones)', 'type' => 'text']); + $comp->add_group_field($comp_group, [ + 'id' => 'skills_text', + 'name' => 'Habilidades', + 'type' => 'textarea', + 'description' => 'Una por línea. Formato: Nombre de la habilidad|85 (el número es el porcentaje 0-100).', + 'attributes' => ['rows' => 7], + ]); + + /* ── MÉTRICAS, IMPACTO Y EXTRAS ── */ + $extra = new_cmb2_box([ + 'id' => 'pflores_extras', + 'title' => 'Métricas, impacto y datos adicionales', + 'object_types' => ['options-page'], + 'option_key' => 'pflores_extras', + 'parent_slug' => 'pflores_perfil', + 'menu_title' => 'Métricas / Impacto', + 'save_button' => 'Guardar cambios', + 'capability' => 'manage_options', + ]); + + $metrics_group = $extra->add_field([ + 'id' => 'pf_metrics', + 'type' => 'group', + 'description' => 'Contadores principales del héroe', + 'options' => ['group_title' => 'Métrica {#}', 'add_button' => 'Añadir métrica', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => false], + ]); + $extra->add_group_field($metrics_group, ['id' => 'value', 'name' => 'Valor (ej: 22+, USD 5 M)', 'type' => 'text']); + $extra->add_group_field($metrics_group, ['id' => 'suffix', 'name' => 'Sufijo (opcional)', 'type' => 'text', 'attributes' => ['size' => 8]]); + $extra->add_group_field($metrics_group, ['id' => 'label', 'name' => 'Etiqueta descriptiva', 'type' => 'text']); + + $hl_group = $extra->add_field([ + 'id' => 'pf_highlights', + 'type' => 'group', + 'description' => 'Logros destacados con número grande', + 'options' => ['group_title' => 'Logro {#}', 'add_button' => 'Añadir logro', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => false], + ]); + $extra->add_group_field($hl_group, ['id' => 'value', 'name' => 'Valor (ej: 74 %, 9/10)', 'type' => 'text']); + $extra->add_group_field($hl_group, ['id' => 'suffix', 'name' => 'Sufijo (opcional)', 'type' => 'text', 'attributes' => ['size' => 8]]); + $extra->add_group_field($hl_group, ['id' => 'label', 'name' => 'Descripción', 'type' => 'text']); + + $impact_group = $extra->add_field([ + 'id' => 'pf_impact', + 'type' => 'group', + 'description' => 'Áreas de impacto ejecutivo', + 'options' => ['group_title' => 'Impacto {#}', 'add_button' => 'Añadir área', 'remove_button' => 'Eliminar', 'sortable' => true, 'closed' => false], + ]); + $extra->add_group_field($impact_group, ['id' => 'title', 'name' => 'Título', 'type' => 'text']); + $extra->add_group_field($impact_group, ['id' => 'body', 'name' => 'Descripción', 'type' => 'textarea_small']); + + $extra->add_field(['id' => 'pf_volunteer_title', 'name' => 'Voluntariado — Título', 'type' => 'text']); + $extra->add_field(['id' => 'pf_volunteer_year', 'name' => 'Voluntariado — Año', 'type' => 'text', 'attributes' => ['size' => 8]]); + $extra->add_field(['id' => 'pf_volunteer_body', 'name' => 'Voluntariado — Descripción', 'type' => 'textarea_small']); +} + +/* ───────────────────────────────────────────── + 2. FUNCIÓN CENTRAL — lee de CMB2 +────────────────────────────────────────────── */ +function pflores_get_cv_data(): array +{ + if (! function_exists('cmb2_get_option')) { + return pflores_cv_fallback_data(); + } + + $perfil = get_option('pflores_perfil', []); + $experiencia = get_option('pflores_experiencia', []); + $formacion = get_option('pflores_formacion', []); + $competencias = get_option('pflores_competencias', []); + $extras = get_option('pflores_extras', []); + + // ── Experiencia ── + $experience = []; + foreach ((array) ($experiencia['pf_experience'] ?? []) as $job) { + $bullets = array_values(array_filter( + array_map('trim', explode("\n", (string) ($job['bullets_text'] ?? ''))) + )); + $experience[] = [ + 'period' => $job['period'] ?? '', + 'title' => $job['title'] ?? '', + 'company' => $job['company'] ?? '', + 'location' => $job['location'] ?? '', + 'lead' => $job['lead'] ?? '', + 'bullets' => $bullets, + ]; + } + + // ── Educación ── + $education = []; + foreach ((array) ($formacion['pf_education'] ?? []) as $edu) { + $education[] = [ + 'year' => $edu['year'] ?? '', + 'title' => $edu['title'] ?? '', + 'institution' => $edu['institution'] ?? '', + 'detail' => $edu['detail'] ?? '', + ]; + } + + // ── Certificaciones ── + $certifications = []; + foreach ((array) ($formacion['pf_certifications'] ?? []) as $cert) { + $certifications[] = [ + 'year' => $cert['year'] ?? '', + 'title' => $cert['title'] ?? '', + 'issuer' => $cert['issuer'] ?? '', + ]; + } + + // ── Competencias (nombre|porcentaje) ── + $competencies = []; + foreach ((array) ($competencias['pf_competencies'] ?? []) as $group) { + $groupName = $group['group_name'] ?? ''; + if ($groupName === '') { + continue; + } + $skills = []; + foreach (array_filter(array_map('trim', explode("\n", (string) ($group['skills_text'] ?? '')))) as $line) { + $parts = explode('|', $line, 2); + $skills[] = ['name' => trim($parts[0]), 'pct' => isset($parts[1]) ? (int) trim($parts[1]) : 80]; + } + $competencies[$groupName] = $skills; + } + + // ── Métricas ── + $metrics = []; + foreach ((array) ($extras['pf_metrics'] ?? []) as $m) { + $metrics[] = ['value' => $m['value'] ?? '', 'suffix' => $m['suffix'] ?? '', 'label' => $m['label'] ?? '']; + } + + // ── Highlights ── + $highlights = []; + foreach ((array) ($extras['pf_highlights'] ?? []) as $h) { + $highlights[] = ['value' => $h['value'] ?? '', 'suffix' => $h['suffix'] ?? '', 'label' => $h['label'] ?? '']; + } + + // ── Impacto ── + $impact = []; + foreach ((array) ($extras['pf_impact'] ?? []) as $item) { + $impact[] = ['title' => $item['title'] ?? '', 'body' => $item['body'] ?? '']; + } + + // ── Foto (CMB2 file field devuelve ID; obtenemos la URL) ── + $photoId = $perfil['pf_photo'] ?? ''; + $photoUrl = $photoId ? (string) wp_get_attachment_url((int) $photoId) : ''; + + return [ + 'hero' => [ + 'eyebrow' => 'Currículum ejecutivo', + 'name' => $perfil['pf_name'] ?? '', + 'role' => $perfil['pf_role'] ?? '', + 'role2' => $perfil['pf_role2'] ?? '', + 'intro' => $perfil['pf_intro'] ?? '', + 'location' => $perfil['pf_location'] ?? '', + 'email' => $perfil['pf_email'] ?? '', + 'phone' => $perfil['pf_phone'] ?? '', + 'linkedin' => $perfil['pf_linkedin'] ?? '', + 'availability' => $perfil['pf_availability'] ?? '', + 'focus' => $perfil['pf_focus'] ?? '', + 'languages' => $perfil['pf_languages'] ?? '', + 'born' => $perfil['pf_born'] ?? '', + 'id' => $perfil['pf_ci'] ?? '', + 'web' => $perfil['pf_web'] ?? '', + ], + 'photo' => $photoUrl, + 'metrics' => $metrics, + 'highlights' => $highlights, + 'summary' => [ + 'title' => $perfil['pf_summary_title'] ?? '', + 'body' => $perfil['pf_summary_body'] ?? '', + ], + 'impact' => $impact, + 'experience' => $experience, + 'education' => $education, + 'certifications' => $certifications, + 'competencies' => $competencies, + 'volunteer' => [ + 'title' => $extras['pf_volunteer_title'] ?? '', + 'year' => $extras['pf_volunteer_year'] ?? '', + 'body' => $extras['pf_volunteer_body'] ?? '', + ], + 'additional' => [ + 'Idiomas' => $perfil['pf_languages'] ?? '', + 'Disponibilidad' => $perfil['pf_availability'] ?? '', + 'C.I.' => $perfil['pf_ci'] ?? '', + 'Fecha de nacimiento' => $perfil['pf_born'] ?? '', + 'Web profesional' => $perfil['pf_web'] ?? '', + ], + ]; +} + +/* ───────────────────────────────────────────── + 3. FALLBACK si CMB2 no está activo +────────────────────────────────────────────── */ +function pflores_cv_fallback_data(): array +{ + return [ + 'hero' => ['eyebrow' => 'Currículum ejecutivo', 'name' => 'Patricia Soledad Flores Sánchez', 'role' => 'Gerente / Jefa de Operaciones Financieras', 'role2' => 'Sector financiero y microfinanzas', 'intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas.', 'location' => 'Loja, Ecuador', 'email' => 'patricflorsanchez@gmail.com', 'phone' => '+593 99 469 8405', 'linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', 'availability' => 'Inmediata | Presencial / Híbrida / Remota', 'focus' => 'Operaciones financieras, control interno y transformación digital', 'languages' => 'Español nativo | Inglés básico en formación activa', 'born' => '3 de agosto de 1980', 'id' => '1103591648', 'web' => 'pflores.linkedcorp.net'], + 'photo' => '', + 'metrics' => [], + 'highlights' => [], + 'summary' => ['title' => '', 'body' => ''], + 'impact' => [], + 'experience' => [], + 'education' => [], + 'certifications' => [], + 'competencies' => [], + 'volunteer' => ['title' => '', 'year' => '', 'body' => ''], + 'additional' => [], + ]; +} diff --git a/backend/wordpress/wp-content/plugins/pflores-cv-fields/populate-data.php b/backend/wordpress/wp-content/plugins/pflores-cv-fields/populate-data.php new file mode 100644 index 0000000..84123c7 --- /dev/null +++ b/backend/wordpress/wp-content/plugins/pflores-cv-fields/populate-data.php @@ -0,0 +1,187 @@ + 'Patricia Soledad Flores Sánchez', + 'pf_role' => 'Gerente / Jefa de Operaciones Financieras', + 'pf_role2' => 'Sector financiero y microfinanzas', + 'pf_intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.', + 'pf_summary_title' => 'Perfil estratégico con enfoque en eficiencia, control y ejecución sostenible', + 'pf_summary_body' => 'Actualmente lidera la operatividad de 26 agencias a nivel nacional y coordina aproximadamente 1.500 operaciones de microcrédito mensuales, por un monto cercano a USD 5 millones. También articula la recaudación de cerca de 28.000 clientes por canales internos y externos, integrando visión operativa, criterio financiero, disciplina de control y liderazgo sostenido del desempeño.', + 'pf_location' => 'Loja, Ecuador', + 'pf_email' => 'patricflorsanchez@gmail.com', + 'pf_phone' => '+593 99 469 8405', + 'pf_linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', + 'pf_availability' => 'Inmediata | Presencial / Híbrida / Remota', + 'pf_focus' => 'Operaciones financieras, control interno y transformación digital', + 'pf_languages' => 'Español nativo | Inglés básico en formación activa', + 'pf_born' => '3 de agosto de 1980', + 'pf_ci' => '1103591648', + 'pf_web' => 'pflores.linkedcorp.net', + 'pf_photo' => '', +]); +$ok[] = '✓ Perfil y contacto (17 campos)'; + +/* ════════════════════════════════════════════ + EXPERIENCIA → option: pflores_experiencia +════════════════════════════════════════════ */ +update_option('pflores_experiencia', [ + 'pf_experience' => [ + [ + 'period' => 'Abr. 2022 – Presente', + 'title' => 'Jefa de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Responsable del desempeño operativo nacional, cumplimiento del POA, control interno, digitalización de procesos y gestión de equipos.', + 'bullets_text' => + "Cumplimiento del POA: 98 % (Q2 2025), 96 % (Q3 2025), 100 % (Q4 2025) y 105 % (Q1 2026).\n" . + "La transformación digital logró que los desembolsos por transferencia representen el 47,80 % del monto total en Q1 2026.\n" . + "Redujo los requerimientos de soporte desde Dirección en un 74 %, de 1.489 a 382, mediante la descentralización de procesos hacia las agencias.\n" . + "Automatizó el proceso del seguro de desgravamen con cálculo de prima, conciliación mensual, liquidación de siniestros y asientos contables automatizados.\n" . + "Elevó el índice de cumplimiento operativo nacional del 65 % al 88 % con la matriz de supervisión y certificación por agencia.\n" . + "Redujo el tiempo de atención del seguro de desgravamen de 17 a 10 días.\n" . + "Redujo los tiempos de desembolso a 1,68 días en Q3 2025, mejor resultado del año frente a la meta institucional máxima de 2 días.\n" . + "La efectividad de recuperación de mora alcanzó 90,60 % en Q1 2026, con USD 3,06 millones recuperados sobre USD 3,37 millones en mora.\n" . + "Redujo el índice de rotación del área del 41,46 % al 10,24 % entre diciembre de 2022 y marzo de 2026 mediante mejoras salariales, remuneración variable, evaluación trimestral y un plan de carrera con certificación de 60 horas para asistentes de caja.\n" . + "Participa en comités estratégicos, auditorías internas y externas, y en representación institucional ante organismos financieros internacionales de desarrollo.", + ], + [ + 'period' => 'Feb. 2021 – Mar. 2022', + 'title' => 'Supervisora de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Supervisó operaciones de 18 agencias a nivel nacional y regularizó procesos críticos inexistentes o incompletos.', + 'bullets_text' => + "Reconstruyó desde cero las bases de cálculo del seguro de desgravamen y regularizó pagos pendientes desde enero de 2021.\n" . + "Elaboró el instructivo de constitución de garantías hipotecarias, proceso que no existía previamente en la institución.\n" . + "Inició el proyecto de automatización de pagos por depósitos y transferencias.", + ], + [ + 'period' => '2019 – 2021', + 'title' => 'Actualización y formación profesional', + 'company' => 'Formación independiente', + 'location' => 'Ecuador', + 'lead' => 'Etapa enfocada en fortalecimiento técnico y actualización profesional tras su salida de Mutualista Pichincha.', + 'bullets_text' => "SAP FI (30 h).\nExcel Empresarial (24 h).\nPrevención de Lavado de Activos — UAFE (5 h).", + ], + [ + 'period' => 'Ago. 2004 – May. 2019', + 'title' => 'Oficial Operativa Regional / Jefa Operativa', + 'company' => 'Mutualista Pichincha', + 'location' => 'Loja, Ecuador', + 'lead' => 'Trayectoria ascendente de 15 años desde Cajera hasta Jefa Operativa Regional.', + 'bullets_text' => + "Reconocida a nivel nacional por excelencia en control operativo.\n" . + "La agencia se mantuvo consistentemente por encima del 80 % en gestión de excepciones de crédito hipotecario.\n" . + "Administró proyecto inmobiliario institucional en Loja.\n" . + "Gestionó alertas ALA/FT con 100 % de cumplimiento en comités trimestrales y auditorías internas sin observaciones significativas de forma sostenida.", + ], + [ + 'period' => 'Ene. 2002 – Nov. 2003', + 'title' => 'Cajera', + 'company' => 'Cooperativa de Ahorro y Crédito El Porvenir', + 'location' => 'Loja, Ecuador', + 'lead' => 'Inicio de carrera en el frente operativo financiero con disciplina de caja y apoyo contable.', + 'bullets_text' => + "Gestión de caja sin faltantes ni sobrantes.\n" . + "Contrato renovado de forma anticipada a los tres meses.\n" . + "Apoyo al área de Contabilidad y reemplazo temporal de la contadora durante vacaciones.", + ], + ], +]); +$ok[] = '✓ Experiencia (5 cargos, 23 bullets)'; + +/* ════════════════════════════════════════════ + FORMACIÓN Y CERTIFICACIONES → pflores_formacion +════════════════════════════════════════════ */ +update_option('pflores_formacion', [ + 'pf_education' => [ + ['year' => '2021', 'title' => 'Máster Universitario en Dirección y Planificación Financiera', 'institution' => 'Universidad Europea Miguel de Cervantes — Valladolid, España', 'detail' => 'Título reconocido por la SENECYT — Ecuador, febrero de 2023 | Reg. 2021/394270.'], + ['year' => '2007', 'title' => 'Ingeniera en Administración en Banca y Finanzas', 'institution' => 'Universidad Técnica Particular de Loja (UTPL)', 'detail' => ''], + ], + 'pf_certifications' => [ + ['year' => '2026', 'title' => 'Mujeres que Lideran: Alta Gerencia', 'issuer' => 'Foro UNIR — Universidad Internacional de La Rioja'], + ['year' => '2025', 'title' => 'Programa Internacional de Habilidades Gerenciales (48 h)', 'issuer' => 'IDEASGROUP — Ecuador / México'], + ['year' => '2025', 'title' => 'Transforma tu Trabajo con Inteligencia Artificial (12 h)', 'issuer' => 'Red de Instituciones Financieras de Desarrollo'], + ['year' => '2025', 'title' => 'Excel: Básico a Dashboard (40 h)', 'issuer' => 'DMARURI Training & Consulting'], + ['year' => '2024', 'title' => 'Administración por Procesos — BPM (64 h)', 'issuer' => 'Escuela Politécnica Nacional — CEC'], + ['year' => '2023', 'title' => 'Formación de Formadores (80 h)', 'issuer' => 'Ministerio del Trabajo del Ecuador | Certificación vigente hasta 2028'], + ['year' => '2023', 'title' => 'Power BI (40 h) | Indicadores Clave de Desempeño — KPI (12 h)', 'issuer' => 'Liderazgo Capacitación & Eventos'], + ['year' => '2020', 'title' => 'Gestión Financiera ERP SAP FI — Key User (30 h)', 'issuer' => 'Socio SAP certificado'], + ['year' => '2020', 'title' => 'Prevención de Lavado de Activos y Financiamiento de Delitos', 'issuer' => 'UAFE — Ecuador'], + ['year' => '2013', 'title' => 'Tributación Integral (36 h)', 'issuer' => 'Universidad Técnica Particular de Loja'], + ], +]); +$ok[] = '✓ Formación (2 títulos) + Certificaciones (10)'; + +/* ════════════════════════════════════════════ + COMPETENCIAS → pflores_competencias +════════════════════════════════════════════ */ +update_option('pflores_competencias', [ + 'pf_competencies' => [ + ['group_name' => 'Finanzas y Operaciones', 'skills_text' => "Gestión de operaciones financieras|95\nControl interno y cumplimiento|95\nGestión de cartera y microfinanzas|90\nConciliación bancaria / Tesorería|90\nALA/FT y prevención de riesgos|88"], + ['group_name' => 'Tecnología y Procesos', 'skills_text' => "Transformación digital / BPM|85\nSAP FI|78\nExcel avanzado y dashboards|85\nPower BI|75\nIA aplicada al trabajo|70"], + ['group_name' => 'Liderazgo', 'skills_text' => "Equipos de hasta 48 personas|95\nGestión del cambio organizacional|90\nFormación de formadores|88\nRemuneración variable y plan de carrera|85\nDiversidad, equidad e inclusión|80"], + ], +]); +$ok[] = '✓ Competencias (3 grupos, 15 habilidades)'; + +/* ════════════════════════════════════════════ + MÉTRICAS, IMPACTO Y EXTRAS → pflores_extras +════════════════════════════════════════════ */ +update_option('pflores_extras', [ + 'pf_metrics' => [ + ['value' => '22+', 'suffix' => '', 'label' => 'años de experiencia en banca y microfinanzas'], + ['value' => '26', 'suffix' => '', 'label' => 'agencias bajo liderazgo operativo nacional'], + ['value' => '48', 'suffix' => '', 'label' => 'personas a cargo en la estructura operativa'], + ['value' => 'USD 5 M','suffix' => '', 'label' => 'en operaciones mensuales de microcrédito'], + ['value' => '28.000', 'suffix' => '', 'label' => 'clientes coordinados en recaudación por canales internos y externos'], + ], + 'pf_highlights' => [ + ['value' => '9/10', 'suffix' => '', 'label' => 'calificación de gestión con resultado excelente en cuatro trimestres consecutivos'], + ['value' => '74 %', 'suffix' => '', 'label' => 'reducción de requerimientos de soporte desde Dirección por descentralización de procesos'], + ['value' => '1,68 días','suffix' => '', 'label' => 'tiempo de desembolso logrado en Q3 2025, mejor resultado del año'], + ['value' => '75 %', 'suffix' => '', 'label' => 'reducción del índice de rotación del área entre 2022 y 2026'], + ], + 'pf_impact' => [ + ['title' => 'Transformación digital', 'body' => 'Lideró desembolsos por transferencia bancaria, integración de APIs bancarias y digitalización de expedientes y pagarés en 13 agencias.'], + ['title' => 'Automatización crítica', 'body' => 'Automatizó el seguro de desgravamen, desde el cálculo de prima hasta la liquidación contable, reduciendo tiempos de 17 a 10 días.'], + ['title' => 'Supervisión nacional', 'body' => 'Implementó una matriz de supervisión y certificación por agencia, elevando el índice de cumplimiento operativo del 65 % al 88 %.'], + ], + 'pf_volunteer_title' => 'Voluntaria — Hogar San Camilo de Lellis', + 'pf_volunteer_year' => '2025', + 'pf_volunteer_body' => 'Hogar de niñas con discapacidad | Loja, Ecuador. Alineado a la visión y misión de responsabilidad social de la Fundación FACES Microfinanzas.', +]); +$ok[] = '✓ Métricas (5) + Highlights (4) + Impacto (3) + Voluntariado'; + +?> +Datos CV cargados + + +

✅ CV de Patricia Flores — datos cargados con CMB2

+ + +

⚠ Elimina este archivo una vez verificado.

+ diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/css/theme.css b/backend/wordpress/wp-content/themes/apex-cv/assets/css/theme.css new file mode 100644 index 0000000..0390eb9 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/assets/css/theme.css @@ -0,0 +1,1194 @@ +/* ========================================================================== + Apex CV — theme.css + Dark professional CV theme · Space Grotesk + ========================================================================== */ + +/* -------------------------------------------------------------------------- + 0. CSS custom properties + -------------------------------------------------------------------------- */ +:root { + --ax-bg: #0b0d12; + --ax-surface: #11141c; + --ax-surface-2: #161a26; + --ax-surface-3: #1c2133; + --ax-border: #1e2235; + --ax-border-light: #252b40; + --ax-text: #e0e3ef; + --ax-muted: #7a8099; + --ax-subtle: #4a5268; + --ax-cyan: #22d3ee; + --ax-cyan-dim: #0e7490; + --ax-cyan-glow: rgba(34, 211, 238, .15); + --ax-gold: #f0b429; + --ax-gold-dim: rgba(240, 180, 41, .12); + --ax-green: #22c55e; + --ax-green-dim: rgba(34, 197, 94, .15); + --ax-red: #f87171; + + --sidebar-w: 280px; + --sidebar-w-sm: 240px; + + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + --radius-full: 9999px; + + --shadow-card: 0 1px 3px rgba(0,0,0,.5), 0 1px 2px rgba(0,0,0,.4); + --shadow-raised: 0 4px 16px rgba(0,0,0,.6); + --transition: 150ms cubic-bezier(.4,0,.2,1); +} + +/* -------------------------------------------------------------------------- + 1. Reset & base + -------------------------------------------------------------------------- */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { + font-size: 16px; + scroll-behavior: smooth; + -webkit-text-size-adjust: 100%; +} + +body { + font-family: 'Space Grotesk', system-ui, sans-serif; + background: var(--ax-bg); + color: var(--ax-text); + line-height: 1.65; + min-height: 100vh; + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.25; + color: var(--ax-text); +} + +a { + color: var(--ax-cyan); + text-decoration: none; + transition: color var(--transition), opacity var(--transition); +} +a:hover { opacity: .8; } + +img { display: block; max-width: 100%; height: auto; } + +ul, ol { list-style: none; } + +/* -------------------------------------------------------------------------- + 2. Layout — shell + -------------------------------------------------------------------------- */ +body.apex-shell { overflow-x: hidden; } + +.ax-layout { + display: flex; + min-height: 100vh; +} + +/* -------------------------------------------------------------------------- + 3. Sidebar + -------------------------------------------------------------------------- */ +.ax-sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: var(--sidebar-w); + background: var(--ax-surface); + border-right: 1px solid var(--ax-border); + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + gap: 0; + z-index: 100; + scrollbar-width: thin; + scrollbar-color: var(--ax-border) transparent; + transition: transform var(--transition), box-shadow var(--transition); +} +.ax-sidebar::-webkit-scrollbar { width: 4px; } +.ax-sidebar::-webkit-scrollbar-track { background: transparent; } +.ax-sidebar::-webkit-scrollbar-thumb { background: var(--ax-border); border-radius: var(--radius-full); } + +/* subtle radial glow in the top-left corner */ +.ax-sidebar::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 260px; + background: radial-gradient(ellipse 140px 120px at 50% -10%, rgba(34,211,238,.08) 0%, transparent 70%); + pointer-events: none; + z-index: 0; +} + +/* -------------------------------------------------------------------------- + 4. Profile block + -------------------------------------------------------------------------- */ +.ax-profile { + position: relative; + z-index: 1; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 2rem 1.25rem 1.5rem; + gap: .5rem; +} + +.ax-profile__avatar-wrap { + position: relative; + margin-bottom: .5rem; +} + +.ax-profile__avatar { + width: 108px; + height: 108px; + border-radius: 50%; + object-fit: cover; + border: 2px solid var(--ax-border-light); + /* pulsing cyan ring */ + box-shadow: + 0 0 0 3px var(--ax-surface), + 0 0 0 5px var(--ax-cyan-dim), + 0 0 0 6px var(--ax-surface); + animation: ax-avatar-ring 3s ease-in-out infinite; +} + +@keyframes ax-avatar-ring { + 0%, 100% { box-shadow: 0 0 0 3px var(--ax-surface), 0 0 0 5px var(--ax-cyan-dim), 0 0 0 6px var(--ax-surface); } + 50% { box-shadow: 0 0 0 3px var(--ax-surface), 0 0 0 6px rgba(34,211,238,.35), 0 0 0 8px var(--ax-surface); } +} + +/* green availability dot */ +.ax-profile__status { + position: absolute; + bottom: 4px; + right: 4px; + width: 13px; + height: 13px; + background: var(--ax-green); + border-radius: 50%; + border: 2px solid var(--ax-surface); + animation: ax-blink 2.5s ease-in-out infinite; +} + +@keyframes ax-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: .4; } +} + +.ax-profile__name { + font-size: 1rem; + font-weight: 700; + letter-spacing: -.01em; + color: var(--ax-text); + line-height: 1.3; +} + +.ax-profile__role { + font-size: .75rem; + font-weight: 600; + color: var(--ax-cyan); + letter-spacing: .02em; + text-transform: uppercase; + line-height: 1.4; +} + +.ax-profile__role2 { + font-size: .7rem; + color: var(--ax-muted); + line-height: 1.4; +} + +/* -------------------------------------------------------------------------- + 5. Sidebar navigation + -------------------------------------------------------------------------- */ +.ax-nav { + position: relative; + z-index: 1; + padding: 0 .75rem; +} + +.ax-nav__list { + display: flex; + flex-direction: column; + gap: .125rem; +} + +.ax-nav__item { position: relative; } + +.ax-nav__link { + display: flex; + align-items: center; + gap: .625rem; + padding: .55rem .875rem; + border-radius: var(--radius-md); + font-size: .8125rem; + font-weight: 500; + color: var(--ax-muted); + transition: background var(--transition), color var(--transition); + position: relative; +} + +.ax-nav__link::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%) scaleY(0); + width: 3px; + height: 60%; + background: var(--ax-cyan); + border-radius: 0 var(--radius-sm) var(--radius-sm) 0; + transition: transform var(--transition); + border-radius: var(--radius-full); +} + +.ax-nav__link:hover { + background: var(--ax-surface-2); + color: var(--ax-text); + opacity: 1; +} + +.ax-nav__link.is-active { + background: rgba(34, 211, 238, .08); + color: var(--ax-cyan); + font-weight: 600; +} +.ax-nav__link.is-active::before { transform: translateY(-50%) scaleY(1); } + +/* -------------------------------------------------------------------------- + 6. Sidebar section blocks + -------------------------------------------------------------------------- */ +.ax-sidebar-section { + position: relative; + z-index: 1; + padding: 1rem 1.25rem; + border-top: 1px solid var(--ax-border); +} + +.ax-sidebar-label { + display: block; + font-size: .6875rem; + font-weight: 700; + letter-spacing: .1em; + text-transform: uppercase; + color: var(--ax-subtle); + margin-bottom: .625rem; +} + +.ax-sidebar-text { + font-size: .75rem; + color: var(--ax-muted); + line-height: 1.55; +} + +/* -------------------------------------------------------------------------- + 7. Contact list + -------------------------------------------------------------------------- */ +.ax-contact-list { + display: flex; + flex-direction: column; + gap: .5rem; +} + +.ax-contact-list__item { + display: flex; + align-items: flex-start; + gap: .5rem; + font-size: .75rem; + color: var(--ax-muted); + word-break: break-all; +} +.ax-contact-list__item a { + color: var(--ax-muted); + transition: color var(--transition); + word-break: break-all; +} +.ax-contact-list__item a:hover { color: var(--ax-cyan); opacity: 1; } + +.ax-icon { + width: 14px; + height: 14px; + flex-shrink: 0; + margin-top: 1px; + color: var(--ax-cyan); +} + +/* -------------------------------------------------------------------------- + 8. Skill pills + -------------------------------------------------------------------------- */ +.ax-skill-pills { + display: flex; + flex-wrap: wrap; + gap: .35rem; +} + +.ax-skill-group-label { + width: 100%; + font-size: .625rem; + font-weight: 700; + letter-spacing: .08em; + text-transform: uppercase; + color: var(--ax-subtle); + margin-top: .5rem; + margin-bottom: .15rem; +} +.ax-skill-group-label:first-child { margin-top: 0; } + +.ax-skill-pill { + display: inline-block; + padding: .2rem .6rem; + background: var(--ax-surface-2); + border: 1px solid var(--ax-border-light); + border-radius: var(--radius-full); + font-size: .65rem; + font-weight: 500; + color: var(--ax-muted); + line-height: 1.5; + transition: border-color var(--transition), color var(--transition), background var(--transition); + white-space: nowrap; +} +.ax-skill-pill:hover { + border-color: var(--ax-cyan); + color: var(--ax-cyan); + background: rgba(34,211,238,.06); +} + +/* -------------------------------------------------------------------------- + 9. Sidebar action buttons + -------------------------------------------------------------------------- */ +.ax-sidebar-section--actions { + display: flex; + flex-direction: column; + gap: .625rem; + margin-top: auto; +} + +.ax-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: .5rem; + padding: .6rem 1rem; + border-radius: var(--radius-md); + font-family: inherit; + font-size: .8125rem; + font-weight: 600; + cursor: pointer; + border: 1px solid transparent; + transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition); + text-align: center; + width: 100%; +} +.ax-btn .ax-icon { width: 15px; height: 15px; } + +.ax-btn--primary { + background: var(--ax-cyan); + color: #0b0d12; + border-color: var(--ax-cyan); +} +.ax-btn--primary:hover { + background: #38e4f5; + border-color: #38e4f5; + box-shadow: 0 0 12px rgba(34,211,238,.4); + color: #0b0d12; + opacity: 1; +} + +.ax-btn--ghost { + background: transparent; + color: var(--ax-muted); + border-color: var(--ax-border-light); +} +.ax-btn--ghost:hover { + color: var(--ax-text); + border-color: var(--ax-muted); + background: var(--ax-surface-2); + opacity: 1; +} + +/* -------------------------------------------------------------------------- + 10. Mobile hamburger toggle + -------------------------------------------------------------------------- */ +.ax-mobile-toggle { + display: none; + position: fixed; + top: 1rem; + left: 1rem; + z-index: 200; + width: 40px; + height: 40px; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 5px; + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-md); + cursor: pointer; + padding: 0; + transition: background var(--transition), border-color var(--transition); +} +.ax-mobile-toggle:hover { + background: var(--ax-surface-2); + border-color: var(--ax-border-light); +} + +.ax-mobile-toggle span { + display: block; + width: 20px; + height: 2px; + background: var(--ax-text); + border-radius: var(--radius-full); + transform-origin: center; + transition: transform var(--transition), opacity var(--transition), width var(--transition); +} + +/* X state when open */ +.ax-mobile-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); } +.ax-mobile-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; width: 0; } +.ax-mobile-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } + +/* -------------------------------------------------------------------------- + 11. Main content area + -------------------------------------------------------------------------- */ +.ax-main { + margin-left: var(--sidebar-w); + flex: 1; + min-width: 0; +} + +.ax-content-wrap { + max-width: 860px; + padding: 3rem 3rem 4rem; + margin: 0 auto; +} + +/* -------------------------------------------------------------------------- + 12. Section base + -------------------------------------------------------------------------- */ +.ax-section { + padding-bottom: 3.5rem; + border-bottom: 1px solid var(--ax-border); + margin-bottom: 3.5rem; +} +.ax-section:last-child { + border-bottom: none; + margin-bottom: 0; + padding-bottom: 0; +} + +.ax-section__head { margin-bottom: 1.5rem; } + +.ax-section__eyebrow { + display: block; + font-size: .6875rem; + font-weight: 700; + letter-spacing: .12em; + text-transform: uppercase; + color: var(--ax-cyan); + margin-bottom: .4rem; +} + +.ax-section__title { + font-size: 1.375rem; + font-weight: 700; + color: var(--ax-text); + line-height: 1.3; +} + +.ax-lead { + font-size: .9375rem; + color: var(--ax-muted); + line-height: 1.75; + max-width: 72ch; +} + +/* -------------------------------------------------------------------------- + 13. Metrics grid (5 columns) + -------------------------------------------------------------------------- */ +.ax-metrics { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: .75rem; + margin-top: 1.75rem; +} + +.ax-metric-card { + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-lg); + padding: 1rem .875rem; + display: flex; + flex-direction: column; + gap: .35rem; + transition: border-color var(--transition), box-shadow var(--transition); +} +.ax-metric-card:hover { + border-color: var(--ax-border-light); + box-shadow: var(--shadow-card); +} + +.ax-metric-card__value { + font-size: 1.25rem; + font-weight: 700; + color: var(--ax-gold); + line-height: 1; + letter-spacing: -.02em; +} + +.ax-metric-card__label { + font-size: .6875rem; + color: var(--ax-muted); + line-height: 1.45; + font-weight: 400; +} + +/* -------------------------------------------------------------------------- + 14. Highlights 2×2 + -------------------------------------------------------------------------- */ +.ax-highlights { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + margin-bottom: 1.75rem; +} + +.ax-highlight-card { + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-lg); + padding: 1.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: .4rem; + transition: border-color var(--transition); +} +.ax-highlight-card:hover { border-color: var(--ax-cyan-dim); } + +.ax-highlight-card__value { + font-size: 1.875rem; + font-weight: 700; + color: var(--ax-cyan); + letter-spacing: -.03em; + line-height: 1; +} + +.ax-highlight-card__label { + font-size: .8125rem; + color: var(--ax-muted); + line-height: 1.5; +} + +/* -------------------------------------------------------------------------- + 15. Impact cards (3 columns) + -------------------------------------------------------------------------- */ +.ax-impact-cards { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.ax-impact-card { + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-left: 3px solid var(--ax-cyan); + border-radius: 0 var(--radius-md) var(--radius-md) 0; + padding: 1rem 1.125rem; + transition: border-left-color var(--transition), box-shadow var(--transition); +} +.ax-impact-card:hover { + border-left-color: #38e4f5; + box-shadow: var(--shadow-card); +} + +.ax-impact-card__title { + font-size: .875rem; + font-weight: 700; + color: var(--ax-text); + margin-bottom: .4rem; +} + +.ax-impact-card__body { + font-size: .8rem; + color: var(--ax-muted); + line-height: 1.6; +} + +/* -------------------------------------------------------------------------- + 16. Timeline (experience) + -------------------------------------------------------------------------- */ +.ax-timeline { + display: flex; + flex-direction: column; + gap: 0; + position: relative; +} + +.ax-timeline__item { + display: grid; + grid-template-columns: 180px 1fr; + gap: 0 1.5rem; + position: relative; + padding-bottom: 2.5rem; +} +.ax-timeline__item:last-child { padding-bottom: 0; } + +/* vertical line along the dot column */ +.ax-timeline__item:not(:last-child)::after { + content: ''; + position: absolute; + left: calc(180px + 1.5rem / 2 - 1px); + top: 8px; + bottom: 0; + width: 1px; + background: var(--ax-border); +} + +.ax-timeline__period { + text-align: right; + padding-top: 2px; + position: relative; + display: flex; + align-items: flex-start; + justify-content: flex-end; + gap: .75rem; +} + +.ax-timeline__period-text { + font-size: .75rem; + font-weight: 600; + color: var(--ax-subtle); + line-height: 1.4; + letter-spacing: .01em; +} + +.ax-timeline__dot { + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--ax-surface); + border: 2px solid var(--ax-cyan); + flex-shrink: 0; + margin-top: 3px; + position: relative; + z-index: 1; + transition: background var(--transition), box-shadow var(--transition); +} +.ax-timeline__item:hover .ax-timeline__dot { + background: var(--ax-cyan); + box-shadow: 0 0 8px rgba(34,211,238,.5); +} + +.ax-timeline__content { padding-top: 0; } + +.ax-timeline__job-title { + font-size: 1rem; + font-weight: 700; + color: var(--ax-text); + margin-bottom: .2rem; +} + +.ax-timeline__meta { + font-size: .8125rem; + color: var(--ax-muted); + margin-bottom: .5rem; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: .25rem; +} + +.ax-timeline__sep { + color: var(--ax-subtle); +} + +.ax-timeline__lead { + font-size: .8375rem; + color: var(--ax-muted); + line-height: 1.65; + margin-bottom: .75rem; + font-style: italic; +} + +.ax-timeline__bullets { + display: flex; + flex-direction: column; + gap: .375rem; +} + +.ax-timeline__bullets li { + font-size: .8125rem; + color: var(--ax-muted); + line-height: 1.6; + padding-left: 1.1rem; + position: relative; +} +.ax-timeline__bullets li::before { + content: '›'; + position: absolute; + left: 0; + top: 0; + color: var(--ax-cyan); + font-weight: 700; + line-height: 1.6; +} + +/* -------------------------------------------------------------------------- + 17. Education cards + -------------------------------------------------------------------------- */ +.ax-edu-cards { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.ax-edu-card { + display: flex; + align-items: flex-start; + gap: 1.25rem; + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-lg); + padding: 1.25rem 1.5rem; + transition: border-color var(--transition), box-shadow var(--transition); +} +.ax-edu-card:hover { + border-color: var(--ax-border-light); + box-shadow: var(--shadow-card); +} + +.ax-edu-card__year { + flex-shrink: 0; + font-size: 1.125rem; + font-weight: 700; + color: var(--ax-gold); + letter-spacing: -.02em; + min-width: 48px; + padding-top: 2px; +} + +.ax-edu-card__title { + font-size: .9375rem; + font-weight: 700; + color: var(--ax-text); + margin-bottom: .3rem; +} + +.ax-edu-card__institution { + font-size: .8125rem; + color: var(--ax-muted); + margin-bottom: .25rem; +} + +.ax-edu-card__detail { + font-size: .75rem; + color: var(--ax-subtle); + font-style: italic; +} + +/* -------------------------------------------------------------------------- + 18. Certifications list + -------------------------------------------------------------------------- */ +.ax-cert-list { + display: flex; + flex-direction: column; + gap: .625rem; +} + +.ax-cert-item { + display: flex; + align-items: flex-start; + gap: 1rem; + padding: .875rem 1.125rem; + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-md); + transition: border-color var(--transition); +} +.ax-cert-item:hover { border-color: var(--ax-border-light); } + +.ax-cert-item__year { + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + padding: .15rem .6rem; + background: rgba(34,211,238,.1); + border: 1px solid var(--ax-cyan-dim); + border-radius: var(--radius-full); + font-size: .6875rem; + font-weight: 700; + color: var(--ax-cyan); + letter-spacing: .04em; + white-space: nowrap; + margin-top: 2px; +} + +.ax-cert-item__title { + font-size: .875rem; + font-weight: 600; + color: var(--ax-text); + margin-bottom: .2rem; + line-height: 1.35; +} + +.ax-cert-item__issuer { + font-size: .75rem; + color: var(--ax-muted); + line-height: 1.45; +} + +/* -------------------------------------------------------------------------- + 19. Extras grid (contacto section) + -------------------------------------------------------------------------- */ +.ax-extras-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.ax-extras-grid__card--bcard { + grid-column: 1 / -1; +} + +.ax-extras-grid__card { + background: var(--ax-surface); + border: 1px solid var(--ax-border); + border-radius: var(--radius-lg); + padding: 1.25rem 1.5rem; +} + +.ax-extras__heading { + font-size: .9375rem; + font-weight: 700; + color: var(--ax-text); + margin-bottom: .2rem; + margin-top: .125rem; +} + +.ax-extras__year { + font-size: .75rem; + font-weight: 700; + color: var(--ax-gold); + margin-bottom: .5rem; +} + +.ax-extras__body { + font-size: .8125rem; + color: var(--ax-muted); + line-height: 1.6; +} + +/* -------------------------------------------------------------------------- + 20. Data list (additional info) + -------------------------------------------------------------------------- */ +.ax-data-list { + display: flex; + flex-direction: column; + gap: .5rem; + margin-top: .25rem; +} + +.ax-data-list__row { + display: flex; + flex-wrap: wrap; + gap: .25rem .5rem; +} + +.ax-data-list dt { + font-size: .75rem; + font-weight: 700; + color: var(--ax-subtle); + min-width: 120px; + flex-shrink: 0; +} + +.ax-data-list dd { + font-size: .8rem; + color: var(--ax-muted); + flex: 1; + min-width: 120px; + line-height: 1.5; +} + +/* -------------------------------------------------------------------------- + 21. Business card + -------------------------------------------------------------------------- */ +.ax-bcard { + background: linear-gradient(135deg, var(--ax-surface-2) 0%, var(--ax-surface-3) 100%); + border: 1px solid var(--ax-border-light); + border-radius: var(--radius-lg); + overflow: hidden; + margin-top: .75rem; +} + +.ax-bcard__header { + padding: 1rem 1.25rem .875rem; + border-bottom: 1px solid var(--ax-border); + background: rgba(34,211,238,.04); +} + +.ax-bcard__name { + font-size: .9375rem; + font-weight: 700; + color: var(--ax-text); + margin-bottom: .2rem; +} + +.ax-bcard__role { + font-size: .75rem; + color: var(--ax-cyan); + font-weight: 500; +} + +.ax-bcard__body { + display: flex; + gap: 1rem; + padding: 1rem 1.25rem; + align-items: center; +} + +.ax-bcard__qr { + flex-shrink: 0; + padding: .375rem; + background: #fff; + border-radius: var(--radius-sm); +} +.ax-bcard__qr img { + display: block; + width: 100px; + height: 100px; + border-radius: 2px; +} + +.ax-bcard__info { + display: flex; + flex-direction: column; + gap: .35rem; +} +.ax-bcard__info p, .ax-bcard__info a { + font-size: .75rem; + color: var(--ax-muted); + line-height: 1.45; +} +.ax-bcard__info a:hover { color: var(--ax-cyan); opacity: 1; } + +/* -------------------------------------------------------------------------- + 22. Sidebar overlay (mobile backdrop) + -------------------------------------------------------------------------- */ +.ax-sidebar-overlay { + display: none; + position: fixed; + inset: 0; + background: rgba(0,0,0,.65); + z-index: 99; + opacity: 0; + transition: opacity 200ms ease; +} +.ax-sidebar-overlay.is-visible { + display: block; + opacity: 1; +} + +/* -------------------------------------------------------------------------- + 23. Responsive — 1024px: narrower sidebar, 3-col metrics, flat timeline + -------------------------------------------------------------------------- */ +@media (max-width: 1024px) { + .ax-sidebar { width: var(--sidebar-w-sm); } + .ax-main { margin-left: var(--sidebar-w-sm); } + + .ax-content-wrap { padding: 2.5rem 2rem 3rem; } + + .ax-metrics { + grid-template-columns: repeat(3, 1fr); + } + + .ax-impact-cards { + grid-template-columns: 1fr 1fr; + } + + /* Flatten timeline: full-width blocks stacked */ + .ax-timeline__item { + grid-template-columns: 1fr; + padding-left: 1.25rem; + padding-bottom: 2rem; + } + .ax-timeline__item::after { + left: 0; + top: 14px; + } + + .ax-timeline__period { + text-align: left; + justify-content: flex-start; + flex-direction: row-reverse; + margin-bottom: .5rem; + } + .ax-timeline__dot { + position: absolute; + left: -5px; + top: 4px; + margin-top: 0; + } +} + +/* -------------------------------------------------------------------------- + 24. Responsive — 768px: mobile sidebar + -------------------------------------------------------------------------- */ +@media (max-width: 768px) { + .ax-mobile-toggle { display: flex; } + + .ax-sidebar { + transform: translateX(-100%); + box-shadow: none; + width: var(--sidebar-w); + } + .ax-sidebar.is-open { + transform: translateX(0); + box-shadow: var(--shadow-raised); + } + + .ax-main { margin-left: 0; } + + .ax-content-wrap { + padding: 5rem 1.25rem 3rem; + max-width: 100%; + } + + .ax-metrics { + grid-template-columns: repeat(2, 1fr); + } + + .ax-highlights { + grid-template-columns: 1fr; + } + + .ax-impact-cards { + grid-template-columns: 1fr; + } + + .ax-section__title { font-size: 1.125rem; } + + .ax-highlight-card__value { font-size: 1.5rem; } + + .ax-extras-grid { + grid-template-columns: 1fr; + } + .ax-extras-grid__card--bcard { + grid-column: 1; + } +} + +@media (max-width: 480px) { + .ax-metrics { grid-template-columns: 1fr 1fr; } + + .ax-bcard__body { flex-direction: column; } + + .ax-timeline__item { + padding-left: 1rem; + } +} + +/* -------------------------------------------------------------------------- + 25. Print styles + -------------------------------------------------------------------------- */ +@media print { + :root { + --ax-bg: #fff; + --ax-surface: #f8f8f8; + --ax-surface-2: #f0f0f0; + --ax-surface-3: #e8e8e8; + --ax-border: #ddd; + --ax-border-light: #ccc; + --ax-text: #111; + --ax-muted: #444; + --ax-subtle: #666; + --ax-cyan: #0077aa; + --ax-cyan-dim: #0077aa; + --ax-gold: #c07800; + --ax-green: #007700; + } + + body { background: #fff; color: #111; } + + .ax-mobile-toggle { display: none !important; } + + .ax-layout { display: block; } + + .ax-sidebar { + position: static; + width: 100%; + border: none; + border-bottom: 2px solid #ccc; + overflow: visible; + transform: none !important; + animation: none !important; + page-break-after: avoid; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: .5rem 1rem; + padding: 1rem; + } + + .ax-sidebar::before { display: none; } + + .ax-profile { + grid-column: 1; + text-align: left; + align-items: flex-start; + padding: 0; + } + + .ax-profile__avatar { + width: 64px; + height: 64px; + animation: none; + box-shadow: none; + } + .ax-profile__status { display: none; } + + .ax-nav { grid-column: 2; } + .ax-nav__list { flex-direction: row; flex-wrap: wrap; gap: .25rem; } + .ax-nav__link { color: #333; font-size: .75rem; padding: .25rem .5rem; } + .ax-nav__link.is-active { color: var(--ax-cyan); } + .ax-nav__link::before { display: none; } + + .ax-sidebar-section { grid-column: 3; border: none; padding: 0; } + .ax-sidebar-section--actions { display: none; } + + .ax-skill-pill { + background: #eee; + border-color: #ccc; + color: #333; + } + + .ax-main { margin-left: 0; } + + .ax-content-wrap { max-width: 100%; padding: 1.5rem 0 0; } + + .ax-section { page-break-inside: avoid; } + + .ax-metric-card, + .ax-highlight-card, + .ax-impact-card, + .ax-edu-card, + .ax-cert-item { + background: #f8f8f8; + border-color: #ddd; + box-shadow: none; + } + + .ax-metrics { grid-template-columns: repeat(5, 1fr); } + .ax-highlights { grid-template-columns: repeat(2, 1fr); } + .ax-impact-cards { grid-template-columns: repeat(3, 1fr); } + + .ax-timeline__item::after { background: #ccc; } + + a { color: inherit; text-decoration: none; } + a[href]::after { content: none; } +} diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx b/backend/wordpress/wp-content/themes/apex-cv/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx new file mode 100644 index 0000000..544bf3d Binary files /dev/null and b/backend/wordpress/wp-content/themes/apex-cv/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx differ diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-profile.jpeg b/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-profile.jpeg new file mode 100644 index 0000000..4151a20 Binary files /dev/null and b/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-profile.jpeg differ diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-qr.png b/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-qr.png new file mode 100644 index 0000000..55703ad Binary files /dev/null and b/backend/wordpress/wp-content/themes/apex-cv/assets/images/patricia-flores-qr.png differ diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/images/profile-placeholder.svg b/backend/wordpress/wp-content/themes/apex-cv/assets/images/profile-placeholder.svg new file mode 100644 index 0000000..3d7dbb0 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/assets/images/profile-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/backend/wordpress/wp-content/themes/apex-cv/assets/js/theme.js b/backend/wordpress/wp-content/themes/apex-cv/assets/js/theme.js new file mode 100644 index 0000000..5c4fe90 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/assets/js/theme.js @@ -0,0 +1,183 @@ +/** + * Apex CV — theme.js + * Mobile sidebar toggle · Print button · Scroll spy + */ +(function () { + 'use strict'; + + /* ----------------------------------------------------------------------- + Helpers + ----------------------------------------------------------------------- */ + function qs(selector, ctx) { + return (ctx || document).querySelector(selector); + } + function qsa(selector, ctx) { + return Array.from((ctx || document).querySelectorAll(selector)); + } + + /* ----------------------------------------------------------------------- + Mobile sidebar + ----------------------------------------------------------------------- */ + var sidebar = qs('#ax-sidebar'); + var toggle = qs('.ax-mobile-toggle'); + var overlay = null; + + function createOverlay() { + if (overlay) return overlay; + overlay = document.createElement('div'); + overlay.className = 'ax-sidebar-overlay'; + overlay.setAttribute('aria-hidden', 'true'); + document.body.appendChild(overlay); + overlay.addEventListener('click', closeSidebar); + return overlay; + } + + function openSidebar() { + if (!sidebar || !toggle) return; + sidebar.classList.add('is-open'); + toggle.setAttribute('aria-expanded', 'true'); + var ov = createOverlay(); + // force reflow before adding class for transition + ov.offsetHeight; // eslint-disable-line no-unused-expressions + ov.classList.add('is-visible'); + document.body.style.overflow = 'hidden'; + } + + function closeSidebar() { + if (!sidebar || !toggle) return; + sidebar.classList.remove('is-open'); + toggle.setAttribute('aria-expanded', 'false'); + if (overlay) overlay.classList.remove('is-visible'); + document.body.style.overflow = ''; + } + + function isMobile() { + return window.innerWidth <= 768; + } + + if (toggle) { + toggle.addEventListener('click', function () { + var isOpen = sidebar && sidebar.classList.contains('is-open'); + if (isOpen) { + closeSidebar(); + } else { + openSidebar(); + } + }); + } + + /* Close sidebar when clicking outside on mobile */ + document.addEventListener('click', function (e) { + if (!isMobile()) return; + if (!sidebar || !sidebar.classList.contains('is-open')) return; + if (sidebar.contains(e.target)) return; + if (toggle && toggle.contains(e.target)) return; + closeSidebar(); + }); + + /* Close sidebar on nav link click (mobile) */ + qsa('.ax-nav__link').forEach(function (link) { + link.addEventListener('click', function () { + if (isMobile()) { + closeSidebar(); + } + }); + }); + + /* Close sidebar on resize back to desktop */ + window.addEventListener('resize', function () { + if (!isMobile() && sidebar && sidebar.classList.contains('is-open')) { + closeSidebar(); + } + }); + + /* ----------------------------------------------------------------------- + Print button + ----------------------------------------------------------------------- */ + qsa('[data-apex-print]').forEach(function (btn) { + btn.addEventListener('click', function () { + window.print(); + }); + }); + + /* ----------------------------------------------------------------------- + Scroll spy via IntersectionObserver + ----------------------------------------------------------------------- */ + var navLinks = qsa('.ax-nav__link'); + + if ('IntersectionObserver' in window && navLinks.length > 0) { + var activeSection = null; + + function setActive(sectionId) { + if (activeSection === sectionId) return; + activeSection = sectionId; + navLinks.forEach(function (link) { + var target = link.getAttribute('data-section') || link.getAttribute('href').replace('#', ''); + if (target === sectionId) { + link.classList.add('is-active'); + link.setAttribute('aria-current', 'true'); + } else { + link.classList.remove('is-active'); + link.removeAttribute('aria-current'); + } + }); + } + + // Collect sections that correspond to nav links + var sectionIds = navLinks.map(function (link) { + return link.getAttribute('data-section') || link.getAttribute('href').replace('#', ''); + }); + + var sectionEls = sectionIds + .map(function (id) { return document.getElementById(id); }) + .filter(Boolean); + + // Track which sections are currently intersecting + var visibleSections = new Set(); + + var observer = new IntersectionObserver( + function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting) { + visibleSections.add(entry.target.id); + } else { + visibleSections.delete(entry.target.id); + } + }); + + // Activate the topmost visible section (matches reading order) + var toActivate = null; + sectionEls.forEach(function (el) { + if (visibleSections.has(el.id) && toActivate === null) { + toActivate = el.id; + } + }); + + if (toActivate) { + setActive(toActivate); + } + }, + { + rootMargin: '-10% 0px -60% 0px', + threshold: 0 + } + ); + + sectionEls.forEach(function (el) { observer.observe(el); }); + } + + /* ----------------------------------------------------------------------- + Smooth scroll for anchor links (fallback for older browsers) + ----------------------------------------------------------------------- */ + qsa('a[href^="#"]').forEach(function (link) { + link.addEventListener('click', function (e) { + var href = link.getAttribute('href'); + if (!href || href === '#') return; + var target = document.querySelector(href); + if (!target) return; + e.preventDefault(); + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }); + }); + +})(); diff --git a/backend/wordpress/wp-content/themes/apex-cv/footer.php b/backend/wordpress/wp-content/themes/apex-cv/footer.php new file mode 100644 index 0000000..4acbb15 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/footer.php @@ -0,0 +1,5 @@ + +
+ + + diff --git a/backend/wordpress/wp-content/themes/apex-cv/front-page.php b/backend/wordpress/wp-content/themes/apex-cv/front-page.php new file mode 100644 index 0000000..b9997a0 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/front-page.php @@ -0,0 +1,351 @@ + + + + + + +
+
+ + +
+
+ +

+
+

+ + +
+ +
+ + +
+ +
+
+ + +
+
+ +

+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+

+

+
+ +
+
+ + +
+
+ +

+
+ +
+ +
+
+ + +
+
+

+

+ + + +

+

+ +
    + +
  • + +
+ +
+
+ +
+
+ + +
+
+ +

+
+ +
+ +
+ +
+

+

+ +

+ +
+
+ +
+
+ + +
+
+ +

+
+ +
+ +
+ +
+

+

+
+
+ +
+
+ + +
+
+ +

+
+ +
+ + +
+ +

+

+

+
+ + +
+ +
+ $value) : ?> +
+
+
+
+ +
+
+ + +
+ + +
+ +
+
+ +
+
+ + diff --git a/backend/wordpress/wp-content/themes/apex-cv/functions.php b/backend/wordpress/wp-content/themes/apex-cv/functions.php new file mode 100644 index 0000000..16db93e --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/functions.php @@ -0,0 +1,118 @@ + __('Menú principal', 'apex'), + ]); +} +add_action('after_setup_theme', 'apex_setup'); + +// --------------------------------------------------------------------------- +// Enqueue assets +// --------------------------------------------------------------------------- +function apex_enqueue_assets(): void +{ + wp_enqueue_style( + 'apex-fonts', + 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap', + [], + null + ); + + wp_enqueue_style( + 'apex-theme', + get_template_directory_uri() . '/assets/css/theme.css', + ['apex-fonts'], + wp_get_theme()->get('Version') + ); + + wp_enqueue_script( + 'apex-theme', + get_template_directory_uri() . '/assets/js/theme.js', + [], + wp_get_theme()->get('Version'), + true + ); +} +add_action('wp_enqueue_scripts', 'apex_enqueue_assets'); + +// --------------------------------------------------------------------------- +// Body classes +// --------------------------------------------------------------------------- +function apex_body_classes(array $classes): array +{ + $classes[] = 'apex-shell'; + + if (is_front_page()) { + $classes[] = 'apex-front-page'; + } + + return $classes; +} +add_filter('body_class', 'apex_body_classes'); + +// --------------------------------------------------------------------------- +// Navigation: render sidebar nav list +// --------------------------------------------------------------------------- +function apex_render_nav(): string +{ + $items = [ + '#sobre-mi' => __('Sobre mí', 'apex'), + '#impacto' => __('Impacto', 'apex'), + '#experiencia' => __('Experiencia', 'apex'), + '#formacion' => __('Formación', 'apex'), + '#certificaciones' => __('Certificaciones', 'apex'), + '#contacto' => __('Contacto', 'apex'), + ]; + + $html = ''; + + return $html; +} + +// --------------------------------------------------------------------------- +// Redirect sample page +// --------------------------------------------------------------------------- +function apex_redirect_default_sample_page(): void +{ + if (! is_page()) { + return; + } + + $page = get_queried_object(); + if (! $page instanceof WP_Post) { + return; + } + + if ($page->post_name === 'pagina-ejemplo' || $page->post_name === 'sample-page') { + wp_safe_redirect(home_url('/'), 301); + exit; + } +} +add_action('template_redirect', 'apex_redirect_default_sample_page', 1); diff --git a/backend/wordpress/wp-content/themes/apex-cv/header.php b/backend/wordpress/wp-content/themes/apex-cv/header.php new file mode 100644 index 0000000..69d52e0 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/header.php @@ -0,0 +1,14 @@ + + +> + + + + + +> + + +
diff --git a/backend/wordpress/wp-content/themes/apex-cv/inc/customizer.php b/backend/wordpress/wp-content/themes/apex-cv/inc/customizer.php new file mode 100644 index 0000000..4548bc0 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/inc/customizer.php @@ -0,0 +1,5 @@ + [ + 'name' => 'Patricia Soledad Flores Sánchez', + 'role' => 'Gerente / Jefa de Operaciones Financieras', + 'role2' => 'Sector financiero y microfinanzas', + 'intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.', + 'location' => 'Loja, Ecuador', + 'email' => 'patricflorsanchez@gmail.com', + 'phone' => '+593 99 469 8405', + 'linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', + 'availability' => 'Inmediata | Presencial / Híbrida / Remota', + 'focus' => 'Operaciones financieras, control interno y transformación digital', + ], + + 'metrics' => [ + ['value' => '22+', 'label' => 'años de experiencia en banca y microfinanzas'], + ['value' => '26', 'label' => 'agencias bajo liderazgo operativo nacional'], + ['value' => '48', 'label' => 'personas a cargo en la estructura operativa'], + ['value' => 'USD 5 M', 'label' => 'en operaciones mensuales de microcrédito'], + ['value' => '28.000', 'label' => 'clientes coordinados en recaudación por canales internos y externos'], + ], + + 'highlights' => [ + ['value' => '9/10', 'label' => 'calificación de gestión con resultado excelente en cuatro trimestres consecutivos'], + ['value' => '74 %', 'label' => 'reducción de requerimientos de soporte desde Dirección por descentralización de procesos'], + ['value' => '1,68 días', 'label' => 'tiempo de desembolso logrado en Q3 2025, mejor resultado del año'], + ['value' => '75 %', 'label' => 'reducción del índice de rotación del área entre 2022 y 2026'], + ], + + 'summary' => [ + 'title' => 'Perfil estratégico con enfoque en eficiencia, control y ejecución sostenible', + 'body' => 'Actualmente lidera la operatividad de 26 agencias a nivel nacional y coordina aproximadamente 1.500 operaciones de microcrédito mensuales, por un monto cercano a USD 5 millones. También articula la recaudación de cerca de 28.000 clientes por canales internos y externos, integrando visión operativa, criterio financiero, disciplina de control y liderazgo sostenido del desempeño.', + ], + + 'impact' => [ + [ + 'title' => 'Transformación digital', + 'body' => 'Lideró desembolsos por transferencia bancaria, integración de APIs bancarias y digitalización de expedientes y pagarés en 13 agencias.', + ], + [ + 'title' => 'Automatización crítica', + 'body' => 'Automatizó el seguro de desgravamen, desde el cálculo de prima hasta la liquidación contable, reduciendo tiempos de 17 a 10 días.', + ], + [ + 'title' => 'Supervisión nacional', + 'body' => 'Implementó una matriz de supervisión y certificación por agencia, elevando el índice de cumplimiento operativo del 65 % al 88 %.', + ], + ], + + 'experience' => [ + [ + 'period' => 'Abr. 2022 – Presente', + 'title' => 'Jefa de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Responsable del desempeño operativo nacional, cumplimiento del POA, control interno, digitalización de procesos y gestión de equipos.', + 'bullets' => [ + 'Cumplimiento del POA: 98 % (Q2 2025), 96 % (Q3 2025), 100 % (Q4 2025) y 105 % (Q1 2026).', + 'La transformación digital logró que los desembolsos por transferencia representen el 47,80 % del monto total en Q1 2026.', + 'Redujo los requerimientos de soporte desde Dirección en un 74 %, de 1.489 a 382, mediante la descentralización de procesos hacia las agencias.', + 'Automatizó el proceso del seguro de desgravamen con cálculo de prima, conciliación mensual, liquidación de siniestros y asientos contables automatizados.', + 'Elevó el índice de cumplimiento operativo nacional del 65 % al 88 % con la matriz de supervisión y certificación por agencia.', + 'Redujo el tiempo de atención del seguro de desgravamen de 17 a 10 días.', + 'Redujo los tiempos de desembolso a 1,68 días en Q3 2025, mejor resultado del año frente a la meta institucional máxima de 2 días.', + 'La efectividad de recuperación de mora alcanzó 90,60 % en Q1 2026, con USD 3,06 millones recuperados sobre USD 3,37 millones en mora.', + 'Redujo el índice de rotación del área del 41,46 % al 10,24 % entre diciembre de 2022 y marzo de 2026 mediante mejoras salariales, remuneración variable, evaluación trimestral y un plan de carrera con certificación de 60 horas para asistentes de caja.', + 'Participa en comités estratégicos, auditorías internas y externas, y en representación institucional ante organismos financieros internacionales de desarrollo.', + ], + ], + [ + 'period' => 'Feb. 2021 – Mar. 2022', + 'title' => 'Supervisora de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Supervisó operaciones de 18 agencias a nivel nacional y regularizó procesos críticos inexistentes o incompletos.', + 'bullets' => [ + 'Reconstruyó desde cero las bases de cálculo del seguro de desgravamen y regularizó pagos pendientes desde enero de 2021.', + 'Elaboró el instructivo de constitución de garantías hipotecarias, proceso que no existía previamente en la institución.', + 'Inició el proyecto de automatización de pagos por depósitos y transferencias.', + ], + ], + [ + 'period' => '2019 – 2021', + 'title' => 'Actualización y formación profesional', + 'company' => 'Formación independiente', + 'location' => 'Ecuador', + 'lead' => 'Etapa enfocada en fortalecimiento técnico y actualización profesional tras su salida de Mutualista Pichincha.', + 'bullets' => [ + 'SAP FI (30 h).', + 'Excel Empresarial (24 h).', + 'Prevención de Lavado de Activos — UAFE (5 h).', + ], + ], + [ + 'period' => 'Ago. 2004 – May. 2019', + 'title' => 'Oficial Operativa Regional / Jefa Operativa', + 'company' => 'Mutualista Pichincha', + 'location' => 'Loja, Ecuador', + 'lead' => 'Trayectoria ascendente de 15 años desde Cajera hasta Jefa Operativa Regional.', + 'bullets' => [ + 'Reconocida a nivel nacional por excelencia en control operativo.', + 'La agencia se mantuvo consistentemente por encima del 80 % en gestión de excepciones de crédito hipotecario.', + 'Administró proyecto inmobiliario institucional en Loja.', + 'Gestionó alertas ALA/FT con 100 % de cumplimiento en comités trimestrales y auditorías internas sin observaciones significativas de forma sostenida.', + ], + ], + [ + 'period' => 'Ene. 2002 – Nov. 2003', + 'title' => 'Cajera', + 'company' => 'Cooperativa de Ahorro y Crédito El Porvenir', + 'location' => 'Loja, Ecuador', + 'lead' => 'Inicio de carrera en el frente operativo financiero con disciplina de caja y apoyo contable.', + 'bullets' => [ + 'Gestión de caja sin faltantes ni sobrantes.', + 'Contrato renovado de forma anticipada a los tres meses.', + 'Apoyo al área de Contabilidad y reemplazo temporal de la contadora durante vacaciones.', + ], + ], + ], + + 'education' => [ + [ + 'year' => '2021', + 'title' => 'Máster Universitario en Dirección y Planificación Financiera', + 'institution' => 'Universidad Europea Miguel de Cervantes — Valladolid, España', + 'detail' => 'Título reconocido por la SENECYT — Ecuador, febrero de 2023 | Reg. 2021/394270.', + ], + [ + 'year' => '2007', + 'title' => 'Ingeniera en Administración en Banca y Finanzas', + 'institution' => 'Universidad Técnica Particular de Loja (UTPL)', + 'detail' => '', + ], + ], + + 'certifications' => [ + ['year' => '2026', 'title' => 'Mujeres que Lideran: Alta Gerencia', 'issuer' => 'Foro UNIR — Universidad Internacional de La Rioja'], + ['year' => '2025', 'title' => 'Programa Internacional de Habilidades Gerenciales (48 h)', 'issuer' => 'IDEASGROUP — Ecuador / México'], + ['year' => '2025', 'title' => 'Transforma tu Trabajo con Inteligencia Artificial (12 h)', 'issuer' => 'Red de Instituciones Financieras de Desarrollo'], + ['year' => '2025', 'title' => 'Excel: Básico a Dashboard (40 h)', 'issuer' => 'DMARURI Training & Consulting'], + ['year' => '2024', 'title' => 'Administración por Procesos — BPM (64 h)', 'issuer' => 'Escuela Politécnica Nacional — CEC'], + ['year' => '2023', 'title' => 'Formación de Formadores (80 h)', 'issuer' => 'Ministerio del Trabajo del Ecuador | Certificación vigente hasta 2028'], + ['year' => '2023', 'title' => 'Power BI (40 h) | Indicadores Clave de Desempeño — KPI (12 h)', 'issuer' => 'Liderazgo Capacitación & Eventos'], + ['year' => '2020', 'title' => 'Gestión Financiera ERP SAP FI — Key User (30 h)', 'issuer' => 'Socio SAP certificado'], + ['year' => '2020', 'title' => 'Prevención de Lavado de Activos y Financiamiento de Delitos', 'issuer' => 'UAFE — Ecuador'], + ['year' => '2013', 'title' => 'Tributación Integral (36 h)', 'issuer' => 'Universidad Técnica Particular de Loja'], + ], + + 'competencies' => [ + 'Finanzas y Operaciones' => [ + ['name' => 'Gestión de operaciones financieras', 'pct' => 98], + ['name' => 'Conciliación bancaria', 'pct' => 97], + ['name' => 'Originación de crédito', 'pct' => 95], + ['name' => 'Gestión de cartera', 'pct' => 95], + ['name' => 'Control interno', 'pct' => 96], + ['name' => 'Tesorería operativa', 'pct' => 94], + ['name' => 'Microfinanzas', 'pct' => 97], + ['name' => 'ALA/FT', 'pct' => 95], + ], + 'Tecnología y Procesos' => [ + ['name' => 'Transformación digital', 'pct' => 92], + ['name' => 'BPM', 'pct' => 88], + ['name' => 'Integración de APIs / Web Services','pct' => 82], + ['name' => 'SAP FI', 'pct' => 80], + ['name' => 'Excel avanzado y dashboards', 'pct' => 90], + ['name' => 'Power BI', 'pct' => 85], + ['name' => 'IA aplicada', 'pct' => 78], + ['name' => 'Core financiero', 'pct' => 93], + ], + 'Liderazgo' => [ + ['name' => 'Equipos de hasta 48 personas', 'pct' => 95], + ['name' => 'Remuneración variable', 'pct' => 90], + ['name' => 'Gestión del cambio', 'pct' => 92], + ['name' => 'Formación de formadores', 'pct' => 88], + ['name' => 'Diversidad, equidad e inclusión', 'pct' => 87], + ], + ], + + 'volunteer' => [ + 'title' => 'Voluntaria — Hogar San Camilo de Lellis', + 'year' => '2025', + 'body' => 'Hogar de niñas con discapacidad | Loja, Ecuador. Alineado a la visión y misión de responsabilidad social de la Fundación FACES Microfinanzas.', + ], + + 'additional' => [ + 'Idiomas' => 'Español nativo | Inglés básico en formación activa', + 'Disponibilidad' => 'Inmediata | Presencial / Híbrida / Remota', + 'C.I.' => '1103591648', + 'Fecha de nacimiento'=> '3 de agosto de 1980', + 'Web profesional' => 'pflores.linkedcorp.net', + ], + ]; +} diff --git a/backend/wordpress/wp-content/themes/apex-cv/inc/visits.php b/backend/wordpress/wp-content/themes/apex-cv/inc/visits.php new file mode 100644 index 0000000..2caeb12 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/inc/visits.php @@ -0,0 +1,130 @@ + 0) { + $views = (int) get_post_meta($frontPage, '_apex_views', true); + update_post_meta($frontPage, '_apex_views', $views + 1); + } + + return; + } + + $postId = get_queried_object_id(); + if ($postId > 0) { + $views = (int) get_post_meta($postId, '_apex_views', true); + update_post_meta($postId, '_apex_views', $views + 1); + } +} +add_action('template_redirect', 'apex_track_visit'); + +function apex_get_client_ip(): string +{ + $keys = [ + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', + 'REMOTE_ADDR', + ]; + + foreach ($keys as $key) { + if (! empty($_SERVER[$key])) { + $value = explode(',', (string) $_SERVER[$key]); + return trim($value[0]); + } + } + + return '0.0.0.0'; +} + +// --------------------------------------------------------------------------- +// Dashboard widget +// --------------------------------------------------------------------------- + +function apex_register_visits_dashboard_widget(): void +{ + wp_add_dashboard_widget( + 'apex_visits_widget', + __('Apex CV: Visitas', 'apex'), + 'apex_render_visits_dashboard_widget' + ); +} +add_action('wp_dashboard_setup', 'apex_register_visits_dashboard_widget'); + +function apex_render_visits_dashboard_widget(): void +{ + $total = (int) get_option('apex_total_visits', 0); + $frontPage = (int) get_option('page_on_front'); + $frontViews = $frontPage > 0 ? (int) get_post_meta($frontPage, '_apex_views', true) : 0; + ?> +
+

+

+

+
+ +
+
+ + +
> +

+
+ +
+
+ + +

+ +
+
+ diff --git a/backend/wordpress/wp-content/themes/apex-cv/style.css b/backend/wordpress/wp-content/themes/apex-cv/style.css new file mode 100644 index 0000000..4ed22b4 --- /dev/null +++ b/backend/wordpress/wp-content/themes/apex-cv/style.css @@ -0,0 +1,9 @@ +/* +Theme Name: Apex CV +Theme URI: https://pflores.linkedcorp.net +Description: Tema oscuro y elegante. Sidebar fijo izquierdo, paleta dark con acento cian. +Version: 1.0.0 +Requires at least: 6.8 +Requires PHP: 8.1 +Text Domain: apex +*/ diff --git a/backend/wordpress/wp-content/themes/lumina b/backend/wordpress/wp-content/themes/lumina new file mode 120000 index 0000000..c28285b --- /dev/null +++ b/backend/wordpress/wp-content/themes/lumina @@ -0,0 +1 @@ +/home/mgonzalez/desarrollo/web/pflores.linkedcorp.net/frontend/lumina-cv-theme \ No newline at end of file diff --git a/backend/wordpress/wp-content/themes/persona-cv b/backend/wordpress/wp-content/themes/persona-cv new file mode 120000 index 0000000..5bb0adc --- /dev/null +++ b/backend/wordpress/wp-content/themes/persona-cv @@ -0,0 +1 @@ +/home/mgonzalez/desarrollo/web/pflores.linkedcorp.net/frontend/persona-cv-theme \ No newline at end of file diff --git a/docs/instalacion-wordpress.md b/docs/instalacion-wordpress.md index 6a444f4..91c79fa 100644 --- a/docs/instalacion-wordpress.md +++ b/docs/instalacion-wordpress.md @@ -1,31 +1,45 @@ # Instalación local -## Puerto +## Acceso -El primer puerto libre detectado fue `8081`. +| Desde | URL | +|---|---| +| Este equipo | `http://127.0.0.1:8081` | +| Red local | `http://192.168.222.40:8081` | +| Admin WP | `http://192.168.222.40:8081/wp-admin` | -## Requisitos faltantes en este entorno +## Arranque -Para que WordPress quede ejecutable aquí mismo, el sistema necesita: +```bash +bash backend/start-wordpress.sh +``` -- PHP con `mysqli` o `pdo_mysql` -- PHP con `dom/xml` -- Recomendado: `mbstring`, `curl`, `gd`, `zip` -- Un servidor MariaDB/MySQL activo o credenciales de una base existente +El servidor escucha en `0.0.0.0:8081` (todas las interfaces). La URL se genera dinámicamente según el host de la petición, por lo que funciona desde cualquier IP sin cambiar configuración. + +## Temas disponibles + +| Slug | Descripción | +|---|---| +| `persona-cv` | **Activo** — Una página, Personage-style, rojo, timeline central | +| `lumina` | Claro, teal+ámbar, sidebar izquierdo | +| `apex-cv` | Oscuro, cian eléctrico, sidebar fijo izquierdo | +| `cvolio` | Oscuro clásico, glassmorphism, paleta café | + +Para cambiar de tema: +```sql +UPDATE cv_options SET option_value = 'slug-del-tema' +WHERE option_name IN ('template','stylesheet'); +``` ## Archivos clave -- Configuración principal: `backend/wordpress/wp-config.php` -- Tema editable: `frontend/cvfolio-theme` +- Configuración: `backend/wordpress/wp-config.php` +- Temas: `frontend/*/` +- Variables de entorno: `.env` - Script de arranque: `backend/start-wordpress.sh` -## Uso del tema +## Requisitos -1. Activa el tema `CVolio`. -2. Crea una página de inicio y asígnala como portada. -3. Edita el contenido principal con bloques de WordPress. -4. Personaliza cabecera, contacto y habilidades desde `Apariencia > Personalizar`. - -## Siguiente paso recomendado - -Instalar las extensiones PHP faltantes y levantar MariaDB local, o apuntar WordPress a una base existente. +- PHP con `mysqli` y `dom` +- Recomendado: `mbstring`, `curl`, `gd`, `zip` +- Base de datos MariaDB en `192.168.222.20` diff --git a/frontend/cvfolio-theme/assets/css/theme.css b/frontend/cvfolio-theme/assets/css/theme.css index d4d0a7f..de58936 100644 --- a/frontend/cvfolio-theme/assets/css/theme.css +++ b/frontend/cvfolio-theme/assets/css/theme.css @@ -1,16 +1,20 @@ :root { - --bg: #f4efe7; - --bg-strong: #eadfd1; - --surface: rgba(255, 255, 255, 0.78); - --surface-strong: #fffdf8; - --text: #1f1d1a; - --muted: #6f685f; - --line: rgba(31, 29, 26, 0.1); - --accent: #d4582b; - --accent-dark: #9f3814; - --accent-soft: #f6c9ae; - --shadow: 0 24px 60px rgba(89, 57, 32, 0.12); - --radius-lg: 28px; + --bg: #ece8e1; + --bg-soft: #f7f5f0; + --surface: rgba(255, 255, 255, 0.82); + --surface-strong: #ffffff; + --surface-dark: #10161d; + --text: #17202a; + --muted: #5d6773; + --line: rgba(23, 32, 42, 0.1); + --accent: #9a7b4f; + --accent-strong: #7b5f39; + --accent-soft: #ece2d1; + --deep: #13202d; + --success: #2f6c60; + --shadow: 0 24px 60px rgba(16, 22, 29, 0.08); + --radius-xl: 32px; + --radius-lg: 24px; --radius-md: 18px; --radius-sm: 999px; --container: 1180px; @@ -28,12 +32,11 @@ html { body { margin: 0; - font-family: "Outfit", sans-serif; color: var(--text); + font-family: "Manrope", sans-serif; background: - radial-gradient(circle at top left, rgba(212, 88, 43, 0.14), transparent 34%), - radial-gradient(circle at right 20%, rgba(135, 148, 255, 0.12), transparent 24%), - linear-gradient(180deg, #f6f1e8 0%, #f9f6f1 48%, #efe5d8 100%); + radial-gradient(circle at top left, rgba(154, 123, 79, 0.15), transparent 26%), + linear-gradient(180deg, #f8f6f2 0%, #f0ece5 46%, #e6e0d5 100%); } a { @@ -41,13 +44,18 @@ a { text-decoration: none; } +img { + max-width: 100%; +} + .site-bg { position: fixed; inset: 0; - background-image: linear-gradient(rgba(31, 29, 26, 0.03) 1px, transparent 1px), - linear-gradient(90deg, rgba(31, 29, 26, 0.03) 1px, transparent 1px); - background-size: 24px 24px; - mask-image: radial-gradient(circle at center, black 45%, transparent 90%); + background: + linear-gradient(rgba(19, 32, 45, 0.028) 1px, transparent 1px), + linear-gradient(90deg, rgba(19, 32, 45, 0.028) 1px, transparent 1px); + background-size: 28px 28px; + mask-image: radial-gradient(circle at center, black 38%, transparent 86%); pointer-events: none; } @@ -67,19 +75,19 @@ a { } .site-header { - padding: 1.2rem 0 0; + padding-top: 1.2rem; } .site-header__inner { - display: flex; + display: grid; + grid-template-columns: auto 1fr auto; align-items: center; - justify-content: space-between; gap: 1rem; - padding: 1rem 1.25rem; - border: 1px solid var(--line); + padding: 0.95rem 1.2rem; + border: 1px solid rgba(19, 32, 45, 0.08); border-radius: var(--radius-sm); - background: rgba(255, 253, 248, 0.78); - backdrop-filter: blur(18px); + background: rgba(255, 255, 255, 0.72); + backdrop-filter: blur(16px); box-shadow: var(--shadow); } @@ -87,94 +95,182 @@ a { h1, h2, h3 { - font-family: "Fraunces", serif; + font-family: "Cormorant Garamond", serif; } .site-brand { - font-size: 1.1rem; + font-size: 1.5rem; font-weight: 700; + letter-spacing: 0.02em; } -.site-nav-toggle { - display: none; - border: 0; - border-radius: var(--radius-sm); - background: var(--accent); - color: #fff; - padding: 0.85rem 1rem; - font: inherit; +.site-nav { + justify-self: center; } .site-nav__menu { display: flex; align-items: center; - gap: 1rem; + justify-content: center; + gap: 1.1rem; + list-style: none; padding: 0; margin: 0; - list-style: none; } .site-nav__menu a { color: var(--muted); - font-weight: 500; + font-size: 0.95rem; + font-weight: 600; +} + +.site-nav__menu a:hover, +.site-nav__menu a:focus-visible { + color: var(--text); +} + +.site-nav-toggle, +.site-print, +.btn, +.cv-chip, +.skill-pill { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 44px; + border-radius: var(--radius-sm); + font: inherit; +} + +.site-nav-toggle { + display: none; + border: 0; + padding: 0.75rem 1rem; + background: var(--deep); + color: #fff; +} + +.site-print { + border: 1px solid rgba(19, 32, 45, 0.12); + background: rgba(255, 255, 255, 0.7); + color: var(--text); + padding: 0.7rem 1rem; + cursor: pointer; } .hero { display: grid; - grid-template-columns: minmax(0, 1.55fr) minmax(300px, 0.95fr); + grid-template-columns: minmax(0, 1.6fr) minmax(320px, 0.9fr); gap: 1.5rem; - padding: 2rem 0 1.5rem; + padding: 1.8rem 0; } .hero__copy, .profile-card, .content-card, .side-card { - border: 1px solid rgba(31, 29, 26, 0.08); + border: 1px solid rgba(19, 32, 45, 0.08); background: var(--surface); backdrop-filter: blur(14px); box-shadow: var(--shadow); } .hero__copy { - padding: 2rem; + position: relative; + overflow: hidden; + padding: 2.1rem; border-radius: 40px; } +.hero__copy::after { + content: ""; + position: absolute; + right: -80px; + bottom: -80px; + width: 260px; + height: 260px; + border-radius: 50%; + background: radial-gradient(circle, rgba(154, 123, 79, 0.14), transparent 68%); +} + .eyebrow { - margin: 0 0 1rem; - font-size: 0.82rem; + margin: 0 0 0.9rem; + color: var(--accent-strong); + font-size: 0.8rem; + font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase; - color: var(--accent-dark); } .hero h1 { margin: 0; - font-size: clamp(2.6rem, 5vw, 5.5rem); - line-height: 0.95; + font-size: clamp(3.1rem, 6vw, 5.5rem); + line-height: 0.92; + letter-spacing: -0.03em; } .hero__role { margin: 1rem 0 0; - font-size: clamp(1.2rem, 2vw, 1.6rem); - color: var(--accent-dark); + color: var(--deep); + font-size: clamp(1.2rem, 2vw, 1.55rem); + font-weight: 700; } .hero__intro, .entry-content, -.side-card p { +.side-card p, +.timeline__item p, +.demo-panel li, +.profile-card__label, +.profile-card__highlights span, +.metric-card span, +.achievement-card span { color: var(--muted); - line-height: 1.7; - font-size: 1rem; + line-height: 1.75; } +.hero__intro { + max-width: 58ch; + margin-top: 1rem; +} + +.hero__metrics, .hero__actions, .chip-grid, -.skill-cloud { +.skill-cloud, +.contact-stack { display: flex; flex-wrap: wrap; - gap: 0.8rem; + gap: 0.85rem; +} + +.hero__metrics { + margin-top: 1.5rem; +} + +.hero__metrics--wide .metric-card { + flex: 1 1 180px; +} + +.metric-card { + min-width: 170px; + padding: 1rem 1.05rem; + border-radius: 22px; + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(19, 32, 45, 0.08); +} + +.hero__metrics--wide { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.metric-card strong, +.achievement-card strong { + display: block; + color: var(--deep); + font-size: 1.5rem; + font-weight: 800; } .hero__actions { @@ -184,59 +280,121 @@ h3 { .btn, .cv-chip, .skill-pill { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 44px; padding: 0.75rem 1.05rem; - border-radius: var(--radius-sm); +} + +.btn { + border: 0; + cursor: pointer; } .btn--primary { - background: var(--accent); + background: var(--deep); color: #fff; - font-weight: 600; + font-weight: 700; +} + +.btn--secondary { + border: 1px solid rgba(19, 32, 45, 0.12); + background: rgba(255, 255, 255, 0.6); + color: var(--text); } .cv-chip, .skill-pill { - background: rgba(255, 255, 255, 0.8); - border: 1px solid rgba(31, 29, 26, 0.08); - color: var(--text); + border: 1px solid rgba(19, 32, 45, 0.08); + background: rgba(255, 255, 255, 0.78); } .profile-card { - border-radius: 30px; - padding: 1.5rem; height: 100%; + padding: 1.45rem; + border-radius: 30px; +} + +.profile-card__media { + margin-bottom: 1.2rem; + border-radius: 24px; + overflow: hidden; + background: + radial-gradient(circle at top left, rgba(154, 123, 79, 0.2), transparent 40%), + linear-gradient(180deg, #f9f5ef 0%, #ede4d7 100%); } .profile-card__label { - margin: 0 0 0.5rem; - color: var(--muted); - font-size: 0.95rem; + margin: 0 0 0.45rem; + font-size: 0.92rem; } .profile-card h2, .side-card h2, -.entry-title { +.entry-title, +.demo-section h2 { margin: 0 0 1rem; - font-size: clamp(1.5rem, 2vw, 2rem); + font-size: clamp(1.9rem, 3vw, 2.45rem); + line-height: 1.04; +} + +.profile-card__highlights { + display: grid; + gap: 0.8rem; + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid rgba(19, 32, 45, 0.08); +} + +.profile-card__highlights strong { + display: block; + margin-top: 0.15rem; +} + +.profile-card__contact { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; +} + +.profile-card--executive .profile-card__media--portrait { + aspect-ratio: 4 / 4.35; +} + +.profile-card--executive .profile-card__media--portrait img { + width: 100%; + height: 100%; + object-fit: cover; } .content-wrap { display: grid; - padding: 0 0 2rem; + padding-bottom: 2rem; } .content-wrap--front { - grid-template-columns: minmax(0, 1.5fr) minmax(280px, 0.8fr); + grid-template-columns: minmax(0, 1.48fr) minmax(290px, 0.82fr); gap: 1.5rem; } .content-card { - border-radius: 32px; padding: 2rem; + border-radius: var(--radius-xl); +} + +.content-card--resume { + padding: 2.2rem; +} + +.resume-section + .resume-section { + margin-top: 2.6rem; +} + +.section-head { + display: grid; + gap: 0.35rem; + margin-bottom: 1.15rem; +} + +.lead-copy { + font-size: 1.08rem; } .entry-content > *:first-child { @@ -245,23 +403,213 @@ h3 { .entry-content h2, .entry-content h3 { - margin-top: 1.8rem; - margin-bottom: 0.75rem; -} - -.entry-content .wp-block-group, -.entry-content .wp-block-columns, -.entry-content .wp-block-cover, -.entry-content .wp-block-media-text { - margin-top: 1.5rem; - margin-bottom: 1.5rem; + margin-top: 1.6rem; + margin-bottom: 0.8rem; } .entry-content .wp-block-group.cv-section { padding: 1.5rem; + border-radius: 22px; + background: rgba(255, 255, 255, 0.58); + border: 1px solid rgba(19, 32, 45, 0.08); +} + +.demo-section + .demo-section, +.demo-section + .demo-grid, +.demo-grid + .demo-section { + margin-top: 2rem; +} + +.achievement-grid, +.demo-grid { + display: grid; + gap: 1rem; +} + +.achievement-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.achievement-card, +.timeline__item, +.demo-panel { + padding: 1.25rem; + border: 1px solid rgba(19, 32, 45, 0.08); border-radius: 24px; - background: rgba(255, 255, 255, 0.52); - border: 1px solid rgba(31, 29, 26, 0.08); + background: rgba(255, 255, 255, 0.68); +} + +.achievement-card { + background: linear-gradient(180deg, rgba(19, 32, 45, 0.96) 0%, rgba(24, 39, 55, 0.96) 100%); +} + +.achievement-grid--executive { + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-bottom: 1rem; +} + +.achievement-card strong, +.achievement-card span { + color: #fff; +} + +.achievement-card--stat { + min-height: 172px; +} + +.impact-grid, +.credentials-grid { + display: grid; + gap: 1rem; +} + +.impact-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.impact-card, +.credential-card, +.cert-item { + padding: 1.25rem; + border: 1px solid rgba(19, 32, 45, 0.08); + border-radius: 22px; + background: rgba(255, 255, 255, 0.74); +} + +.impact-card h3, +.credential-card h3, +.cert-item h3, +.competency-block h3 { + margin: 0 0 0.55rem; + font-size: 1.2rem; +} + +.credentials-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.credential-card__year, +.cert-item__year { + display: inline-flex; + margin-bottom: 0.9rem; + padding: 0.3rem 0.7rem; + border-radius: var(--radius-sm); + background: var(--accent-soft); + color: var(--accent-strong); + font-size: 0.8rem; + font-weight: 800; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.credential-card__detail, +.side-card__meta, +.timeline__meta, +.cert-item p { + color: var(--muted); +} + +.timeline__meta { + margin-top: -0.15rem; + margin-bottom: 0.75rem; + font-weight: 700; +} + +.timeline__bullets { + margin: 0.9rem 0 0; + padding-left: 1rem; + color: var(--muted); + line-height: 1.7; +} + +.timeline__bullets li + li { + margin-top: 0.45rem; +} + +.cert-list { + display: grid; + gap: 0.9rem; +} + +.cert-item { + display: grid; + grid-template-columns: 92px 1fr; + gap: 1rem; + align-items: start; +} + +.cert-item__year { + justify-content: center; + width: 100%; + margin-bottom: 0; +} + +.timeline { + position: relative; + display: grid; + gap: 1rem; +} + +.timeline::before { + content: ""; + position: absolute; + top: 0.5rem; + bottom: 0.5rem; + left: 0.75rem; + width: 1px; + background: rgba(154, 123, 79, 0.4); +} + +.timeline__item { + position: relative; + padding-left: 2.25rem; +} + +.timeline__item::before { + content: ""; + position: absolute; + top: 1.2rem; + left: 0.35rem; + width: 0.8rem; + height: 0.8rem; + border-radius: 50%; + background: var(--accent); + box-shadow: 0 0 0 6px rgba(154, 123, 79, 0.18); +} + +.timeline__year { + display: inline-flex; + margin-bottom: 0.75rem; + padding: 0.36rem 0.78rem; + border-radius: var(--radius-sm); + background: var(--accent-soft); + color: var(--accent-strong); + font-size: 0.82rem; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.timeline__item h3 { + margin: 0 0 0.45rem; + font-size: 1.35rem; +} + +.timeline__item--rich { + padding-right: 1.2rem; +} + +.demo-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.demo-panel ul { + margin: 0; + padding-left: 1.05rem; +} + +.demo-panel--accent { + background: linear-gradient(180deg, rgba(236, 226, 209, 0.75), rgba(255, 255, 255, 0.72)); } .side-stack { @@ -270,42 +618,157 @@ h3 { align-content: start; } +.side-stack--resume { + position: sticky; + top: 1.5rem; + align-self: start; +} + .side-card { - padding: 1.5rem; - border-radius: 26px; + padding: 1.45rem; + border-radius: 28px; +} + +.side-card--business-card { + background: linear-gradient(180deg, rgba(19, 32, 45, 0.98) 0%, rgba(20, 31, 43, 0.98) 100%); + color: #fff; +} + +.side-card--business-card .eyebrow, +.side-card--business-card p, +.side-card--business-card h2 { + color: #fff; +} + +.competency-block + .competency-block { + margin-top: 1.25rem; +} + +.skill-cloud { + gap: 0.75rem; +} + +.skill-pill { + color: var(--text); + font-weight: 600; } .site-footer { - padding: 0 0 2.5rem; + padding-bottom: 2rem; } .site-footer__inner { - padding: 1rem 0; + padding: 0.8rem 0; color: var(--muted); text-align: center; } -@media (max-width: 920px) { - .hero, - .content-wrap--front { - grid-template-columns: 1fr; +.info-list { + display: grid; + gap: 0.95rem; + margin: 0 0 1.2rem; +} + +.info-list div { + display: grid; + gap: 0.2rem; +} + +.info-list dt { + color: var(--accent-strong); + font-size: 0.82rem; + font-weight: 800; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.info-list dd { + margin: 0; + color: var(--text); +} + +.business-card { + padding: 1rem; + border-radius: 22px; + background: + radial-gradient(circle at top left, rgba(154, 123, 79, 0.35), transparent 34%), + linear-gradient(160deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.04)); + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.business-card__top { + margin-bottom: 1rem; +} + +.business-card__name { + margin: 0; + font-family: "Cormorant Garamond", serif; + font-size: 1.55rem; + font-weight: 700; + line-height: 1; +} + +.business-card__role { + margin: 0.45rem 0 0; + color: rgba(255, 255, 255, 0.82); + font-size: 0.94rem; + line-height: 1.5; +} + +.business-card__body { + display: grid; + grid-template-columns: 110px 1fr; + gap: 1rem; + align-items: center; +} + +.business-card__qr { + padding: 0.55rem; + border-radius: 18px; + background: #fff; +} + +.business-card__qr img { + display: block; + width: 100%; + height: auto; +} + +.business-card__info { + display: grid; + gap: 0.35rem; +} + +.business-card__info p, +.business-card__info a { + margin: 0; + color: rgba(255, 255, 255, 0.88); + word-break: break-word; +} + +@media (max-width: 980px) { + .site-header__inner { + grid-template-columns: auto auto; } .site-nav-toggle { display: inline-flex; + justify-self: end; + } + + .site-print { + order: 4; + grid-column: 1 / -1; + justify-self: start; } .site-nav { - position: absolute; - top: calc(100% + 0.75rem); - right: 1rem; - left: 1rem; + grid-column: 1 / -1; display: none; - padding: 1rem; - border: 1px solid var(--line); - border-radius: 20px; - background: rgba(255, 253, 248, 0.96); - box-shadow: var(--shadow); + padding: 0.95rem; + border-radius: 18px; + border: 1px solid rgba(19, 32, 45, 0.08); + background: rgba(255, 255, 255, 0.92); } .site-nav.is-open { @@ -313,29 +776,96 @@ h3 { } .site-nav__menu { - flex-direction: column; align-items: flex-start; + flex-direction: column; + } + + .hero, + .content-wrap--front { + grid-template-columns: 1fr; + } + + .achievement-grid { + grid-template-columns: 1fr; + } + + .hero__metrics--wide { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .impact-grid, + .credentials-grid { + grid-template-columns: 1fr; + } + + .side-stack--resume { + position: static; } } @media (max-width: 640px) { - .site-header__inner, - .hero__copy, - .profile-card, - .content-card, - .side-card { - border-radius: 24px; - } - .site-header__inner, .hero__copy, .profile-card, .content-card, .side-card { padding: 1.2rem; + border-radius: 22px; } .hero h1 { - font-size: 2.5rem; + font-size: 2.75rem; + } + + .metric-card { + min-width: unset; + width: 100%; + } + + .hero__metrics--wide { + grid-template-columns: 1fr; + } + + .demo-grid { + grid-template-columns: 1fr; + } + + .cert-item { + grid-template-columns: 1fr; + } + + .business-card__body { + grid-template-columns: 1fr; + } +} + +@media print { + .site-header, + .site-footer, + .site-bg, + .hero__actions, + .site-print, + .site-nav-toggle { + display: none !important; + } + + body { + background: #fff; + } + + .hero, + .content-wrap--front { + grid-template-columns: 1fr; + } + + .hero__copy, + .profile-card, + .content-card, + .side-card, + .timeline__item, + .demo-panel, + .achievement-card { + box-shadow: none; + background: #fff; } } diff --git a/frontend/cvfolio-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx b/frontend/cvfolio-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx new file mode 100644 index 0000000..544bf3d Binary files /dev/null and b/frontend/cvfolio-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx differ diff --git a/frontend/cvfolio-theme/assets/images/patricia-flores-profile.jpeg b/frontend/cvfolio-theme/assets/images/patricia-flores-profile.jpeg new file mode 100644 index 0000000..4151a20 Binary files /dev/null and b/frontend/cvfolio-theme/assets/images/patricia-flores-profile.jpeg differ diff --git a/frontend/cvfolio-theme/assets/images/patricia-flores-qr.png b/frontend/cvfolio-theme/assets/images/patricia-flores-qr.png new file mode 100644 index 0000000..55703ad Binary files /dev/null and b/frontend/cvfolio-theme/assets/images/patricia-flores-qr.png differ diff --git a/frontend/cvfolio-theme/assets/images/profile-placeholder.svg b/frontend/cvfolio-theme/assets/images/profile-placeholder.svg new file mode 100644 index 0000000..3d7dbb0 --- /dev/null +++ b/frontend/cvfolio-theme/assets/images/profile-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/cvfolio-theme/assets/js/theme.js b/frontend/cvfolio-theme/assets/js/theme.js index 75207ac..fff06f3 100644 --- a/frontend/cvfolio-theme/assets/js/theme.js +++ b/frontend/cvfolio-theme/assets/js/theme.js @@ -1,6 +1,7 @@ document.addEventListener("DOMContentLoaded", () => { const toggle = document.querySelector(".site-nav-toggle"); const nav = document.querySelector(".site-nav"); + const printButtons = document.querySelectorAll("[data-cvolio-print]"); if (toggle && nav) { toggle.addEventListener("click", () => { @@ -9,4 +10,10 @@ document.addEventListener("DOMContentLoaded", () => { nav.classList.toggle("is-open", !expanded); }); } + + printButtons.forEach((button) => { + button.addEventListener("click", () => { + window.print(); + }); + }); }); diff --git a/frontend/cvfolio-theme/front-page.php b/frontend/cvfolio-theme/front-page.php index ae43fcd..bede85f 100644 --- a/frontend/cvfolio-theme/front-page.php +++ b/frontend/cvfolio-theme/front-page.php @@ -4,64 +4,235 @@ declare(strict_types=1); get_header(); -$hero_name = get_theme_mod('cvolio_hero_name', get_bloginfo('name')); -$hero_role = get_theme_mod('cvolio_hero_role', __('Profesional creativo y estratega digital', 'cvolio')); -$hero_intro = get_theme_mod('cvolio_hero_intro', __('Presenta tu experiencia, logros, proyectos y propuesta de valor con un formato claro, elegante y listo para cualquier dispositivo.', 'cvolio')); -$hero_location = get_theme_mod('cvolio_contact_location', 'Guayaquil, Ecuador'); -$hero_email = get_theme_mod('cvolio_contact_email', 'hola@ejemplo.com'); -$hero_phone = get_theme_mod('cvolio_contact_phone', '+593 999 999 999'); -$hero_cta_label = get_theme_mod('cvolio_hero_cta_label', __('Ver experiencia', 'cvolio')); -$hero_cta_url = get_theme_mod('cvolio_hero_cta_url', '#perfil'); -$skills = array_filter(array_map('trim', explode(',', (string) get_theme_mod('cvolio_skills_list', 'UX Strategy, Liderazgo, Frontend, WordPress, SEO, Analitica')))); -$linkedin = get_theme_mod('cvolio_social_linkedin', ''); -$github = get_theme_mod('cvolio_social_github', ''); -$portfolio = get_theme_mod('cvolio_social_portfolio', ''); +$cv = function_exists('pflores_get_cv_data') ? pflores_get_cv_data() : cvolio_cv_data(); +$hero = $cv['hero']; +$metrics = $cv['metrics']; +$highlights = $cv['highlights']; +$summary = $cv['summary']; +$impact = $cv['impact']; +$experience = $cv['experience']; +$education = $cv['education']; +$certifications = $cv['certifications']; +$competencies = $cv['competencies']; +$volunteer = $cv['volunteer']; +$additional = $cv['additional']; +$qrImage = get_template_directory_uri() . '/assets/images/patricia-flores-qr.png'; +$publicUrl = 'https://pflores.linkedcorp.net'; + +$heroName = $hero['name']; +$heroRole = $hero['role']; +$heroIntro = $hero['intro']; +$heroLocation = $hero['location']; +$heroEmail = $hero['email']; +$heroPhone = $hero['phone']; +$heroCtaLabel = 'Ver trayectoria'; +$heroCtaUrl = '#experiencia'; +$linkedin = $hero['linkedin']; +// Compatibilidad CVolio: skills como array plano para pills +$skillsFlat = []; +foreach ($competencies as $groupItems) { + foreach ((array) $groupItems as $item) { + $skillsFlat[] = is_array($item) ? ($item['name'] ?? '') : (string) $item; + } +} +$skills = array_filter($skillsFlat); +$acfPhoto = $cv['photo'] ?? ''; +$profileImage = $acfPhoto ?: get_template_directory_uri() . '/assets/images/patricia-flores-profile.jpeg'; +$cvFile = get_template_directory_uri() . '/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx'; ?> -
+
-

-

-

-

+

+

+

+

+ +
+ +
+ + +
+ +
+
- - + + - + + + +
-
-
+
+
+
+
+

+

+
+

+
+ +
+
+

+

+
+ +
+ +
+ + +
+ +
+ +
+ +
+

+

+
+ +
+
+ +
+
+

+

+
+ +
+ +
+ +
+

+

+

+
    + +
  • + +
+
+
+ +
+
+ +
+
+

+

+
+ +
+ +
+ +

+

+ +

+ +
+ +
+
+ +
+
+

+

+
+ +
+ +
+ +
+

+

+
+
+ +
+
+ -
> -
- -
-
+ +
+
+

+

+
+
+ +
+
+
-
diff --git a/frontend/cvfolio-theme/functions.php b/frontend/cvfolio-theme/functions.php index ac20564..7d2eca5 100644 --- a/frontend/cvfolio-theme/functions.php +++ b/frontend/cvfolio-theme/functions.php @@ -3,7 +3,9 @@ declare(strict_types=1); require_once get_template_directory() . '/inc/customizer.php'; +require_once get_template_directory() . '/inc/cv-data.php'; require_once get_template_directory() . '/inc/patterns.php'; +require_once get_template_directory() . '/inc/visits.php'; function cvolio_setup(): void { @@ -24,7 +26,7 @@ function cvolio_enqueue_assets(): void { wp_enqueue_style( 'cvolio-fonts', - 'https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Fraunces:opsz,wght@9..144,600;9..144,700&display=swap', + 'https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Cormorant+Garamond:wght@500;600;700&display=swap', [], null ); @@ -58,6 +60,60 @@ function cvolio_body_classes(array $classes): array } add_filter('body_class', 'cvolio_body_classes'); +function cvolio_redirect_default_sample_page(): void +{ + if (! is_page()) { + return; + } + + $samplePage = get_queried_object(); + if (! $samplePage instanceof WP_Post) { + return; + } + + if ($samplePage->post_name !== 'pagina-ejemplo') { + return; + } + + wp_safe_redirect(home_url('/'), 301); + exit; +} +add_action('template_redirect', 'cvolio_redirect_default_sample_page', 1); + +function cvolio_render_primary_menu(): string +{ + if (has_nav_menu('primary')) { + return trim((string) wp_nav_menu([ + 'theme_location' => 'primary', + 'container' => false, + 'fallback_cb' => false, + 'menu_class' => 'site-nav__menu', + 'echo' => false, + ])); + } + + $items = [ + '#resumen' => __('Resumen', 'cvolio'), + '#impacto' => __('Impacto', 'cvolio'), + '#experiencia' => __('Experiencia', 'cvolio'), + '#formacion' => __('Formación', 'cvolio'), + '#certificaciones' => __('Certificaciones', 'cvolio'), + '#contacto' => __('Contacto', 'cvolio'), + ]; + + $html = ''; + + return $html; +} + function cvolio_render_contact_link(string $type, string $value): string { if ($value === '') { diff --git a/frontend/cvfolio-theme/header.php b/frontend/cvfolio-theme/header.php index 880ab98..e3e8838 100644 --- a/frontend/cvfolio-theme/header.php +++ b/frontend/cvfolio-theme/header.php @@ -23,15 +23,9 @@ $brand = get_theme_mod('cvolio_hero_name', get_bloginfo('name')); Menu +
diff --git a/frontend/cvfolio-theme/inc/customizer.php b/frontend/cvfolio-theme/inc/customizer.php index 528f8df..4681cec 100644 --- a/frontend/cvfolio-theme/inc/customizer.php +++ b/frontend/cvfolio-theme/inc/customizer.php @@ -10,20 +10,31 @@ function cvolio_customize_register(WP_Customize_Manager $wp_customize): void ]); $fields = [ - 'cvolio_hero_name' => ['label' => __('Nombre principal', 'cvolio'), 'default' => 'Patricia Flores'], - 'cvolio_hero_role' => ['label' => __('Rol o titular', 'cvolio'), 'default' => 'Directora de proyectos, estratega digital y lider de producto'], - 'cvolio_hero_intro' => ['label' => __('Introduccion', 'cvolio'), 'default' => 'Perfil profesional con enfoque humano, resultados medibles y una presentacion visual moderna.'], - 'cvolio_hero_cta_label' => ['label' => __('Texto del boton', 'cvolio'), 'default' => 'Ver experiencia'], - 'cvolio_hero_cta_url' => ['label' => __('Enlace del boton', 'cvolio'), 'default' => '#perfil'], - 'cvolio_contact_location' => ['label' => __('Ubicacion', 'cvolio'), 'default' => 'Guayaquil, Ecuador'], - 'cvolio_contact_email' => ['label' => __('Correo', 'cvolio'), 'default' => 'hola@ejemplo.com'], - 'cvolio_contact_phone' => ['label' => __('Telefono', 'cvolio'), 'default' => '+593 999 999 999'], - 'cvolio_skills_list' => ['label' => __('Habilidades separadas por coma', 'cvolio'), 'default' => 'Liderazgo, Comunicacion, WordPress, UX, SEO, Analitica'], - 'cvolio_social_linkedin' => ['label' => __('LinkedIn', 'cvolio'), 'default' => ''], + 'cvolio_hero_name' => ['label' => __('Nombre principal', 'cvolio'), 'default' => 'Patricia Soledad Flores Sánchez'], + 'cvolio_hero_role' => ['label' => __('Rol o titular', 'cvolio'), 'default' => 'Gerente / Jefa de Operaciones Financieras | Sector financiero y microfinanzas'], + 'cvolio_hero_intro' => ['label' => __('Introduccion', 'cvolio'), 'default' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Especialista en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.'], + 'cvolio_hero_cta_label' => ['label' => __('Texto del boton', 'cvolio'), 'default' => 'Ver trayectoria'], + 'cvolio_hero_cta_url' => ['label' => __('Enlace del boton', 'cvolio'), 'default' => '#experiencia'], + 'cvolio_contact_location' => ['label' => __('Ubicacion', 'cvolio'), 'default' => 'Loja, Ecuador'], + 'cvolio_contact_email' => ['label' => __('Correo', 'cvolio'), 'default' => 'patricflorsanchez@gmail.com'], + 'cvolio_contact_phone' => ['label' => __('Telefono', 'cvolio'), 'default' => '+593 99 469 8405'], + 'cvolio_skills_list' => ['label' => __('Habilidades separadas por coma', 'cvolio'), 'default' => 'Liderazgo, Gestión de operaciones financieras, Control interno, Transformación digital, Microfinanzas, Gestión de cartera, SAP FI, Power BI'], + 'cvolio_social_linkedin' => ['label' => __('LinkedIn', 'cvolio'), 'default' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/'], 'cvolio_social_github' => ['label' => __('GitHub', 'cvolio'), 'default' => ''], 'cvolio_social_portfolio' => ['label' => __('Portfolio', 'cvolio'), 'default' => ''], ]; + $wp_customize->add_setting('cvolio_profile_image', [ + 'default' => '', + 'sanitize_callback' => 'esc_url_raw', + ]); + + $wp_customize->add_control(new WP_Customize_Image_Control($wp_customize, 'cvolio_profile_image', [ + 'label' => __('Foto de perfil', 'cvolio'), + 'section' => 'cvolio_hero', + 'settings' => 'cvolio_profile_image', + ])); + foreach ($fields as $key => $field) { $wp_customize->add_setting($key, [ 'default' => $field['default'], diff --git a/frontend/cvfolio-theme/inc/cv-data.php b/frontend/cvfolio-theme/inc/cv-data.php new file mode 100644 index 0000000..b379ce0 --- /dev/null +++ b/frontend/cvfolio-theme/inc/cv-data.php @@ -0,0 +1,189 @@ + [ + 'eyebrow' => 'Currículum ejecutivo', + 'name' => 'Patricia Soledad Flores Sánchez', + 'role' => 'Gerente / Jefa de Operaciones Financieras | Sector financiero y microfinanzas', + 'intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.', + 'location' => 'Loja, Ecuador', + 'email' => 'patricflorsanchez@gmail.com', + 'phone' => '+593 99 469 8405', + 'linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', + 'availability' => 'Inmediata | Presencial / Híbrida / Remota', + 'focus' => 'Operaciones financieras, control interno y transformación digital', + ], + 'metrics' => [ + ['value' => '22+', 'label' => 'años de experiencia en banca y microfinanzas'], + ['value' => '26', 'label' => 'agencias bajo liderazgo operativo nacional'], + ['value' => '48', 'label' => 'personas a cargo en la estructura operativa'], + ['value' => 'USD 5 M', 'label' => 'en operaciones mensuales de microcrédito'], + ['value' => '28.000', 'label' => 'clientes coordinados en recaudación por canales internos y externos'], + ], + 'highlights' => [ + ['value' => '9/10', 'label' => 'calificación de gestión con resultado excelente en cuatro trimestres consecutivos'], + ['value' => '74 %', 'label' => 'reducción de requerimientos de soporte desde Dirección por descentralización de procesos'], + ['value' => '1,68 días', 'label' => 'tiempo de desembolso logrado en Q3 2025, mejor resultado del año'], + ['value' => '75 %', 'label' => 'reducción del índice de rotación del área entre 2022 y 2026'], + ], + 'summary' => [ + 'title' => 'Perfil estratégico con enfoque en eficiencia, control y ejecución sostenible', + 'body' => 'Actualmente lidera la operatividad de 26 agencias a nivel nacional y coordina aproximadamente 1.500 operaciones de microcrédito mensuales, por un monto cercano a USD 5 millones. También articula la recaudación de cerca de 28.000 clientes por canales internos y externos, integrando visión operativa, criterio financiero, disciplina de control y liderazgo sostenido del desempeño.', + ], + 'impact' => [ + [ + 'title' => 'Transformación digital', + 'body' => 'Lideró desembolsos por transferencia bancaria, integración de APIs bancarias y digitalización de expedientes y pagarés en 13 agencias.', + ], + [ + 'title' => 'Automatización crítica', + 'body' => 'Automatizó el seguro de desgravamen, desde el cálculo de prima hasta la liquidación contable, reduciendo tiempos de 17 a 10 días.', + ], + [ + 'title' => 'Supervisión nacional', + 'body' => 'Implementó una matriz de supervisión y certificación por agencia, elevando el índice de cumplimiento operativo del 65 % al 88 %.', + ], + ], + 'experience' => [ + [ + 'period' => 'Abr. 2022 – Presente', + 'title' => 'Jefa de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Responsable del desempeño operativo nacional, cumplimiento del POA, control interno, digitalización de procesos y gestión de equipos.', + 'bullets' => [ + 'Cumplimiento del POA: 98 % (Q2 2025), 96 % (Q3 2025), 100 % (Q4 2025) y 105 % (Q1 2026).', + 'La transformación digital logró que los desembolsos por transferencia representen el 47,80 % del monto total en Q1 2026.', + 'Redujo los requerimientos de soporte desde Dirección en un 74 %, de 1.489 a 382, mediante la descentralización de procesos hacia las agencias.', + 'Automatizó el proceso del seguro de desgravamen con cálculo de prima, conciliación mensual, liquidación de siniestros y asientos contables automatizados.', + 'Elevó el índice de cumplimiento operativo nacional del 65 % al 88 % con la matriz de supervisión y certificación por agencia.', + 'Redujo el tiempo de atención del seguro de desgravamen de 17 a 10 días.', + 'Redujo los tiempos de desembolso a 1,68 días en Q3 2025, mejor resultado del año frente a la meta institucional máxima de 2 días.', + 'La efectividad de recuperación de mora alcanzó 90,60 % en Q1 2026, con USD 3,06 millones recuperados sobre USD 3,37 millones en mora.', + 'Redujo el índice de rotación del área del 41,46 % al 10,24 % entre diciembre de 2022 y marzo de 2026 mediante mejoras salariales, remuneración variable, evaluación trimestral y un plan de carrera con certificación de 60 horas para asistentes de caja.', + 'Participa en comités estratégicos, auditorías internas y externas, y en representación institucional ante organismos financieros internacionales de desarrollo.', + ], + ], + [ + 'period' => 'Feb. 2021 – Mar. 2022', + 'title' => 'Supervisora de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Supervisó operaciones de 18 agencias a nivel nacional y regularizó procesos críticos inexistentes o incompletos.', + 'bullets' => [ + 'Reconstruyó desde cero las bases de cálculo del seguro de desgravamen y regularizó pagos pendientes desde enero de 2021.', + 'Elaboró el instructivo de constitución de garantías hipotecarias, proceso que no existía previamente en la institución.', + 'Inició el proyecto de automatización de pagos por depósitos y transferencias.', + ], + ], + [ + 'period' => '2019 – 2021', + 'title' => 'Actualización y formación profesional', + 'company' => 'Formación independiente', + 'location' => 'Ecuador', + 'lead' => 'Etapa enfocada en fortalecimiento técnico y actualización profesional tras su salida de Mutualista Pichincha.', + 'bullets' => [ + 'SAP FI (30 h).', + 'Excel Empresarial (24 h).', + 'Prevención de Lavado de Activos — UAFE (5 h).', + ], + ], + [ + 'period' => 'Ago. 2004 – May. 2019', + 'title' => 'Oficial Operativa Regional / Jefa Operativa', + 'company' => 'Mutualista Pichincha', + 'location' => 'Loja, Ecuador', + 'lead' => 'Trayectoria ascendente de 15 años desde Cajera hasta Jefa Operativa Regional.', + 'bullets' => [ + 'Reconocida a nivel nacional por excelencia en control operativo.', + 'La agencia se mantuvo consistentemente por encima del 80 % en gestión de excepciones de crédito hipotecario.', + 'Administró proyecto inmobiliario institucional en Loja.', + 'Gestionó alertas ALA/FT con 100 % de cumplimiento en comités trimestrales y auditorías internas sin observaciones significativas de forma sostenida.', + ], + ], + [ + 'period' => 'Ene. 2002 – Nov. 2003', + 'title' => 'Cajera', + 'company' => 'Cooperativa de Ahorro y Crédito El Porvenir', + 'location' => 'Loja, Ecuador', + 'lead' => 'Inicio de carrera en el frente operativo financiero con disciplina de caja y apoyo contable.', + 'bullets' => [ + 'Gestión de caja sin faltantes ni sobrantes.', + 'Contrato renovado de forma anticipada a los tres meses.', + 'Apoyo al área de Contabilidad y reemplazo temporal de la contadora durante vacaciones.', + ], + ], + ], + 'education' => [ + [ + 'year' => '2021', + 'title' => 'Máster Universitario en Dirección y Planificación Financiera', + 'institution' => 'Universidad Europea Miguel de Cervantes — Valladolid, España', + 'detail' => 'Título reconocido por la SENECYT — Ecuador, febrero de 2023 | Reg. 2021/394270.', + ], + [ + 'year' => '2007', + 'title' => 'Ingeniera en Administración en Banca y Finanzas', + 'institution' => 'Universidad Técnica Particular de Loja (UTPL)', + 'detail' => '', + ], + ], + 'certifications' => [ + ['year' => '2026', 'title' => 'Mujeres que Lideran: Alta Gerencia', 'issuer' => 'Foro UNIR — Universidad Internacional de La Rioja'], + ['year' => '2025', 'title' => 'Programa Internacional de Habilidades Gerenciales (48 h)', 'issuer' => 'IDEASGROUP — Ecuador / México'], + ['year' => '2025', 'title' => 'Transforma tu Trabajo con Inteligencia Artificial (12 h)', 'issuer' => 'Red de Instituciones Financieras de Desarrollo'], + ['year' => '2025', 'title' => 'Excel: Básico a Dashboard (40 h)', 'issuer' => 'DMARURI Training & Consulting'], + ['year' => '2024', 'title' => 'Administración por Procesos — BPM (64 h)', 'issuer' => 'Escuela Politécnica Nacional — CEC'], + ['year' => '2023', 'title' => 'Formación de Formadores (80 h)', 'issuer' => 'Ministerio del Trabajo del Ecuador | Certificación vigente hasta 2028'], + ['year' => '2023', 'title' => 'Power BI (40 h) | Indicadores Clave de Desempeño — KPI (12 h)', 'issuer' => 'Liderazgo Capacitación & Eventos'], + ['year' => '2020', 'title' => 'Gestión Financiera ERP SAP FI — Key User (30 h)', 'issuer' => 'Socio SAP certificado'], + ['year' => '2020', 'title' => 'Prevención de Lavado de Activos y Financiamiento de Delitos', 'issuer' => 'UAFE — Ecuador'], + ['year' => '2013', 'title' => 'Tributación Integral (36 h)', 'issuer' => 'Universidad Técnica Particular de Loja'], + ], + 'competencies' => [ + 'Finanzas y Operaciones' => [ + 'Gestión de operaciones financieras', + 'Conciliación bancaria', + 'Originación de crédito', + 'Gestión de cartera', + 'Control interno', + 'Tesorería operativa', + 'Microfinanzas', + 'ALA/FT', + ], + 'Tecnología y Procesos' => [ + 'Transformación digital', + 'BPM', + 'Integración de APIs / Web Services', + 'SAP FI', + 'Excel avanzado y dashboards', + 'Power BI', + 'IA aplicada', + 'Core financiero', + ], + 'Liderazgo' => [ + 'Equipos de hasta 48 personas', + 'Remuneración variable', + 'Gestión del cambio', + 'Formación de formadores', + 'Diversidad, equidad e inclusión', + ], + ], + 'volunteer' => [ + 'title' => 'Voluntaria — Hogar San Camilo de Lellis', + 'year' => '2025', + 'body' => 'Hogar de niñas con discapacidad | Loja, Ecuador. Alineado a la visión y misión de responsabilidad social de la Fundación FACES Microfinanzas.', + ], + 'additional' => [ + 'Idiomas' => 'Español nativo | Inglés básico en formación activa', + 'Disponibilidad' => 'Inmediata | Presencial / Híbrida / Remota', + 'C.I.' => '1103591648', + 'Fecha de nacimiento' => '3 de agosto de 1980', + 'Web profesional' => 'pflres.linkedcorp.net', + ], + ]; +} diff --git a/frontend/cvfolio-theme/inc/visits.php b/frontend/cvfolio-theme/inc/visits.php new file mode 100644 index 0000000..e5dc0f7 --- /dev/null +++ b/frontend/cvfolio-theme/inc/visits.php @@ -0,0 +1,118 @@ + 0) { + $views = (int) get_post_meta($frontPage, '_cvolio_views', true); + update_post_meta($frontPage, '_cvolio_views', $views + 1); + } + + return; + } + + $postId = get_queried_object_id(); + if ($postId > 0) { + $views = (int) get_post_meta($postId, '_cvolio_views', true); + update_post_meta($postId, '_cvolio_views', $views + 1); + } +} +add_action('template_redirect', 'cvolio_track_visit'); + +function cvolio_get_client_ip(): string +{ + $keys = [ + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', + 'REMOTE_ADDR', + ]; + + foreach ($keys as $key) { + if (! empty($_SERVER[$key])) { + $value = explode(',', (string) $_SERVER[$key]); + return trim($value[0]); + } + } + + return '0.0.0.0'; +} + +function cvolio_register_visits_dashboard_widget(): void +{ + wp_add_dashboard_widget( + 'cvolio_visits_widget', + __('CVolio: Visitas', 'cvolio'), + 'cvolio_render_visits_dashboard_widget' + ); +} +add_action('wp_dashboard_setup', 'cvolio_register_visits_dashboard_widget'); + +function cvolio_render_visits_dashboard_widget(): void +{ + $total = (int) get_option('cvolio_total_visits', 0); + $frontPage = (int) get_option('page_on_front'); + $frontViews = $frontPage > 0 ? (int) get_post_meta($frontPage, '_cvolio_views', true) : 0; + ?> +
+

+

+

+
+ div { + padding: var(--sp-3); + background: var(--lm-slate-50); + border-radius: var(--r-md); +} + +.lm-quick-info dt { + font-size: .7rem; + font-weight: 700; + letter-spacing: .08em; + text-transform: uppercase; + color: var(--lm-primary); + margin-bottom: var(--sp-1); +} + +.lm-quick-info dd { + font-size: .825rem; + color: var(--lm-slate-700); + line-height: 1.5; +} + +/* Widget — competencias */ +.lm-widget--skills { border-top: 3px solid var(--lm-teal-500); } + +.lm-skill-group { margin-bottom: var(--sp-4); } +.lm-skill-group:last-child { margin-bottom: 0; } + +.lm-skill-group h3 { + font-size: .75rem; + font-weight: 700; + letter-spacing: .07em; + text-transform: uppercase; + color: var(--lm-slate-500); + margin-bottom: var(--sp-2); +} + +.lm-pills { + display: flex; + flex-wrap: wrap; + gap: var(--sp-1) var(--sp-2); +} + +.lm-pill { + display: inline-block; + padding: .25rem .6rem; + font-size: .75rem; + font-weight: 600; + background: var(--lm-teal-50); + color: var(--lm-teal-700); + border: 1px solid var(--lm-teal-100); + border-radius: var(--r-full); + line-height: 1.4; + transition: background .15s; +} + +.lm-pill:hover { background: var(--lm-teal-100); } + +/* Info list */ +.lm-info-list { + display: flex; + flex-direction: column; + gap: var(--sp-3); + margin-bottom: var(--sp-4); +} + +.lm-info-list > div { + padding-bottom: var(--sp-3); + border-bottom: 1px solid var(--lm-border); +} + +.lm-info-list > div:last-child { border-bottom: none; padding-bottom: 0; } + +.lm-info-list dt { + font-size: .7rem; + font-weight: 700; + letter-spacing: .07em; + text-transform: uppercase; + color: var(--lm-text-faint); + margin-bottom: var(--sp-1); +} + +.lm-info-list dd { + font-size: .825rem; + color: var(--lm-slate-700); +} + +.lm-contact-links { + display: flex; + flex-wrap: wrap; + gap: var(--sp-2); + margin-top: var(--sp-2); +} + +/* Business card */ +.lm-widget--card { border-top: 3px solid var(--lm-accent); } + +.lm-bcard { + margin-top: var(--sp-3); + border: 1px solid var(--lm-border); + border-radius: var(--r-lg); + overflow: hidden; + font-size: .8rem; +} + +.lm-bcard__header { + padding: var(--sp-4); + background: linear-gradient(135deg, var(--lm-teal-700), var(--lm-teal-900)); + color: #fff; +} + +.lm-bcard__name { + font-size: .9rem; + font-weight: 700; + color: #fff; + margin-bottom: var(--sp-1); +} + +.lm-bcard__role { + font-size: .7rem; + color: var(--lm-teal-100); + line-height: 1.4; +} + +.lm-bcard__body { + display: grid; + grid-template-columns: 80px 1fr; + gap: var(--sp-3); + padding: var(--sp-4); + background: var(--lm-slate-50); +} + +.lm-bcard__qr img { + width: 80px; + height: 80px; + border-radius: var(--r-sm); +} + +.lm-bcard__info { + display: flex; + flex-direction: column; + gap: var(--sp-1); + color: var(--lm-slate-600); + font-size: .75rem; +} + +.lm-bcard__info a { + color: var(--lm-primary); + font-weight: 600; + word-break: break-all; +} + +/* --- 11. Contenido principal ----------------------------- */ +.lm-content { + display: flex; + flex-direction: column; + gap: var(--sp-2); +} + +/* Sección interna */ +.lm-section-inner { + background: var(--lm-surface); + border: 1px solid var(--lm-border); + border-radius: var(--r-xl); + padding: var(--sp-8); + box-shadow: var(--shadow-sm); +} + +.lm-section-inner + .lm-section-inner { + margin-top: var(--sp-4); +} + +.lm-section-head { + margin-bottom: var(--sp-8); + padding-bottom: var(--sp-5); + border-bottom: 2px solid var(--lm-teal-50); +} + +.lm-section-head h2 { + font-size: clamp(1.15rem, 2vw, 1.4rem); + font-weight: 700; + color: var(--lm-slate-800); +} + +/* --- 12. Timeline de experiencia ------------------------- */ +.lm-timeline { + display: flex; + flex-direction: column; + gap: 0; +} + +.lm-timeline__item { + display: grid; + grid-template-columns: 16px 1fr; + gap: 0 var(--sp-5); + position: relative; + padding-bottom: var(--sp-8); +} + +.lm-timeline__item:last-child { padding-bottom: 0; } + +/* Línea vertical */ +.lm-timeline__item::before { + content: ''; + position: absolute; + left: 7px; + top: 16px; + bottom: 0; + width: 2px; + background: var(--lm-teal-100); +} + +.lm-timeline__item:last-child::before { display: none; } + +/* Dot */ +.lm-timeline__marker { + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--lm-surface); + border: 3px solid var(--lm-accent); + flex-shrink: 0; + margin-top: 4px; + position: relative; + z-index: 1; + box-shadow: 0 0 0 3px var(--lm-amber-100); +} + +.lm-timeline__body { min-width: 0; } + +.lm-timeline__period { + display: inline-block; + font-size: .72rem; + font-weight: 700; + letter-spacing: .06em; + text-transform: uppercase; + color: var(--lm-primary); + background: var(--lm-teal-50); + border: 1px solid var(--lm-teal-100); + border-radius: var(--r-full); + padding: .15rem .6rem; + margin-bottom: var(--sp-2); +} + +.lm-timeline__body h3 { + font-size: 1.05rem; + font-weight: 700; + color: var(--lm-slate-900); + margin-bottom: var(--sp-1); +} + +.lm-timeline__meta { + font-size: .82rem; + color: var(--lm-text-muted); + margin-bottom: var(--sp-2); +} + +.lm-timeline__meta strong { color: var(--lm-slate-700); } + +.lm-timeline__lead { + font-size: .875rem; + color: var(--lm-slate-700); + font-style: italic; + margin-bottom: var(--sp-3); + line-height: 1.6; +} + +.lm-timeline__bullets { + display: flex; + flex-direction: column; + gap: var(--sp-2); + list-style: none; +} + +.lm-timeline__bullets li { + font-size: .85rem; + color: var(--lm-text-muted); + line-height: 1.6; + padding-left: var(--sp-5); + position: relative; +} + +.lm-timeline__bullets li::before { + content: ''; + position: absolute; + left: 0; + top: .5em; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--lm-teal-400); +} + +/* --- 13. Formación académica ----------------------------- */ +.lm-edu-grid { + display: flex; + flex-direction: column; + gap: var(--sp-4); +} + +.lm-edu-card { + display: grid; + grid-template-columns: 64px 1fr; + gap: var(--sp-4); + padding: var(--sp-5); + background: var(--lm-slate-50); + border: 1px solid var(--lm-border); + border-left: 4px solid var(--lm-accent); + border-radius: var(--r-lg); + transition: box-shadow .2s; +} + +.lm-edu-card:hover { box-shadow: var(--shadow-md); } + +.lm-edu-card__year { + display: flex; + align-items: flex-start; + justify-content: center; + padding-top: 2px; + font-size: 1.2rem; + font-weight: 800; + color: var(--lm-amber-500); + line-height: 1; +} + +.lm-edu-card h3 { + font-size: .95rem; + font-weight: 700; + color: var(--lm-slate-800); + margin-bottom: var(--sp-1); +} + +.lm-edu-card p { + font-size: .82rem; + color: var(--lm-text-muted); + line-height: 1.5; +} + +.lm-edu-card__detail { + margin-top: var(--sp-2) !important; + font-size: .78rem !important; + color: var(--lm-primary) !important; + font-weight: 500; +} + +/* --- 14. Certificaciones --------------------------------- */ +.lm-cert-list { + display: flex; + flex-direction: column; + gap: var(--sp-1); +} + +.lm-cert-item { + display: grid; + grid-template-columns: 56px 1fr; + gap: var(--sp-4); + align-items: center; + padding: var(--sp-3) var(--sp-4); + border-radius: var(--r-md); + transition: background .15s; +} + +.lm-cert-item:hover { background: var(--lm-slate-50); } + +.lm-cert-item__year { + font-size: .72rem; + font-weight: 700; + background: var(--lm-teal-600); + color: #fff; + padding: .2rem .4rem; + border-radius: var(--r-sm); + text-align: center; + letter-spacing: .04em; +} + +.lm-cert-item h3 { + font-size: .875rem; + font-weight: 600; + color: var(--lm-slate-800); + margin-bottom: var(--sp-1); +} + +.lm-cert-item p { + font-size: .78rem; + color: var(--lm-text-muted); +} + +/* --- 15. Footer ------------------------------------------ */ +.lm-footer { + background: var(--lm-teal-900); + color: var(--lm-teal-100); + padding-block: var(--sp-6); + margin-top: var(--sp-12); +} + +.lm-footer__inner { + max-width: 1240px; + margin-inline: auto; + padding-inline: var(--sp-6); + display: flex; + justify-content: space-between; + align-items: center; +} + +.lm-footer__copy { font-size: .825rem; opacity: .7; } + +.lm-footer__top { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: rgba(255,255,255,.1); + color: #fff; + border-radius: var(--r-full); + font-size: 1.1rem; + text-decoration: none; + transition: background .2s; +} +.lm-footer__top:hover { background: rgba(255,255,255,.2); color: #fff; } + +/* --- 16. Main --- */ +.lm-main { min-height: 60vh; } + +/* --- 17. Sección de texto simple (index.php) ------------ */ +.lm-wrap { + max-width: 800px; + margin: var(--sp-16) auto; + padding-inline: var(--sp-6); +} + +.lm-card { + background: var(--lm-surface); + border: 1px solid var(--lm-border); + border-radius: var(--r-xl); + padding: var(--sp-10); + box-shadow: var(--shadow-sm); +} + +.lm-entry__title { + font-size: 2rem; + margin-bottom: var(--sp-6); + color: var(--lm-slate-900); +} + +.lm-entry__body { line-height: 1.75; color: var(--lm-text-muted); } +.lm-entry__body p + p { margin-top: var(--sp-4); } + +/* --- 18. Responsive: 1024px ----------------------------- */ +@media (max-width: 1024px) { + .lm-body { + grid-template-columns: 300px 1fr; + gap: var(--sp-6); + } + + .lm-hero__metrics { + grid-template-columns: repeat(3, 1fr); + } + + .lm-metric:nth-child(4), + .lm-metric:nth-child(5) { + border-top: 1px solid rgba(255,255,255,.08); + } +} + +/* --- 19. Responsive: 860px ------------------------------ */ +@media (max-width: 860px) { + .lm-body { + grid-template-columns: 1fr; + } + + .lm-sidebar { + position: static; + max-height: none; + overflow-y: visible; + display: grid; + grid-template-columns: repeat(2, 1fr); + } + + .lm-widget--card { grid-column: 1 / -1; } + + .lm-highlights-grid { + grid-template-columns: repeat(2, 1fr); + } + + .lm-impact-row { + grid-template-columns: 1fr; + } +} + +/* --- 20. Responsive: 640px ------------------------------ */ +@media (max-width: 640px) { + :root { --header-h: 56px; } + + .lm-nav { + display: none; + position: absolute; + top: var(--header-h); + left: 0; + right: 0; + background: var(--lm-surface); + border-bottom: 1px solid var(--lm-border); + padding: var(--sp-4); + box-shadow: var(--shadow-md); + z-index: 99; + } + + .lm-nav.is-open { display: block; } + + .lm-nav__list { + flex-direction: column; + gap: 0; + } + + .lm-nav__list li a { padding: var(--sp-3) var(--sp-4); } + + .lm-nav-toggle { display: flex; } + .lm-header__actions { display: none; } + + .lm-hero { padding-top: var(--sp-10); } + .lm-hero__avatar { width: 110px; height: 110px; } + .lm-hero__actions { gap: var(--sp-2); } + + .lm-hero__metrics { grid-template-columns: repeat(2, 1fr); } + .lm-metric:last-child { + grid-column: 1 / -1; + border-top: 1px solid rgba(255,255,255,.08); + } + + .lm-highlights-section { padding-block: var(--sp-10); } + .lm-highlights-grid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); } + .lm-impact-row { grid-template-columns: 1fr; gap: var(--sp-4); } + + .lm-body { padding-block: var(--sp-6); gap: var(--sp-6); } + + .lm-sidebar { grid-template-columns: 1fr; } + .lm-widget--card { grid-column: auto; } + + .lm-section-inner { padding: var(--sp-5); } + .lm-section-inner + .lm-section-inner { margin-top: var(--sp-3); } + + .lm-edu-card { grid-template-columns: 50px 1fr; } +} + +/* --- 21. Print ------------------------------------------ */ +@media print { + :root { --header-h: 0px; } + + .lm-header, + .lm-hero__actions, + .lm-footer, + .lm-nav-toggle, + .lm-header__actions { display: none !important; } + + .lm-hero { + background: none !important; + color: #000; + padding-top: var(--sp-8); + page-break-inside: avoid; + } + + .lm-hero__bg-pattern, + .lm-hero__wave { display: none; } + + .lm-hero__name, + .lm-hero__role { color: #000 !important; } + .lm-hero__intro { color: #333 !important; } + .lm-hero__chips .lm-chip--hero { color: #000 !important; border-color: #ccc !important; background: #f5f5f5 !important; } + + .lm-hero__metrics { background: #f0f0f0 !important; } + .lm-metric strong { color: #000 !important; } + .lm-metric span { color: #444 !important; } + + .lm-body { + grid-template-columns: 280px 1fr; + gap: var(--sp-6); + padding-top: var(--sp-4); + } + + .lm-sidebar { position: static; max-height: none; overflow: visible; } + + .lm-widget, + .lm-section-inner { box-shadow: none; border-color: #ddd; page-break-inside: avoid; } + + .lm-timeline__item { page-break-inside: avoid; } + .lm-edu-card { page-break-inside: avoid; } + + body { font-size: 11pt; } + a { color: #000 !important; text-decoration: none !important; } + .lm-chip { border-color: #ccc !important; background: #f5f5f5 !important; color: #333 !important; } +} diff --git a/frontend/lumina-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx b/frontend/lumina-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx new file mode 100644 index 0000000..544bf3d Binary files /dev/null and b/frontend/lumina-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx differ diff --git a/frontend/lumina-cv-theme/assets/images/patricia-flores-profile.jpeg b/frontend/lumina-cv-theme/assets/images/patricia-flores-profile.jpeg new file mode 100644 index 0000000..4151a20 Binary files /dev/null and b/frontend/lumina-cv-theme/assets/images/patricia-flores-profile.jpeg differ diff --git a/frontend/lumina-cv-theme/assets/images/patricia-flores-qr.png b/frontend/lumina-cv-theme/assets/images/patricia-flores-qr.png new file mode 100644 index 0000000..55703ad Binary files /dev/null and b/frontend/lumina-cv-theme/assets/images/patricia-flores-qr.png differ diff --git a/frontend/lumina-cv-theme/assets/images/profile-placeholder.svg b/frontend/lumina-cv-theme/assets/images/profile-placeholder.svg new file mode 100644 index 0000000..3d7dbb0 --- /dev/null +++ b/frontend/lumina-cv-theme/assets/images/profile-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/lumina-cv-theme/assets/js/theme.js b/frontend/lumina-cv-theme/assets/js/theme.js new file mode 100644 index 0000000..6b0caf6 --- /dev/null +++ b/frontend/lumina-cv-theme/assets/js/theme.js @@ -0,0 +1,60 @@ +document.addEventListener("DOMContentLoaded", () => { + const toggle = document.querySelector(".lm-nav-toggle"); + const nav = document.querySelector(".lm-nav"); + + if (toggle && nav) { + toggle.addEventListener("click", () => { + const expanded = toggle.getAttribute("aria-expanded") === "true"; + toggle.setAttribute("aria-expanded", String(!expanded)); + nav.classList.toggle("is-open", !expanded); + }); + + document.addEventListener("click", (e) => { + if (!nav.contains(e.target) && !toggle.contains(e.target)) { + nav.classList.remove("is-open"); + toggle.setAttribute("aria-expanded", "false"); + } + }); + } + + document.querySelectorAll("[data-lumina-print]").forEach((btn) => { + btn.addEventListener("click", () => window.print()); + }); + + const header = document.querySelector(".lm-header"); + if (header) { + let lastY = window.scrollY; + window.addEventListener("scroll", () => { + const currentY = window.scrollY; + if (currentY > 80) { + header.style.boxShadow = "0 2px 16px rgba(0,0,0,.12)"; + } else { + header.style.boxShadow = ""; + } + lastY = currentY; + }, { passive: true }); + } + + const sections = document.querySelectorAll("[id]"); + const navLinks = document.querySelectorAll(".lm-nav__list a[href^='#']"); + + if (navLinks.length && sections.length) { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + navLinks.forEach((link) => { + link.classList.toggle( + "is-active", + link.getAttribute("href") === `#${entry.target.id}` + ); + }); + } + }); + }, + { rootMargin: "-20% 0px -70% 0px" } + ); + + sections.forEach((section) => observer.observe(section)); + } +}); diff --git a/frontend/lumina-cv-theme/footer.php b/frontend/lumina-cv-theme/footer.php new file mode 100644 index 0000000..eaf0f9a --- /dev/null +++ b/frontend/lumina-cv-theme/footer.php @@ -0,0 +1,18 @@ + +
+ +
+ +
+ + + + diff --git a/frontend/lumina-cv-theme/front-page.php b/frontend/lumina-cv-theme/front-page.php new file mode 100644 index 0000000..40a0ebf --- /dev/null +++ b/frontend/lumina-cv-theme/front-page.php @@ -0,0 +1,343 @@ + + + +
+ + +
+
+ <?php echo esc_attr($heroName); ?> +
+ +

+

+

+

+ +
+ + + + + + + + + + + + + + + + + + + + LinkedIn + + +
+ +
+ + + + + + + + +
+
+ + +
+ +
+ + +
+ +
+ + + +
+ + +
+
+
+

+

+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +

+

+
+ +
+
+
+ + +
+ + + + + +
+ + +
+
+

+

+
+ +
+ +
+ +
+ +

+

+ + · +

+

+
    + +
  • + +
+
+
+ +
+
+ + +
+
+

+

+
+ +
+ +
+ +
+

+

+ +

+ +
+
+ +
+
+ + +
+
+

+

+
+ +
+ +
+ +
+

+

+
+
+ +
+
+ + + + +
+
+

+

+
+
+ +
+
+ + + +
+ +
+ + diff --git a/frontend/lumina-cv-theme/functions.php b/frontend/lumina-cv-theme/functions.php new file mode 100644 index 0000000..9fc2e4f --- /dev/null +++ b/frontend/lumina-cv-theme/functions.php @@ -0,0 +1,133 @@ + __('Menú principal', 'lumina'), + ]); +} +add_action('after_setup_theme', 'lumina_setup'); + +function lumina_enqueue_assets(): void +{ + wp_enqueue_style( + 'lumina-fonts', + 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap', + [], + null + ); + + wp_enqueue_style( + 'lumina-theme', + get_template_directory_uri() . '/assets/css/theme.css', + ['lumina-fonts'], + wp_get_theme()->get('Version') + ); + + wp_enqueue_script( + 'lumina-theme', + get_template_directory_uri() . '/assets/js/theme.js', + [], + wp_get_theme()->get('Version'), + true + ); +} +add_action('wp_enqueue_scripts', 'lumina_enqueue_assets'); + +function lumina_body_classes(array $classes): array +{ + $classes[] = 'lumina-shell'; + + if (is_front_page()) { + $classes[] = 'lumina-front'; + } + + return $classes; +} +add_filter('body_class', 'lumina_body_classes'); + +function lumina_redirect_sample_page(): void +{ + if (! is_page()) { + return; + } + + $page = get_queried_object(); + if (! $page instanceof WP_Post) { + return; + } + + if ($page->post_name !== 'pagina-ejemplo') { + return; + } + + wp_safe_redirect(home_url('/'), 301); + exit; +} +add_action('template_redirect', 'lumina_redirect_sample_page', 1); + +function lumina_render_primary_menu(): string +{ + if (has_nav_menu('primary')) { + return trim((string) wp_nav_menu([ + 'theme_location' => 'primary', + 'container' => false, + 'fallback_cb' => false, + 'menu_class' => 'lm-nav__list', + 'echo' => false, + ])); + } + + $items = [ + '#resumen' => __('Perfil', 'lumina'), + '#impacto' => __('Impacto', 'lumina'), + '#experiencia' => __('Experiencia', 'lumina'), + '#formacion' => __('Formación', 'lumina'), + '#certificaciones' => __('Certificaciones', 'lumina'), + '#contacto' => __('Contacto', 'lumina'), + ]; + + $html = '
    '; + foreach ($items as $href => $label) { + $html .= sprintf( + '
  • %s
  • ', + esc_url($href), + esc_html($label) + ); + } + $html .= '
'; + + return $html; +} + +function lumina_contact_link(string $type, string $value): string +{ + if ($value === '') { + return ''; + } + + $href = match ($type) { + 'email' => 'mailto:' . antispambot($value), + 'phone' => 'tel:' . preg_replace('/[^0-9+]/', '', $value), + default => esc_url($value), + }; + + return sprintf( + '%s', + esc_url($href), + esc_html($value) + ); +} diff --git a/frontend/lumina-cv-theme/header.php b/frontend/lumina-cv-theme/header.php new file mode 100644 index 0000000..f2604d8 --- /dev/null +++ b/frontend/lumina-cv-theme/header.php @@ -0,0 +1,41 @@ + + +> + + + + + +> + + +
+
+ + + + + + + + + + +
+
+ +
diff --git a/frontend/lumina-cv-theme/inc/customizer.php b/frontend/lumina-cv-theme/inc/customizer.php new file mode 100644 index 0000000..4fc8dc2 --- /dev/null +++ b/frontend/lumina-cv-theme/inc/customizer.php @@ -0,0 +1,53 @@ +add_section('lumina_hero', [ + 'title' => __('Lumina CV: Presentación', 'lumina'), + 'priority' => 30, + ]); + + $fields = [ + 'lm_hero_name' => ['label' => __('Nombre principal', 'lumina'), 'default' => 'Patricia Soledad Flores Sánchez'], + 'lm_hero_role' => ['label' => __('Rol o titular', 'lumina'), 'default' => 'Gerente / Jefa de Operaciones Financieras | Sector financiero y microfinanzas'], + 'lm_hero_intro' => ['label' => __('Introducción', 'lumina'), 'default' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.'], + 'lm_hero_cta_label' => ['label' => __('Texto del botón', 'lumina'), 'default' => 'Ver trayectoria'], + 'lm_hero_cta_url' => ['label' => __('Enlace del botón', 'lumina'), 'default' => '#experiencia'], + 'lm_contact_location'=> ['label' => __('Ubicación', 'lumina'), 'default' => 'Loja, Ecuador'], + 'lm_contact_email' => ['label' => __('Correo', 'lumina'), 'default' => 'patricflorsanchez@gmail.com'], + 'lm_contact_phone' => ['label' => __('Teléfono', 'lumina'), 'default' => '+593 99 469 8405'], + 'lm_social_linkedin' => ['label' => __('LinkedIn', 'lumina'), 'default' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/'], + 'lm_social_github' => ['label' => __('GitHub', 'lumina'), 'default' => ''], + ]; + + $wp_customize->add_setting('lm_profile_image', [ + 'default' => '', + 'sanitize_callback' => 'esc_url_raw', + ]); + + $wp_customize->add_control(new WP_Customize_Image_Control($wp_customize, 'lm_profile_image', [ + 'label' => __('Foto de perfil', 'lumina'), + 'section' => 'lumina_hero', + 'settings' => 'lm_profile_image', + ])); + + foreach ($fields as $key => $field) { + $isUrl = str_contains($key, 'url') + || str_contains($key, 'linkedin') + || str_contains($key, 'github'); + + $wp_customize->add_setting($key, [ + 'default' => $field['default'], + 'sanitize_callback' => $isUrl ? 'esc_url_raw' : 'sanitize_text_field', + ]); + + $wp_customize->add_control($key, [ + 'label' => $field['label'], + 'section' => 'lumina_hero', + 'type' => $key === 'lm_hero_intro' ? 'textarea' : 'text', + ]); + } +} +add_action('customize_register', 'lumina_customize_register'); diff --git a/frontend/lumina-cv-theme/inc/cv-data.php b/frontend/lumina-cv-theme/inc/cv-data.php new file mode 100644 index 0000000..eb498c3 --- /dev/null +++ b/frontend/lumina-cv-theme/inc/cv-data.php @@ -0,0 +1,189 @@ + [ + 'eyebrow' => 'Currículum ejecutivo', + 'name' => 'Patricia Soledad Flores Sánchez', + 'role' => 'Gerente / Jefa de Operaciones Financieras | Sector financiero y microfinanzas', + 'intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.', + 'location' => 'Loja, Ecuador', + 'email' => 'patricflorsanchez@gmail.com', + 'phone' => '+593 99 469 8405', + 'linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', + 'availability' => 'Inmediata | Presencial / Híbrida / Remota', + 'focus' => 'Operaciones financieras, control interno y transformación digital', + ], + 'metrics' => [ + ['value' => '22+', 'label' => 'años de experiencia en banca y microfinanzas'], + ['value' => '26', 'label' => 'agencias bajo liderazgo operativo nacional'], + ['value' => '48', 'label' => 'personas a cargo en la estructura operativa'], + ['value' => 'USD 5 M', 'label' => 'en operaciones mensuales de microcrédito'], + ['value' => '28.000', 'label' => 'clientes coordinados en recaudación por canales internos y externos'], + ], + 'highlights' => [ + ['value' => '9/10', 'label' => 'calificación de gestión con resultado excelente en cuatro trimestres consecutivos'], + ['value' => '74 %', 'label' => 'reducción de requerimientos de soporte desde Dirección por descentralización de procesos'], + ['value' => '1,68 días', 'label' => 'tiempo de desembolso logrado en Q3 2025, mejor resultado del año'], + ['value' => '75 %', 'label' => 'reducción del índice de rotación del área entre 2022 y 2026'], + ], + 'summary' => [ + 'title' => 'Perfil estratégico con enfoque en eficiencia, control y ejecución sostenible', + 'body' => 'Actualmente lidera la operatividad de 26 agencias a nivel nacional y coordina aproximadamente 1.500 operaciones de microcrédito mensuales, por un monto cercano a USD 5 millones. También articula la recaudación de cerca de 28.000 clientes por canales internos y externos, integrando visión operativa, criterio financiero, disciplina de control y liderazgo sostenido del desempeño.', + ], + 'impact' => [ + [ + 'title' => 'Transformación digital', + 'body' => 'Lideró desembolsos por transferencia bancaria, integración de APIs bancarias y digitalización de expedientes y pagarés en 13 agencias.', + ], + [ + 'title' => 'Automatización crítica', + 'body' => 'Automatizó el seguro de desgravamen, desde el cálculo de prima hasta la liquidación contable, reduciendo tiempos de 17 a 10 días.', + ], + [ + 'title' => 'Supervisión nacional', + 'body' => 'Implementó una matriz de supervisión y certificación por agencia, elevando el índice de cumplimiento operativo del 65 % al 88 %.', + ], + ], + 'experience' => [ + [ + 'period' => 'Abr. 2022 – Presente', + 'title' => 'Jefa de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Responsable del desempeño operativo nacional, cumplimiento del POA, control interno, digitalización de procesos y gestión de equipos.', + 'bullets' => [ + 'Cumplimiento del POA: 98 % (Q2 2025), 96 % (Q3 2025), 100 % (Q4 2025) y 105 % (Q1 2026).', + 'La transformación digital logró que los desembolsos por transferencia representen el 47,80 % del monto total en Q1 2026.', + 'Redujo los requerimientos de soporte desde Dirección en un 74 %, de 1.489 a 382, mediante la descentralización de procesos hacia las agencias.', + 'Automatizó el proceso del seguro de desgravamen con cálculo de prima, conciliación mensual, liquidación de siniestros y asientos contables automatizados.', + 'Elevó el índice de cumplimiento operativo nacional del 65 % al 88 % con la matriz de supervisión y certificación por agencia.', + 'Redujo el tiempo de atención del seguro de desgravamen de 17 a 10 días.', + 'Redujo los tiempos de desembolso a 1,68 días en Q3 2025, mejor resultado del año frente a la meta institucional máxima de 2 días.', + 'La efectividad de recuperación de mora alcanzó 90,60 % en Q1 2026, con USD 3,06 millones recuperados sobre USD 3,37 millones en mora.', + 'Redujo el índice de rotación del área del 41,46 % al 10,24 % entre diciembre de 2022 y marzo de 2026 mediante mejoras salariales, remuneración variable, evaluación trimestral y un plan de carrera con certificación de 60 horas para asistentes de caja.', + 'Participa en comités estratégicos, auditorías internas y externas, y en representación institucional ante organismos financieros internacionales de desarrollo.', + ], + ], + [ + 'period' => 'Feb. 2021 – Mar. 2022', + 'title' => 'Supervisora de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', + 'location' => 'Loja, Ecuador', + 'lead' => 'Supervisó operaciones de 18 agencias a nivel nacional y regularizó procesos críticos inexistentes o incompletos.', + 'bullets' => [ + 'Reconstruyó desde cero las bases de cálculo del seguro de desgravamen y regularizó pagos pendientes desde enero de 2021.', + 'Elaboró el instructivo de constitución de garantías hipotecarias, proceso que no existía previamente en la institución.', + 'Inició el proyecto de automatización de pagos por depósitos y transferencias.', + ], + ], + [ + 'period' => '2019 – 2021', + 'title' => 'Actualización y formación profesional', + 'company' => 'Formación independiente', + 'location' => 'Ecuador', + 'lead' => 'Etapa enfocada en fortalecimiento técnico y actualización profesional tras su salida de Mutualista Pichincha.', + 'bullets' => [ + 'SAP FI (30 h).', + 'Excel Empresarial (24 h).', + 'Prevención de Lavado de Activos — UAFE (5 h).', + ], + ], + [ + 'period' => 'Ago. 2004 – May. 2019', + 'title' => 'Oficial Operativa Regional / Jefa Operativa', + 'company' => 'Mutualista Pichincha', + 'location' => 'Loja, Ecuador', + 'lead' => 'Trayectoria ascendente de 15 años desde Cajera hasta Jefa Operativa Regional.', + 'bullets' => [ + 'Reconocida a nivel nacional por excelencia en control operativo.', + 'La agencia se mantuvo consistentemente por encima del 80 % en gestión de excepciones de crédito hipotecario.', + 'Administró proyecto inmobiliario institucional en Loja.', + 'Gestionó alertas ALA/FT con 100 % de cumplimiento en comités trimestrales y auditorías internas sin observaciones significativas de forma sostenida.', + ], + ], + [ + 'period' => 'Ene. 2002 – Nov. 2003', + 'title' => 'Cajera', + 'company' => 'Cooperativa de Ahorro y Crédito El Porvenir', + 'location' => 'Loja, Ecuador', + 'lead' => 'Inicio de carrera en el frente operativo financiero con disciplina de caja y apoyo contable.', + 'bullets' => [ + 'Gestión de caja sin faltantes ni sobrantes.', + 'Contrato renovado de forma anticipada a los tres meses.', + 'Apoyo al área de Contabilidad y reemplazo temporal de la contadora durante vacaciones.', + ], + ], + ], + 'education' => [ + [ + 'year' => '2021', + 'title' => 'Máster Universitario en Dirección y Planificación Financiera', + 'institution' => 'Universidad Europea Miguel de Cervantes — Valladolid, España', + 'detail' => 'Título reconocido por la SENECYT — Ecuador, febrero de 2023 | Reg. 2021/394270.', + ], + [ + 'year' => '2007', + 'title' => 'Ingeniera en Administración en Banca y Finanzas', + 'institution' => 'Universidad Técnica Particular de Loja (UTPL)', + 'detail' => '', + ], + ], + 'certifications' => [ + ['year' => '2026', 'title' => 'Mujeres que Lideran: Alta Gerencia', 'issuer' => 'Foro UNIR — Universidad Internacional de La Rioja'], + ['year' => '2025', 'title' => 'Programa Internacional de Habilidades Gerenciales (48 h)', 'issuer' => 'IDEASGROUP — Ecuador / México'], + ['year' => '2025', 'title' => 'Transforma tu Trabajo con Inteligencia Artificial (12 h)', 'issuer' => 'Red de Instituciones Financieras de Desarrollo'], + ['year' => '2025', 'title' => 'Excel: Básico a Dashboard (40 h)', 'issuer' => 'DMARURI Training & Consulting'], + ['year' => '2024', 'title' => 'Administración por Procesos — BPM (64 h)', 'issuer' => 'Escuela Politécnica Nacional — CEC'], + ['year' => '2023', 'title' => 'Formación de Formadores (80 h)', 'issuer' => 'Ministerio del Trabajo del Ecuador | Certificación vigente hasta 2028'], + ['year' => '2023', 'title' => 'Power BI (40 h) | Indicadores Clave de Desempeño — KPI (12 h)', 'issuer' => 'Liderazgo Capacitación & Eventos'], + ['year' => '2020', 'title' => 'Gestión Financiera ERP SAP FI — Key User (30 h)', 'issuer' => 'Socio SAP certificado'], + ['year' => '2020', 'title' => 'Prevención de Lavado de Activos y Financiamiento de Delitos', 'issuer' => 'UAFE — Ecuador'], + ['year' => '2013', 'title' => 'Tributación Integral (36 h)', 'issuer' => 'Universidad Técnica Particular de Loja'], + ], + 'competencies' => [ + 'Finanzas y Operaciones' => [ + 'Gestión de operaciones financieras', + 'Conciliación bancaria', + 'Originación de crédito', + 'Gestión de cartera', + 'Control interno', + 'Tesorería operativa', + 'Microfinanzas', + 'ALA/FT', + ], + 'Tecnología y Procesos' => [ + 'Transformación digital', + 'BPM', + 'Integración de APIs / Web Services', + 'SAP FI', + 'Excel avanzado y dashboards', + 'Power BI', + 'IA aplicada', + 'Core financiero', + ], + 'Liderazgo' => [ + 'Equipos de hasta 48 personas', + 'Remuneración variable', + 'Gestión del cambio', + 'Formación de formadores', + 'Diversidad, equidad e inclusión', + ], + ], + 'volunteer' => [ + 'title' => 'Voluntaria — Hogar San Camilo de Lellis', + 'year' => '2025', + 'body' => 'Hogar de niñas con discapacidad | Loja, Ecuador. Alineado a la visión y misión de responsabilidad social de la Fundación FACES Microfinanzas.', + ], + 'additional' => [ + 'Idiomas' => 'Español nativo | Inglés básico en formación activa', + 'Disponibilidad' => 'Inmediata | Presencial / Híbrida / Remota', + 'C.I.' => '1103591648', + 'Fecha de nacimiento' => '3 de agosto de 1980', + 'Web profesional' => 'pflres.linkedcorp.net', + ], + ]; +} diff --git a/frontend/lumina-cv-theme/inc/visits.php b/frontend/lumina-cv-theme/inc/visits.php new file mode 100644 index 0000000..590c2a1 --- /dev/null +++ b/frontend/lumina-cv-theme/inc/visits.php @@ -0,0 +1,116 @@ + 0) { + $views = (int) get_post_meta($frontPage, '_lumina_views', true); + update_post_meta($frontPage, '_lumina_views', $views + 1); + } + + return; + } + + $postId = get_queried_object_id(); + if ($postId > 0) { + $views = (int) get_post_meta($postId, '_lumina_views', true); + update_post_meta($postId, '_lumina_views', $views + 1); + } +} +add_action('template_redirect', 'lumina_track_visit'); + +function lumina_get_client_ip(): string +{ + $keys = [ + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', + 'REMOTE_ADDR', + ]; + + foreach ($keys as $key) { + if (! empty($_SERVER[$key])) { + $value = explode(',', (string) $_SERVER[$key]); + return trim($value[0]); + } + } + + return '0.0.0.0'; +} + +function lumina_register_visits_widget(): void +{ + wp_add_dashboard_widget( + 'lumina_visits_widget', + __('Lumina CV: Visitas', 'lumina'), + 'lumina_render_visits_widget' + ); +} +add_action('wp_dashboard_setup', 'lumina_register_visits_widget'); + +function lumina_render_visits_widget(): void +{ + $total = (int) get_option('lumina_total_visits', 0); + $frontPage = (int) get_option('page_on_front'); + $frontViews = $frontPage > 0 ? (int) get_post_meta($frontPage, '_lumina_views', true) : 0; + ?> +

+

+

+ + +
+
+ + +
> +

+
+ +
+
+ + +
+

+

+
+ +
+
+ + diff --git a/frontend/lumina-cv-theme/style.css b/frontend/lumina-cv-theme/style.css new file mode 100644 index 0000000..5522307 --- /dev/null +++ b/frontend/lumina-cv-theme/style.css @@ -0,0 +1,11 @@ +/* +Theme Name: Lumina CV +Theme URI: https://pflres.linkedcorp.net +Author: mgonzalez +Description: Tema WordPress moderno, alegre y profesional para hoja de vida ejecutiva. Diseño claro con paleta teal y ámbar, tipografía limpia y layout de sidebar izquierdo. +Version: 1.0.0 +Requires at least: 6.8 +Tested up to: 6.8 +Requires PHP: 8.1 +Text Domain: lumina +*/ diff --git a/frontend/persona-cv-theme/assets/css/theme.css b/frontend/persona-cv-theme/assets/css/theme.css new file mode 100644 index 0000000..b4b6d0f --- /dev/null +++ b/frontend/persona-cv-theme/assets/css/theme.css @@ -0,0 +1,458 @@ +/* ========================================================= + Persona CV — Inspirado en Personage / Pixflow + Raleway + Open Sans | Secciones alternas | Timeline central + ========================================================= */ + +:root { + --pn-accent: #c0392b; + --pn-accent-h: #a93226; + --pn-dark: #1a2332; + --pn-dark-2: #243045; + --pn-white: #ffffff; + --pn-gray: #f5f6f8; + --pn-text: #2d3436; + --pn-muted: #636e72; + --pn-border: #e0e3e8; + --pn-header-h: 70px; + --pn-container: 1140px; + --radius: 6px; + --shadow: 0 4px 20px rgba(0,0,0,.1); + --shadow-lg: 0 8px 40px rgba(0,0,0,.15); +} + +*,*::before,*::after{box-sizing:border-box;margin:0;padding:0} +html{scroll-behavior:smooth;scroll-padding-top:var(--pn-header-h);font-size:16px} +body{font-family:'Open Sans',system-ui,sans-serif;color:var(--pn-text);background:var(--pn-white);-webkit-font-smoothing:antialiased;overflow-x:hidden} +img{display:block;max-width:100%} +a{color:var(--pn-accent);text-decoration:none;transition:color .2s,opacity .2s} +a:hover{color:var(--pn-accent-h)} +ul{list-style:none} +h1,h2,h3,h4{font-family:'Raleway',sans-serif;line-height:1.25;color:var(--pn-text)} + +/* ── CONTENEDOR ── */ +.pn-container{max-width:var(--pn-container);margin-inline:auto;padding-inline:2rem} + +/* ── HEADER ── */ +.pn-header{ + position:fixed;top:0;left:0;right:0;z-index:100; + height:var(--pn-header-h); + background:rgba(255,255,255,.97); + border-bottom:1px solid var(--pn-border); + box-shadow:0 2px 12px rgba(0,0,0,.06); + transition:background .3s,box-shadow .3s; +} +.pn-header.is-scrolled{background:#fff;box-shadow:var(--shadow)} +.pn-header__inner{ + max-width:var(--pn-container);margin-inline:auto;padding-inline:2rem; + height:100%;display:flex;align-items:center;gap:2rem; +} +.pn-brand{ + display:flex;align-items:center;gap:.5rem; + font-family:'Raleway',sans-serif;font-size:.95rem;font-weight:700; + color:var(--pn-text);letter-spacing:.04em;text-decoration:none;white-space:nowrap;flex-shrink:0; +} +.pn-brand:hover{color:var(--pn-accent)} +.pn-brand__dot{width:8px;height:8px;border-radius:50%;background:var(--pn-accent);flex-shrink:0} + +.pn-nav{margin-left:auto} +.pn-nav__list{display:flex;align-items:center;gap:.25rem} +.pn-nav__list li a{ + display:block;padding:.4rem .75rem; + font-family:'Raleway',sans-serif;font-size:.72rem;font-weight:600; + letter-spacing:.1em;text-transform:uppercase;color:var(--pn-muted); + border-radius:var(--radius);transition:color .2s;text-decoration:none; +} +.pn-nav__list li a:hover,.pn-nav__list li a.is-active{color:var(--pn-accent)} +.pn-header__actions{flex-shrink:0} + +.pn-hamburger{ + display:none;flex-direction:column;justify-content:center;align-items:center; + gap:5px;width:40px;height:40px;background:none;border:none;cursor:pointer;margin-left:auto; +} +.pn-hamburger span{display:block;width:22px;height:2px;background:var(--pn-text);border-radius:2px;transition:transform .25s,opacity .25s} + +/* ── BOTONES ── */ +.pn-btn{ + display:inline-flex;align-items:center;gap:.5rem; + padding:.75rem 1.75rem;border-radius:var(--radius); + font-family:'Raleway',sans-serif;font-size:.8rem;font-weight:700; + letter-spacing:.08em;text-transform:uppercase;cursor:pointer; + border:2px solid transparent;transition:all .25s;text-decoration:none;line-height:1; +} +.pn-btn svg{width:14px;height:14px;flex-shrink:0} +.pn-btn--accent{background:var(--pn-accent);color:#fff;border-color:var(--pn-accent)} +.pn-btn--accent:hover{background:var(--pn-accent-h);border-color:var(--pn-accent-h);color:#fff} +.pn-btn--white{background:transparent;color:#fff;border-color:rgba(255,255,255,.7)} +.pn-btn--white:hover{background:#fff;color:var(--pn-accent)} +.pn-btn--outline{background:transparent;color:var(--pn-accent);border-color:var(--pn-accent)} +.pn-btn--outline:hover{background:var(--pn-accent);color:#fff} +.pn-btn--sm{padding:.5rem 1.1rem;font-size:.72rem} + +/* ── SECCIONES ── */ +.pn-section{padding:5rem 0} +.pn-section--white{background:var(--pn-white)} +.pn-section--gray{background:var(--pn-gray)} +.pn-section--dark{background:var(--pn-dark)} +.pn-main{padding-top:var(--pn-header-h)} + +/* ── CABECERA DE SECCIÓN ── */ +.pn-section-head{text-align:center;margin-bottom:3.5rem} +.pn-eyebrow{ + display:block;font-family:'Raleway',sans-serif;font-size:.72rem; + font-weight:600;letter-spacing:.2em;text-transform:uppercase; + color:var(--pn-muted);margin-bottom:1rem; +} +.pn-eyebrow--light{color:rgba(255,255,255,.5)} +.pn-section-head h2{ + font-family:'Raleway',sans-serif;font-size:clamp(1.8rem,3vw,2.5rem); + font-weight:700;color:var(--pn-text);letter-spacing:.02em;margin-bottom:.75rem; +} +.pn-section-head__title--light,.pn-section--dark .pn-section-head h2{color:#fff} +.pn-section-bar{width:48px;height:3px;background:var(--pn-accent);border-radius:2px;margin:0 auto} + +/* ── HERO ── */ +.pn-hero{ + position:relative;width:100%;height:100vh;min-height:600px; + display:flex;align-items:center;justify-content:center;overflow:hidden; +} +.pn-hero__bg{ + position:absolute;inset:0;background-size:cover;background-position:center top; + filter:grayscale(20%) brightness(.4);transform:scale(1.05); + transition:transform 8s ease; +} +.pn-hero:hover .pn-hero__bg{transform:scale(1)} +.pn-hero__overlay{ + position:absolute;inset:0; + background:linear-gradient(160deg,rgba(10,18,36,.85) 0%,rgba(192,57,43,.2) 100%); +} +.pn-hero__content{ + position:relative;z-index:2;text-align:center; + display:flex;flex-direction:column;align-items:center;gap:1.25rem; + padding-inline:2rem;max-width:800px; +} +.pn-hero__avatar{ + width:130px;height:130px;border-radius:50%;overflow:hidden; + border:4px solid rgba(255,255,255,.9); + box-shadow:0 0 0 8px rgba(192,57,43,.25),var(--shadow-lg); + flex-shrink:0; +} +.pn-hero__avatar img{width:100%;height:100%;object-fit:cover} +.pn-hero__eyebrow{ + font-family:'Raleway',sans-serif;font-size:.72rem;font-weight:600; + letter-spacing:.25em;text-transform:uppercase;color:var(--pn-accent); +} +.pn-hero__name{ + font-family:'Raleway',sans-serif;font-size:clamp(2.2rem,5vw,3.5rem); + font-weight:800;color:#fff;letter-spacing:.03em;line-height:1.1; +} +.pn-hero__roles{ + font-family:'Raleway',sans-serif;font-size:clamp(.9rem,1.8vw,1.1rem); + font-weight:300;color:rgba(255,255,255,.8);letter-spacing:.05em; + display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:.5rem; +} +.pn-hero__roles-sep{color:var(--pn-accent);font-weight:400} +.pn-hero__chips{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem} +.pn-hero__chip{ + display:inline-flex;align-items:center;gap:.4rem; + padding:.3rem .9rem;border-radius:var(--radius); + font-size:.75rem;font-weight:500; + background:rgba(255,255,255,.12);color:rgba(255,255,255,.85); + border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(4px); +} +.pn-hero__chip--avail{color:#6fcf97;border-color:rgba(111,207,151,.4);background:rgba(111,207,151,.1)} +.pn-dot{width:6px;height:6px;border-radius:50%;background:#6fcf97;animation:pn-blink 2s ease-in-out infinite} +@keyframes pn-blink{0%,100%{opacity:1}50%{opacity:.3}} +.pn-hero__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem} +.pn-hero__socials{display:flex;gap:.75rem;justify-content:center} +.pn-social-icon{ + width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center; + background:rgba(255,255,255,.12);color:#fff;border:1px solid rgba(255,255,255,.25); + transition:background .2s,border-color .2s,color .2s; +} +.pn-social-icon:hover{background:var(--pn-accent);border-color:var(--pn-accent);color:#fff} +.pn-social-icon svg{width:15px;height:15px} +.pn-social-icon--dark{background:var(--pn-gray);color:var(--pn-text);border-color:var(--pn-border)} +.pn-social-icon--dark:hover{background:var(--pn-accent);color:#fff;border-color:var(--pn-accent)} +.pn-scroll-hint{ + position:absolute;bottom:2rem;left:50%;transform:translateX(-50%); + width:26px;height:44px;border:2px solid rgba(255,255,255,.4);border-radius:13px; + display:flex;align-items:flex-start;justify-content:center;padding-top:6px; +} +.pn-scroll-hint span{ + width:4px;height:8px;background:rgba(255,255,255,.7);border-radius:2px; + animation:pn-scroll 2s ease-in-out infinite; +} +@keyframes pn-scroll{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(16px)}} + +/* ── SOBRE MÍ ── */ +.pn-about-grid{display:grid;grid-template-columns:280px 1fr;gap:4rem;align-items:start} +.pn-about-photo-col{text-align:center} +.pn-about-photo{ + width:220px;height:220px;border-radius:50%;overflow:hidden;margin:0 auto 1.25rem; + box-shadow:0 0 0 6px var(--pn-gray),0 0 0 10px var(--pn-accent),var(--shadow-lg); +} +.pn-about-photo img{width:100%;height:100%;object-fit:cover} +.pn-about-name{font-family:'Raleway',sans-serif;font-size:1.15rem;font-weight:700;margin-bottom:.25rem} +.pn-about-role{font-size:.82rem;color:var(--pn-muted);line-height:1.5;margin-bottom:1rem} +.pn-about-socials{display:flex;justify-content:center;gap:.5rem} +.pn-about-text-col h3{ + font-family:'Raleway',sans-serif;font-size:1.35rem;font-weight:700; + margin-bottom:1rem;color:var(--pn-text); +} +.pn-about-text-col p{font-size:.95rem;color:var(--pn-muted);line-height:1.8;margin-bottom:2rem} +.pn-personal-details{border-top:2px solid var(--pn-gray);padding-top:1.5rem;display:flex;flex-direction:column;gap:.75rem} +.pn-pd-row{display:grid;grid-template-columns:130px 1fr;gap:1rem;font-size:.875rem} +.pn-pd-row span{color:var(--pn-muted);font-weight:600;text-transform:uppercase;font-size:.72rem;letter-spacing:.06em;padding-top:2px} +.pn-pd-row strong{color:var(--pn-text);font-weight:500} +.pn-pd-row strong a{color:var(--pn-accent)} + +/* ── CONTADORES ── */ +.pn-counters{ + display:grid;grid-template-columns:repeat(5,1fr);gap:1px; + background:rgba(255,255,255,.08);border-radius:var(--radius);overflow:hidden; +} +.pn-counter{ + display:flex;flex-direction:column;align-items:center;gap:.75rem; + padding:2.5rem 1rem;text-align:center;background:transparent; + transition:background .2s;position:relative; +} +.pn-counter::after{ + content:'';position:absolute;right:0;top:25%;bottom:25%; + width:1px;background:rgba(255,255,255,.1); +} +.pn-counter:last-child::after{display:none} +.pn-counter:hover{background:rgba(255,255,255,.04)} +.pn-counter__number{ + font-family:'Raleway',sans-serif;font-size:clamp(2.2rem,4vw,3rem); + font-weight:800;color:var(--pn-accent);line-height:1; +} +.pn-counter__label{font-size:.78rem;color:rgba(255,255,255,.55);letter-spacing:.06em;text-transform:uppercase} + +/* ── HIGHLIGHTS ── */ +.pn-highlights-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1.5rem;margin-bottom:3rem} +.pn-highlight-card{ + text-align:center;padding:2rem 1.5rem;background:#fff; + border:1px solid var(--pn-border);border-radius:var(--radius); + box-shadow:var(--shadow);transition:transform .2s,box-shadow .2s;position:relative;overflow:hidden; +} +.pn-highlight-card::before{ + content:'';position:absolute;top:0;left:0;right:0;height:3px;background:var(--pn-accent); +} +.pn-highlight-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg)} +.pn-highlight-card__value{ + font-family:'Raleway',sans-serif;font-size:clamp(1.8rem,3vw,2.6rem); + font-weight:800;color:var(--pn-accent);line-height:1;margin-bottom:.75rem; +} +.pn-highlight-card p{font-size:.82rem;color:var(--pn-muted);line-height:1.5} +.pn-impact-row{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem} +.pn-impact-card{ + padding:2rem;background:#fff;border:1px solid var(--pn-border);border-radius:var(--radius); + box-shadow:var(--shadow);transition:transform .2s,box-shadow .2s; +} +.pn-impact-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)} +.pn-impact-card__icon{ + width:48px;height:48px;background:rgba(192,57,43,.08);border-radius:var(--radius); + color:var(--pn-accent);padding:.75rem;margin-bottom:1rem; +} +.pn-impact-card__icon svg{width:100%;height:100%} +.pn-impact-card h3{font-family:'Raleway',sans-serif;font-size:1rem;font-weight:700;margin-bottom:.5rem} +.pn-impact-card p{font-size:.875rem;color:var(--pn-muted);line-height:1.65} + +/* ── TIMELINE CENTRADO ── */ +.pn-timeline{position:relative;padding:1rem 0} +.pn-timeline::before{ + content:'';position:absolute;left:50%;transform:translateX(-50%); + top:0;bottom:0;width:2px;background:var(--pn-border); +} +.pn-tl-entry{ + display:flex;width:100%;position:relative;margin-bottom:3rem; +} +.pn-tl-entry--left{justify-content:flex-end;padding-right:calc(50% + 2.5rem)} +.pn-tl-entry--right{justify-content:flex-start;padding-left:calc(50% + 2.5rem)} +.pn-tl-content{ + width:100%;max-width:440px;background:#fff;border:1px solid var(--pn-border); + border-radius:var(--radius);padding:1.75rem;box-shadow:var(--shadow); + transition:transform .2s,box-shadow .2s; +} +.pn-tl-content:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)} +.pn-tl-dot{ + position:absolute;left:50%;top:1.75rem;transform:translateX(-50%); + width:16px;height:16px;border-radius:50%;background:#fff; + border:3px solid var(--pn-accent);z-index:1; + box-shadow:0 0 0 4px rgba(192,57,43,.15); +} +.pn-tl-period{ + display:inline-block;padding:.2rem .7rem;border-radius:var(--radius); + font-family:'Raleway',sans-serif;font-size:.68rem;font-weight:700; + letter-spacing:.06em;text-transform:uppercase; + background:rgba(192,57,43,.08);color:var(--pn-accent);margin-bottom:.75rem; +} +.pn-tl-content h3{font-family:'Raleway',sans-serif;font-size:1.05rem;font-weight:700;margin-bottom:.25rem} +.pn-tl-company{font-size:.83rem;font-weight:600;color:var(--pn-accent);margin-bottom:.5rem} +.pn-tl-company em{font-style:normal;color:var(--pn-muted);font-weight:400} +.pn-tl-lead{font-size:.875rem;color:var(--pn-muted);font-style:italic;line-height:1.65;margin-bottom:.75rem} +.pn-tl-bullets{display:flex;flex-direction:column;gap:.4rem} +.pn-tl-bullets li{font-size:.83rem;color:var(--pn-muted);line-height:1.6;padding-left:1rem;position:relative} +.pn-tl-bullets li::before{content:'▸';position:absolute;left:0;top:0;color:var(--pn-accent);font-size:.7rem;line-height:1.8} + +/* ── FORMACIÓN ── */ +.pn-edu-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:2rem} +.pn-edu-card{ + display:flex;gap:1.5rem;align-items:flex-start;padding:2rem; + background:#fff;border:1px solid var(--pn-border);border-radius:var(--radius); + box-shadow:var(--shadow);transition:transform .2s,box-shadow .2s; +} +.pn-edu-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg)} +.pn-edu-card__year{ + font-family:'Raleway',sans-serif;font-size:1.4rem;font-weight:800; + color:var(--pn-accent);flex-shrink:0;line-height:1;padding-top:2px; +} +.pn-edu-card h3{font-family:'Raleway',sans-serif;font-size:.95rem;font-weight:700;margin-bottom:.4rem;line-height:1.4} +.pn-edu-card__inst{font-size:.83rem;color:var(--pn-muted);line-height:1.5} +.pn-edu-card__detail{font-size:.78rem;color:var(--pn-accent);margin-top:.4rem;font-style:italic} + +/* ── COMPETENCIAS / SKILLS ── */ +.pn-skills-cols{display:grid;grid-template-columns:repeat(3,1fr);gap:3rem} +.pn-skills-group h3{ + font-family:'Raleway',sans-serif;font-size:.9rem;font-weight:700; + letter-spacing:.06em;text-transform:uppercase;color:var(--pn-text); + margin-bottom:1.5rem;padding-bottom:.5rem;border-bottom:2px solid var(--pn-accent); +} +.pn-skill{margin-bottom:1.25rem} +.pn-skill__header{ + display:flex;justify-content:space-between;align-items:center; + font-size:.82rem;color:var(--pn-text);margin-bottom:.4rem; +} +.pn-skill__header span:last-child{font-weight:700;color:var(--pn-accent);font-size:.78rem} +.pn-skill__bar{height:6px;background:var(--pn-border);border-radius:3px;overflow:hidden} +.pn-skill__fill{height:100%;background:var(--pn-accent);border-radius:3px;width:0%;transition:width .8s cubic-bezier(.4,0,.2,1)} + +/* ── CERTIFICACIONES ── */ +.pn-certs-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1rem} +.pn-cert{ + display:flex;gap:1.25rem;align-items:flex-start; + padding:1.25rem;background:#fff;border:1px solid var(--pn-border); + border-radius:var(--radius);transition:border-color .2s,box-shadow .2s; +} +.pn-cert:hover{border-color:var(--pn-accent);box-shadow:var(--shadow)} +.pn-cert__year{ + font-family:'Raleway',sans-serif;font-size:.72rem;font-weight:700; + background:var(--pn-accent);color:#fff;padding:.25rem .5rem; + border-radius:var(--radius);flex-shrink:0;letter-spacing:.04em; + align-self:flex-start; +} +.pn-cert h3{font-size:.875rem;font-weight:600;color:var(--pn-text);margin-bottom:.25rem;line-height:1.4} +.pn-cert p{font-size:.78rem;color:var(--pn-muted)} + +/* ── CONTACTO ── */ +.pn-contact-grid{display:grid;grid-template-columns:1fr 360px;gap:4rem;align-items:start} +.pn-contact-list{display:flex;flex-direction:column;gap:1.25rem;margin-bottom:2.5rem} +.pn-contact-list li{display:flex;align-items:flex-start;gap:.875rem;color:rgba(255,255,255,.7);font-size:.9rem} +.pn-contact-list li a{color:rgba(255,255,255,.7);transition:color .2s} +.pn-contact-list li a:hover{color:var(--pn-accent)} +.pn-contact-list__icon{width:18px;height:18px;flex-shrink:0;color:var(--pn-accent);margin-top:1px} +.pn-contact-list__icon svg{width:100%;height:100%} +.pn-contact-extra{ + padding:1.5rem;background:rgba(255,255,255,.05);border-radius:var(--radius); + border-left:3px solid var(--pn-accent);margin-bottom:2rem; +} +.pn-contact-extra h4{font-family:'Raleway',sans-serif;font-size:.72rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--pn-accent);margin-bottom:.5rem} +.pn-contact-extra__title{font-size:.9rem;font-weight:600;color:#fff;margin-bottom:.5rem} +.pn-contact-extra__title span{font-weight:400;color:rgba(255,255,255,.5);margin-left:.5rem} +.pn-contact-extra p:last-child{font-size:.83rem;color:rgba(255,255,255,.5);line-height:1.6} +.pn-extra-dl{display:flex;flex-direction:column;gap:.875rem} +.pn-extra-dl>div{display:grid;grid-template-columns:140px 1fr;gap:.5rem;padding-bottom:.875rem;border-bottom:1px solid rgba(255,255,255,.08)} +.pn-extra-dl>div:last-child{border-bottom:none} +.pn-extra-dl dt{font-size:.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:rgba(255,255,255,.3)} +.pn-extra-dl dd{font-size:.83rem;color:rgba(255,255,255,.65)} + +.pn-bcard-label{font-family:'Raleway',sans-serif;font-size:.68rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--pn-accent);margin-bottom:1rem} +.pn-bcard{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);border-radius:var(--radius);overflow:hidden} +.pn-bcard__front{display:flex;align-items:center;gap:1rem;padding:1.25rem 1.5rem;border-bottom:1px solid rgba(255,255,255,.08)} +.pn-bcard__photo{width:52px;height:52px;border-radius:50%;overflow:hidden;flex-shrink:0;border:2px solid var(--pn-accent)} +.pn-bcard__photo img{width:100%;height:100%;object-fit:cover} +.pn-bcard__name{font-family:'Raleway',sans-serif;font-size:.92rem;font-weight:700;color:#fff;margin-bottom:.2rem} +.pn-bcard__role{font-size:.72rem;color:rgba(255,255,255,.5);line-height:1.4} +.pn-bcard__details{display:grid;grid-template-columns:80px 1fr;gap:1rem;padding:1.25rem 1.5rem} +.pn-bcard__qr img{width:80px;height:80px;border-radius:var(--radius);filter:invert(1) opacity(.85)} +.pn-bcard__info{display:flex;flex-direction:column;gap:3px;font-size:.75rem;color:rgba(255,255,255,.5)} +.pn-bcard__info a{color:var(--pn-accent);font-weight:500;word-break:break-all} + +.pn-footer-bar{ + margin-top:4rem;padding-top:2rem;border-top:1px solid rgba(255,255,255,.08); + text-align:center;font-size:.78rem;color:rgba(255,255,255,.3);letter-spacing:.04em; +} +.pn-wp-content{margin-top:2rem;padding:1.5rem;background:rgba(255,255,255,.05);border-radius:var(--radius);color:rgba(255,255,255,.7);font-size:.9rem;line-height:1.75} + +/* ── SCROLL TOP ── */ +.pn-scroll-top{ + position:fixed;bottom:2rem;right:2rem;z-index:90; + width:44px;height:44px;background:var(--pn-accent);color:#fff; + border-radius:50%;display:flex;align-items:center;justify-content:center; + font-size:1.4rem;box-shadow:var(--shadow); + opacity:0;visibility:hidden;transform:translateY(10px); + transition:opacity .3s,visibility .3s,transform .3s; +} +.pn-scroll-top.is-visible{opacity:1;visibility:visible;transform:translateY(0)} +.pn-scroll-top:hover{background:var(--pn-accent-h);color:#fff} + +/* ── RESPONSIVE 1024 ── */ +@media(max-width:1024px){ + .pn-counters{grid-template-columns:repeat(3,1fr)} + .pn-counter:nth-child(3)::after{display:none} + .pn-skills-cols{grid-template-columns:repeat(2,1fr)} + .pn-contact-grid{grid-template-columns:1fr} + .pn-about-grid{grid-template-columns:220px 1fr;gap:2.5rem} + .pn-highlights-grid{grid-template-columns:repeat(2,1fr)} +} + +/* ── RESPONSIVE 768 ── */ +@media(max-width:768px){ + :root{--pn-header-h:60px} + .pn-section{padding:3.5rem 0} + + .pn-hamburger{display:flex} + .pn-nav{ + display:none;position:absolute;top:var(--pn-header-h);left:0;right:0; + background:#fff;border-bottom:1px solid var(--pn-border); + padding:1rem;box-shadow:var(--shadow);z-index:99; + } + .pn-nav.is-open{display:block} + .pn-nav__list{flex-direction:column;gap:.25rem} + .pn-nav__list li a{padding:.6rem 1rem;font-size:.8rem} + .pn-header__actions{display:none} + + .pn-about-grid{grid-template-columns:1fr} + .pn-about-photo-col{max-width:280px;margin:0 auto} + + .pn-timeline::before{left:20px} + .pn-tl-entry--left,.pn-tl-entry--right{padding:0 0 0 50px;justify-content:flex-start} + .pn-tl-dot{left:14px;transform:none} + .pn-tl-content{max-width:100%} + + .pn-edu-grid{grid-template-columns:1fr} + .pn-skills-cols{grid-template-columns:1fr} + .pn-certs-grid{grid-template-columns:1fr} + .pn-impact-row{grid-template-columns:1fr} + .pn-highlights-grid{grid-template-columns:repeat(2,1fr)} + + .pn-counters{grid-template-columns:repeat(2,1fr)} + .pn-counter::after{display:none} + .pn-counter:nth-child(odd)::after{display:block} + + .pn-hero__avatar{width:100px;height:100px} +} + +/* ── PRINT ── */ +@media print{ + .pn-header,.pn-scroll-top,.pn-scroll-hint,.pn-hero__actions{display:none!important} + .pn-hero{height:auto;min-height:0;padding:3rem 2rem} + .pn-hero__bg{display:none} + .pn-hero__overlay{background:var(--pn-dark)!important} + .pn-main{padding-top:0} + .pn-section{padding:2rem 0} + .pn-tl-content,.pn-highlight-card,.pn-edu-card{box-shadow:none;page-break-inside:avoid} + .pn-skill__fill{background:var(--pn-accent)!important} + a{color:inherit!important} + .pn-section--dark{background:#1a2332!important} +} diff --git a/frontend/persona-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx b/frontend/persona-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx new file mode 100644 index 0000000..544bf3d Binary files /dev/null and b/frontend/persona-cv-theme/assets/docs/CV_Patricia_Flores_Sanchez_ES_FINAL.docx differ diff --git a/frontend/persona-cv-theme/assets/images/patricia-flores-profile.jpeg b/frontend/persona-cv-theme/assets/images/patricia-flores-profile.jpeg new file mode 100644 index 0000000..4151a20 Binary files /dev/null and b/frontend/persona-cv-theme/assets/images/patricia-flores-profile.jpeg differ diff --git a/frontend/persona-cv-theme/assets/images/patricia-flores-qr.png b/frontend/persona-cv-theme/assets/images/patricia-flores-qr.png new file mode 100644 index 0000000..55703ad Binary files /dev/null and b/frontend/persona-cv-theme/assets/images/patricia-flores-qr.png differ diff --git a/frontend/persona-cv-theme/assets/images/profile-placeholder.svg b/frontend/persona-cv-theme/assets/images/profile-placeholder.svg new file mode 100644 index 0000000..3d7dbb0 --- /dev/null +++ b/frontend/persona-cv-theme/assets/images/profile-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/persona-cv-theme/assets/js/theme.js b/frontend/persona-cv-theme/assets/js/theme.js new file mode 100644 index 0000000..3d7c4d6 --- /dev/null +++ b/frontend/persona-cv-theme/assets/js/theme.js @@ -0,0 +1,97 @@ +document.addEventListener("DOMContentLoaded", () => { + + /* ── Header scroll ── */ + const header = document.querySelector(".pn-header"); + const scrollTop = document.querySelector(".pn-scroll-top"); + window.addEventListener("scroll", () => { + const y = window.scrollY; + if (header) header.classList.toggle("is-scrolled", y > 60); + if (scrollTop) scrollTop.classList.toggle("is-visible", y > 400); + }, { passive: true }); + + /* ── Mobile nav ── */ + const hamburger = document.querySelector(".pn-hamburger"); + const nav = document.querySelector(".pn-nav"); + if (hamburger && nav) { + hamburger.addEventListener("click", () => { + const open = nav.classList.toggle("is-open"); + hamburger.setAttribute("aria-expanded", String(open)); + const s = hamburger.querySelectorAll("span"); + if (s.length === 3) { + s[0].style.transform = open ? "rotate(45deg) translate(5px, 5px)" : ""; + s[1].style.opacity = open ? "0" : ""; + s[2].style.transform = open ? "rotate(-45deg) translate(5px, -5px)" : ""; + } + }); + document.addEventListener("click", e => { + if (!nav.contains(e.target) && !hamburger.contains(e.target)) { + nav.classList.remove("is-open"); + hamburger.setAttribute("aria-expanded", "false"); + hamburger.querySelectorAll("span").forEach(s => { s.style.transform = ""; s.style.opacity = ""; }); + } + }); + } + + /* ── Nav active on scroll ── */ + const navLinks = Array.from(document.querySelectorAll(".pn-nav__list a[href^='#']")); + const sections = Array.from(document.querySelectorAll("section[id]")); + if (navLinks.length && sections.length) { + const obs = new IntersectionObserver(entries => { + entries.forEach(e => { + if (e.isIntersecting) { + const id = "#" + e.target.id; + navLinks.forEach(l => l.classList.toggle("is-active", l.getAttribute("href") === id)); + } + }); + }, { rootMargin: "-20% 0px -70% 0px" }); + sections.forEach(s => obs.observe(s)); + } + navLinks.forEach(l => l.addEventListener("click", () => { + if (nav) nav.classList.remove("is-open"); + })); + + /* ── Animated counters ── */ + const counters = Array.from(document.querySelectorAll(".pn-counter__val")); + const counterObs = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting) return; + counterObs.unobserve(entry.target); + const el = entry.target; + const end = parseInt(el.dataset.count, 10); + const duration = 1800; + const step = Math.ceil(end / (duration / 16)); + let current = 0; + const tick = () => { + current = Math.min(current + step, end); + el.textContent = current.toLocaleString("es-EC"); + if (current < end) requestAnimationFrame(tick); + }; + requestAnimationFrame(tick); + }); + }, { threshold: 0.3 }); + counters.forEach(c => counterObs.observe(c)); + + /* ── Skill bars animation ── */ + const skillBars = Array.from(document.querySelectorAll(".pn-skill__fill")); + const skillObs = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (!entry.isIntersecting) return; + skillObs.unobserve(entry.target); + const bar = entry.target; + const pct = bar.dataset.pct; + setTimeout(() => { bar.style.width = pct + "%"; }, 100); + }); + }, { threshold: 0.2 }); + skillBars.forEach(b => skillObs.observe(b)); + + /* ── Print ── */ + document.querySelectorAll("[data-persona-print]").forEach(b => b.addEventListener("click", () => window.print())); + + /* ── Scroll top ── */ + if (scrollTop) { + scrollTop.addEventListener("click", e => { + e.preventDefault(); + window.scrollTo({ top: 0, behavior: "smooth" }); + }); + } +}); diff --git a/frontend/persona-cv-theme/footer.php b/frontend/persona-cv-theme/footer.php new file mode 100644 index 0000000..fcb1e0c --- /dev/null +++ b/frontend/persona-cv-theme/footer.php @@ -0,0 +1,6 @@ + +
+⇧ + + + diff --git a/frontend/persona-cv-theme/front-page.php b/frontend/persona-cv-theme/front-page.php new file mode 100644 index 0000000..b6d456b --- /dev/null +++ b/frontend/persona-cv-theme/front-page.php @@ -0,0 +1,382 @@ + + + +
+
+
+
+
+ <?php echo esc_attr($heroName); ?> +
+

+

+
+ + | + +
+
+ + + + + + + + +
+
+ + + + + +
+
+ + + + + + + + + + + +
+
+ + + +
+ + +
+
+
+ ── ── +

+
+
+ +
+
+
+ <?php echo esc_attr($heroName); ?> +
+

+

+
+ + + + +
+ +
+ +
+

+

+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ +
+ + 0 + + +
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+
+ +
+
+ + +
+

+
+ +
+ +
+ +
+ +

+

+
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+
+ $job) : ?> +
+
+ +

+

+ + · +

+

+
    + +
  • + +
+
+ +
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+
+ +
+ +
+

+

+ +

+ +
+
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+
+ $items) : ?> +
+

+ +
+
+ + % +
+
+
+
+
+ +
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+
+ +
+ +
+

+

+
+
+ +
+
+
+ + +
+
+
+ ── ── +

+
+
+ +
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • + +
  • + + LinkedIn +
  • + +
+ +
+

+

+

+
+ +
+ $value) : ?> +
+ +
+
+ +
+

+
+
+
+ <?php echo esc_attr($heroName); ?> +
+
+

+

+
+
+
+
+ <?php esc_attr_e('QR perfil web', 'persona'); ?> +
+
+

+

+

+ +
+
+
+
+
+ + +
+ + + +
+
+ + diff --git a/frontend/persona-cv-theme/functions.php b/frontend/persona-cv-theme/functions.php new file mode 100644 index 0000000..b9944cc --- /dev/null +++ b/frontend/persona-cv-theme/functions.php @@ -0,0 +1,67 @@ + __('Menú principal', 'persona')]); +} +add_action('after_setup_theme', 'persona_setup'); + +function persona_enqueue_assets(): void { + wp_enqueue_style('persona-fonts', + 'https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;500;600;700;800&family=Open+Sans:wght@300;400;600&display=swap', + [], null); + wp_enqueue_style('persona-theme', + get_template_directory_uri() . '/assets/css/theme.css', + ['persona-fonts'], wp_get_theme()->get('Version')); + wp_enqueue_script('persona-theme', + get_template_directory_uri() . '/assets/js/theme.js', + [], wp_get_theme()->get('Version'), true); +} +add_action('wp_enqueue_scripts', 'persona_enqueue_assets'); + +function persona_body_classes(array $classes): array { + $classes[] = 'persona-shell'; + if (is_front_page()) $classes[] = 'persona-front'; + return $classes; +} +add_filter('body_class', 'persona_body_classes'); + +function persona_redirect_sample(): void { + if (!is_page()) return; + $p = get_queried_object(); + if ($p instanceof WP_Post && $p->post_name === 'pagina-ejemplo') { + wp_safe_redirect(home_url('/'), 301); exit; + } +} +add_action('template_redirect', 'persona_redirect_sample', 1); + +function persona_render_nav(): string { + if (has_nav_menu('primary')) { + return trim((string) wp_nav_menu([ + 'theme_location' => 'primary', 'container' => false, + 'fallback_cb' => false, 'menu_class' => 'pn-nav__list', 'echo' => false, + ])); + } + $items = [ + '#sobre-mi' => 'Sobre mí', + '#impacto' => 'Impacto', + '#experiencia' => 'Experiencia', + '#formacion' => 'Formación', + '#competencias' => 'Competencias', + '#certificaciones' => 'Certificaciones', + '#contacto' => 'Contacto', + ]; + $html = '
    '; + foreach ($items as $href => $label) { + $html .= sprintf('
  • %s
  • ', esc_url($href), esc_html($label)); + } + return $html . '
'; +} diff --git a/frontend/persona-cv-theme/header.php b/frontend/persona-cv-theme/header.php new file mode 100644 index 0000000..83f0708 --- /dev/null +++ b/frontend/persona-cv-theme/header.php @@ -0,0 +1,34 @@ + + +> + + + + + +> + + +
+
+ + + + + + +
+
+ +
diff --git a/frontend/persona-cv-theme/inc/customizer.php b/frontend/persona-cv-theme/inc/customizer.php new file mode 100644 index 0000000..9b13d7d --- /dev/null +++ b/frontend/persona-cv-theme/inc/customizer.php @@ -0,0 +1,23 @@ +add_section('persona_hero', ['title' => __('Persona CV: Presentación', 'persona'), 'priority' => 30]); + $fields = [ + 'pn_hero_name' => ['label' => __('Nombre', 'persona'), 'default' => 'Patricia Soledad Flores Sánchez'], + 'pn_hero_role' => ['label' => __('Rol', 'persona'), 'default' => 'Gerente / Jefa de Operaciones Financieras'], + 'pn_contact_location' => ['label' => __('Ubicación', 'persona'), 'default' => 'Loja, Ecuador'], + 'pn_contact_email' => ['label' => __('Correo', 'persona'), 'default' => 'patricflorsanchez@gmail.com'], + 'pn_contact_phone' => ['label' => __('Teléfono', 'persona'), 'default' => '+593 99 469 8405'], + 'pn_social_linkedin' => ['label' => __('LinkedIn URL', 'persona'), 'default' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/'], + ]; + $wp_customize->add_setting('pn_profile_image', ['default' => '', 'sanitize_callback' => 'esc_url_raw']); + $wp_customize->add_control(new WP_Customize_Image_Control($wp_customize, 'pn_profile_image', [ + 'label' => __('Foto de perfil', 'persona'), 'section' => 'persona_hero', 'settings' => 'pn_profile_image', + ])); + foreach ($fields as $key => $field) { + $isUrl = str_contains($key, 'linkedin'); + $wp_customize->add_setting($key, ['default' => $field['default'], 'sanitize_callback' => $isUrl ? 'esc_url_raw' : 'sanitize_text_field']); + $wp_customize->add_control($key, ['label' => $field['label'], 'section' => 'persona_hero', 'type' => 'text']); + } +} +add_action('customize_register', 'persona_customize_register'); diff --git a/frontend/persona-cv-theme/inc/cv-data.php b/frontend/persona-cv-theme/inc/cv-data.php new file mode 100644 index 0000000..b7b5e8e --- /dev/null +++ b/frontend/persona-cv-theme/inc/cv-data.php @@ -0,0 +1,157 @@ + [ + 'eyebrow' => 'Currículum ejecutivo', + 'name' => 'Patricia Soledad Flores Sánchez', + 'role' => 'Gerente / Jefa de Operaciones Financieras', + 'role2' => 'Sector financiero y microfinanzas', + 'intro' => 'Ejecutiva financiera con más de 22 años de trayectoria en banca y microfinanzas. Máster en Dirección y Planificación Financiera, reconocido por la SENECYT en Ecuador, con especialización en gestión operativa, transformación digital de procesos, control interno y liderazgo de equipos multidisciplinarios.', + 'location' => 'Loja, Ecuador', + 'email' => 'patricflorsanchez@gmail.com', + 'phone' => '+593 99 469 8405', + 'linkedin' => 'https://www.linkedin.com/in/patricia-soledad-flores-s%C3%A1nchez-43b84418/', + 'availability' => 'Inmediata | Presencial / Híbrida / Remota', + 'focus' => 'Operaciones financieras, control interno y transformación digital', + 'languages' => 'Español nativo | Inglés básico en formación activa', + 'born' => '3 de agosto de 1980', + 'id' => '1103591648', + 'web' => 'pflres.linkedcorp.net', + ], + 'metrics' => [ + ['value' => '22', 'suffix' => '+', 'label' => 'Años de experiencia'], + ['value' => '26', 'suffix' => '', 'label' => 'Agencias lideradas'], + ['value' => '48', 'suffix' => '', 'label' => 'Personas a cargo'], + ['value' => '5', 'suffix' => 'M', 'label' => 'USD ops. mensuales'], + ['value' => '28000', 'suffix' => '', 'label' => 'Clientes coordinados'], + ], + 'highlights' => [ + ['value' => '9/10', 'label' => 'Calificación excelente 4 trimestres consecutivos'], + ['value' => '74%', 'label' => 'Reducción requerimientos soporte a Dirección'], + ['value' => '1.68', 'suffix' => 'días', 'label' => 'Tiempo desembolso Q3 2025, mejor del año'], + ['value' => '75%', 'label' => 'Reducción rotación de personal 2022–2026'], + ], + 'summary' => [ + 'title' => 'Perfil estratégico con enfoque en eficiencia, control y ejecución sostenible', + 'body' => 'Actualmente lidera la operatividad de 26 agencias a nivel nacional y coordina aproximadamente 1.500 operaciones de microcrédito mensuales, por un monto cercano a USD 5 millones. También articula la recaudación de cerca de 28.000 clientes por canales internos y externos, integrando visión operativa, criterio financiero, disciplina de control y liderazgo sostenido del desempeño.', + ], + 'impact' => [ + ['title' => 'Transformación digital', 'body' => 'Lideró desembolsos por transferencia bancaria, integración de APIs bancarias y digitalización de expedientes y pagarés en 13 agencias.'], + ['title' => 'Automatización crítica', 'body' => 'Automatizó el seguro de desgravamen, desde el cálculo de prima hasta la liquidación contable, reduciendo tiempos de 17 a 10 días.'], + ['title' => 'Supervisión nacional', 'body' => 'Implementó una matriz de supervisión y certificación por agencia, elevando el índice de cumplimiento operativo del 65 % al 88 %.'], + ], + 'experience' => [ + [ + 'period' => 'Abr. 2022 – Presente', 'title' => 'Jefa de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', 'location' => 'Loja, Ecuador', + 'lead' => 'Responsable del desempeño operativo nacional, cumplimiento del POA, control interno, digitalización de procesos y gestión de equipos.', + 'bullets' => [ + 'Cumplimiento del POA: 98 % (Q2 2025), 96 % (Q3 2025), 100 % (Q4 2025) y 105 % (Q1 2026).', + 'Desembolsos por transferencia: 47,80 % del monto total en Q1 2026.', + 'Redujo requerimientos de soporte un 74 %, de 1.489 a 382, mediante descentralización.', + 'Automatizó el seguro de desgravamen con asientos contables automatizados.', + 'Elevó el cumplimiento operativo nacional del 65 % al 88 %.', + 'Redujo el tiempo de desembolso a 1,68 días en Q3 2025 (meta: máx. 2 días).', + 'Recuperación de mora: 90,60 % en Q1 2026 — USD 3,06 M sobre USD 3,37 M.', + 'Redujo la rotación del área del 41,46 % al 10,24 % (dic. 2022 – mar. 2026).', + 'Participa en comités estratégicos, auditorías y representación institucional internacional.', + ], + ], + [ + 'period' => 'Feb. 2021 – Mar. 2022', 'title' => 'Supervisora de Operaciones', + 'company' => 'Fundación FACES Microfinanzas', 'location' => 'Loja, Ecuador', + 'lead' => 'Supervisó operaciones de 18 agencias a nivel nacional y regularizó procesos críticos inexistentes o incompletos.', + 'bullets' => [ + 'Reconstruyó desde cero las bases de cálculo del seguro de desgravamen.', + 'Elaboró el instructivo de garantías hipotecarias (proceso inexistente previamente).', + 'Inició el proyecto de automatización de pagos por depósitos y transferencias.', + ], + ], + [ + 'period' => '2019 – 2021', 'title' => 'Actualización y formación profesional', + 'company' => 'Formación independiente', 'location' => 'Ecuador', + 'lead' => 'Etapa de fortalecimiento técnico tras salida de Mutualista Pichincha.', + 'bullets' => ['SAP FI (30 h).', 'Excel Empresarial (24 h).', 'Prevención de Lavado de Activos — UAFE (5 h).'], + ], + [ + 'period' => 'Ago. 2004 – May. 2019', 'title' => 'Oficial Operativa Regional / Jefa Operativa', + 'company' => 'Mutualista Pichincha', 'location' => 'Loja, Ecuador', + 'lead' => 'Trayectoria ascendente de 15 años desde Cajera hasta Jefa Operativa Regional.', + 'bullets' => [ + 'Reconocida a nivel nacional por excelencia en control operativo.', + 'Agencia por encima del 80 % en gestión de excepciones de crédito hipotecario.', + 'Administró proyecto inmobiliario institucional en Loja.', + 'Cumplimiento ALA/FT al 100 % en comités y auditorías sin observaciones.', + ], + ], + [ + 'period' => 'Ene. 2002 – Nov. 2003', 'title' => 'Cajera', + 'company' => 'Cooperativa de Ahorro y Crédito El Porvenir', 'location' => 'Loja, Ecuador', + 'lead' => 'Inicio de carrera en el frente operativo financiero con disciplina de caja y apoyo contable.', + 'bullets' => [ + 'Gestión de caja sin faltantes ni sobrantes.', + 'Contrato renovado anticipadamente a los tres meses.', + 'Apoyo a Contabilidad y reemplazo temporal de la contadora.', + ], + ], + ], + 'education' => [ + [ + 'year' => '2021', 'title' => 'Máster Universitario en Dirección y Planificación Financiera', + 'institution' => 'Universidad Europea Miguel de Cervantes — Valladolid, España', + 'detail' => 'Reconocido por la SENECYT — Ecuador, febrero de 2023 | Reg. 2021/394270.', + ], + [ + 'year' => '2007', 'title' => 'Ingeniera en Administración en Banca y Finanzas', + 'institution' => 'Universidad Técnica Particular de Loja (UTPL)', 'detail' => '', + ], + ], + 'certifications' => [ + ['year' => '2026', 'title' => 'Mujeres que Lideran: Alta Gerencia', 'issuer' => 'Foro UNIR — Universidad Internacional de La Rioja'], + ['year' => '2025', 'title' => 'Programa Internacional de Habilidades Gerenciales (48 h)', 'issuer' => 'IDEASGROUP — Ecuador / México'], + ['year' => '2025', 'title' => 'Transforma tu Trabajo con Inteligencia Artificial (12 h)', 'issuer' => 'Red de Instituciones Financieras de Desarrollo'], + ['year' => '2025', 'title' => 'Excel: Básico a Dashboard (40 h)', 'issuer' => 'DMARURI Training & Consulting'], + ['year' => '2024', 'title' => 'Administración por Procesos — BPM (64 h)', 'issuer' => 'Escuela Politécnica Nacional — CEC'], + ['year' => '2023', 'title' => 'Formación de Formadores (80 h)', 'issuer' => 'Ministerio del Trabajo del Ecuador | Vigente hasta 2028'], + ['year' => '2023', 'title' => 'Power BI (40 h) | KPI (12 h)', 'issuer' => 'Liderazgo Capacitación & Eventos'], + ['year' => '2020', 'title' => 'Gestión Financiera ERP SAP FI — Key User (30 h)', 'issuer' => 'Socio SAP certificado'], + ['year' => '2020', 'title' => 'Prevención de Lavado de Activos y Financiamiento de Delitos', 'issuer' => 'UAFE — Ecuador'], + ['year' => '2013', 'title' => 'Tributación Integral (36 h)', 'issuer' => 'Universidad Técnica Particular de Loja'], + ], + 'competencies' => [ + 'Finanzas y Operaciones' => [ + ['name' => 'Gestión de operaciones financieras', 'pct' => 95], + ['name' => 'Control interno y cumplimiento', 'pct' => 95], + ['name' => 'Gestión de cartera y microfinanzas', 'pct' => 90], + ['name' => 'Conciliación bancaria / Tesorería', 'pct' => 90], + ['name' => 'ALA/FT y prevención de riesgos', 'pct' => 88], + ], + 'Tecnología y Procesos' => [ + ['name' => 'Transformación digital / BPM', 'pct' => 85], + ['name' => 'SAP FI', 'pct' => 78], + ['name' => 'Excel avanzado y dashboards', 'pct' => 85], + ['name' => 'Power BI', 'pct' => 75], + ['name' => 'IA aplicada al trabajo', 'pct' => 70], + ], + 'Liderazgo' => [ + ['name' => 'Gestión de equipos (hasta 48 p.)', 'pct' => 95], + ['name' => 'Gestión del cambio organizacional', 'pct' => 90], + ['name' => 'Formación de formadores', 'pct' => 88], + ['name' => 'Remuneración variable y carrera', 'pct' => 85], + ], + ], + 'volunteer' => [ + 'title' => 'Voluntaria — Hogar San Camilo de Lellis', + 'year' => '2025', + 'body' => 'Hogar de niñas con discapacidad | Loja, Ecuador. Alineado a la responsabilidad social de la Fundación FACES Microfinanzas.', + ], + 'additional' => [ + 'Idiomas' => 'Español nativo | Inglés básico en formación activa', + 'Disponibilidad' => 'Inmediata | Presencial / Híbrida / Remota', + 'C.I.' => '1103591648', + 'Fecha de nacimiento' => '3 de agosto de 1980', + 'Web profesional' => 'pflres.linkedcorp.net', + ], + ]; +} diff --git a/frontend/persona-cv-theme/inc/visits.php b/frontend/persona-cv-theme/inc/visits.php new file mode 100644 index 0000000..9c0fdb9 --- /dev/null +++ b/frontend/persona-cv-theme/inc/visits.php @@ -0,0 +1,35 @@ + 0) update_post_meta($fp, '_persona_views', (int) get_post_meta($fp, '_persona_views', true) + 1); + return; + } + $id = get_queried_object_id(); + if ($id > 0) update_post_meta($id, '_persona_views', (int) get_post_meta($id, '_persona_views', true) + 1); +} +add_action('template_redirect', 'persona_track_visit'); +function persona_client_ip(): string { + foreach (['HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $k) { + if (!empty($_SERVER[$k])) return trim(explode(',', (string) $_SERVER[$k])[0]); + } + return '0.0.0.0'; +} +add_action('wp_dashboard_setup', function() { + wp_add_dashboard_widget('persona_visits', __('Persona CV: Visitas', 'persona'), function() { + printf('

%s %d

', esc_html__('Total:', 'persona'), (int) get_option('persona_total_visits', 0)); + }); +}); diff --git a/frontend/persona-cv-theme/index.php b/frontend/persona-cv-theme/index.php new file mode 100644 index 0000000..9f37482 --- /dev/null +++ b/frontend/persona-cv-theme/index.php @@ -0,0 +1,12 @@ + +
+
+ +
>

+ +
+
+ diff --git a/frontend/persona-cv-theme/style.css b/frontend/persona-cv-theme/style.css new file mode 100644 index 0000000..f6a9c64 --- /dev/null +++ b/frontend/persona-cv-theme/style.css @@ -0,0 +1,11 @@ +/* +Theme Name: Persona CV +Theme URI: https://pflres.linkedcorp.net +Author: mgonzalez +Description: Tema WordPress de una página, inspirado en Personage/Pixflow. Hero fullscreen, nav sticky, secciones alternas, timeline centrado, barras de skills, contadores animados. Raleway + Open Sans. +Version: 1.0.0 +Requires at least: 6.8 +Tested up to: 6.8 +Requires PHP: 8.1 +Text Domain: persona +*/ diff --git a/img-pflores.jpeg b/img-pflores.jpeg new file mode 100644 index 0000000..4151a20 Binary files /dev/null and b/img-pflores.jpeg differ diff --git a/tarjeta-patricia-flores-qr.png b/tarjeta-patricia-flores-qr.png new file mode 100644 index 0000000..55703ad Binary files /dev/null and b/tarjeta-patricia-flores-qr.png differ diff --git a/tarjeta-presentacion-patricia-flores.html b/tarjeta-presentacion-patricia-flores.html new file mode 100644 index 0000000..6861e2d --- /dev/null +++ b/tarjeta-presentacion-patricia-flores.html @@ -0,0 +1,253 @@ + + + + + + Tarjeta de Presentación | Patricia Flores + + + +
+
+
+ Patricia Soledad Flores Sánchez +
+

Tarjeta de presentación

+

Patricia Soledad Flores Sánchez

+

Gerente / Jefa de Operaciones Financieras | Sector financiero y microfinanzas

+
+
+ +
+
+ 22+ + Años de trayectoria en banca y microfinanzas. +
+
+ 26 + Agencias bajo liderazgo operativo a nivel nacional. +
+
+ 48 + Personas coordinadas en la estructura operativa. +
+
+ + +
+ + +
+ +