Skip to content

Commit

Permalink
fix: move components into separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyWMitchell committed Dec 13, 2024
1 parent de917de commit b5481f1
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 333 deletions.
339 changes: 6 additions & 333 deletions src/smart-components/access-management/EditUserGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,345 +1,18 @@
import ContentHeader from '@patternfly/react-component-groups/dist/esm/ContentHeader';
import { FormGroup, PageSection, PageSectionVariants, Pagination, Tab, Tabs } from '@patternfly/react-core';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PageSection, PageSectionVariants } from '@patternfly/react-core';
import React, { useEffect } from 'react';
import { useIntl } from 'react-intl';
import Messages from '../../Messages';
import { FormRenderer, UseFieldApiConfig, componentTypes, useFieldApi, useFormApi, validatorTypes } from '@data-driven-forms/react-form-renderer';
import { FormRenderer, componentTypes, validatorTypes } from '@data-driven-forms/react-form-renderer';
import componentMapper from '@data-driven-forms/pf4-component-mapper/component-mapper';
import { FormTemplate } from '@data-driven-forms/pf4-component-mapper';
import { DataView, DataViewTable, DataViewToolbar, useDataViewPagination, useDataViewSelection } from '@patternfly/react-data-view';
import { useDispatch, useSelector } from 'react-redux';
import { fetchGroup, fetchGroups, fetchServiceAccountsForGroup, updateGroup } from '../../redux/actions/group-actions';
import { fetchGroup, fetchGroups, updateGroup } from '../../redux/actions/group-actions';
import { RBACStore } from '../../redux/store';
import { useNavigate, useParams } from 'react-router-dom';
import { fetchUsers } from '../../redux/actions/user-actions';
import { mappedProps } from '../../helpers/shared/helpers';
import { fetchServiceAccounts } from '../../redux/actions/service-account-actions';
import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
import { ServiceAccountsState } from '../../redux/reducers/service-account-reducer';
import { LAST_PAGE, ServiceAccount } from '../../helpers/service-account/service-account-helper';
import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups';
import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat';
import { EditGroupUsersAndServiceAccounts } from './EditUserGroupUsersAndServiceAccounts';

interface Diff {
added: string[];
removed: string[];
}

interface UsersTableProps {
onChange: (userDiff: Diff) => void;
groupId: string;
}

const PER_PAGE_OPTIONS = [
{ title: '5', value: 5 },
{ title: '10', value: 10 },
{ title: '20', value: 20 },
{ title: '50', value: 50 },
{ title: '100', value: 100 },
];

