Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: broken search filters and login method modal #154

Open
wants to merge 6 commits into
base: 0.14
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.14.0]

- Fixes an issue where the delete button (for login methods) would be visible when there is only one login method
- Modifies Modal message when deleting a login method
- Fixes search issues on User Management tab
- Fixes search issues on Tenant Management tab

## [0.13.0]

- Adds support for SAML metadata
Expand Down
75 changes: 44 additions & 31 deletions src/ui/components/search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type SearchType = {
type searchProp = {
onSearch: (paginationToken?: string, search?: object) => Promise<void>;
loading: boolean;
searchRef?: React.RefObject<{ getSearchQuery: () => Record<string, string> }>;
};

type action = "chn" | "del";
Expand Down Expand Up @@ -67,6 +68,40 @@ const tagToImg = (tag: string) => {
}
};

const generateSearchQuery = (searches: SearchType[]) => {
const tempQueryMap: Record<string, string> = {};
searches.forEach((el) => {
let value = el.value.trim();

if (el.tag === "phone") {
try {
const parsed = parsePhoneNumber(value);

if (parsed !== undefined) {
value = parsed.format("E.164");
}
} catch (e) {
let temp = value;

if (!temp.startsWith("+")) {
temp = "+" + temp;
}

temp = temp.replace(/[()\s]/g, "");
value = temp;
}
}

if (el.tag in tempQueryMap) {
const temp = tempQueryMap[el.tag] + ";" + value;
tempQueryMap[el.tag] = temp;
} else {
tempQueryMap[el.tag] = value;
}
});
return tempQueryMap;
};

const Search: React.FC<searchProp> = (props: searchProp) => {
const [active, setActive] = useState<boolean>(false);
const [searches, setSearches] = useState<SearchType[] | []>([]);
Expand All @@ -92,42 +127,20 @@ const Search: React.FC<searchProp> = (props: searchProp) => {
if (props.loading) {
return;
}

const tempQueryMap: Record<string, string> = {};
searches.forEach((el) => {
let value = el.value.trim();

if (el.tag === "phone") {
try {
const parsed = parsePhoneNumber(value);

if (parsed !== undefined) {
value = parsed.format("E.164");
}
} catch (e) {
let temp = value;

if (!temp.startsWith("+")) {
temp = "+" + temp;
}

temp = temp.replace(/[()\s]/g, "");
value = temp;
}
}

if (el.tag in tempQueryMap) {
const temp = tempQueryMap[el.tag] + ";" + value;
tempQueryMap[el.tag] = temp;
} else {
tempQueryMap[el.tag] = value;
}
});
const tempQueryMap = generateSearchQuery(searches);
await props.onSearch(undefined, tempQueryMap);
},
[searches]
);

useEffect(() => {
if (props.searchRef) {
props.searchRef.current = {
getSearchQuery: () => generateSearchQuery(searches),
};
}
}, [props.searchRef, searches]);

