Skip to content

Commit

Permalink
User details update (#1285)
Browse files Browse the repository at this point in the history
* Add antd pro-design

* Registering a checkmark

* Add url to new user view details

* silence table stripping for a more crisp clean look

* Second iteration ideating data flow

* Export fhirDataType components

* Update user management constantts

* Add second iteration of user detail views

* Move userDeleteWith popup confirm button to own component

* Add react import to user delete btn component file

* Change leave text on group detail view to red

* Test edit btn works

* Fix roleDetail view and add tests

* Temporarily remove location details view

* Make description list responsive

* Replace previous view details components

* Wrap microcopy in translator function

* Recognize practitionerDetail fhir resource in rbac

* Integrate rbac into user detail view

* Fix import

* Fix permission string

* Update tests

* Fix practitionerDetails endpoint

* Fix url collision to user details

* Remove surplus pro-components library

* Fix user details url route

* Invalidate only query that refetches user queries

* Code cleanup

* Update tests

* Update lock file

* Revert "Remove surplus pro-components library"

This reverts commit acd64b8.

* Fix lint issues
  • Loading branch information
peterMuriuki authored Jan 22, 2024
1 parent cd3facc commit 405e57e
Show file tree
Hide file tree
Showing 30 changed files with 2,198 additions and 722 deletions.
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@2fd/ant-design-icons": "^2.6.0",
"@ant-design/icons": "^4.7.0",
"@ant-design/pro-components": "^2.6.32",
"@onaio/connected-private-route": "^0.0.11",
"@onaio/connected-reducer-registry": "^0.0.3",
"@onaio/gatekeeper": "1.0.0",
Expand Down
10 changes: 10 additions & 0 deletions app/src/App/fhir-apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ import {
import {
CreateEditUser as FHIRConnectedCreateEditUser,
UserList as FhirUserList,
USER_DETAILS_URL,
UserDetailsV2,
} from '@opensrp/fhir-user-management';
import { Home } from '../containers/pages/Home/Home';
import {
Expand Down Expand Up @@ -224,6 +226,14 @@ const FHIRApps = () => {
permissions={['iam_user.read']}
component={FhirUserList}
/>
<PrivateComponent
redirectPath={APP_CALLBACK_URL}
disableLoginProtection={DISABLE_LOGIN_PROTECTION}
exact
path={`${USER_DETAILS_URL}/:id`}
permissions={['iam_user.read']}
component={UserDetailsV2}
/>
<PrivateComponent
redirectPath={APP_CALLBACK_URL}
disableLoginProtection={DISABLE_LOGIN_PROTECTION}
Expand Down
1 change: 1 addition & 0 deletions packages/fhir-keycloak-user-management/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dist"
],
"peerDependencies": {
"@ant-design/pro-components": "^2.6.32",
"@opensrp/store": "^0.0.10",
"antd": "^5.5.1",
"i18next": "^19.8.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Popconfirm, Button } from 'antd';
import { KEYCLOAK_URL_USERS } from '@opensrp/user-management';
import { sendErrorNotification } from '@opensrp/notifications';
import { deleteUser } from '../UserList/ListView/utils';
import { useTranslation } from '../../mls';
import { useQueryClient } from 'react-query';
import React from 'react';

export interface UserDeleteBtnProp {
afterActions?: () => void;
keycloakBaseUrl: string;
fhirBaseUrl: string;
resourceId: string;
}

export const UserDeleteBtn = (props: UserDeleteBtnProp) => {
const { afterActions, keycloakBaseUrl, fhirBaseUrl, resourceId } = props;
const { t } = useTranslation();
const queryClient = useQueryClient();

return (
<Popconfirm
key="delete-user"
title={t('Are you sure you want to delete this user?')}
okText={t('Yes')}
cancelText={t('No')}
onConfirm={async () => {
await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t);
try {
return await queryClient.invalidateQueries({
queryKey: [KEYCLOAK_URL_USERS],
exact: true,
});
} catch {
return sendErrorNotification(
t('Failed to update data, please refresh the page to see the most recent changes')
);
} finally {
afterActions?.();
}
}}
>
<Button data-testid="delete-user" danger type="link">
{t('Delete')}
</Button>
</Popconfirm>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import {
TableLayout,
BrokenPage,
searchQuery,
useSearchParams,
viewDetailsQuery,
} from '@opensrp/react-utils';
import { PlusOutlined } from '@ant-design/icons';
import { URL_USER_CREATE, KEYCLOAK_URL_USERS } from '@opensrp/user-management';
Expand All @@ -20,8 +18,6 @@ import { PageHeader } from '@opensrp/react-utils';
import { getExtraData } from '@onaio/session-reducer';
import { KeycloakUser } from '@opensrp/user-management';
import { useSelector } from 'react-redux';
import { ViewDetailsWrapper } from '../ViewDetails';
import { useQueryClient } from 'react-query';
import { useTranslation } from '@opensrp/i18n';
import { RbacCheck, useUserRole } from '@opensrp/rbac';

Expand All @@ -41,13 +37,9 @@ export const UserList = (props: OrganizationListProps) => {
const history = useHistory();
const match = useRouteMatch();
const extraData = useSelector(getExtraData);
const queryClient = useQueryClient();
const { t } = useTranslation();
const userRole = useUserRole();

const { sParams, addParam } = useSearchParams();
const resourceId = sParams.get(viewDetailsQuery) ?? undefined;

const { isLoading, data, error, isFetching } = useQuery([KEYCLOAK_URL_USERS], () =>
loadKeycloakResources(keycloakBaseURL, KEYCLOAK_URL_USERS)
);
Expand Down Expand Up @@ -77,17 +69,7 @@ export const UserList = (props: OrganizationListProps) => {
};
});

const onViewDetails = (resourceId: string) => addParam(viewDetailsQuery, resourceId);
const columns = getTableColumns(
keycloakBaseURL,
fhirBaseURL,
extraData,
queryClient,
t,
onViewDetails,
userRole,
history
);
const columns = getTableColumns(keycloakBaseURL, fhirBaseURL, extraData, t, userRole, history);

const searchFormProps = {
defaultValue: getQueryParams(location)[searchQuery],
Expand Down Expand Up @@ -134,11 +116,6 @@ export const UserList = (props: OrganizationListProps) => {
</div>
<TableLayout {...tableProps} />
</Col>
<ViewDetailsWrapper
resourceId={resourceId}
fhirBaseUrl={fhirBaseURL}
keycloakBaseUrl={keycloakBaseURL}
/>
</Row>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
import * as React from 'react';
import { Popconfirm, Divider, Dropdown, Button } from 'antd';
import { Divider, Dropdown, Button } from 'antd';
import type { MenuProps } from 'antd';
import { MoreOutlined } from '@ant-design/icons';
import { deleteUser } from './utils';
import { Link } from 'react-router-dom';
import {
KeycloakUser,
URL_USER_EDIT,
KEYCLOAK_URL_USERS,
URL_USER_CREDENTIALS,
} from '@opensrp/user-management';
import { KeycloakUser, URL_USER_EDIT, URL_USER_CREDENTIALS } from '@opensrp/user-management';
import { Dictionary } from '@onaio/utils';
import { Column } from '@opensrp/react-utils';
import { sendErrorNotification } from '@opensrp/notifications';
import { QueryClient } from 'react-query';
import type { TFunction } from '@opensrp/i18n';
import { RbacCheck, UserRole } from '@opensrp/rbac';
import { History } from 'history';
import { UserDeleteBtn } from '../../UserDeleteBtn';
import { USER_DETAILS_URL } from '../../../constants';

/**
* Get table columns for user list
*
* @param keycloakBaseUrl - keycloak base url
* @param baseUrl - server base url
* @param extraData - session data about logged in user
* @param queryClient - react query client
* @param t - translator function
* @param onViewDetails - callback when view details is clicked.
* @param userRole - role of logged in user.
* @param history - history object for managing navigation
*/
export const getTableColumns = (
keycloakBaseUrl: string,
baseUrl: string,
extraData: Dictionary,
queryClient: QueryClient,
t: TFunction,
onViewDetails: (recordId: string) => void,
userRole: UserRole,
history: History
): Column<KeycloakUser>[] => {
Expand All @@ -61,44 +51,16 @@ export const getTableColumns = (
});

const getItems = (record: KeycloakUser): MenuProps['items'] => {
return [
const items = [
{
key: '1',
permissions: [],
permissions: ['iam_user.read'],
label: (
<Button onClick={() => onViewDetails(record.id)} type="link">
<Button onClick={() => history.push(`${USER_DETAILS_URL}/${record.id}`)} type="link">
{t('View Details')}
</Button>
),
},
{
key: '2',
permissions: ['iam_user.delete'],
label: (
<Popconfirm
title={t('Are you sure you want to delete this user?')}
okText={t('Yes')}
cancelText={t('No')}
onConfirm={async () => {
await deleteUser(keycloakBaseUrl, baseUrl, record.id, t);
try {
return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]);
} catch {
return sendErrorNotification(
t('Failed to update data, please refresh the page to see the most recent changes')
);
}
}}
>
{user_id &&
(record.id === user_id ? null : (
<Button data-testid="delete-user" danger type="link" style={{ color: '#' }}>
{t('Delete')}
</Button>
))}
</Popconfirm>
),
},
{
key: '3',
permissions: ['iam_user.update'],
Expand All @@ -114,7 +76,22 @@ export const getTableColumns = (
</Button>
),
},
]
];
// don't show delete for the current logged in user - back compatibility reasons.
if (user_id && record.id !== user_id) {
items.push({
key: '2',
permissions: ['iam_user.delete'],
label: (
<UserDeleteBtn
keycloakBaseUrl={keycloakBaseUrl}
fhirBaseUrl={baseUrl}
resourceId={record.id}
/>
),
});
}
return items
.filter((item) => userRole.hasPermissions(item.permissions))
.map((item) => {
const { permissions, ...rest } = item;
Expand Down
Loading

0 comments on commit 405e57e

Please sign in to comment.