Skip to content
This repository has been archived by the owner on Apr 18, 2023. It is now read-only.

Commit

Permalink
Default Permissions tab (PROJQUAY-4570)
Browse files Browse the repository at this point in the history
Signed-off-by: harishsurf <[email protected]>
  • Loading branch information
harishsurf committed Dec 2, 2022
1 parent 78938df commit ce61555
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 53 deletions.
29 changes: 29 additions & 0 deletions src/atoms/OrganizationListState.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
import {atom} from 'recoil';
import {SearchState} from 'src/components/toolbar/SearchTypes';
import {IOrganization} from 'src/resources/OrganizationResource';
import ColumnNames from 'src/routes/OrganizationsList/ColumnNames';

export const refreshPageState = atom({
key: 'refreshOrgPageState',
default: 0,
});

// Organization List page
export const selectedOrgsState = atom<IOrganization[]>({
key: 'selectedOrgsState',
default: [],
});
export const searchOrgsState = atom<SearchState>({
key: 'searchOrgsState',
default: {
query: '',
field: ColumnNames.name,
},
});

// OrgList -> Default Permissions tab
export const selectedPermissionsState = atom({
key: 'selectedPermissionsState',
default: [],
});
export const searchPermissionsState = atom<SearchState>({
key: 'searchPermissionsState',
default: {
query: '',
field: ColumnNames.name,
},
});
16 changes: 0 additions & 16 deletions src/atoms/UserState.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
import {atom} from 'recoil';
import {IOrganization} from 'src/resources/OrganizationResource';
import ColumnNames from 'src/routes/OrganizationsList/ColumnNames';
import {SearchState} from 'src/components/toolbar/SearchTypes';

export const CurrentUsernameState = atom({
key: 'currentUsernameState',
default: '',
});

export const selectedOrgsState = atom<IOrganization[]>({
key: 'selectedOrgsState',
default: [],
});

export const searchOrgsState = atom<SearchState>({
key: 'searchOrgsState',
default: {
query: '',
field: ColumnNames.name,
},
});
11 changes: 11 additions & 0 deletions src/resources/PermissionsResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface IPermission {
name: string;
}

export async function fetchDefaultPermissions() {
// const response: AxiosResponse<IUserResource> = await axios.get(
// '/api/v1/user/',
// );
// assertHttpCode(response.status, 200);
// return response.data;
}
64 changes: 32 additions & 32 deletions src/routes/OrganizationsList/Organization/Organization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@ import {
TabTitleText,
Title,
} from '@patternfly/react-core';
import {useLocation} from 'react-router-dom';
import {NavigationPath} from 'src/routes/NavigationPath';
import {useCallback, useState} from 'react';
import {useLocation, useNavigate} from 'react-router-dom';
import {useState} from 'react';
import RepositoriesList from 'src/routes/RepositoriesList/RepositoriesList';
import {QuayBreadcrumb} from 'src/components/breadcrumb/Breadcrumb';
import DefaultPermissions from './Tabs/DefaultPermissions/DefaultPermissions';

