Skip to content

Commit

Permalink
added search-dropdown in add user modal (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssb-jnk authored Mar 22, 2024
1 parent 33e97af commit e3f3c68
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 40 deletions.
108 changes: 69 additions & 39 deletions src/pages/TeamDetail/TeamDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
Divider,
Tabs,
Button,
Input,
Dropdown,
Tag,
Link,
Expand All @@ -40,6 +39,7 @@ import { XCircle } from 'react-feather'
import FormattedTableColumn from '../../components/FormattedTableColumn'
import SidebarModal from '../../components/SidebarModal/SidebarModal'
import DeleteLink from '../../components/DeleteLink/DeleteLink'
import { fetchUserSearchData, User } from '../../services/teamMembers'

interface UserInfo {
name?: string
Expand Down Expand Up @@ -83,11 +83,14 @@ const SHARED_BUCKETS_TAB = {
],
}

const defaultEmail = {
key: 'add-user-email',
const defaultSelectedUserDropdown = {
key: 'add-selected-user',
error: false,
errorMessage: `Ugyldig epost`,
value: '',
errorMessage: `Ugyldig navn`,
}
const defaultSelectedUser = {
id: 'search',
title: 'Søk ...',
}

const defaultAddUserKey = 'add-user-selected-group'
Expand All @@ -104,7 +107,9 @@ const TeamDetail = () => {
const { setBreadcrumbTeamDetailDisplayName } = useContext(DaplaCtrlContext)
const [error, setError] = useState<ApiError | undefined>()
const [loadingTeamData, setLoadingTeamData] = useState<boolean>(true)
const [loadingUsers, setLoadingUsers] = useState<boolean>(false)
const [teamDetailData, setTeamDetailData] = useState<TeamDetailData>()
const [userData, setUserData] = useState<User[]>()
const [teamDetailTableTitle, setTeamDetailTableTitle] = useState<string>(TEAM_USERS_TAB.title)
const [teamDetailTableHeaderColumns, setTeamDetailTableHeaderColumns] = useState<TableData['columns']>(
TEAM_USERS_TAB.columns
Expand All @@ -113,7 +118,8 @@ const TeamDetail = () => {

// Add users to team
const [openAddUserSidebarModal, setOpenAddUserSidebarModal] = useState<boolean>(false)
const [email, setEmail] = useState(defaultEmail)
const [selectedUserDropdown, setSelectedUserDropdown] = useState(defaultSelectedUserDropdown)
const [selectedUser, setSelectedUser] = useState(defaultSelectedUser)
const [selectedGroupAddUser, setSelectedGroupAddUser] = useState({
...defaultSelectedGroup,
key: defaultAddUserKey,
Expand Down Expand Up @@ -281,6 +287,31 @@ const TeamDetail = () => {
}
}, [prepTeamData])

const getUsersAutoCompleteData = () => {
if (userData) return
setLoadingUsers(true)
fetchUserSearchData()
.then((users) => {
const filteredUsers = users.filter(
(allUsers) =>
!(teamDetailData?.team as Team).users?.some(
(teamUsers) => allUsers.principal_name === teamUsers.principal_name
)
)
setUserData(filteredUsers)
})
.catch((error) => {
setError(error as ApiError)
})
.finally(() => setLoadingUsers(false))
}

useEffect(() => {
if (openAddUserSidebarModal) {
getUsersAutoCompleteData()
}
}, [openAddUserSidebarModal])

const handleTabClick = (tab: string) => setActiveTab(tab)

const renderErrorAlert = () => {
Expand Down Expand Up @@ -330,6 +361,11 @@ const TeamDetail = () => {
}
}

const handleAddUser = (item: DropdownItems) => {
setSelectedUserDropdown({ ...selectedUserDropdown, key: `${defaultSelectedUserDropdown.key}-${item.id}` })
setSelectedUser(item)
}

const removeDuplicateDropdownItems = (items: DropdownItems[]) => {
return items.reduce((acc: DropdownItems[], dropdownItem: DropdownItems) => {
const ids = acc.map((obj) => obj.id)
Expand Down Expand Up @@ -379,20 +415,20 @@ const TeamDetail = () => {
}

const handleAddUserOnSubmit = () => {
if (email.value === '') setEmail({ ...email, error: true })
const isSelectedUserValid = selectedUser.id !== 'search'
if (!isSelectedUserValid) setSelectedUserDropdown({ ...selectedUserDropdown, error: true })
if (!teamGroupTags.length)
setTeamGroupTagsError({
...teamGroupTagsError,
error: true,
})

if (email.value !== '' && teamGroupTags.length) {
setEmail({ ...email, key: `add-user-${email.value}` })
if (isSelectedUserValid && teamGroupTags.length) {
setAddUserToTeamErrors([])
setShowAddUserSpinner(true)
addUserToGroups(
teamGroupTags.map((group) => group.id),
email.value
selectedUser.id
)
.then((response) => {
const errorsList = getErrorList(response)
Expand All @@ -402,7 +438,7 @@ const TeamDetail = () => {
setOpenAddUserSidebarModal(false)
setTeamGroupTags([])
// Reset fields with their respective keys; re-initializes component
setEmail({ ...defaultEmail })
setSelectedUserDropdown({ ...defaultSelectedUserDropdown })
setSelectedGroupAddUser({ ...defaultSelectedGroup, key: defaultAddUserKey })
}
})
Expand Down Expand Up @@ -540,13 +576,6 @@ const TeamDetail = () => {
)
}

const isUserInputValid = (value?: string) => {
const regEx = /^[\w-]+@ssb\.no$/
const userVal = value || email.value
const testUser = userVal.match(regEx)
return !!testUser
}

const teamModalHeader = teamDetailData
? {
modalType: 'Medlem',
Expand All @@ -572,27 +601,28 @@ const TeamDetail = () => {
modalBodyTitle: 'Legg person til teamet',
modalBody: (
<>
<Input
key={email.key}
className={styles.inputSpacing}
label='Kort epost'
value={email.value}
error={email.error}
errorMessage={email.errorMessage}
onBlur={() =>
setEmail({
...email,
error: !isUserInputValid(),
})
}
handleChange={(value: string) =>
setEmail({
...email,
value,
error: email.error ? !isUserInputValid(value) : false,
})
}
/>
{!loadingUsers ? (
<Dropdown
key={selectedUserDropdown.key}
className={styles.inputSpacing}
header='Navn'
selectedItem={selectedUser}
items={userData?.map(({ principal_name, display_name }) => {
return {
id: principal_name,
title: formatDisplayName(display_name),
}
})}
onSelect={(item: DropdownItems) => handleAddUser(item)}
error={selectedUserDropdown.error}
errorMessage={selectedUserDropdown.errorMessage}
searchable
/>
) : (
<div className={styles.inputSpacing}>
<Skeleton variant='rectangular' animation='wave' height={65} />
</div>
)}
<Dropdown
key={selectedGroupAddUser.key}
className={styles.dropdownSpacing}
Expand Down
37 changes: 36 additions & 1 deletion src/services/teamMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface TeamMembersData {
[key: string]: UsersData // myUsers, allUsers
}

interface UsersData {
export interface UsersData {
users: User[]
}

Expand Down Expand Up @@ -171,3 +171,38 @@ export const fetchAllTeamMembersData = async (principalName: string): Promise<Te
}
}
}

export const fetchUserSearchData = async (): Promise<User[]> => {
const usersUrl = new URL(`${USERS_URL}`, window.location.origin)
const selects = ['display_name', 'principal_name', 'section_name']

usersUrl.searchParams.append('select', selects.join(','))

try {
const allUsersData = await fetchAPIData(usersUrl.toString())

if (!allUsersData) throw new ApiError(500, 'No json data returned')
if (!allUsersData._embedded || !allUsersData._embedded.users) throw new ApiError(500, 'Did not receive users data')

const prepData = allUsersData._embedded.users.map((user: User) => {
const prepUserData = {
...user,
...user._embedded,
}
delete prepUserData._embedded
return prepUserData
})
delete prepData._embedded

return prepData
} catch (error) {
if (error instanceof ApiError) {
console.error('Failed to fetch user search data:', error)
throw error
} else {
const apiError = new ApiError(500, 'An unexpected error occurred')
console.error('Failed to fetch user search data:', apiError)
throw apiError
}
}
}

0 comments on commit e3f3c68

Please sign in to comment.