LogicalTrue commited on
Commit
3fe34ab
·
1 Parent(s): 55c3b9c

feat: add mobile profile and privacy configuration modals

Browse files
Files changed (1) hide show
  1. frontend/viewer.html +213 -0
frontend/viewer.html CHANGED
@@ -276,6 +276,13 @@
276
  <div style="padding: 6px 10px; margin-bottom: 6px; font-size: 0.8rem; font-weight: 700; color: #ffeb3b; display: flex; align-items: center; gap: 6px; border-bottom: 1px solid rgba(255,255,255,0.08);">
277
  🪙 <span id="mobCredits">0</span> Créditos
278
  </div>
 
 
 
 
 
 
 
279
  <button id="btnMobLogout" style="width: 100%; text-align: left; background: transparent; border: none; color: #ef4444; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px;">
280
  🚪 <span>Salir</span>
281
  </button>
@@ -494,6 +501,89 @@
494
  </div>
495
  </div>
496
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  <script>
498
  const urlParams = new URLSearchParams(window.location.search);
499
  const modelUrl = urlParams.get('model');
@@ -1451,6 +1541,129 @@
1451
  });
1452
  }
1453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1454
  // Logout handler
1455
  const btnMobLogout = document.getElementById('btnMobLogout');
1456
  if (btnMobLogout) {
 
276
  <div style="padding: 6px 10px; margin-bottom: 6px; font-size: 0.8rem; font-weight: 700; color: #ffeb3b; display: flex; align-items: center; gap: 6px; border-bottom: 1px solid rgba(255,255,255,0.08);">
277
  🪙 <span id="mobCredits">0</span> Créditos
278
  </div>
279
+ <button id="btnMobProfile" style="width: 100%; text-align: left; background: transparent; border: none; color: #ffffff; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px;">
280
+ 👤 <span>Mi perfil</span>
281
+ </button>
282
+ <button id="btnMobPrivacy" style="width: 100%; text-align: left; background: transparent; border: none; color: #4caf50; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px;">
283
+ 🛡️ <span>Privacidad y Seguridad</span>
284
+ </button>
285
+ <div style="height: 1px; background: rgba(255,255,255,0.08); margin: 4px 0;"></div>
286
  <button id="btnMobLogout" style="width: 100%; text-align: left; background: transparent; border: none; color: #ef4444; padding: 8px 10px; border-radius: 8px; font-size: 0.8rem; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px;">
287
  🚪 <span>Salir</span>
288
  </button>
 
501
  </div>
502
  </div>
503
 
504
+ <!-- User Profile Modal (Mobile) -->
505
+ <div id="mobProfileModal" style="display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: rgba(10, 10, 15, 0.95); backdrop-filter: blur(12px); flex-direction: column; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box;">
506
+ <div style="width: 100%; max-width: 420px; background: #161622; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; padding: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.8);">
507
+ <div style="display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;">
508
+ <div style="font-size: 1.1rem; font-weight: 700; color: #fff; display: flex; align-items: center; gap: 6px;">
509
+ 👤 <span>Mi Perfil</span>
510
+ </div>
511
+ <button type="button" id="btnCloseMobProfile" style="background: rgba(255,255,255,0.1); border: none; color: #fff; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-weight: bold; font-size: 0.9rem;">✕</button>
512
+ </div>
513
+ <form id="mobProfileForm" style="display: flex; flex-direction: column; gap: 14px;">
514
+ <div style="display: flex; align-items: center; gap: 12px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.1); padding: 12px; border-radius: 12px;">
515
+ <div style="position: relative; width: 56px; height: 56px; border-radius: 50%; overflow: hidden; border: 2px solid #7c3aed; flex-shrink: 0; background: #000;">
516
+ <img id="mobProfileAvatarPreview" src="" alt="Avatar" style="width: 100%; height: 100%; object-fit: cover;">
517
+ </div>
518
+ <div style="flex: 1; display: flex; flex-direction: column; gap: 4px;">
519
+ <label for="mobProfileAvatarInput" style="font-size: 0.75rem; padding: 6px 12px; background: rgba(124, 58, 237, 0.2); border: 1px solid #7c3aed; color: #a78bfa; border-radius: 8px; font-weight: 600; cursor: pointer; display: inline-block; width: fit-content;">
520
+ 📷 Cambiar foto
521
+ </label>
522
+ <input type="file" id="mobProfileAvatarInput" accept="image/*" style="display: none;">
523
+ <span style="font-size: 0.7rem; color: #a1a1aa;">Formato JPG o PNG</span>
524
+ </div>
525
+ </div>
526
+
527
+ <div>
528
+ <label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Apodo / Nickname</label>
529
+ <input type="text" id="mobProfileNickInput" required style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
530
+ </div>
531
+
532
+ <div>
533
+ <label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Nombre Completo (Opcional)</label>
534
+ <input type="text" id="mobProfileFullNameInput" style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
535
+ </div>
536
+
537
+ <div style="display: flex; gap: 8px; justify-content: flex-end; margin-top: 6px;">
538
+ <button type="button" id="btnCancelMobProfile" style="background: rgba(255,255,255,0.1); border: none; color: #fff; padding: 10px 16px; border-radius: 8px; font-weight: 600; font-size: 0.85rem; cursor: pointer;">Cancelar</button>
539
+ <button type="submit" style="background: #7c3aed; border: none; color: #fff; padding: 10px 18px; border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer;">Guardar</button>
540
+ </div>
541
+ </form>
542
+ </div>
543
+ </div>
544
+
545
+ <!-- Privacy & Security Modal (Mobile) -->
546
+ <div id="mobPrivacyModal" style="display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: rgba(10, 10, 15, 0.95); backdrop-filter: blur(12px); flex-direction: column; align-items: center; justify-content: center; padding: 16px; box-sizing: border-box;">
547
+ <div style="width: 100%; max-width: 420px; background: #161622; border: 1px solid rgba(255,255,255,0.12); border-radius: 16px; padding: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.8);">
548
+ <div style="display: flex; justify-content: space-between; align-items: center; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;">
549
+ <div style="font-size: 1.1rem; font-weight: 700; color: #4caf50; display: flex; align-items: center; gap: 6px;">
550
+ 🛡️ <span>Privacidad y Seguridad</span>
551
+ </div>
552
+ <button type="button" id="btnCloseMobPrivacy" style="background: rgba(255,255,255,0.1); border: none; color: #fff; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-weight: bold; font-size: 0.9rem;">✕</button>
553
+ </div>
554
+ <form id="mobPrivacyForm" style="display: flex; flex-direction: column; gap: 12px;">
555
+ <div>
556
+ <label style="font-size: 0.8rem; color: #a1a1aa; display: block; margin-bottom: 6px; font-weight: 600;">Correo Electrónico</label>
557
+ <input type="email" id="mobPrivacyEmailInput" style="width: 100%; box-sizing: border-box; padding: 10px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.9rem;">
558
+ </div>
559
+
560
+ <div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: 10px; margin-top: 4px;">
561
+ <div style="font-size: 0.85rem; font-weight: 700; color: #fff; margin-bottom: 8px;">Cambiar Contraseña</div>
562
+
563
+ <div style="margin-bottom: 8px;">
564
+ <label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Contraseña Actual</label>
565
+ <input type="password" id="mobPrivacyCurrentPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
566
+ </div>
567
+
568
+ <div style="margin-bottom: 8px;">
569
+ <label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Nueva Contraseña</label>
570
+ <input type="password" id="mobPrivacyNewPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
571
+ </div>
572
+
573
+ <div>
574
+ <label style="font-size: 0.75rem; color: #a1a1aa; display: block; margin-bottom: 4px;">Repetir Nueva Contraseña</label>
575
+ <input type="password" id="mobPrivacyConfirmPasswordInput" style="width: 100%; box-sizing: border-box; padding: 8px 12px; background: #1e1e24; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; color: #fff; outline: none; font-size: 0.85rem;">
576
+ </div>
577
+ </div>
578
+
579
+ <div style="display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px;">
580
+ <button type="button" id="btnCancelMobPrivacy" style="background: rgba(255,255,255,0.1); border: none; color: #fff; padding: 10px 16px; border-radius: 8px; font-weight: 600; font-size: 0.85rem; cursor: pointer;">Cancelar</button>
581
+ <button type="submit" style="background: #4caf50; border: none; color: #fff; padding: 10px 18px; border-radius: 8px; font-weight: 700; font-size: 0.85rem; cursor: pointer;">Guardar</button>
582
+ </div>
583
+ </form>
584
+ </div>
585
+ </div>
586
+
587
  <script>
588
  const urlParams = new URLSearchParams(window.location.search);
589
  const modelUrl = urlParams.get('model');
 
1541
  });
