Compare commits

..

No commits in common. "174287f5bf80d69de7bbbcd9e37ae262c41247d5" and "cd9380bdc2c95461ed0609207135c238510be9b0" have entirely different histories.

10 changed files with 40 additions and 727 deletions

View File

@ -1,120 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using QRRapidoApp.Data;
using QRRapidoApp.Models;
using System.Diagnostics;
using System.Security.Claims;
namespace QRRapidoApp.Controllers
{
/// <summary>
/// Controller for handling user ratings
/// </summary>
[Route("api/ratings")]
[ApiController]
public class RatingsController : ControllerBase
{
private readonly MongoDbContext _dbContext;
private readonly ILogger<RatingsController> _logger;
public RatingsController(MongoDbContext dbContext, ILogger<RatingsController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
/// <summary>
/// Submit a new rating
/// </summary>
[HttpPost]
public async Task<IActionResult> SubmitRating([FromBody] RatingSubmissionDto submission)
{
var stopwatch = Stopwatch.StartNew();
using (_logger.BeginScope(new Dictionary<string, object>
{
["Rating"] = submission.Rating,
["HasComment"] = !string.IsNullOrWhiteSpace(submission.Comment)
}))
{
_logger.LogInformation("Rating submission - Rating: {Rating}, HasName: {HasName}, HasEmail: {HasEmail}, HasComment: {HasComment}",
submission.Rating, !string.IsNullOrWhiteSpace(submission.Name), !string.IsNullOrWhiteSpace(submission.Email), !string.IsNullOrWhiteSpace(submission.Comment));
try
{
// Validate rating value
if (submission.Rating < 1 || submission.Rating > 5)
{
_logger.LogWarning("Invalid rating value - Rating: {Rating}", submission.Rating);
return BadRequest(new { error = "Rating must be between 1 and 5" });
}
// Check MongoDB connection
if (!_dbContext.IsConnected || _dbContext.Ratings == null)
{
_logger.LogError("MongoDB not connected or Ratings collection unavailable");
return StatusCode(503, new { error = "Database unavailable" });
}
// Get user ID if authenticated
var userId = User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
// Get culture from request
var culture = System.Globalization.CultureInfo.CurrentUICulture.Name;
// Get IP address
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
// Create rating object
var rating = new Rating
{
RatingValue = submission.Rating,
Name = submission.Name,
Email = submission.Email,
Comment = submission.Comment,
Url = submission.Url ?? string.Empty,
UserAgent = submission.UserAgent ?? string.Empty,
UserId = userId,
CreatedAt = DateTime.UtcNow,
IpAddress = ipAddress,
Culture = culture
};
// Save to MongoDB
await _dbContext.Ratings.InsertOneAsync(rating);
stopwatch.Stop();
_logger.LogInformation("Rating saved successfully - RatingId: {RatingId}, Rating: {Rating}, UserId: {UserId}, ProcessingTime: {ProcessingTimeMs}ms",
rating.Id, rating.RatingValue, userId ?? "anonymous", stopwatch.ElapsedMilliseconds);
return Ok(new
{
success = true,
message = "Thank you for your feedback!",
ratingId = rating.Id
});
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "Error saving rating - ProcessingTime: {ProcessingTimeMs}ms",
stopwatch.ElapsedMilliseconds);
return StatusCode(500, new { error = "Error saving rating" });
}
}
}
}
/// <summary>
/// DTO for rating submission
/// </summary>
public class RatingSubmissionDto
{
public int Rating { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? Comment { get; set; }
public string? Url { get; set; }
public string? UserAgent { get; set; }
}
}

View File

@ -35,7 +35,6 @@ namespace QRRapidoApp.Data
public IMongoCollection<QRCodeHistory> QRCodeHistory => _database.GetCollection<QRCodeHistory>("qrCodeHistory");
public IMongoCollection<Plan> Plans => _database.GetCollection<Plan>("plans");
public IMongoCollection<AdFreeSession>? AdFreeSessions => _isConnected ? _database?.GetCollection<AdFreeSession>("ad_free_sessions") : null;
public IMongoCollection<Rating>? Ratings => _isConnected ? _database?.GetCollection<Rating>("ratings") : null;
public IMongoDatabase? Database => _isConnected ? _database : null;
public bool IsConnected => _isConnected;
@ -93,16 +92,6 @@ namespace QRRapidoApp.Data
)
)
});
// Rating indexes
var ratingIndexKeys = Builders<Rating>.IndexKeys;
await Ratings.Indexes.CreateManyAsync(new[]
{
new CreateIndexModel<Rating>(ratingIndexKeys.Descending(r => r.CreatedAt)),
new CreateIndexModel<Rating>(ratingIndexKeys.Ascending(r => r.UserId)),
new CreateIndexModel<Rating>(ratingIndexKeys.Ascending(r => r.RatingValue)),
new CreateIndexModel<Rating>(ratingIndexKeys.Ascending(r => r.Culture))
});
}
}
}