// useEffect to call everytime searches change
useEffect(() => {
getSearchResult(searches).catch(console.error);
Expand Down
4 changes: 4 additions & 0 deletions src/ui/components/userDetail/loginMethods/LoginMethods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ type MethodProps = {
updateContext: (val: LoginMethod, ind: number) => void;
index: number;
showUnlink: boolean;
showDelete: boolean;
};

const Methods: React.FC<MethodProps> = ({
Expand All @@ -146,6 +147,7 @@ const Methods: React.FC<MethodProps> = ({
updateContext,
index,
showUnlink,
showDelete,
}) => {
const { sendUserEmailVerification: sendUserEmailVerificationApi } = useVerifyUserTokenService();
const { showModal, showToast } = useContext(PopupContentContext);
Expand Down Expand Up @@ -252,6 +254,7 @@ const Methods: React.FC<MethodProps> = ({
onDelete={onDeleteCallback}
onUnlink={onUnlinkCallback}
showUnlink={showUnlink}
showDelete={showDelete}
/>
)}
{isEditing && (
Expand Down Expand Up @@ -462,6 +465,7 @@ export const LoginMethods: React.FC<LoginMethodProps> = ({ refetchAllData, refet
showUnlink={
methods.length > 1 || (userDetail.details.isPrimaryUser === true && methods.length === 1)
}
showDelete={methods.length > 1}
/>
))}
</LayoutPanel>
Expand Down
19 changes: 11 additions & 8 deletions src/ui/components/userDetail/loginMethods/components/dropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ type DropDOwnProps = {
onUnlink: () => void | null;
onDelete: () => void | null;
showUnlink: boolean;
showDelete: boolean;
};

export const DropDown = ({ onEdit, onUnlink, onDelete, showUnlink }: DropDOwnProps) => {
export const DropDown = ({ onEdit, onUnlink, onDelete, showUnlink, showDelete }: DropDOwnProps) => {
const [open, setOpen] = useState(false);
const [hover, setHover] = useState(false);
const ref = useRef(null);
Expand Down Expand Up @@ -47,13 +48,15 @@ export const DropDown = ({ onEdit, onUnlink, onDelete, showUnlink }: DropDOwnPro
Unlink
</div>
)}
<div onClick={() => close(onDelete)}>
<img
src={getImageUrl("delete-login-method.png")}
alt=""
/>{" "}
Delete
</div>
{showDelete && (
<div onClick={() => close(onDelete)}>
<img
src={getImageUrl("delete-login-method.png")}
alt=""
/>{" "}
Delete
</div>
)}
</div>
</div>
);
Expand Down
7 changes: 4 additions & 3 deletions src/ui/components/userDetail/userDetailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -513,10 +513,11 @@ export const UserDeleteConfirmation: FC<UserDeleteConfirmationProps> = ({ user,
return (
<div className="user-detail-form">
<p>
To delete the user, please confirm by typing the {inputType}: <span>{informationToEnter}</span> below
To delete the {loginMethod ? "login method" : "user"}, please confirm by typing the {inputType}:{" "}
<span>{informationToEnter}</span> below
</p>
<p>{""}</p>
<p>This will also delete any accounts linked to this user</p>
{!loginMethod && <p>This will also delete any accounts linked to this user</p>}
<div className="user-delete-input-container">
<InputField
type="text"
Expand Down Expand Up @@ -567,7 +568,7 @@ export const getUserDeleteConfirmationProps = (props: UserDeleteConfirmationTrig
onConfirmed={onConfirmedDelete}
/>
),
header: <h2>Delete User?</h2>,
header: <h2>Delete {loginMethod ? "login method" : "user"}?</h2>,
closeCallbackRef: closeConfirmDeleteRef,
} as LayoutModalProps;
};
Expand Down
5 changes: 5 additions & 0 deletions src/ui/pages/tenants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ const TenantList = ({
void getTenants();
}, []);

useEffect(() => {
// When user changes the search query, we reset the pagination to the first page
setCurrentActivePage(1);
}, [searchQuery]);

return (
<div className="tenants-container">
<h1 className="tenants-title">Tenant Management</h1>
Expand Down
8 changes: 7 additions & 1 deletion src/ui/pages/usersList/UsersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export const UsersList: React.FC<UserListProps> = ({
const [isSearch, setIsSearch] = useState<boolean>(false);
const [showCreateUserDialog, setShowCreateUserDialog] = useState(false);
const [paginationTokenByOffset, setPaginationTokenByOffset] = useState<NextPaginationTokenByOffset>({});
const searchRef = useRef<{ getSearchQuery: () => Record<string, string> }>(null);

const { fetchUsers } = useFetchUsersService();
const { fetchCount } = useFetchCount();
Expand Down Expand Up @@ -215,7 +216,11 @@ export const UsersList: React.FC<UserListProps> = ({
const loadCount = async () => {
setLoading(true);
const tenantId = getSelectedTenant();
const [countResult] = await Promise.all([fetchCount(tenantId).catch(() => undefined), loadUsers()]);
const searchQueryMap = searchRef?.current?.getSearchQuery();
const [countResult] = await Promise.all([
fetchCount(tenantId).catch(() => undefined),
loadUsers(undefined, searchQueryMap),
]);
if (countResult) {
setCount(countResult.count);
}
Expand Down Expand Up @@ -295,6 +300,7 @@ export const UsersList: React.FC<UserListProps> = ({
<Search
onSearch={loadUsers}
loading={loading}
searchRef={searchRef}
/>
)}

Expand Down
Loading