wagtail-enap-designsystem 1.2.1.130__py3-none-any.whl → 1.2.1.131__py3-none-any.whl

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.

Potentially problematic release.


This version of wagtail-enap-designsystem might be problematic. Click here for more details.

@@ -955,52 +955,63 @@
955
955
  {% block content %}
956
956
  {% load static %}
957
957
  <section class="padding-mobile">
958
- <div class="sistema-votacao-wrapper"
959
- {% if page.background_image_fundo %}
960
- {% for block in page.background_image_fundo %}
961
- {% if block.block_type == 'background_image_stream' and block.value %}
962
- style="background-image: url('{{ block.value.file.url }}');
963
- background-size: cover;
964
- background-position: center;
965
- background-repeat: no-repeat;
966
- background-attachment: fixed;"
967
- {% endif %}
968
- {% endfor %}
969
- {% endif %}>
970
- <div class="sistema-votacao-header{% if page.imagem_fundo %} has-background{% endif %}"
971
- {% if page.imagem_fundo %}
972
- {% for block in page.imagem_fundo %}
973
- style="background-image: url('{{ block.value.file.url }}'); object-fit: cover; object-position: top;"
958
+ <div class="sistema-votacao-wrapper"
959
+ {% if page.background_image_fundo %}
960
+ {% for block in page.background_image_fundo %}
961
+ {% if block.block_type == 'background_image_stream' and block.value %}
962
+ style="background-image: url('{{ block.value.file.url }}');
963
+ background-size: cover;
964
+ background-position: center;
965
+ background-repeat: no-repeat;
966
+ background-attachment: fixed;"
967
+ {% endif %}
974
968
  {% endfor %}
975
- {% endif %}>
976
- <div class="votacao-content">
977
- <div class="sistema-votacao-icon">
978
- <i class="fas fa-vote-yea"></i>
979
- </div>
980
- <h1 class="sistema-votacao-title">{{ page.title }}</h1>
981
- <p class="sistema-votacao-subtitle">{{ page.subtitulo }}</p>
982
-
983
- {% if page.descricao_header %}
984
- <div class="sistema-votacao-descricao">
985
- {{ page.descricao_header|richtext }}
969
+ {% endif %}>
970
+ <div class="sistema-votacao-header{% if page.imagem_fundo %} has-background{% endif %}"
971
+ {% if page.imagem_fundo %}
972
+ {% for block in page.imagem_fundo %}
973
+ style="background-image: url('{{ block.value.file.url }}'); object-fit: cover; object-position: top;"
974
+ {% endfor %}
975
+ {% endif %}>
976
+ <div class="votacao-content">
977
+ <div class="sistema-votacao-icon">
978
+ <i class="fas fa-vote-yea"></i>
986
979
  </div>
987
- {% endif %}
988
- </div>
989
- </div>
980
+ <h1 class="sistema-votacao-title">{{ page.title }}</h1>
981
+ <p class="sistema-votacao-subtitle">{{ page.subtitulo }}</p>
982
+
983
+ {% if page.descricao_header %}
984
+ <div class="sistema-votacao-descricao">
985
+ {{ page.descricao_header|richtext }}
986
+ </div>
987
+ {% endif %}
988
+ </div>
989
+ </div>
990
990
 
991
991
 
992
- {% if page.conteudo_pagina %}
993
- {% for block in page.conteudo_pagina %}
994
- {% include_block block %}
995
- {% endfor %}
996
- {% endif %}
992
+ {% if page.conteudo_pagina %}
993
+ {% for block in page.conteudo_pagina %}
994
+ {% include_block block %}
995
+ {% endfor %}
996
+ {% endif %}
997
997
 
998
- <div class="recaptcha-votacao-container">
999
- {% include 'enap_designsystem/blocks/recaptcha.html' with value=recaptcha_config %}
1000
- </div>
998
+ {% if page.exigir_recaptcha %}
999
+ <div class="recaptcha-gate" style="display: block;">
1000
+ <div class="recaptcha-content" style="max-width: 600px; margin: 2rem auto; padding: 2rem; background: white; border-radius: 12px;">
1001
+ <h2>Verificação de Segurança</h2>
1002
+ <p>Para acessar o sistema de votação, confirme que você não é um robô:</p>
1003
+
1004
+ {% include 'enap_designsystem/blocks/recaptcha.html' with value=recaptcha_config %}
1005
+
1006
+ <button type="button" id="prosseguir-votacao" disabled style="margin-top: 1rem; padding: 1rem 2rem; background: #ccc; border: none; border-radius: 50px; width: 100%;">
1007
+ Complete a verificação acima
1008
+ </button>
1009
+ </div>
1010
+ </div>
1011
+ {% endif %}
1001
1012
 
1002
1013
 
1003
- <div class="sistema-votacao-container">
1014
+ <div class="sistema-votacao-container" {% if page.exigir_recaptcha %}style="display: none;"{% endif %}>>
1004
1015
  <!-- Header -->
1005
1016
 
1006
1017
 
@@ -1144,82 +1155,82 @@
1144
1155
  {% endif %}
1145
1156
  </div>
1146
1157
  </div>
1147
- </div>
1158
+ </div>
1148
1159
 
1149
- <!-- Modal de Conclusão -->
1150
- <div id="modal-conclusao" class="sistema-modal-conclusao">
1151
- <div class="sistema-modal-conclusao-content">
1152
- <div class="sistema-conclusao-icon">
1153
- <i class="fas fa-trophy"></i>
1154
- </div>
1155
- <h3 class="sistema-conclusao-title">Parabéns!</h3>
1156
- <p class="sistema-conclusao-subtitle">Você votou em todas as categorias disponíveis!</p>
1157
-
1158
- <div class="sistema-conclusao-stats">
1159
- <div class="sistema-conclusao-stats-title">Sua Participação</div>
1160
- <div class="sistema-stats-grid">
1161
- <div class="sistema-stat-item">
1162
- <span class="sistema-stat-number" id="total-categorias-votadas">{{ total_categorias }}</span>
1163
- <span class="sistema-stat-label">Categorias</span>
1164
- </div>
1165
- <div class="sistema-stat-item">
1166
- <span class="sistema-stat-number" id="total-projetos-visualizados">-</span>
1167
- <span class="sistema-stat-label">Projetos</span>
1160
+ <!-- Modal de Conclusão -->
1161
+ <div id="modal-conclusao" class="sistema-modal-conclusao">
1162
+ <div class="sistema-modal-conclusao-content">
1163
+ <div class="sistema-conclusao-icon">
1164
+ <i class="fas fa-trophy"></i>
1165
+ </div>
1166
+ <h3 class="sistema-conclusao-title">Parabéns!</h3>
1167
+ <p class="sistema-conclusao-subtitle">Você votou em todas as categorias disponíveis!</p>
1168
+
1169
+ <div class="sistema-conclusao-stats">
1170
+ <div class="sistema-conclusao-stats-title">Sua Participação</div>
1171
+ <div class="sistema-stats-grid">
1172
+ <div class="sistema-stat-item">
1173
+ <span class="sistema-stat-number" id="total-categorias-votadas">{{ total_categorias }}</span>
1174
+ <span class="sistema-stat-label">Categorias</span>
1175
+ </div>
1176
+ <div class="sistema-stat-item">
1177
+ <span class="sistema-stat-number" id="total-projetos-visualizados">-</span>
1178
+ <span class="sistema-stat-label">Projetos</span>
1179
+ </div>
1168
1180
  </div>
