Skip to content

Commit a81f4d4

Browse files
authored
[RBLS-2837] UI improvements part 2 (#548)
Updated multiple UI components.
1 parent 4ab0e50 commit a81f4d4

File tree

8 files changed

+91
-59
lines changed

8 files changed

+91
-59
lines changed

audiences-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "audiences",
3-
"version": "2.1.0",
3+
"version": "2.2.0-alpha",
44
"description": "Audiences SCIM client",
55
"files": [
66
"dist/*.*",

audiences-react/src/AudienceForm/CriteriaList.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ export function CriteriaList({ onEditCriteria }: CriteriaListProps) {
99
const { value: context, removeCriteria } = useAudiencesContext()
1010

1111
const handleRemoveCriteria = (index: number) => {
12-
if (confirm("Remove criteria?")) {
13-
removeCriteria(index)
14-
}
12+
removeCriteria(index)
1513
}
1614

1715
return (

audiences-react/src/AudienceForm/MembersModalButton/index.tsx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ import type { GroupCriterion, ScimObject } from "../../types"
1616
import styles from "./style.module.css"
1717
import { useAudiencesContext } from "../../audiences"
1818

19+
/**
20+
* @description This has to be hardcoded because the API returns an object where the key for the user details
21+
* can only be accessed using this SCIM_USER_KEY constant. We should update this later.
22+
*/
23+
const SCIM_USER_KEY =
24+
"urn:ietf:params:scim:schemas:extension:authservice:2.0:User" as const
25+
1926
type MembersModalButtonProps = any & {
2027
criterion?: GroupCriterion
2128
title: React.ReactNode
@@ -75,7 +82,6 @@ export function MembersModalButton({
7582
<Body color="light" text={title} />
7683
</Flex>
7784
</Dialog.Header>
78-
7985
{current && (
8086
<Dialog.Body className="px-5">
8187
<Flex orientation="column" align="stretch">
@@ -85,16 +91,24 @@ export function MembersModalButton({
8591
value={search}
8692
/>
8793
<List className={styles.list}>
88-
{current.users.map((user: ScimObject, index: number) => (
89-
<ListItem key={`users-${index}`} padding="xs">
90-
<User
91-
avatar
92-
avatarUrl={get(user, "photos.0.value")}
93-
margin="none"
94-
name={user.displayName}
95-
/>
96-
</ListItem>
97-
))}
94+
{current.users.map((user: ScimObject, index: number) => {
95+
const extension =
96+
user[SCIM_USER_KEY] ||
97+
({} as ScimObject[typeof SCIM_USER_KEY])
98+
return (
99+
<ListItem key={`users-${index}`} padding="xs">
100+
<User
101+
avatar
102+
avatarUrl={get(user, "photos.0.value")}
103+
margin="none"
104+
name={user.displayName}
105+
orientation="horizontal"
106+
territory={extension?.territoryAbbr}
107+
title={user.title}
108+
/>
109+
</ListItem>
110+
)
111+
})}
98112
<ListItem>
99113
{current.users.length < current.count && (
100114
<Button

audiences-react/src/AudienceForm/ScimResourceTypeahead.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ function playbookOptions(objects: ScimObject[]): PlaybookOption[] {
1717
...object,
1818
value: parseInt(object.id),
1919
label: object.displayName,
20-
imageUrl: get(object, "photos.0.value"),
2120
}))
2221
: []
2322
}

audiences-react/src/AudienceForm/MobileTypeahead.tsx renamed to audiences-react/src/AudienceForm/UsersTypeahead.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import { useContext, useEffect, useRef } from "react"
44
import { components, SingleValueProps, OptionProps } from "react-select"
55
import Audiences from "../audiences"
66
import { ScimObject } from "../types"
7+
import { CSSObject } from "@emotion/react"
8+
9+
const customStyles = {
10+
menuPortal: (base: CSSObject) => ({
11+
...base,
12+
zIndex: 2000,
13+
}),
14+
}
715

816
/**
917
* @description This has to be hardcoded because the API returns an object where the key for the user details
@@ -17,31 +25,34 @@ type PlaybookOption = ScimObject & {
1725
label: string
1826
}
1927

20-
function playbookOptions(objects: ScimObject[]): PlaybookOption[] {
28+
function playbookOptions(
29+
objects: ScimObject[],
30+
isMobile?: boolean,
31+
): PlaybookOption[] {
2132
return objects
2233
? objects.map((object: ScimObject) => ({
2334
...object,
2435
value: parseInt(object.id),
2536
label: object.displayName,
26-
imageUrl: get(object, "photos.0.value"),
37+
imageUrl: !isMobile && get(object, "photos.0.value"),
2738
}))
2839
: []
2940
}
3041

31-
type MobileTypeaheadProps = {
42+
type UsersTypeaheadProps = {
3243
label: string
3344
value: ScimObject[]
3445
onChange: (values: ScimObject[]) => void
3546
resourceId: string
3647
isMobile?: boolean
3748
}
38-
export function MobileTypeahead({
49+
export function UsersTypeahead({
3950
resourceId,
4051
onChange,
4152
value,
4253
isMobile,
4354
...typeaheadProps
44-
}: MobileTypeaheadProps) {
55+
}: UsersTypeaheadProps) {
4556
const { query } = useContext(Audiences)!
4657

4758
function handleChange(value: any, ...event: any[]) {
@@ -52,7 +63,7 @@ export function MobileTypeahead({
5263
debounce(
5364
async (search: string, callback: (options: PlaybookOption[]) => void) => {
5465
const options = await query(resourceId, search)
55-
callback(playbookOptions(options))
66+
callback(playbookOptions(options, isMobile))
5667
},
5768
600,
5869
),
@@ -109,10 +120,13 @@ export function MobileTypeahead({
109120
loadOptions={loadOptions}
110121
placeholder=""
111122
{...typeaheadProps}
112-
value={playbookOptions(value)}
123+
value={playbookOptions(value, isMobile)}
113124
onChange={handleChange}
125+
menuPortalTarget={document.body}
126+
styles={customStyles}
114127
/>
115-
{value &&
128+
{isMobile &&
129+
value &&
116130
value.map((user: ScimObject) => {
117131
// NOTE: This is a workaround for the SCIM API's structure
118132
const extension =

audiences-react/src/AudienceForm/index.tsx

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { ScimObject } from "../types"
1212
import { toSentence } from "./toSentence"
1313

1414
import { ScimResourceTypeahead } from "./ScimResourceTypeahead"
15-
import { MobileTypeahead } from "./MobileTypeahead"
15+
import { UsersTypeahead } from "./UsersTypeahead"
1616
import { CriteriaList } from "./CriteriaList"
1717
import { CriteriaForm } from "./CriteriaForm"
1818
import { ActionBar } from "./ActionBar"
@@ -25,6 +25,7 @@ type AudienceFormProps = {
2525
allowIndividuals: boolean
2626
allowMatchAll: boolean
2727
isMobile?: boolean
28+
isPrivate?: boolean
2829
onSkip?: () => void
2930
}
3031

@@ -34,11 +35,11 @@ export const AudienceForm = ({
3435
allowIndividuals = true,
3536
allowMatchAll = true,
3637
isMobile,
38+
isPrivate,
3739
onSkip,
3840
}: AudienceFormProps) => {
3941
const [editing, setEditing] = useState<number>()
4042
const { error, value: context, change } = useAudiencesContext()
41-
4243
if (isEmpty(context)) {
4344
return null
4445
}
@@ -73,38 +74,31 @@ export const AudienceForm = ({
7374
)}
7475
{!context.match_all && (
7576
<>
76-
{allowIndividuals &&
77-
(isMobile ? (
78-
<MobileTypeahead
79-
label="Add Individuals"
80-
value={context.extra_users || []}
81-
onChange={(users: ScimObject[]) =>
82-
change("extra_users", users)
83-
}
84-
resourceId={userResource}
85-
/>
86-
) : (
87-
<ScimResourceTypeahead
88-
label="Add Individuals"
89-
value={context.extra_users || []}
90-
onChange={(users: ScimObject[]) =>
91-
change("extra_users", users)
92-
}
93-
resourceId={userResource}
94-
/>
95-
))}
96-
<CriteriaList onEditCriteria={setEditing} />
97-
<FlexItem alignSelf="center">
98-
<Button
99-
fixedWidth
100-
marginTop="md"
101-
paddingX={isMobile && "xs"}
102-
size={isMobile ? "sm" : "md"}
103-
onClick={() => setEditing(context.criteria.length)}
104-
text={`Add Members by ${toSentence(groupResources)}`}
105-
variant="link"
77+
{allowIndividuals && (
78+
<UsersTypeahead
79+
label="Add Individuals"
80+
value={context.extra_users || []}
81+
isMobile={isMobile}
82+
onChange={(users: ScimObject[]) =>
83+
change("extra_users", users)
84+
}
85+
resourceId={userResource}
10686
/>
107-
</FlexItem>
87+
)}
88+
<CriteriaList onEditCriteria={setEditing} />
89+
{isPrivate !== false && (
90+
<FlexItem alignSelf="center">
91+
<Button
92+
fixedWidth
93+
marginTop="md"
94+
paddingX={isMobile && "xs"}
95+
size={isMobile ? "sm" : "md"}
96+
onClick={() => setEditing(context.criteria.length)}
97+
text={`Add Members by ${toSentence(groupResources)}`}
98+
variant="link"
99+
/>
100+
</FlexItem>
101+
)}
108102
</>
109103
)}
110104
</Flex>

audiences-react/src/audiences.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,23 @@ export function useAudiences(
6969
search?: string,
7070
offset?: number,
7171
) {
72-
return get<{ count: number; users: ScimObject[] }>(
72+
const result = await get<{ count: number; users: ScimObject[] }>(
7373
`${key}/users/${criterion?.id || ""}?offset=${offset}&search=${search}`,
7474
)
75+
result.users = result.users.sort((a, b) =>
76+
a.displayName.localeCompare(b.displayName),
77+
)
78+
return result
7579
}
7680

7781
async function query(resourceId: string, displayName: string) {
78-
return await get<ScimObject[]>(`scim/${resourceId}?query=${displayName}`)
82+
const results = await get<ScimObject[]>(
83+
`scim/${resourceId}?query=${displayName}`,
84+
)
85+
if (resourceId !== "Territories") {
86+
return results.sort((a, b) => a.displayName.localeCompare(b.displayName))
87+
}
88+
return results
7989
}
8090

8191
async function save() {

audiences-react/src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type AudienceEditorProps = {
1919
allowMatchAll?: boolean
2020
fetchOptions?: Parameters<typeof useAudiences>[2]
2121
isMobile?: boolean
22+
isPrivate?: boolean
2223
onSkip?: () => void
2324
}
2425
export function AudienceEditor({
@@ -28,6 +29,7 @@ export function AudienceEditor({
2829
allowMatchAll = true,
2930
fetchOptions = {},
3031
isMobile = false,
32+
isPrivate,
3133
onSkip,
3234
}: AudienceEditorProps) {
3335
const audiencesUri = context ? uri : audiencesRoot(uri)
@@ -42,6 +44,7 @@ export function AudienceEditor({
4244
allowIndividuals={allowIndividuals}
4345
allowMatchAll={allowMatchAll}
4446
isMobile={isMobile}
47+
isPrivate={isPrivate}
4548
onSkip={onSkip}
4649
/>
4750
</Audiences.Provider>

0 commit comments

Comments
 (0)