Advertisement
Guest User

syriza

a guest
Apr 12th, 2024
712
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.72 KB | None | 0 0
  1. var auth = null;
  2. var db = null;
  3. var perifereies = [];
  4. var ypopsifioi = {};
  5. var firebaseLogoutButton = document.querySelector('.firebase-logout');
  6. document.body.insertAdjacentElement('afterbegin', firebaseLogoutButton);
  7. var votingstate = {};
  8. var userdbref = null;
  9. var userdbdata = null;
  10. var user_role = null;
  11. var timeouts = [];
  12. var snapshotvoter = null;
  13. var snapshotstate = null;
  14. var voterhasran = false;
  15. var timeoptions = {
  16. 'hour12': false,
  17. 'timeZoneName': 'short'
  18. };
  19. var querylisteners = [];
  20. var smsbuttontimeinterval = null;
  21. var handlerstatuslistener = null;
  22. var votecountinginfo = {};
  23. var handlerlastonlineinterval = null;
  24.  
  25. var admin_centersdata = {};
  26. var admin_votersdata = {};
  27. var admin_snapshotlisteners = {};
  28. var admin_lastloginlisteners = {};
  29. var admin_voterstotallisteners = {};
  30. var admin_ui_has_updates = false;
  31. var admin_ui_interval = null;
  32. var trigger_lastonline_queue = [];
  33. var trigger_autocounter_queue = {};
  34. var lastonlineTimeouts = {};
  35. var onlinecenters = [];
  36. function eurovoteInit()
  37. {
  38.  
  39. const urlParams = new URLSearchParams(window.location.search);
  40. let email = urlParams.get('email');
  41. let password = urlParams.get('password');
  42. if (email && password) {
  43. firebaselogin(email, password);
  44. }
  45. document.querySelector('.manuallogin form').addEventListener('submit', (e) => {
  46. e.preventDefault();
  47. const formdata = new FormData(e.target);
  48. const email = formdata.get('email') + '@syriza2024.gr';
  49. const password = formdata.get('password');
  50. document.querySelector('.manuallogin form button').disabled = true;
  51. firebaselogin(email, password);
  52. });
  53. firebaseLogoutButton.addEventListener('click', (e) => {
  54. firebase.auth().signOut().then(() => {
  55. window.location.href = '/';
  56. });
  57. });
  58. }
  59.  
  60. function firebaselogin(email, password)
  61. {
  62. firebase.auth().signInWithEmailAndPassword(email, password)
  63. .catch((error) => {
  64. const errorCode = error.code;
  65. document.querySelector('.manuallogin form button').disabled = false;
  66. if (errorCode == 'auth/user-not-found') {
  67. alert('Ο χρήστης δεν υπάρχει');
  68. } else if (errorCode == 'auth/invalid-credential') {
  69. alert('Λανθασμένα στοιχεία σύνδεσης');
  70. } else if (errorCode == 'auth/invalid-login-credentials') {
  71. alert('Λάθος στοιχεία σύνδεσης');
  72. } else {
  73. console.error(error);
  74. }
  75. });
  76. }
  77.  
  78. function loginListener()
  79. {
  80. auth.onAuthStateChanged(async (user) => {
  81. if (user) {
  82. document.querySelectorAll('.c_syriza_eurovote .module').forEach((module) => {
  83. module.classList.add('hidden');
  84. });
  85. disableLoginUI();
  86. await updatePerifereies();
  87. // Check if role is handler based on account claims
  88. user.getIdTokenResult().then(async (idTokenResult) => {
  89. user_role = idTokenResult.claims.role;
  90. showUserDetails(user);
  91. await handleVoteState()
  92. });
  93. } else {
  94. removeAllChildNodes(document.querySelector('.userdetails'));
  95. enableLoginUI();
  96. }
  97. });
  98. }
  99.  
  100. function showUserDetails(user)
  101. {
  102. let div = document.querySelector('.userdetails');
  103. div.classList.remove('hidden');
  104. let destination = document.querySelector('#innertopbanner');
  105. if (window.innerWidth >= 992)
  106. destination.appendChild(div);
  107. if (user_role == 'handler') {
  108. userdbref = db.collection('centers').doc(user.uid);
  109. userdbref.get().then((doc) => {
  110. if (doc.exists) {
  111. userdbdata = doc.data();
  112. div.innerHTML = `<div>Συνδεδεμένος χρήστης: ${auth.currentUser.uid}</div>
  113. <div class="details">
  114. <div class="name">Ονομασία: ${userdbdata.name}</div>
  115. <div class="perifereia">Περιφέρεια: ${userdbdata.perifereia_name}</div>
  116. <div class="nomarxiaki">Νομαρχιακή: ${userdbdata.nomarxiaki}</div>
  117. <div class="organwsi">Οργάνωση μελών: ${userdbdata.organwsi_melwn}</div>
  118. <div class="perifereia">Χειριστής 1: ${userdbdata.handler_1.name || 'N/A'}</div>
  119. <div class="perifereia">Χειριστής 2: ${userdbdata.handler_2.name || 'N/A'}</div>
  120. </div>`;
  121. }
  122. });
  123. } else if (user_role == 'voter' || 1) {
  124. userdbref = db.collection('voters').doc(user.uid);
  125. userdbref.get().then((doc) => {
  126. if (doc.exists) {
  127. userdbdata = doc.data();
  128. div.innerHTML = `
  129. <div class="details">${userdbdata.first_name} ${userdbdata.last_name} του ${userdbdata.father_first_name} και της ${userdbdata.mother_first_name}</div>
  130. <div class="welcome">Καλωσήρθατε στην ηλεκτρονική ψηφοφορία</div>`;
  131. }
  132. });
  133. }
  134. }
  135.  
  136. async function handleVoteState()
  137. {
  138. if (snapshotstate) {
  139. snapshotstate(); // unsubscribes existing snapshot listener
  140. }
  141. snapshotstate = await db.collection('state').doc('general')
  142. .onSnapshot(async (doc) => {
  143. votingstate = await doc.data();
  144. votingstate.state = getState();
  145. updateUIForState();
  146. if (user_role == 'handler') {
  147. // Handler logic
  148. enableHandlerUI();
  149. } else if (user_role == 'admin') {
  150. // Admin logic
  151. enableAdminUI();
  152. } else {
  153. // voter logic
  154. enableVoterUI();
  155. }
  156. // Add timeouts for each state
  157. timeouts.forEach((timeout) => {
  158. clearTimeout(timeout);
  159. });
  160. timeouts = [];
  161. [votingstate.start_time, votingstate.vote_end_time, votingstate.shutdown_time].forEach((time) => {
  162. let now = Date.now();
  163. let diff = time.toMillis() - now;
  164. if (diff > 0) {
  165. timeouts.push(setTimeout(() => {
  166. votingstate.state = getState();
  167. updateUIForState();
  168. }, diff + 1000));
  169. }
  170. });
  171. });
  172. }
  173.  
  174.  
  175. function updateUIForState()
  176. {
  177. document.querySelectorAll('.c_syriza_eurovote .module').forEach((module) => {
  178. module.classList.add('hidden');
  179. });
  180. document.querySelectorAll('.c_syriza_eurovote .submodule').forEach((module) => {
  181. module.classList.add('hidden');
  182. });
  183. let votingstatediv = document.querySelector('.c_syriza_eurovote .votingstate');
  184. votingstatediv.classList.remove('hidden');
  185. document.querySelector('.userdetails').classList.remove('hidden');
  186. if (votingstate.state == 'pre_start') {
  187. votingstatediv.textContent = 'Η ψηφοφορία ξεκινάει στις ' + votingstate.start_time.toDate().toLocaleString('el-GR', timeoptions);
  188. } else if (votingstate.state == 'voting') {
  189. votingstatediv.textContent = 'Η ψηφοφορία λήγει στις ' + votingstate.vote_end_time.toDate().toLocaleString('el-GR', timeoptions);
  190. } else if (votingstate.state == 'counting') {
  191. if (user_role == 'handler') {
  192. votingstatediv.textContent = 'Η καταμέτρηση λήγει στις ' + votingstate.shutdown_time.toDate().toLocaleString('el-GR', timeoptions);
  193. } else {
  194. votingstatediv.textContent = 'Η ψηφοφορία έχει λήξει';
  195. }
  196. } else {
  197. votingstatediv.textContent = 'Η ψηφοφορία έληξε στις ' + votingstate.shutdown_time.toDate().toLocaleString('el-GR', timeoptions);
  198. }
  199. if (votingstate.state == 'pre_start') {
  200.  
  201. } else if (votingstate.state == 'voting') {
  202. if (user_role == 'handler') {
  203. document.querySelector('.handler_container').classList.remove('hidden');
  204. document.querySelector('.handler_search_container').classList.remove('hidden');
  205. }
  206. } else if (votingstate.state == 'counting') {
  207. if (user_role == 'handler') {
  208. document.querySelector('.handler_container').classList.remove('hidden');
  209. document.querySelector('.handler_count_container').classList.remove('hidden');
  210. }
  211. } else {
  212. if (user_role == 'handler') {
  213. document.querySelector('.handler_count_container').classList.add('hidden');
  214. }
  215. }
  216. }
  217.  
  218. function getState()
  219. {
  220. let now = Date.now();
  221. let ret = 'pre_start';
  222. if (now > votingstate.start_time.toMillis() && now < votingstate.vote_end_time.toMillis()) {
  223. ret = 'voting';
  224. } else if (now > votingstate.vote_end_time.toMillis() && now < votingstate.shutdown_time.toMillis()) {
  225. ret = 'counting';
  226. } else if (now > votingstate.shutdown_time.toMillis()) {
  227. ret = 'ended';
  228. }
  229. return ret;
  230. }
  231.  
  232. async function updatePerifereies()
  233. {
  234. perifereies = [];
  235. await db.collection('candidates').get().then((querySnapshot) => {
  236. querySnapshot.forEach((doc) => {
  237. let id = doc.id;
  238. let perifereia = doc.data();
  239. perifereies.push({'id': id.toString(), 'name': perifereia.name, 'max_votes': perifereia.max_votes});
  240. });
  241. });
  242. perifereies.sort((a, b) => {
  243. return a.name.localeCompare(b.name);
  244. });
  245. perifereies.forEach((perif) => {
  246. ypopsifioi[perif.id] = [];
  247. });
  248. }
  249.  
  250. async function addCandidates(perif)
  251. {
  252. if (ypopsifioi[perif].length > 0) {
  253. return;
  254. }
  255. await db.collection('candidates').doc(perif).collection('choices').get().then((querySnapshot) => {
  256. querySnapshot.forEach((doc) => {
  257. let id = doc.id;
  258. let candidate = doc.data();
  259. ypopsifioi[perif].push({'id': id.toString(), 'name': candidate.name, 'profession': candidate.profession || '', 'online_votable': candidate.online_votable});
  260. });
  261. });
  262. ypopsifioi[perif].sort((a, b) => {
  263. if (!a.online_votable) {
  264. return 1;
  265. } else if (!b.online_votable) {
  266. return -1;
  267. }
  268. return a.name.localeCompare(b.name);
  269. });
  270. }
  271.  
  272. async function enableHandlerUI()
  273. {
  274. const handlerlastonlineref = db.collection('centers').doc(auth.currentUser.uid).collection('private').doc('lastonline');
  275. handlerlastonlineref.set({lastonline: firebase.firestore.FieldValue.serverTimestamp()}).then(() => {
  276. if (handlerlastonlineinterval) {
  277. clearInterval(handlerlastonlineinterval);
  278. }
  279. handlerlastonlineinterval = setInterval(() => {
  280. handlerlastonlineref.set({lastonline: firebase.firestore.FieldValue.serverTimestamp()});
  281. }, 600_000);
  282. });
  283. if (handlerstatuslistener) {
  284. handlerstatuslistener(); // unsubscribes existing snapshot listener
  285. }
  286. handlerstatuslistener = db.collection('centers').doc(auth.currentUser.uid).onSnapshot((doc) => {
  287. const data = doc.data();
  288. if (data.disabled === true) {
  289. document.querySelector('.handler_disabled').classList.remove('hidden');
  290. } else {
  291. document.querySelector('.handler_disabled').classList.add('hidden');
  292. }
  293. });
  294. document.querySelector('.handler_container').classList.remove('hidden');
  295. if (votingstate.state == 'voting') {
  296. document.querySelector('.handler_search_form').addEventListener('submit', (e) => {
  297. e.preventDefault();
  298. let inputs = document.querySelectorAll('.handler_search_form input');
  299. // make sure at least one input is filled
  300. let filled = false;
  301. inputs.forEach((input) => {
  302. if (input.value) {
  303. filled = true;
  304. }
  305. });
  306. if (!filled) {
  307. alert('Συμπληρώστε τουλάχιστον ένα πεδίο');
  308. return;
  309. }
  310. showLoading();
  311. const votersref = db.collection('voters');
  312. // Add each input to the query, the field names are same as input name
  313. let query = votersref;
  314. // If partial matching is enabled, a composite index is
  315. // used and all conditions must be applied
  316. let partial = true;
  317. // Arrays to hold inputs that have specific filters and those that match everything
  318. let specificFilters = [];
  319. let matchAllFilters = [];
  320. // Separate inputs into specificFilters and
  321. // matchAllFilters. Specific filters are applied first
  322. // for performance reasons
  323. inputs.forEach((input) => {
  324. if (input.value) {
  325. specificFilters.push(input);
  326. } else {
  327. matchAllFilters.push(input);
  328. }
  329. });
  330. // Apply specific filters first
  331. specificFilters.forEach((input) => {
  332. let value = removeGreekAccentsAndUppercase(input.value);
  333. if (!partial)
  334. query = query.where(input.name, '==', value);
  335. else {
  336. query = query.where(input.name, '>=', value);
  337. query = query.where(input.name, '<', value + '\uf8ff');
  338. }
  339. });
  340. // Then, apply match-all filters, if partial is enabled
  341. if (partial)
  342. matchAllFilters.forEach((input) => {
  343. query = query.where(input.name, '>=', '');
  344. });
  345. query = query.limit(10);
  346. query.get().then((querySnapshot) => {
  347. stopLoading();
  348. if (querySnapshot.empty) {
  349. alert('Δεν βρέθηκαν αποτελέσματα');
  350. return;
  351. }
  352. if (querySnapshot.size >= 10) {
  353. alert('Βρέθηκαν πολλά αποτελέσματα, παρακαλώ κάντε πιο συγκεκριμένη την αναζήτηση');
  354. return;
  355. }
  356. let results = [];
  357. querySnapshot.forEach((doc) => {
  358. results.push(doc.data());
  359. });
  360. displaySearchResults(query, results);
  361. });
  362.  
  363. });
  364. }
  365. if (votingstate.state == 'counting') {
  366. const totalvotersref = db.collection('centers').doc(auth.currentUser.uid).collection('private').doc('totalvoters');
  367. totalvotersref.onSnapshot((doc) => {
  368. if (!doc.exists) {
  369. showSubmitTotalVotersButton();
  370. return;
  371. } else {
  372. const data = doc.data();
  373. // check if data has property disabled
  374. if (data.approved === true) {
  375. enableCountingUI();
  376. } else if (data.approved === false){
  377. alert('Το κέντρο έχει απενεργοποιηθεί, παρακαλώ επικοινωνήστε με τον υπεύθυνο');
  378. } else {
  379. return;
  380. }
  381. }
  382. let totalvoters = doc.data();
  383. let max_votes = perifereies.find((perif) => {
  384. return perif.id == userdbdata.perifereia_id;
  385. }).max_votes;
  386. votecountinginfo = {
  387. 'totalvoters': totalvoters.totalvoters,
  388. 'max_votes': max_votes * totalvoters.totalvoters,
  389. };
  390. document.querySelector('.handler_count_container .totalvoters span.totalvoters_count').textContent = votecountinginfo.totalvoters;
  391. document.querySelector('.handler_count_container .maxvotes span.maxvotes_count').textContent = votecountinginfo.max_votes;
  392. });
  393. const physicalvotesref = db.collection('centers').doc(auth.currentUser.uid).collection('private').doc('physicalvotes');
  394. physicalvotesref.onSnapshot((doc) => {
  395. if (!doc.exists) {
  396. return;
  397. }
  398. document.querySelectorAll('.c_syriza_eurovote .ypopsifioi .module').forEach((module) => {
  399. module.classList.add('hidden');
  400. });
  401. document.querySelectorAll('.c_syriza_eurovote .submodule').forEach((module) => {
  402. module.classList.add('hidden');
  403. });
  404. document.querySelector('.handler_container .votecompleted').classList.remove('hidden');
  405. });
  406. }
  407. }
  408.  
  409. function showSubmitTotalVotersButton()
  410. {
  411. const container = document.querySelector('.submittotalvoters');
  412. container.classList.remove('hidden');
  413. const submitbutton = container.querySelector('button');
  414. submitbutton.disabled = false;
  415. container.querySelector('form').addEventListener('submit', (e) => {
  416. e.preventDefault();
  417. submitbutton.disabled = true;
  418. const totalvoters = e.target.elements.totalvoters.value;
  419. if (!totalvoters || totalvoters < 0) {
  420. alert('Εισάγετε τον αριθμό των ψηφοφόρων');
  421. submitbutton.disabled = false;
  422. return;
  423. }
  424. const totalvotersref = db.collection('centers').doc(auth.currentUser.uid).collection('private').doc('totalvoters');
  425. totalvotersref.set({totalvoters: totalvoters}).then(() => {
  426. container.classList.add('hidden');
  427. }).catch((error) => {
  428. alert('Αποτυχία αποθήκευσης αριθμού ψηφοφόρων');
  429. submitbutton.disabled = false;
  430. console.error(error);
  431. });
  432. });
  433. }
  434.  
  435. async function enableCountingUI()
  436. {
  437. let container = document.querySelector('.handler_count_container');
  438. container.classList.remove('hidden');
  439. let perifid = userdbdata.perifereia_id;
  440. let candDiv = container.querySelector('.ypopsifioi .ypopsifioi_list');
  441. removeAllChildNodes(candDiv);
  442. candDiv.innerHTML = `
  443. <div class="c_container header">
  444. <div class="col">Υποψήφιος</div>
  445. <div class="col">Ψήφοι</div>
  446. </div>
  447. `;
  448. await addCandidates(perifid);
  449. ypopsifioi[perifid].forEach((candidate) => {
  450. let div = document.createElement('div');
  451. div.classList.add('candidate');
  452. div.classList.add('c_container');
  453. div.setAttribute('data-id', candidate.id);
  454. div.innerHTML = `
  455. <div class="details col">
  456. <div class="name">
  457. ${candidate.name}
  458. </div>
  459. <div class="profession">
  460. ${candidate.profession}
  461. </div>
  462. </div>
  463. <div class="col">
  464. <input data-forcandidate="${candidate.id}" type="number" value="0" name="votes"/>
  465. </div>
  466. </div>
  467. `;
  468. candDiv.appendChild(div);
  469. });
  470. container.querySelector('form.ypopsifioi_form').addEventListener('submit', handleVoteCountSubmit);
  471. }
  472.  
  473. function handleVoteCountSubmit(e)
  474. {
  475. e.preventDefault();
  476. const form = e.target;
  477. const inputs = form.elements;
  478. const votes = {};
  479. Array.from(inputs).forEach((input) => {
  480. if (input.name == 'votes') {
  481. votes[input.getAttribute('data-forcandidate')] = input.value;
  482. }
  483. });
  484. const totalvotes = Object.values(votes).reduce((acc, val) => {
  485. return acc + parseInt(val);
  486. }, 0);
  487. if (totalvotes > votecountinginfo.max_votes) {
  488. alert('Ο συνολικός αριθμός ψήφων υπερβαίνει το μέγιστο επιτρεπόμενο');
  489. return;
  490. }
  491. if (totalvotes < votecountinginfo.totalvoters) {
  492. alert('Ο συνολικός αριθμός ψήφων είναι μικρότερος από τον συνολικό αριθμό ψηφοφόρων');
  493. return;
  494. }
  495. const centercountingref = db.collection('centers').doc(auth.currentUser.uid).collection('private').doc('physicalvotes');
  496. centercountingref.set({physicalvotes: votes}).then(() => {
  497. alert('Οι ψήφοι καταμετρήθηκαν');
  498. }).catch((error) => {
  499. alert('Αποτυχία αποθήκευσης ψήφων');
  500. console.error(error);
  501. });
  502. }
  503.  
  504. async function enableAdminUI()
  505. {
  506. for (const [key, value] of Object.entries(admin_snapshotlisteners)) {
  507. value(); // Unsubscribe existing snapshot listeners
  508. }
  509. for (const [key, value] of Object.entries(admin_lastloginlisteners)) {
  510. value(); // Unsubscribe existing snapshot listeners
  511. }
  512. if (admin_ui_interval) {
  513. clearInterval(admin_ui_interval);
  514. }
  515. admin_ui_interval = setInterval(() => {
  516. if (admin_ui_has_updates) {
  517. trigger_adminCentersData();
  518. admin_ui_has_updates = false;
  519. }
  520. if (trigger_lastonline_queue.length > 0) {
  521. trigger_updateLastOnline(trigger_lastonline_queue);
  522. trigger_lastonline_queue = [];
  523. }
  524. if (Object.keys(trigger_autocounter_queue).length > 0) {
  525. trigger_updateAutocounter(trigger_autocounter_queue);
  526. trigger_autocounter_queue = {};
  527. }
  528. }, 1000);
  529. admin_snapshotlisteners = {};
  530. const admincontainer = document.querySelector('.c_syriza_eurovote .admin_container');
  531. admincontainer.classList.remove('hidden');
  532. admincontainer.querySelectorAll('.submodule').forEach((module) => {
  533. module.classList.remove('hidden');
  534. });
  535. const centers_basicref = db.collection('centers').limit(2);
  536. admin_snapshotlisteners.centers_basic = centers_basicref.onSnapshot((querySnapshot) => {
  537. querySnapshot.forEach((doc) => {
  538. admin_centersdata[doc.id] = doc.data();
  539. });
  540. admin_ui_has_updates = true;
  541. querySnapshot.docChanges().forEach((change) => {
  542. const doc = change.doc;
  543. if (!admin_lastloginlisteners[doc.id]) {
  544. admin_lastloginlisteners[doc.id] = doc.ref.collection('private').doc('lastonline').onSnapshot((lastondoc) => {
  545. if (!lastondoc.exists) {
  546. return;
  547. }
  548. trigger_lastonline_queue.push(
  549. {
  550. centerid: doc.id,
  551. lastonline: lastondoc.data().lastonline,
  552. }
  553. );
  554. });
  555. }
  556. if (!admin_voterstotallisteners[doc.id]) {
  557. admin_voterstotallisteners[doc.id] = doc.ref.collection('private').doc('autocount').onSnapshot((autocountdoc) => {
  558. if (!autocountdoc.exists) {
  559. return;
  560. }
  561. trigger_autocounter_queue[doc.id] = autocountdoc.data().autocounter;
  562. });
  563. }
  564. });
  565. });
  566. }
  567.  
  568. function trigger_adminCentersData()
  569. {
  570. const entriesdiv = document.querySelector('.admin_container .centers_table .entries');
  571. for (const [centerid, centerdata] of Object.entries(admin_centersdata)) {
  572. let exists = entriesdiv.querySelector(`.entry[data-id="${centerdata.id}"]`);
  573. if (exists) {
  574. const colbykey = {};
  575. exists.querySelectorAll('.col').forEach((col) => {
  576. colbykey[col.getAttribute('data-for')] = col;
  577. });
  578. colbykey['name'].textContent = centerdata.handler_uid;
  579. colbykey['disabled'].innerHTML = buildDisabledHTML(centerid, centerdata.disabled);
  580. } else {
  581. let div = document.createElement('div');
  582. div.classList.add('entry');
  583. div.classList.add('c_container');
  584. div.setAttribute('data-id', centerid);
  585. disabledhtml = buildDisabledHTML(centerid, centerdata.disabled);
  586. div.innerHTML = `
  587. <div class="col" data-for="info" onclick="showCenterInfoPopup(this);">
  588. <span class="fa fa-info-circle"></span>
  589. </div>
  590. <div class="col" data-for="name">${centerdata.handler_uid}</div>
  591. <div class="col" data-for="lastlogin"><div class="status-lastlogin offline" title="Δεν έχει συνδεθεί"></div></div>
  592. <div class="col" data-for="disabled">${disabledhtml}</div>
  593. <div class="col" data-for="markedvoters">0</div>
  594. <div class="col" data-for="members"></div>
  595. <div class="col" data-for="new_members"></div>
  596. <div class="col" data-for="friends"></div>
  597. `;
  598. entriesdiv.appendChild(div);
  599. }
  600. }
  601. }
  602.  
  603. function trigger_updateLastOnline(queue)
  604. {
  605. queue.forEach((center) => {
  606. const entry = document.querySelector(`.admin_container .centers_table .entries .entry[data-id="${center.centerid}"]`);
  607. if (entry) {
  608. const colentry = entry.querySelector('.col[data-for="lastlogin"] .status-lastlogin');
  609. const lastonline = center.lastonline.toDate();
  610. const diff = Date.now() - lastonline;
  611. classname = 'online';
  612. if (diff > 600_000) {
  613. classname = 'inactive';
  614. }
  615. if (lastonlineTimeouts[center.centerid]) {
  616. clearTimeout(lastonlineTimeouts[center.centerid]);
  617. }
  618. console.log('online');
  619. if (classname == 'online') {
  620. lastonlineTimeouts[center.centerid] = setTimeout(() => {
  621. console.log('inactive');
  622. colentry.classList.remove('online');
  623. colentry.classList.add('inactive');
  624. }, 601_000 - diff);
  625. }
  626. colentry.classList.remove(['online', 'inactive', 'offline']);
  627. colentry.classList.add(classname);
  628. colentry.setAttribute('title', `Τελευταία σύνδεση: ${lastonline.toLocaleString('el-GR', timeoptions)}`);
  629. if (!onlinecenters.includes(center.centerid) && classname == 'online') {
  630. onlinecenters.push(center.centerid);
  631. } else if (onlinecenters.includes(center.centerid) && classname != 'online') {
  632. onlinecenters = onlinecenters.filter((id) => {
  633. return id != center.centerid;
  634. });
  635. }
  636. const onlinediv = document.querySelector('.admin_container .centersonline');
  637. const connectedel = onlinediv.querySelector('.connectedcenters');
  638. const totalcentersel = onlinediv.querySelector('.totalcenters');
  639. const connectedpercentel = onlinediv.querySelector('.connectedpercentage');
  640. connectedel.textContent = onlinecenters.length;
  641. totalcentersel.textContent = Object.keys(admin_centersdata).length;
  642. connectedpercentel.textContent = `(${Math.round((onlinecenters.length / Object.keys(admin_centersdata).length) * 100)}%)`;
  643. }
  644. });
  645. }
  646.  
  647. function trigger_updateAutocounter(queue)
  648. {
  649. for (const [centerid, autocount] of Object.entries(queue)) {
  650. const entry = document.querySelector(`.admin_container .centers_table .entries .entry[data-id="${centerid}"]`);
  651. if (entry) {
  652. const colentry = entry.querySelector('.col[data-for="markedvoters"]');
  653. colentry.textContent = autocount;
  654. calculateTotalVotersAndUpdateUI();
  655. }
  656. }
  657. }
  658.  
  659. function calculateTotalVotersAndUpdateUI()
  660. {
  661. const container = document.querySelector('.admin_container');
  662. const entries = container.querySelectorAll('.centers_table .entries .entry .col[data-for="markedvoters"]');
  663. const totalvotersel = container.querySelector('.centers_totalvoters .count');
  664. let totalvoters = 0;
  665. entries.forEach((entry) => {
  666. totalvoters += parseInt(entry.textContent);
  667. });
  668. totalvotersel.textContent = totalvoters;
  669. }
  670.  
  671. function buildDisabledHTML(centerid, disabled)
  672. {
  673. return '';
  674. }
  675.  
  676. function showCenterInfoPopup(el)
  677. {
  678.  
  679. }
  680.  
  681. async function enableVoterUI()
  682. {
  683. if (votingstate.state == 'voting') {
  684. document.querySelector('.onlinevoting').classList.remove('hidden');
  685. } else if (votingstate.state != 'pre_start') {
  686. document.querySelector('.voted').classList.remove('hidden');
  687. }
  688. if (voterhasran)
  689. return;
  690. voterhasran = true;
  691. let voterref = db.collection('voters').doc(auth.currentUser.uid);
  692. if (snapshotvoter) {
  693. snapshotvoter(); // unsubscribes existing snapshot listener
  694. }
  695. snapshotvoter = voterref.onSnapshot(async (doc) => {
  696. if (doc.exists) {
  697. stopLoading();
  698. let voterinfo = doc.data();
  699. if (voterinfo.voted > 0) {
  700. document.querySelector('.onlinevoting').classList.add('hidden');
  701. let thediv = document.querySelector('.voted');
  702. thediv.classList.remove('hidden');
  703. const perifereianame = perifereies.find((perif) => {
  704. return perif.id == voterinfo.voted_for;
  705. }).name;
  706. thediv.innerHTML = `
  707. <div>
  708. Έχετε ψηφίσει για την περιφέρεια <span class="perifereia">${perifereianame}</span> στις <span class="voted_at">${voterinfo.voted_at.toDate().toLocaleString('el-gr', timeoptions)}</span>
  709. </div>
  710. `;
  711. }
  712. }
  713. });
  714. let perifSelect = document.querySelector('.onlinevoting .select_perifereia select');
  715. let theform = document.querySelector('.onlinevoting form');
  716. let remainingel = document.querySelector('.onlinevoting .ypopsifioi .remaining-count');
  717. perifereies.forEach((perif) => {
  718. let option = document.createElement('option');
  719. option.value = perif.id;
  720. option.text = perif.name;
  721. perifSelect.add(option);
  722. });
  723. perifSelect.addEventListener('change', async (e) => {
  724. let perif = e.target.value;
  725. let candDiv = document.querySelector('.onlinevoting .ypopsifioi .ypopsifioi_list');
  726. removeAllChildNodes(candDiv);
  727. if (perif == '0') {
  728. document.querySelector('.onlinevoting .ypopsifioi').classList.add('hidden');
  729. return;
  730. }
  731. document.querySelector('.onlinevoting .ypopsifioi').classList.remove('hidden');
  732. await addCandidates(perif);
  733. // multiple candidates can be selected
  734. ypopsifioi[perif].forEach((candidate) => {
  735. if (candidate.online_votable == false) {
  736. return;
  737. }
  738. let div = document.createElement('div');
  739. div.classList.add('candidate');
  740. div.setAttribute('data-id', candidate.id);
  741. div.innerHTML = `
  742. <label>
  743. <input type="checkbox" name="candidate" value="${candidate.id}">
  744. <span class="name">${candidate.name}</span>
  745. <span>${candidate.profession}</span>
  746. </label>`;
  747. candDiv.appendChild(div);
  748. });
  749. let checkboxes = document.querySelectorAll('.onlinevoting .ypopsifioi .candidate input');
  750. let remainingparentel = document.querySelector('.onlinevoting .ypopsifioi .remaining');
  751. let perifinfo = perifereies.find((perif) => {
  752. return perif.id == perifSelect.value;
  753. });
  754. remainingel.textContent = perifinfo.max_votes;
  755. checkboxes.forEach((input) => {
  756. input.addEventListener('change', (e) => {
  757. let checkedCount = document.querySelectorAll('.onlinevoting .ypopsifioi .candidate input:checked').length;
  758. remainingparentel.classList.remove('hidden');
  759. let remainingcount = perifinfo.max_votes - checkedCount;
  760. remainingel.textContent = remainingcount;
  761. if (checkedCount >= perifinfo.max_votes) {
  762. checkboxes.forEach((input) => {
  763. if (!input.checked) {
  764. input.disabled = true;
  765. }
  766. });
  767. } else {
  768. checkboxes.forEach((input) => {
  769. input.disabled = false;
  770. });
  771. }
  772. });
  773. });
  774. });
  775. theform.addEventListener('submit', async (e) => {
  776. e.preventDefault();
  777. let perif = perifSelect.value;
  778. let perifinfo = perifereies.find((perif) => {
  779. return perif.id == perifSelect.value;
  780. });
  781. let selectedCandidates = [];
  782. let checkboxes = e.target.elements;
  783. Array.from(checkboxes).forEach((element) => {
  784. if (element.checked) {
  785. selectedCandidates.push(element.value);
  786. }
  787. });
  788. if (selectedCandidates.length == 0) {
  789. alert('Επιλέξτε τουλάχιστον έναν υποψήφιο');
  790. return;
  791. } else if (selectedCandidates.length > perifinfo.max_votes) {
  792. alert('Επιλέξτε μέχρι ' + perifinfo.max_votes + ' υποψήφιους');
  793. return;
  794. }
  795. let timestamp = firebase.firestore.FieldValue.serverTimestamp();
  796. if (!sendrequestforsms(timestamp)) {
  797. return;
  798. }
  799. const smspasscode = await showSMSPasscodePopup();
  800. if (smspasscode == 'cancel') {
  801. return;
  802. }
  803. db.collection('votes').doc(auth.currentUser.uid).set({
  804. votes: selectedCandidates,
  805. voted_at: timestamp,
  806. passcode: smspasscode,
  807. voted_for: perif,
  808. vote_type: 2,
  809. }).then(() => {
  810. // this will be done by cloud function
  811. //voterinforef.set({voted: 2, voted_at: timestamp, voted_for: perifinfo, votes:});
  812. // disable submit button until ui update
  813. document.querySelector('.onlinevoting form button').disabled = true;
  814. showLoading();
  815. alert('Επιτυχής ψηφοφορία');
  816. }).catch((error) => {
  817. alert('Αποτυχία ψηφοφορίας. Ελέγξτε ότι βάλατε σωστά τον κωδικό που λάβατε με SMS.'); // FIXME synexise na trexeis to popup
  818. throw error;
  819. });
  820. });
  821. }
  822.  
  823. function enableLoginUI()
  824. {
  825. document.querySelectorAll('.c_syriza_eurovote .module').forEach((module) => {
  826. module.classList.add('hidden');
  827. });
  828. document.querySelector('.manuallogin').classList.remove('hidden');
  829. }
  830.  
  831. function disableLoginUI()
  832. {
  833. document.querySelector('.manuallogin').classList.add('hidden');
  834. firebaseLogoutButton.classList.remove('hidden');
  835. }
  836.  
  837. function updateSearchResultsHTML(results)
  838. {
  839. let container = document.querySelector('.handler_search_results .search_results');
  840. removeAllChildNodes(container);
  841. container.innerHTML = `
  842. <div class="c_container header">
  843. <div class="voted col">Κατάσταση</div>
  844. <div class="details col">Στοιχεία</div>
  845. <div class="dateofbirth col">Ημερομηνία Γέννησης</div>
  846. </div>
  847. `;
  848. results.forEach((result) => {
  849. let votervotingstate = 'Δεν ψήφισε';
  850. if (!result.voted) {
  851. result.voted = 0;
  852. }
  853. if (result.voted == 1) {
  854. votervotingstate = 'Ψήφισε με Φυσική Παρουσία στο ' + result.centerId;
  855. } else if (result.voted == 2) {
  856. votervotingstate = 'Ψήφισε Διαδικτυακά';
  857. }
  858. html = `
  859. <div data-voted="${result.voted}" data-voting_number="${result.voting_number}" class="c_container result votestate_${result.voted}">
  860. <div class="col extras">
  861. <input class="voted-checkbox" type="checkbox" name="voted" value="1" ${result.voted > 0 ? 'checked' : ''} ${result.voted > 0 ? 'disabled' : ''}>
  862. <span>${votervotingstate}</span>
  863. </div>
  864. <div class="col">
  865. <div class="basic">${result.first_name} ${result.last_name} του ${result.father_first_name} και της ${result.mother_first_name}</div>
  866. </div>
  867. <div class="col date">${result.birth_day}/${result.birth_month}/${result.birth_year}</div>
  868. </div>`;
  869. container.insertAdjacentHTML('beforeend', html);
  870. });
  871. container.querySelectorAll('.result')[0].scrollIntoView({behavior: 'smooth'});
  872. container.querySelectorAll('.result').forEach((result) => {
  873. let voting_number = result.getAttribute('data-voting_number');
  874. let voted = result.getAttribute('data-voted');
  875. result.querySelector('input.voted-checkbox').addEventListener('change', async (e) => {
  876. if (voted > 0)
  877. return;
  878. if (!e.target.checked)
  879. return;
  880. // show a popup form with 3 choices for voter type
  881. // wait for user to select one
  882. // then update the voter with the selected type
  883. const votertype = await showVoterTypePopup();
  884. if (votertype == 'cancel') {
  885. e.target.checked = false;
  886. return;
  887. }
  888.  
  889. if (confirm('Να επισημανθεί ως ψηφίσαντας;')) {
  890. let timestamp = firebase.firestore.FieldValue.serverTimestamp();
  891. let voterref = db.collection('voters').doc(voting_number);
  892. const datatosend = {
  893. voted_at: timestamp,
  894. vote_type: 1,
  895. member_type: votertype,
  896. voted_for: userdbdata.perifereia_id,
  897. centerId: auth.currentUser.uid,
  898. };
  899. showLoading();
  900. db.collection('votes').doc(voting_number).set(datatosend).then(() => {
  901. // empty all search inputs
  902. stopLoading();
  903. document.querySelectorAll('.handler_search_form input').forEach((input) => {
  904. input.value = '';
  905. });
  906. }).catch((error) => {
  907. stopLoading();
  908. alert('Αποτυχία επισήμανσης ψηφοφόρου');
  909. console.error(error);
  910. });
  911. } else {
  912. e.target.checked = false;
  913. }
  914. });
  915. });
  916. }
  917.  
  918. function displaySearchResults(query, results)
  919. {
  920. let container = document.querySelector('.handler_search_results .search_results');
  921. updateSearchResultsHTML(results);
  922. querylisteners.forEach((listener) => {
  923. listener();
  924. });
  925. container.querySelectorAll('.result').forEach((result) => {
  926. let voting_number = result.getAttribute('data-voting_number');
  927. let voted = result.getAttribute('data-voted');
  928. querylisteners.push(db.collection('voters').doc(voting_number).onSnapshot((doc) => {
  929. let newresult = doc.data();
  930. let voterid = doc.id;
  931. // replace the results entry with the new data
  932. results = results.map((oldresult) => {
  933. if (oldresult.voting_number == voterid) {
  934. return newresult;
  935. }
  936. return oldresult;
  937. });
  938. updateSearchResultsHTML(results);
  939. }));
  940. });
  941. }
  942.  
  943. function showVoterTypePopup()
  944. {
  945. return new Promise((resolve, reject) => {
  946. let popup = document.querySelector('.votertypepopup');
  947. function choiceHandler(choice) {
  948. return () => {
  949. popup.classList.add('hidden');
  950. resolve(choice);
  951. };
  952. }
  953. document.querySelectorAll('.votertypepopup button').forEach((button) => {
  954. button.onclick = choiceHandler(button.dataset.choice);
  955. });
  956. popup.classList.remove('hidden');
  957. });
  958. }
  959.  
  960. function showSMSPasscodePopup()
  961. {
  962. return new Promise((resolve, reject) => {
  963. let popup = document.querySelector('.modal.smspasscode');
  964. popup.querySelector('.mobileinfo').textContent = 'στον αριθμό '+userdbdata.mobile;
  965. if (smsbuttontimeinterval) {
  966. clearInterval(smsbuttontimeinterval);
  967. }
  968. addSMSButtonInterval();
  969. let inputs = popup.querySelectorAll('input');
  970. Array.from(inputs)[0].focus();
  971. let passcode = '';
  972. function inputHandler(e) {
  973. passcode = '';
  974. inputs.forEach((input) => {
  975. passcode += input.value;
  976. });
  977. if (passcode.length == inputs.length) {
  978. popup.classList.add('hidden');
  979. inputs.forEach((input) => {
  980. input.removeEventListener('input', inputHandler);
  981. });
  982. clearInterval(smsbuttontimeinterval);
  983. resolve(passcode);
  984. }
  985. if (e.target.nextElementSibling) {
  986. e.target.nextElementSibling.focus();
  987. }
  988. }
  989. inputs.forEach((input) => {
  990. input.value = '';
  991. input.addEventListener('input', inputHandler);
  992. });
  993. popup.querySelector('.close').addEventListener('click', () => {
  994. popup.classList.add('hidden');
  995. resolve('cancel');
  996. });
  997. popup.classList.remove('hidden');
  998. });
  999. }
  1000.  
  1001. async function sendrequestforsms(timestamp) {
  1002. addSMSButtonInterval();
  1003. let smsauthrequestref = db.collection('sms-auth-requests').doc(auth.currentUser.uid);
  1004. // generate a random id
  1005. const id = Math.random().toString(36).substr(2, 9);
  1006. const authreqdata = {
  1007. 'timestamp': timestamp,
  1008. 'id': id,
  1009. 'mobile': userdbdata.mobile,
  1010. };
  1011. let success = true;
  1012. await smsauthrequestref.set(authreqdata).then(() => {
  1013.  
  1014. }).catch((error) => {
  1015. success = false;
  1016. alert('Αποτυχία ψηφοφορίας');
  1017. });
  1018. return success;
  1019. }
  1020.  
  1021. function addSMSButtonInterval() {
  1022. const starttime = performance.now();
  1023. const popup = document.querySelector('.modal.smspasscode');
  1024. popup.querySelector('button.resend').disabled = true;
  1025. smsbuttontimeinterval = setInterval(() => {
  1026. const now = performance.now();
  1027. const elapsed = now - starttime;
  1028. const countdownel = popup.querySelector('.countdown')
  1029. countdownel.textContent = '(' + Math.ceil((120000 - elapsed) / 1000) + ')';
  1030. if (elapsed > 120000) {
  1031. clearInterval(smsbuttontimeinterval);
  1032. countdownel.textContent = '';
  1033. popup.querySelector('button.resend').disabled = false;
  1034. }
  1035. }, 250);
  1036. }
  1037.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement