gym-gus / index.html
Elkusta's picture
No puedo seleccionar n煤mero de series en el objetivo de cada ejercicio - Initial Deployment
40cf759 verified
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GymProgress Tracker</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #3498db;
--secondary: #2ecc71;
--dark: #2c3e50;
--light: #ecf0f1;
--danger: #e74c3c;
--warning: #f39c12;
--gray: #95a5a6;
--border: #bdc3c7;
--purple: #9b59b6;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #2c3e50);
color: var(--light);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1400px;
margin: 0 auto;
background-color: rgba(44, 62, 80, 0.95);
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
overflow: hidden;
}
header {
background: linear-gradient(to right, var(--dark), #1a2a6c);
padding: 20px;
text-align: center;
border-bottom: 3px solid var(--primary);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
color: white;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
h1 i {
color: var(--secondary);
}
.subtitle {
font-size: 1.1rem;
opacity: 0.9;
max-width: 700px;
margin: 0 auto;
}
.app-controls {
display: flex;
justify-content: space-between;
padding: 20px;
background-color: rgba(52, 73, 94, 0.7);
flex-wrap: wrap;
gap: 15px;
border-bottom: 1px solid var(--border);
}
.week-nav {
display: flex;
gap: 10px;
align-items: center;
}
.week-nav h2 {
min-width: 120px;
text-align: center;
}
button {
background-color: var(--primary);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 8px;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
button:active {
transform: translateY(0);
}
button.secondary {
background-color: var(--secondary);
}
button.warning {
background-color: var(--warning);
}
button.purple {
background-color: var(--purple);
}
.week-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 25px;
padding: 25px;
}
.day-card {
background: linear-gradient(to bottom right, rgba(44, 62, 80, 0.8), rgba(52, 73, 94, 0.9));
border-radius: 12px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.4);
padding: 20px;
border: 1px solid var(--primary);
transition: transform 0.3s;
}
.day-card:hover {
transform: translateY(-5px);
}
.day-header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid var(--primary);
padding-bottom: 15px;
margin-bottom: 20px;
}
.day-title {
font-weight: 700;
font-size: 1.4rem;
color: var(--secondary);
display: flex;
align-items: center;
gap: 10px;
}
.add-exercise-btn {
background-color: var(--secondary);
padding: 8px 15px;
font-size: 0.9rem;
}
.exercise {
background: rgba(25, 42, 58, 0.7);
border-radius: 8px;
margin-bottom: 20px;
border: 1px solid rgba(189, 195, 199, 0.2);
overflow: hidden;
transition: all 0.3s;
}
.exercise-header {
display: flex;
justify-content: space-between;
cursor: pointer;
padding: 15px;
align-items: center;
background: rgba(30, 50, 70, 0.6);
}
.exercise-header.active {
background: rgba(52, 73, 94, 0.8);
border-bottom: 1px solid var(--primary);
}
.exercise-name {
font-weight: 700;
font-size: 1.2rem;
color: var(--light);
display: flex;
align-items: center;
gap: 10px;
}
.exercise-image {
width: 60px;
height: 60px;
border-radius: 5px;
object-fit: cover;
margin-right: 10px;
border: 2px solid var(--primary);
/* Asegurar que se ajusta en m贸viles */
max-width: 100%;
}
.exercise-target {
background: rgba(52, 152, 219, 0.2);
border-radius: 6px;
padding: 5px 10px;
font-size: 0.9rem;
color: var(--light);
margin-top: 5px;
display: inline-flex;
align-items: center;
gap: 5px;
cursor: pointer;
transition: all 0.3s;
}
.exercise-target:hover {
background: rgba(52, 152, 219, 0.4);
}
.exercise-controls {
display: flex;
gap: 10px;
}
.icon-btn {
background: none;
border: none;
color: var(--light);
cursor: pointer;
font-size: 1.1rem;
padding: 5px;
border-radius: 50%;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.icon-btn:hover {
background-color: rgba(255, 255, 255, 0.1);
transform: scale(1.1);
}
.icon-btn.delete {
color: var(--danger);
}
.exercise-content {
display: none;
padding: 15px;
}
.sets-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-top: 15px;
}
.set {
display: flex;
flex-direction: column;
background: rgba(30, 50, 70, 0.6);
border-radius: 8px;
padding: 12px;
border: 1px solid rgba(52, 152, 219, 0.3);
position: relative;
}
.set-number {
position: absolute;
top: 5px;
left: 5px;
font-size: 0.8rem;
color: var(--gray);
}
.set-inputs {
display: flex;
gap: 10px;
margin-top: 15px;
}
.set-inputs input {
width: 100%;
padding: 10px;
background: rgba(25, 42, 58, 0.8);
border: 1px solid var(--primary);
border-radius: 6px;
text-align: center;
font-size: 1rem;
color: white;
}
.set-inputs input:focus {
outline: none;
border-color: var(--secondary);
box-shadow: 0 0 0 2px rgba(46, 204, 113, 0.3);
}
.rir-input {
width: 100%;
padding: 10px;
background: rgba(155, 89, 182, 0.2);
border: 1px solid var(--purple);
border-radius: 6px;
text-align: center;
font-size: 1rem;
color: white;
margin-top: 10px;
}
.rir-input:focus {
outline: none;
border-color: #8e44ad;
box-shadow: 0 0 0 2px rgba(155, 89, 182, 0.3);
}
.prev-data {
font-size: 0.85rem;
color: var(--warning);
text-align: center;
margin-top: 8px;
font-style: italic;
}
.reps-info {
text-align: center;
font-size: 0.9rem;
color: var(--gray);
margin-top: 5px;
}
#chart-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.chart-container {
background: linear-gradient(to bottom right, #1a2a6c, #2c3e50);
width: 90%;
max-width: 900px;
padding: 30px;
border-radius: 15px;
position: relative;
border: 2px solid var(--primary);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.6);
}
.close-btn {
position: absolute;
top: 15px;
right: 15px;
background: var(--danger);
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
cursor: pointer;
font-size: 1.2rem;
transition: all 0.3s;
}
.close-btn:hover {
transform: rotate(90deg) scale(1.1);
}
.modal-header {
margin-bottom: 25px;
text-align: center;
}
#chart-title {
color: var(--secondary);
font-size: 1.8rem;
}
#exercise-name {
color: white;
font-weight: bold;
}
.empty-state {
text-align: center;
padding: 30px;
color: var(--gray);
font-size: 1.1rem;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 15px;
color: var(--primary);
}
.footer {
text-align: center;
padding: 20px;
color: var(--gray);
font-size: 0.9rem;
border-top: 1px solid rgba(189, 195, 199, 0.2);
}
.exercise-form {
background: rgba(25, 42, 58, 0.8);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
display: none;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: var(--light);
}
.form-group input, .form-group select {
width: 100%;
padding: 12px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(15, 32, 48, 0.7);
color: white;
font-size: 1rem;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
.rir-info {
background: rgba(155, 89, 182, 0.1);
border-left: 4px solid var(--purple);
padding: 10px;
margin-top: 15px;
border-radius: 0 4px 4px 0;
font-size: 0.9rem;
}
.youtube-btn {
background: #ff0000;
color: white;
padding: 8px 15px;
border-radius: 4px;
display: inline-flex;
align-items: center;
gap: 5px;
text-decoration: none;
margin-top: 10px;
transition: all 0.3s;
}
.youtube-btn:hover {
background: #cc0000;
transform: translateY(-2px);
}
.exercise-db-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: none;
justify-content: center;
align-items: center;
z-index: 1001;
}
.db-container {
background: linear-gradient(to bottom right, #1a2a6c, #2c3e50);
width: 90%;
max-width: 600px;
padding: 30px;
border-radius: 15px;
position: relative;
border: 2px solid var(--primary);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.6);
}
.category-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 15px;
margin-top: 20px;
}
.category-card {
background: rgba(52, 73, 94, 0.7);
border-radius: 8px;
padding: 15px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
border: 1px solid rgba(52, 152, 219, 0.3);
}
.category-card:hover {
transform: translateY(-5px);
border-color: var(--secondary);
}
.exercise-list {
max-height: 400px;
overflow-y: auto;
margin-top: 20px;
}
.exercise-item {
background: rgba(30, 50, 70, 0.6);
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid rgba(52, 152, 219, 0.3);
transition: all 0.3s;
}
.exercise-item:hover {
border-color: var(--secondary);
background: rgba(30, 50, 70, 0.8);
}
.tech-btn {
background: var(--warning);
color: var(--dark);
padding: 8px 12px;
border-radius: 4px;
display: inline-flex;
align-items: center;
gap: 5px;
text-decoration: none;
margin-top: 10px;
font-weight: 600;
transition: all 0.3s;
}
.tech-btn:hover {
background: #e67e22;
transform: translateY(-2px);
}
.target-edit-form {
display: flex;
gap: 10px;
margin-top: 10px;
}
.target-input {
flex: 1;
padding: 8px;
border-radius: 4px;
border: 1px solid var(--primary);
background: rgba(25, 42, 58, 0.8);
color: white;
}
.save-target-btn {
background: var(--secondary);
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
}
/* Nuevos estilos para funcionalidades a帽adidas */
.weight-section {
display: flex;
align-items: center;
gap: 15px;
padding: 15px 25px;
background: rgba(25, 42, 58, 0.8);
border-bottom: 1px solid var(--border);
}
.weight-input {
display: flex;
gap: 10px;
align-items: center;
}
.weight-input input {
width: 100px;
padding: 10px;
border-radius: 8px;
border: 1px solid var(--primary);
background: rgba(15, 32, 48, 0.7);
color: white;
text-align: center;
}
.feelings-section {
padding: 20px;
margin: 0 25px 25px;
background: rgba(25, 42, 58, 0.8);
border-radius: 12px;
border: 1px solid var(--secondary);
}
.feelings-section h3 {
margin-bottom: 15px;
color: var(--secondary);
display: flex;
align-items: center;
gap: 10px;
}
#week-feelings {
width: 100%;
min-height: 100px;
padding: 15px;
border-radius: 8px;
border: 1px solid var(--border);
background: rgba(15, 32, 48, 0.7);
color: white;
font-size: 1rem;
resize: vertical;
}
#weight-chart-btn {
background-color: var(--purple);
}
/* Modal de peso */
#weight-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
/* Objetivos separados */
.target-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 5px;
}
.target-box {
background: rgba(52, 152, 219, 0.2);
border-radius: 6px;
padding: 8px 12px;
font-size: 0.9rem;
color: var(--light);
display: flex;
align-items: center;
gap: 5px;
cursor: pointer;
transition: all 0.2s;
}
.target-box:hover {
background: rgba(52, 152, 219, 0.4);
}
.target-box[data-type="edit-target"] {
margin-top: 10px;
background: rgba(46, 204, 113, 0.2);
}
.target-box[data-type="edit-target"]:hover {
background: rgba(46, 204, 113, 0.4);
}
.target-box i {
color: var(--secondary);
}
/* Nuevos estilos para edici贸n de objetivos */
.target-edit-select {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid var(--primary);
background: rgba(25, 42, 58, 0.8);
color: white;
}
.target-edit-form-container {
display: flex;
flex-direction: column;
gap: 10px;
background: rgba(30, 50, 70, 0.8);
padding: 15px;
border-radius: 8px;
margin-top: 10px;
border: 1px solid var(--secondary);
}
.target-edit-label {
font-size: 0.9rem;
color: var(--light);
}
.target-edit-input {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid var(--primary);
background: rgba(25, 42, 58, 0.8);
color: white;
}
.target-edit-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 10px;
}
@media (max-width: 768px) {
.week-grid {
grid-template-columns: 1fr;
padding: 15px;
}
.app-controls {
flex-direction: column;
align-items: center;
}
h1 {
font-size: 2rem;
}
.set-inputs {
flex-direction: column;
gap: 8px;
}
.weight-section {
flex-direction: column;
gap: 10px;
}
.exercise-name {
flex-wrap: wrap;
}
.exercise-image {
width: 50px;
height: 50px;
}
.target-container {
flex-wrap: wrap;
}
}
/* Animaciones */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.day-card {
animation: fadeIn 0.5s ease-out;
}
.exercise {
animation: fadeIn 0.3s ease-out;
}
.pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.4); }
70% { box-shadow: 0 0 0 10px rgba(46, 204, 113, 0); }
100% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0); }
}
.slide-down {
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from { max-height: 0; opacity: 0; }
to { max-height: 1000px; opacity: 1; }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1><i class="fas fa-dumbbell"></i> GymProgress Tracker</h1>
<p class="subtitle">Registra ejercicios, pesos, repeticiones, objetivos y RIR. Visualiza tu progreso.</p>
</header>
<div class="app-controls">
<div class="week-nav">
<button id="prev-week"><i class="fas fa-arrow-left"></i> Semana Anterior</button>
<h2 id="current-week">Semana 1</h2>
<button id="next-week">Pr贸xima Semana <i class="fas fa-arrow-right"></i></button>
</div>
<button id="complete-week" class="secondary pulse"><i class="fas fa-check-circle"></i> Completar Semana</button>
<button id="add-sample" class="warning"><i class="fas fa-plus-circle"></i> Ejemplo R谩pido</button>
<button id="weight-chart-btn" class="purple"><i class="fas fa-weight-scale"></i> Gr谩fico Peso</button>
</div>
<div class="weight-section">
<div class="weight-input">
<label for="body-weight">Peso Corporal (kg):</label>
<input type="number" id="body-weight" min="0" step="0.1" placeholder="Escribe tu peso">
<button id="save-weight">Guardar</button>
</div>
<div class="weight-info">
<p>Registra tu peso al final de cada semana para seguir tu evoluci贸n</p>
</div>
</div>
<div class="week-grid">
<!-- D铆as generados din谩micamente con JS -->
</div>
<div class="feelings-section">
<h3><i class="fas fa-heart"></i> Sensaciones de la Semana</h3>
<textarea id="week-feelings" placeholder="Escribe aqu铆 c贸mo te has sentido esta semana..."></textarea>
</div>
<div class="footer">
<p>GymProgress Tracker - Registra tu progreso en el gimnasio | &copy; 2023</p>
</div>
</div>
<!-- Modal de Gr谩fico -->
<div id="chart-modal">
<div class="chart-container">
<div class="close-btn"><i class="fas fa-times"></i></div>
<div class="modal-header">
<h3 id="chart-title">Progresi贸n de <span id="exercise-name"></span></h3>
</div>
<canvas id="progress-chart"></canvas>
</div>
</div>
<!-- Modal de Base de Datos de Ejercicios -->
<div id="exercise-db-modal" class="exercise-db-modal">
<div class="db-container">
<div class="close-btn" id="close-db-modal"><i class="fas fa-times"></i></div>
<div class="modal-header">
<h3>Base de Datos de Ejercicios</h3>
<p>Selecciona una categor铆a para ver los ejercicios disponibles</p>
</div>
<div class="category-grid" id="category-grid">
<!-- Categor铆as generadas din谩micamente -->
</div>
<div class="exercise-list" id="exercise-list">
<!-- Ejercicios generados din谩micamente -->
</div>
</div>
</div>
<!-- Modal de Gr谩fico de Peso -->
<div id="weight-modal">
<div class="chart-container">
<div class="close-btn" id="close-weight-modal"><i class="fas fa-times"></i></div>
<div class="modal-header">
<h3>Evoluci贸n del Peso Corporal</h3>
</div>
<canvas id="weight-chart"></canvas>
</div>
</div>
<script>
// Configuraci贸n inicial
const APP_STORAGE_KEY = 'gymProgressData';
const DAYS = ['Lunes', 'Martes', 'Mi茅rcoles', 'Jueves', 'Viernes', 'S谩bado', 'Domingo'];
// Base de datos de ejercicios con categor铆as y enlaces de YouTube
const exerciseDatabase = {
categories: ['Pecho', 'Espalda', 'Piernas', 'Hombros', 'Brazos', 'Abdomen', 'Cardio'],
exercises: [
// Pecho
{
name: 'Press Banca',
category: 'Pecho',
videoLink: 'https://www.youtube.com/watch?v=gRVjAtPip0Y',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Press Inclinado',
category: 'Pecho',
videoLink: 'https://www.youtube.com/watch?v=SrqOu55lrYU',
imageUrl: 'https://images.unsplash.com/photo-1593079831268-3381b0db4a77?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Press Declinado',
category: 'Pecho',
videoLink: 'https://www.youtube.com/watch?v=H9B_4G7QwVU',
imageUrl: 'https://images.unsplash.com/photo-1526506118085-60ce8714f8c5?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Aperturas',
category: 'Pecho',
videoLink: 'https://www.youtube.com/watch?v=Z57CtFmRMxA',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 3,
repsTarget: "12-15"
},
{
name: 'Fondos en Paralelas',
category: 'Pecho',
videoLink: 'https://www.youtube.com/watch?v=2z8JmcrW-As',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
// Espalda
{
name: 'Dominadas',
category: 'Espalda',
videoLink: 'https://www.youtube.com/watch?v=eGo4IYlbE5g',
imageUrl: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Remo con Barra',
category: 'Espalda',
videoLink: 'https://www.youtube.com/watch?v=FWJR5Ve8bnQ',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Jal贸n al Pecho',
category: 'Espalda',
videoLink: 'https://www.youtube.com/watch?v=CAwf7n6Luuc',
imageUrl: 'https://images.unsplash.com/photo-1571019614242-c5c5dee9f50b?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Remo en Polea Baja',
category: 'Espalda',
videoLink: 'https://www.youtube.com/watch?v=GZbfZ033f74',
imageUrl: 'https://images.unsplash.com/photo-1550345332-09e3ac987658?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Peso Muerto',
category: 'Espalda',
videoLink: 'https://www.youtube.com/watch?v=r4MzxtBKyNE',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200',
sets: 3,
repsTarget: "5-6"
},
// Piernas
{
name: 'Sentadillas',
category: 'Piernas',
videoLink: 'https://www.youtube.com/watch?v=Dy28eq2PjcI',
imageUrl: 'https://images.unsplash.com/photo-1534258936925-c58bed479fcb?q=80&w=200',
sets: 5,
repsTarget: "5"
},
{
name: 'Prensa de Piernas',
category: 'Piernas',
videoLink: 'https://www.youtube.com/watch?v=IZxyjW7MPJQ',
imageUrl: 'https://images.unsplash.com/photo-1549060279-7e168fce7090?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Zancadas',
category: 'Piernas',
videoLink: 'https://www.youtube.com/watch?v=QOVaHwm-Q6U',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200',
sets: 3,
repsTarget: "12-15"
},
{
name: 'Extensiones de Cu谩driceps',
category: 'Piernas',
videoLink: 'https://www.youtube.com/watch?v=YyvSfVjQeL0',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "12-15"
},
{
name: 'Curl de Femoral',
category: 'Piernas',
videoLink: 'https://www.youtube.com/watch?v=oFxEDkppbSQ',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
// Hombros
{
name: 'Press Militar',
category: 'Hombros',
videoLink: 'https://www.youtube.com/watch?v=QAQ64hK4Xxs',
imageUrl: 'https://images.unsplash.com/photo-1593079831268-3381b0db4a77?q=80&w=200',
sets: 4,
repsTarget: "8-10"
},
{
name: 'Elevaciones Laterales',
category: 'Hombros',
videoLink: 'https://www.youtube.com/watch?v=3VcKaXpzqRo',
imageUrl: 'https://images.unsplash.com/photo-1526506118085-60ce8714f8c5?q=80&w=200',
sets: 4,
repsTarget: "12-15"
},
{
name: 'Face Pull',
category: 'Hombros',
videoLink: 'https://www.youtube.com/watch?v=rep-qVOkqgk',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 4,
repsTarget: "15-20"
},
{
name: 'Encogimientos',
category: 'Hombros',
videoLink: 'https://www.youtube.com/watch?v=GK0QZv6bQeQ',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "12-15"
},
{
name: 'Press Arnold',
category: 'Hombros',
videoLink: 'https://www.youtube.com/watch?v=6Z15_WdXmVw',
imageUrl: 'https://images.unsplash.com/photo-1550345332-09e3ac987658?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
// Brazos
{
name: 'Curl de B铆ceps',
category: 'Brazos',
videoLink: 'https://www.youtube.com/watch?v=ykJmrZ5v0Oo',
imageUrl: 'https://images.unsplash.com/photo-1534258936925-c58bed479fcb?q=80&w=200',
sets: 4,
repsTarget: "8-12"
},
{
name: 'Curl Martillo',
category: 'Brazos',
videoLink: 'https://www.youtube.com/watch?v=TwD-YGVP4Bk',
imageUrl: 'https://images.unsplash.com/photo-1549060279-7e168fce7090?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Fondos para Tr铆ceps',
category: 'Brazos',
videoLink: 'https://www.youtube.com/watch?v=6kALZikXxLc',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Press Franc茅s',
category: 'Brazos',
videoLink: 'https://www.youtube.com/watch?v=_gsUck-7M74',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "10-12"
},
{
name: 'Curl Concentrado',
category: 'Brazos',
videoLink: 'https://www.youtube.com/watch?v=0AUGkch3tzc',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 3,
repsTarget: "10-12"
},
// Abdomen
{
name: 'Plancha',
category: 'Abdomen',
videoLink: 'https://www.youtube.com/watch?v=pSHjTRCQxIw',
imageUrl: 'https://images.unsplash.com/photo-1593079831268-3381b0db4a77?q=80&w=200',
sets: 3,
repsTarget: "30-60s"
},
{
name: 'Crunch',
category: 'Abdomen',
videoLink: 'https://www.youtube.com/watch?v=Xyd_fa5zoEU',
imageUrl: 'https://images.unsplash.com/photo-1526506118085-60ce8714f8c5?q=80&w=200',
sets: 4,
repsTarget: "15-20"
},
{
name: 'Elevaci贸n de Piernas',
category: 'Abdomen',
videoLink: 'https://www.youtube.com/watch?v=JB2oyawG9KI',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 4,
repsTarget: "12-15"
},
{
name: 'Russian Twist',
category: 'Abdomen',
videoLink: 'https://www.youtube.com/watch?v=wkD8rjkodUI',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "15-20"
},
{
name: 'Mountain Climbers',
category: 'Abdomen',
videoLink: 'https://www.youtube.com/watch?v=nmwgirgXLYM',
imageUrl: 'https://images.unsplash.com/photo-1550345332-09e3ac987658?q=80&w=200',
sets: 3,
repsTarget: "30-45s"
},
// Cardio
{
name: 'Burpees',
category: 'Cardio',
videoLink: 'https://www.youtube.com/watch?v=auBLPXO8Fww',
imageUrl: 'https://images.unsplash.com/photo-1534258936925-c58bed479fcb?q=80&w=200',
sets: 4,
repsTarget: "10-15"
},
{
name: 'Saltos de Tijera',
category: 'Cardio',
videoLink: 'https://www.youtube.com/watch?v=UpH7rm0cYbM',
imageUrl: 'https://images.unsplash.com/photo-1549060279-7e168fce7090?q=80&w=200',
sets: 4,
repsTarget: "30-45s"
},
{
name: 'Correr en el Lugar',
category: 'Cardio',
videoLink: 'https://www.youtube.com/watch?v=KQF1o9X5K9g',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200',
sets: 3,
repsTarget: "2-3min"
},
{
name: 'Escaladores',
category: 'Cardio',
videoLink: 'https://www.youtube.com/watch?v=cnyTQDSE884',
imageUrl: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200',
sets: 4,
repsTarget: "30-45s"
},
{
name: 'Skipping Alto',
category: 'Cardio',
videoLink: 'https://www.youtube.com/watch?v=oDdkytliOqE',
imageUrl: 'https://images.unsplash.com/photo-1517344884509-a0c97ec11bcc?q=80&w=200',
sets: 4,
repsTarget: "30-45s"
}
]
};
// Datos iniciales de ejemplo
let appData = {
currentWeek: 1,
weeks: {
1: {
completed: false,
bodyWeight: 75.5,
feelings: "Primera semana de entrenamiento, me siento motivado pero con agujetas.",
days: {
'Lunes': [
{
name: 'Press Banca',
sets: 4,
reps: [8, 8, 8, 8],
weights: [60, 65, 70, 72.5],
rirs: [2, 2, 1, 0],
target: "4x8-10",
repsTarget: "8-10",
videoLink: 'https://www.youtube.com/watch?v=gRVjAtPip0Y',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200'
},
{
name: 'Dominadas',
sets: 4,
reps: [10, 10, 10, 10],
weights: [null, null, null, null],
rirs: [null, null, null, null],
target: "4x10-12",
repsTarget: "10-12",
videoLink: 'https://www.youtube.com/watch?v=eGo4IYlbE5g',
imageUrl: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?q=80&w=200'
}
],
'Martes': [
{
name: 'Sentadillas',
sets: 5,
reps: [5, 5, 5, 5, 5],
weights: [80, 85, 90, 95, 100],
rirs: [2, 2, 1, 1, 0],
target: "5x5",
repsTarget: "5",
videoLink: 'https://www.youtube.com/watch?v=Dy28eq2PjcI',
imageUrl: 'https://images.unsplash.com/photo-1534258936925-c58bed479fcb?q=80&w=200'
}
],
'Mi茅rcoles': [
{
name: 'Press Militar',
sets: 4,
reps: [8, 8, 8, 8],
weights: [30, 32.5, 35, 37.5],
rirs: [2, 2, 1, 0],
target: "4x8-10",
repsTarget: "8-10",
videoLink: 'https://www.youtube.com/watch?v=QAQ64hK4Xxs',
imageUrl: 'https://images.unsplash.com/photo-1593079831268-3381b0db4a77?q=80&w=200'
}
],
'Jueves': [],
'Viernes': [
{
name: 'Peso Muerto',
sets: 3,
reps: [5, 5, 5],
weights: [100, 110, 120],
rirs: [2, 1, 0],
target: "3x5",
repsTarget: "5",
videoLink: 'https://www.youtube.com/watch?v=r4MzxtBKyNE',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200'
}
],
'S谩bado': [],
'Domingo': []
}
}
}
};
// Elementos DOM
const weekGrid = document.querySelector('.week-grid');
const currentWeekElement = document.getElementById('current-week');
const prevWeekBtn = document.getElementById('prev-week');
const nextWeekBtn = document.getElementById('next-week');
const completeWeekBtn = document.getElementById('complete-week');
const chartModal = document.getElementById('chart-modal');
const closeBtn = document.querySelector('.close-btn');
const chartCanvas = document.getElementById('progress-chart');
const exerciseNameElement = document.getElementById('exercise-name');
const addSampleBtn = document.getElementById('add-sample');
const exerciseDbModal = document.getElementById('exercise-db-modal');
const closeDbModalBtn = document.getElementById('close-db-modal');
const categoryGrid = document.getElementById('category-grid');
const exerciseList = document.getElementById('exercise-list');
const bodyWeightInput = document.getElementById('body-weight');
const saveWeightBtn = document.getElementById('save-weight');
const weightChartBtn = document.getElementById('weight-chart-btn');
const weightModal = document.getElementById('weight-modal');
const closeWeightModalBtn = document.getElementById('close-weight-modal');
const weekFeelingsTextarea = document.getElementById('week-feelings');
// Variables globales
let currentDayForExercise = '';
let currentOpenExercise = null;
// Cargar datos guardados
function loadData() {
const savedData = localStorage.getItem(APP_STORAGE_KEY);
if (savedData) {
appData = JSON.parse(savedData);
}
renderWeek();
}
// Guardar datos
function saveData() {
localStorage.setItem(APP_STORAGE_KEY, JSON.stringify(appData));
}
// Renderizar semana actual
function renderWeek() {
const week = appData.weeks[appData.currentWeek];
if (!week) {
// Si la semana no existe, crearla con estructura vac铆a
appData.weeks[appData.currentWeek] = {
completed: false,
bodyWeight: null,
feelings: "",
days: Object.fromEntries(DAYS.map(day => [day, []]))
};
saveData();
}
currentWeekElement.textContent = `Semana ${appData.currentWeek}`;
weekGrid.innerHTML = '';
// Actualizar peso corporal y sensaciones
bodyWeightInput.value = week.bodyWeight || '';
weekFeelingsTextarea.value = week.feelings || '';
DAYS.forEach(day => {
const exercises = appData.weeks[appData.currentWeek].days[day] || [];
const dayCard = document.createElement('div');
dayCard.className = 'day-card';
dayCard.innerHTML = `
<div class="day-header">
<div class="day-title">
<i class="fas fa-calendar-day"></i>${day}
</div>
<button class="add-exercise-btn" data-day="${day}">
<i class="fas fa-plus"></i> Ejercicio
</button>
</div>
`;
if (exercises.length === 0) {
dayCard.innerHTML += `
<div class="empty-state">
<i class="fas fa-dumbbell"></i>
<p>No hay ejercicios registrados para este d铆a</p>
<p>A帽ade tu primer ejercicio usando el bot贸n superior</p>
</div>
`;
} else {
exercises.forEach((exercise, exIndex) => {
const exerciseEl = document.createElement('div');
exerciseEl.className = 'exercise';
// Buscar datos de la semana anterior para referencia
let prevWeekWeights = [];
let prevWeekReps = [];
if (appData.currentWeek > 1) {
const prevWeek = appData.weeks[appData.currentWeek - 1];
if (prevWeek && prevWeek.days[day]) {
const prevExercise = prevWeek.days[day].find(e => e.name === exercise.name);
if (prevExercise) {
prevWeekWeights = [...prevExercise.weights];
prevWeekReps = [...prevExercise.reps];
}
}
}
exerciseEl.innerHTML = `
<div class="exercise-header" data-day="${day}" data-exercise="${exIndex}">
<div>
<div class="exercise-name">
<img src="${exercise.imageUrl || 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?q=80&w=200'}" alt="${exercise.name}" class="exercise-image">
${exercise.name}
</div>
<div class="target-container">
<div class="target-box" data-day="${day}" data-exercise="${exIndex}" data-type="sets">
<i class="fas fa-layer-group"></i> Series: ${exercise.sets}
</div>
<div class="target-box" data-day="${day}" data-exercise="${exIndex}" data-type="reps">
<i class="fas fa-redo"></i> Reps: ${exercise.repsTarget || "?"}
</div>
</div>
</div>
<div class="exercise-controls">
<button class="icon-btn delete" data-day="${day}" data-exercise="${exIndex}">
<i class="fas fa-trash"></i>
</button>
<button class="icon-btn" data-day="${day}" data-exercise="${exIndex}">
<i class="fas fa-chart-line"></i>
</button>
</div>
</div>
<div class="exercise-content">
${exercise.videoLink ? `
<a href="${exercise.videoLink}" target="_blank" class="tech-btn">
<i class="fab fa-youtube"></i> Ver T茅cnica en YouTube
</a>
` : ''}
<div class="sets-container">
${Array.from({ length: exercise.sets }, (_, i) => `
<div class="set">
<div class="set-number">Serie ${i+1}</div>
<div class="set-inputs">
<input type="number"
min="0"
step="0.5"
data-type="weight"
data-day="${day}"
data-exercise="${exIndex}"
data-set="${i}"
value="${exercise.weights[i] !== null ? exercise.weights[i] : ''}"
placeholder="Peso (kg)">
<input type="number"
min="0"
step="1"
data-type="reps"
data-day="${day}"
data-exercise="${exIndex}"
data-set="${i}"
value="${exercise.reps[i] !== null ? exercise.reps[i] : ''}"
placeholder="Reps">
</div>
<input type="number"
min="0"
max="5"
step="1"
class="rir-input"
data-type="rir"
data-day="${day}"
data-exercise="${exIndex}"
data-set="${i}"
value="${exercise.rirs[i] !== null ? exercise.rirs[i] : ''}"
placeholder="RIR">
${prevWeekWeights[i] !== undefined ? `
<div class="prev-data">Sem. ${appData.currentWeek-1}: ${prevWeekWeights[i]} kg x ${prevWeekReps[i]} reps</div>
` : ''}
</div>
`).join('')}
</div>
<div class="rir-info">
<strong>RIR (Repeticiones en Reserva):</strong> Indica cu谩ntas repeticiones te quedaban en reserva.
Ej: RIR 2 = podr铆as haber hecho 2 repeticiones m谩s.
</div>
</div>
`;
dayCard.appendChild(exerciseEl);
});
}
weekGrid.appendChild(dayCard);
});
// Agregar event listeners
document.querySelectorAll('.set input, .rir-input').forEach(input => {
input.addEventListener('change', handleSetChange);
});
document.querySelectorAll('.exercise-header').forEach(header => {
header.addEventListener('click', (e) => {
if (!e.target.closest('.icon-btn')) {
toggleExerciseContent(e.currentTarget);
}
});
});
document.querySelectorAll('.icon-btn:not(.delete)').forEach(btn => {
btn.addEventListener('click', showExerciseChart);
});
document.querySelectorAll('.icon-btn.delete').forEach(btn => {
btn.addEventListener('click', deleteExercise);
});
document.querySelectorAll('.add-exercise-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
currentDayForExercise = e.currentTarget.dataset.day;
showExerciseDatabase();
});
});
document.querySelectorAll('.target-box').forEach(target => {
target.addEventListener('click', (e) => {
e.stopPropagation();
showTargetEditForm(e.currentTarget);
});
});
}
// Mostrar formulario para editar objetivo (series y repeticiones)
function showTargetEditForm(targetDiv) {
// Cerrar cualquier edici贸n abierta primero
document.querySelectorAll('.target-box').forEach(box => {
if (box !== targetDiv) {
const originalContent = box.dataset.originalContent;
if (originalContent) {
box.innerHTML = originalContent;
delete box.dataset.originalContent;
}
}
});
// Guardar contenido original
if (!targetDiv.dataset.originalContent) {
targetDiv.dataset.originalContent = targetDiv.innerHTML;
}
const day = targetDiv.dataset.day;
const exerciseIndex = parseInt(targetDiv.dataset.exercise);
const exercise = appData.weeks[appData.currentWeek].days[day][exerciseIndex];
const isSets = targetDiv.dataset.type === 'sets';
if (isSets) {
// Mostrar selector para series
let optionsHTML = '';
for (let i = 1; i <= 10; i++) {
optionsHTML += `<option value="${i}" ${i === exercise.sets ? 'selected' : ''}>${i}</option>`;
}
targetDiv.innerHTML = `
<div class="target-edit-form-container">
<select class="target-edit-select">
${optionsHTML}
</select>
<div class="target-edit-actions">
<button class="save-target-btn" data-is-sets="true">
<i class="fas fa-check"></i> Guardar
</button>
<button class="cancel-target-btn" type="button">
<i class="fas fa-times"></i> Cancelar
</button>
</div>
</div>
`;
// Manejar guardado espec铆fico para series
targetDiv.querySelector('.save-target-btn').addEventListener('click', function(e) {
e.stopPropagation();
const select = targetDiv.querySelector('.target-edit-select');
const newSets = parseInt(select.value);
if (newSets > 0 && newSets <= 10) {
exercise.sets = newSets;
// Ajustar arrays al nuevo tama帽o
exercise.reps = adjustArrayLength(exercise.reps, newSets);
exercise.weights = adjustArrayLength(exercise.weights, newSets);
exercise.rirs = adjustArrayLength(exercise.rirs, newSets);
exercise.target = `${exercise.sets}x${exercise.repsTarget}`;
saveData();
renderWeek();
}
});
} else {
// Mostrar input para repeticiones
targetDiv.innerHTML = `
<div class="target-edit-form-container">
<input type="text" class="target-edit-input"
value="${exercise.repsTarget}"
placeholder="Ej: 8-12">
<div class="target-edit-actions">
<button class="save-target-btn" data-is-sets="false">
<i class="fas fa-check"></i> Guardar
</button>
<button class="cancel-target-btn" type="button">
<i class="fas fa-times"></i> Cancelar
</button>
</div>
</div>
`;
// Manejar guardado espec铆fico para repeticiones
targetDiv.querySelector('.save-target-btn').addEventListener('click', function(e) {
e.stopPropagation();
const input = targetDiv.querySelector('.target-edit-input');
exercise.repsTarget = input.value.trim();
exercise.target = `${exercise.sets}x${exercise.repsTarget}`;
saveData();
renderWeek();
});
}
// Enfocar el input o select autom谩ticamente
const inputElement = targetDiv.querySelector('.target-edit-select, .target-edit-input');
if (inputElement) {
inputElement.focus();
}
// Manejar cancelaci贸n
targetDiv.querySelector('.cancel-target-btn').addEventListener('click', (e) => {
e.stopPropagation();
targetDiv.innerHTML = targetDiv.dataset.originalContent;
delete targetDiv.dataset.originalContent;
});
// Funci贸n auxiliar para ajustar longitud de arrays
function adjustArrayLength(arr, newLength) {
if (newLength > arr.length) {
return [...arr, ...Array(newLength - arr.length).fill(null)];
} else if (newLength < arr.length) {
return arr.slice(0, newLength);
}
return arr;
}
}
// Alternar contenido del ejercicio (acorde贸n)
function toggleExerciseContent(header) {
const content = header.nextElementSibling;
const exercise = header.parentElement;
// Cerrar ejercicio actualmente abierto
if (currentOpenExercise && currentOpenExercise !== exercise) {
const currentHeader = currentOpenExercise.querySelector('.exercise-header');
const currentContent = currentOpenExercise.querySelector('.exercise-content');
currentHeader.classList.remove('active');
currentContent.style.display = 'none';
}
// Alternar el ejercicio clickeado
if (content.style.display === 'block') {
header.classList.remove('active');
content.style.display = 'none';
currentOpenExercise = null;
} else {
header.classList.add('active');
content.style.display = 'block';
content.classList.add('slide-down');
currentOpenExercise = exercise;
}
}
// Mostrar base de datos de ejercicios
function showExerciseDatabase() {
// Limpiar contenido
categoryGrid.innerHTML = '';
exerciseList.innerHTML = '<div class="empty-state"><i class="fas fa-dumbbell"></i><p>Selecciona una categor铆a para ver los ejercicios</p></div>';
// A帽adir categor铆as
exerciseDatabase.categories.forEach(category => {
const categoryCard = document.createElement('div');
categoryCard.className = 'category-card';
categoryCard.innerHTML = `<i class="fas fa-dumbbell"></i><h4>${category}</h4>`;
categoryCard.addEventListener('click', () => {
showExercisesByCategory(category);
});
categoryGrid.appendChild(categoryCard);
});
// Mostrar modal
exerciseDbModal.style.display = 'flex';
}
// Mostrar ejercicios por categor铆a
function showExercisesByCategory(category) {
exerciseList.innerHTML = '';
const categoryExercises = exerciseDatabase.exercises.filter(
exercise => exercise.category === category
);
if (categoryExercises.length === 0) {
exerciseList.innerHTML = '<div class="empty-state"><i class="fas fa-dumbbell"></i><p>No hay ejercicios en esta categor铆a</p></div>';
return;
}
categoryExercises.forEach(exercise => {
const exerciseItem = document.createElement('div');
exerciseItem.className = 'exercise-item';
exerciseItem.innerHTML = `
<div>
<strong>${exercise.name}</strong>
<div>${exercise.category}</div>
<div class="target-container">
<div class="target-box">
<i class="fas fa-layer-group"></i> Series: ${exercise.sets}
</div>
<div class="target-box">
<i class="fas fa-redo"></i> Reps: ${exercise.repsTarget}
</div>
</div>
</div>
<button class="secondary add-exercise-from-db"
data-name="${exercise.name}"
data-video="${exercise.videoLink}"
data-image="${exercise.imageUrl}"
data-sets="${exercise.sets}"
data-repstarget="${exercise.repsTarget}">
<i class="fas fa-plus"></i> A帽adir
</button>
`;
exerciseList.appendChild(exerciseItem);
});
// Event listeners para a帽adir ejercicios
document.querySelectorAll('.add-exercise-from-db').forEach(btn => {
btn.addEventListener('click', (e) => {
const name = e.currentTarget.dataset.name;
const videoLink = e.currentTarget.dataset.video;
const imageUrl = e.currentTarget.dataset.image;
const sets = parseInt(e.currentTarget.dataset.sets);
const repsTarget = e.currentTarget.dataset.repstarget;
addExerciseFromDatabase(name, videoLink, imageUrl, sets, repsTarget);
});
});
}
// A帽adir ejercicio desde la base de datos
function addExerciseFromDatabase(name, videoLink, imageUrl) {
// Pedir al usuario que ingrese series y repeticiones
const sets = prompt("驴Cu谩ntas series quieres hacer para este ejercicio?");
const repsTarget = prompt("驴Cu谩l es tu objetivo de repeticiones por serie? (Ej: 8-12)");
if (!sets || !repsTarget) return;
const numSets = parseInt(sets);
// Crear nuevo ejercicio
const newExercise = {
name: name,
sets: numSets,
reps: Array(numSets).fill(null),
weights: Array(numSets).fill(null),
rirs: Array(numSets).fill(null),
target: `${numSets}x${repsTarget}`,
repsTarget: repsTarget,
videoLink: videoLink,
imageUrl: imageUrl
};
// A帽adir a los datos
appData.weeks[appData.currentWeek].days[currentDayForExercise].push(newExercise);
saveData();
// Cerrar modal y renderizar semana
exerciseDbModal.style.display = 'none';
renderWeek();
}
// Manejar cambios en series (peso, repeticiones o RIR)
function handleSetChange(e) {
const input = e.target;
const day = input.dataset.day;
const exerciseIndex = parseInt(input.dataset.exercise);
const setIndex = parseInt(input.dataset.set);
const type = input.dataset.type;
const value = input.value === '' ? null : parseFloat(input.value);
// Actualizar datos
if (type === 'weight') {
appData.weeks[appData.currentWeek].days[day][exerciseIndex].weights[setIndex] = value;
} else if (type === 'reps') {
appData.weeks[appData.currentWeek].days[day][exerciseIndex].reps[setIndex] = value;
} else if (type === 'rir') {
appData.weeks[appData.currentWeek].days[day][exerciseIndex].rirs[setIndex] = value;
}
saveData();
}
// Guardar peso corporal
function saveBodyWeight() {
const weightValue = bodyWeightInput.value;
appData.weeks[appData.currentWeek].bodyWeight = weightValue ? parseFloat(weightValue) : null;
saveData();
// Mostrar feedback
if (weightValue) {
alert(`Peso guardado: ${weightValue} kg`);
} else {
alert("Peso borrado correctamente");
}
}
// Guardar sensaciones
function saveFeelings() {
appData.weeks[appData.currentWeek].feelings = weekFeelingsTextarea.value;
saveData();
}
// Mostrar gr谩fico de evoluci贸n del peso
function showWeightChart() {
// Recopilar datos de peso de todas las semanas
const weightData = [];
for (let weekNum = 1; weekNum <= appData.currentWeek; weekNum++) {
const week = appData.weeks[weekNum];
if (week && week.bodyWeight !== null) {
weightData.push({
week: weekNum,
weight: week.bodyWeight
});
}
}
if (weightData.length > 0) {
// Mostrar modal
weightModal.style.display = 'flex';
const ctx = document.getElementById('weight-chart').getContext('2d');
// Destruir gr谩fico anterior si existe
if (window.weightChart) {
window.weightChart.destroy();
}
window.weightChart = new Chart(ctx, {
type: 'line',
data: {
labels: weightData.map(d => `Semana ${d.week}`),
datasets: [{
label: 'Peso Corporal (kg)',
data: weightData.map(d => d.weight),
borderColor: '#3498db',
backgroundColor: 'rgba(52, 152, 219, 0.1)',
borderWidth: 4,
pointBackgroundColor: '#9b59b6',
pointRadius: 6,
pointHoverRadius: 8,
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Evoluci贸n del Peso Corporal',
font: {
size: 18
}
},
legend: {
position: 'top',
}
},
scales: {
y: {
beginAtZero: false,
title: {
display: true,
text: 'Peso (kg)'
}
},
x: {
title: {
display: true,
text: 'Semanas'
}
}
}
}
});
} else {
alert('No hay datos de peso registrados para mostrar.');
}
}
// Completar semana actual
function completeCurrentWeek() {
appData.weeks[appData.currentWeek].completed = true;
// Crear nueva semana con la misma estructura
const nextWeekNum = appData.currentWeek + 1;
appData.weeks[nextWeekNum] = JSON.parse(JSON.stringify(appData.weeks[appData.currentWeek]));
// Resetear pesos y repeticiones para la nueva semana
Object.values(appData.weeks[nextWeekNum].days).forEach(exercises => {
exercises.forEach(exercise => {
exercise.weights = exercise.weights.map(() => null);
exercise.reps = exercise.reps.map(() => null);
exercise.rirs = exercise.rirs.map(() => null);
});
});
// Resetear peso corporal y sensaciones
appData.weeks[nextWeekNum].bodyWeight = null;
appData.weeks[nextWeekNum].feelings = "";
appData.currentWeek = nextWeekNum;
saveData();
renderWeek();
// Mostrar notificaci贸n
alert(`隆Semana completada! Se ha creado la semana ${nextWeekNum} con la misma estructura.`);
}
// Mostrar gr谩fico de progresi贸n
function showExerciseChart(e) {
e.stopPropagation();
const btn = e.currentTarget;
const day = btn.dataset.day;
const exerciseIndex = parseInt(btn.dataset.exercise);
const exerciseName = appData.weeks[appData.currentWeek].days[day][exerciseIndex].name;
// Obtener datos hist贸ricos
const historicalData = [];
for (let weekNum = 1; weekNum <= appData.currentWeek; weekNum++) {
const week = appData.weeks[weekNum];
if (week && week.days[day]) {
const exercise = week.days[day].find(e => e.name === exerciseName);
if (exercise) {
// Calcular mejor peso de la sesi贸n
const bestWeight = exercise.weights.reduce((max, weight) => {
return weight !== null && weight > max ? weight : max;
}, 0);
if (bestWeight > 0) {
historicalData.push({
week: weekNum,
weight: bestWeight
});
}
}
}
}
if (historicalData.length > 0) {
// Mostrar gr谩fico
exerciseNameElement.textContent = exerciseName;
chartModal.style.display = 'flex';
// Destruir gr谩fico anterior si existe
if (window.progressChart) {
window.progressChart.destroy();
}
const ctx = chartCanvas.getContext('2d');
window.progressChart = new Chart(ctx, {
type: 'line',
data: {
labels: historicalData.map(d => `Semana ${d.week}`),
datasets: [{
label: `Peso m谩ximo (kg) - ${exerciseName}`,
data: historicalData.map(d => d.weight),
borderColor: '#2ecc71',
backgroundColor: 'rgba(46, 204, 113, 0.1)',
borderWidth: 4,
pointBackgroundColor: '#3498db',
pointRadius: 6,
pointHoverRadius: 8,
tension: 0.3,
fill: true
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Progresi贸n de Pesos',
font: {
size: 18
}
},
legend: {
position: 'top',
},
tooltip: {
mode: 'index',
intersect: false,
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Peso (kg)',
font: {
size: 14,
weight: 'bold'
}
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
},
x: {
title: {
display: true,
text: 'Semanas',
font: {
size: 14,
weight: 'bold'
}
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
},
animation: {
duration: 2000,
easing: 'easeOutQuart'
}
}
});
} else {
alert(`No hay datos hist贸ricos para mostrar la progresi贸n de ${exerciseName}`);
}
}
// Eliminar ejercicio
function deleteExercise(e) {
e.stopPropagation();
const btn = e.currentTarget;
const day = btn.dataset.day;
const exerciseIndex = parseInt(btn.dataset.exercise);
if (confirm('驴Est谩s seguro de que quieres eliminar este ejercicio?')) {
appData.weeks[appData.currentWeek].days[day].splice(exerciseIndex, 1);
saveData();
renderWeek();
}
}
// A帽adir datos de ejemplo
function addSampleData() {
appData = {
currentWeek: 1,
weeks: {
1: {
completed: false,
bodyWeight: 75.5,
feelings: "Primera semana de entrenamiento, me siento motivado pero con agujetas.",
days: {
'Lunes': [
{
name: 'Press Banca',
sets: 4,
reps: [8, 8, 8, 8],
weights: [60, 65, 70, 72.5],
rirs: [2, 2, 1, 0],
target: "4x8-10",
repsTarget: "8-10",
videoLink: 'https://www.youtube.com/watch?v=gRVjAtPip0Y',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200'
},
{
name: 'Dominadas',
sets: 4,
reps: [10, 10, 10, 10],
weights: [null, null, null, null],
rirs: [null, null, null, null],
target: "4x10-12",
repsTarget: "10-12",
videoLink: 'https://www.youtube.com/watch?v=eGo4IYlbE5g',
imageUrl: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?q=80&w=200'
}
],
'Martes': [
{
name: 'Sentadillas',
sets: 5,
reps: [5, 5, 5, 5, 5],
weights: [80, 85, 90, 95, 100],
rirs: [2, 2, 1, 1, 0],
target: "5x5",
repsTarget: "5",
videoLink: 'https://www.youtube.com/watch?v=Dy28eq2PjcI',
imageUrl: 'https://images.unsplash.com/photo-1534258936925-c58bed479fcb?q=80&w=200'
}
],
'Mi茅rcoles': [
{
name: 'Press Militar',
sets: 4,
reps: [8, 8, 8, 8],
weights: [30, 32.5, 35, 37.5],
rirs: [2, 2, 1, 0],
target: "4x8-10",
repsTarget: "8-10",
videoLink: 'https://www.youtube.com/watch?v=QAQ64hK4Xxs',
imageUrl: 'https://images.unsplash.com/photo-1593079831268-3381b0db4a77?q=80&w=200'
}
],
'Jueves': [],
'Viernes': [
{
name: 'Peso Muerto',
sets: 3,
reps: [5, 5, 5],
weights: [100, 110, 120],
rirs: [2, 1, 0],
target: "3x5",
repsTarget: "5",
videoLink: 'https://www.youtube.com/watch?v=r4MzxtBKyNE',
imageUrl: 'https://images.unsplash.com/photo-1534367507877-0edd93bd013b?q=80&w=200'
}
],
'S谩bado': [],
'Domingo': []
}
}
}
};
saveData();
renderWeek();
}
// Event Listeners
completeWeekBtn.addEventListener('click', completeCurrentWeek);
prevWeekBtn.addEventListener('click', () => {
if (appData.currentWeek > 1) {
appData.currentWeek--;
saveData();
renderWeek();
}
});
nextWeekBtn.addEventListener('click', () => {
if (appData.weeks[appData.currentWeek + 1]) {
appData.currentWeek++;
saveData();
renderWeek();
}
});
closeBtn.addEventListener('click', () => {
chartModal.style.display = 'none';
});
closeDbModalBtn.addEventListener('click', () => {
exerciseDbModal.style.display = 'none';
});
closeWeightModalBtn.addEventListener('click', () => {
weightModal.style.display = 'none';
});
addSampleBtn.addEventListener('click', addSampleData);
saveWeightBtn.addEventListener('click', saveBodyWeight);
weightChartBtn.addEventListener('click', showWeightChart);
weekFeelingsTextarea.addEventListener('change', saveFeelings);
// Mejor delegaci贸n de eventos para edici贸n de objetivos
document.addEventListener('click', function(e) {
const targetBox = e.target.closest('.target-box');
if (targetBox && !targetBox.querySelector('.target-edit-form-container')) {
e.stopPropagation();
showTargetEditForm(targetBox);
}
});
// Cerrar modales al hacer clic fuera del contenido
chartModal.addEventListener('click', (e) => {
if (e.target === chartModal) {
chartModal.style.display = 'none';
}
});
exerciseDbModal.addEventListener('click', (e) => {
if (e.target === exerciseDbModal) {
exerciseDbModal.style.display = 'none';
}
});
weightModal.addEventListener('click', (e) => {
if (e.target === weightModal) {
weightModal.style.display = 'none';
}
});
// Inicializar aplicaci贸n
loadData();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 馃К <a href="https://enzostvs-deepsite.hf.space?remix=Elkusta/gym-gus" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>