1542
  }
1543
 
1544
+ // Mobile Profile and Privacy Modals Bindings
1545
+ const btnMobProfile = document.getElementById('btnMobProfile');
1546
+ const btnMobPrivacy = document.getElementById('btnMobPrivacy');
1547
+ const mobProfileModal = document.getElementById('mobProfileModal');
1548
+ const mobPrivacyModal = document.getElementById('mobPrivacyModal');
1549
+ const btnCloseMobProfile = document.getElementById('btnCloseMobProfile');
1550
+ const btnCancelMobProfile = document.getElementById('btnCancelMobProfile');
1551
+ const btnCloseMobPrivacy = document.getElementById('btnCloseMobPrivacy');
1552
+ const btnCancelMobPrivacy = document.getElementById('btnCancelMobPrivacy');
1553
+ const mobProfileForm = document.getElementById('mobProfileForm');
1554
+ const mobPrivacyForm = document.getElementById('mobPrivacyForm');
1555
+ const mobProfileAvatarInput = document.getElementById('mobProfileAvatarInput');
1556
+ const mobProfileAvatarPreview = document.getElementById('mobProfileAvatarPreview');
1557
+
1558
+ if (btnMobProfile) {
1559
+ btnMobProfile.addEventListener('click', (e) => {
1560
+ e.stopPropagation();
1561
+ if (mobUserDropdown) mobUserDropdown.style.display = 'none';
1562
+ fetch('/api/me').then(res => res.json()).then(data => {
1563
+ const nickInput = document.getElementById('mobProfileNickInput');
1564
+ const fullNameInput = document.getElementById('mobProfileFullNameInput');
1565
+ if (nickInput) nickInput.value = data.nick || data.username || '';
1566
+ if (fullNameInput) fullNameInput.value = data.full_name || '';
1567
+ if (mobProfileAvatarPreview && data.avatar) mobProfileAvatarPreview.src = data.avatar;
1568
+ if (mobProfileModal) mobProfileModal.style.display = 'flex';
1569
+ }).catch(() => {
1570
+ if (mobProfileModal) mobProfileModal.style.display = 'flex';
1571
+ });
1572
+ });
1573
+ }
1574
+
1575
+ if (btnMobPrivacy) {
1576
+ btnMobPrivacy.addEventListener('click', (e) => {
1577
+ e.stopPropagation();
1578
+ if (mobUserDropdown) mobUserDropdown.style.display = 'none';
1579
+ fetch('/api/me').then(res => res.json()).then(data => {
1580
+ const emailInput = document.getElementById('mobPrivacyEmailInput');
1581
+ if (emailInput) emailInput.value = data.email || '';
1582
+ if (mobPrivacyModal) mobPrivacyModal.style.display = 'flex';
1583
+ }).catch(() => {
1584
+ if (mobPrivacyModal) mobPrivacyModal.style.display = 'flex';
1585
+ });
1586
+ });
1587
+ }
1588
+
1589
+ if (mobProfileAvatarInput && mobProfileAvatarPreview) {
1590
+ mobProfileAvatarInput.addEventListener('change', (e) => {
1591
+ const file = e.target.files[0];
1592
+ if (file) {
1593
+ const reader = new FileReader();
1594
+ reader.onload = (evt) => {
1595
+ mobProfileAvatarPreview.src = evt.target.result;
1596
+ };
1597
+ reader.readAsDataURL(file);
1598
+ }
1599
+ });
1600
+ }
1601
+
1602
+ [btnCloseMobProfile, btnCancelMobProfile].forEach(btn => {
1603
+ if (btn) btn.addEventListener('click', () => mobProfileModal.style.display = 'none');
1604
+ });
1605
+
1606
+ [btnCloseMobPrivacy, btnCancelMobPrivacy].forEach(btn => {
1607
+ if (btn) btn.addEventListener('click', () => mobPrivacyModal.style.display = 'none');
1608
+ });
1609
+
1610
+ if (mobProfileForm) {
1611
+ mobProfileForm.addEventListener('submit', async (e) => {
1612
+ e.preventDefault();
1613
+ const nick = document.getElementById('mobProfileNickInput')?.value.trim();
1614
+ const full_name = document.getElementById('mobProfileFullNameInput')?.value.trim();
1615
+ const avatar = mobProfileAvatarPreview?.src;
1616
+ try {
1617
+ const res = await fetch('/api/update-profile', {
1618
+ method: 'POST',
1619
+ headers: { 'Content-Type': 'application/json' },
1620
+ body: JSON.stringify({ nick, full_name, avatar })
1621
+ });
1622
+ if (res.ok) {
1623
+ alert('¡Perfil actualizado con éxito!');
1624
+ if (mobProfileModal) mobProfileModal.style.display = 'none';
1625
+ fetchUserInfo();
1626
+ } else {
1627
+ alert('Error al actualizar el perfil.');
1628
+ }
1629
+ } catch (err) {
1630
+ alert('Error de conexión al actualizar perfil.');
1631
+ }
1632
+ });
1633
+ }
1634
+
1635
+ if (mobPrivacyForm) {
1636
+ mobPrivacyForm.addEventListener('submit', async (e) => {
1637
+ e.preventDefault();
1638
+ const email = document.getElementById('mobPrivacyEmailInput')?.value.trim();
1639
+ const current_password = document.getElementById('mobPrivacyCurrentPasswordInput')?.value;
1640
+ const new_password = document.getElementById('mobPrivacyNewPasswordInput')?.value;
1641
+ const confirm_password = document.getElementById('mobPrivacyConfirmPasswordInput')?.value;
1642
+
1643
+ if (new_password && new_password !== confirm_password) {
1644
+ alert('Las contraseñas no coinciden.');
1645
+ return;
1646
+ }
1647
+
1648
+ try {
1649
+ const res = await fetch('/api/update-privacy', {
1650
+ method: 'POST',
1651
+ headers: { 'Content-Type': 'application/json' },
1652
+ body: JSON.stringify({ email, current_password, new_password })
1653
+ });
1654
+ const data = await res.json();
1655
+ if (res.ok && data.success) {
1656
+ alert('¡Configuración de privacidad guardada!');
1657
+ if (mobPrivacyModal) mobPrivacyModal.style.display = 'none';
1658
+ } else {
1659
+ alert(data.error || 'Error al guardar privacidad.');
1660
+ }
1661
+ } catch (err) {
1662
+ alert('Error de conexión al guardar privacidad.');
1663
+ }
1664
+ });
1665
+ }
1666
+
1667
  // Logout handler
1668
  const btnMobLogout = document.getElementById('btnMobLogout');
1669
  if (btnMobLogout) {