Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 14584 | Rev 15762 | Ir a la última revisión | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
15761 stevensc 1
/* eslint-disable camelcase */
14581 stevensc 2
/* eslint-disable react/prop-types */
15761 stevensc 3
import React, { useEffect, useRef, useState } from 'react'
4
import { axios } from '../../../utils'
5
import { Modal } from 'react-bootstrap'
6
import { useForm } from 'react-hook-form'
7
import { addNotification } from '../../../redux/notification/notification.actions'
8
import { useDispatch } from 'react-redux'
14176 stevensc 9
import styled from 'styled-components'
15761 stevensc 10
import Datetime from 'react-datetime'
11
import SearchIcon from '@mui/icons-material/Search'
14176 stevensc 12
import ConfirmModal from '../../../shared/confirm-modal/ConfirmModal'
15761 stevensc 13
import EmptySection from '../../../shared/empty-section/EmptySection'
14
import FormErrorFeedback from '../../../shared/form-error-feedback/FormErrorFeedback'
11350 nelberth 15
 
15761 stevensc 16
import 'react-datetime/css/react-datetime.css'
17
import ChatList from '../../../components/chat/ChatList'
18
import TextBox from '../../../components/chat/TextBox'
11350 nelberth 19
 
20
const StyledShowOptions = styled.div`
21
  height: 342px;
22
  flex-direction: column;
15761 stevensc 23
  gap: 0.5rem;
11350 nelberth 24
  overflow-y: auto;
25
  position: relative;
26
  &.show {
27
    display: flex;
28
  }
29
  &.hide {
30
    display: none;
31
  }
32
  .optionBack {
33
    margin: 1rem 0 0.5rem 1rem;
34
    cursor: pointer;
35
  }
36
  .optionsTab {
37
    &__option {
38
      padding: 0.5rem;
39
      border-bottom: 1px solid #e2e2e2;
40
      cursor: pointer;
41
      &:hover {
42
        background-color: #e2e2e2;
43
      }
44
      &__icon {
45
        margin-right: 0.3rem;
46
      }
47
    }
48
  }
49
  .addPersonToGroupTab {
50
    display: flex;
51
    flex-direction: column;
52
    &__person {
53
      display: flex;
54
      justify-content: space-between;
55
      align-items: center;
56
      padding: 0.2rem 0.5rem;
57
      border-bottom: 1px solid #e2e2e2;
58
    }
59
  }
14176 stevensc 60
`
11350 nelberth 61
 
15761 stevensc 62
const OPTIONS = {
63
  GROUP: 'group',
64
  CONFERENCE: 'conference',
65
  ADD_CONTACTS: 'addContacts',
66
  LIST_CONTACTS: 'listContacts',
67
  INITIAL: null,
68
}
14581 stevensc 69
 