const UsersTable: React.FunctionComponent<UsersTableProps> = ({ onChange, groupId }) => {
const dispatch = useDispatch();
const pagination = useDataViewPagination({ perPage: 20 });
const { page, perPage, onSetPage, onPerPageSelect } = pagination;
const initialUserIds = useRef<string[]>([]);

const selection = useDataViewSelection({
matchOption: (a, b) => a.id === b.id,
});
const { selected, onSelect, isSelected } = selection;

useEffect(() => {
return () => {
onSelect(false);
initialUserIds.current = [];
};
}, []);

const { users, groupUsers, totalCount } = useSelector((state: RBACStore) => ({
users: state.userReducer?.users?.data || [],
groupUsers: state.groupReducer?.selectedGroup?.members?.data || [],
totalCount: state.userReducer?.users?.meta?.count,
}));

const rows = useMemo(
() =>
users.map((user) => ({
id: user.username,
row: [user.is_org_admin ? 'Yes' : 'No', user.username, user.email, user.first_name, user.last_name, user.is_active ? 'Active' : 'Inactive'],
})),
[users, groupId]
);

const fetchData = useCallback(
(apiProps: { count: number; limit: number; offset: number; orderBy: string }) => {
const { count, limit, offset, orderBy } = apiProps;
dispatch(fetchUsers({ ...mappedProps({ count, limit, offset, orderBy }), usesMetaInURL: true }));
},
[dispatch]
);

useEffect(() => {
fetchData({
limit: perPage,
offset: (page - 1) * perPage,
orderBy: 'username',
count: totalCount || 0,
});
}, [fetchData, page, perPage]);

useEffect(() => {
onSelect(false);
initialUserIds.current = [];
const initialSelectedUsers = groupUsers.map((user) => ({ id: user.username }));
onSelect(true, initialSelectedUsers);
initialUserIds.current = initialSelectedUsers.map((user) => user.id);
}, [groupUsers]);

useEffect(() => {
const selectedUserIds = selection.selected.map((user) => user.id);
const added = selectedUserIds.filter((id) => !initialUserIds.current.includes(id));
const removed = initialUserIds.current.filter((id) => !selectedUserIds.includes(id));
onChange({ added, removed });
}, [selection.selected]);

const pageSelected = rows.length > 0 && rows.every(isSelected);
const pagePartiallySelected = !pageSelected && rows.some(isSelected);
const handleBulkSelect = (value: BulkSelectValue) => {
if (value === BulkSelectValue.none) {
onSelect(false);
} else if (value === BulkSelectValue.page) {
onSelect(true, rows);
} else if (value === BulkSelectValue.nonePage) {
onSelect(false, rows);
}
};

return (
<DataView selection={{ ...selection }}>
<DataViewToolbar
pagination={
<Pagination
perPageOptions={PER_PAGE_OPTIONS}
itemCount={totalCount}
page={page}
perPage={perPage}
onSetPage={onSetPage}
onPerPageSelect={onPerPageSelect}
/>
}
bulkSelect={
<BulkSelect
isDataPaginated
pageCount={users.length}
selectedCount={selected.length}
totalCount={totalCount}
pageSelected={pageSelected}
pagePartiallySelected={pagePartiallySelected}
onSelect={handleBulkSelect}
/>
}
/>
<DataViewTable variant="compact" columns={['Org Admin', 'Username', 'Email', 'First name', 'Last name', 'Status']} rows={rows} />
</DataView>
);
};

interface ServiceAccountsTableProps {
groupId?: string;
onChange: (serviceAccounts: Diff) => void;
}

const reducer = ({ serviceAccountReducer }: { serviceAccountReducer: ServiceAccountsState }) => ({
serviceAccounts: serviceAccountReducer.serviceAccounts,
status: serviceAccountReducer.status,
isLoading: serviceAccountReducer.isLoading,
limit: serviceAccountReducer.limit,
offset: serviceAccountReducer.offset,
});

