fm-lp-factory
Version:
package created to build web ui components for FidelizarMais LP services
307 lines (254 loc) • 8.43 kB
JavaScript
import { updateFmTemplateCss } from "../index.js";
let sectionElement = null;
let allReviews = [];
let currentPage = 0;
const PAGE_SIZE = 2;
export default (component) => {
const section = document.createElement("section");
section.classList.add("fm-nps-card-review", "fm-section-container", "is-hidden");
css();
section.innerHTML = template
.replace("%title%", component.data.title)
.replace("%subtitle%", component.data.subtitle)
.replace("%description%", component.data.description);
fetchStoreReviews(component.data.storeName, component.data.storeId, component.data.secretKey, component.data.dominio).then(async (data) => {
if (data && data.length > 0) {
section.classList.remove("is-hidden");
allReviews = data;
currentPage = 0;
await loadNextPage();
showLoadMoreButton();
} else {
throw new Error("Nenhum review encontrado");
}
}).catch(console.error);
document.querySelector("#fm-landing-page-content").appendChild(section);
sectionElement = section;
};
const fetchStoreReviews = async (storeName, storeId, secretKey, dominio) => {
const myHeaders = new Headers({
"x-store-key": storeId,
"x-secret-key": secretKey,
"x-language-custom": "pt-BR",
"Origin": dominio
});
const requestOptions = {
method: "GET",
headers: myHeaders
};
try {
const response = await fetch(
`https://zeus-nps.fidelizarmais.com/api/v1/protected/reviews/${storeName}`,
requestOptions
);
if (!response.ok) return [];
const result = await response.json();
const all = Array.isArray(result.data?.reviews) ? result.data.reviews : [];
return all.filter(r => (r.comment || r.answer || "").trim().length > 0).sort((a, b) => (b.rating || 0) - (a.rating || 0));
} catch (error) {
return [];
}
};
const loadNextPage = async () => {
const start = currentPage * PAGE_SIZE;
const end = start + PAGE_SIZE;
const slice = allReviews.slice(start, end);
const cards = await Promise.all(slice.map(buildCard));
appendCards(cards);
currentPage++;
if (currentPage * PAGE_SIZE >= allReviews.length) {
document.querySelector(".fm-nps-load-more")?.classList.add("is-hidden");
}
};
const showLoadMoreButton = () => {
const container = document.createElement("div");
container.className = "fm-nps-load-more";
const button = document.createElement("button");
button.innerText = "CARREGAR MAIS AVALIAÇÕES ↻";
button.onclick = loadNextPage;
container.appendChild(button);
sectionElement.appendChild(container);
};
const initNpsList = (cards) => {
const container = sectionElement.querySelector(".fm-nps-review-cards");
container.innerHTML = "";
appendCards(cards);
};
const appendCards = (cards) => {
const container = sectionElement.querySelector(".fm-nps-review-cards");
cards.forEach(card => container.appendChild(card));
};
const buildCard = async (data) => {
const card = document.createElement("div");
card.classList.add("fm-card-light", "fm-nps-card");
// ✅ Garante data válida
const rawDate = data.created;
const date = rawDate ? new Date(rawDate) : null;
const formattedDate = date
? `${date.getUTCDate().toString().padStart(2, '0')}/${(date.getUTCMonth() + 1).toString().padStart(2, '0')}/${date.getUTCFullYear()}`
: "Data indisponível";
// ✅ Garante texto default se faltar dados
const username = data.customerName || data.name || "Usuário";
const answer = data.answer || data.comment || "Sem comentário.";
card.innerHTML = cardTemplate
.replaceAll("%username%", username)
.replaceAll("%answer%", answer)
.replaceAll("%date%", formattedDate)
.replaceAll("%score%", (data.rating || "–").toFixed ? data.rating.toFixed(1) : data.rating);
return card;
};
const template = /*html*/ `
<div class="fm-nps-review-container">
<div class="fm-nps-review-header">
<span class="fm-nps-review-subtitle">%title%</span>
<span class="fm-nps-review-title">%subtitle%</span>
<span class="fm-nps-review-description">%description%</span>
</div>
<div class="fm-nps-review-cards"></div>
</div>
`;
const cardTemplate = /*html*/ `
<div class="fm-nps-review-card">
<div class="review-content">
<strong>%username%</strong>
<p>%answer%</p>
<div class="bottom-info">
<span class="stars">★ %score%</span>
<span class="date">%date%</span>
</div>
</div>
</div>
`;
const css = () => {
updateFmTemplateCss(/*css*/`
.fm-nps-card-review.is-hidden {
display: none;
}
.fm-nps-review-container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
text-align: center;
}
.fm-nps-review-header {
margin-bottom: 40px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 12px;
}
.fm-nps-review-subtitle {
color:
text-transform: uppercase;
font-weight: bold;
font-size: 0.85rem;
letter-spacing: 0.15rem;
}
.fm-nps-review-title {
font-size: 1.75rem;
font-weight: 900;
text-transform: uppercase;
}
.fm-nps-review-description {
font-size: 0.95rem;
color:
max-width: 700px;
line-height: 1.5;
}
.fm-nps-review-subtitle {
color:
text-transform: uppercase;
font-weight: bold;
display: block;
margin-bottom: 10px;
}
.fm-nps-review-title {
font-size: 36px;
font-weight: 900;
}
.fm-nps-review-cards {
display: grid;
grid-template-columns: repeat(2, 1fr); /* 2 colunas */
gap: 20px;
}
.fm-nps-review-card {
background:
color:
border-radius: 10px;
padding: 20px;
display: flex;
align-items: flex-start;
gap: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.fm-nps-review-card img.avatar {
width: 60px;
height: 60px;
border-radius: 50%;
object-fit: cover;
background:
}
.review-content {
flex: 1;
text-align: left;
}
.review-content strong {
display: block;
font-weight: 800;
font-size: 0.95rem;
margin-bottom: 6px;
}
.review-content .bottom-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
.review-content .stars {
color:
font-weight: bold;
font-size: 0.9rem;
}
.review-content .date {
font-size: 0.85rem;
color:
}
.review-content p {
margin: 0 0 10px;
font-size: 0.95rem;
line-height: 1.4;
}
.review-content .date {
font-size: 0.85rem;
color:
text-align: right;
display: block;
}
.fm-nps-load-more {
margin-top: 30px;
display: flex;
justify-content: center;
}
.fm-nps-load-more button {
background-color:
color: white;
font-weight: bold;
padding: 12px 24px;
font-size: 0.95rem;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
}
.fm-nps-load-more button:hover {
background-color:
transform: scale(1.02);
}
@media (max-width: 480px) {
.fm-nps-review-cards {
grid-template-columns: 1fr;
}
}
`);
};