data-explorer-routing-and-admins-context-menu-for-resources
[arvados.git] / src / views-components / sharing-dialog / people-select.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { Autocomplete } from '~/components/autocomplete/autocomplete';
7 import { connect, DispatchProp } from 'react-redux';
8 import { ServiceRepository } from '~/services/services';
9 import { FilterBuilder } from '../../services/api/filter-builder';
10 import { debounce } from 'debounce';
11 import { ListItemText, Typography } from '@material-ui/core';
12 import { noop } from 'lodash/fp';
13 import { GroupClass } from '~/models/group';
14
15 export interface Person {
16     name: string;
17     email: string;
18     uuid: string;
19 }
20
21 export interface PeopleSelectProps {
22
23     items: Person[];
24     label?: string;
25     autofocus?: boolean;
26     onlyPeople?: boolean;
27
28     onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
29     onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
30     onCreate?: (person: Person) => void;
31     onDelete?: (index: number) => void;
32     onSelect?: (person: Person) => void;
33
34 }
35
36 export interface PeopleSelectState {
37     value: string;
38     suggestions: any[];
39 }
40
41 export const PeopleSelect = connect()(
42     class PeopleSelect extends React.Component<PeopleSelectProps & DispatchProp, PeopleSelectState> {
43
44         state: PeopleSelectState = {
45             value: '',
46             suggestions: []
47         };
48
49         render() {
50             const { label = 'Share' } = this.props;
51
52             return (
53                 <Autocomplete
54                     label={label}
55                     value={this.state.value}
56                     items={this.props.items}
57                     suggestions={this.state.suggestions}
58                     autofocus={this.props.autofocus}
59                     onChange={this.handleChange}
60                     onCreate={this.handleCreate}
61                     onSelect={this.handleSelect}
62                     onDelete={this.handleDelete}
63                     onFocus={this.props.onFocus}
64                     onBlur={this.props.onBlur}
65                     renderChipValue={this.renderChipValue}
66                     renderSuggestion={this.renderSuggestion} />
67             );
68         }
69
70         renderChipValue({ name, uuid }: Person) {
71             return name ? name : uuid;
72         }
73
74         renderSuggestion({ firstName, lastName, email, name }: any) {
75             return (
76                 <ListItemText>
77                     {name ?
78                         <Typography noWrap>{name}</Typography> :
79                         <Typography noWrap>{`${firstName} ${lastName} <<${email}>>`}</Typography>}
80                 </ListItemText>
81             );
82         }
83
84         handleDelete = (_: Person, index: number) => {
85             const { onDelete = noop } = this.props;
86             onDelete(index);
87         }
88
89         handleCreate = () => {
90             const { onCreate } = this.props;
91             if (onCreate) {
92                 this.setState({ value: '', suggestions: [] });
93                 onCreate({
94                     email: '',
95                     name: '',
96                     uuid: this.state.value,
97                 });
98             }
99         }
100
101         handleSelect = ({ email, firstName, lastName, uuid, name }: any) => {
102             const { onSelect = noop } = this.props;
103             this.setState({ value: '', suggestions: [] });
104             onSelect({
105                 email,
106                 name: `${name ? name : `${firstName} ${lastName}`}`,
107                 uuid,
108             });
109         }
110
111         handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
112             this.setState({ value: event.target.value }, this.getSuggestions);
113         }
114
115         getSuggestions = debounce(() => this.props.dispatch<any>(this.requestSuggestions), 500);
116
117         requestSuggestions = async (_: void, __: void, { userService, groupsService }: ServiceRepository) => {
118             const { value } = this.state;
119             const filterGroups = new FilterBuilder()
120                 .addNotIn('groupClass', [GroupClass.PROJECT])
121                 .addILike('name', value)
122                 .getFilters();
123             const groupItems = await groupsService.list({ filters: filterGroups, limit: 5 });
124             const filterUsers = new FilterBuilder()
125                 .addILike('email', value)
126                 .getFilters();
127             const userItems: any = await userService.list({ filters: filterUsers, limit: 5 });
128             const items = groupItems.items.concat(userItems.items);
129             this.setState({ suggestions: this.props.onlyPeople ? userItems.items : items });
130         }
131     });