const ServiceAccountsTable: React.FunctionComponent<ServiceAccountsTableProps> = ({ groupId, onChange }) => {
const dispatch = useDispatch();
const pagination = useDataViewPagination({ perPage: 20 });
const { page, perPage, onSetPage, onPerPageSelect } = pagination;
const { auth, getEnvironmentDetails } = useChrome();
const initialServiceAccountIds = useRef<string[]>([]);

const selection = useDataViewSelection({
matchOption: (a, b) => a.id === b.id,
});
const { onSelect, selected } = selection;

useEffect(() => {
return () => {
onSelect(false);
initialServiceAccountIds.current = [];
};
}, []);

const { serviceAccounts, status } = useSelector(reducer);
const calculateTotalCount = () => {
if (!serviceAccounts) return 0;
const currentCount = (page - 1) * perPage + serviceAccounts.length;
return status === LAST_PAGE ? currentCount : currentCount + 1;
};
const totalCount = calculateTotalCount();

const { groupServiceAccounts: groupServiceAccounts } = useSelector((state: RBACStore) => ({
groupServiceAccounts: state.groupReducer?.selectedGroup?.serviceAccounts?.data || [],
}));

const fetchData = useCallback(
async (apiProps: { count: number; limit: number; offset: number; orderBy: string }) => {
if (groupId) {
const { count, limit, offset, orderBy } = apiProps;
const env = getEnvironmentDetails();
const token = await auth.getToken();
dispatch(fetchServiceAccounts({ ...mappedProps({ count, limit, offset, orderBy, token, sso: env?.sso }) }));
dispatch(fetchServiceAccountsForGroup(groupId, {}));
}
},
[dispatch, groupId]
);

useEffect(() => {
fetchData({
limit: perPage,
offset: (page - 1) * perPage,
orderBy: 'username',
count: totalCount || 0,
});
}, [fetchData, page, perPage]);

const processedServiceAccounts = serviceAccounts ? serviceAccounts.slice(0, perPage) : [];
const rows = useMemo(
() =>
processedServiceAccounts.map((account: ServiceAccount) => ({
id: account.uuid,
row: [
account.name,
account.description,
account.clientId,
account.createdBy,
<DateFormat key={`${account.name}-date`} date={account.createdAt} />,
],
})),
[processedServiceAccounts, groupId]
);

useEffect(() => {
// on mount, select the accounts that are in the current group
onSelect(false);
initialServiceAccountIds.current = [];
const initialSelectedServiceAccounts = groupServiceAccounts.map((account) => ({ id: account.clientId }));
onSelect(true, initialSelectedServiceAccounts);
initialServiceAccountIds.current = initialSelectedServiceAccounts.map((account) => account.id);
}, [groupServiceAccounts]);

useEffect(() => {
const selectedServiceAccountIds = selection.selected.map((account) => account.id);
const added = selectedServiceAccountIds.filter((id) => !initialServiceAccountIds.current.includes(id));
const removed = initialServiceAccountIds.current.filter((id) => !selectedServiceAccountIds.includes(id));
onChange({ added, removed });
}, [selection.selected]);

const handleBulkSelect = (value: BulkSelectValue) => {
if (value === BulkSelectValue.none) {
onSelect(false);
} else if (value === BulkSelectValue.page) {
onSelect(true, rows);
} else if (value === BulkSelectValue.nonePage) {
onSelect(false, rows);
}
};

const pageSelected = rows.length > 0 && rows.every((row) => selection.isSelected(row));
const pagePartiallySelected = !pageSelected && rows.some((row) => selection.isSelected(row));

return (
<DataView selection={{ ...selection }}>
<DataViewToolbar
pagination={
<Pagination
isCompact
perPageOptions={PER_PAGE_OPTIONS}
itemCount={totalCount}
page={page}
perPage={perPage}
onSetPage={onSetPage}
onPerPageSelect={onPerPageSelect}
toggleTemplate={() => {
const firstIndex = (page - 1) * perPage + 1;
const lastIndex = Math.min(page * perPage, totalCount);
const totalNumber = status === LAST_PAGE ? (page - 1) * perPage + serviceAccounts.length : 'many';
return (
<React.Fragment>
<b>
{firstIndex} - {lastIndex}
</b>{' '}
of <b>{totalNumber}</b>
</React.Fragment>
);
}}
/>
}
bulkSelect={
<BulkSelect
pageCount={serviceAccounts.length}
selectedCount={selected.length}
totalCount={totalCount}
pageSelected={pageSelected}
pagePartiallySelected={pagePartiallySelected}
onSelect={handleBulkSelect}
/>
}
/>
<DataViewTable variant="compact" columns={['Name', 'Description', 'Client ID', 'Owner', 'Time created']} rows={rows} />
</DataView>
);
};

const EditGroupUsersAndServiceAccounts: React.FunctionComponent<UseFieldApiConfig> = (props) => {
const [activeTabKey, setActiveTabKey] = useState(0);
const formOptions = useFormApi();
const { input, groupId } = useFieldApi(props);
const values = formOptions.getState().values[input.name];

const handleUserChange = (users: Diff) => {
input.onChange({
users,
serviceAccounts: values?.serviceAccounts,
});
};

const handleServiceAccountsChange = (serviceAccounts: Diff) => {
input.onChange({
users: values?.users,
serviceAccounts,
});
};

const handleTabSelect = (_: React.MouseEvent<HTMLElement, MouseEvent>, key: string | number) => {
setActiveTabKey(Number(key));
};

return (
<React.Fragment>
<FormGroup label="Select users and/or service accounts">
<Tabs activeKey={activeTabKey} onSelect={handleTabSelect}>
<Tab eventKey={0} title="Users">
<UsersTable groupId={groupId} onChange={handleUserChange} />
</Tab>
<Tab eventKey={1} title="Service accounts">
<ServiceAccountsTable groupId={groupId} onChange={handleServiceAccountsChange} />
</Tab>
</Tabs>
</FormGroup>
</React.Fragment>
);
};

const EditUserGroup: React.FunctionComponent = () => {
export const EditUserGroup: React.FunctionComponent = () => {
const intl = useIntl();
const dispatch = useDispatch();
const params = useParams();
Expand Down
Loading

0 comments on commit b5481f1

Please sign in to comment.