feat/live-preview #8

Merged
ricardo merged 43 commits from feat/live-preview into main 2025-08-18 00:50:03 +00:00
5 changed files with 594 additions and 275 deletions
Showing only changes of commit 603698c9c0 - Show all commits

View File

@ -24,7 +24,8 @@ public class UserPageController : Controller
}
//[Route("{category}/{slug}")]
[ResponseCache(Duration = 300, VaryByQueryKeys = new[] { "category", "slug" })]
//VOltar a linha abaixo em prod
//[ResponseCache(Duration = 300, VaryByQueryKeys = new[] { "category", "slug" })]
public async Task<IActionResult> Display(string category, string slug)
{
var userPage = await _userPageService.GetPageAsync(category, slug);

View File

@ -31,6 +31,7 @@
<meta name="keywords" content="linktree, links, página profissional, perfil, redes sociais, cartão digital" />
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="icon" type="image/x-icon" href="~/favicon.ico" />

View File

@ -1,16 +1,14 @@
@model BCards.Web.Models.PageTheme
@{
var theme = Model ?? new BCards.Web.Models.PageTheme
{
Name = "Padrão",
PrimaryColor = "#2563eb",
SecondaryColor = "#1d4ed8",
BackgroundColor = "#ffffff",
TextColor = "#1f2937"
};
// Debug - vamos ver o que está chegando
// Console.WriteLine($"Theme recebido: {Model?.Name ?? "NULL"} - Primary: {Model?.PrimaryColor ?? "NULL"}");
{
Name = "Padrão",
PrimaryColor = "#2563eb",
SecondaryColor = "#1d4ed8",
BackgroundColor = "#ffffff",
TextColor = "#1f2937"
};
}
:root {
@ -18,28 +16,27 @@
--secondary-color: @theme.SecondaryColor;
--background-color: @theme.BackgroundColor;
--text-color: @theme.TextColor;
--card-bg: rgba(255, 255, 255, 0.95);
--border-color: rgba(0, 0, 0, 0.1);
}
.user-page {
background-color: var(--background-color);
color: var(--text-color);
@if (!string.IsNullOrEmpty(theme.BackgroundImage))
{
@:background-image: url('@theme.BackgroundImage');
@:background-size: cover;
@:background-position: center;
@:background-attachment: fixed;
}
min-height: 100vh;
padding: 2rem 0;
}
.profile-card {
background-color: rgba(255, 255, 255, 0.95);
background-color: var(--card-bg);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border: 1px solid var(--border-color);
max-width: 500px;
margin: 0 auto;
}
.profile-image {
@ -56,15 +53,20 @@
height: 120px;
border-radius: 50%;
border: 4px solid var(--primary-color);
background-color: rgba(255, 255, 255, 0.1);
background-color: var(--card-bg);
color: var(--primary-color);
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
margin: 0 auto;
}
.profile-name {
color: var(--primary-color);
font-size: 2rem;
font-weight: 600;
margin-bottom: 0.5rem;
margin: 1rem 0 0.5rem 0;
}
.profile-bio {
@ -74,104 +76,347 @@
font-size: 1.1rem;
}
/* ========== LINKS CONTAINER ========== */
.links-container {
margin-bottom: 2rem;
}
.link-button {
background-color: var(--primary-color);
color: white !important;
border: none;
padding: 0.75rem 1.5rem;
/* ========== UNIVERSAL LINK STYLE (TODOS OS LINKS IGUAIS) ========== */
.universal-link {
border: 1px solid var(--border-color);
border-radius: 12px;
text-decoration: none;
display: block;
margin-bottom: 0.75rem;
text-align: center;
font-weight: 500;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
max-width: 100%;
font-size: 0.95rem;
background-color: var(--card-bg);
transition: all 0.3s ease;
}
.link-button:hover {
background-color: var(--secondary-color);
.universal-link:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
color: white !important;
text-decoration: none;
}
.link-button:active {
transform: translateY(0);
.universal-link-header {
background-color: var(--primary-color);
color: white !important;
padding: 0.75rem 1rem;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
text-decoration: none !important;
transition: background-color 0.3s ease;
position: relative;
font-weight: 500;
}
.universal-link-header:hover {
background-color: var(--secondary-color);
color: white !important;
text-decoration: none !important;
}
.universal-link-content {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
text-align: left;
}
/* Thumbnail para produtos */
.link-thumbnail {
width: 40px;
height: 40px;
border-radius: 6px;
object-fit: cover;
margin-right: 0.75rem;
flex-shrink: 0;
background-color: rgba(255, 255, 255, 0.2);
}
/* Ícone para links normais */
.link-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 0.75rem;
flex-shrink: 0;
font-size: 1.2rem;
color: white;
}
.link-text-container {
flex: 1;
min-width: 0;
}
.link-title {
font-size: 1.1rem;
margin-bottom: 0.25rem;
font-weight: 600;
font-size: 1rem;
color: white;
margin: 0;
line-height: 1.2;
/* Truncate long titles */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.link-description {
.link-subtitle {
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.8);
margin: 0;
line-height: 1.1;
/* Truncate long subtitles */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Seta de expansão */
.expand-arrow {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
border-radius: 6px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
margin-left: 0.5rem;
}
.expand-arrow:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
.expand-arrow i {
font-size: 0.9rem;
opacity: 0.9;
transition: transform 0.3s ease;
}
.link-icon {
font-size: 1.2rem;
margin-right: 0.5rem;
.expand-arrow.expanded i {
transform: rotate(180deg);
}
.profile-footer {
border-top: 1px solid rgba(0, 0, 0, 0.1);
padding-top: 1rem;
/* Conteúdo expandido */
.universal-link-details {
padding: 0;
background-color: var(--card-bg);
border-top: 1px solid var(--border-color);
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out, padding 0.3s ease;
}
.profile-footer a {
.universal-link-details.show {
max-height: 400px;
padding: 1rem;
}
/* Imagem expandida para produtos */
.expanded-image {
width: 100%;
max-width: 200px;
height: auto;
border-radius: 8px;
margin-bottom: 1rem;
display: block;
margin-left: auto;
margin-right: auto;
}
.expanded-description {
color: var(--text-color);
opacity: 0.8;
font-size: 0.9rem;
line-height: 1.5;
margin-bottom: 0.75rem;
}
.expanded-price {
color: #28a745;
font-weight: 700;
font-size: 1.1rem;
margin-bottom: 0.75rem;
text-align: center;
}
.expanded-action {
color: var(--primary-color);
font-size: 0.85rem;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
}
/* ========== FOOTER ========== */
.profile-footer {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid var(--border-color);
text-align: center;
}
.footer-promo {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.footer-promo:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.footer-promo-header {
display: flex;
align-items: center;
justify-content: space-between;
color: var(--primary-color);
font-weight: 600;
font-size: 0.9rem;
}
.footer-promo-header i {
transition: transform 0.3s ease;
}
.footer-promo-header.expanded i {
transform: rotate(180deg);
}
.footer-promo-content {
margin-top: 0.5rem;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out, margin-top 0.3s ease;
color: var(--text-color);
opacity: 0.8;
font-size: 0.85rem;
line-height: 1.4;
}
.footer-promo-content.show {
max-height: 200px;
margin-top: 0.75rem;
}
.footer-promo-button {
background-color: var(--primary-color);
color: white !important;
border: none;
padding: 0.4rem 0.8rem;
border-radius: 6px;
text-decoration: none !important;
font-size: 0.8rem;
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin-top: 0.5rem;
transition: background-color 0.3s ease;
}
.footer-promo-button:hover {
background-color: var(--secondary-color);
color: white !important;
text-decoration: none !important;
}
.footer-credits {
font-size: 0.8rem;
color: var(--text-color);
opacity: 0.6;
}
.footer-credits a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
}
.profile-footer a:hover {
.footer-credits a:hover {
color: var(--secondary-color);
text-decoration: underline;
}
/* Responsive Design */
/* ========== ANIMATIONS ========== */
@@keyframes slideDown {
from {
max-height: 0;
padding-top: 0;
padding-bottom: 0;
opacity: 0;
}
to {
max-height: 400px;
padding-top: 1rem;
padding-bottom: 1rem;
opacity: 1;
}
}
/* ========== RESPONSIVE DESIGN ========== */
@@media (max-width: 768px) {
.user-page {
padding: 1rem 0;
}
.profile-card {
padding: 1.5rem;
margin: 1rem;
margin: 0 1rem;
border-radius: 15px;
}
.profile-image,
.profile-image-placeholder {
width: 100px;
height: 100px;
}
.profile-name {
font-size: 1.75rem;
}
.profile-bio {
font-size: 1rem;
.universal-link-header {
padding: 0.65rem 0.8rem;
}
.link-button {
padding: 0.65rem 1.25rem;
font-size: 0.9rem;
margin-bottom: 0.6rem;
}
.link-title {
font-size: 0.95rem;
}
.link-description {
.link-subtitle {
font-size: 0.8rem;
}
.link-thumbnail,
.link-icon {
width: 36px;
height: 36px;
margin-right: 0.5rem;
}
.expand-arrow {
width: 28px;
height: 28px;
}
.expand-arrow i {
font-size: 0.8rem;
}
}
@ -179,150 +424,70 @@
@@media (max-width: 480px) {
.profile-card {
padding: 1rem;
margin: 0.5rem;
margin: 0 0.5rem;
}
.profile-image,
.profile-image-placeholder {
width: 80px;
height: 80px;
}
.profile-name {
font-size: 1.5rem;
}
.link-button {
padding: 0.6rem 1rem;
font-size: 0.85rem;
margin-bottom: 0.5rem;
.universal-link-header {
padding: 0.6rem 0.8rem;
}
}
/* Dark theme adjustments */
@@media (prefers-color-scheme: dark) {
.user-page[data-theme="dark"] .profile-card {
background-color: rgba(17, 24, 39, 0.95);
color: #f9fafb;
}
}
/* Animation for link buttons */
.link-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.link-button:hover::before {
left: 100%;
}
/* Product Link Card Styles */
.product-link-card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 15px;
overflow: hidden;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
}
.product-link-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
border-color: var(--primary-color);
}
.product-image {
height: 120px;
width: 100%;
object-fit: cover;
border-radius: 15px 0 0 15px;
}
.product-title {
color: var(--primary-color);
font-weight: 600;
margin-bottom: 0.5rem;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-description {
font-size: 0.85rem;
line-height: 1.4;
margin-bottom: 0.5rem;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-price {
font-size: 1.1rem;
font-weight: 700;
color: #28a745 !important;
}
.product-link .card-body {
padding: 1rem;
}
/* Responsive adjustments for product cards */
@@media (max-width: 768px) {
.product-image {
height: 100px;
border-radius: 15px 15px 0 0;
}
.product-link-card .row {
flex-direction: column;
}
.product-link-card .col-md-4,
.product-link-card .col-md-8 {
flex: none;
width: 100%;
max-width: 100%;
}
.product-title {
font-size: 1rem;
}
.product-description {
font-size: 0.8rem;
}
.product-price {
font-size: 1rem;
}
}
@@media (max-width: 480px) {
.product-image {
height: 80px;
}
.product-link .card-body {
padding: 0.75rem;
}
.product-title {
.link-title {
font-size: 0.9rem;
}
.product-description {
.link-subtitle {
font-size: 0.75rem;
-webkit-line-clamp: 1;
}
.link-thumbnail,
.link-icon {
width: 32px;
height: 32px;
margin-right: 0.5rem;
}
.expand-arrow {
width: 26px;
height: 26px;
}
.expanded-image {
max-width: 150px;
}
}
/* ========== DARK THEME COMPATIBILITY ========== */
.user-page[data-theme="dark"] .profile-card,
.user-page[data-theme="dark"] .universal-link,
.user-page[data-theme="dark"] .footer-promo {
background-color: rgba(31, 41, 55, 0.95);
border-color: rgba(255, 255, 255, 0.1);
}
.user-page[data-theme="dark"] .universal-link-details,
.user-page[data-theme="dark"] .footer-promo-content {
background-color: rgba(31, 41, 55, 0.95);
border-color: rgba(255, 255, 255, 0.1);
}
/* Accessibility */
.universal-link-header:focus,
.expand-arrow:focus {
outline: 2px solid rgba(255, 255, 255, 0.5);
outline-offset: 2px;
}
/* Smooth scroll for mobile */
.universal-link-details {
scroll-behavior: smooth;
}

View File

@ -42,7 +42,9 @@
}
<link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/userpage.css" asp-append-version="true" />
<link rel="icon" type="image/x-icon" href="~/favicon.ico" />
@await RenderSectionAsync("Styles", required: false)

View File

@ -11,7 +11,15 @@
@section Styles {
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@Html.Raw(await Html.PartialAsync("_ThemeStyles", Model.Theme))
@{
var partialOutput = await Html.PartialAsync("_ThemeStyles", Model.Theme);
using (var writer = new System.IO.StringWriter())
{
partialOutput.WriteTo(writer, HtmlEncoder);
@Html.Raw(writer.ToString())
}
}
</style>
}
@ -27,20 +35,20 @@
}
else
{
<div class="profile-image-placeholder mb-3 mx-auto d-flex align-items-center justify-content-center">
<i class="fs-1">👤</i>
<div class="profile-image-placeholder mb-3 mx-auto">
👤
</div>
}
<!-- Profile Info -->
<h1 class="profile-name">@Model.DisplayName</h1>
@if (!string.IsNullOrEmpty(Model.Bio))
{
<p class="profile-bio">@Model.Bio</p>
}
<!-- Links -->
<!-- Links Container -->
<div class="links-container">
@if (Model.Links?.Any(l => l.IsActive) == true)
{
@ -49,76 +57,122 @@
var link = Model.Links[i];
if (link.IsActive)
{
@if (link.Type == BCards.Web.Models.LinkType.Product)
{
<!-- Card de Produto -->
<a href="@link.Url"
target="_blank"
rel="noopener noreferrer"
class="text-decoration-none mb-3 d-block product-link"
data-link-index="@i"
onclick="recordClick('@Model.Id', @i)">
<div class="card product-link-card">
<div class="row g-0">
@if (!string.IsNullOrEmpty(link.ProductImage))
{
<div class="col-md-4">
<img src="@link.ProductImage"
class="img-fluid product-image"
alt="@(link.ProductTitle ?? link.Title)"
loading="lazy"
onerror="this.style.display='none'">
</div>
}
<div class="@(string.IsNullOrEmpty(link.ProductImage) ? "col-12" : "col-md-8")">
<div class="card-body">
<h6 class="card-title product-title">
@(!string.IsNullOrEmpty(link.ProductTitle) ? link.ProductTitle : link.Title)
</h6>
@if (!string.IsNullOrEmpty(link.ProductDescription))
{
<p class="card-text small text-muted product-description">
@link.ProductDescription
</p>
}
@if (!string.IsNullOrEmpty(link.ProductPrice))
{
<p class="card-text">
<strong class="text-success product-price">@link.ProductPrice</strong>
</p>
}
<small class="text-muted">
<i class="fas fa-external-link-alt me-1"></i>
Ver produto
</small>
</div>
var hasExpandableContent = (!string.IsNullOrEmpty(link.Description) ||
(link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductDescription)));
<!-- Universal Link Style (TODOS OS LINKS IGUAIS) -->
<div class="universal-link" data-link-id="@i">
<!-- Header clicável (vai para o link) -->
<a href="@link.Url"
class="universal-link-header"
onclick="recordClick('@Model.Id', @i)"
target="_blank"
rel="noopener noreferrer">
<div class="universal-link-content">
@if (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductImage))
{
<!-- Thumbnail para produtos -->
<img src="@link.ProductImage"
alt="@(link.ProductTitle ?? link.Title)"
class="link-thumbnail"
loading="lazy"
onerror="this.style.display='none'">
}
else if (!string.IsNullOrEmpty(link.Icon))
{
<!-- Ícone para links normais -->
<div class="link-icon">
<i class="@link.Icon"></i>
</div>
}
else
{
<!-- Ícone padrão se não tiver -->
<div class="link-icon">
<i class="fas fa-link"></i>
</div>
}
<div class="link-text-container">
<div class="link-title">
@if (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductTitle))
{
@link.ProductTitle
}
else
{
@link.Title
}
</div>
@if (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductPrice))
{
<div class="link-subtitle">@link.ProductPrice</div>
}
else if (!string.IsNullOrEmpty(link.Description) && link.Description.Length > 50)
{
<div class="link-subtitle">@(link.Description.Substring(0, 50))...</div>
}
</div>
</div>
</a>
}
else
{
<!-- Link Normal -->
<a href="@link.Url"
target="_blank"
rel="noopener noreferrer"
class="link-button"
data-link-index="@i"
onclick="recordClick('@Model.Id', @i)">
@if (!string.IsNullOrEmpty(link.Icon))
@if (hasExpandableContent)
{
<i class="@link.Icon"></i>
<!-- Seta de expansão (só aparece se tem conteúdo expandível) -->
<button class="expand-arrow"
type="button"
onclick="event.preventDefault(); event.stopPropagation(); toggleLinkDetails(@i)">
<i class="fas fa-chevron-down"></i>
</button>
}
<div>
<div class="link-title">@link.Title</div>
@if (!string.IsNullOrEmpty(link.Description))
{
<div class="link-description">@link.Description</div>
}
</div>
</a>
}
@if (hasExpandableContent)
{
<!-- Conteúdo expandível -->
<div class="universal-link-details" id="details-@i">
@if (link.Type == BCards.Web.Models.LinkType.Product)
{
<!-- Conteúdo expandido para produtos -->
@if (!string.IsNullOrEmpty(link.ProductImage))
{
<img src="@link.ProductImage"
alt="@(link.ProductTitle ?? link.Title)"
class="expanded-image"
loading="lazy">
}
@if (!string.IsNullOrEmpty(link.ProductPrice))
{
<div class="expanded-price">@link.ProductPrice</div>
}
@if (!string.IsNullOrEmpty(link.ProductDescription))
{
<div class="expanded-description">
@link.ProductDescription
</div>
}
}
else
{
<!-- Conteúdo expandido para links normais -->
@if (!string.IsNullOrEmpty(link.Description))
{
<div class="expanded-description">
@link.Description
</div>
}
}
<div class="expanded-action">
<i class="fas fa-external-link-alt"></i>
Clique no título acima para abrir
</div>
</div>
}
</div>
}
}
}
@ -131,10 +185,29 @@
</div>
<!-- Footer -->
<div class="profile-footer mt-4 pt-3 border-top">
<small class="text-muted">
Criado com <a href="@Url.Action("Index", "Home")" class="text-decoration-none">BCards</a>
</small>
<div class="profile-footer">
<!-- Promoção BCards -->
<div class="footer-promo" onclick="togglePromo(this)">
<div class="footer-promo-header">
<span>💡 Gostou desta página?</span>
<i class="fas fa-chevron-down"></i>
</div>
<div class="footer-promo-content">
Crie a sua própria página personalizada com <strong>BCards</strong>!
É rápido, fácil e profissional. Compartilhe todos os seus links em um só lugar.
<div class="mt-2">
<a href="@Url.Action("Index", "Home")" class="footer-promo-button">
<i class="fas fa-rocket"></i>
Criar Minha Página
</a>
</div>
</div>
</div>
<div class="footer-credits">
Criado com <a href="@Url.Action("Index", "Home")">BCards</a>
</div>
</div>
</div>
</div>
@ -151,8 +224,8 @@
@section Scripts {
<script>
// Função original de rastreamento de cliques
function recordClick(pageId, linkIndex) {
// Record click asynchronously
fetch('/click/' + pageId, {
method: 'POST',
headers: {
@ -163,5 +236,82 @@
console.log('Error recording click:', error);
});
}
// Toggle link details (função universal para todos os links)
function toggleLinkDetails(linkIndex) {
const currentDetails = document.getElementById('details-' + linkIndex);
const currentArrow = document.querySelector('[data-link-id="' + linkIndex + '"] .expand-arrow');
if (!currentDetails || !currentArrow) return;
const isCurrentlyExpanded = currentDetails.classList.contains('show');
// Fechar todos os outros links primeiro (auto-close)
const allDetails = document.querySelectorAll('.universal-link-details');
const allArrows = document.querySelectorAll('.expand-arrow');
allDetails.forEach(details => {
details.classList.remove('show');
});
allArrows.forEach(arrow => {
arrow.classList.remove('expanded');
const icon = arrow.querySelector('i');
if (icon) {
icon.style.transform = 'rotate(0deg)';
}
});
// Se não estava expandido, expandir este
if (!isCurrentlyExpanded) {
currentDetails.classList.add('show');
currentArrow.classList.add('expanded');
const icon = currentArrow.querySelector('i');
if (icon) {
icon.style.transform = 'rotate(180deg)';
}
}
}
// Toggle footer promo
function togglePromo(element) {
const content = element.querySelector('.footer-promo-content');
const arrow = element.querySelector('.footer-promo-header i');
if (content.classList.contains('show')) {
content.classList.remove('show');
arrow.style.transform = 'rotate(0deg)';
element.querySelector('.footer-promo-header').classList.remove('expanded');
} else {
content.classList.add('show');
arrow.style.transform = 'rotate(180deg)';
element.querySelector('.footer-promo-header').classList.add('expanded');
}
}
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
// Garantir que todos os accordions comecem fechados
const allDetails = document.querySelectorAll('.universal-link-details, .footer-promo-content');
allDetails.forEach(detail => {
detail.classList.remove('show');
});
const allArrows = document.querySelectorAll('.expand-arrow i, .footer-promo-header i');
allArrows.forEach(arrow => {
arrow.style.transform = 'rotate(0deg)';
});
// Adicionar eventos de teclado para acessibilidade
const expandButtons = document.querySelectorAll('.expand-arrow');
expandButtons.forEach(button => {
button.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
button.click();
}
});
});
});
</script>
}