1169
1181
  </div>
1170
- </div>
1171
-
1172
- <div class="sistema-conclusao-actions">
1173
- <button class="sistema-btn-votar-novamente" onclick="SistemaVotacao.reiniciarVotacao()">
1174
- <i class="fas fa-redo"></i>
1175
- Votar Novamente
1176
- </button>
1177
- <button class="sistema-btn-encerrar-definitivo" onclick="SistemaVotacao.encerrarDefinitivo()">
1178
- <i class="fas fa-check"></i>
1179
- Encerrar
1180
- </button>
1182
+
1183
+ <div class="sistema-conclusao-actions">
1184
+ <button class="sistema-btn-votar-novamente" onclick="SistemaVotacao.reiniciarVotacao()">
1185
+ <i class="fas fa-redo"></i>
1186
+ Votar Novamente
1187
+ </button>
1188
+ <button class="sistema-btn-encerrar-definitivo" onclick="SistemaVotacao.encerrarDefinitivo()">
1189
+ <i class="fas fa-check"></i>
1190
+ Encerrar
1191
+ </button>
1192
+ </div>
1181
1193
  </div>
1182
1194
  </div>
1183
- </div>
1184
-
1185
- <!-- Modal de Escolha Pós-Voto -->
1186
- <div id="modal-pos-voto" class="sistema-modal-overlay">
1187
- <div class="sistema-modal-content">
1188
- <div class="sistema-modal-header">
1189
- <h3 class="sistema-modal-title">Voto registrado com sucesso!</h3>
1190
- </div>
1191
1195
 
1192
- <!-- Progresso da Votação no Modal -->
1193
- {% if page.mostrar_progresso and categorias %}
1194
- <div class="sistema-progresso-section" style="position: static; margin: 1.5rem 0; padding: 1.5rem; right: auto; bottom: auto;">
1195
- <div class="sistema-progresso-header">
1196
- <h2 class="sistema-progresso-title">Progresso da Votação</h2>
1197
- <span class="sistema-progresso-status" id="progresso-status-modal">0 de {{ total_categorias }} categorias</span>
1196
+ <!-- Modal de Escolha Pós-Voto -->
1197
+ <div id="modal-pos-voto" class="sistema-modal-overlay">
1198
+ <div class="sistema-modal-content">
1199
+ <div class="sistema-modal-header">
1200
+ <h3 class="sistema-modal-title">Voto registrado com sucesso!</h3>
1198
1201
  </div>
1199
- <div class="sistema-progresso-bar">
1200
- <div class="sistema-progresso-fill" id="progresso-fill-modal" style="width: 0%"></div>
1202
+
1203
+ <!-- Progresso da Votação no Modal -->
1204
+ {% if page.mostrar_progresso and categorias %}
1205
+ <div class="sistema-progresso-section" style="position: static; margin: 1.5rem 0; padding: 1.5rem; right: auto; bottom: auto;">
1206
+ <div class="sistema-progresso-header">
1207
+ <h2 class="sistema-progresso-title">Progresso da Votação</h2>
1208
+ <span class="sistema-progresso-status" id="progresso-status-modal">0 de {{ total_categorias }} categorias</span>
1209
+ </div>
1210
+ <div class="sistema-progresso-bar">
1211
+ <div class="sistema-progresso-fill" id="progresso-fill-modal" style="width: 0%"></div>
1212
+ </div>
1213
+ <p class="sistema-progresso-text" id="progresso-text-modal">0% concluído</p>
1201
1214
  </div>
1202
- <p class="sistema-progresso-text" id="progresso-text-modal">0% concluído</p>
1203
- </div>
1204
- {% endif %}
1215
+ {% endif %}
1205
1216
 
1206
-
1207
- <div class="sistema-modal-actions">
1208
- <button class="sistema-btn-continuar" onclick="SistemaVotacao.continuarVotacao()">
1209
- Votar na próxima categoria
1210
- <i class="fas fa-arrow-right"></i>
1211
- </button>
1212
- <button class="sistema-btn-finalizar" onclick="SistemaVotacao.finalizarVotacao()">
1213
- <i class="fas fa-check-circle"></i>
1214
- <span>Finalizar Votação</span>
1215
- </button>
1217
+
1218
+ <div class="sistema-modal-actions">
1219
+ <button class="sistema-btn-continuar" onclick="SistemaVotacao.continuarVotacao()">
1220
+ Votar na próxima categoria
1221
+ <i class="fas fa-arrow-right"></i>
1222
+ </button>
1223
+ <button class="sistema-btn-finalizar" onclick="SistemaVotacao.finalizarVotacao()">
1224
+ <i class="fas fa-check-circle"></i>
1225
+ <span>Finalizar Votação</span>
1226
+ </button>
1227
+ </div>
1216
1228
  </div>
1217
1229
  </div>
1218
- </div>
1219
1230
 
1220
- <!-- Mensagem de Feedback -->
1221
- <div id="feedback-message" class="sistema-feedback-message"></div>
1222
- {% endblock %}
1231
+ <!-- Mensagem de Feedback -->
1232
+ <div id="feedback-message" class="sistema-feedback-message"></div>
1233
+ {% endblock %}
1223
1234
 
1224
1235
  {% block footer %}
1225
1236
  {% include "enap_designsystem/includes/footer.html" %}
@@ -1231,498 +1242,519 @@
1231
1242
 
1232
1243
  </section>
1233
1244
  <script>
