File size: 6,565 Bytes
4304c6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
'use client'
import { Fragment, useCallback, useMemo, useState } from 'react'
import { useContext } from 'use-context-selector'
import { XMarkIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import { ReactMultiEmail } from 'react-multi-email'
import { Listbox, Transition } from '@headlessui/react'
import { CheckIcon } from '@heroicons/react/20/solid'
import cn from 'classnames'
import s from './index.module.css'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import { inviteMember } from '@/service/common'
import { emailRegex } from '@/config'
import { ToastContext } from '@/app/components/base/toast'
import type { InvitationResult } from '@/models/common'
import I18n from '@/context/i18n'

import 'react-multi-email/dist/style.css'
type IInviteModalProps = {
  onCancel: () => void
  onSend: (invitationResults: InvitationResult[]) => void
}

const InviteModal = ({

  onCancel,

  onSend,

}: IInviteModalProps) => {
  const { t } = useTranslation()
  const [emails, setEmails] = useState<string[]>([])
  const { notify } = useContext(ToastContext)

  const { locale } = useContext(I18n)

  const InvitingRoles = useMemo(() => [
    {
      name: 'normal',
      description: t('common.members.normalTip'),
    },
    {
      name: 'admin',
      description: t('common.members.adminTip'),
    },
  ], [t])
  const [role, setRole] = useState(InvitingRoles[0])

  const handleSend = useCallback(async () => {
    if (emails.map((email: string) => emailRegex.test(email)).every(Boolean)) {
      try {
        const { result, invitation_results } = await inviteMember({
          url: '/workspaces/current/members/invite-email',
          body: { emails, role: role.name, language: locale },
        })

        if (result === 'success') {
          onCancel()
          onSend(invitation_results)
        }
      }
      catch (e) {}
    }
    else {
      notify({ type: 'error', message: t('common.members.emailInvalid') })
    }
  }, [role, emails, notify, onCancel, onSend, t])

  return (
    <div className={cn(s.wrap)}>

      <Modal overflowVisible isShow onClose={() => {}} className={cn(s.modal)} wrapperClassName='z-20'>

        <div className='flex justify-between mb-2'>

          <div className='text-xl font-semibold text-gray-900'>{t('common.members.inviteTeamMember')}</div>

          <XMarkIcon className='w-4 h-4 cursor-pointer' onClick={onCancel} />

        </div>

        <div className='mb-7 text-[13px] text-gray-500'>{t('common.members.inviteTeamMemberTip')}</div>

        <div>

          <div className='mb-2 text-sm font-medium text-gray-900'>{t('common.members.email')}</div>

          <div className='mb-8 h-36 flex items-stretch'>

            <ReactMultiEmail

              className={cn('w-full pt-2 px-3 outline-none border-none',

                'appearance-none text-sm text-gray-900 rounded-lg overflow-y-auto',

                s.emailsInput,

              )}

              autoFocus

              emails={emails}

              inputClassName='bg-transparent'

              onChange={setEmails}

              getLabel={(email, index, removeEmail) =>

                <div data-tag key={index} className={cn(s.emailBackground)}>

                  <div data-tag-item>{email}</div>

                  <span data-tag-handle onClick={() => removeEmail(index)}>

                      ×

                  </span>

                </div>

              }

              placeholder={t('common.members.emailPlaceholder') || ''}

            />

          </div>

          <Listbox value={role} onChange={setRole}>

            <div className="relative pb-6">

              <Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-gray-100 outline-none border-none appearance-none text-sm text-gray-900 rounded-lg">

                <span className="block truncate capitalize">{t('common.members.invitedAsRole', { role: t(`common.members.${role.name}`) })}</span>

              </Listbox.Button>

              <Transition

                as={Fragment}

                leave="transition ease-in duration-200"

                leaveFrom="opacity-200"

                leaveTo="opacity-0"

              >

                <Listbox.Options className="absolute w-full py-1 my-2 overflow-auto text-base bg-white rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">

                  {InvitingRoles.map(role =>

                    <Listbox.Option

                      key={role.name}

                      className={({ active }) =>

                        `${active ? ' bg-gray-50 rounded-xl' : ' bg-transparent'}

                          cursor-default select-none relative py-2 px-4 mx-2 flex flex-col`

                      }

                      value={role}

                    >

                      {({ selected }) => (

                        <div className='flex flex-row'>

                          <span

                            className={cn(

                              'text-indigo-600 w-8',

                              'flex items-center',

                            )}

                          >

                            {selected && (<CheckIcon className="h-5 w-5" aria-hidden="true" />)}

                          </span>

                          <div className=' flex flex-col flex-grow'>

                            <span className={`${selected ? 'font-medium' : 'font-normal'} capitalize block truncate`}>

                              {t(`common.members.${role.name}`)}

                            </span>

                            <span className={`${selected ? 'font-medium' : 'font-normal'} capitalize block truncate`}>

                              {role.description}

                            </span>

                          </div>

                        </div>

                      )}

                    </Listbox.Option>,

                  )}

                </Listbox.Options>

              </Transition>

            </div>

          </Listbox>

          <Button

            tabIndex={0}

            className='w-full text-sm font-medium'

            onClick={handleSend}

            disabled={!emails.length}

            type='primary'

          >

            {t('common.members.sendInvite')}

          </Button>

        </div>

      </Modal>

    </div>
  )
}

export default InviteModal