Skip to content

Commit

Permalink
feat(bulk-import): allow user to select repositories from side panel
Browse files Browse the repository at this point in the history
Signed-off-by: Yi Cai <yicai@redhat.com>
  • Loading branch information
ciiay committed Apr 18, 2024
1 parent d7030a4 commit 001e002
Show file tree
Hide file tree
Showing 11 changed files with 1,101 additions and 176 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { useState } from 'react';

import { Link } from '@backstage/core-components';

import {
Button,
Card,
Container,
Drawer,
IconButton,
makeStyles,
Typography,
} from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';

import { AddRepositoriesData, AddRepositoriesFormValues } from '../../types';
import { urlHelper } from '../../utils/repository-utils';
import { AddRepositoriesTableToolbar } from './AddRepositoriesTableToolbar';
import { RepositoriesTable } from './RepositoriesTable';

type AddRepositoriesDrawerProps = {
open: boolean;
onClose: () => void;
onSelect: (ids: number[], drawerOrgId: number) => void;
title: string;
data: AddRepositoriesData;
selectedRepositoriesFormData: AddRepositoriesFormValues;
checkedRepos: number[];
};

const useStyles = makeStyles(theme => ({
createButton: {
marginRight: theme.spacing(1),
},
sidePanelfooter: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'right',
marginTop: theme.spacing(2),
position: 'fixed',
bottom: '20px',
},
drawerPaper: {
['@media (max-width: 960px)']: {
'& > div[class*="MuiDrawer-paper-"]': {
width: '-webkit-fill-available',
},
},
},
}));