View File

@ -1,42 +0,0 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace QRRapidoApp.Models
{
public class Rating
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } = string.Empty;
[BsonElement("rating")]
public int RatingValue { get; set; } // 1-5 stars
[BsonElement("name")]
public string? Name { get; set; }
[BsonElement("email")]
public string? Email { get; set; }
[BsonElement("comment")]
public string? Comment { get; set; }
[BsonElement("url")]
public string Url { get; set; } = string.Empty;
[BsonElement("userAgent")]
public string UserAgent { get; set; } = string.Empty;
[BsonElement("userId")]
public string? UserId { get; set; } // If authenticated
[BsonElement("createdAt")]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
[BsonElement("ipAddress")]
public string? IpAddress { get; set; }
[BsonElement("culture")]
public string Culture { get; set; } = "pt-BR";
}
}

View File

@ -1196,36 +1196,6 @@
<data name="PremiumSupportOptionForm" xml:space="preserve">
<value>Enviar formulario</value>
</data>
<data name="RateOurService" xml:space="preserve">
<value>Evaluar nuestro servicio</value>
</data>
<data name="RatingQuestion" xml:space="preserve">
<value>¿Cómo evaluás tu experiencia?</value>
</data>
<data name="YourName" xml:space="preserve">
<value>Tu nombre</value>
</data>
<data name="EnterYourName" xml:space="preserve">
<value>Ingresá tu nombre</value>
</data>
<data name="YourEmail" xml:space="preserve">
<value>Tu e-mail</value>
</data>
<data name="YourComment" xml:space="preserve">
<value>Tu comentario</value>
</data>
<data name="TellUsMore" xml:space="preserve">
<value>Contanos más sobre tu experiencia...</value>
</data>
<data name="SendRating" xml:space="preserve">
<value>Enviar Evaluación</value>
</data>
<data name="RatingThanks" xml:space="preserve">
<value>¡Gracias por tu evaluación! Tu opinión es muy importante para nosotros.</value>
</data>
<data name="Optional" xml:space="preserve">
<value>opcional</value>
</data>
<data name="PremiumSupportFormPageTitle" xml:space="preserve">
<value>Contacto con Soporte Premium</value>
</data>

View File

@ -1286,36 +1286,6 @@
<data name="PremiumSupportOptionForm" xml:space="preserve">
<value>Enviar formulário</value>
</data>
<data name="RateOurService" xml:space="preserve">
<value>Avaliar nosso serviço</value>
</data>
<data name="RatingQuestion" xml:space="preserve">
<value>Como você avalia sua experiência?</value>
</data>
<data name="YourName" xml:space="preserve">
<value>Seu nome</value>
</data>
<data name="EnterYourName" xml:space="preserve">
<value>Digite seu nome</value>
</data>
<data name="YourEmail" xml:space="preserve">
<value>Seu e-mail</value>
</data>
<data name="YourComment" xml:space="preserve">
<value>Seu comentário</value>
</data>
<data name="TellUsMore" xml:space="preserve">
<value>Conte-nos mais sobre sua experiência...</value>
</data>
<data name="SendRating" xml:space="preserve">
<value>Enviar Avaliação</value>
</data>
<data name="RatingThanks" xml:space="preserve">
<value>Obrigado pela sua avaliação! Seu feedback é muito importante para nós.</value>
</data>
<data name="Optional" xml:space="preserve">
<value>opcional</value>
</data>
<data name="PremiumSupportFormPageTitle" xml:space="preserve">
<value>Contato com Suporte Premium</value>
</data>

View File

