88
99import { useEffect , useReducer } from 'react'
1010import { useNavigate , useParams } from 'react-router-dom'
11- import { useServices } from '@kernel/common'
11+ import { useServices , AutocompleteInput , errorUtils } from '@kernel/common'
1212
1313import AppConfig from 'App.config'
1414
1515const MODES = { create : 'create' , update : 'update' }
16- const KEYS = [ 'name' , 'memberIdsText' ]
17- const STATE_KEYS = [ 'group' , 'groups' , 'member' , 'members' , 'error' , 'status' , 'taskService' ]
16+ const KEYS = [ 'name' , 'memberIdsText' , 'groupMembers' ]
17+ const STATE_KEYS = [ 'group' , 'groups' , 'member' , 'members' , 'profiles' , ' error', 'status' , 'taskService' ]
1818const INITIAL_STATE = STATE_KEYS . concat ( KEYS )
1919 . reduce ( ( acc , k ) => Object . assign ( acc , { [ k ] : '' } ) , { } )
2020
@@ -23,6 +23,8 @@ Object.keys(INITIAL_STATE)
2323 . forEach ( ( k ) => {
2424 actions [ k ] = ( state , e ) => Object . assign ( { } , state , { [ k ] : e } )
2525 } )
26+ INITIAL_STATE . groupMembers = [ ]
27+ INITIAL_STATE . profiles = [ ]
2628
2729const reducer = ( state , action ) => {
2830 try {
@@ -50,8 +52,8 @@ const value = (state, type) => {
5052}
5153
5254// dedupe, sort
53- const textToArray = ( s ) => [ ... new Set ( s . split ( ',' ) . map ( ( e ) => e . trim ( ) ) ) ] . sort ( )
54- const arrayToText = ( arr ) => arr . join ( ', ' )
55+ const getMemberIds = ( groupMembers ) => groupMembers . map ( groupMember => groupMember . id )
56+ const transformProfiles = ( profiles ) => Object . values ( profiles ) . map ( ( { data : { memberId , name } } ) => ( { id : memberId , name } ) )
5557const resetAlerts = ( dispatch ) => {
5658 dispatch ( { type : 'error' , payload : '' } )
5759 dispatch ( { type : 'status' , payload : 'submitting' } )
@@ -60,9 +62,9 @@ const resetAlerts = (dispatch) => {
6062const create = async ( state , dispatch , e ) => {
6163 e . preventDefault ( )
6264 resetAlerts ( dispatch )
63- const { groups, memberIdsText , name, taskService } = state
64- const memberIds = textToArray ( memberIdsText )
65- if ( ! name . length || ! memberIdsText . length ) {
65+ const { groups, groupMembers , name, taskService } = state
66+ const memberIds = getMemberIds ( groupMembers )
67+ if ( ! name . length || ! groupMembers . length ) {
6668 dispatch ( { type : 'error' , payload : 'name and member ids are required' } )
6769 return
6870 }
@@ -80,9 +82,9 @@ const create = async (state, dispatch, e) => {
8082const update = async ( state , dispatch , e ) => {
8183 e . preventDefault ( )
8284 resetAlerts ( dispatch )
83- const { group, groups, memberIdsText , name, taskService } = state
85+ const { group, groups, groupMembers , name, taskService } = state
8486 const groupId = group . id
85- const memberIds = textToArray ( memberIdsText )
87+ const memberIds = getMemberIds ( groupMembers )
8688 try {
8789 if ( group . data . name !== name ) {
8890 await groups . patch ( groupId , { name } )
@@ -107,6 +109,7 @@ const Form = () => {
107109
108110 const { services, currentUser } = useServices ( )
109111 const user = currentUser ( )
112+ const { readable } = errorUtils
110113
111114 useEffect ( ( ) => {
112115 if ( ! user || user . role > AppConfig . minRole ) {
@@ -117,14 +120,22 @@ const Form = () => {
117120 useEffect ( ( ) => {
118121 ( async ( ) => {
119122 dispatch ( { type : 'status' , payload : 'Loading' } )
120- const { entityFactory, taskService } = await services ( )
123+ const { entityFactory, taskService, queryService } = await services ( )
121124 dispatch ( { type : 'taskService' , payload : taskService } )
122125 const members = await entityFactory ( { resource : 'member' } )
123126 const member = await members . get ( user . iss )
124127 const groups = await entityFactory ( { resource : 'group' } )
128+ let transformedProfiles = [ ]
129+ try {
130+ const { profiles } = await queryService . recommend ( )
131+ transformedProfiles = transformProfiles ( profiles )
132+ } catch ( error ) {
133+ dispatch ( { type : 'error' , payload : readable ( error . message ) } )
134+ }
125135 dispatch ( { type : 'members' , payload : members } )
126136 dispatch ( { type : 'member' , payload : member } )
127137 dispatch ( { type : 'groups' , payload : groups } )
138+ dispatch ( { type : 'profiles' , payload : transformedProfiles } )
128139 if ( mode === MODES . update ) {
129140 const entity = await groups . get ( group )
130141 dispatch ( { type : 'group' , payload : entity } )
@@ -133,10 +144,9 @@ const Form = () => {
133144 . forEach ( ( [ k , v ] ) => {
134145 let type = k
135146 let payload = v
136- // TODO: more ergonomic way to select group memebers
137147 if ( k === 'memberIds' ) {
138- type = 'memberIdsText '
139- payload = arrayToText ( v )
148+ type = 'groupMembers '
149+ payload = transformedProfiles . filter ( item => v . includes ( item . id ) )
140150 }
141151 dispatch ( { type, payload } )
142152 } )
@@ -155,10 +165,11 @@ const Form = () => {
155165 />
156166 </ label >
157167 < label className = 'block' >
158- < span className = 'text-gray-700' > Member Ids (comma separated)</ span >
159- < input
160- type = 'text' multiple className = { formClass }
161- value = { value ( state , 'memberIdsText' ) } onChange = { change . bind ( null , dispatch , 'memberIdsText' ) }
168+ < span className = 'text-gray-700' > Member Names</ span >
169+ < AutocompleteInput
170+ items = { value ( state , 'profiles' ) }
171+ selectedItems = { value ( state , 'groupMembers' ) }
172+ setSelectedItems = { items => dispatch ( { type : 'groupMembers' , payload : items } ) }
162173 />
163174 </ label >
164175 < label className = 'block' >
0 commit comments