export const AddRepositoriesDrawer = ({
open,
onClose,
onSelect,
title,
data,
selectedRepositoriesFormData,
checkedRepos,
}: AddRepositoriesDrawerProps) => {
const classes = useStyles();
const [searchString, setSearchString] = useState<string>('');

const [selectedReposID, setSelectedReposID] =
useState<number[]>(checkedRepos);

const updateSelectedReposInDrawer = (ids: number[]) => {
setSelectedReposID(ids);
};

const handleSelectRepoFromDrawer = (selected: number[]) => {
onSelect(selected, data?.id);
onClose();
};

return (
<Drawer
anchor="right"
open={open}
variant="temporary"
className={classes.drawerPaper}
>
<Container
style={{
padding: '20px',
height: '100%',
display: 'flex',
flexDirection: 'column',
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>
<Typography variant="h5">{data?.name}</Typography>
<Link to={data?.url}>
{urlHelper(data?.url)}
<OpenInNewIcon
style={{ verticalAlign: 'sub', paddingTop: '7px' }}
/>
</Link>
</div>
<div>
<IconButton onClick={onClose} className="align-right">
<CloseIcon />
</IconButton>
</div>
</div>
<Card style={{ marginTop: '20px', marginBottom: '60px' }}>
<AddRepositoriesTableToolbar
title={title}
setSearchString={setSearchString}
selectedReposFromDrawer={selectedReposID}
selectedRepositoriesFormData={selectedRepositoriesFormData}
activeOrganization={data}
/>
<RepositoriesTable
searchString={searchString}
selectedOrgRepos={selectedReposID}
updateSelectedReposInDrawer={updateSelectedReposInDrawer}
drawerOrganization={data}
/>
</Card>
<div className={classes.sidePanelfooter}>
<span>
<Button
variant="contained"
color="primary"
onClick={() => handleSelectRepoFromDrawer(selectedReposID)}
className={classes.createButton}
aria-labelledby="select-from-drawer"
>
Select
</Button>
</span>
<span>
<Button
aria-labelledby="cancel-drawer-select"
variant="outlined"
onClick={onClose}
>
Cancel
</Button>
</span>
</div>
</Container>
</Drawer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import { FormikErrors } from 'formik';

import { AddRepositoriesFormValues } from '../../types';
import { getRepositoriesSelected } from '../../utils/repository-utils';
import { AddRepositoriesData, AddRepositoriesFormValues } from '../../types';
import { RepositoriesSearchBar } from './AddRepositoriesSearchBar';

export const AddRepositoriesTableToolbar = ({
Expand All @@ -16,24 +15,29 @@ export const AddRepositoriesTableToolbar = ({
selectedRepositoriesFormData,
setFieldValue,
onPageChange,
activeOrganization,
selectedReposFromDrawer,
}: {
title: string;
setSearchString: (str: string) => void;
selectedRepositoriesFormData: AddRepositoriesFormValues;
setFieldValue: (
setFieldValue?: (
field: string,
value: any,
shouldValidate?: boolean,
) => Promise<FormikErrors<AddRepositoriesFormValues>> | Promise<void>;
onPageChange: (page: number) => void;
onPageChange?: (page: number) => void;
activeOrganization?: AddRepositoriesData;
selectedReposFromDrawer?: number[];
}) => {
const [selection, setSelection] = React.useState('repository');
const [search, setSearch] = React.useState<string>('');
const [selectedReposNumber, setSelectedReposNumber] = React.useState(0);
const handleToggle = (
_event: React.MouseEvent<HTMLElement>,
type: string,
) => {
if (type) {
if (type && setFieldValue && onPageChange) {
setSelection(type);
setFieldValue('repositoryType', type);
onPageChange(0);
Expand All @@ -45,6 +49,23 @@ export const AddRepositoriesTableToolbar = ({
setSearch(filter);
};

React.useEffect(() => {
if (activeOrganization && selectedReposFromDrawer) {
const thisSelectedReposCount = activeOrganization.repositories?.filter(
repo => selectedReposFromDrawer.includes(repo.id) && repo.id > -1,
).length;
setSelectedReposNumber(thisSelectedReposCount || 0);
} else {
setSelectedReposNumber(
selectedRepositoriesFormData.repositories?.length || 0,
);
}
}, [
selectedReposFromDrawer,
selectedRepositoriesFormData,
activeOrganization,
]);

return (
<Toolbar
sx={{
Expand All @@ -54,19 +75,21 @@ export const AddRepositoriesTableToolbar = ({
}}
>
<Typography sx={{ flex: '1 1 100%' }} variant="h5" id={title}>
{`${title} (${getRepositoriesSelected(selectedRepositoriesFormData)})`}
{`${title} (${selectedReposNumber})`}
</Typography>
<ToggleButtonGroup
size="medium"
color="primary"
value={selection}
exclusive
onChange={handleToggle}
aria-label="repository-type"
>
<ToggleButton value="repository">Repositories</ToggleButton>
<ToggleButton value="organization">Organization</ToggleButton>
</ToggleButtonGroup>
{!activeOrganization && (
<ToggleButtonGroup
size="medium"
color="primary"
value={selection}
exclusive
onChange={handleToggle}
aria-label="repository-type"
>
<ToggleButton value="repository">Repositories</ToggleButton>
<ToggleButton value="organization">Organization</ToggleButton>
</ToggleButtonGroup>
)}
<RepositoriesSearchBar value={search} onChange={handleSearch} />
</Toolbar>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,45 @@ import { AddRepositoriesData } from '../../types';
import {
getRepositoryStatusForOrg,
getSelectedRepositories,
urlHelper,
} from '../../utils/repository-utils';

const tableRowStyle = {
lineHeight: '1.5rem',
fontSize: '0.875rem',
padding: '15px 16px 15px 24px',
};

export const OrganizationTableRow = ({
onOrgRowSelected,
data,
alreadyAdded,
}: {
onOrgRowSelected: (org: AddRepositoriesData) => void;
data: AddRepositoriesData;
alreadyAdded: number;
}) => {
return (
<TableRow hover>
<TableCell component="th" scope="row" padding="none">
<TableCell component="th" scope="row" padding="none" sx={tableRowStyle}>
{data.name}
</TableCell>
<TableCell align="left">
<TableCell align="left" sx={tableRowStyle}>
<Link to={data.url}>
<>
{data.url}
{urlHelper(data.url)}
<OpenInNewIcon
style={{ verticalAlign: 'bottom', paddingTop: '7px' }}
style={{ verticalAlign: 'sub', paddingTop: '7px' }}
/>
</>
</Link>
</TableCell>
<TableCell align="left">
<>{getSelectedRepositories(data.selectedRepositories)}</>
<TableCell align="left" sx={tableRowStyle}>
{getSelectedRepositories(onOrgRowSelected, data, alreadyAdded)}
</TableCell>
<TableCell align="left" sx={tableRowStyle}>
{getRepositoryStatusForOrg(data, alreadyAdded)}
</TableCell>
<TableCell align="left">{getRepositoryStatusForOrg(data)}</TableCell>
</TableRow>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TableColumn } from '@backstage/core-components';

export const ReposSelectDrawerColumnHeader: TableColumn[] = [
{
id: 'name',
title: 'Name',
field: 'name',
},
{
id: 'url',
title: 'URL',
field: 'url',
},
{
id: 'catalogInfoYaml',
title: '',
field: 'catalogInfoYaml',
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import TableSortLabel from '@mui/material/TableSortLabel';
import { Order } from '../../types';
import { OrganizationColumnHeader } from './OrganizationColumnHeader';
import { RepositoriesColumnHeader } from './RepositoriesColumnHeader';
import { ReposSelectDrawerColumnHeader } from './ReposSelectDrawerColumnHeader';

export const RepositoriesHeader = ({
onSelectAllClick,
Expand All @@ -18,6 +19,7 @@ export const RepositoriesHeader = ({
rowCount,
onRequestSort,
showOrganizations,
isRepoSelectDrawer = false,
}: {
numSelected: number;
onRequestSort: (event: React.MouseEvent<unknown>, property: any) => void;
Expand All @@ -26,17 +28,35 @@ export const RepositoriesHeader = ({
orderBy: string;
rowCount: number;
showOrganizations: boolean;
isRepoSelectDrawer?: boolean;
}) => {
const createSortHandler =
(property: any) => (event: React.MouseEvent<unknown>) => {
onRequestSort(event, property);
};

const getColumnHeader = () => {
if (showOrganizations) {
return OrganizationColumnHeader;
}
if (isRepoSelectDrawer) {
return ReposSelectDrawerColumnHeader;
}
return RepositoriesColumnHeader;
};

const tableRowStyle = {
lineHeight: '1.5rem',
fontSize: '0.875rem',
padding: '15px 16px 15px 24px',
fontWeight: '700',
};

return (
<TableHead>
<TableRow>
{!showOrganizations && (
<TableCell padding="checkbox">
<TableCell padding="none">
<Checkbox
color="primary"
indeterminate={numSelected > 0 && numSelected < rowCount}
Expand All @@ -48,17 +68,12 @@ export const RepositoriesHeader = ({
/>
</TableCell>
)}
{(showOrganizations
? OrganizationColumnHeader
: RepositoriesColumnHeader
).map(headCell => (
{getColumnHeader().map(headCell => (
<TableCell
key={headCell.id as string}
align="left"
padding="normal"
style={{
fontWeight: '800',
}}
sx={tableRowStyle}
sortDirection={orderBy === headCell.field ? order : 'asc'}
>
<TableSortLabel
Expand Down
Loading

0 comments on commit 001e002

Please sign in to comment.