Skip to content

Commit 0354b22

Browse files
committed
feat: add TeamSearchField component for team selection in the Getting Started widget
Signed-off-by: amitamrutiya <amitamrutiya2210@gmail.com>
1 parent 5cf0184 commit 0354b22

1 file changed

Lines changed: 213 additions & 0 deletions

File tree

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/* eslint-disable @typescript-eslint/ban-ts-comment */
2+
/* eslint-disable @typescript-eslint/no-explicit-any */
3+
// @ts-nocheck
4+
import { Autocomplete, Box, Typography } from '@mui/material';
5+
import { styled } from '@mui/material/styles';
6+
import { debounce } from 'lodash';
7+
import React, { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
8+
import { Chip, CircularProgress, TextField, Tooltip } from '../../../base';
9+
import { iconSmall } from '../../../constants/iconsSizes';
10+
import { CloseIcon } from '../../../icons';
11+
12+
interface Team {
13+
id: string;
14+
ID: string;
15+
name: string;
16+
deleted_at: {
17+
Valid: boolean;
18+
};
19+
}
20+
21+
interface TeamSearchFieldProps {
22+
teamsData: Team[];
23+
setTeamsData: Dispatch<SetStateAction<Team[]>>;
24+
label?: string;
25+
orgID: string;
26+
disabled?: boolean;
27+
useLazyGetTeamsQuery: any;
28+
useNotificationHandlers: () => {
29+
handleError: (message: string) => void;
30+
};
31+
}
32+
33+
interface TeamListContainerProps {
34+
hasTeams: boolean;
35+
}
36+
37+
const TeamListContainer = styled(Box)<TeamListContainerProps>(({ theme, hasTeams }) => ({
38+
display: 'flex',
39+
flexWrap: 'wrap',
40+
gap: theme.spacing(0.5),
41+
marginTop: hasTeams ? theme.spacing(1.7) : 0
42+
}));
43+
44+
const ToggleButton = styled(Typography)(({ theme }) => ({
45+
cursor: 'pointer',
46+
marginLeft: theme.spacing(0.5),
47+
color: theme.palette.text.primary,
48+
fontWeight: 600,
49+
'&:hover': {
50+
color: theme.palette.primary.main
51+
}
52+
}));
53+
54+
const TeamSearchField: React.FC<TeamSearchFieldProps> = ({
55+
teamsData,
56+
setTeamsData,
57+
label,
58+
orgID,
59+
disabled = false,
60+
useLazyGetTeamsQuery,
61+
useNotificationHandlers
62+
}) => {
63+
const [error, setError] = useState<boolean>(false);
64+
const [inputValue, setInputValue] = useState<string>('');
65+
const [options, setOptions] = useState<Team[]>([]);
66+
const [isLoading, setIsLoading] = useState<boolean>(false);
67+
const [showAllTeams, setShowAllTeams] = useState<boolean>(false);
68+
const [getTeams] = useLazyGetTeamsQuery();
69+
const { handleError } = useNotificationHandlers();
70+
71+
const fetchSuggestions = useMemo(
72+
() =>
73+
debounce((searchValue: string) => {
74+
if (!orgID) return;
75+
76+
setIsLoading(true);
77+
getTeams({ orgId: orgID, search: searchValue }, true)
78+
.unwrap()
79+
.then((response: any) => {
80+
setOptions(typeof response === 'string' ? [] : response?.teams);
81+
setIsLoading(false);
82+
})
83+
.catch((err: any) => {
84+
handleError(err?.message);
85+
setIsLoading(false);
86+
});
87+
}, 300),
88+
[orgID, getTeams, handleError]
89+
);
90+
91+
const handleDelete = (teamId: string): void => {
92+
setTeamsData(teamsData.filter((team) => team.id !== teamId));
93+
};
94+
95+
const handleAdd = (_: string, value: Team): void => {
96+
setTeamsData((prevData: Team[]) => {
97+
const isDuplicate = prevData.some((team) => team.id === value.id);
98+
if (isDuplicate) {
99+
setError(true);
100+
return prevData;
101+
}
102+
setError(false);
103+
return [...prevData, value];
104+
});
105+
setInputValue('');
106+
};
107+
108+
const handleInputChange = (_: string, value: string): void => {
109+
if (typeof value === 'string') {
110+
setError(false);
111+
fetchSuggestions(value);
112+
}
113+
};
114+
115+
useEffect(() => {
116+
fetchSuggestions('');
117+
}, [fetchSuggestions, orgID]);
118+
119+
const toggleShowAllTeams = (): void => {
120+
setShowAllTeams((prev) => !prev);
121+
};
122+
123+
return (
124+
<>
125+
<Autocomplete
126+
disabled={disabled}
127+
disablePortal
128+
id="team-search-field"
129+
sx={{ width: 'auto' }}
130+
disableClearable
131+
loading={isLoading}
132+
value={inputValue}
133+
filterSelectedOptions
134+
noOptionsText={isLoading ? 'Loading...' : 'No team found'}
135+
onChange={handleAdd}
136+
onInputChange={handleInputChange}
137+
options={options}
138+
filterOptions={(x) => x}
139+
getOptionLabel={() => ''}
140+
clearOnBlur
141+
isOptionEqualToValue={(option, value) => option === value}
142+
renderInput={(params) => (
143+
<TextField
144+
{...params}
145+
label={label || 'Add Team'}
146+
error={error}
147+
helperText={error ? 'Team Already Selected' : ''}
148+
fullWidth
149+
InputProps={{
150+
...params.InputProps,
151+
endAdornment: isLoading ? <CircularProgress color="inherit" size={20} /> : null
152+
}}
153+
/>
154+
)}
155+
renderOption={(props, option) => {
156+
if (!option?.deleted_at.Valid) {
157+
return (
158+
<Box
159+
component="li"
160+
sx={{ '& > img': { mr: 2, flexShrink: 0 } }}
161+
key={option.ID}
162+
{...props}
163+
>
164+
<Typography>{option.name}</Typography>
165+
</Box>
166+
);
167+
}
168+
return null;
169+
}}
170+
/>
171+
<TeamListContainer hasTeams={teamsData.length > 0}>
172+
{teamsData.length > 0 && (
173+
<>
174+
{showAllTeams ? (
175+
<>
176+
{teamsData.map((team) => (
177+
<Chip
178+
key={team.ID}
179+
label={team.name}
180+
size="small"
181+
onDelete={() => handleDelete(team.id)}
182+
deleteIcon={
183+
<Tooltip title="Remove Team">
184+
<CloseIcon style={iconSmall} />
185+
</Tooltip>
186+
}
187+
/>
188+
))}
189+
<ToggleButton onClick={toggleShowAllTeams}>Hide</ToggleButton>
190+
</>
191+
) : (
192+
<Chip
193+
label={teamsData[teamsData.length - 1].name}
194+
size="small"
195+
onDelete={() => handleDelete(teamsData[teamsData.length - 1].id)}
196+
deleteIcon={
197+
<Tooltip title="Remove Team">
198+
<CloseIcon style={iconSmall} />
199+
</Tooltip>
200+
}
201+
/>
202+
)}
203+
{!showAllTeams && teamsData.length > 1 && (
204+
<ToggleButton onClick={toggleShowAllTeams}>(+{teamsData.length - 1})</ToggleButton>
205+
)}
206+
</>
207+
)}
208+
</TeamListContainer>
209+
</>
210+
);
211+
};
212+
213+
export default TeamSearchField;

0 commit comments

Comments
 (0)