15761 stevensc 70
const PersonalChat = ({
71
  entity,
72
  onClose,
73
  onMinimize,
74
  onRead,
75
  not_seen_messages,
76
  minimized,
77
  timezones,
78
}) => {
79
  const {
80
    // id,
81
    image,
82
    name,
83
    online,
84
    type,
85
    url_get_all_messages,
86
    url_send,
87
    url_upload,
88
    profile,
89
    url_leave,
90
    url_delete,
91
    url_add_user_to_group,
92
    url_get_contact_group_list,
93
    url_get_contacts_availables_for_group,
94
    url_zoom,
95
  } = entity
11350 nelberth 96
 
15761 stevensc 97
  const [optionTab, setOptionTab] = useState(OPTIONS.INITIAL)
11350 nelberth 98
 
15761 stevensc 99
  const [availableContactsToAdd, setAvailableContactsToAdd] = useState([])
100
  const [groupContactsList, setGroupContactsList] = useState([])
11350 nelberth 101
 
15761 stevensc 102
  const [confirmModalShow, setConfirmModalShow] = useState(false)
103
  const [showConferenceModal, setShowConferenceModal] = useState(false)
11350 nelberth 104
 
15761 stevensc 105
  const [search, setSearch] = useState('')
11350 nelberth 106
 
15761 stevensc 107
  const [messages, setMessages] = useState([])
108
  const [oldMessages, setOldMessages] = useState([])
109
  const [currentPage, setCurrentPage] = useState(1)
110
  const [lastPage, setLastPage] = useState(1)
11350 nelberth 111
 
15761 stevensc 112
  const [loading, setLoading] = useState(false)
11350 nelberth 113
 
15761 stevensc 114
  const filtredGroupList = groupContactsList.filter((conversation) =>
115
    conversation.name.toLowerCase().includes(search.toLowerCase())
116
  )
11350 nelberth 117
 
15761 stevensc 118
  // refs
119
  const conversationListEl = useRef(null)
120
  const loader = useRef(null)
121
  const modalActionUrl = useRef('')
11350 nelberth 122
 
15761 stevensc 123
  const handleActive = () => {
124
    onRead(entity)
125
    onMinimize(entity)
126
  }
11350 nelberth 127
 
15761 stevensc 128
  const handleGetMessages = async () => {
129
    setLoading(true)
130
    const response = await axios.get(url_get_all_messages)
131
    const resData = response.data
132
    if (!resData.success) {
133
      return 'ha ocurrido un error'
134
    }
135
    const updatedMessages = [...resData.data.items].reverse()
136
    const newMessages = updatedMessages.reduce((acum, updatedMessage) => {
137
      if (
138
        messages.findIndex((message) => message.id === updatedMessage.id) === -1
139
      ) {
140
        acum = [...acum, updatedMessage]
141
      }
142
      return acum
143
    }, [])
11350 nelberth 144
 
15761 stevensc 145
    if (newMessages.length > 0) {
146
      setMessages([...messages, ...newMessages])
147
      setLoading(false)
148
      setLastPage(resData.data.pages)
149
      scrollToBottom()
150
    } else {
151
      setLoading(false)
152
    }
153
  }
14581 stevensc 154
 
15761 stevensc 155
  const handleLoadMore = async () => {
156
    await axios
157
      .get(`${url_get_all_messages}?page=${currentPage}`)
158
      .then((response) => {
159
        const resData = response.data
160
        if (resData.success) {
161
          if (resData.data.page > 1) {
162
            const updatedOldMessages = [...resData.data.items].reverse()
163
            setOldMessages([...updatedOldMessages, ...oldMessages])
164
            /* scrollDownBy(100); */
165
          }
166
        }
167
      })
168
  }
14581 stevensc 169
 
15761 stevensc 170
  const handleCloseChat = () => onClose(entity)
11350 nelberth 171
 
15761 stevensc 172
  const sendMessage = async (message) => {
173
    const formData = new FormData()
174
    formData.append('message', emojione.toShort(message))
175
    await axios.post(url_send, formData).then((response) => {
176
      const resData = response.data
177
      if (resData.success) {
178
        let newMessage = resData.data
11350 nelberth 179
 
15761 stevensc 180
        online
181
          ? (newMessage = { ...newMessage, not_received: false })
182
          : (newMessage = { ...newMessage, not_received: true })
11350 nelberth 183
 
15761 stevensc 184
        setMessages([...messages, newMessage])
185
      }
186
    })
187
    // await handleGetMessages()
188
    // setResponseMessage(null)
189
  }
11350 nelberth 190
 
15761 stevensc 191
  const showOptions = (tab = OPTIONS.INITIAL) => {
192
    onMinimize(entity, false)
193
    setOptionTab(tab)
194
  }
11350 nelberth 195
 
15761 stevensc 196
  const handleAddPersonToGroup = async (id) => {
197
    const formData = new FormData()
198
    formData.append('uid', id)
199
    await axios.post(url_add_user_to_group, formData).then((response) => {
200
      const resData = response.data
201
      if (resData.success) {
202
        loadPersonsAvailable()
203
      }
204
    })
205
  }
11350 nelberth 206
 
15761 stevensc 207
  const handleConfirmModalAction = async () => {
208
    try {
209
      const { data } = await axios.post(modalActionUrl.current)
210
      if (!data.success) console.log('Error in confirm modal action')
211
      handleConfirmModalShow()
212
      onClose(entity)
213
      return
214
    } catch (error) {
215
      console.log(error)
216
    }
217
  }
11350 nelberth 218
 
15761 stevensc 219
  const handleObserver = (entities) => {
220
    const target = entities[0]
221
    if (target.isIntersecting) {
222
      setCurrentPage((prevState) => prevState + 1)
223
    }
224
  }
11350 nelberth 225
 
15761 stevensc 226
  const scrollToBottom = () => {
227
    if (conversationListEl.current) {
228
      conversationListEl.current.scrollTop =
229
        conversationListEl.current.scrollHeight * 9
230
    }
231
  }
11350 nelberth 232
 
15761 stevensc 233
  const handleConfirmModalShow = () => setConfirmModalShow(!confirmModalShow)
11350 nelberth 234
 
15761 stevensc 235
  const handleConfirmModalAccept = () => handleConfirmModalAction()
11350 nelberth 236
 
15761 stevensc 237
  /* const handleResponseMessage = (element) => {
238
    element.mtype === 'text'
239
      ? setResponseMessage(element)
240
      : setResponseMessage({ ...element, m: 'Archivo adjunto' })
11350 nelberth 241
 
15761 stevensc 242
    textAreaEl.current && textAreaEl.current.focus()
243
  } */
11350 nelberth 244
 
15761 stevensc 245
  const displayConferenceModal = () =>
246
    setShowConferenceModal(!showConferenceModal)
11350 nelberth 247
 
15761 stevensc 248
  const loadPersonsAvailable = async () => {
249
    await axios.get(url_get_contacts_availables_for_group).then((response) => {
250
      const resData = response.data
251
      if (resData.success) {
252
        setAvailableContactsToAdd(resData.data)
253
      }
254
    })
255
  }
11350 nelberth 256
 
15761 stevensc 257
  const loadGroupContacts = async () => {
258
    await axios.get(url_get_contact_group_list).then((response) => {
259
      const resData = response.data
260
      if (resData.success) {
261
        setGroupContactsList(resData.data)
262
      }
263
    })
264
  }
11350 nelberth 265
 
15761 stevensc 266
  const handleDeletePersonFromGroup = async (urlDeletePersonFromGroup) => {
267
    await axios.post(urlDeletePersonFromGroup).then((response) => {
268
      const resData = response.data
269
      if (resData.success) {
270
        loadGroupContacts()
271
      }
272
    })
273
  }
11350 nelberth 274
 
15761 stevensc 275
  const tabsOptions = {
276
    group: (
277
      <ul>
278
        {url_get_contact_group_list && (
279
          <li
280
            className="optionsTab__option"
281
            onClick={() => showOptions(OPTIONS.LIST_CONTACTS)}
282
          >
283
            <span className="optionsTab__option__icon">
284
              <i className="fa fa-group" />
285
              Integrantes
286
            </span>
287
          </li>
288
        )}
289
        {url_add_user_to_group && (
290
          <li
291
            className="optionsTab__option"
292
            onClick={() => showOptions(OPTIONS.ADD_CONTACTS)}
293
          >
294
            <span className="optionsTab__option__icon">
295
              <i className="fa fa-user-plus"></i>
296
            </span>
297
            {CHAT_LABELS.ADD_CONTACTS}
298
          </li>
299
        )}
300
        {url_zoom && (
301
          <li className="optionsTab__option" onClick={displayConferenceModal}>
302
            <span className="optionsTab__option__icon">
303
              <i className="fa fa-user-plus" />
304
            </span>
305
            {CHAT_LABELS.CREATE_CONFERENCE}
306
          </li>
307
        )}
308
        {url_delete && (
309
          <li
310
            className="optionsTab__option"
311
            style={{ color: 'red' }}
312
            onClick={() => {
313
              handleConfirmModalShow()
314
              modalActionUrl.current = url_delete
315
            }}
316
          >
317
            <span className="optionsTab__option__icon">
318
              <i className="fa fa-trash"></i>
319
            </span>
320
            Eliminar grupo
321
          </li>
322
        )}
323
        {!!url_leave && (
324
          <li
325
            className="optionsTab__option"
326
            style={{ color: 'red' }}
327
            onClick={() => {
328
              handleConfirmModalShow()
329
              modalActionUrl.current = url_leave
330
            }}
331
          >
332
            <span className="optionsTab__option__icon">
333
              <i className="fa fa-user-times"></i>
334
            </span>
335
            Dejar grupo
336
          </li>
337
        )}
338
      </ul>
339
    ),
340
    conference: (
341
      <ul>
342
        <li className="optionsTab__option" onClick={displayConferenceModal}>
343
          <span className="optionsTab__option__icon">
344
            <i className="fa fa-user-plus" />
345
          </span>
346
          {CHAT_LABELS.CREATE_CONFERENCE}
347
        </li>
348
      </ul>
349
    ),
350
    addContacts: (
351
      <>
352
        {availableContactsToAdd.length ? (
353
          <EmptySection message={CHAT_LABELS.NOT_CONTACTS} align="left" />
354
        ) : (
355
          availableContactsToAdd.map(({ image, name, id }) => (
356
            <div className="addPersonToGroupTab__person" key={id}>
357
              <div className="d-inline-flex" style={{ gap: '5px' }}>
358
                <img
359
                  className="chat-image img-circle pull-left"
360
                  height="36"
361
                  width="36"
362
                  src={image}
363
                  alt="image-image"
364
                />
365
                <div className="name">{name}</div>
366
              </div>
367
              <span
368
                className="cursor-pointer"
369
                onClick={() => handleAddPersonToGroup(id)}
370
              >
371
                <i className="fa fa-plus-circle" />
372
              </span>
373
            </div>
374
          ))
375
        )}
376
      </>
377
    ),
378
    listContacts: (
379
      <>
380
        <div className="group__search">
381
          <SearchIcon />
382
          <input
383
            type="text"
384
            placeholder={CHAT_LABELS.SEARCH}
385
            onChange={(e) => setSearch(e.target.value)}
386
          />
387
        </div>
388
        {filtredGroupList.length ? (
389
          filtredGroupList.map(({ image, name, url_remove_from_group, id }) => {
390
            return (
391
              <div className="addPersonToGroupTab__person" key={id}>
392
                <div style={{ display: 'flex', alignItems: 'center' }}>
393
                  <img
394
                    className="chat-image img-circle pull-left"
395
                    height="36"
396
                    width="36"
397
                    src={image}
398
                    alt="image-image"
399
                  />
400
                  <div className="name">{name}</div>
401
                </div>
402
                {url_remove_from_group && (
403
                  <span
404
                    className="cursor-pointer"
405
                    onClick={() =>
406
                      handleDeletePersonFromGroup(url_remove_from_group)
407
                    }
408
                  >
409
                    <i className="fa fa-user-times" />
410
                  </span>
411
                )}
412
              </div>
413
            )
414
          })
415
        ) : (
416
          <div className="addPersonToGroupTab__person">
417
            {CHAT_LABELS.NOT_CONTACTS}
418
          </div>
419
        )}
420
      </>
421
    ),
422
  }
14581 stevensc 423
 
15761 stevensc 424
  // getMessageOnMaximize and subscribe to infinite Loader
425
  useEffect(async () => {
426
    const options = {
427
      root: null,
428
      rootMargin: '20px',
429
      treshold: 1.0,
430
    }
431
    const observer = new IntersectionObserver(handleObserver, options)
432
    if (!minimized) {
433
      await handleGetMessages()
434
      // loader observer
435
      if (loader.current) {
436
        observer.observe(loader.current)
437
      }
438
    }
439
    return () => {
440
      if (loader.current) {
441
        observer.unobserve(loader.current)
442
      }
443
    }
444
  }, [minimized])
11350 nelberth 445
 
15761 stevensc 446
  // LoadMore on change page
447
  useEffect(() => {
448
    let loadMore = () => handleLoadMore()
449
    loadMore()
450
    return () => {
451
      loadMore = null
452
    }
453
  }, [currentPage])
11350 nelberth 454
 
15761 stevensc 455
  // getMessagesInterval
456
  useEffect(() => {
457
    if (window.location.pathname === '/group/my-groups') {
458
      const items = document.getElementsByClassName('sc-jSgupP')
459
      if (items && items.length > 0) {
460
        items[0].style.display = 'none'
461
      }
462
    }
463
  }, [minimized])
11350 nelberth 464
 
15761 stevensc 465
  useEffect(() => {
466
    let timer
467
    if (!minimized && !loading) {
468
      timer = setTimeout(() => handleGetMessages(), 1000)
469
    }
470
    return () => {
471
      clearTimeout(timer)
472
    }
473
  }, [minimized, loading])
11350 nelberth 474
 
15761 stevensc 475
  // useEffect for tabs changing
476
  useEffect(() => {
477
    if (optionTab === 'addContacts') loadPersonsAvailable()
478
    if (optionTab === 'listContacts') loadGroupContacts()
479
  }, [optionTab])
11350 nelberth 480
 
15761 stevensc 481
  return (
482
    <>
483
      <div className="personal-chat">
484
        <div className={`chat-header ${not_seen_messages ? 'notify' : ''}`}>
485
          <img src={image} alt="avatar-image" />
486
          <div className="info-content">
487
            <a href={profile} target="_blank" rel="noreferrer">
488
              {name}
489
            </a>
490
            {type === 'user' && (
491
              <small className={online ? 'online' : 'offline'}>
492
                {online ? 'En línea' : 'Desconectado'}
493
              </small>
494
            )}
495
          </div>
496
          <div className="options ml-auto">
497
            <i
498
              className="cursor-pointer fa fa-gear"
499
              onClick={() =>
500
                showOptions(
501
                  type === 'user' ? OPTIONS.CONFERENCE : OPTIONS.GROUP
502
                )
503
              }
504
            />
505
            <i
506
              className={'cursor-pointer fa fa-minus-circle'}
507
              onClick={handleActive}
508
            />
509
            <i
510
              className="cursor-pointer fa fa-times-circle"
511
              onClick={handleCloseChat}
512
            />
513
          </div>
514
        </div>
11350 nelberth 515
 
15761 stevensc 516
        <div
517
          className="panel-body"
518
          style={{ display: !minimized ? 'block' : 'none' }}
519
        >
520
          {optionTab ? (
521
            <StyledShowOptions>
522
              <span className="optionBack" onClick={() => showOptions()}>
523
                <i className="fa icon-arrow-left" />
524
              </span>
525
              <div className="optionsTab">{tabsOptions[optionTab]}</div>
526
            </StyledShowOptions>
527
          ) : (
528
            <>
529
              <ChatList
530
                messages={[...oldMessages, ...messages]}
531
                currentPage={currentPage}
532
                lastPage={lastPage}
533
                conversationList={conversationListEl}
534
                loader={loader}
535
              />
536
              <TextBox
537
                uploadUrl={url_upload}
538
                isNotSeen={not_seen_messages}
539
                markRead={() => onRead(entity)}
540
                onSend={sendMessage}
541
              />
542
            </>
543
          )}
544
        </div>
545
      </div>
546
      <ConfirmModal
547
        show={confirmModalShow}
548
        onClose={handleConfirmModalShow}
549
        onAccept={handleConfirmModalAccept}
550
      />
551
      <ConferenceModal
552
        show={showConferenceModal}
553
        timezones={timezones}
554
        zoomUrl={url_zoom}
555
        onCreate={() => {
556
          showOptions()
557
          displayConferenceModal()
558
        }}
559
      />
560
    </>
561
  )
562
}
11350 nelberth 563
 