export default function Organization() {
const location = useLocation();
const repositoryName = location.pathname.split('/')[2];

const [activeTabKey, setActiveTabKey] = useState<string | number>(0);

const onTabSelect = useCallback(
(
_event: React.MouseEvent<HTMLElement, MouseEvent>,
tabIndex: string | number,
) => setActiveTabKey(tabIndex),
[],
);
const navigate = useNavigate();

// const onTabSelect = useCallback(
// (
// _event: React.MouseEvent<HTMLElement, MouseEvent>,
// tabIndex: string | number,
// ) => setActiveTabKey(tabIndex),
// [],
// );

enum OrgDetailTabs {
Repositories = 'repositories',
DefaultPermissions = 'permissions',
}

const repositoriesSubNav = [
{
href: NavigationPath.organizationDetail,
name: 'Repositories',
component: <RepositoriesList />,
},
// Commenting till needed.
// {
// href: NavigationPath.orgDetailUsageLogsTab,
// name: 'Usage Logs',
// component: <UsageLogsTab />,
// },
];
const onTabSelect = (e, tabIndex: string | number = 0) => {
setActiveTabKey(tabIndex);
navigate(
`${location.pathname}?tab=${Object.values(OrgDetailTabs)[tabIndex]}`,
);
};

return (
<Page>
Expand All @@ -57,15 +57,15 @@ export default function Organization() {
className="no-padding-on-sides"
>
<Tabs activeKey={activeTabKey} onSelect={onTabSelect}>
{repositoriesSubNav.map((nav, idx) => (
<Tab
key={idx}
eventKey={idx}
title={<TabTitleText>{nav.name}</TabTitleText>}
>
{nav.component}
</Tab>
))}
<Tab eventKey={0} title={<TabTitleText>Repositories</TabTitleText>}>
<RepositoriesList />
</Tab>
<Tab
eventKey={1}
title={<TabTitleText>Default permissions</TabTitleText>}
>
<DefaultPermissions />
</Tab>
</Tabs>
</PageSection>
</Page>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {Drawer, DrawerContent, DrawerContentBody} from '@patternfly/react-core';

export default function CreatePermissionModal(
props: CreatePermissionModalProps,
) {
const panelContent = {};
return (
<Drawer isExpanded={props.isExpanded} onExpand={props.onExpand}>
<DrawerContent panelContent={panelContent}>
<DrawerContentBody>content-body</DrawerContentBody>
<DrawerContentBody hasPadding>
content-body with padding
</DrawerContentBody>
<DrawerContentBody>content-body</DrawerContentBody>
</DrawerContent>
</Drawer>
);
}

interface CreatePermissionModalProps {
isExpanded?: boolean;
onExpand: () => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import {
PageSection,
PageSectionVariants,
TextContent,
Text,
TextVariants,
SearchInput,
Toolbar,
ToolbarContent,
Flex,
FlexItem,
Button,
} from '@patternfly/react-core';
import {
TableComposable,
Tbody,
Td,
Th,
Thead,
Tr,
} from '@patternfly/react-table';
import {useEffect, useState} from 'react';
import {useRecoilState, useResetRecoilState} from 'recoil';
import {
searchPermissionsState,
selectedPermissionsState,
} from 'src/atoms/OrganizationListState';
import {DropdownCheckbox} from 'src/components/toolbar/DropdownCheckbox';
import {SearchDropdown} from 'src/components/toolbar/SearchDropdown';
import {
fetchDefaultPermissions,
IPermission,
} from 'src/resources/PermissionsResource';
import CreatePermissionModal from './CreatePermissionModal';

export default function DefaultPermissions() {
const [permissionsList, setPermissionsList] = useState<IPermission[]>([]);

const [search, setSearch] = useRecoilState(searchPermissionsState);
const resetSearch = useResetRecoilState(searchPermissionsState);

const [isCreatePermModalOpen, setCreatePermModalOpen] = useState(true);

const filteredPermissionsList =
search.query !== ''
? permissionsList?.filter((permission) =>
permission.name.includes(search.query),
)
: permissionsList;

// Select checkbox related states
const [selectedPermissions, setSelectedPermissions] = useRecoilState(
selectedPermissionsState,
);
const isPermissionSelectable = (permission: IPermission) =>
permission.name !== ''; // Arbitrary logic for this example
const selectAllPermissions = (isSelecting = true) =>
setSelectedPermissions(
isSelecting ? filteredPermissionsList.map((p) => p.name) : [],
);

const setPermissionSelected = (permission: IPermission, isSelecting = true) =>
setSelectedPermissions((prevSelected) => {
const otherSelectedPermissions = prevSelected.filter(
(p) => p !== permission.name,
);
return isSelecting && isPermissionSelectable(permission)
? [...otherSelectedPermissions, permission.name]
: otherSelectedPermissions;
});

const isPermissionSelected = (permission: IPermission) =>
selectedPermissions.includes(permission.name);

const onSelectPermission = (
permission: IPermission,
rowIndex: number,
isSelecting: boolean,
) => {
setPermissionSelected(permission, isSelecting);
};

async function fetchPermissions() {
// clearing previous states
resetSearch();
setPermissionsList([]);
setSelectedPermissions([]);
try {
const user = await fetchDefaultPermissions();
// setUserState(user);
} catch (err) {
console.error(err);
}
}

useEffect(() => {
fetchPermissions();
}, []);

const permissionColumnNames = {
repoCreatedBy: 'Repository Created By',
permAppliedTo: 'Permission Applied To',
permission: 'Permission',
};

const createPermissionModal = (
<CreatePermissionModal
isExpanded={isCreatePermModalOpen}
onExpand={() => handleCreatePermOnClick}
/>
);

const handleCreatePermOnClick = () => {
setCreatePermModalOpen(!isCreatePermModalOpen);
};

return (
<PageSection variant={PageSectionVariants.light}>
<TextContent>
<Text component={TextVariants.p}>
The Default permissions panel defines permissions that should be
granted automatically to a repository when it is created, in addition
to the default of the repository&apos;s creator. Permissions are
assigned based on the user who created the repository.
</Text>
<Text component={TextVariants.p}>
Note: Permissions added here do not automatically get added to
existing repositories.
</Text>
</TextContent>
<Toolbar>
<ToolbarContent>
<DropdownCheckbox
selectedItems={selectedPermissions}
deSelectAll={setSelectedPermissions}
allItemsList={permissionsList}
itemsPerPageList={permissionsList}
onItemSelect={onSelectPermission}
/>
<SearchDropdown
items={[permissionColumnNames.repoCreatedBy]}
searchState={search}
setSearchState={setSearch}
/>
<Flex className="pf-u-mr-md">
<FlexItem>
<SearchInput searchState={search} onChange={setSearch} />
</FlexItem>
</Flex>
<Button
aria-expanded={isCreatePermModalOpen}
onClick={handleCreatePermOnClick}
>
Create default permission
</Button>
{/* <ToolbarButton
id="create-default-permission"
buttonValue="Create default permission"
Modal={createPermissionModal}
isModalOpen={isCreatePermModalOpen}
setModalOpen={setCreatePermModalOpen}
/> */}
</ToolbarContent>
</Toolbar>
<TableComposable aria-label="Selectable table">
<Thead>
<Tr>
<Th />
<Th>{permissionColumnNames.repoCreatedBy}</Th>
<Th>{permissionColumnNames.permAppliedTo}</Th>
<Th>{permissionColumnNames.permission}</Th>
</Tr>
</Thead>
<Tbody>
{permissionsList?.map((permission, rowIndex) => (
<Tr key={permission.name}>
<Td
select={{
rowIndex,
onSelect: (_event, isSelecting) =>
onSelectPermission(permission, rowIndex, isSelecting),
isSelected: isPermissionSelected(permission),
disable: !isPermissionSelectable(permission),
}}
/>
<Td dataLabel={permissionColumnNames.repoCreatedBy}>
{permissionColumnNames.repoCreatedBy}
</Td>
<Td dataLabel={permissionColumnNames.repoCreatedBy}>
{permissionColumnNames.repoCreatedBy}
</Td>
<Td dataLabel={permissionColumnNames.repoCreatedBy}>
{permissionColumnNames.repoCreatedBy}
</Td>
</Tr>
))}
</Tbody>
</TableComposable>
</PageSection>
);
}

This file was deleted.

2 changes: 1 addition & 1 deletion src/routes/OrganizationsList/OrganizationToolBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {SearchInput} from 'src/components/toolbar/SearchInput';
import {ToolbarButton} from 'src/components/toolbar/ToolbarButton';
import {Kebab} from 'src/components/toolbar/Kebab';
import {ToolbarPagination} from 'src/components/toolbar/ToolbarPagination';
import {searchOrgsState} from 'src/atoms/UserState';
import {searchOrgsState} from 'src/atoms/OrganizationListState';
import {useRecoilState} from 'recoil';
import * as React from 'react';
import ColumnNames from './ColumnNames';
Expand Down
Loading

0 comments on commit ce61555

Please sign in to comment.