1- import React from 'react'
1+ import React , { useState } from 'react'
22import { Formik , Field , Form , FormErrorMessage } from 'formik'
3- import { Button , Flex , Input } from '@chakra-ui/react'
3+ import {
4+ Box ,
5+ Button ,
6+ Flex ,
7+ Input ,
8+ List ,
9+ ListItem ,
10+ ListIcon ,
11+ Link ,
12+ Code ,
13+ Text ,
14+ } from '@chakra-ui/react'
15+ import { AtSignIcon , AddIcon } from '@chakra-ui/icons'
16+ import join from 'url-join'
17+
418import logger from '../lib/logger'
519
6- export default function AddMemberForm ( { onSubmit } ) {
20+ const APP_URL = process . env . APP_URL
21+ const OSM_DOMAIN = process . env . OSM_DOMAIN
22+
23+ export function AddMemberByIdForm ( { onSubmit } ) {
724 return (
825 < Formik
926 initialValues = { { osmId : '' } }
@@ -24,15 +41,21 @@ export default function AddMemberForm({ onSubmit }) {
2441 const addMemberText = `Add Member ${ isSubmitting ? ' 🕙' : '' } `
2542
2643 return (
27- < Flex as = { Form } alignItems = 'center' >
44+ < Flex
45+ as = { Form }
46+ alignItems = 'center'
47+ justifyContent = 'space-between'
48+ width = { '100%' }
49+ gap = { 2 }
50+ >
2851 < Field
2952 as = { Input }
3053 type = 'text'
3154 name = 'osmId'
3255 id = 'osmId'
3356 placeholder = 'OSM ID'
3457 value = { values . osmId }
35- style = { { width : '6rem' } }
58+ flex = { 1 }
3659 />
3760 { status && status . msg && (
3861 < FormErrorMessage > { status . msg } </ FormErrorMessage >
@@ -53,3 +76,129 @@ export default function AddMemberForm({ onSubmit }) {
5376 />
5477 )
5578}
79+
80+ export function AddMemberByUsernameForm ( { onSubmit } ) {
81+ const [ searchResult , setSearchResult ] = useState ( )
82+ const searchUsername = async ( data , setStatus ) => {
83+ setStatus ( 'searching' )
84+ let res = await fetch ( join ( APP_URL , `/api/users?search=${ data . username } ` ) )
85+ if ( res . status === 200 ) {
86+ const data = await res . json ( )
87+ if ( data ?. users . length ) {
88+ setSearchResult ( data . users )
89+ setStatus ( 'successSearch' )
90+ } else {
91+ setSearchResult ( [ ] )
92+ setStatus ( 'noResults' )
93+ }
94+ } else {
95+ setSearchResult ( [ ] )
96+ setStatus ( 'noResults' )
97+ }
98+ }
99+ const submit = async ( uid , username , actions ) => {
100+ actions . setSubmitting ( true )
101+
102+ try {
103+ await onSubmit ( { osmId : uid , username } )
104+ actions . setSubmitting ( false )
105+ actions . resetForm ( { username : '' } )
106+ setSearchResult ( [ ] )
107+ } catch ( e ) {
108+ logger . error ( e )
109+ actions . setSubmitting ( false )
110+ actions . setStatus ( e . message )
111+ }
112+ }
113+ return (
114+ < Formik
115+ initialValues = { { username : '' } }
116+ render = { ( {
117+ status,
118+ setStatus,
119+ isSubmitting,
120+ values,
121+ setSubmitting,
122+ resetForm,
123+ } ) => {
124+ return (
125+ < >
126+ < Flex
127+ as = { Form }
128+ alignItems = 'center'
129+ justifyContent = 'space-between'
130+ width = { '100%' }
131+ gap = { 2 }
132+ >
133+ < Field
134+ as = { Input }
135+ type = 'text'
136+ name = 'username'
137+ id = 'username'
138+ placeholder = 'Search OSM Username'
139+ value = { values . username }
140+ flex = { 1 }
141+ />
142+ { status && status . msg && (
143+ < FormErrorMessage > { status . msg } </ FormErrorMessage >
144+ ) }
145+ < Button
146+ textTransform = { 'lowercase' }
147+ onClick = { ( ) => searchUsername ( values , setStatus ) }
148+ variant = 'outline'
149+ isLoading = { status === 'searching' }
150+ loadingText = 'Searching'
151+ isDisabled = { status === 'searching' || ! values . username }
152+ >
153+ Search
154+ </ Button >
155+ </ Flex >
156+ < Box display = 'flex' justifyContent = 'stretch' py = { 3 } px = { 1 } >
157+ < List spacing = { 5 } fontSize = 'sm' width = { '100%' } >
158+ { searchResult ?. length &&
159+ searchResult . map ( ( u ) => (
160+ < ListItem
161+ key = { u . id }
162+ display = 'flex'
163+ alignItems = 'center'
164+ justifyContent = 'space-between'
165+ marginTop = '1rem'
166+ >
167+ < ListIcon as = { AtSignIcon } color = 'brand.600' />
168+ < Link href = { join ( OSM_DOMAIN , '/user' , u . name ) } isExternal >
169+ { u . name }
170+ </ Link >
171+ < Code ml = { 2 } > { u . id } </ Code >
172+ < Button
173+ ml = 'auto'
174+ textTransform = 'lowercase'
175+ onClick = { async ( ) =>
176+ submit ( u . id , u . name , {
177+ setStatus,
178+ setSubmitting,
179+ resetForm,
180+ } )
181+ }
182+ size = 'sm'
183+ isLoading = { isSubmitting }
184+ loadingText = 'Adding'
185+ isDisabled = { isSubmitting }
186+ leftIcon = { < AddIcon /> }
187+ >
188+ Add
189+ </ Button >
190+ </ ListItem >
191+ ) ) }
192+ { status === 'noResults' && (
193+ < Text as = 'b' >
194+ No results found. Try typing the exact OSM username.
195+ </ Text >
196+ ) }
197+ </ List >
198+ </ Box >
199+ </ >
200+ )
201+ } }
202+ />
203+ )
204+ }
0 commit comments