diff --git a/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetails.tsx b/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetails.tsx new file mode 100644 index 00000000..b9019d76 --- /dev/null +++ b/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetails.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { + Button, + DrawerActions, + DrawerHead, + DrawerPanelBody, + DrawerPanelContent, + Title, +} from '@patternfly/react-core'; +import { WorkspaceAggregatedDetailsActions } from '~/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetailsActions'; + +type WorkspaceAggregatedDetailsProps = { + workspaceNames: string[]; + onCloseClick: React.MouseEventHandler; + onDeleteClick: React.MouseEventHandler; +}; + +export const WorkspaceAggregatedDetails: React.FunctionComponent< + WorkspaceAggregatedDetailsProps +> = ({ workspaceNames, onCloseClick, onDeleteClick }) => ( + + + Multiple selected workspaces + + + + + + + + {'Selected workspaces: '} + + {workspaceNames.join(', ')} + + +); diff --git a/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetailsActions.tsx b/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetailsActions.tsx new file mode 100644 index 00000000..40f711f1 --- /dev/null +++ b/workspaces/frontend/src/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetailsActions.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import { + Dropdown, + DropdownList, + MenuToggle, + DropdownItem, + Flex, + FlexItem, +} from '@patternfly/react-core'; + +interface WorkspaceAggregatedDetailsActionsProps { + onDeleteClick: React.MouseEventHandler; +} + +export const WorkspaceAggregatedDetailsActions: React.FC< + WorkspaceAggregatedDetailsActionsProps +> = ({ onDeleteClick }) => { + const [isOpen, setOpen] = React.useState(false); + + return ( + + + setOpen(false)} + onOpenChange={(open) => setOpen(open)} + popperProps={{ position: 'end' }} + toggle={(toggleRef) => ( + setOpen(!isOpen)} + isExpanded={isOpen} + aria-label="Workspace aggregated details action toggle" + data-testid="workspace-aggregated-details-action-toggle" + > + Actions + + )} + > + + + Delete selected + + + + + + ); +}; diff --git a/workspaces/frontend/src/app/pages/Workspaces/ExpandedWorkspaceRow.tsx b/workspaces/frontend/src/app/pages/Workspaces/ExpandedWorkspaceRow.tsx index 7131f3d6..07728276 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/ExpandedWorkspaceRow.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/ExpandedWorkspaceRow.tsx @@ -1,37 +1,21 @@ import * as React from 'react'; import { ExpandableRowContent, Td, Tr } from '@patternfly/react-table'; -import { Workspace, WorkspacesColumnNames } from '~/shared/types'; +import { Workspace } from '~/shared/types'; import { DataVolumesList } from '~/app/pages/Workspaces/DataVolumesList'; interface ExpandedWorkspaceRowProps { workspace: Workspace; - columnNames: WorkspacesColumnNames; } -export const ExpandedWorkspaceRow: React.FC = ({ - workspace, - columnNames, -}) => { - const renderExpandedData = () => - Object.keys(columnNames).map((colName) => { - switch (colName) { - case 'name': - return ( - - - - - - ); - default: - return ; - } - }); - - return ( - - - {renderExpandedData()} - - ); -}; +export const ExpandedWorkspaceRow: React.FC = ({ workspace }) => ( + + + + + + + + + + +); diff --git a/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx b/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx index f8c17a06..d4488eae 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx @@ -46,11 +46,11 @@ import { WorkspaceConnectAction } from '~/app/pages/Workspaces/WorkspaceConnectA import { WorkspaceStartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStartActionModal'; import { WorkspaceRestartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRestartActionModal'; import { WorkspaceStopActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStopActionModal'; +import { WorkspaceAggregatedDetails } from '~/app/pages/Workspaces/DetailsAggregated/WorkspaceAggregatedDetails'; import Filter, { FilteredColumn } from 'shared/components/Filter'; import { formatRam } from 'shared/utilities/WorkspaceUtils'; export enum ActionType { - ViewDetails, Edit, Delete, Start, @@ -225,17 +225,6 @@ export const Workspaces: React.FunctionComponent = () => { const [isActionAlertModalOpen, setIsActionAlertModalOpen] = React.useState(false); const [activeActionType, setActiveActionType] = React.useState(null); - const selectWorkspace = React.useCallback( - (newSelectedWorkspace) => { - if (selectedWorkspace?.name === newSelectedWorkspace?.name) { - setSelectedWorkspace(null); - } else { - setSelectedWorkspace(newSelectedWorkspace); - } - }, - [selectedWorkspace], - ); - const setWorkspaceExpanded = (workspace: Workspace, isExpanding = true) => setExpandedWorkspacesNames((prevExpanded) => { const newExpandedWorkspacesNames = prevExpanded.filter((wsName) => wsName !== workspace.name); @@ -341,11 +330,6 @@ export const Workspaces: React.FunctionComponent = () => { // Actions - const viewDetailsClick = React.useCallback((workspace: Workspace) => { - setSelectedWorkspace(workspace); - setActiveActionType(ActionType.ViewDetails); - }, []); - const editAction = React.useCallback((workspace: Workspace) => { setSelectedWorkspace(workspace); setActiveActionType(ActionType.Edit); @@ -383,11 +367,6 @@ export const Workspaces: React.FunctionComponent = () => { const workspaceDefaultActions = (workspace: Workspace): IActions => { const workspaceState = workspace.status.state; const workspaceActions = [ - { - id: 'view-details', - title: 'View Details', - onClick: () => viewDetailsClick(workspace), - }, { id: 'edit', title: 'Edit', @@ -526,25 +505,49 @@ export const Workspaces: React.FunctionComponent = () => { setPage(newPage); }; - const workspaceDetailsContent = ( - <> - {selectedWorkspace && ( - selectWorkspace(null)} - onEditClick={() => editAction(selectedWorkspace)} - onDeleteClick={() => handleDeleteClick(selectedWorkspace)} - /> - )} - - ); + const [selectedWorkspaceNames, setSelectedWorkspaceNames] = React.useState([]); + const setWorkspaceSelected = (workspace: Workspace, isSelecting = true) => + setSelectedWorkspaceNames((prevSelected) => { + const otherSelectedWorkspaceNames = prevSelected.filter((w) => w !== workspace.name); + return isSelecting + ? [...otherSelectedWorkspaceNames, workspace.name] + : otherSelectedWorkspaceNames; + }); + const selectAllWorkspaces = (isSelecting = true) => + setSelectedWorkspaceNames(isSelecting ? sortedWorkspaces.map((r) => r.name) : []); + const areAllWorkspacesSelected = selectedWorkspaceNames.length === sortedWorkspaces.length; + const isWorkspaceSelected = (workspace: Workspace) => + selectedWorkspaceNames.includes(workspace.name); + + const workspaceDetailsContent = () => { + const selectedWorkspaceForDetails = + selectedWorkspaceNames.length === 1 + ? sortedWorkspaces.find((w) => w.name === selectedWorkspaceNames[0]) + : undefined; + return ( + <> + {selectedWorkspaceForDetails && ( + selectAllWorkspaces(false)} + onEditClick={() => editAction(selectedWorkspaceForDetails)} + onDeleteClick={() => handleDeleteClick(selectedWorkspaceForDetails)} + /> + )} + {selectedWorkspaceNames.length > 1 && ( + selectAllWorkspaces(false)} + onDeleteClick={() => console.log('Delete selected workspaces')} + /> + )} + + ); + }; return ( - - + = 1}> + @@ -562,6 +565,13 @@ export const Workspaces: React.FunctionComponent = () => { + selectAllWorkspaces(isSelecting), + isSelected: areAllWorkspacesSelected, + }} + aria-label="Row select" + /> {Object.values(columnNames).map((columnName, index) => ( { setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)), }} /> + + setWorkspaceSelected(workspace, isSelecting), + isSelected: isWorkspaceSelected(workspace), + }} + /> {workspaceRedirectStatus[workspace.kind] ? getRedirectStatusIcon( @@ -644,9 +662,7 @@ export const Workspaces: React.FunctionComponent = () => { /> - {isWorkspaceExpanded(workspace) && ( - - )} + {isWorkspaceExpanded(workspace) && } ))}