[ARVADOS-WORKBENCH2] updated: 2.1.0-257-g91b752bb

Git user git at public.arvados.org
Tue Mar 16 21:34:46 UTC 2021


Summary of changes:
 src/plugins/sample-tracker/extraction.tsx      | 227 +++++++++++++++++++++++++
 src/plugins/sample-tracker/index.tsx           |  18 +-
 src/plugins/sample-tracker/patient.tsx         |  37 +++-
 src/plugins/sample-tracker/sample.tsx          | 215 +++++++++++++++++++++++
 src/plugins/sample-tracker/sampleList.tsx      | 226 ++----------------------
 src/store/context-menu/context-menu-actions.ts |   4 +-
 6 files changed, 503 insertions(+), 224 deletions(-)
 create mode 100644 src/plugins/sample-tracker/extraction.tsx
 create mode 100644 src/plugins/sample-tracker/sample.tsx

       via  91b752bb64edab61fede6668e4bada1ffc112861 (commit)
       via  3ed41c51eb23edaba255e753c242636f0d524e5c (commit)
      from  5c3eaae04d6fb9cbcb50c05bc77b90964955e49e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 91b752bb64edab61fede6668e4bada1ffc112861
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Tue Mar 16 17:34:33 2021 -0400

    WIP extraction dialog
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/plugins/sample-tracker/extraction.tsx b/src/plugins/sample-tracker/extraction.tsx
index d1f58a9b..af99f095 100644
--- a/src/plugins/sample-tracker/extraction.tsx
+++ b/src/plugins/sample-tracker/extraction.tsx
@@ -4,19 +4,29 @@
 
 import * as React from 'react';
 import { dialogActions } from "~/store/dialog/dialog-actions";
+import { WithDialogProps } from '~/store/dialog/with-dialog';
+import { FormDialog } from '~/components/form-dialog/form-dialog';
 import { ServiceRepository } from "~/services/services";
-import { Dispatch } from "redux";
-import { WrappedFieldProps, initialize } from 'redux-form';
+import { compose, Dispatch } from "redux";
+import { reduxForm, WrappedFieldProps, initialize, InjectedFormProps, Field, startSubmit, reset } from 'redux-form';
 import { RootState } from '~/store/store';
-import { FormControl } from '@material-ui/core';
+import { TextField } from "~/components/text-field/text-field";
+import { getResource } from "~/store/resources/resources";
+import { FormControl, InputLabel } from '@material-ui/core';
 import {
-    patientRoutePath
+    patientBaseRoutePath, patientRoutePath
 } from './patientList';
+import {
+    sampleBaseRoutePath
+} from './sampleList';
 import { matchPath } from "react-router";
 import { MenuItem, Select } from '@material-ui/core';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { DispatchProp, connect } from 'react-redux';
 import { withStyles, WithStyles } from '@material-ui/core/styles';
+import { LinkResource } from "~/models/link";
+import { GroupResource } from "~/models/group";
+import { withDialog } from "~/store/dialog/with-dialog";
 
 const EXTRACTION_CREATE_FORM_NAME = "extractionCreateFormName";
 
@@ -42,7 +52,7 @@ export interface ExtractionCreateFormDialogData {
     batchUuid: string;
 }
 
-// type DialogExtractionProps = WithDialogProps<{}> & InjectedFormProps<ExtractionCreateFormDialogData>;
+type DialogExtractionProps = WithDialogProps<{}> & InjectedFormProps<ExtractionCreateFormDialogData>;
 
 type CssRules = 'selectWidth';
 
@@ -52,7 +62,7 @@ const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
     }
 }));
 
