[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