1234
- const SistemaVotacao = {
1235
- // Estado da aplicação
1236
- votosCategoria: new Set(),
1237
- categoriasOrdem: [],
1238
- proximaCategoriaData: null,
1239
- elementos: {},
1240
- configuracao: {
1241
- votacaoAtiva: {% if votacao_ativa %}true{% else %}false{% endif %},
1242
- totalCategorias: {{ total_categorias|default:0 }},
1243
- permitirMultiplosVotos: {% if page.permitir_multiplos_votos %}true{% else %}false{% endif %},
1244
- // URL corrigida - usar path absoluto
1245
- urlVotar: '/votar/'
1246
- },
1247
-
1248
- // Inicialização
1249
- init() {
1250
- console.log('🚀 Inicializando Sistema de Votação...');
1245
+ // reCAPTCHA handler
1246
+ window.onRecaptchaSuccess = function(response) {
1247
+ console.log('reCAPTCHA completado');
1251
1248
 
1252
- this.cacheElementos();
1253
- this.configurarTabs();
1254
- this.configurarCategorias();
1255
- this.configurarModais();
1256
- this.configurarCSRF();
1257
-
1258
- console.log('✅ Sistema inicializado');
1259
- console.log('📊 Configuração:', this.configuracao);
1260
- },
1261
-
1262
- // Cache de elementos DOM
1263
- cacheElementos() {
1264
- this.elementos = {
1265
- tabButtons: document.querySelectorAll('.sistema-tab-button'),
1266
- tabContents: document.querySelectorAll('.sistema-tab-content'),
1267
- progressoFill: document.getElementById('progresso-fill'),
1268
- progressoStatus: document.getElementById('progresso-status'),
1269
- progressoText: document.querySelector('.sistema-progresso-text'),
1270
- progressoFillModal: document.getElementById('progresso-fill-modal'),
1271
- progressoStatusModal: document.getElementById('progresso-status-modal'),
1272
- progressoTextModal: document.getElementById('progresso-text-modal'),
1273
- modalPosVoto: document.getElementById('modal-pos-voto'),
1274
- modalConclusao: document.getElementById('modal-conclusao'),
1275
- previewProxima: document.getElementById('preview-proxima-categoria'),
1276
- feedbackMessage: document.getElementById('feedback-message')
1277
- };
1278
-
1279
- console.log('DOM Elements cached:', Object.keys(this.elementos).length);
1280
- },
1281
-
1282
- // Configurar sistema de tabs
1283
- configurarTabs() {
1284
- this.elementos.tabButtons.forEach((button, index) => {
1285
- const targetId = button.getAttribute('data-tab-target');
1286
- const categoriaId = button.getAttribute('data-categoria-id');
1249
+ // Habilitar botão
1250
+ const botao = document.getElementById('prosseguir-votacao');
1251
+ if (botao) {
1252
+ botao.disabled = false;
1253
+ botao.innerHTML = 'Iniciar Votação';
1254
+ botao.style.background = '#007D7A';
1255
+ botao.style.color = 'white';
1256
+
1257
+ // Configurar clique
1258
+ botao.onclick = function() {
1259
+ document.querySelector('.recaptcha-gate').style.display = 'none';
1260
+ document.querySelector('.sistema-votacao-container').style.display = 'block';
1261
+ SistemaVotacao.init();
1262
+ };
1263
+ }
1264
+ };
1265
+
1266
+ const SistemaVotacao = {
1267
+ // Estado da aplicação
1268
+ votosCategoria: new Set(),
1269
+ categoriasOrdem: [],
1270
+ proximaCategoriaData: null,
1271
+ elementos: {},
1272
+ configuracao: {
1273
+ votacaoAtiva: {% if votacao_ativa %}true{% else %}false{% endif %},
1274
+ totalCategorias: {{ total_categorias|default:0 }},
1275
+ permitirMultiplosVotos: {% if page.permitir_multiplos_votos %}true{% else %}false{% endif %},
1276
+ // URL corrigida - usar path absoluto
1277
+ urlVotar: '/votar/'
1278
+ },
1279
+
1280
+ // Inicialização
1281
+ init() {
1282
+ console.log('🚀 Inicializando Sistema de Votação...');
1287
1283
 
1288
- button.addEventListener('click', (e) => {
1289
- e.preventDefault();
1290
- this.trocarTab(targetId, button);
1284
+ this.cacheElementos();
1285
+ this.configurarTabs();
1286
+ this.configurarCategorias();
1287
+ this.configurarModais();
1288
+ this.configurarCSRF();
1289
+
1290
+ console.log('✅ Sistema inicializado');
1291
+ console.log('📊 Configuração:', this.configuracao);
1292
+ },
1293
+
1294
+ // Cache de elementos DOM
1295
+ cacheElementos() {
1296
+ this.elementos = {
1297
+ tabButtons: document.querySelectorAll('.sistema-tab-button'),
1298
+ tabContents: document.querySelectorAll('.sistema-tab-content'),
1299
+ progressoFill: document.getElementById('progresso-fill'),
1300
+ progressoStatus: document.getElementById('progresso-status'),
1301
+ progressoText: document.querySelector('.sistema-progresso-text'),
1302
+ progressoFillModal: document.getElementById('progresso-fill-modal'),
1303
+ progressoStatusModal: document.getElementById('progresso-status-modal'),
1304
+ progressoTextModal: document.getElementById('progresso-text-modal'),
1305
+ modalPosVoto: document.getElementById('modal-pos-voto'),
1306
+ modalConclusao: document.getElementById('modal-conclusao'),
1307
+ previewProxima: document.getElementById('preview-proxima-categoria'),
1308
+ feedbackMessage: document.getElementById('feedback-message')
1309
+ };
1310
+
1311
+ console.log('DOM Elements cached:', Object.keys(this.elementos).length);
1312
+ },
1313
+
1314
+ // Configurar sistema de tabs
1315
+ configurarTabs() {
1316
+ this.elementos.tabButtons.forEach((button, index) => {
1317
+ const targetId = button.getAttribute('data-tab-target');
1318
+ const categoriaId = button.getAttribute('data-categoria-id');
1319
+
1320
+ button.addEventListener('click', (e) => {
1321
+ e.preventDefault();
1322
+ this.trocarTab(targetId, button);
1323
+ });
1291
1324
  });
1292
- });
1293
- },
1325
+ },
1294
1326
 
1295
- // Trocar tab ativa
1296
- trocarTab(targetId, clickedButton) {
1297
- const targetContent = document.getElementById(targetId);
1298
-
1299
- if (!targetContent) {
1300
- console.error('❌ Conteúdo não encontrado:', targetId);
1301
- return;
1302
- }
1327
+ // Trocar tab ativa
1328
+ trocarTab(targetId, clickedButton) {
1329
+ const targetContent = document.getElementById(targetId);
1330
+
1331
+ if (!targetContent) {
1332
+ console.error('❌ Conteúdo não encontrado:', targetId);
1333
+ return;
1334
+ }
1303
1335
 
1304
- // Remover active de todos
1305
- this.elementos.tabButtons.forEach(btn => btn.classList.remove('active'));
1306
- this.elementos.tabContents.forEach(content => content.classList.remove('active'));
1336
+ // Remover active de todos
1337
+ this.elementos.tabButtons.forEach(btn => btn.classList.remove('active'));
1338
+ this.elementos.tabContents.forEach(content => content.classList.remove('active'));
1307
1339
 
1308
- // Ativar clicado
1309
- clickedButton.classList.add('active');
1310
- targetContent.classList.add('active');
1340
+ // Ativar clicado
1341
+ clickedButton.classList.add('active');
1342
+ targetContent.classList.add('active');
1311
1343
 
1312
- // Scroll suave
1313
- setTimeout(() => {
1314
- const header = targetContent.querySelector('.sistema-categoria-header');
1315
- if (header) {
1316
- header.scrollIntoView({ behavior: 'smooth', block: 'start' });
1317
- }
1318
- }, 100);
1319
- },
1320
-
1321
- // Configurar categorias
1322
- configurarCategorias() {
1323
- this.elementos.tabButtons.forEach(button => {
1324
- const categoriaId = parseInt(button.getAttribute('data-categoria-id'));
1325
- const iconElement = button.querySelector('i');
1326
-
1327
- this.categoriasOrdem.push({
1328
- id: categoriaId,
1329
- nome: button.textContent.trim(),
1330
- icone: iconElement ? iconElement.className : 'fas fa-folder'
1331
- });
1332
- });
1333
- },
1334
-
1335
- // Configurar modais
1336
- configurarModais() {
1337
- if (this.elementos.modalPosVoto) {
1338
- this.elementos.modalPosVoto.addEventListener('click', (e) => {
1339
- if (e.target === this.elementos.modalPosVoto) {
1340
- this.pararVotacao();
1341
- }
1342
- });
1343
- }
1344
-
1345
- if (this.elementos.modalConclusao) {
1346
- this.elementos.modalConclusao.addEventListener('click', (e) => {
1347
- if (e.target === this.elementos.modalConclusao) {
1348
- this.encerrarDefinitivo();
1344
+ // Scroll suave
1345
+ setTimeout(() => {
1346
+ const header = targetContent.querySelector('.sistema-categoria-header');
1347
+ if (header) {
1348
+ header.scrollIntoView({ behavior: 'smooth', block: 'start' });
1349
1349
  }
1350
+ }, 100);
1351
+ },
1352
+
1353
+ // Configurar categorias
1354
+ configurarCategorias() {
1355
+ this.elementos.tabButtons.forEach(button => {
1356
+ const categoriaId = parseInt(button.getAttribute('data-categoria-id'));
1357
+ const iconElement = button.querySelector('i');
1358
+
1359
+ this.categoriasOrdem.push({
1360
+ id: categoriaId,
1361
+ nome: button.textContent.trim(),
1362
+ icone: iconElement ? iconElement.className : 'fas fa-folder'
1363
+ });
1350
1364
  });
1351
- }
1352
- },
1365
+ },
1353
1366
 
1354
- // Configurar CSRF Token
1355
- configurarCSRF() {
1356
- const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]');
1357
- if (!csrfToken) {
1358
- console.warn('⚠️ CSRF token não encontrado');
1359
- }
1360
- },
1367
+ // Configurar modais
1368
+ configurarModais() {
1369
+ if (this.elementos.modalPosVoto) {
1370
+ this.elementos.modalPosVoto.addEventListener('click', (e) => {
1371
+ if (e.target === this.elementos.modalPosVoto) {
1372
+ this.pararVotacao();
1373
+ }
1374
+ });
1375
+ }
1376
+
1377
+ if (this.elementos.modalConclusao) {
1378
+ this.elementos.modalConclusao.addEventListener('click', (e) => {
1379
+ if (e.target === this.elementos.modalConclusao) {
1380
+ this.encerrarDefinitivo();
1381
+ }
1382
+ });
1383
+ }
1384
+ },
1361
1385
 
1362
- // FUNÇÃO PRINCIPAL DE VOTAÇÃO - CORRIGIDA
1363
- async votar(projetoId, categoriaId, button) {
1364
- console.log('🗳️ Iniciando votação...', { projetoId, categoriaId });
1365
-
1366
- if (!this.configuracao.votacaoAtiva) {
1367
- this.showFeedback('Votação não está ativa no momento.', 'error');
1368
- return;
1369
- }
1386
+ // Configurar CSRF Token
1387
+ configurarCSRF() {
1388
+ const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]');
1389
+ if (!csrfToken) {
1390
+ console.warn('⚠️ CSRF token não encontrado');
1391
+ }
1392
+ },
1370
1393
 
1371
- // Verificar se botão foi usado
1372
- if (button.classList.contains('votado') || button.disabled) {
1373
- this.showFeedback('Você votou neste projeto.', 'warning');
1374
- return;
1375
- }
1394
+ // FUNÇÃO PRINCIPAL DE VOTAÇÃO - CORRIGIDA
1395
+ async votar(projetoId, categoriaId, button) {
1396
+ console.log('🗳️ Iniciando votação...', { projetoId, categoriaId });
1397
+
1398
+ if (!this.configuracao.votacaoAtiva) {
1399
+ this.showFeedback('Votação não está ativa no momento.', 'error');
1400
+ return;
1401
+ }
1376
1402
 
1377
- // Estado de loading
1378
- this.setLoadingState(button, true);
1403
+ // Verificar se botão já foi usado
1404
+ if (button.classList.contains('votado') || button.disabled) {
1405
+ this.showFeedback('Você já votou neste projeto.', 'warning');
1406
+ return;
1407
+ }
1379
1408
 
1380
- try {
1381
- // Preparar dados
1382
- const dados = {
1383
- projeto_id: parseInt(projetoId),
1384
- categoria_id: parseInt(categoriaId)
1385
- };
1409
+ // Estado de loading
1410
+ this.setLoadingState(button, true);
1386
1411
 
1387
- console.log('📤 Enviando dados:', dados);
1412
+ try {
1413
+ // Preparar dados
1414
+ const dados = {
1415
+ projeto_id: parseInt(projetoId),
1416
+ categoria_id: parseInt(categoriaId)
1417
+ };
1418
+
1419
+ console.log('📤 Enviando dados:', dados);
1420
+
1421
+ // Headers da requisição
1422
+ const headers = {
1423
+ 'Content-Type': 'application/json',
1424
+ };
1425
+
1426
+ // Adicionar CSRF token se disponível
1427
+ const csrfToken = this.getCsrfToken();
1428
+ if (csrfToken) {
1429
+ headers['X-CSRFToken'] = csrfToken;
1430
+ }
1388
1431
 
1389
- // Headers da requisição
1390
- const headers = {
1391
- 'Content-Type': 'application/json',
1392
- };
1432
+ // Fazer requisição
1433
+ const response = await fetch(this.configuracao.urlVotar, {
1434
+ method: 'POST',
1435
+ headers: headers,
1436
+ body: JSON.stringify(dados),
1437
+ credentials: 'same-origin'
1438
+ });
1393
1439
 
1394
- // Adicionar CSRF token se disponível
1395
- const csrfToken = this.getCsrfToken();
1396
- if (csrfToken) {
1397
- headers['X-CSRFToken'] = csrfToken;
1398
- }
1440
+ console.log('📥 Response status:', response.status);
1399
1441
 
1400
- // Fazer requisição
1401
- const response = await fetch(this.configuracao.urlVotar, {
1402
- method: 'POST',
1403
- headers: headers,
1404
- body: JSON.stringify(dados),
1405
- credentials: 'same-origin'
1406
- });
1442
+ // Verificar se response é válido
1443
+ if (!response.ok) {
1444
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1445
+ }
1407
1446
 
1408
- console.log('📥 Response status:', response.status);
1447
+ // Parse do JSON
1448
+ let data;
1449
+ try {
1450
+ data = await response.json();
1451
+ console.log('📊 Response data:', data);
1452
+ } catch (parseError) {
1453
+ console.error('❌ Erro ao fazer parse do JSON:', parseError);
1454
+ throw new Error('Resposta inválida do servidor');
1455
+ }
1409
1456
 
1410
- // Verificar se response é válido
1411
- if (!response.ok) {
1412
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1413
- }
1457
+ if (data.success) {
1458
+ this.processarVotoSucesso(projetoId, categoriaId, button, data);
1459
+ } else {
1460
+ throw new Error(data.message || 'Erro desconhecido ao registrar voto');
1461
+ }
1414
1462
 
1415
- // Parse do JSON
1416
- let data;
1417
- try {
1418
- data = await response.json();
1419
- console.log('📊 Response data:', data);
1420
- } catch (parseError) {
1421
- console.error('❌ Erro ao fazer parse do JSON:', parseError);
1422
- throw new Error('Resposta inválida do servidor');
1463
+ } catch (error) {
1464
+ console.error('❌ Erro completo:', error);
1465
+ this.showFeedback(`Erro: ${error.message}`, 'error');
1466
+ this.setLoadingState(button, false);
1423
1467
  }
1468
+ },
1424
1469
 
1425
- if (data.success) {
1426
- this.processarVotoSucesso(projetoId, categoriaId, button, data);
1427
- } else {
1428
- throw new Error(data.message || 'Erro desconhecido ao registrar voto');
1470
+ // Processar voto bem-sucedido
1471
+ processarVotoSucesso(projetoId, categoriaId, button, data) {
1472
+ console.log('✅ Voto processado com sucesso:', data);
1473
+
1474
+ // Atualizar visual do botão
1475
+ button.classList.add('votado');
1476
+ button.innerHTML = `
1477
+ <i class="fas fa-check-circle"></i>
1478
+ Voto registrado!
1479
+ `;
1480
+ button.disabled = true;
1481
+
1482
+ // Atualizar contador se existir
1483
+ this.atualizarContador(projetoId, data.total_votos);
1484
+
1485
+ // Marcar categoria como votada
1486
+ this.votosCategoria.add(parseInt(categoriaId));
1487
+ this.atualizarProgresso();
1488
+
1489
+ // Desabilitar outros botões da categoria se necessário
1490
+ if (!this.configuracao.permitirMultiplosVotos) {
1491
+ this.desabilitarOutrosBotoes(categoriaId, button);
1429
1492
  }
1430
-
1431
- } catch (error) {
1432
- console.error('❌ Erro completo:', error);
1433
- this.showFeedback(`Erro: ${error.message}`, 'error');
1434
- this.setLoadingState(button, false);
1435
- }
1436
- },
1437
-
1438
- // Processar voto bem-sucedido
1439
- processarVotoSucesso(projetoId, categoriaId, button, data) {
1440
- console.log('✅ Voto processado com sucesso:', data);
1441
-
1442
- // Atualizar visual do botão
1443
- button.classList.add('votado');
1444
- button.innerHTML = `
1445
- <i class="fas fa-check-circle"></i>
1446
- Voto registrado!
1447
- `;
1448
- button.disabled = true;
1449
-
1450
- // Atualizar contador se existir
1451
- this.atualizarContador(projetoId, data.total_votos);
1452
-
1453
- // Marcar categoria como votada
1454
- this.votosCategoria.add(parseInt(categoriaId));
1455
- this.atualizarProgresso();
1456
-
1457
- // Desabilitar outros botões da categoria se necessário
1458
- if (!this.configuracao.permitirMultiplosVotos) {
1459
- this.desabilitarOutrosBotoes(categoriaId, button);
1460
- }
1461
-
1462
- // Mostrar feedback
1463
- this.showFeedback(`Voto registrado em "${data.projeto_titulo || 'projeto'}"!`, 'success');
1464
-
1465
- // Mostrar próximo passo após delay
1466
- setTimeout(() => {
1467
- this.mostrarModalPosVoto(categoriaId);
1468
- }, 2000);
1469
- },
1470
-
1471
- // Atualizar contador de votos
1472
- atualizarContador(projetoId, totalVotos) {
1473
- const contadorElement = document.getElementById(`votos-${projetoId}`);
1474
- if (contadorElement) {
1475
- const novoTotal = totalVotos || 1;
1476
- contadorElement.textContent = novoTotal;
1477
1493
 
1478
- // Animação
1479
- contadorElement.style.transform = 'scale(1.2)';
1480
- contadorElement.style.color = '#28a745';
1494
+ // Mostrar feedback
1495
+ this.showFeedback(`Voto registrado em "${data.projeto_titulo || 'projeto'}"!`, 'success');
1481
1496
 
1497
+ // Mostrar próximo passo após delay
1482
1498
  setTimeout(() => {
1483
- contadorElement.style.transform = 'scale(1)';
1484
- contadorElement.style.color = '';
1485
- }, 500);
1486
- }
1487
- },
1488
-
1489
- // Desabilitar outros botões da categoria
1490
- desabilitarOutrosBotoes(categoriaId, buttonAtivo) {
1491
- const categorySection = document.getElementById(`categoria-${categoriaId}`);
1492
- if (categorySection) {
1493
- const outrosBotoes = categorySection.querySelectorAll('.sistema-btn-votar');
1494
- outrosBotoes.forEach(btn => {
1495
- if (btn !== buttonAtivo && !btn.classList.contains('votado')) {
1496
- btn.disabled = true;
1497
- btn.classList.add('disabled');
1498
- btn.innerHTML = `
1499
- <i class="fas fa-ban"></i>
1500
- Você votou nesta categoria
1501
- `;
1502
- }
1503
- });
1504
- }
1505
- },
1506
-
1507
- // Estado de loading do botão
1508
- setLoadingState(button, loading) {
1509
- if (loading) {
1510
- button.classList.add('loading');
1511
- button.disabled = true;
1512
- button.innerHTML = `
1513
- <i class="fas fa-spinner fa-spin"></i>
1514
- Votando...
1515
- `;
1516
- } else if (!button.classList.contains('votado')) {
1517
- button.classList.remove('loading');
1518
- button.disabled = false;
1519
- button.innerHTML = 'Votar neste Projeto';
1520
- }
1521
- },
1499
+ this.mostrarModalPosVoto(categoriaId);
1500
+ }, 2000);
1501
+ },
1502
+
1503
+ // Atualizar contador de votos
1504
+ atualizarContador(projetoId, totalVotos) {
1505
+ const contadorElement = document.getElementById(`votos-${projetoId}`);
1506
+ if (contadorElement) {
1507
+ const novoTotal = totalVotos || 1;
1508
+ contadorElement.textContent = novoTotal;
1509
+
1510
+ // Animação
1511
+ contadorElement.style.transform = 'scale(1.2)';
1512
+ contadorElement.style.color = '#28a745';
1513
+
1514
+ setTimeout(() => {
1515
+ contadorElement.style.transform = 'scale(1)';
1516
+ contadorElement.style.color = '';
1517
+ }, 500);
1518
+ }
1519
+ },
1520
+
1521
+ // Desabilitar outros botões da categoria
1522
+ desabilitarOutrosBotoes(categoriaId, buttonAtivo) {
1523
+ const categorySection = document.getElementById(`categoria-${categoriaId}`);
1524
+ if (categorySection) {
1525
+ const outrosBotoes = categorySection.querySelectorAll('.sistema-btn-votar');
1526
+ outrosBotoes.forEach(btn => {
1527
+ if (btn !== buttonAtivo && !btn.classList.contains('votado')) {
1528
+ btn.disabled = true;
1529
+ btn.classList.add('disabled');
1530
+ btn.innerHTML = `
1531
+ <i class="fas fa-ban"></i>
1532
+ Você votou nesta categoria
1533
+ `;
1534
+ }
1535
+ });
1536
+ }
1537
+ },
1538
+
1539
+ // Estado de loading do botão
1540
+ setLoadingState(button, loading) {
1541
+ if (loading) {
1542
+ button.classList.add('loading');
1543
+ button.disabled = true;
1544
+ button.innerHTML = `
1545
+ <i class="fas fa-spinner fa-spin"></i>
1546
+ Votando...
1547
+ `;
1548
+ } else if (!button.classList.contains('votado')) {
1549
+ button.classList.remove('loading');
1550
+ button.disabled = false;
1551
+ button.innerHTML = 'Votar neste Projeto';
1552
+ }
1553
+ },
1522
1554
 
1523
- // Mostrar modal pós-voto
1524
- mostrarModalPosVoto(categoriaAtualId) {
1525
- const indiceAtual = this.categoriasOrdem.findIndex(cat => cat.id === parseInt(categoriaAtualId));
1526
- const proximoIndice = indiceAtual + 1;
1527
-
1528
- if (proximoIndice < this.categoriasOrdem.length) {
1529
- this.proximaCategoriaData = this.categoriasOrdem[proximoIndice];
1530
- this.gerarPreviewProximaCategoria(this.proximaCategoriaData.id);
1555
+ // Mostrar modal pós-voto
1556
+ mostrarModalPosVoto(categoriaAtualId) {
1557
+ const indiceAtual = this.categoriasOrdem.findIndex(cat => cat.id === parseInt(categoriaAtualId));
1558
+ const proximoIndice = indiceAtual + 1;
1531
1559
 
1532
- if (this.elementos.modalPosVoto) {
1533
- this.elementos.modalPosVoto.classList.add('show');
1560
+ if (proximoIndice < this.categoriasOrdem.length) {
1561
+ this.proximaCategoriaData = this.categoriasOrdem[proximoIndice];
1562
+ this.gerarPreviewProximaCategoria(this.proximaCategoriaData.id);
1563
+
1564
+ if (this.elementos.modalPosVoto) {
1565
+ this.elementos.modalPosVoto.classList.add('show');
1566
+ }
1567
+ } else {
1568
+ this.mostrarModalConclusao();
1534
1569
  }
1535
- } else {
1536
- this.mostrarModalConclusao();
1537
- }
1538
- },
1570
+ },
1539
1571
 
1540
- // Gerar preview da próxima categoria
1541
- gerarPreviewProximaCategoria(proximaCategoriaId) {
1542
- const proximaCategoria = document.getElementById(`categoria-${proximaCategoriaId}`);
1543
- const projetos = proximaCategoria ? proximaCategoria.querySelectorAll('.sistema-projeto-card') : [];
1544
-
1545
- let previewHTML = '';
1546
-
1547
- if (projetos.length > 0) {
1548
- const categoriaInfo = this.categoriasOrdem.find(cat => cat.id === proximaCategoriaId);
1572
+ // Gerar preview da próxima categoria
1573
+ gerarPreviewProximaCategoria(proximaCategoriaId) {
1574
+ const proximaCategoria = document.getElementById(`categoria-${proximaCategoriaId}`);
1575
+ const projetos = proximaCategoria ? proximaCategoria.querySelectorAll('.sistema-projeto-card') : [];
1549
1576
 
1550
- previewHTML = `
1551
- <div class="sistema-preview-header">
1552
- <div class="sistema-preview-icon">
1553
- <i class="${categoriaInfo.icone}"></i>
1554
- </div>
1555
- <h4 class="sistema-preview-title">Próxima Categoria: ${categoriaInfo.nome}</h4>
1556
- </div>
1557
- <div class="sistema-preview-projetos">
1558
- `;
1577
+ let previewHTML = '';
1559
1578
 
1560
- projetos.forEach(projeto => {
1561
- const titulo = projeto.querySelector('.sistema-projeto-title')?.textContent || 'Projeto';
1562
- const equipe = projeto.querySelector('.sistema-equipe-nome')?.textContent || 'Equipe';
1579
+ if (projetos.length > 0) {
1580
+ const categoriaInfo = this.categoriasOrdem.find(cat => cat.id === proximaCategoriaId);
1563
1581
 
1564
- previewHTML += `
1565
- <div class="sistema-preview-projeto">
1566
- <div class="sistema-preview-projeto-titulo">${titulo}</div>
1567
- <div class="sistema-preview-projeto-equipe">por ${equipe}</div>
1582
+ previewHTML = `
1583
+ <div class="sistema-preview-header">
1584
+ <div class="sistema-preview-icon">
1585
+ <i class="${categoriaInfo.icone}"></i>
1586
+ </div>
1587
+ <h4 class="sistema-preview-title">Próxima Categoria: ${categoriaInfo.nome}</h4>
1568
1588
  </div>
1589
+ <div class="sistema-preview-projetos">
1569
1590
  `;
1570
- });
1591
+
1592
+ projetos.forEach(projeto => {
1593
+ const titulo = projeto.querySelector('.sistema-projeto-title')?.textContent || 'Projeto';
1594
+ const equipe = projeto.querySelector('.sistema-equipe-nome')?.textContent || 'Equipe';
1595
+
1596
+ previewHTML += `
1597
+ <div class="sistema-preview-projeto">
1598
+ <div class="sistema-preview-projeto-titulo">${titulo}</div>
1599
+ <div class="sistema-preview-projeto-equipe">por ${equipe}</div>
1600
+ </div>
1601
+ `;
1602
+ });
1603
+
1604
+ previewHTML += `</div>`;
1605
+ }
1571
1606
 
1572
- previewHTML += `</div>`;
1573
- }
1574
-
1575
- if (this.elementos.previewProxima) {
1576
- this.elementos.previewProxima.innerHTML = previewHTML;
1577
- }
1578
- },
1607
+ if (this.elementos.previewProxima) {
1608
+ this.elementos.previewProxima.innerHTML = previewHTML;
1609
+ }
1610
+ },
1579
1611
 
1580
- // Continuar votação
1581
- continuarVotacao() {
1582
- if (this.elementos.modalPosVoto) {
1583
- this.elementos.modalPosVoto.classList.remove('show');
1584
- }
1585
-
1586
- if (this.proximaCategoriaData) {
1587
- const proximaTab = document.querySelector(`[data-categoria-id="${this.proximaCategoriaData.id}"]`);
1588
- if (proximaTab) {
1589
- proximaTab.click();
1612
+ // Continuar votação
1613
+ continuarVotacao() {
1614
+ if (this.elementos.modalPosVoto) {
1615
+ this.elementos.modalPosVoto.classList.remove('show');
1590
1616
  }
1591
- }
1592
- },
1617
+
1618
+ if (this.proximaCategoriaData) {
1619
+ const proximaTab = document.querySelector(`[data-categoria-id="${this.proximaCategoriaData.id}"]`);
1620
+ if (proximaTab) {
1621
+ proximaTab.click();
1622
+ }
1623
+ }
1624
+ },
1593
1625
 
1594
- // Parar votação
1595
- pararVotacao() {
1596
- if (this.elementos.modalPosVoto) {
1597
- this.elementos.modalPosVoto.classList.remove('show');
1598
- }
1599
- this.showFeedback('Obrigado pela sua participação!', 'success');
1600
- },
1626
+ // Parar votação
1627
+ pararVotacao() {
1628
+ if (this.elementos.modalPosVoto) {
1629
+ this.elementos.modalPosVoto.classList.remove('show');
1630
+ }
1631
+ this.showFeedback('Obrigado pela sua participação!', 'success');
1632
+ },
1601
1633
 
1602
- // Mostrar modal de conclusão
1603
- mostrarModalConclusao() {
1604
- if (this.elementos.modalConclusao) {
1605
- this.elementos.modalConclusao.classList.add('show');
1606
- }
1607
- },
1634
+ // Mostrar modal de conclusão
1635
+ mostrarModalConclusao() {
1636
+ if (this.elementos.modalConclusao) {
1637
+ this.elementos.modalConclusao.classList.add('show');
1638
+ }
1639
+ },
1608
1640
 
1609
- // Reiniciar votação
1610
- reiniciarVotacao() {
1611
- if (this.elementos.modalConclusao) {
1612
- this.elementos.modalConclusao.classList.remove('show');
1613
- }
1614
-
1615
- // Reset estado
1616
- this.votosCategoria.clear();
1617
- this.proximaCategoriaData = null;
1618
-
1619
- // Reset botões
1620
- const botoesVotar = document.querySelectorAll('.sistema-btn-votar');
1621
- botoesVotar.forEach(botao => {
1622
- botao.classList.remove('votado', 'loading', 'disabled');
1623
- botao.disabled = false;
1624
- botao.textContent = 'Votar neste Projeto';
1625
- });
1626
-
1627
- this.atualizarProgresso();
1628
-
1629
- const primeiraTab = document.querySelector('.sistema-tab-button');
1630
- if (primeiraTab) {
1631
- primeiraTab.click();
1632
- }
1633
-
1634
- this.showFeedback('Nova votação iniciada!', 'success');
1635
- },
1641
+ // Reiniciar votação
1642
+ reiniciarVotacao() {
1643
+ if (this.elementos.modalConclusao) {
1644
+ this.elementos.modalConclusao.classList.remove('show');
1645
+ }
1646
+
1647
+ // Reset estado
1648
+ this.votosCategoria.clear();
1649
+ this.proximaCategoriaData = null;
1650
+
1651
+ // Reset botões
1652
+ const botoesVotar = document.querySelectorAll('.sistema-btn-votar');
1653
+ botoesVotar.forEach(botao => {
1654
+ botao.classList.remove('votado', 'loading', 'disabled');
1655
+ botao.disabled = false;
1656
+ botao.textContent = 'Votar neste Projeto';
1657
+ });
1658
+
1659
+ this.atualizarProgresso();
1660
+
1661
+ const primeiraTab = document.querySelector('.sistema-tab-button');
1662
+ if (primeiraTab) {
1663
+ primeiraTab.click();
1664
+ }
1665
+
1666
+ this.showFeedback('Nova votação iniciada!', 'success');
1667
+ },
1636
1668
 
1637
- // Encerrar definitivo
1638
- encerrarDefinitivo() {
1639
- if (this.elementos.modalConclusao) {
1640
- this.elementos.modalConclusao.classList.remove('show');
1641
- }
1642
- this.showFeedback('Obrigado pela sua participação!', 'success');
1643
- },
1644
-
1645
- // Finalizar votação
1646
- finalizarVotacao() {
1647
- if (this.votosCategoria.size === 0) {
1648
- this.showFeedback('Você ainda não votou em nenhuma categoria.', 'warning');
1649
- return;
1650
- }
1651
-
1652
- const categoriasRestantes = this.configuracao.totalCategorias - this.votosCategoria.size;
1653
-
1654
- let mensagem = 'Tem certeza que deseja finalizar sua votação?';
1655
- if (categoriasRestantes > 0) {
1656
- mensagem += `\n\nAinda há ${categoriasRestantes} categoria${categoriasRestantes > 1 ? 's' : ''} disponíveis.`;
1657
- }
1658
-
1659
- if (confirm(mensagem)) {
1660
- this.showFeedback('Obrigado pela participação! Recarregando...', 'success');
1661
- setTimeout(() => window.location.reload(), 1500);
1662
- }
1663
- },
1669
+ // Encerrar definitivo
1670
+ encerrarDefinitivo() {
1671
+ if (this.elementos.modalConclusao) {
1672
+ this.elementos.modalConclusao.classList.remove('show');
1673
+ }
1674
+ this.showFeedback('Obrigado pela sua participação!', 'success');
1675
+ },
1676
+
1677
+ // Finalizar votação
1678
+ finalizarVotacao() {
1679
+ if (this.votosCategoria.size === 0) {
1680
+ this.showFeedback('Você ainda não votou em nenhuma categoria.', 'warning');
1681
+ return;
1682
+ }
1683
+
1684
+ const categoriasRestantes = this.configuracao.totalCategorias - this.votosCategoria.size;
1685
+
1686
+ let mensagem = 'Tem certeza que deseja finalizar sua votação?';
1687
+ if (categoriasRestantes > 0) {
1688
+ mensagem += `\n\nAinda há ${categoriasRestantes} categoria${categoriasRestantes > 1 ? 's' : ''} disponíveis.`;
1689
+ }
1690
+
1691
+ if (confirm(mensagem)) {
1692
+ this.showFeedback('Obrigado pela participação! Recarregando...', 'success');
1693
+ setTimeout(() => window.location.reload(), 1500);
1694
+ }
1695
+ },
1664
1696
 
1665
- // Atualizar progresso
1666
- atualizarProgresso() {
1667
- const votadas = this.votosCategoria.size;
1668
- const percentual = Math.round((votadas / this.configuracao.totalCategorias) * 100);
1669
-
1670
- // Atualizar elementos de progresso
1671
- const elementos = [
1672
- { fill: this.elementos.progressoFill, status: this.elementos.progressoStatus, text: this.elementos.progressoText },
1673
- { fill: this.elementos.progressoFillModal, status: this.elementos.progressoStatusModal, text: this.elementos.progressoTextModal }
1674
- ];
1675
-
1676
- elementos.forEach(({ fill, status, text }) => {
1677
- if (fill) fill.style.width = `${percentual}%`;
1678
- if (status) status.textContent = `${votadas} de ${this.configuracao.totalCategorias} categorias`;
1679
- if (text) text.textContent = `${percentual}% concluído`;
1680
- });
1681
- },
1682
-
1683
- // Mostrar feedback
1684
- showFeedback(message, type = 'success') {
1685
- if (!this.elementos.feedbackMessage) {
1686
- console.log(`Feedback: ${message} (${type})`);
1687
- return;
1688
- }
1689
-
1690
- this.elementos.feedbackMessage.textContent = message;
1691
- this.elementos.feedbackMessage.className = `sistema-feedback-message ${type} show`;
1692
-
1693
- setTimeout(() => {
1694
- this.elementos.feedbackMessage.classList.remove('show');
1695
- }, 4000);
1696
- },
1697
-
1698
- // Obter CSRF token
1699
- getCsrfToken() {
1700
- const csrfInput = document.querySelector('[name=csrfmiddlewaretoken]');
1701
- if (csrfInput) {
1702
- return csrfInput.value;
1703
- }
1704
-
1705
- // Fallback do cookie
1706
- const cookies = document.cookie.split(';');
1707
- for (let cookie of cookies) {
1708
- const [name, value] = cookie.trim().split('=');
1709
- if (name === 'csrftoken') {
1710
- return value;
1697
+ // Atualizar progresso
1698
+ atualizarProgresso() {
1699
+ const votadas = this.votosCategoria.size;
1700
+ const percentual = Math.round((votadas / this.configuracao.totalCategorias) * 100);
1701
+
1702
+ // Atualizar elementos de progresso
1703
+ const elementos = [
1704
+ { fill: this.elementos.progressoFill, status: this.elementos.progressoStatus, text: this.elementos.progressoText },
1705
+ { fill: this.elementos.progressoFillModal, status: this.elementos.progressoStatusModal, text: this.elementos.progressoTextModal }
1706
+ ];
1707
+
1708
+ elementos.forEach(({ fill, status, text }) => {
1709
+ if (fill) fill.style.width = `${percentual}%`;
1710
+ if (status) status.textContent = `${votadas} de ${this.configuracao.totalCategorias} categorias`;
1711
+ if (text) text.textContent = `${percentual}% concluído`;
1712
+ });
1713
+ },
1714
+
1715
+ // Mostrar feedback
1716
+ showFeedback(message, type = 'success') {
1717
+ if (!this.elementos.feedbackMessage) {
1718
+ console.log(`Feedback: ${message} (${type})`);
1719
+ return;
1720
+ }
1721
+
1722
+ this.elementos.feedbackMessage.textContent = message;
1723
+ this.elementos.feedbackMessage.className = `sistema-feedback-message ${type} show`;
1724
+
1725
+ setTimeout(() => {
1726
+ this.elementos.feedbackMessage.classList.remove('show');
1727
+ }, 4000);
1728
+ },
1729
+
1730
+ // Obter CSRF token
1731
+ getCsrfToken() {
1732
+ const csrfInput = document.querySelector('[name=csrfmiddlewaretoken]');
1733
+ if (csrfInput) {
1734
+ return csrfInput.value;
1735
+ }
1736
+
1737
+ // Fallback do cookie
1738
+ const cookies = document.cookie.split(';');
1739
+ for (let cookie of cookies) {
1740
+ const [name, value] = cookie.trim().split('=');
1741
+ if (name === 'csrftoken') {
1742
+ return value;
1743
+ }
1711
1744
  }
1745
+
1746
+ console.warn('⚠️ CSRF token não encontrado');
1747
+ return '';
1712
1748
  }
1713
-
1714
- console.warn('⚠️ CSRF token não encontrado');
1715
- return '';
1716
- }
1717
- };
1749
+ };
1718
1750
 
1719
- // Inicialização
1720
- document.addEventListener('DOMContentLoaded', function() {
1721
- console.log('📄 DOM carregado, inicializando sistema...');
1722
- SistemaVotacao.init();
1723
- });
1751
+ // Inicialização
1752
+ document.addEventListener('DOMContentLoaded', function() {
1753
+ console.log('📄 DOM carregado, inicializando sistema...');
1754
+ SistemaVotacao.init();
1755
+ });
1724
1756
 
1725
- // Expor globalmente
1726
- window.SistemaVotacao = SistemaVotacao;
1757
+ // Expor globalmente
1758
+ window.SistemaVotacao = SistemaVotacao;
1727
1759
  </script>
1728
1760
  {% endblock %}