-export const ExtractionTypeSelect = styles(
+export const SampleStateSelect = styles(
     ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
         <FormControl className={classes.selectWidth}>
             <Select
@@ -72,7 +82,7 @@ export const ExtractionTypeSelect = styles(
             </Select>
         </FormControl>);
 
-export const SampleStateSelect = styles(
+export const ExtractionTypeSelect = styles(
     ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
         <FormControl className={classes.selectWidth}>
             <Select
@@ -86,7 +96,6 @@ export const SampleStateSelect = styles(
             </Select>
         </FormControl>);
 
-/*
 const ExtractionAddFields = () => <span>
 
     <InputLabel>Extraction type</InputLabel>
@@ -117,11 +126,10 @@ const ExtractionAddFields = () => <span>
     />
 
     <InputLabel>State</InputLabel>
-    <Field
+    <div><Field
         name='state'
-        component={TextField}
-        type="date"
-    />
+        component={SampleStateSelect}
+    /></div>
 
 </span>;
 
@@ -153,12 +161,13 @@ const makeExtractionId = (data: ExtractionCreateFormDialogData, state: RootState
     return id;
 };
 
- const createExtraction = (data: ExtractionCreateFormDialogData) =>
+const createExtraction = (data: ExtractionCreateFormDialogData) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
 
         dispatch(startSubmit(EXTRACTION_CREATE_FORM_NAME));
-        const extractionId = makeExtractionId(data, getState());
-        await services.linkService.create({
+        // const extractionId =
+        makeExtractionId(data, getState());
+        /*await services.linkService.create({
             ownerUuid: data.patientUuid,
             name: extractionId,
             linkClass: extractionTrackerExtractionType,
@@ -170,11 +179,12 @@ const makeExtractionId = (data: ExtractionCreateFormDialogData, state: RootState
                 "sample_tracker:flow_started_at": data.flowStartedAt,
                 "sample_tracker:flow_completed_at": data.flowCompletedAt,
             },
-        });
+        });*/
         dispatch(dialogActions.CLOSE_DIALOG({ id: EXTRACTION_CREATE_FORM_NAME }));
         dispatch(reset(EXTRACTION_CREATE_FORM_NAME));
     };
 
+
 export const CreateExtractionDialog = compose(
     withDialog(EXTRACTION_CREATE_FORM_NAME),
     reduxForm<ExtractionCreateFormDialogData>({
@@ -184,11 +194,11 @@ export const CreateExtractionDialog = compose(
         }
     })
 )(DialogExtractionCreate);
-*/
 
-const openExtractionCreateDialog = (patientUuid: string) =>
+
+export const openExtractionCreateDialog = (sampleUuid: string) =>
     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(initialize(EXTRACTION_CREATE_FORM_NAME, { patientUuid }));
+        dispatch(initialize(EXTRACTION_CREATE_FORM_NAME, { sampleUuid }));
         dispatch(dialogActions.OPEN_DIALOG({ id: EXTRACTION_CREATE_FORM_NAME, data: {} }));
     };
 
diff --git a/src/plugins/sample-tracker/index.tsx b/src/plugins/sample-tracker/index.tsx
index 01f46111..faadae13 100644
--- a/src/plugins/sample-tracker/index.tsx
+++ b/src/plugins/sample-tracker/index.tsx
@@ -15,6 +15,11 @@ import { activateSidePanelTreeItem } from '~/store/side-panel-tree/side-panel-tr
 import { setBreadcrumbs, setSidePanelBreadcrumbs } from '~/store/breadcrumbs/breadcrumbs-actions';
 import { Location } from 'history';
 import { handleFirstTimeLoad } from '~/store/workbench/workbench-actions';
+import { dataExplorerMiddleware } from "~/store/data-explorer/data-explorer-middleware";
+import { getResource } from "~/store/resources/resources";
+import { GroupResource } from "~/models/group";
+import { addMenuActionSet } from '~/views-components/context-menu/context-menu';
+
 import {
     AddStudyMenuComponent, StudyListMainPanel, CreateStudyDialog,
     studyListPanelColumns, studyListPanelActions, openStudyListPanel,
@@ -39,10 +44,7 @@ import {
 import {
     openPatientPanel, PatientMainPanel, PATIENT_SAMPLE_MENU, patientSampleActionSet
 } from './patient';
-import { dataExplorerMiddleware } from "~/store/data-explorer/data-explorer-middleware";
-import { getResource } from "~/store/resources/resources";
-import { GroupResource } from "~/models/group";
-import { addMenuActionSet } from '~/views-components/context-menu/context-menu';
+import { CreateExtractionDialog } from './extraction';
 
 const categoryName = "Studies";
 
@@ -136,6 +138,7 @@ export const register = (pluginConfig: PluginConfig) => {
     pluginConfig.dialogs.push(<CreateStudyDialog />);
     pluginConfig.dialogs.push(<CreatePatientDialog />);
     pluginConfig.dialogs.push(<CreateSampleDialog />);
+    pluginConfig.dialogs.push(<CreateExtractionDialog />);
 
     pluginConfig.middlewares.push((elms, services) => {
         elms.push(dataExplorerMiddleware(
diff --git a/src/plugins/sample-tracker/patient.tsx b/src/plugins/sample-tracker/patient.tsx
index fc765ac1..2384cbac 100644
--- a/src/plugins/sample-tracker/patient.tsx
+++ b/src/plugins/sample-tracker/patient.tsx
@@ -13,6 +13,7 @@ import { DataTableDefaultView } from '~/components/data-table-default-view/data-
 import { openContextMenu } from '~/store/context-menu/context-menu-actions';
 import { ResourceKind } from '~/models/resource';
 import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
+import { openExtractionCreateDialog } from "./extraction";
 
 import { PATIENT_PANEL_CURRENT_UUID } from './patientList';
 import { SAMPLE_LIST_PANEL_ID, sampleListPanelActions } from './sampleList';
@@ -40,7 +41,7 @@ const handleContextMenu = (dispatch: Dispatch) =>
         // const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         dispatch<any>(openContextMenu(event, {
             name: "",
-            uuid: "",
+            uuid: resourceUuid,
             ownerUuid: "",
             isTrashed: false,
             kind: ResourceKind.NONE,
@@ -53,6 +54,7 @@ export const patientSampleActionSet: ContextMenuActionSet = [[
     {
         name: "Add extraction",
         execute: (dispatch, resource) => {
+            dispatch<any>(openExtractionCreateDialog(resource.uuid));
         }
     },
 ]];

commit 3ed41c51eb23edaba255e753c242636f0d524e5c
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Tue Mar 16 17:09:40 2021 -0400

    Adding extractions to samples
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/src/plugins/sample-tracker/extraction.tsx b/src/plugins/sample-tracker/extraction.tsx
new file mode 100644
index 00000000..d1f58a9b
--- /dev/null
+++ b/src/plugins/sample-tracker/extraction.tsx
@@ -0,0 +1,217 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { ServiceRepository } from "~/services/services";
+import { Dispatch } from "redux";
+import { WrappedFieldProps, initialize } from 'redux-form';
+import { RootState } from '~/store/store';
+import { FormControl } from '@material-ui/core';
+import {
+    patientRoutePath
+} from './patientList';
+import { matchPath } from "react-router";
+import { MenuItem, Select } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { DispatchProp, connect } from 'react-redux';
+import { withStyles, WithStyles } from '@material-ui/core/styles';
+
+const EXTRACTION_CREATE_FORM_NAME = "extractionCreateFormName";
+
+enum ExtractionType {
+    DNA = "DNA",
+    RNA = "RNA",
+}
+
+enum AnalysisState {
+    NEW = "NEW",
+    AT_SEQUENCING = "AT_SEQUENCING",
+    SEQUENCED = "SEQUENCED",
+    ANALYSIS_COMPLETE = "ANALYSIS_COMPLETE"
+}
+
+export interface ExtractionCreateFormDialogData {
+    sampleUuid: string;
+    extractionType: ExtractionType;
+    additionalId: number;
+    sentForSequencing: string;
+    sequencingCompleted: string;
+    state: AnalysisState;
+    batchUuid: string;
+}
+
+// type DialogExtractionProps = WithDialogProps<{}> & InjectedFormProps<ExtractionCreateFormDialogData>;
+
+type CssRules = 'selectWidth';
+
+const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
+    selectWidth: {
+        width: theme.spacing.unit * 20,
+    }
+}));
+
+export const ExtractionTypeSelect = styles(
+    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
+        <FormControl className={classes.selectWidth}>
+            <Select
+                {...input}>
+                <MenuItem value={AnalysisState.NEW}>
+                    NEW
+		</MenuItem>
+                <MenuItem value={AnalysisState.AT_SEQUENCING}>
+                    AT_SEQUENCING
+		</MenuItem>
+                <MenuItem value={AnalysisState.SEQUENCED}>
+                    SEQUENCED
+		</MenuItem>
+                <MenuItem value={AnalysisState.ANALYSIS_COMPLETE}>
+                    ANALYSIS_COMPLETE
+		</MenuItem>
+            </Select>
+        </FormControl>);
+
+export const SampleStateSelect = styles(
+    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
+        <FormControl className={classes.selectWidth}>
+            <Select
+                {...input}>
+                <MenuItem value={ExtractionType.DNA}>
+                    DNA
+		</MenuItem>
+                <MenuItem value={ExtractionType.RNA}>
+                    RNA
+		</MenuItem>
+            </Select>
+        </FormControl>);
+
+/*
+const ExtractionAddFields = () => <span>
+
+    <InputLabel>Extraction type</InputLabel>
+    <div>
+        <Field
+            name='extractionType'
+            component={ExtractionTypeSelect} />
+    </div>
+
+    <InputLabel>Additional id</InputLabel>
+    <Field
+        name='timePoint'
+        component={TextField}
+        type="number" />
+
+    <InputLabel>Sent for sequencing</InputLabel>
+    <Field
+        name='sentForSequencing'
+        component={TextField}
+        type="date"
+    />
+
+    <InputLabel>Sequencing completed</InputLabel>
+    <Field
+        name='sequencingCompleted'
+        component={TextField}
+        type="date"
+    />
+
+    <InputLabel>State</InputLabel>
+    <Field
+        name='state'
+        component={TextField}
+        type="date"
+    />
+
+</span>;
+
+
+const DialogExtractionCreate = (props: DialogExtractionProps) =>
+    <FormDialog
+        dialogTitle='Add extraction'
+        formFields={ExtractionAddFields}
+        submitLabel='Add a extraction'
+        {...props}
+    />;
+
+const makeExtractionId = (data: ExtractionCreateFormDialogData, state: RootState): string => {
+    const rscSamp = getResource<LinkResource>(sampleBaseRoutePath + "/" + data.sampleUuid)(state.resources);
+    const rscPat = getResource<GroupResource>(patientBaseRoutePath + "/" + rscSamp!.ownerUuid)(state.resources);
+    let id = rscPat!.name + "_" + data.extractionType + "_";
+
+    if (rscSamp!.properties["sample_tracker:time_point"] < 10) {
+        id = id + "_0" + rscSamp!.properties["sample_tracker:time_point"];
+    } else {
+        id = id + "_" + rscSamp!.properties["sample_tracker:time_point"];
+    }
+    if (data.additionalId < 10) {
+        id = id + "_0" + data.additionalId;
+    } else {
+        id = id + "_" + data.additionalId;
+    }
+
+    return id;
+};
+
+ const createExtraction = (data: ExtractionCreateFormDialogData) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+
+        dispatch(startSubmit(EXTRACTION_CREATE_FORM_NAME));
+        const extractionId = makeExtractionId(data, getState());
+        await services.linkService.create({
+            ownerUuid: data.patientUuid,
+            name: extractionId,
+            linkClass: extractionTrackerExtractionType,
+            properties: {
+                "sample_tracker:collection_type": data.collectionType,
+                "sample_tracker:extraction_type": data.extractionType,
+                "sample_tracker:collected_at": data.collectedAt,
+                "sample_tracker:time_point": data.timePoint,
+                "sample_tracker:flow_started_at": data.flowStartedAt,
+                "sample_tracker:flow_completed_at": data.flowCompletedAt,
+            },
+        });
+        dispatch(dialogActions.CLOSE_DIALOG({ id: EXTRACTION_CREATE_FORM_NAME }));
+        dispatch(reset(EXTRACTION_CREATE_FORM_NAME));
+    };
+
+export const CreateExtractionDialog = compose(
+    withDialog(EXTRACTION_CREATE_FORM_NAME),
+    reduxForm<ExtractionCreateFormDialogData>({
+        form: EXTRACTION_CREATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(createExtraction(data));
+        }
+    })
+)(DialogExtractionCreate);
+*/
+
+const openExtractionCreateDialog = (patientUuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(initialize(EXTRACTION_CREATE_FORM_NAME, { patientUuid }));
+        dispatch(dialogActions.OPEN_DIALOG({ id: EXTRACTION_CREATE_FORM_NAME, data: {} }));
+    };
+
+
+export interface MenuItemProps {
+    className?: string;
+    patientUuid?: string;
+}
+
+export interface PatientPathId {
+    uuid: string;
+}
+
+export const extractionsMapStateToProps = (state: RootState) => {
+    const props: MenuItemProps = {};
+    const patientid = matchPath<PatientPathId>(state.router.location!.pathname, { path: patientRoutePath, exact: true });
+    if (patientid) {
+        props.patientUuid = patientid.params.uuid;
+    }
+    return props;
+};
+
+export const AddExtractionMenuComponent = connect<{}, {}, MenuItemProps>(extractionsMapStateToProps)(
+    ({ patientUuid, dispatch, className }: MenuItemProps & DispatchProp<any>) =>
+        <MenuItem className={className} onClick={() => dispatch(openExtractionCreateDialog(patientUuid!))} disabled={!patientUuid}>Add Extraction</MenuItem >
+);
diff --git a/src/plugins/sample-tracker/index.tsx b/src/plugins/sample-tracker/index.tsx
index 46ab489c..01f46111 100644
--- a/src/plugins/sample-tracker/index.tsx
+++ b/src/plugins/sample-tracker/index.tsx
@@ -27,18 +27,22 @@ import {
     patientListPanelColumns, patientListPanelActions, patientRoutePath, patientBaseRoutePath
 } from './patientList';
 import {
-    AddSampleMenuComponent, CreateSampleDialog, SampleListPanelMiddlewareService,
+    SampleListPanelMiddlewareService,
     SAMPLE_LIST_PANEL_ID, sampleListPanelColumns, sampleListPanelActions
 } from './sampleList';
+import {
+    AddSampleMenuComponent, CreateSampleDialog
+} from './sample';
 import {
     openStudyPanel, StudyMainPanel
 } from './study';
 import {
-    openPatientPanel, PatientMainPanel
+    openPatientPanel, PatientMainPanel, PATIENT_SAMPLE_MENU, patientSampleActionSet
 } from './patient';
 import { dataExplorerMiddleware } from "~/store/data-explorer/data-explorer-middleware";
 import { getResource } from "~/store/resources/resources";
 import { GroupResource } from "~/models/group";
+import { addMenuActionSet } from '~/views-components/context-menu/context-menu';
 
 const categoryName = "Studies";
 
@@ -147,4 +151,5 @@ export const register = (pluginConfig: PluginConfig) => {
         return elms;
     });
 
+    addMenuActionSet(PATIENT_SAMPLE_MENU, patientSampleActionSet);
 };
diff --git a/src/plugins/sample-tracker/patient.tsx b/src/plugins/sample-tracker/patient.tsx
index f01b587d..fc765ac1 100644
--- a/src/plugins/sample-tracker/patient.tsx
+++ b/src/plugins/sample-tracker/patient.tsx
@@ -3,17 +3,22 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { connect } from 'react-redux';
+import { DispatchProp, connect } from 'react-redux';
 import { Dispatch } from "redux";
 import { propertiesActions } from "~/store/properties/properties-actions";
 import { getProperty } from '~/store/properties/properties';
 import { RootState } from '~/store/store';
 import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { openContextMenu } from '~/store/context-menu/context-menu-actions';
+import { ResourceKind } from '~/models/resource';
+import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
 
 import { PATIENT_PANEL_CURRENT_UUID } from './patientList';
 import { SAMPLE_LIST_PANEL_ID, sampleListPanelActions } from './sampleList';
 
+export const PATIENT_SAMPLE_MENU = "Sample Tracker - Patient Sample menu";
+
 export const openPatientPanel = (projectUuid: string) =>
     (dispatch: Dispatch) => {
         dispatch(propertiesActions.SET_PROPERTY({ key: PATIENT_PANEL_CURRENT_UUID, value: projectUuid }));
@@ -28,8 +33,32 @@ export const patientMapStateToProps = (state: RootState) => ({
     patientUuid: getProperty(PATIENT_PANEL_CURRENT_UUID)(state.properties),
 });
 
+const handleContextMenu = (dispatch: Dispatch) =>
+    (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
+        // const { resources } = this.props;
+        // const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
+        // const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
+        dispatch<any>(openContextMenu(event, {
+            name: "",
+            uuid: "",
+            ownerUuid: "",
+            isTrashed: false,
+            kind: ResourceKind.NONE,
+            menuKind: PATIENT_SAMPLE_MENU
+        }));
+    };
+
+
+export const patientSampleActionSet: ContextMenuActionSet = [[
+    {
+        name: "Add extraction",
+        execute: (dispatch, resource) => {
+        }
+    },
+]];
+
 export const PatientMainPanel = connect(patientMapStateToProps)(
-    ({ patientUuid }: PatientProps) =>
+    ({ dispatch, patientUuid }: PatientProps & DispatchProp<any>) =>
         <div>
             <DataExplorer
                 id={SAMPLE_LIST_PANEL_ID}
@@ -37,7 +66,7 @@ export const PatientMainPanel = connect(patientMapStateToProps)(
                 hideColumnSelector={true}
                 onRowClick={(uuid: string) => { }}
                 onRowDoubleClick={(uuid: string) => { }}
-                onContextMenu={(event: React.MouseEvent<HTMLElement>, resourceUuid: string) => { }}
+                onContextMenu={handleContextMenu(dispatch)}
                 contextMenuColumn={true}
                 dataTableDefaultView={
                     <DataTableDefaultView />
diff --git a/src/plugins/sample-tracker/sample.tsx b/src/plugins/sample-tracker/sample.tsx
new file mode 100644
index 00000000..51b91ac1
--- /dev/null
+++ b/src/plugins/sample-tracker/sample.tsx
@@ -0,0 +1,215 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { WithDialogProps } from '~/store/dialog/with-dialog';
+import { FormDialog } from '~/components/form-dialog/form-dialog';
+import { dialogActions } from "~/store/dialog/dialog-actions";
+import { ServiceRepository } from "~/services/services";
+import { compose, Dispatch } from "redux";
+import { reduxForm, WrappedFieldProps, InjectedFormProps, Field, reset, startSubmit, initialize } from 'redux-form';
+import { withDialog } from "~/store/dialog/with-dialog";
+import { RootState } from '~/store/store';
+import { TextField } from "~/components/text-field/text-field";
+import { FormControl, InputLabel } from '@material-ui/core';
+import {
+    patientRoutePath, patientBaseRoutePath
+} from './patientList';
+import { matchPath } from "react-router";
+import { MenuItem, Select } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { getResource } from "~/store/resources/resources";
+import { sampleTrackerSampleType } from "./sampleList";
+import { DispatchProp, connect } from 'react-redux';
+import { withStyles, WithStyles } from '@material-ui/core/styles';
+import { GroupResource } from "~/models/group";
+
+const SAMPLE_CREATE_FORM_NAME = "sampleCreateFormName";
+
+export interface SampleCreateFormDialogData {
+    patientUuid: string;
+    collectionType: CollectionType;
+    sampleType: SampleType;
+    collectedAt: string;
+    timePoint: number;
+    flowStartedAt: string;
+    flowCompletedAt: string;
+}
+
+type DialogSampleProps = WithDialogProps<{}> & InjectedFormProps<SampleCreateFormDialogData>;
+
+type CssRules = 'selectWidth';
+
+const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
+    selectWidth: {
+        width: theme.spacing.unit * 20,
+    }
+}));
+
+enum CollectionType {
+    PERIPHERAL_BLOOD = "peripheral_blood",
+    BONE_MARROW = "bone_marrow"
+}
+
+enum SampleType {
+    TUMOR = "tumor",
+    NORMAL = "normal"
+}
+
+export const CollectionTypeSelect = styles(
+    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
+        <FormControl className={classes.selectWidth}>
+            <Select
+                {...input}>
+                <MenuItem value={CollectionType.PERIPHERAL_BLOOD}>
+                    Peripheral Blood
+		</MenuItem>
+                <MenuItem value={CollectionType.BONE_MARROW}>
+                    Bone Marrow
+		</MenuItem>
+            </Select>
+        </FormControl>);
+
+export const SampleTypeSelect = styles(
+    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
+        <FormControl className={classes.selectWidth}>
+            <Select
+                {...input}>
+                <MenuItem value={SampleType.TUMOR}>
+                    Tumor
+		</MenuItem>
+                <MenuItem value={SampleType.NORMAL}>
+                    Normal
+		</MenuItem>
+            </Select>
+        </FormControl>);
+
+const SampleAddFields = () => <span>
+
+    <InputLabel>Patient time point</InputLabel>
+    <Field
+        name='timePoint'
+        component={TextField}
+        type="number" />
+
+    <InputLabel>Collection date</InputLabel>
+    <Field
+        name='collectedAt'
+        component={TextField}
+        type="date" />
+
+    <InputLabel>Collection type</InputLabel>
+    <div>
+        <Field
+            name='collectionType'
+            component={CollectionTypeSelect} />
+    </div>
+
+    <InputLabel>Sample type</InputLabel>
+    <div>
+        <Field
+            name='sampleType'
+            component={SampleTypeSelect} />
+    </div>
+    <InputLabel>Flow started at</InputLabel>
+    <Field
+        name='flowStartedAt'
+        component={TextField}
+        type="date"
+    />
+
+    <InputLabel>Flow ended at</InputLabel>
+    <Field
+        name='flowEndedAt'
+        component={TextField}
+        type="date"
+    />
+</span>;
+
+const DialogSampleCreate = (props: DialogSampleProps) =>
+    <FormDialog
+        dialogTitle='Add sample'
+        formFields={SampleAddFields}
+        submitLabel='Add a sample'
+        {...props}
+    />;
+
+const makeSampleId = (data: SampleCreateFormDialogData, state: RootState): string => {
+    const rsc = getResource<GroupResource>(patientBaseRoutePath + "/" + data.patientUuid)(state.resources);
+    let id = rsc!.name;
+    if (data.collectionType === CollectionType.PERIPHERAL_BLOOD) {
+        id = id + "_PB";
+    }
+    if (data.collectionType === CollectionType.BONE_MARROW) {
+        id = id + "_BM";
+    }
+    if (data.timePoint < 10) {
+        id = id + "_0" + data.timePoint;
+    } else {
+        id = id + "_" + data.timePoint;
+    }
+    return id;
+};
+
+const createSample = (data: SampleCreateFormDialogData) =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+
+        dispatch(startSubmit(SAMPLE_CREATE_FORM_NAME));
+        const sampleId = makeSampleId(data, getState());
+        await services.linkService.create({
+            ownerUuid: data.patientUuid,
+            name: sampleId,
+            linkClass: sampleTrackerSampleType,
+            properties: {
+                "sample_tracker:collection_type": data.collectionType,
+                "sample_tracker:sample_type": data.sampleType,
+                "sample_tracker:collected_at": data.collectedAt,
+                "sample_tracker:time_point": data.timePoint,
+                "sample_tracker:flow_started_at": data.flowStartedAt,
+                "sample_tracker:flow_completed_at": data.flowCompletedAt,
+            },
+        });
+        dispatch(dialogActions.CLOSE_DIALOG({ id: SAMPLE_CREATE_FORM_NAME }));
+        dispatch(reset(SAMPLE_CREATE_FORM_NAME));
+    };
+
+export const CreateSampleDialog = compose(
+    withDialog(SAMPLE_CREATE_FORM_NAME),
+    reduxForm<SampleCreateFormDialogData>({
+        form: SAMPLE_CREATE_FORM_NAME,
+        onSubmit: (data, dispatch) => {
+            dispatch(createSample(data));
+        }
+    })
+)(DialogSampleCreate);
+
+const openSampleCreateDialog = (patientUuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(initialize(SAMPLE_CREATE_FORM_NAME, { patientUuid }));
+        dispatch(dialogActions.OPEN_DIALOG({ id: SAMPLE_CREATE_FORM_NAME, data: {} }));
+    };
+
+
+export interface MenuItemProps {
+    className?: string;
+    patientUuid?: string;
+}
+
+export interface PatientPathId {
+    uuid: string;
+}
+
+export const samplesMapStateToProps = (state: RootState) => {
+    const props: MenuItemProps = {};
+    const patientid = matchPath<PatientPathId>(state.router.location!.pathname, { path: patientRoutePath, exact: true });
+    if (patientid) {
+        props.patientUuid = patientid.params.uuid;
+    }
+    return props;
+};
+
+export const AddSampleMenuComponent = connect<{}, {}, MenuItemProps>(samplesMapStateToProps)(
+    ({ patientUuid, dispatch, className }: MenuItemProps & DispatchProp<any>) =>
+        <MenuItem className={className} onClick={() => dispatch(openSampleCreateDialog(patientUuid!))} disabled={!patientUuid}>Add Sample</MenuItem >
+);
diff --git a/src/plugins/sample-tracker/sampleList.tsx b/src/plugins/sample-tracker/sampleList.tsx
index 3edfc833..4c94e6b8 100644
--- a/src/plugins/sample-tracker/sampleList.tsx
+++ b/src/plugins/sample-tracker/sampleList.tsx
@@ -3,13 +3,8 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { WithDialogProps } from '~/store/dialog/with-dialog';
-import { FormDialog } from '~/components/form-dialog/form-dialog';
-import { dialogActions } from "~/store/dialog/dialog-actions";
 import { ServiceRepository } from "~/services/services";
-import { compose, MiddlewareAPI, Dispatch } from "redux";
-import { reduxForm, initialize, WrappedFieldProps, InjectedFormProps, Field, reset, startSubmit } from 'redux-form';
-import { withDialog } from "~/store/dialog/with-dialog";
+import { MiddlewareAPI, Dispatch } from "redux";
 import { RootState } from '~/store/store';
 import { DispatchProp, connect } from 'react-redux';
 import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
@@ -19,226 +14,30 @@ import { createTree } from '~/models/tree';
 import { ResourceName } from '~/views-components/data-explorer/renderers';
 import { SortDirection } from '~/components/data-table/data-column';
 import { bindDataExplorerActions } from "~/store/data-explorer/data-explorer-action";
-import { TextField } from "~/components/text-field/text-field";
-import { FormControl, InputLabel } from '@material-ui/core';
-import { withStyles, WithStyles } from '@material-ui/core/styles';
 
 import {
     DataExplorerMiddlewareService,
     listResultsToDataExplorerItemsMeta,
     dataExplorerToListParams
 } from '~/store/data-explorer/data-explorer-middleware-service';
-import { GroupResource } from "~/models/group";
 import { LinkResource } from "~/models/link";
 import { ListResults } from '~/services/common-service/common-service';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions.ts';
 import { DataExplorer as DataExplorerState, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer';
 import { FilterBuilder, joinFilters } from "~/services/api/filter-builder";
 import { updateResources } from "~/store/resources/resources-actions";
-import {
-    patientRoutePath, patientBaseRoutePath
-} from './patientList';
-import { matchPath } from "react-router";
 import { getProperty } from '~/store/properties/properties';
-import { MenuItem, Select } from '@material-ui/core';
-import { ArvadosTheme } from '~/common/custom-theme';
 import { getResource } from "~/store/resources/resources";
 
-const SAMPLE_CREATE_FORM_NAME = "sampleCreateFormName";
 export const SAMPLE_LIST_PANEL_ID = "sampleListPanel";
 export const sampleListPanelActions = bindDataExplorerActions(SAMPLE_LIST_PANEL_ID);
 export const sampleTrackerSampleType = "sample_tracker:sample";
+export const sampleTrackerExtractionType = "sample_tracker:extraction";
 export const PATIENT_PANEL_CURRENT_UUID = "PatientPanelCurrentUUID";
 export const SAMPLE_PANEL_CURRENT_UUID = "SamplePanelCurrentUUID";
 export const sampleBaseRoutePath = "/SampleTracker/Sample";
 export const sampleRoutePath = sampleBaseRoutePath + "/:uuid";
 
-export interface SampleCreateFormDialogData {
-    patientUuid: string;
-    collectionType: string;
-    sampleType: string;
-    collectedAt: string;
-    timePoint: number;
-    flowStartedAt: string;
-    flowCompletedAt: string;
-}
-
-type DialogSampleProps = WithDialogProps<{}> & InjectedFormProps<SampleCreateFormDialogData>;
-
-type CssRules = 'selectWidth';
-
-const styles = withStyles<CssRules>((theme: ArvadosTheme) => ({
-    selectWidth: {
-        width: theme.spacing.unit * 20,
-    }
-}));
-
-enum CollectionType {
-    PERIPHERAL_BLOOD = 'peripheral_blood',
-    BONE_MARROW = 'bone_marrow'
-}
-
-enum SampleType {
-    TUMOR = 'tumor',
-    NORMAL = 'normal'
-}
-
-export const CollectionTypeSelect = styles(
-    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
-        <FormControl className={classes.selectWidth}>
-            <Select
-                {...input}>
-                <MenuItem value={CollectionType.PERIPHERAL_BLOOD}>
-                    Peripheral Blood
-		</MenuItem>
-                <MenuItem value={CollectionType.BONE_MARROW}>
-                    Bone Marrow
-		</MenuItem>
-            </Select>
-        </FormControl>);
-
-export const SampleTypeSelect = styles(
-    ({ classes, input }: WrappedFieldProps & WithStyles<CssRules>) =>
-        <FormControl className={classes.selectWidth}>
-            <Select
-                {...input}>
-                <MenuItem value={SampleType.TUMOR}>
-                    Tumor
-		</MenuItem>
-                <MenuItem value={SampleType.NORMAL}>
-                    Normal
-		</MenuItem>
-            </Select>
-        </FormControl>);
-
-const SampleAddFields = () => <span>
-
-    <InputLabel>Patient time point</InputLabel>
-    <Field
-        name='timePoint'
-        component={TextField}
-        type="number" />
-
-    <InputLabel>Collection date</InputLabel>
-    <Field
-        name='collectedAt'
-        component={TextField}
-        type="date" />
-
-    <InputLabel>Collection type</InputLabel>
-    <div>
-        <Field
-            name='collectionType'
-            component={CollectionTypeSelect} />
-    </div>
-
-    <InputLabel>Sample type</InputLabel>
-    <div>
-        <Field
-            name='sampleType'
-            component={SampleTypeSelect} />
-    </div>
-    <InputLabel>Flow started at</InputLabel>
-    <Field
-        name='flowStartedAt'
-        component={TextField}
-        type="date"
-    />
-
-    <InputLabel>Flow ended at</InputLabel>
-    <Field
-        name='flowEndedAt'
-        component={TextField}
-        type="date"
-    />
-</span>;
-
-const DialogSampleCreate = (props: DialogSampleProps) =>
-    <FormDialog
-        dialogTitle='Add sample'
-        formFields={SampleAddFields}
-        submitLabel='Add a sample'
-        {...props}
-    />;
-
-const makeSampleId = (data: SampleCreateFormDialogData, state: RootState): string => {
-    const rsc = getResource<GroupResource>(patientBaseRoutePath + "/" + data.patientUuid)(state.resources);
-    let id = rsc!.name;
-    if (data.collectionType === CollectionType.PERIPHERAL_BLOOD) {
-        id = id + "_PB";
-    }
-    if (data.collectionType === CollectionType.BONE_MARROW) {
-        id = id + "_BM";
-    }
-    if (data.timePoint < 10) {
-        id = id + "_0" + data.timePoint;
-    } else {
-        id = id + "_" + data.timePoint;
-    }
-    return id;
-};
-
-const createSample = (data: SampleCreateFormDialogData) =>
-    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-
-        dispatch(startSubmit(SAMPLE_CREATE_FORM_NAME));
-        const sampleId = makeSampleId(data, getState());
-        await services.linkService.create({
-            ownerUuid: data.patientUuid,
-            name: sampleId,
-            linkClass: sampleTrackerSampleType,
-            properties: {
-                "sample_tracker:collection_type": data.collectionType,
-                "sample_tracker:sample_type": data.sampleType,
-                "sample_tracker:collected_at": data.collectedAt,
-                "sample_tracker:time_point": data.timePoint,
-                "sample_tracker:flow_started_at": data.flowStartedAt,
-                "sample_tracker:flow_completed_at": data.flowCompletedAt,
-            },
-        });
-        dispatch(dialogActions.CLOSE_DIALOG({ id: SAMPLE_CREATE_FORM_NAME }));
-        dispatch(reset(SAMPLE_CREATE_FORM_NAME));
-    };
-
-export const CreateSampleDialog = compose(
-    withDialog(SAMPLE_CREATE_FORM_NAME),
-    reduxForm<SampleCreateFormDialogData>({
-        form: SAMPLE_CREATE_FORM_NAME,
-        onSubmit: (data, dispatch) => {
-            dispatch(createSample(data));
-        }
-    })
-)(DialogSampleCreate);
-
-export interface MenuItemProps {
-    className?: string;
-    patientUuid?: string;
-}
-
-export interface PatientPathId {
-    uuid: string;
-}
-
-export const samplesMapStateToProps = (state: RootState) => {
-    const props: MenuItemProps = {};
-    const patientid = matchPath<PatientPathId>(state.router.location!.pathname, { path: patientRoutePath, exact: true });
-    if (patientid) {
-        props.patientUuid = patientid.params.uuid;
-    }
-    return props;
-};
-
-const openSampleCreateDialog = (patientUuid: string) =>
-    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch(initialize(SAMPLE_CREATE_FORM_NAME, { patientUuid }));
-        dispatch(dialogActions.OPEN_DIALOG({ id: SAMPLE_CREATE_FORM_NAME, data: {} }));
-    };
-
-export const AddSampleMenuComponent = connect<{}, {}, MenuItemProps>(samplesMapStateToProps)(
-    ({ patientUuid, dispatch, className }: MenuItemProps & DispatchProp<any>) =>
-        <MenuItem className={className} onClick={() => dispatch(openSampleCreateDialog(patientUuid!))} disabled={!patientUuid}>Add Sample</MenuItem >
-);
-
 
 enum SamplePanelColumnNames {
     NAME = "Name",
@@ -388,17 +187,16 @@ export interface TrackerProps {
     className?: string;
 }
 
-export const SampleListPanel = connect(samplesMapStateToProps)(
-    ({ }: TrackerProps) =>
-        <DataExplorer
-            id={SAMPLE_LIST_PANEL_ID}
-            onRowClick={(uuid: string) => { }}
-            onRowDoubleClick={(uuid: string) => { }}
-            onContextMenu={(event: React.MouseEvent<HTMLElement>, resourceUuid: string) => { }}
-            contextMenuColumn={true}
-            dataTableDefaultView={
-                <DataTableDefaultView />
-            } />);
+export const SampleListPanel = ({ }: TrackerProps) =>
+    <DataExplorer
+        id={SAMPLE_LIST_PANEL_ID}
+        onRowClick={(uuid: string) => { }}
+        onRowDoubleClick={(uuid: string) => { }}
+        onContextMenu={(event: React.MouseEvent<HTMLElement>, resourceUuid: string) => { }}
+        contextMenuColumn={true}
+        dataTableDefaultView={
+            <DataTableDefaultView />
+        } />;
 
 const setItems = (listResults: ListResults<LinkResource>) =>
     sampleListPanelActions.SET_ITEMS({
diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts
index 22553885..2982d052 100644
--- a/src/store/context-menu/context-menu-actions.ts
+++ b/src/store/context-menu/context-menu-actions.ts
@@ -34,7 +34,7 @@ export type ContextMenuResource = {
     ownerUuid: string;
     description?: string;
     kind: ResourceKind,
-    menuKind: ContextMenuKind;
+    menuKind: ContextMenuKind | string;
     isTrashed?: boolean;
     isEditable?: boolean;
     outputUuid?: string;
@@ -167,7 +167,7 @@ export const openProjectContextMenu = (event: React.MouseEvent<HTMLElement>, res
                 kind: res.kind,
                 menuKind,
                 ownerUuid: res.ownerUuid,
-                isTrashed: ('isTrashed' in res) ? res.isTrashed: false,
+                isTrashed: ('isTrashed' in res) ? res.isTrashed : false,
             }));
         }
     };

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list