feat: randomize community clans section
This commit is contained in:
107
ai/tasks/done/TASK-040-randomized-community-clans-section.md
Normal file
107
ai/tasks/done/TASK-040-randomized-community-clans-section.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# TASK-040-randomized-community-clans-section
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Implementar una sección única de clanes/comunidades en la landing principal, mostrando los clanes indicados con sus logos y enlaces a Discord, en orden aleatorio en cada carga de página.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
La home ya incorpora una sección de clanes/comunidad, pero ahora hay una definición concreta de qué clanes deben aparecer, qué logos deben usarse y qué Discord debe enlazarse en cada caso. La sección debe ser única, clara y mantenible, y el orden de los clanes debe variar aleatoriamente cada vez que se cargue la página.
|
||||||
|
|
||||||
|
## Datos a usar
|
||||||
|
Los clanes/comunidades que deben mostrarse son estos:
|
||||||
|
|
||||||
|
1. LCM
|
||||||
|
- Discord: `https://discord.gg/9F9S353QZv`
|
||||||
|
- Logo: usar la imagen proporcionada para LCM
|
||||||
|
|
||||||
|
2. La 129
|
||||||
|
- Discord: placeholder por ahora
|
||||||
|
- Logo: usar la imagen proporcionada para La 129
|
||||||
|
|
||||||
|
3. 250 Hispania
|
||||||
|
- Discord: `https://discord.gg/3E62Yb6Aw3`
|
||||||
|
- Logo: usar solo el escudo de la imagen proporcionada, quitando la parte de texto `Historia de la 250` y quedándose únicamente con el emblema/escudo
|
||||||
|
|
||||||
|
4. H9H
|
||||||
|
- Discord: `https://discord.gg/tYnXK7MQjB`
|
||||||
|
- Logo: placeholder por ahora
|
||||||
|
|
||||||
|
5. BxB
|
||||||
|
- Discord: placeholder por ahora
|
||||||
|
- Logo: usar la imagen proporcionada para BxB
|
||||||
|
|
||||||
|
6. 7dv
|
||||||
|
- Discord: `https://discord.gg/3sxNQZwrg6`
|
||||||
|
- Logo: usar la imagen proporcionada para 7dv
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
1. Revisar la landing actual y la sección de clanes/comunidad ya existente.
|
||||||
|
2. Ajustar la sección para que muestre exactamente estos 6 clanes/comunidades y no una lista distinta.
|
||||||
|
3. Mantener toda la información de los clanes en una estructura de datos clara y mantenible, preferiblemente en JS si eso encaja mejor con el renderizado actual.
|
||||||
|
4. Implementar la sección para mostrar, como mínimo, por cada clan:
|
||||||
|
- logo
|
||||||
|
- nombre
|
||||||
|
- botón o enlace a Discord
|
||||||
|
5. Hacer que el orden de aparición de los clanes sea aleatorio en cada carga de página.
|
||||||
|
6. Asegurar que la aleatorización no rompe la estructura visual ni genera duplicados.
|
||||||
|
7. Para los clanes con Discord aún no definido (`La 129` y `BxB`), mostrar un estado coherente y no roto, por ejemplo:
|
||||||
|
- botón deshabilitado
|
||||||
|
- etiqueta `Próximamente`
|
||||||
|
- o equivalente visual claro y honesto
|
||||||
|
8. Para los clanes con logo pendiente (`H9H`), usar un placeholder visual coherente con la estética de la web.
|
||||||
|
9. Para `250 Hispania`, usar solo el escudo/emblema, sin el texto `Historia de la 250`.
|
||||||
|
10. Mantener la sección como una única sección de comunidad/clanes en la home, sin duplicarla.
|
||||||
|
11. Asegurar que la sección funciona correctamente en escritorio y móvil.
|
||||||
|
12. Mantener la estética coherente con la landing actual.
|
||||||
|
13. Al completar la implementación:
|
||||||
|
- dejar el repositorio consistente
|
||||||
|
- hacer commit
|
||||||
|
- hacer push al remoto si el entorno lo permite
|
||||||
|
|
||||||
|
## Files to Read First
|
||||||
|
- AGENTS.md
|
||||||
|
- frontend/index.html
|
||||||
|
- frontend/assets/css/styles.css
|
||||||
|
- frontend/assets/js/main.js
|
||||||
|
- cualquier archivo actual de datos/renderizado de la sección de clanes si ya existe
|
||||||
|
- assets actuales relacionados con logos de comunidad
|
||||||
|
|
||||||
|
## Expected Files to Modify
|
||||||
|
- frontend/index.html
|
||||||
|
- frontend/assets/css/styles.css
|
||||||
|
- frontend/assets/js/main.js
|
||||||
|
- opcionalmente un archivo de datos/config si mejora claridad, por ejemplo:
|
||||||
|
- frontend/assets/js/community-clans.js
|
||||||
|
- opcionalmente nuevos assets referenciados correctamente si la implementación los necesita, por ejemplo dentro de:
|
||||||
|
- frontend/assets/img/clans/
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
- No romper la landing actual.
|
||||||
|
- No crear varias secciones de clanes; debe quedar una sola.
|
||||||
|
- No introducir frameworks nuevos.
|
||||||
|
- No depender de widgets externos o iframes de Discord para esta sección.
|
||||||
|
- No hacer cambios destructivos.
|
||||||
|
- Mantener el trabajo centrado en la sección de clanes, su contenido real y el orden aleatorio por carga.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
- La home muestra una única sección de clanes/comunidades.
|
||||||
|
- Aparecen exactamente estos 6 clanes/comunidades:
|
||||||
|
- LCM
|
||||||
|
- La 129
|
||||||
|
- 250 Hispania
|
||||||
|
- H9H
|
||||||
|
- BxB
|
||||||
|
- 7dv
|
||||||
|
- El orden cambia aleatoriamente en cada carga de página.
|
||||||
|
- Los enlaces de Discord correctos funcionan para:
|
||||||
|
- LCM
|
||||||
|
- 250 Hispania
|
||||||
|
- H9H
|
||||||
|
- 7dv
|
||||||
|
- Los clanes sin enlace definitivo no muestran botones rotos.
|
||||||
|
- `250 Hispania` usa solo el escudo/emblema, sin el texto lateral.
|
||||||
|
- La sección sigue siendo visualmente coherente y responsive.
|
||||||
|
- Los cambios quedan committeados y se hace push al remoto si el entorno lo permite.
|
||||||
|
|
||||||
|
## Change Budget
|
||||||
|
- Preferir menos de 6 archivos modificados o creados.
|
||||||
|
- Preferir menos de 260 líneas cambiadas.
|
||||||
@@ -790,6 +790,14 @@ h2 {
|
|||||||
background: linear-gradient(180deg, rgba(19, 24, 16, 0.82), rgba(10, 13, 9, 0.66));
|
background: linear-gradient(180deg, rgba(19, 24, 16, 0.82), rgba(10, 13, 9, 0.66));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clan-card__logo--wide {
|
||||||
|
padding-inline: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clan-card__logo--shield {
|
||||||
|
padding-inline: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.clan-card__logo img {
|
.clan-card__logo img {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -798,6 +806,29 @@ h2 {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clan-card__logo-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 84px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
border: 1px dashed rgba(210, 182, 118, 0.22);
|
||||||
|
border-radius: 14px;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(28, 32, 24, 0.88), rgba(12, 15, 11, 0.7)),
|
||||||
|
repeating-linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(255, 255, 255, 0.02) 0,
|
||||||
|
rgba(255, 255, 255, 0.02) 10px,
|
||||||
|
transparent 10px,
|
||||||
|
transparent 20px
|
||||||
|
);
|
||||||
|
color: var(--accent-warm);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
.clan-card__copy {
|
.clan-card__copy {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -831,6 +862,14 @@ h2 {
|
|||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.server-action-link--disabled {
|
||||||
|
border-color: rgba(159, 168, 141, 0.18);
|
||||||
|
background: linear-gradient(180deg, rgba(42, 46, 39, 0.5), rgba(19, 22, 17, 0.7));
|
||||||
|
color: var(--muted);
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1120px) {
|
@media (max-width: 1120px) {
|
||||||
.hero__brand {
|
.hero__brand {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -947,6 +986,10 @@ h2 {
|
|||||||
justify-items: start;
|
justify-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clan-card__logo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.clan-card__link {
|
.clan-card__link {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
frontend/assets/img/clans/250hispania-shield.png
Normal file
BIN
frontend/assets/img/clans/250hispania-shield.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
frontend/assets/img/clans/250hispania.png
Normal file
BIN
frontend/assets/img/clans/250hispania.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
frontend/assets/img/clans/7dv.png
Normal file
BIN
frontend/assets/img/clans/7dv.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
frontend/assets/img/clans/bxb.png
Normal file
BIN
frontend/assets/img/clans/bxb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 362 KiB |
BIN
frontend/assets/img/clans/la129.png
Normal file
BIN
frontend/assets/img/clans/la129.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
frontend/assets/img/clans/lcm.png
Normal file
BIN
frontend/assets/img/clans/lcm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 MiB |
@@ -6,13 +6,70 @@ const SERVER_HISTORY_URLS = Object.freeze({
|
|||||||
});
|
});
|
||||||
const COMMUNITY_CLANS = Object.freeze([
|
const COMMUNITY_CLANS = Object.freeze([
|
||||||
{
|
{
|
||||||
name: "Comunidad Hispana HLL Vietnam",
|
name: "LCM",
|
||||||
badge: "Comunidad anfitriona",
|
badge: "Comunidad aliada",
|
||||||
description:
|
description:
|
||||||
"Punto principal de reunion para escuadras, anuncios y acceso directo al Discord del proyecto.",
|
"Clan visible dentro de la red comunitaria hispana con acceso confirmado a Discord.",
|
||||||
logoSrc: "./assets/img/logo.png",
|
logoSrc: "./assets/img/clans/lcm.png",
|
||||||
logoAlt: "Logo de Comunidad Hispana HLL Vietnam",
|
logoAlt: "Logo de LCM",
|
||||||
discordUrl: "https://discord.com/invite/PedEqZ2Xsa",
|
logoClassName: "",
|
||||||
|
discordUrl: "https://discord.gg/9F9S353QZv",
|
||||||
|
discordLabel: "Abrir Discord",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "La 129",
|
||||||
|
badge: "Discord pendiente",
|
||||||
|
description:
|
||||||
|
"Presencia confirmada para la seccion comunitaria mientras se publica su acceso definitivo.",
|
||||||
|
logoSrc: "./assets/img/clans/la129.png",
|
||||||
|
logoAlt: "Logo de La 129",
|
||||||
|
logoClassName: "clan-card__logo--wide",
|
||||||
|
discordUrl: "",
|
||||||
|
discordLabel: "Proximamente",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "250 Hispania",
|
||||||
|
badge: "Comunidad aliada",
|
||||||
|
description:
|
||||||
|
"Entrada mostrada con su emblema principal y acceso confirmado al Discord compartido.",
|
||||||
|
logoSrc: "./assets/img/clans/250hispania-shield.png",
|
||||||
|
logoAlt: "Escudo de 250 Hispania",
|
||||||
|
logoClassName: "clan-card__logo--shield",
|
||||||
|
discordUrl: "https://discord.gg/3E62Yb6Aw3",
|
||||||
|
discordLabel: "Abrir Discord",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "H9H",
|
||||||
|
badge: "Logo pendiente",
|
||||||
|
description:
|
||||||
|
"Comunidad incluida en la rotacion mientras se incorpora su identidad visual final.",
|
||||||
|
logoSrc: "",
|
||||||
|
logoAlt: "",
|
||||||
|
logoClassName: "",
|
||||||
|
discordUrl: "https://discord.gg/tYnXK7MQjB",
|
||||||
|
discordLabel: "Abrir Discord",
|
||||||
|
placeholderLabel: "H9H",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "BxB",
|
||||||
|
badge: "Discord pendiente",
|
||||||
|
description:
|
||||||
|
"Clan incorporado a la red visible con su emblema actual y enlace aun por confirmar.",
|
||||||
|
logoSrc: "./assets/img/clans/bxb.png",
|
||||||
|
logoAlt: "Logo de BxB",
|
||||||
|
logoClassName: "",
|
||||||
|
discordUrl: "",
|
||||||
|
discordLabel: "Proximamente",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "7dv",
|
||||||
|
badge: "Comunidad aliada",
|
||||||
|
description:
|
||||||
|
"Entrada activa dentro de la seccion con acceso disponible a su Discord comunitario.",
|
||||||
|
logoSrc: "./assets/img/clans/7dv.png",
|
||||||
|
logoAlt: "Logo de 7dv",
|
||||||
|
logoClassName: "",
|
||||||
|
discordUrl: "https://discord.gg/3sxNQZwrg6",
|
||||||
discordLabel: "Abrir Discord",
|
discordLabel: "Abrir Discord",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -246,28 +303,70 @@ function hydrateCommunityClans(listNode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listNode.innerHTML = COMMUNITY_CLANS.map((clan) => renderCommunityClanCard(clan)).join("");
|
listNode.innerHTML = shuffleItems(COMMUNITY_CLANS)
|
||||||
|
.map((clan) => renderCommunityClanCard(clan))
|
||||||
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCommunityClanCard(clan) {
|
function renderCommunityClanCard(clan) {
|
||||||
|
const logoMarkup = renderClanLogo(clan);
|
||||||
|
const discordMarkup = renderClanDiscordLink(clan);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<article class="clan-card">
|
<article class="clan-card">
|
||||||
<div class="clan-card__brand">
|
<div class="clan-card__brand">
|
||||||
<div class="clan-card__logo">
|
${logoMarkup}
|
||||||
<img
|
|
||||||
src="${escapeHtml(clan.logoSrc)}"
|
|
||||||
alt="${escapeHtml(clan.logoAlt)}"
|
|
||||||
width="1024"
|
|
||||||
height="1044"
|
|
||||||
decoding="async"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="clan-card__copy">
|
<div class="clan-card__copy">
|
||||||
<p class="clan-card__eyebrow">${escapeHtml(clan.badge)}</p>
|
<p class="clan-card__eyebrow">${escapeHtml(clan.badge)}</p>
|
||||||
<h3>${escapeHtml(clan.name)}</h3>
|
<h3>${escapeHtml(clan.name)}</h3>
|
||||||
<p>${escapeHtml(clan.description)}</p>
|
<p>${escapeHtml(clan.description)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${discordMarkup}
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderClanLogo(clan) {
|
||||||
|
const logoClassNames = ["clan-card__logo"];
|
||||||
|
if (clan.logoClassName) {
|
||||||
|
logoClassNames.push(clan.logoClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clan.logoSrc) {
|
||||||
|
return `
|
||||||
|
<div class="${escapeHtml(logoClassNames.join(" "))}">
|
||||||
|
<img
|
||||||
|
src="${escapeHtml(clan.logoSrc)}"
|
||||||
|
alt="${escapeHtml(clan.logoAlt)}"
|
||||||
|
decoding="async"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="${escapeHtml(logoClassNames.join(" "))}">
|
||||||
|
<div class="clan-card__logo-placeholder" aria-label="Logo pendiente de ${escapeHtml(clan.name)}">
|
||||||
|
${escapeHtml(clan.placeholderLabel || clan.name)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderClanDiscordLink(clan) {
|
||||||
|
if (!clan.discordUrl) {
|
||||||
|
return `
|
||||||
|
<span
|
||||||
|
class="server-action-link server-action-link--disabled clan-card__link"
|
||||||
|
aria-disabled="true"
|
||||||
|
>
|
||||||
|
${escapeHtml(clan.discordLabel)}
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
<a
|
<a
|
||||||
class="server-action-link clan-card__link"
|
class="server-action-link clan-card__link"
|
||||||
href="${escapeHtml(clan.discordUrl)}"
|
href="${escapeHtml(clan.discordUrl)}"
|
||||||
@@ -276,7 +375,6 @@ function renderCommunityClanCard(clan) {
|
|||||||
>
|
>
|
||||||
${escapeHtml(clan.discordLabel)}
|
${escapeHtml(clan.discordLabel)}
|
||||||
</a>
|
</a>
|
||||||
</article>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,6 +482,19 @@ async function fetchJson(url) {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shuffleItems(items) {
|
||||||
|
const shuffledItems = [...items];
|
||||||
|
for (let currentIndex = shuffledItems.length - 1; currentIndex > 0; currentIndex -= 1) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * (currentIndex + 1));
|
||||||
|
[shuffledItems[currentIndex], shuffledItems[randomIndex]] = [
|
||||||
|
shuffledItems[randomIndex],
|
||||||
|
shuffledItems[currentIndex],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return shuffledItems;
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value) {
|
||||||
return String(value)
|
return String(value)
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
|
|||||||
@@ -172,40 +172,10 @@
|
|||||||
<h2 id="clans-title">Red de comunidad</h2>
|
<h2 id="clans-title">Red de comunidad</h2>
|
||||||
</div>
|
</div>
|
||||||
<p class="panel__intro">
|
<p class="panel__intro">
|
||||||
Espacio para mantener visibles las comunidades y clanes con los que
|
Clanes y comunidades activas de la escena hispana con presencia
|
||||||
compartimos actividad, eventos o coordinacion en Discord.
|
confirmada o en preparacion para esta red compartida.
|
||||||
</p>
|
</p>
|
||||||
<div class="clans-grid" id="community-clans-list">
|
<div class="clans-grid" id="community-clans-list"></div>
|
||||||
<article class="clan-card">
|
|
||||||
<div class="clan-card__brand">
|
|
||||||
<div class="clan-card__logo">
|
|
||||||
<img
|
|
||||||
src="./assets/img/logo.png"
|
|
||||||
alt="Logo de Comunidad Hispana HLL Vietnam"
|
|
||||||
width="1024"
|
|
||||||
height="1044"
|
|
||||||
decoding="async"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="clan-card__copy">
|
|
||||||
<p class="clan-card__eyebrow">Comunidad anfitriona</p>
|
|
||||||
<h3>Comunidad Hispana HLL Vietnam</h3>
|
|
||||||
<p>
|
|
||||||
Punto principal de reunion para escuadras, anuncios y acceso
|
|
||||||
directo al Discord del proyecto.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
class="server-action-link clan-card__link"
|
|
||||||
href="https://discord.com/invite/PedEqZ2Xsa"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Abrir Discord
|
|
||||||
</a>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Reference in New Issue
Block a user