@davidbc01/telar 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/dist/carrito.html +0 -31
- package/dist/entrar.html +0 -36
- package/dist/index.html +0 -42
- package/dist/producto-(id).html +0 -38
- package/dist/telar.css +0 -118
- package/dist/telar.js +0 -251
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ Telar compila a HTML + CSS + JavaScript optimizados. El desarrollador nunca toca
|
|
|
110
110
|
| CLI — compilar, servir, verificar | ✅ Completo |
|
|
111
111
|
| Publicado en npm | ✅ Completo |
|
|
112
112
|
| Live reload en telar servir | ✅ Completo |
|
|
113
|
-
| Extensión VS Code |
|
|
113
|
+
| Extensión VS Code | ✅ Completo |
|
|
114
114
|
| Lanzamiento público | 🟪 Pendiente |
|
|
115
115
|
|
|
116
116
|
## Hoja de ruta
|
|
@@ -130,7 +130,7 @@ Telar compila a HTML + CSS + JavaScript optimizados. El desarrollador nunca toca
|
|
|
130
130
|
|
|
131
131
|
### v0.3 - Experiencia de desarrollo
|
|
132
132
|
- [x] Live reload en `telar servir`
|
|
133
|
-
- [
|
|
133
|
+
- [x] Extensión para VS Code
|
|
134
134
|
- [ ] Mensajes de error mejorados con contexto visual
|
|
135
135
|
|
|
136
136
|
### v1.0 - Lanzamiento público
|
package/package.json
CHANGED
package/dist/carrito.html
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="es">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Tu carrito</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<link rel="stylesheet" href="telar.css">
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<main role="main">
|
|
14
|
-
<h1>Tu carrito</h1>
|
|
15
|
-
<div data-si="hay-resultados">
|
|
16
|
-
|
|
17
|
-
</div>
|
|
18
|
-
<p class="campo" data-campo="pedido.productos">{pedido.productos}</p>
|
|
19
|
-
<p class="campo" data-campo="Total: (pedido.total) €">{Total: (pedido.total) €}</p>
|
|
20
|
-
<a href="/pago" class="boton" role="button">Finalizar compra</a>
|
|
21
|
-
<div data-si="hay-resultados">
|
|
22
|
-
|
|
23
|
-
</div>
|
|
24
|
-
<section class="lista" data-modelo="Tu carrito está vacío">
|
|
25
|
-
<div class="cargando" aria-live="polite">Cargando...</div>
|
|
26
|
-
</section>
|
|
27
|
-
<a href="/inicio" class="boton" role="button">Ver productos</a>
|
|
28
|
-
</main>
|
|
29
|
-
<script src="telar.js" defer></script>
|
|
30
|
-
</body>
|
|
31
|
-
</html>
|
package/dist/entrar.html
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="es">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Entrar</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<link rel="stylesheet" href="telar.css">
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<main role="main">
|
|
14
|
-
<h1>Entrar</h1>
|
|
15
|
-
<div class="campo-grupo">
|
|
16
|
-
<label for="correo-electrnico">Correo electrónico</label>
|
|
17
|
-
<input type="email" id="correo-electrnico" name="correo-electrnico" autocomplete="email">
|
|
18
|
-
</div>
|
|
19
|
-
<div class="campo-grupo">
|
|
20
|
-
<label for="contrasea">Contraseña</label>
|
|
21
|
-
<input type="contraseña" id="contrasea" name="contrasea" >
|
|
22
|
-
</div>
|
|
23
|
-
<button class="boton" data-accion="iniciarSesion" type="button">
|
|
24
|
-
Entrar
|
|
25
|
-
</button>
|
|
26
|
-
<div class="error" role="alert" hidden>
|
|
27
|
-
|
|
28
|
-
</div>
|
|
29
|
-
<section class="lista" data-modelo="Correo o contraseña incorrectos">
|
|
30
|
-
<div class="cargando" aria-live="polite">Cargando...</div>
|
|
31
|
-
</section>
|
|
32
|
-
<a href="/recuperarContraseña" class="boton" role="button">¿Olvidaste tu contraseña?</a>
|
|
33
|
-
</main>
|
|
34
|
-
<script src="telar.js" defer></script>
|
|
35
|
-
</body>
|
|
36
|
-
</html>
|
package/dist/index.html
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="es">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Bienvenido a MiTienda</title>
|
|
7
|
-
<meta name="description" content="Los mejores productos al mejor precio">
|
|
8
|
-
<meta http-equiv="Cache-Control" content="max-age=600">
|
|
9
|
-
<meta name="mobile-web-app-capable" content="yes">
|
|
10
|
-
<link rel="stylesheet" href="telar.css">
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<main role="main">
|
|
14
|
-
<h1>Bienvenido a MiTienda</h1>
|
|
15
|
-
<p class="descripcion">Los mejores productos al mejor precio</p>
|
|
16
|
-
<section class="lista" data-modelo="Producto" data-maximo="8" data-ordenar="fecha" data-recientes="true">
|
|
17
|
-
<div class="cargando" aria-live="polite">Cargando...</div>
|
|
18
|
-
<div class="error" role="alert" hidden>
|
|
19
|
-
|
|
20
|
-
</div>
|
|
21
|
-
</section>
|
|
22
|
-
<p class="campo" data-campo="No se pudieron cargar los productos.">{No se pudieron cargar los productos.}</p>
|
|
23
|
-
<button class="reintentar" data-reintentar="10" type="button">
|
|
24
|
-
Reintentar
|
|
25
|
-
</button>
|
|
26
|
-
<div data-si="usuario-conectado">
|
|
27
|
-
|
|
28
|
-
</div>
|
|
29
|
-
<p class="campo" data-campo="Hola, (usuario.nombre)">{Hola, (usuario.nombre)}</p>
|
|
30
|
-
<a href="/cuenta" class="boton" role="button">Mi cuenta</a>
|
|
31
|
-
<a href="/carrito" class="boton" role="button">Mi carrito</a>
|
|
32
|
-
<div data-si="hay-resultados">
|
|
33
|
-
|
|
34
|
-
</div>
|
|
35
|
-
<a href="/login" class="boton" role="button">Entrar</a>
|
|
36
|
-
<a href="/registro" class="boton" role="button">Registrarse</a>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</main>
|
|
40
|
-
<script src="telar.js" defer></script>
|
|
41
|
-
</body>
|
|
42
|
-
</html>
|
package/dist/producto-(id).html
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="es">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>(producto.nombre)</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<meta name="mobile-web-app-capable" content="yes">
|
|
10
|
-
<link rel="stylesheet" href="telar.css">
|
|
11
|
-
</head>
|
|
12
|
-
<body>
|
|
13
|
-
<main role="main">
|
|
14
|
-
<h1>(producto.nombre)</h1>
|
|
15
|
-
<p class="campo" data-campo="producto.imagen">{producto.imagen}</p>
|
|
16
|
-
<p class="campo" data-campo="producto.nombre">{producto.nombre}</p>
|
|
17
|
-
<p class="campo" data-campo="producto.precio">{producto.precio}</p>
|
|
18
|
-
<p class="campo" data-campo="producto.descripción">{producto.descripción}</p>
|
|
19
|
-
<div data-si="producto.stock-mayor-0">
|
|
20
|
-
|
|
21
|
-
</div>
|
|
22
|
-
<button class="boton" data-accion="añadirAlCarrito" type="button">
|
|
23
|
-
Añadir al carrito
|
|
24
|
-
</button>
|
|
25
|
-
<div data-si="hay-resultados">
|
|
26
|
-
|
|
27
|
-
</div>
|
|
28
|
-
<section class="lista" data-modelo="Sin stock — avísame cuando llegue">
|
|
29
|
-
<div class="cargando" aria-live="polite">Cargando...</div>
|
|
30
|
-
</section>
|
|
31
|
-
<button class="boton" data-accion="suscribirStock" type="button">
|
|
32
|
-
Avisarme
|
|
33
|
-
</button>
|
|
34
|
-
|
|
35
|
-
</main>
|
|
36
|
-
<script src="telar.js" defer></script>
|
|
37
|
-
</body>
|
|
38
|
-
</html>
|
package/dist/telar.css
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/* Telar — estilos base generados automáticamente */
|
|
2
|
-
|
|
3
|
-
*, *::before, *::after {
|
|
4
|
-
box-sizing: border-box;
|
|
5
|
-
margin: 0;
|
|
6
|
-
padding: 0;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
:root {
|
|
10
|
-
--color-fondo: #ffffff;
|
|
11
|
-
--color-texto: #1a1a1a;
|
|
12
|
-
--color-primario: #5B4AB7;
|
|
13
|
-
--color-borde: #e0e0e0;
|
|
14
|
-
--color-error: #dc2626;
|
|
15
|
-
--radio: 8px;
|
|
16
|
-
--espacio: 1rem;
|
|
17
|
-
font-size: 16px;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
body {
|
|
21
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
22
|
-
background: var(--color-fondo);
|
|
23
|
-
color: var(--color-texto);
|
|
24
|
-
line-height: 1.6;
|
|
25
|
-
padding: var(--espacio);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
main {
|
|
29
|
-
max-width: 680px;
|
|
30
|
-
margin: 0 auto;
|
|
31
|
-
padding: var(--espacio);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
h1 {
|
|
35
|
-
font-size: 2rem;
|
|
36
|
-
font-weight: 700;
|
|
37
|
-
margin-bottom: var(--espacio);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.descripcion {
|
|
41
|
-
color: #555;
|
|
42
|
-
margin-bottom: var(--espacio);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.boton {
|
|
46
|
-
display: inline-block;
|
|
47
|
-
padding: 0.6rem 1.2rem;
|
|
48
|
-
background: var(--color-primario);
|
|
49
|
-
color: white;
|
|
50
|
-
border: none;
|
|
51
|
-
border-radius: var(--radio);
|
|
52
|
-
font-size: 1rem;
|
|
53
|
-
cursor: pointer;
|
|
54
|
-
text-decoration: none;
|
|
55
|
-
margin: 0.25rem 0;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.boton:hover {
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.campo-grupo {
|
|
63
|
-
display: flex;
|
|
64
|
-
flex-direction: column;
|
|
65
|
-
gap: 0.4rem;
|
|
66
|
-
margin-bottom: var(--espacio);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
label {
|
|
70
|
-
font-weight: 500;
|
|
71
|
-
font-size: 0.9rem;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
input, textarea {
|
|
75
|
-
padding: 0.6rem 0.8rem;
|
|
76
|
-
border: 1px solid var(--color-borde);
|
|
77
|
-
border-radius: var(--radio);
|
|
78
|
-
font-size: 1rem;
|
|
79
|
-
width: 100%;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
input:focus, textarea:focus {
|
|
83
|
-
outline: 2px solid var(--color-primario);
|
|
84
|
-
border-color: transparent;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.lista {
|
|
88
|
-
margin: var(--espacio) 0;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.cargando {
|
|
92
|
-
color: #888;
|
|
93
|
-
font-size: 0.9rem;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.error {
|
|
97
|
-
color: var(--color-error);
|
|
98
|
-
font-size: 0.9rem;
|
|
99
|
-
padding: 0.5rem;
|
|
100
|
-
border: 1px solid var(--color-error);
|
|
101
|
-
border-radius: var(--radio);
|
|
102
|
-
margin: 0.5rem 0;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.reintentar {
|
|
106
|
-
background: none;
|
|
107
|
-
border: 1px solid var(--color-borde);
|
|
108
|
-
border-radius: var(--radio);
|
|
109
|
-
padding: 0.4rem 0.8rem;
|
|
110
|
-
cursor: pointer;
|
|
111
|
-
font-size: 0.85rem;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/* Responsive — optimizar para móvil */
|
|
115
|
-
@media (max-width: 600px) {
|
|
116
|
-
h1 { font-size: 1.5rem; }
|
|
117
|
-
main { padding: 0.5rem; }
|
|
118
|
-
}
|
package/dist/telar.js
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
// Telar runtime — generado automáticamente
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const Telar = {
|
|
5
|
-
// Estado de la sesión
|
|
6
|
-
usuario: null,
|
|
7
|
-
|
|
8
|
-
// Inicializar sesión desde localStorage
|
|
9
|
-
iniciarSesion() {
|
|
10
|
-
try {
|
|
11
|
-
const datos = localStorage.getItem('telar_usuario')
|
|
12
|
-
if (datos) this.usuario = JSON.parse(datos)
|
|
13
|
-
} catch (e) {
|
|
14
|
-
this.usuario = null
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
// Comprobar condiciones
|
|
19
|
-
evaluar(condicion) {
|
|
20
|
-
switch (condicion) {
|
|
21
|
-
case 'usuario-conectado': return this.usuario !== null
|
|
22
|
-
case 'usuario-admin': return this.usuario?.rol === 'admin'
|
|
23
|
-
case 'hay-resultados': return true // se actualiza dinámicamente
|
|
24
|
-
default: return false
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
// Mostrar u ocultar elementos según condición
|
|
29
|
-
aplicarCondicion(condicion) {
|
|
30
|
-
const elementos = document.querySelectorAll(`[data-si="${condicion}"]`)
|
|
31
|
-
const elementosNo = document.querySelectorAll(`[data-si-no="${condicion}"]`)
|
|
32
|
-
const valor = this.evaluar(condicion)
|
|
33
|
-
|
|
34
|
-
elementos.forEach(el => {
|
|
35
|
-
el.style.display = valor ? '' : 'none'
|
|
36
|
-
})
|
|
37
|
-
elementosNo.forEach(el => {
|
|
38
|
-
el.style.display = valor ? 'none' : ''
|
|
39
|
-
})
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// Cargar datos desde la API
|
|
43
|
-
async cargar(modelo, opciones = {}) {
|
|
44
|
-
const params = new URLSearchParams()
|
|
45
|
-
if (opciones.maximo) params.set('limit', opciones.maximo)
|
|
46
|
-
if (opciones.ordenar) params.set('sort', opciones.ordenar)
|
|
47
|
-
if (opciones.recientes) params.set('recientes', 'true')
|
|
48
|
-
|
|
49
|
-
const url = `/api/${modelo.toLowerCase()}?${params}`
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const res = await fetch(url)
|
|
53
|
-
if (!res.ok) throw new Error(`Error ${res.status}`)
|
|
54
|
-
return await res.json()
|
|
55
|
-
} catch (error) {
|
|
56
|
-
throw error
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
// Mostrar error en un contenedor
|
|
61
|
-
mostrarError(contenedor, mensaje) {
|
|
62
|
-
const errorEl = contenedor.querySelector('.error')
|
|
63
|
-
const cargandoEl = contenedor.querySelector('.cargando')
|
|
64
|
-
if (cargandoEl) cargandoEl.style.display = 'none'
|
|
65
|
-
if (errorEl) {
|
|
66
|
-
errorEl.textContent = mensaje
|
|
67
|
-
errorEl.removeAttribute('hidden')
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Renderizar lista de items
|
|
72
|
-
renderizarLista(contenedor, items, modelo) {
|
|
73
|
-
const cargandoEl = contenedor.querySelector('.cargando')
|
|
74
|
-
const errorEl = contenedor.querySelector('.error')
|
|
75
|
-
if (cargandoEl) cargandoEl.style.display = 'none'
|
|
76
|
-
if (errorEl) errorEl.setAttribute('hidden', '')
|
|
77
|
-
|
|
78
|
-
if (!items || items.length === 0) {
|
|
79
|
-
contenedor.setAttribute('data-vacio', 'true')
|
|
80
|
-
this.aplicarCondicion('hay-resultados')
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Renderizar cada item
|
|
85
|
-
const lista = document.createElement('ul')
|
|
86
|
-
lista.className = 'telar-lista'
|
|
87
|
-
items.forEach(item => {
|
|
88
|
-
const li = document.createElement('li')
|
|
89
|
-
li.className = 'telar-item'
|
|
90
|
-
li.innerHTML = this.renderizarItem(item, modelo)
|
|
91
|
-
lista.appendChild(li)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
contenedor.appendChild(lista)
|
|
95
|
-
this.aplicarCondicion('hay-resultados')
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
// Renderizar un item individual
|
|
99
|
-
renderizarItem(item, modelo) {
|
|
100
|
-
return Object.entries(item)
|
|
101
|
-
.map(([clave, valor]) => `<p><strong>${clave}:</strong> ${valor}</p>`)
|
|
102
|
-
.join('')
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
// Reintentar una operación después de N segundos
|
|
106
|
-
reintentar(fn, segundos) {
|
|
107
|
-
setTimeout(fn, segundos * 1000)
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
// Aplicar condiciones dinámicas
|
|
112
|
-
function aplicarCondiciones() {
|
|
113
|
-
Telar.aplicarCondicion('usuario-conectado');
|
|
114
|
-
Telar.aplicarCondicion('hay-resultados');
|
|
115
|
-
Telar.aplicarCondicion('producto.stock-mayor-0');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Cargar Producto
|
|
119
|
-
async function cargarProducto() {
|
|
120
|
-
const contenedor = document.querySelector('[data-modelo="Producto"]')
|
|
121
|
-
if (!contenedor) return
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
const datos = await Telar.cargar('Producto', { recientes: true, maximo: '8', ordenar: 'fecha' })
|
|
125
|
-
Telar.renderizarLista(contenedor, datos, 'Producto')
|
|
126
|
-
} catch (error) {
|
|
127
|
-
Telar.mostrarError(contenedor, 'Error al cargar los datos')
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Cargar Sin stock — avísame cuando llegue
|
|
133
|
-
async function cargarSin stock — avísame cuando llegue() {
|
|
134
|
-
const contenedor = document.querySelector('[data-modelo="Sin stock — avísame cuando llegue"]')
|
|
135
|
-
if (!contenedor) return
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
const datos = await Telar.cargar('Sin stock — avísame cuando llegue', { })
|
|
139
|
-
Telar.renderizarLista(contenedor, datos, 'Sin stock — avísame cuando llegue')
|
|
140
|
-
} catch (error) {
|
|
141
|
-
Telar.mostrarError(contenedor, 'Error al cargar los datos')
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Cargar Tu carrito está vacío
|
|
147
|
-
async function cargarTu carrito está vacío() {
|
|
148
|
-
const contenedor = document.querySelector('[data-modelo="Tu carrito está vacío"]')
|
|
149
|
-
if (!contenedor) return
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const datos = await Telar.cargar('Tu carrito está vacío', { })
|
|
153
|
-
Telar.renderizarLista(contenedor, datos, 'Tu carrito está vacío')
|
|
154
|
-
} catch (error) {
|
|
155
|
-
Telar.mostrarError(contenedor, 'Error al cargar los datos')
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Cargar Correo o contraseña incorrectos
|
|
161
|
-
async function cargarCorreo o contraseña incorrectos() {
|
|
162
|
-
const contenedor = document.querySelector('[data-modelo="Correo o contraseña incorrectos"]')
|
|
163
|
-
if (!contenedor) return
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const datos = await Telar.cargar('Correo o contraseña incorrectos', { })
|
|
167
|
-
Telar.renderizarLista(contenedor, datos, 'Correo o contraseña incorrectos')
|
|
168
|
-
} catch (error) {
|
|
169
|
-
Telar.mostrarError(contenedor, 'Error al cargar los datos')
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// Acción: añadirAlCarrito
|
|
176
|
-
async function añadirAlCarrito() {
|
|
177
|
-
const boton = document.querySelector('[data-accion="añadirAlCarrito"]')
|
|
178
|
-
const errorEl = boton?.nextElementSibling
|
|
179
|
-
|
|
180
|
-
try {
|
|
181
|
-
if (boton) boton.disabled = true
|
|
182
|
-
const res = await fetch('/api/accion/añadirAlCarrito', { method: 'POST' })
|
|
183
|
-
if (!res.ok) throw new Error()
|
|
184
|
-
// Acción completada — redirigir o actualizar según contexto
|
|
185
|
-
} catch (error) {
|
|
186
|
-
if (errorEl && errorEl.classList.contains('error')) {
|
|
187
|
-
errorEl.removeAttribute('hidden')
|
|
188
|
-
}
|
|
189
|
-
} finally {
|
|
190
|
-
if (boton) boton.disabled = false
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Acción: suscribirStock
|
|
195
|
-
async function suscribirStock() {
|
|
196
|
-
const boton = document.querySelector('[data-accion="suscribirStock"]')
|
|
197
|
-
const errorEl = boton?.nextElementSibling
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
if (boton) boton.disabled = true
|
|
201
|
-
const res = await fetch('/api/accion/suscribirStock', { method: 'POST' })
|
|
202
|
-
if (!res.ok) throw new Error()
|
|
203
|
-
// Acción completada — redirigir o actualizar según contexto
|
|
204
|
-
} catch (error) {
|
|
205
|
-
if (errorEl && errorEl.classList.contains('error')) {
|
|
206
|
-
errorEl.removeAttribute('hidden')
|
|
207
|
-
}
|
|
208
|
-
} finally {
|
|
209
|
-
if (boton) boton.disabled = false
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Acción: iniciarSesion
|
|
214
|
-
async function iniciarSesion() {
|
|
215
|
-
const boton = document.querySelector('[data-accion="iniciarSesion"]')
|
|
216
|
-
const errorEl = boton?.nextElementSibling
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
if (boton) boton.disabled = true
|
|
220
|
-
const res = await fetch('/api/accion/iniciarSesion', { method: 'POST' })
|
|
221
|
-
if (!res.ok) throw new Error()
|
|
222
|
-
// Acción completada — redirigir o actualizar según contexto
|
|
223
|
-
} catch (error) {
|
|
224
|
-
if (errorEl && errorEl.classList.contains('error')) {
|
|
225
|
-
errorEl.removeAttribute('hidden')
|
|
226
|
-
}
|
|
227
|
-
} finally {
|
|
228
|
-
if (boton) boton.disabled = false
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Registrar listeners de acciones
|
|
233
|
-
function registrarAcciones() {
|
|
234
|
-
document.querySelector('[data-accion="añadirAlCarrito"]')
|
|
235
|
-
?.addEventListener('click', añadirAlCarrito);
|
|
236
|
-
document.querySelector('[data-accion="suscribirStock"]')
|
|
237
|
-
?.addEventListener('click', suscribirStock);
|
|
238
|
-
document.querySelector('[data-accion="iniciarSesion"]')
|
|
239
|
-
?.addEventListener('click', iniciarSesion);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Inicializar cuando el DOM esté listo
|
|
243
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
244
|
-
Telar.iniciarSesion();
|
|
245
|
-
aplicarCondiciones();
|
|
246
|
-
registrarAcciones();
|
|
247
|
-
cargarProducto();
|
|
248
|
-
cargarSin stock — avísame cuando llegue();
|
|
249
|
-
cargarTu carrito está vacío();
|
|
250
|
-
cargarCorreo o contraseña incorrectos();
|
|
251
|
-
});
|