From: Stephen Smith Date: Tue, 15 Nov 2022 14:43:01 +0000 (-0500) Subject: 19504: Add process / output collection parent process & resource icons to breadcrumbs X-Git-Tag: 2.5.0~13^2~16 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/0ba78ca92c70bf7196ed400a655aa2d625fe8475 19504: Add process / output collection parent process & resource icons to breadcrumbs Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- diff --git a/src/components/breadcrumbs/breadcrumbs.tsx b/src/components/breadcrumbs/breadcrumbs.tsx index 71796661..0348b814 100644 --- a/src/components/breadcrumbs/breadcrumbs.tsx +++ b/src/components/breadcrumbs/breadcrumbs.tsx @@ -9,12 +9,12 @@ import { withStyles } from '@material-ui/core'; import { IllegalNamingWarning } from '../warning/warning'; import { IconType, FreezeIcon } from 'components/icon/icon'; import grey from '@material-ui/core/colors/grey'; -import { ResourceBreadcrumb } from 'store/breadcrumbs/breadcrumbs-actions'; import { ResourcesState } from 'store/resources/resources'; export interface Breadcrumb { label: string; icon?: IconType; + uuid: string; } type CssRules = "item" | "currentItem" | "label" | "icon" | "frozenIcon"; @@ -42,10 +42,10 @@ const styles: StyleRulesCallback = theme => ({ }); export interface BreadcrumbsProps { - items: ResourceBreadcrumb[]; + items: Breadcrumb[]; resources: ResourcesState; - onClick: (breadcrumb: ResourceBreadcrumb) => void; - onContextMenu: (event: React.MouseEvent, breadcrumb: ResourceBreadcrumb) => void; + onClick: (breadcrumb: Breadcrumb) => void; + onContextMenu: (event: React.MouseEvent, breadcrumb: Breadcrumb) => void; } export const Breadcrumbs = withStyles(styles)( diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index a6c118fc..7fd32e54 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -56,6 +56,7 @@ import RestoreFromTrash from '@material-ui/icons/History'; import Search from '@material-ui/icons/Search'; import SettingsApplications from '@material-ui/icons/SettingsApplications'; import SettingsEthernet from '@material-ui/icons/SettingsEthernet'; +import Settings from '@material-ui/icons/Settings'; import Star from '@material-ui/icons/Star'; import StarBorder from '@material-ui/icons/StarBorder'; import Warning from '@material-ui/icons/Warning'; @@ -216,3 +217,4 @@ export const ActiveIcon: IconType = (props) => export const SetupIcon: IconType = (props) => ; export const InactiveIcon: IconType = (props) => ; export const ImageIcon: IconType = (props) => ; +export const ProcessBreadcrumbIcon: IconType = (props) => ; diff --git a/src/store/breadcrumbs/breadcrumbs-actions.ts b/src/store/breadcrumbs/breadcrumbs-actions.ts index 08e1a132..65b2870b 100644 --- a/src/store/breadcrumbs/breadcrumbs-actions.ts +++ b/src/store/breadcrumbs/breadcrumbs-actions.ts @@ -5,7 +5,6 @@ import { Dispatch } from 'redux'; import { RootState } from 'store/store'; import { getUserUuid } from "common/getuser"; -import { Breadcrumb } from 'components/breadcrumbs/breadcrumbs'; import { getResource } from 'store/resources/resources'; import { TreePicker } from '../tree-picker/tree-picker'; import { getSidePanelTreeBranch, getSidePanelTreeNodeAncestorsIds } from '../side-panel-tree/side-panel-tree-actions'; @@ -18,28 +17,56 @@ import { ResourceKind } from 'models/resource'; import { GroupResource } from 'models/group'; import { extractUuidKind } from 'models/resource'; import { UserResource } from 'models/user'; +import { containerRequestFieldsNoMounts } from 'store/all-processes-panel/all-processes-panel-middleware-service'; +import { FilterBuilder } from 'services/api/filter-builder'; +import { ProcessResource } from 'models/process'; +import { OrderBuilder } from 'services/api/order-builder'; +import { Breadcrumb } from 'components/breadcrumbs/breadcrumbs'; +import { ContainerRequestResource } from 'models/container-request'; +import { CollectionIcon, IconType, ProcessBreadcrumbIcon, ProjectIcon } from 'components/icon/icon'; +import { CollectionResource } from 'models/collection'; export const BREADCRUMBS = 'breadcrumbs'; -export interface ResourceBreadcrumb extends Breadcrumb { - uuid: string; -} - -export const setBreadcrumbs = (breadcrumbs: any, currentItem?: any) => { +export const setBreadcrumbs = (breadcrumbs: any, currentItem?: CollectionResource | ContainerRequestResource | GroupResource) => { if (currentItem) { - const addLastItem = { label: currentItem.name, uuid: currentItem.uuid }; - breadcrumbs.push(addLastItem); + breadcrumbs.push(resourceToBreadcrumb(currentItem)); } return propertiesActions.SET_PROPERTY({ key: BREADCRUMBS, value: breadcrumbs }); }; +const resourceToBreadcrumbIcon = (resource: CollectionResource | ContainerRequestResource | GroupResource): IconType | undefined => { + switch (resource.kind) { + case ResourceKind.PROJECT: + return ProjectIcon; + break; + + case ResourceKind.PROCESS: + return ProcessBreadcrumbIcon; + break; + + case ResourceKind.COLLECTION: + return CollectionIcon; + break; + + default: + return undefined; + break; + } +} + +const resourceToBreadcrumb = (resource: CollectionResource | ContainerRequestResource | GroupResource) => ({ + label: resource.name, + uuid: resource.uuid, + icon: resourceToBreadcrumbIcon(resource), +}) -const getSidePanelTreeBreadcrumbs = (uuid: string) => (treePicker: TreePicker): ResourceBreadcrumb[] => { +const getSidePanelTreeBreadcrumbs = (uuid: string) => (treePicker: TreePicker): Breadcrumb[] => { const nodes = getSidePanelTreeBranch(uuid)(treePicker); return nodes.map(node => typeof node.value === 'string' ? { label: node.value, uuid: node.id } - : { label: node.value.name, uuid: node.value.uuid }); + : resourceToBreadcrumb(node.value)); }; export const setSidePanelBreadcrumbs = (uuid: string) => @@ -52,9 +79,17 @@ export const setSidePanelBreadcrumbs = (uuid: string) => if (uuidKind === ResourceKind.COLLECTION) { const collectionItem = item ? item : await services.collectionService.get(currentUuid); + const parentProcessItem = await getCollectionParent(collectionItem)(services); + if (parentProcessItem) { + breadcrumbs.push(resourceToBreadcrumb(parentProcessItem)); + } dispatch(setBreadcrumbs(breadcrumbs, collectionItem)); } else if (uuidKind === ResourceKind.PROCESS) { const processItem = await services.containerRequestService.get(currentUuid); + const parentProcessItem = await getProcessParent(processItem)(services); + if (parentProcessItem) { + breadcrumbs.push(resourceToBreadcrumb(parentProcessItem)); + } dispatch(setBreadcrumbs(breadcrumbs, processItem)); } dispatch(setBreadcrumbs(breadcrumbs)); @@ -70,28 +105,69 @@ export const setCategoryBreadcrumbs = (uuid: string, category: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const ancestors = await services.ancestorsService.ancestors(uuid, ''); dispatch(updateResources(ancestors)); - const initialBreadcrumbs: ResourceBreadcrumb[] = [ + const initialBreadcrumbs: Breadcrumb[] = [ { label: category, uuid: category } ]; const { collectionPanel: { item } } = getState(); const path = getState().router.location!.pathname; const currentUuid = path.split('/')[2]; const uuidKind = extractUuidKind(currentUuid); - const breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) => + let breadcrumbs = ancestors.reduce((breadcrumbs, ancestor) => ancestor.kind === ResourceKind.GROUP - ? [...breadcrumbs, { label: ancestor.name, uuid: ancestor.uuid }] + ? [...breadcrumbs, resourceToBreadcrumb(ancestor)] : breadcrumbs, initialBreadcrumbs); if (uuidKind === ResourceKind.COLLECTION) { const collectionItem = item ? item : await services.collectionService.get(currentUuid); + const parentProcessItem = await getCollectionParent(collectionItem)(services); + if (parentProcessItem) { + breadcrumbs.push(resourceToBreadcrumb(parentProcessItem)); + } dispatch(setBreadcrumbs(breadcrumbs, collectionItem)); } else if (uuidKind === ResourceKind.PROCESS) { const processItem = await services.containerRequestService.get(currentUuid); + const parentProcessItem = await getProcessParent(processItem)(services); + if (parentProcessItem) { + breadcrumbs.push(resourceToBreadcrumb(parentProcessItem)); + } dispatch(setBreadcrumbs(breadcrumbs, processItem)); } dispatch(setBreadcrumbs(breadcrumbs)); }; +const getProcessParent = (childProcess: ContainerRequestResource) => + async (services: ServiceRepository): Promise => { + if (childProcess.requestingContainerUuid) { + const parentProcesses = await services.containerRequestService.list({ + order: new OrderBuilder().addAsc('createdAt').getOrder(), + filters: new FilterBuilder().addEqual('container_uuid', childProcess.requestingContainerUuid).getFilters(), + select: containerRequestFieldsNoMounts, + }); + if (parentProcesses.items.length > 0) { + return parentProcesses.items[0]; + } else { + return undefined; + } + } else { + return undefined; + } + } + +const getCollectionParent = (collection: CollectionResource) => + async (services: ServiceRepository): Promise => { + const parentProcesses = await services.containerRequestService.list({ + order: new OrderBuilder().addAsc('createdAt').getOrder(), + filters: new FilterBuilder().addEqual('output_uuid', collection.uuid).getFilters(), + select: containerRequestFieldsNoMounts, + }); + if (parentProcesses.items.length > 0) { + return parentProcesses.items[0]; + } else { + return undefined; + } + } + + export const setProjectBreadcrumbs = (uuid: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const ancestors = getSidePanelTreeNodeAncestorsIds(uuid)(getState().treePicker); @@ -121,7 +197,7 @@ export const setGroupDetailsBreadcrumbs = (groupUuid: string) => const group = getResource(groupUuid)(getState().resources); - const breadcrumbs: ResourceBreadcrumb[] = [ + const breadcrumbs: Breadcrumb[] = [ { label: SidePanelTreeCategory.GROUPS, uuid: SidePanelTreeCategory.GROUPS }, { label: group ? group.name : (await services.groupsService.get(groupUuid)).name, uuid: groupUuid }, ]; @@ -140,13 +216,13 @@ export const setUserProfileBreadcrumbs = (userUuid: string) => try { const user = getResource(userUuid)(getState().resources) || await services.userService.get(userUuid, false); - const breadcrumbs: ResourceBreadcrumb[] = [ + const breadcrumbs: Breadcrumb[] = [ { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL }, { label: user ? user.username : userUuid, uuid: userUuid }, ]; dispatch(setBreadcrumbs(breadcrumbs)); } catch (e) { - const breadcrumbs: ResourceBreadcrumb[] = [ + const breadcrumbs: Breadcrumb[] = [ { label: USERS_PANEL_LABEL, uuid: USERS_PANEL_LABEL }, { label: userUuid, uuid: userUuid }, ]; diff --git a/src/views-components/breadcrumbs/breadcrumbs.ts b/src/views-components/breadcrumbs/breadcrumbs.ts index c4134aed..0334097d 100644 --- a/src/views-components/breadcrumbs/breadcrumbs.ts +++ b/src/views-components/breadcrumbs/breadcrumbs.ts @@ -8,25 +8,24 @@ import { RootState } from 'store/store'; import { Dispatch } from 'redux'; import { navigateTo } from 'store/navigation/navigation-action'; import { getProperty } from '../../store/properties/properties'; -import { ResourceBreadcrumb, BREADCRUMBS } from '../../store/breadcrumbs/breadcrumbs-actions'; +import { BREADCRUMBS } from '../../store/breadcrumbs/breadcrumbs-actions'; import { openSidePanelContextMenu } from 'store/context-menu/context-menu-actions'; -import { ProjectResource } from "models/project"; type BreadcrumbsDataProps = Pick; type BreadcrumbsActionProps = Pick; const mapStateToProps = () => ({ properties, resources }: RootState): BreadcrumbsDataProps => ({ - items: (getProperty(BREADCRUMBS)(properties) || []), + items: (getProperty(BREADCRUMBS)(properties) || []), resources, }); const mapDispatchToProps = (dispatch: Dispatch): BreadcrumbsActionProps => ({ - onClick: ({ uuid }: Breadcrumb & ProjectResource) => { + onClick: ({ uuid }: Breadcrumb) => { dispatch(navigateTo(uuid)); }, - onContextMenu: (event, breadcrumb: Breadcrumb & ProjectResource) => { + onContextMenu: (event, breadcrumb: Breadcrumb) => { dispatch(openSidePanelContextMenu(event, breadcrumb.uuid)); } }); -export const Breadcrumbs = connect(mapStateToProps(), mapDispatchToProps)(BreadcrumbsComponent); \ No newline at end of file +export const Breadcrumbs = connect(mapStateToProps(), mapDispatchToProps)(BreadcrumbsComponent);