EventoDetalleGrupo.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. import React, { useEffect, useState } from "react";
  2. import {
  3. Col,
  4. Row,
  5. Form,
  6. Input,
  7. Modal,
  8. Card,
  9. Avatar,
  10. Select,
  11. Button,
  12. DatePicker,
  13. message,
  14. Spin,
  15. } from "antd";
  16. import {
  17. EditOutlined,
  18. DeliveredProcedureOutlined,
  19. BarChartOutlined,
  20. BellFilled,
  21. } from "@ant-design/icons";
  22. import { useHistory } from "react-router-dom";
  23. import {
  24. addDoc,
  25. collection,
  26. doc,
  27. getDoc,
  28. updateDoc,
  29. query as firebaseQuery,
  30. getDocs,
  31. where,
  32. orderBy,
  33. } from "firebase/firestore";
  34. import { useQuery, useAuth } from "../../../hooks";
  35. import { ViewLoading } from "../../../components";
  36. import excelCondensado from "../../../services/excelCondensado";
  37. import { DefaultLayout } from "../../../components/layouts";
  38. import { firestore } from "../../../services/firebase";
  39. import locale from "antd/es/date-picker/locale/es_ES";
  40. import moment from "moment";
  41. import "moment/locale/es-mx";
  42. const { RangePicker } = DatePicker;
  43. const { REACT_APP_WEB_URL } = process.env;
  44. const EventoDetalleGrupo = () => {
  45. const query = useQuery();
  46. const history = useHistory();
  47. const { session } = useAuth();
  48. const { TextArea } = Input;
  49. const { Meta } = Card;
  50. const { Option } = Select;
  51. const id = query.get("id");
  52. const tag = query.get("tag");
  53. const editando = !!id;
  54. const urlweb = "/administracion/eventos";
  55. const [form] = Form.useForm();
  56. const [formProcesar] = Form.useForm();
  57. const [grupoEvento, setGrupoEvento] = useState([]);
  58. const [cargandoEvento, setCargandoEvento] = useState(true);
  59. const [eventos, setEventos] = useState([]);
  60. const [visible, setVisible] = useState(false);
  61. const [visibleReporte, setVisibleReporte] = useState(false);
  62. const [procesando, setProcesando] = useState(false);
  63. const [evento, setEvento] = useState(null);
  64. const [textAreaData, setTextAreaData] = useState("");
  65. const [previaImg, setPreviaImg] = useState(null);
  66. const [guardando, setGuardando] = useState({
  67. nuevo: false,
  68. faltante: false,
  69. felicitacion: false,
  70. condensado: false,
  71. });
  72. const acciones = [
  73. { id: 0, value: "Ver", label: "Ver", icon: "👁" },
  74. { id: 1, value: "Me gusta", label: "Me gusta", icon: "👍" },
  75. { id: 2, value: "Compartir", label: "Compartir", icon: "🔄" },
  76. ];
  77. const buttons = [
  78. {
  79. text: "Notificar nuevo evento",
  80. to: () => notificarNuevoEvento(),
  81. icon: <BellFilled />,
  82. props: {
  83. disabled: false,
  84. style: { background: "#2196f3", color: "#fff" },
  85. type: "default",
  86. size: "large",
  87. loading: guardando.nuevo,
  88. },
  89. },
  90. {
  91. text: "Enviar faltantes",
  92. to: () => enviarRecordatorios(),
  93. icon: <BellFilled />,
  94. props: {
  95. disabled: false,
  96. type: "danger",
  97. size: "large",
  98. loading: guardando.faltante,
  99. },
  100. },
  101. {
  102. text: "Enviar felicitación",
  103. to: () => enviarFelicitacion(),
  104. icon: <BellFilled />,
  105. props: {
  106. disabled: false,
  107. type: "primary",
  108. size: "large",
  109. loading: guardando.felicitacion,
  110. },
  111. },
  112. {
  113. text: "Reporte condensado",
  114. to: () => setVisibleReporte(true),
  115. icon: <BarChartOutlined />,
  116. props: { disabled: false, type: "dashed", size: "large" },
  117. },
  118. ];
  119. function b64_to_utf8(str) {
  120. return decodeURIComponent(escape(window.atob(str)));
  121. }
  122. const guardarRegistro = async () => {
  123. try {
  124. const json = JSON.parse(textAreaData);
  125. const accion = b64_to_utf8(json.accion).split(".");
  126. const lista = json[json.tipo][accion[0]];
  127. const body = {
  128. json: textAreaData,
  129. accion: accion,
  130. lista: lista,
  131. timestamp: new Date(),
  132. evento: evento,
  133. usuario: {
  134. uid: session?.uid,
  135. email: session?.email,
  136. displayName: session?.displayName,
  137. },
  138. };
  139. await addDoc(collection(firestore, "procesos"), body);
  140. } catch (error) {
  141. console.log("error al guardar registro: ", error);
  142. }
  143. };
  144. const procesarUsuarios = async () => {
  145. try {
  146. setProcesando(true);
  147. let usuarios = await obtenerUsuariosGrupo();
  148. const json = JSON.parse(textAreaData);
  149. const accion = b64_to_utf8(json.accion).split(".");
  150. const lista = json[json.tipo][accion[0]];
  151. if (json?.tipo !== evento?.redSocial?.toLowerCase()) {
  152. message.warning({
  153. content: "La red social es incorrecta",
  154. });
  155. return;
  156. }
  157. let resultado = { ...evento.resultado };
  158. for (let u = 0; u < usuarios?.length; u++) {
  159. let usuarioTipo = usuarios[u][json.tipo]?.toLowerCase();
  160. if (resultado[usuarios[u]?.uid] === undefined) {
  161. resultado[usuarios[u]?.uid] = [];
  162. }
  163. if (usuarioTipo?.toLowerCase()) {
  164. for (let i = 0; i < lista.length; i++) {
  165. const nick = lista[i]?.replace("@", "").toLowerCase();
  166. if (nick?.includes(usuarios[u][json.tipo])) {
  167. if (!resultado[usuarios[u]?.uid].includes(accion[0])) {
  168. resultado[usuarios[u]?.uid].push(accion[0]);
  169. }
  170. }
  171. }
  172. }
  173. }
  174. // Hay indices que salen en undefined y salió error al guardar en firebase
  175. try {
  176. resultado = JSON.parse(JSON.stringify(resultado));
  177. } catch (e) { }
  178. // Sale un error en firebase de que no puede guardar información en undefined (no se encontró dónde)
  179. try {
  180. usuarios = JSON.parse(JSON.stringify(usuarios));
  181. } catch (e) { }
  182. let body = {
  183. usuarios,
  184. resultado,
  185. };
  186. await updateDoc(doc(firestore, "eventos", evento?.id), body);
  187. history.push(
  188. urlweb +
  189. "/resultado-grupo-evento?id=" +
  190. evento?.id +
  191. "&tag=" +
  192. evento?.tag,
  193. );
  194. setVisible(false);
  195. } catch (error) {
  196. console.log("Error al procesar: ", error);
  197. message.warning({
  198. content: "Atención: hubo un problema al procesar la información",
  199. });
  200. } finally {
  201. setProcesando(false);
  202. }
  203. };
  204. const obtenerUsuariosGrupo = async () => {
  205. const _grupos = grupoEvento?.grupos;
  206. // Verificar que el evento no tenga grupos
  207. if (_grupos?.length === 0) {
  208. message.warning({
  209. content: "Atención: el evento no tiene grupos.",
  210. });
  211. return;
  212. }
  213. try {
  214. const q = firebaseQuery(
  215. collection(firestore, "usuarios"),
  216. where("grupos", "array-contains-any", _grupos),
  217. );
  218. const querySnapshot = await getDocs(q);
  219. const docs = [];
  220. querySnapshot.forEach((doc) => {
  221. const data = doc.data();
  222. docs.push({
  223. grupos: data?.grupos,
  224. facebook: data?.facebook?.toLowerCase().trim(),
  225. twitterVerificado: data?.twitterVerificado ? true : false,
  226. facebookVerificado: data?.facebookVerificado ? true : false,
  227. instagramVerificado: data?.instagramVerificado ? true : false,
  228. twitter: data?.twitter?.toLowerCase().trim(),
  229. instagram: data?.instagram?.toLowerCase().trim(),
  230. telefono: data?.telefono,
  231. verificado: data?.verificado,
  232. nombre: data?.nombre,
  233. uid: data?.uid,
  234. id: doc.id,
  235. });
  236. });
  237. return docs;
  238. } catch (error) {
  239. console.log("error al obtener grupos: ", error);
  240. }
  241. };
  242. const abrirPublicacion = (e) => {
  243. window.open(e?.url, "_blank", "noopener,noreferrer");
  244. };
  245. const obtenerEnlaceYager = (e) => {
  246. const url = REACT_APP_WEB_URL + "evento-laud?id=" + e?.id;
  247. navigator.clipboard.writeText(url);
  248. message.info({
  249. content: "Se ha copiado el enlace al portapapeles.",
  250. });
  251. };
  252. const notificarNuevoEvento = async () => {
  253. try {
  254. setGuardando({ ...guardando, nuevo: true });
  255. const _usuarios = grupoEvento?.usuarios;
  256. const _redesEvento = grupoEvento?.redes;
  257. const plantilla = "nuevo_evento";
  258. if (_usuarios?.length < 1) {
  259. message.info("Este evento no ha sido procesado aún.");
  260. return;
  261. }
  262. const arrUsuarios = [];
  263. const eventoFb = _redesEvento.find((i) => i?.redSocial === "Facebook")
  264. ? true
  265. : false;
  266. const eventoIg = _redesEvento.find((i) => i?.redSocial === "Instagram")
  267. ? true
  268. : false;
  269. const eventoTw = _redesEvento.find((i) => i?.redSocial === "Twitter")
  270. ? true
  271. : false;
  272. const eventoTt = _redesEvento.find((i) => i?.redSocial === "TikTok")
  273. ? true
  274. : false;
  275. _usuarios?.forEach((u) => {
  276. const uid = u?.uid;
  277. let tieneFb = false;
  278. let tieneIg = false;
  279. let tieneTw = false;
  280. if (u?.facebookVerificado) tieneFb = true;
  281. if (u?.instagramVerificado) tieneIg = true;
  282. if (u?.twitterVerificado) tieneTw = true;
  283. const usuario = {
  284. uid,
  285. nombre: u?.nombre,
  286. telefono: u?.telefono,
  287. fb: eventoFb && tieneFb,
  288. ig: eventoIg && tieneIg,
  289. tw: eventoTw && tieneTw,
  290. tt: eventoTt /* && tieneTw */,
  291. };
  292. arrUsuarios.push(usuario);
  293. });
  294. const body = {
  295. idEvento: id,
  296. plantilla: plantilla,
  297. titulo: grupoEvento?.nombre,
  298. estatus: "Nuevo",
  299. enviado: null,
  300. fecha: new Date(),
  301. url: eventos.map((e) => `${REACT_APP_WEB_URL}evento-laud?id=${e?.id}`),
  302. urlFB: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Facebook")?.id}`,
  303. urlIG: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Instagram")?.id}`,
  304. urlTW: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Twitter")?.id}`,
  305. urlTT: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "TikTok")?.id}`,
  306. usuarios: arrUsuarios,
  307. };
  308. if (!eventoFb) {
  309. body.urlFB = "";
  310. }
  311. if (!eventoIg) {
  312. body.urlIG = "";
  313. }
  314. if (!eventoTw) {
  315. body.urlTW = "";
  316. }
  317. if (!eventoTt) {
  318. body.urlTT = "";
  319. }
  320. // Buscamos si existe un registro con el ID del evento y la misma plantilla
  321. const q = firebaseQuery(
  322. collection(firestore, "notificaciones"),
  323. where("idEvento", "==", id),
  324. );
  325. const querySnapshot = await getDocs(q);
  326. let c = 0;
  327. querySnapshot.forEach((doc) => {
  328. if (doc.data().plantilla === plantilla) {
  329. c++;
  330. }
  331. });
  332. if (c > 0) {
  333. message.info("Ya hay un recordatorio de este evento.");
  334. return;
  335. }
  336. await addDoc(collection(firestore, "notificaciones"), body);
  337. message.success("Recordatorio enviado.");
  338. } catch (error) {
  339. message.error("Error al enviar la notificacion");
  340. console.log("error al notificar: ", error);
  341. } finally {
  342. setGuardando({ ...guardando, nuevo: false });
  343. }
  344. };
  345. const enviarRecordatorios = async () => {
  346. try {
  347. setGuardando({ ...guardando, faltante: true });
  348. const _usuarios = grupoEvento?.usuarios;
  349. const plantilla = "recordatorio";
  350. if (_usuarios?.length < 1) {
  351. message.info("Este evento no ha sido procesado aún.");
  352. return;
  353. }
  354. let eventoFb = null;
  355. let eventoTw = null;
  356. let eventoIg = null;
  357. let eventoTt = null;
  358. for (let i = 0; i < eventos.length; i++) {
  359. if (eventos[i].redSocial === "Facebook") {
  360. eventoFb = eventos[i];
  361. } else if (eventos[i].redSocial === "Instagram") {
  362. eventoIg = eventos[i];
  363. } else if (eventos[i].redSocial === "Twitter") {
  364. eventoTw = eventos[i];
  365. } else if (eventos[i].redSocial === "TikTok") {
  366. eventoTt = eventos[i];
  367. }
  368. }
  369. const arrUsuarios = [];
  370. _usuarios?.forEach((u) => {
  371. const uid = u?.uid;
  372. let tieneFb = false;
  373. let tieneIg = false;
  374. let tieneTw = false;
  375. if (u?.facebookVerificado) {
  376. tieneFb = true;
  377. }
  378. if (u?.instagramVerificado) {
  379. tieneIg = true;
  380. }
  381. if (u?.twitterVerificado) {
  382. tieneTw = true;
  383. }
  384. const usuario = {
  385. uid,
  386. nombre: u?.nombre,
  387. telefono: u?.telefono,
  388. fb: tieneFb && eventoFb?.resultado[uid]?.length === 0,
  389. ig: tieneIg && eventoIg?.resultado[uid]?.length === 0,
  390. tw: tieneTw && eventoTw?.resultado[uid]?.length === 0,
  391. tt: /* tieneTw && */eventoTt?.resultado[uid]?.length === 0,
  392. };
  393. /* if (!usuario.fb && !usuario.ig && !usuario.tw) {
  394. return true;
  395. } */
  396. arrUsuarios.push(usuario);
  397. });
  398. const body = {
  399. idEvento: id,
  400. plantilla: plantilla,
  401. titulo: grupoEvento?.nombre,
  402. estatus: "Nuevo",
  403. enviado: null,
  404. fecha: new Date(),
  405. urlFB: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Facebook")?.id}`,
  406. urlIG: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Instagram")?.id}`,
  407. urlTW: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Twitter")?.id}`,
  408. urlTT: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "TikTok")?.id}`,
  409. usuarios: arrUsuarios,
  410. };
  411. if (!eventoFb) {
  412. body.urlFB = "";
  413. }
  414. if (!eventoIg) {
  415. body.urlIG = "";
  416. }
  417. if (!eventoTw) {
  418. body.urlTW = "";
  419. }
  420. if (!eventoTt) {
  421. body.urlTT = "";
  422. }
  423. // Buscamos si existe un registro con el ID del evento y la misma plantilla
  424. const q = firebaseQuery(
  425. collection(firestore, "notificaciones"),
  426. where("idEvento", "==", id),
  427. );
  428. const querySnapshot = await getDocs(q);
  429. let c = 0;
  430. querySnapshot.forEach((doc) => {
  431. if (doc.data().plantilla === plantilla) {
  432. c++;
  433. }
  434. });
  435. if (c > 0) {
  436. message.info("Ya hay un recordatorio de este evento.");
  437. return;
  438. }
  439. await addDoc(collection(firestore, "notificaciones"), body);
  440. message.success("Recordatorio guardado.");
  441. } catch (error) {
  442. message.error("Error al enviar notifiación.");
  443. console.log("error al enviar notifiacion: ", error);
  444. } finally {
  445. setGuardando({ ...guardando, faltante: false });
  446. }
  447. };
  448. const enviarFelicitacion = async () => {
  449. try {
  450. setGuardando({ ...guardando, felicitacion: true });
  451. const _usuarios = grupoEvento.usuarios;
  452. const plantilla = "evento_gracias";
  453. if (_usuarios?.length < 1) {
  454. message.info("Este evento no ha sido procesaod aún.");
  455. return;
  456. }
  457. let eventoFb = null;
  458. let eventoTw = null;
  459. let eventoIg = null;
  460. let eventoTt = null;
  461. for (let i = 0; i < eventos.length; i++) {
  462. if (eventos[i]?.redSocial === "Facebook") {
  463. eventoFb = eventos[i];
  464. } else if (eventos[i]?.redSocial === "Instagram") {
  465. eventoIg = eventos[i];
  466. } else if (eventos[i]?.redSocial === "Twitter") {
  467. eventoTw = eventos[i];
  468. } else if (eventos[i]?.redSocial === "TikTok") {
  469. eventoTt = eventos[i];
  470. }
  471. }
  472. const arrUsuarios = [];
  473. _usuarios?.forEach((u) => {
  474. const uid = u?.uid;
  475. let tieneFb = false;
  476. let tieneIg = false;
  477. let tieneTw = false;
  478. let cr = 0;
  479. if (u?.facebookVerificado) {
  480. tieneFb = true;
  481. cr = cr + 1;
  482. }
  483. if (u?.instagramVerificado) {
  484. tieneIg = true;
  485. cr = cr + 1;
  486. }
  487. if (u?.twitterVerificado) {
  488. tieneTw = true;
  489. cr = cr + 1;
  490. }
  491. const res =
  492. eventoFb?.resultado[uid].length +
  493. eventoIg?.resultado[uid].length +
  494. eventoTw?.resultado[uid].length +
  495. eventoTt?.resultado[uid].length;
  496. const usuario = {
  497. uid,
  498. nombre: u?.nombre,
  499. telefono: u?.telefono,
  500. fb: tieneFb && eventoFb?.resultado[uid]?.length > 0,
  501. ig: tieneIg && eventoIg?.resultado[uid]?.length > 0,
  502. tw: tieneTw && eventoTw?.resultado[uid]?.length > 0,
  503. tt: /* tieneTw && */eventoTw?.resultado[uid]?.length > 0,
  504. };
  505. // if( (res/cr) === 1 ) {
  506. arrUsuarios.push(usuario);
  507. // }
  508. });
  509. if (arrUsuarios?.length < 1) {
  510. message.info("No hay usuarios para notificar.");
  511. return;
  512. }
  513. const body = {
  514. idEvento: id,
  515. plantilla: plantilla,
  516. titulo: grupoEvento?.nombre,
  517. enviado: null,
  518. estatus: "Nuevo",
  519. fecha: new Date(),
  520. urlFB: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Facebook")?.id}`,
  521. urlIG: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Instagram")?.id}`,
  522. urlTW: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "Twitter")?.id}`,
  523. urlTT: `${REACT_APP_WEB_URL}evento-laud?id=${eventos?.find((e) => e.redSocial === "TikTok")?.id}`,
  524. usuarios: arrUsuarios,
  525. };
  526. if (!eventoFb) {
  527. body.urlFB = "";
  528. }
  529. if (!eventoIg) {
  530. body.urlIG = "";
  531. }
  532. if (!eventoTw) {
  533. body.urlTW = "";
  534. }
  535. if (!eventoTt) {
  536. body.urlTT = "";
  537. }
  538. // Buscamos si existe un registro con el ID del evento y la misma plantilla
  539. const q = firebaseQuery(
  540. collection(firestore, "notificaciones"),
  541. where("idEvento", "==", id),
  542. );
  543. const querySnapshot = await getDocs(q);
  544. let c = 0;
  545. querySnapshot.forEach((doc) => {
  546. if (doc.data().plantilla === plantilla) {
  547. c++;
  548. }
  549. });
  550. if (c > 0) {
  551. message.info("Ya hay un recordatorio de este evento.");
  552. return;
  553. }
  554. await addDoc(collection(firestore, "notificaciones"), body);
  555. message.success("Recordatorio enviado.");
  556. } catch (error) {
  557. message.error("Error al enviar la notificacion");
  558. console.log("error al enviar felicitación: ", error);
  559. } finally {
  560. setGuardando({ ...guardando, felicitacion: false });
  561. }
  562. };
  563. const reporteCondesado = async () => {
  564. try {
  565. setGuardando({ ...guardando, condensado: true });
  566. // Obtener usuarios con sus redes sociales verificadas.
  567. const usuariosGrupo = await obtenerUsuariosGrupo();
  568. const eventos = [];
  569. const usuarios = [];
  570. const dataExcel = [];
  571. const fechas = formProcesar.getFieldValue("fechas");
  572. if (!fechas) {
  573. message.info("Debes de seleccionar un rango de fechas.");
  574. return;
  575. }
  576. const fechaInicio = new Date(fechas[0]?._d.setHours(0, 0, 0));
  577. const fechaFin = new Date(fechas[1]?._d.setHours(23, 59, 59));
  578. const queryConstraints = [
  579. where("timestamp", ">=", fechaInicio),
  580. where("timestamp", "<=", fechaFin),
  581. ];
  582. const ref = collection(firestore, "eventos");
  583. const q = firebaseQuery(
  584. ref,
  585. ...queryConstraints,
  586. orderBy("timestamp", "desc"),
  587. );
  588. const querySnapshot = await getDocs(q);
  589. // Crear arreglo de eventos.
  590. // * eventos []
  591. querySnapshot.forEach((doc) => {
  592. const data = doc.data();
  593. if (data?.estatus) {
  594. eventos.push({ ...data, id: doc.id });
  595. }
  596. });
  597. if (eventos.length < 1) {
  598. message.info("No hay eventos en el rango de fechas seleccionado.");
  599. return;
  600. }
  601. // Crear arreglo de usuarios.
  602. // * usuarios []
  603. usuariosGrupo.forEach((u) => {
  604. const tieneFb = u?.facebookVerificado ? 1 : 0;
  605. const tieneIg = u?.instagramVerificado ? 1 : 0;
  606. const tieneTw = u?.twitterVerificado ? 1 : 0;
  607. let usr = {
  608. uid: u?.uid,
  609. nombre: u?.nombre,
  610. tipo: `${tieneFb + tieneTw + tieneIg} RRSS`,
  611. redesActivas: tieneFb + tieneTw + tieneIg,
  612. tieneFb,
  613. tieneTw,
  614. tieneIg,
  615. facebook: tieneFb ? u?.facebook : "",
  616. instagram: tieneIg ? u?.instagram : "",
  617. twitter: tieneTw ? u?.twitter : "",
  618. verificado: u?.verificado !== "" ? "Verificado" : "",
  619. };
  620. usuarios.push(usr);
  621. });
  622. let columnasExcel = [
  623. { title: "UID", key: "uid" },
  624. { title: "Nombre", key: "nombre" },
  625. { title: "Tipo", key: "tipo" },
  626. { title: "Facebook", key: "facebook" },
  627. { title: "Instagram", key: "instagram" },
  628. { title: "Twitter", key: "twitter" },
  629. { title: "Verificado", key: "verificado" },
  630. ];
  631. // * columnasExcel []
  632. let totalEventos = {
  633. Twitter: 0,
  634. Facebook: 0,
  635. Instagram: 0,
  636. };
  637. eventos.forEach((e) => {
  638. const fecha = moment(e?.timestamp?.toDate()).format("DD-MM-YYYY");
  639. let red = "";
  640. if (e?.redSocial === "Twitter") {
  641. red = "TW";
  642. } else if (e?.redSocial === "Facebook") {
  643. red = "FB";
  644. } else if (e?.redSocial === "Instagram") {
  645. red = "INS";
  646. }
  647. totalEventos[e?.redSocial] += 1;
  648. columnasExcel.push({ title: `${red} - ${fecha}`, key: `${e?.id}` });
  649. });
  650. const usuariosExcel = [];
  651. let arrResultados = {};
  652. usuarios.forEach((u) => {
  653. const uid = u?.uid;
  654. columnasExcel.forEach((c) => {
  655. if (
  656. c?.key === "nombre" ||
  657. c?.key === "uid" ||
  658. c?.key === "tipo" ||
  659. c?.key === "facebook" ||
  660. c?.key === "instagram" ||
  661. c?.key === "twitter" ||
  662. c?.key === "verificado"
  663. )
  664. return true;
  665. if (!arrResultados[uid]) {
  666. arrResultados[uid] = {};
  667. }
  668. const evento = eventos?.find((e) => e?.id === c?.key);
  669. let flag = false;
  670. if (evento?.redSocial === "Instagram" && u?.tieneIg) {
  671. flag = true;
  672. }
  673. if (evento?.redSocial === "Facebook" && u?.tieneFb) {
  674. flag = true;
  675. }
  676. if (evento?.redSocial === "Twitter" && u?.tieneTw) {
  677. flag = true;
  678. }
  679. arrResultados[uid][c?.key] =
  680. flag && evento?.resultado[uid]?.length > 0 ? 1 : 0;
  681. });
  682. usuariosExcel.push({
  683. ...u,
  684. totalFb: u?.tieneFb && totalEventos.Facebook,
  685. totalTw: u?.tieneTw && totalEventos.Twitter,
  686. totalIg: u?.tieneIg && totalEventos.Instagram,
  687. });
  688. });
  689. // * return dataExcel []
  690. // Contamos participaciones (arreglo de usuarios)
  691. for (let i = 0; i < usuariosExcel.length; i++) {
  692. const resEvento = Object.keys(arrResultados[usuariosExcel[i]?.uid]).map(
  693. (key) => [key, arrResultados[usuariosExcel[i]?.uid][key]],
  694. );
  695. // Contamos
  696. let z = 0;
  697. for (let r = 0, l = resEvento?.length; r < l; r++) {
  698. z += arrResultados[usuariosExcel[i]?.uid][resEvento[r][0]];
  699. }
  700. const totalParticipacion =
  701. usuariosExcel[i]?.totalFb +
  702. usuariosExcel[i]?.totalIg +
  703. usuariosExcel[i]?.totalTw;
  704. const participacion = isNaN(z / totalParticipacion)
  705. ? 0
  706. : z / totalParticipacion;
  707. const obj = Object.assign(
  708. {
  709. ...usuariosExcel[i],
  710. participo: z,
  711. participaEn: totalParticipacion,
  712. participacion: participacion,
  713. },
  714. arrResultados[usuariosExcel[i]?.uid],
  715. );
  716. dataExcel.push(obj);
  717. }
  718. columnasExcel.push({ title: "Participando", key: "participaEn" });
  719. columnasExcel.push({ title: "Participó", key: "participo" });
  720. columnasExcel.push({ title: "Total", key: "participacion" });
  721. await excelCondensado(
  722. columnasExcel,
  723. dataExcel,
  724. totalEventos,
  725. eventos?.length,
  726. `Reporte condensado ${fechas[0].format("DD-MM-YYYY")}-${fechas[1].format("DD-MM-YYYY")}`,
  727. `Condensado de participación`,
  728. `Inicio: ${fechas[0].format("DD-MM-YYYY")} Fin:${fechas[1].format("DD-MM-YYYY")}`,
  729. "/LAUD-Logotipo.png",
  730. "",
  731. );
  732. } catch (error) {
  733. console.log("error al generar reporte consendado: ", error);
  734. } finally {
  735. setGuardando({ ...guardando, condensado: false });
  736. }
  737. };
  738. // Obtener grupo
  739. useEffect(() => {
  740. let mounted = true;
  741. if (mounted && editando) {
  742. try {
  743. setTimeout(async () => {
  744. const docRef = doc(firestore, "gruposEvento", id);
  745. const docSnap = await getDoc(docRef);
  746. setGrupoEvento(docSnap.data());
  747. }, 0);
  748. } catch (error) {
  749. console.log("error al obtener evento: ", error);
  750. } finally {
  751. setCargandoEvento(false);
  752. }
  753. }
  754. return () => (mounted = false);
  755. }, [editando, id]);
  756. // Obtener eventos del grupo
  757. useEffect(() => {
  758. let mounted = true;
  759. if (mounted && editando) {
  760. try {
  761. setTimeout(async () => {
  762. const q = firebaseQuery(
  763. collection(firestore, "eventos"),
  764. where("tag", "==", tag),
  765. );
  766. const querySnapshot = await getDocs(q);
  767. const docs = [];
  768. querySnapshot.forEach((doc) => {
  769. docs.push({
  770. ...doc.data(),
  771. id: doc.id,
  772. });
  773. });
  774. setEventos(docs);
  775. }, 0);
  776. } catch (error) {
  777. console.log("error al obtener evento: ", error);
  778. } finally {
  779. setCargandoEvento(false);
  780. }
  781. }
  782. return () => (mounted = false);
  783. }, [editando, tag]);
  784. useEffect(() => {
  785. let mounted = true;
  786. if (mounted && grupoEvento && editando) {
  787. setPreviaImg(grupoEvento?.pathFirebase);
  788. form.setFieldsValue({
  789. ...grupoEvento,
  790. fechaFinal: moment(grupoEvento?.fechaFinal),
  791. fechaInicio: moment(grupoEvento?.fechaInicio),
  792. });
  793. }
  794. return () => (mounted = false);
  795. }, [editando, grupoEvento, form]);
  796. if (cargandoEvento) return <ViewLoading />;
  797. return (
  798. <>
  799. {/* Modal Procesar */}
  800. <Modal
  801. title="Procesar"
  802. centered
  803. open={visible}
  804. okText="Procesar"
  805. confirmLoading={procesando}
  806. onOk={(e) => {
  807. if (textAreaData === "") {
  808. message.warning({
  809. content: "Atención: no dejar vacio",
  810. });
  811. return;
  812. }
  813. procesarUsuarios();
  814. guardarRegistro();
  815. }}
  816. onCancel={() => {
  817. setVisible(false);
  818. }}
  819. width={1000}
  820. >
  821. <Row>
  822. <Col span={24}>
  823. <TextArea
  824. onChange={(e) => setTextAreaData(e.target.value)}
  825. placeholder="Pegue el texto aqui"
  826. rows={6}
  827. />
  828. </Col>
  829. </Row>
  830. </Modal>
  831. {/* Modal Generar Reporte */}
  832. <Modal
  833. title="Generar Reporte"
  834. centered
  835. open={visibleReporte}
  836. okText="Generar Reporte"
  837. confirmLoading={procesando}
  838. onOk={(e) => {
  839. reporteCondesado();
  840. }}
  841. onCancel={() => {
  842. setVisibleReporte(false);
  843. }}
  844. okButtonProps={{ loading: guardando.condensado }}
  845. width={500}
  846. >
  847. <Row gutter={10} justify="center">
  848. <Col>
  849. {guardando.condensado && <Spin tip="Generando reporte..." />}
  850. </Col>
  851. <Col span={24} hidden={guardando.condensado}>
  852. <Form
  853. name="formProcesar"
  854. form={formProcesar}
  855. autoComplete="off"
  856. layout="vertical"
  857. >
  858. <Form.Item
  859. label="Selecciona un rango de fechas"
  860. name="fechas"
  861. required
  862. >
  863. <RangePicker style={{ width: "100%" }} format="DD-MM-YYYY" />
  864. </Form.Item>
  865. </Form>
  866. </Col>
  867. </Row>
  868. </Modal>
  869. <DefaultLayout multipleButtonData={buttons}>
  870. <Form name="form" form={form} autoComplete="off" layout="vertical">
  871. {/* Imagen - Nombre - Ciudad - Descripción - Url - Red social */}
  872. <Row gutter={10}>
  873. {/* Columna 1 */}
  874. <Col
  875. className="gutter-row"
  876. xs={{ span: 24 }}
  877. sm={{ span: 24 }}
  878. md={{ span: 8 }}
  879. lg={{ span: 8 }}
  880. >
  881. <Row
  882. gutter={10}
  883. style={{
  884. display: "flex",
  885. flexDirection: "row",
  886. height: "100%",
  887. }}
  888. >
  889. <Col span={24}>
  890. {previaImg ? (
  891. <img src={previaImg} width="100%" alt="" />
  892. ) : null}
  893. </Col>
  894. </Row>
  895. </Col>
  896. {/* Columna 2 */}
  897. <Col
  898. className="gutter-row"
  899. xs={{ span: 24 }}
  900. sm={{ span: 24 }}
  901. md={{ span: 16 }}
  902. lg={{ span: 16 }}
  903. >
  904. {/* Nombre evento - Ciuad */}
  905. <Row gutter={10}>
  906. <Col
  907. className="gutter-row"
  908. xs={{ span: 24 }}
  909. sm={{ span: 24 }}
  910. md={{ span: 12 }}
  911. lg={{ span: 12 }}
  912. >
  913. <Form.Item label="Nombre del evento" name="nombre">
  914. <Input readOnly />
  915. </Form.Item>
  916. </Col>
  917. <Col
  918. className="gutter-row"
  919. xs={{ span: 24 }}
  920. sm={{ span: 24 }}
  921. md={{ span: 12 }}
  922. lg={{ span: 12 }}
  923. >
  924. <Form.Item label="Ciudad" name="ciudad">
  925. <Input readOnly />
  926. </Form.Item>
  927. </Col>
  928. </Row>
  929. {/* Descripción */}
  930. <Row gutter={10}>
  931. <Col className="gutter-row" span={24}>
  932. <Form.Item label="Descripción" name="descripcion">
  933. <TextArea
  934. placeholder="Ingrese una descripción..."
  935. readOnly
  936. rows={4}
  937. />
  938. </Form.Item>
  939. </Col>
  940. </Row>
  941. </Col>
  942. </Row>
  943. {/* Fecha de inicio - Fecha fin - sin fecha - Accion */}
  944. <Row gutter={10} style={{ marginTop: 30 }}>
  945. <Col
  946. className="gutter-row"
  947. xs={{ span: 24 }}
  948. sm={{ span: 24 }}
  949. md={{ span: 8 }}
  950. lg={{ span: 8 }}
  951. >
  952. <Form.Item label="Fecha de inicio" name="fechaInicio">
  953. <DatePicker
  954. disabled
  955. format="YYYY-MM-DD HH:mm"
  956. style={{ width: "100%" }}
  957. locale={locale}
  958. showTime={{
  959. defaultValue: moment("00:00", "HH:mm"),
  960. }}
  961. />
  962. </Form.Item>
  963. </Col>
  964. <Col
  965. className="gutter-row"
  966. xs={{ span: 24 }}
  967. sm={{ span: 24 }}
  968. md={{ span: 8 }}
  969. lg={{ span: 8 }}
  970. >
  971. <Form.Item label="Fecha de finalización" name="fechaFinal">
  972. <DatePicker
  973. disabled
  974. format="YYYY-MM-DD HH:mm"
  975. style={{ width: "100%" }}
  976. locale={locale}
  977. showTime={{
  978. defaultValue: moment("00:00", "HH:mm"),
  979. }}
  980. />
  981. </Form.Item>
  982. </Col>
  983. <Col
  984. className="gutter-row"
  985. xs={{ span: 24 }}
  986. sm={{ span: 24 }}
  987. md={{ span: 8 }}
  988. lg={{ span: 8 }}
  989. >
  990. <Form.Item label="Acción" name="accion">
  991. <Select
  992. disabled
  993. mode="multiple"
  994. style={{ width: "100%" }}
  995. placeholder="Selecciona una o más opciones"
  996. optionLabelProp="label"
  997. >
  998. {acciones?.map((item) => (
  999. <Option
  1000. value={item?.value}
  1001. label={item?.label}
  1002. key={item?.id}
  1003. >
  1004. <div className="demo-option-label-item">
  1005. <span role="img" aria-label={item?.label}>
  1006. {item?.icon}
  1007. </span>
  1008. {item?.label}
  1009. </div>
  1010. </Option>
  1011. ))}
  1012. </Select>
  1013. </Form.Item>
  1014. </Col>
  1015. </Row>
  1016. {/* Redes sociales */}
  1017. <Row gutter={10}>
  1018. {eventos.map((e, index) => (
  1019. <Col
  1020. key={`evento-${index}`}
  1021. xs={{ span: 24 }}
  1022. sm={{ span: 8 }}
  1023. md={{ span: 8 }}
  1024. lg={{ span: 8 }}
  1025. >
  1026. <Card
  1027. style={{
  1028. width: "100%",
  1029. }}
  1030. actions={[
  1031. <Button
  1032. type="text"
  1033. onClick={() => {
  1034. setVisible(true);
  1035. setEvento(e);
  1036. }}
  1037. icon={<DeliveredProcedureOutlined />}
  1038. >
  1039. Procesar
  1040. </Button>,
  1041. <Button
  1042. type="text"
  1043. onClick={() =>
  1044. history.push(
  1045. urlweb + "/detalle?id=" + e?.id + "&tag=" + e?.tag,
  1046. )
  1047. }
  1048. icon={<EditOutlined />}
  1049. >
  1050. Editar
  1051. </Button>,
  1052. <Button
  1053. type="text"
  1054. onClick={() =>
  1055. history.push(
  1056. urlweb +
  1057. "/resultado-grupo-evento?id=" +
  1058. e?.id +
  1059. "&tag=" +
  1060. e?.tag,
  1061. )
  1062. }
  1063. icon={<BarChartOutlined />}
  1064. >
  1065. Resultados
  1066. </Button>,
  1067. ]}
  1068. >
  1069. <Meta
  1070. avatar={<Avatar src={`/assets/${e?.redSocial}.png`} />}
  1071. title={
  1072. <Row gutter={10}>
  1073. <Col>
  1074. <Button onClick={() => abrirPublicacion(e)}>
  1075. Abrir publicación
  1076. </Button>
  1077. </Col>
  1078. <Col>
  1079. <Button onClick={() => obtenerEnlaceYager(e)}>
  1080. Enlace Laud
  1081. </Button>
  1082. </Col>
  1083. </Row>
  1084. }
  1085. />
  1086. </Card>
  1087. </Col>
  1088. ))}
  1089. </Row>
  1090. </Form>
  1091. </DefaultLayout>
  1092. </>
  1093. );
  1094. };
  1095. export default EventoDetalleGrupo;