@ -133,7 +133,7 @@
</script>
<!-- Google Analytics 4 - Optimized with defer -->
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-64FCDJGT44"></script>
<script defer src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script defer>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
@ -144,7 +144,7 @@
});
gtag('js', new Date());
gtag('config', 'G-64FCDJGT44', {
gtag('config', 'GA_MEASUREMENT_ID', {
send_page_view: false
});
@ -186,7 +186,6 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" media="print" onload="this.media='all'">
<link rel="stylesheet" href="~/css/vendor/fontawesome.min.css" asp-append-version="true" media="print" onload="this.media='all'" />
<link rel="stylesheet" href="~/css/telegram-fab.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/rating.css" asp-append-version="true" />
<!-- Custom CSS - Critical above fold with cache busting -->
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
@ -420,13 +419,14 @@
<!-- Cookie Consent Banner -->
@await Html.PartialAsync("_CookieConsent")
<!-- Support FAB - Available for all users, with rating option -->
@await Html.PartialAsync("_TelegramPremiumFab", isPremiumUser)
@if (isPremiumUser)
{
@await Html.PartialAsync("_TelegramPremiumFab")
}
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/telegram-fab.js" asp-append-version="true" defer></script>
<script src="~/js/rating.js" asp-append-version="true" defer></script>
@if (isDevelopment)
{

View File

@ -1,10 +1,8 @@
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<QRRapidoApp.Resources.SharedResource> Localizer
@inject Microsoft.Extensions.Configuration.IConfiguration Configuration
@model bool
@{
var isPremiumUser = Model; // Receives isPremiumUser from _Layout
var telegramUrl = Configuration["Support:TelegramUrl"] ?? "https://t.me/jobmakerbr";
var formConfigured = !string.IsNullOrWhiteSpace(Configuration["Support:FormspreeUrl"]);
var formLink = Url.Action("PremiumContact", "Support");
@ -18,17 +16,8 @@
role="menu"
aria-label="@Localizer["PremiumSupportMenuIntro"]"
hidden>
@if (isPremiumUser)
{
<p class="support-fab-text">@Localizer["PremiumSupportMenuIntro"]</p>
}
else
{
<p class="support-fab-text">@Localizer["RateOurService"]</p>
}
<div class="support-fab-actions">
@if (isPremiumUser)
{
<a class="support-fab-link support-telegram"
href="@telegramUrl"
target="_blank"
@ -48,14 +37,6 @@
<span>@Localizer["PremiumSupportOptionForm"]</span>
</a>
}
}
<button type="button"
class="support-fab-link support-rating"
data-rating-trigger
role="menuitem">
<i class="fas fa-star icon" aria-hidden="true"></i>
<span>@Localizer["RateOurService"]</span>
</button>
</div>
</div>
@ -68,66 +49,3 @@
<span class="fab-trigger-text">@Localizer["PremiumSupportFabButton"]</span>
</button>
</div>
<!-- Rating Modal -->
<div class="modal fade" id="ratingModal" tabindex="-1" aria-labelledby="ratingModalLabel" aria-hidden="true" data-rating-modal>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title w-100 text-center" id="ratingModalLabel">
<i class="fas fa-star text-warning me-2"></i>
@Localizer["RateOurService"]
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Localizer["Close"]"></button>
</div>
<div class="modal-body">
<form id="ratingForm">
<!-- Star Rating -->
<div class="text-center mb-4">
<p class="mb-3">@Localizer["RatingQuestion"]</p>
<div class="star-rating" data-star-rating>
<i class="far fa-star star" data-star="1"></i>
<i class="far fa-star star" data-star="2"></i>
<i class="far fa-star star" data-star="3"></i>
<i class="far fa-star star" data-star="4"></i>
<i class="far fa-star star" data-star="5"></i>
</div>
<input type="hidden" name="rating" id="ratingValue" required>
</div>
<!-- Name Field -->
<div class="mb-3">
<label for="ratingName" class="form-label">@Localizer["YourName"] <span class="text-muted">(@Localizer["Optional"])</span></label>
<input type="text" class="form-control" id="ratingName" name="name" placeholder="@Localizer["EnterYourName"]">
</div>
<!-- Email Field -->
<div class="mb-3">
<label for="ratingEmail" class="form-label">@Localizer["YourEmail"] <span class="text-muted">(@Localizer["Optional"])</span></label>
<input type="email" class="form-control" id="ratingEmail" name="email" placeholder="nome@exemplo.com">
</div>
<!-- Comment Field -->
<div class="mb-3">
<label for="ratingComment" class="form-label">@Localizer["YourComment"] <span class="text-muted">(@Localizer["Optional"])</span></label>
<textarea class="form-control" id="ratingComment" name="comment" rows="3" placeholder="@Localizer["TellUsMore"]"></textarea>
</div>
<!-- Submit Button -->
<div class="d-grid">
<button type="submit" class="btn btn-primary btn-lg" id="submitRating">
<i class="fas fa-paper-plane me-2"></i>
@Localizer["SendRating"]
</button>
</div>
</form>
<!-- Success Message -->
<div class="alert alert-success mt-3 d-none" id="ratingSuccess">
<i class="fas fa-check-circle me-2"></i>
@Localizer["RatingThanks"]
</div>
</div>
</div>
</div>
</div>

View File

@ -1,137 +0,0 @@
/* Rating System Styles */
/* Star Rating Component */
.star-rating {
display: inline-flex;
gap: 0.5rem;
font-size: 2.5rem;
user-select: none;
}
.star-rating .star {
cursor: pointer;
transition: all 0.2s ease;
color: #e0e0e0;
}
.star-rating .star:hover {
transform: scale(1.2);
}
.star-rating .star.fas {
color: #ffc107;
}
.star-rating .star.text-warning {
color: #ffc107 !important;
}
/* Rating Modal Customizations */
#ratingModal .modal-content {
border-radius: 1rem;
border: none;
box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.2);
}
#ratingModal .modal-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 1rem 1rem 0 0;
padding: 1.5rem;
}
#ratingModal .modal-title {
font-weight: 600;
font-size: 1.25rem;
}
#ratingModal .btn-close {
filter: brightness(0) invert(1);
}
#ratingModal .modal-body {
padding: 2rem;
}
/* Rating button styles are in telegram-fab.css */
/* Form Styling */
#ratingForm .form-label {
font-weight: 500;
color: #495057;
margin-bottom: 0.5rem;
}
#ratingForm .form-control {
border-radius: 0.5rem;
border: 1px solid #dee2e6;
padding: 0.75rem 1rem;
transition: all 0.2s ease;
}
#ratingForm .form-control:focus {
border-color: #667eea;
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
#ratingForm textarea.form-control {
resize: vertical;
min-height: 100px;
}
/* Submit Button */
#submitRating {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 0.5rem;
padding: 0.875rem 1.5rem;
font-weight: 600;
transition: all 0.3s ease;
}
#submitRating:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 0.5rem 1rem rgba(102, 126, 234, 0.3);
}
#submitRating:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Success Message */
#ratingSuccess {
border-radius: 0.75rem;
background-color: #d4edda;
border-color: #c3e6cb;
color: #155724;
padding: 1rem 1.25rem;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive Adjustments */
@media (max-width: 576px) {
.star-rating {
font-size: 2rem;
gap: 0.25rem;
}
#ratingModal .modal-body {
padding: 1.5rem;
}
#ratingModal .modal-header {
padding: 1rem;
}
}

View File

@ -83,104 +83,44 @@
}
.support-fab-link {
display: flex;
display: inline-flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border-radius: 0.75rem;
transition: all 0.3s ease;
text-decoration: none;
color: #fff;
background: none;
border: none;
width: 100%;
text-align: left;
font: inherit;
cursor: pointer;
position: relative;
overflow: hidden;
}
.support-fab-link::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.05);
opacity: 0;
transition: opacity 0.3s ease;
z-index: 0;
}
.support-fab-link:hover::before,
.support-fab-link:focus-visible::before {
opacity: 1;
justify-content: center;
gap: 0.5rem;
border-radius: 999px;
padding: 0.625rem 0.75rem;
transition: transform 0.2s ease, background-color 0.2s ease;
}
.support-fab-link .icon {
font-size: 1.25rem;
width: 2.5rem;
height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.5rem;
flex-shrink: 0;
transition: all 0.3s ease;
z-index: 1;
font-size: 1rem;
}
.support-fab-link span {
flex: 1;
font-weight: 500;
z-index: 1;
position: relative;
.support-fab-link.support-telegram {
background: rgba(59, 130, 246, 0.15);
color: #60a5fa;
}
/* Telegram Button */
.support-fab-link.support-telegram .icon {
background: linear-gradient(135deg, #0088cc 0%, #005580 100%);
.support-fab-link.support-telegram:hover,
.support-fab-link.support-telegram:focus-visible {
background: rgba(59, 130, 246, 0.25);
color: #fff;
box-shadow: 0 4px 12px rgba(0, 136, 204, 0.3);
}
.support-fab-link.support-telegram:hover .icon,
.support-fab-link.support-telegram:focus-visible .icon {
transform: scale(1.1) rotate(-5deg);
box-shadow: 0 6px 16px rgba(0, 136, 204, 0.5);
.support-fab-link.support-form {
background: rgba(249, 115, 22, 0.15);
color: #f97316;
}
/* Form Button */
.support-fab-link.support-form .icon {
background: linear-gradient(135deg, #f97316 0%, #c2410c 100%);
.support-fab-link.support-form:hover,
.support-fab-link.support-form:focus-visible {
background: rgba(249, 115, 22, 0.25);
color: #fff;
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.3);
}
.support-fab-link.support-form:hover .icon,
.support-fab-link.support-form:focus-visible .icon {
transform: scale(1.1) rotate(-5deg);
box-shadow: 0 6px 16px rgba(249, 115, 22, 0.5);
}
/* Rating Button */
.support-fab-link.support-rating .icon {
background: linear-gradient(135deg, #ffc107 0%, #ff9800 100%);
color: #fff;
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3);
}
.support-fab-link.support-rating:hover .icon,
.support-fab-link.support-rating:focus-visible .icon {
transform: scale(1.1) rotate(-5deg);
box-shadow: 0 6px 16px rgba(255, 193, 7, 0.5);
}
.support-fab-link:hover,
.support-fab-link:focus-visible {
transform: translateX(-4px);
transform: translateY(-1px);
text-decoration: none;
}

View File

@ -1,175 +0,0 @@
// Rating System for QR Rapido
(function() {
'use strict';
let selectedRating = 0;
const ratingModal = document.querySelector('[data-rating-modal]');
const ratingForm = document.getElementById('ratingForm');
const ratingValue = document.getElementById('ratingValue');
const successMessage = document.getElementById('ratingSuccess');
const submitButton = document.getElementById('submitRating');
// Initialize
function init() {
setupEventListeners();
}
function setupEventListeners() {
// Rating trigger button
const ratingTrigger = document.querySelector('[data-rating-trigger]');
if (ratingTrigger) {
ratingTrigger.addEventListener('click', openRatingModal);
}
// Star rating clicks
const stars = document.querySelectorAll('[data-star]');
stars.forEach(star => {
star.addEventListener('click', handleStarClick);
star.addEventListener('mouseover', handleStarHover);
});
// Star rating container mouse leave
const starContainer = document.querySelector('[data-star-rating]');
if (starContainer) {
starContainer.addEventListener('mouseleave', resetStarHover);
}
// Form submission
if (ratingForm) {
ratingForm.addEventListener('submit', handleFormSubmit);
}
// Modal reset on close
if (ratingModal) {
ratingModal.addEventListener('hidden.bs.modal', resetForm);
}
}
function openRatingModal(e) {
e.preventDefault();
// Close support FAB menu
const fabMenu = document.querySelector('[data-support-fab-menu]');
if (fabMenu) {
fabMenu.hidden = true;
}
// Open rating modal
const modal = new bootstrap.Modal(ratingModal);
modal.show();
}
function handleStarClick(e) {
const starValue = parseInt(e.currentTarget.dataset.star);
selectedRating = starValue;
ratingValue.value = starValue;
updateStars(starValue, true);
}
function handleStarHover(e) {
const starValue = parseInt(e.currentTarget.dataset.star);
updateStars(starValue, false);
}
function resetStarHover() {
updateStars(selectedRating, true);
}
function updateStars(rating, permanent) {
const stars = document.querySelectorAll('[data-star]');
stars.forEach(star => {
const starValue = parseInt(star.dataset.star);
if (starValue <= rating) {
star.classList.remove('far');
star.classList.add('fas', 'text-warning');
} else {
star.classList.remove('fas', 'text-warning');
star.classList.add('far');
}
});
}
async function handleFormSubmit(e) {
e.preventDefault();
// Validate rating
if (selectedRating === 0) {
alert('Por favor, selecione uma avaliação com estrelas.');
return;
}
// Get form data
const formData = {
rating: selectedRating,
name: document.getElementById('ratingName').value.trim(),
email: document.getElementById('ratingEmail').value.trim(),
comment: document.getElementById('ratingComment').value.trim(),
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString()
};
// Disable submit button
submitButton.disabled = true;
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enviando...';
try {
// Send to backend
const response = await fetch('/api/ratings', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (response.ok) {
// Show success message
ratingForm.classList.add('d-none');
successMessage.classList.remove('d-none');
// Close modal after 3 seconds
setTimeout(() => {
const modal = bootstrap.Modal.getInstance(ratingModal);
if (modal) {
modal.hide();
}
}, 3000);
} else {
throw new Error('Failed to submit rating');
}
} catch (error) {
console.error('Error submitting rating:', error);
alert('Erro ao enviar avaliação. Por favor, tente novamente.');
// Re-enable submit button
submitButton.disabled = false;
submitButton.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Enviar Avaliação';
}
}
function resetForm() {
// Reset stars
selectedRating = 0;
ratingValue.value = '';
updateStars(0, true);
// Reset form fields
ratingForm.reset();
// Show form, hide success message
ratingForm.classList.remove('d-none');
successMessage.classList.add('d-none');
// Re-enable submit button
submitButton.disabled = false;
submitButton.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Enviar Avaliação';
}
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();