15761 stevensc 564
const StyleModal = ({
565
  title = 'Crea una conferencia',
566
  size = 'md',
567
  show = false,
568
  children,
569
}) => {
570
  const [isShow, setIsShow] = useState(show)
11350 nelberth 571
 
15761 stevensc 572
  useEffect(() => {
573
    setIsShow(show)
574
  }, [show])
11350 nelberth 575
 
15761 stevensc 576
  const closeModal = () => setIsShow(false)
11350 nelberth 577
 
15761 stevensc 578
  return (
579
    <Modal show={isShow} onHide={closeModal} style={{ overflowY: 'scroll' }}>
580
      <Modal.Header closeButton>
581
        <Modal.Title>{title}</Modal.Title>
582
      </Modal.Header>
583
      <Modal.Body>{children}</Modal.Body>
584
    </Modal>
585
  )
586
}
11350 nelberth 587
 
15761 stevensc 588
export const ConferenceModal = ({
589
  show = false,
590
  timezones = {},
591
  zoomUrl = '',
592
  onCreate = () => null,
593
}) => {
594
  const dt = new Date()
595
  const { handleSubmit, register, errors, reset } = useForm({ mode: 'all' })
596
  const [date, setDate] = useState({
597
    year: dt.toLocaleString('default', { year: 'numeric' }),
598
    month: dt.toLocaleString('default', { month: '2-digit' }),
599
    day: dt.toLocaleString('default', { day: '2-digit' }),
600
  })
601
  const [time, setTime] = useState(
602
    dt.toLocaleString('es', {
603
      hour: 'numeric',
604
      minute: '2-digit',
605
      second: '2-digit',
606
    })
607
  )
608
  const [coferenceType, setConferenceType] = useState(1)
609
  const dispatch = useDispatch()
11350 nelberth 610
 
15761 stevensc 611
  const handleChange = (value) => setConferenceType(value)
11350 nelberth 612
 
15761 stevensc 613
  const handleDateTime = (value) => {
614
    setDate({
615
      ...date,
616
      year: new Intl.DateTimeFormat('es', { year: 'numeric' }).format(value),
617
      month: new Intl.DateTimeFormat('es', { month: '2-digit' }).format(value),
618
      day: new Intl.DateTimeFormat('es', { day: '2-digit' }).format(value),
619
    })
620
    setTime(
621
      new Intl.DateTimeFormat('es', {
622
        hour: 'numeric',
623
        minute: '2-digit',
624
        second: 'numeric',
625
      }).format(value)
626
    )
627
  }
11350 nelberth 628
 
15761 stevensc 629
  const onSubmit = async (data) => {
630
    try {
631
      const formData = new FormData()
632
 
633
      Object.entries(data).forEach(([key, value]) =>
634
        formData.append(key, value)
635
      )
636
      formData.append('date', `${date.year}-${date.month}-${date.day}`)
637
      formData.append('time', time)
638
 
639
      const { data: response } = await axios.post(zoomUrl, formData)
640
 
641
      if (!response.success && typeof response.data === 'string') {
642
        dispatch(addNotification({ msg: response.data, style: 'danger' }))
643
        return
644
      }
645
 
646
      if (!response.success && typeof response.data === 'object') {
647
        Object.entries(response.data).forEach(([key, value]) => {
648
          dispatch(
649
            addNotification({ msg: `${key}: ${value[0]}`, style: 'danger' })
650
          )
651
        })
652
        return
653
      }
654
 
655
      dispatch(addNotification({ msg: response.data, style: 'success' }))
656
      onCreate()
657
      reset()
658
    } catch (error) {
659
      console.log(`Error: ${error.message}`)
660
      return dispatch(
661
        addNotification({ msg: 'Ha ocurrido un error', style: 'danger' })
662
      )
663
    }
664
  }
665
 
666
  return (
667
    <StyleModal title="Crea una conferencia" show={show}>
668
      <form onSubmit={handleSubmit(onSubmit)} autoComplete="new-password">
669
        <div className="form-group">
670
          <label htmlFor="first_name">Título</label>
671
          <input
672
            type="text"
673
            name="title"
674
            className="form-control"
675
            maxLength={128}
676
            ref={register({ required: 'Por favor ingrese un título' })}
677
          />
678
          {errors.title && (
679
            <FormErrorFeedback>{errors.title.message}</FormErrorFeedback>
680
          )}
681
        </div>
682
        <div className="form-group">
683
          <label htmlFor="first_name">Descripción</label>
684
          <input
685
            type="text"
686
            name="description"
687
            className="form-control"
688
            ref={register({ required: 'Por favor ingrese una descripción' })}
689
          />
690
          {errors.description && (
691
            <FormErrorFeedback>{errors.description.message}</FormErrorFeedback>
692
          )}
693
        </div>
694
        <div className="form-group">
695
          <label htmlFor="timezone">Tipo de conferencia</label>
696
          <select
697
            name="type"
698
            className="form-control"
699
            onChange={({ target }) => handleChange(target.value)}
700
            ref={register}
701
          >
702
            <option value="i">Inmediata</option>
703
            <option value="s">Programada</option>
704
          </select>
705
        </div>
706
        {coferenceType === 's' && (
707
          <div className="form-group">
708
            <label htmlFor="timezone">Horario</label>
709
            <Datetime
710
              dateFormat="DD-MM-YYYY"
711
              onChange={(e) => {
712
                if (e.toDate) {
713
                  handleDateTime(e.toDate())
714
                }
715
              }}
716
              inputProps={{ className: 'form-control' }}
717
              initialValue={Date.parse(new Date())}
718
              closeOnSelect
719
            />
720
          </div>
721
        )}
722
        <div className="form-group">
723
          <label htmlFor="timezone">Zona horaria</label>
724
          <select
725
            className="form-control"
726
            name="timezone"
727
            ref={register({ required: 'Por favor elige una Zona horaria' })}
728
          >
729
            <option value="" hidden>
730
              Zona horaria
731
            </option>
732
            {Object.entries(timezones).map(([key, value]) => (
733
              <option value={key} key={key}>
734
                {value}
735
              </option>
736
            ))}
737
          </select>
738
          {errors.timezone && (
739
            <FormErrorFeedback>{errors.timezone.message}</FormErrorFeedback>
740
          )}
741
        </div>
742
        <div className="form-group">
743
          <label htmlFor="timezone">Duración</label>
744
          <select className="form-control" name="duration" ref={register}>
745
            <option value={5}>5-min</option>
746
            <option value={10}>10-min</option>
747
            <option value={15}>15-min</option>
748
            <option value={20}>20-min</option>
749
            <option value={25}>25-min</option>
750
            <option value={30}>30-min</option>
751
            <option value={35}>35-min</option>
752
            <option value={40}>40-min</option>
753
            <option value={45}>45-min</option>
754
          </select>
755
        </div>
756
        <div className="form-group">
757
          <label htmlFor="first_name">Contraseña de ingreso</label>
758
          <input
759
            type="password"
760
            name="password"
761
            className="form-control"
762
            ref={register({
763
              required: 'Por favor ingrese una contraseña',
764
              maxLength: {
765
                value: 6,
766
                message: 'La contraseña debe tener al menos 6 digitos',
767
              },
768
            })}
769
          />
770
          {errors.password && (
771
            <FormErrorFeedback>{errors.password.message}</FormErrorFeedback>
772
          )}
773
        </div>
774
        <button className="btn btn-primary" type="submit">
775
          Crear
776
        </button>
777
      </form>
778
    </StyleModal>
779
  )
14176 stevensc 780
}
11350 nelberth 781
 
15761 stevensc 782
export default React.memo(PersonalChat)