Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat/html-display
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlougheed committed Oct 29, 2024
2 parents 5c15e5e + 464c7ac commit 42acd14
Show file tree
Hide file tree
Showing 50 changed files with 273 additions and 226 deletions.
4 changes: 2 additions & 2 deletions src/components/ReferenceGenomesContent.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCallback, useMemo } from "react";
import { useDispatch } from "react-redux";

import { Button, Descriptions, Dropdown, Layout, Modal, Popover, Space, Table } from "antd";
import { BarsOutlined, DeleteOutlined, ImportOutlined } from "@ant-design/icons";
Expand All @@ -13,6 +12,7 @@ import { useResourcePermissionsWrapper } from "@/hooks";
import { deleteReferenceGenomeIfPossible } from "@/modules/reference/actions";
import { useReferenceGenomes } from "@/modules/reference/hooks";
import { useService, useServices, useWorkflows } from "@/modules/services/hooks";
import { useAppDispatch } from "@/store";
import { LAYOUT_CONTENT_STYLE } from "@/styles/layoutContent";

import SitePageHeader from "./SitePageHeader";
Expand All @@ -21,7 +21,7 @@ import { useStartIngestionFlow } from "./manager/workflowCommon";
const DEFAULT_REF_INGEST_WORKFLOW_ID = "fasta_ref";

const ReferenceGenomesContent = () => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();

const { hasAttempted: hasAttemptedServiceFetch } = useServices();
const { permissions } = useResourcePermissionsWrapper(RESOURCE_EVERYTHING);
Expand Down
4 changes: 2 additions & 2 deletions src/components/ServiceContent.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect } from "react";
import { useSelector } from "react-redux";

import { Col, Layout, Row, Spin, Statistic, Typography } from "antd";

Expand All @@ -10,14 +9,15 @@ import { SITE_NAME } from "@/constants";
import { EM_DASH } from "@/constants";
import { BENTO_URL } from "@/config";
import { useProjects } from "@/modules/metadata/hooks";
import { useAppSelector } from "@/store";

const ServiceContent = () => {
useEffect(() => {
document.title = `${SITE_NAME}: Admin / Services`;
}, []);

const { items: projects, isFetching: isFetchingProjects } = useProjects();
const isFetching = useSelector((state) => state.user.isFetchingDependentData) || isFetchingProjects;
const isFetching = useAppSelector((state) => state.user.isFetchingDependentData) || isFetchingProjects;

return (
<>
Expand Down
14 changes: 7 additions & 7 deletions src/components/SiteHeader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { memo, useCallback, useMemo, useState } from "react";
import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";
import {
viewNotifications,
Expand Down Expand Up @@ -30,16 +29,17 @@ import {

import { BENTO_CBIOPORTAL_ENABLED, BENTO_GRAFANA_URL, BENTO_MONITORING_ENABLED, CUSTOM_HEADER } from "@/config";
import { useEverythingPermissions } from "@/hooks";
import { showNotificationDrawer } from "@/modules/notifications/actions";
import { useNotifications } from "@/modules/notifications/hooks";
import { matchingMenuKeys, transformMenuItem } from "@/utils/menu";

import OverviewSettingsControl from "./overview/OverviewSettingsControl";
import {
useCanQueryAtLeastOneProjectOrDataset,
useHasValidGrafanaRole,
useManagerPermissions,
} from "@/modules/authz/hooks";
import { showNotificationDrawer } from "@/modules/notifications/actions";
import { useNotifications } from "@/modules/notifications/hooks";
import { useAppDispatch } from "@/store";
import { matchingMenuKeys, transformMenuItem } from "@/utils/menu";

import OverviewSettingsControl from "./overview/OverviewSettingsControl";

const LinkedLogo = memo(() => (
<Link to="/">
Expand All @@ -62,7 +62,7 @@ const openGrafanaInNewTab = () => {
};

const SiteHeader = () => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();

const performAuth = usePerformAuth();

Expand Down
5 changes: 3 additions & 2 deletions src/components/charts/PieChart.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useCallback, useMemo } from "react";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import PropTypes from "prop-types";

import { PieChart as BentoPie } from "bento-charts";

import { setAutoQueryPageTransition } from "@/modules/explorer/actions";
import { useAppDispatch } from "@/store";

import ChartContainer from "./ChartContainer";

const PieChart = ({
Expand All @@ -18,7 +19,7 @@ const PieChart = ({
clickable = false,
sortData = true,
}) => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();
const navigate = useNavigate();

const onAutoQueryTransition = useCallback(
Expand Down
6 changes: 3 additions & 3 deletions src/components/datasets/Dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
deleteDatasetLinkedFieldSetIfPossible,
} from "@/modules/metadata/actions";

import { fetchDatasetDataTypesSummariesIfPossible, fetchDatasetSummariesIfPossible } from "@/modules/datasets/actions";
import { fetchDatasetDataTypesIfPossible, fetchDatasetSummariesIfNeeded } from "@/modules/datasets/actions";

import { INITIAL_DATA_USE_VALUE } from "@/duo";
import { simpleDeepCopy, nop } from "@/utils/misc";
Expand Down Expand Up @@ -335,8 +335,8 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
deleteProjectDataset: (dataset) => dispatch(deleteProjectDatasetIfPossible(ownProps.project, dataset)),
deleteLinkedFieldSet: (dataset, linkedFieldSet, linkedFieldSetIndex) =>
dispatch(deleteDatasetLinkedFieldSetIfPossible(dataset, linkedFieldSet, linkedFieldSetIndex)),
fetchDatasetSummary: (datasetId) => dispatch(fetchDatasetSummariesIfPossible(datasetId)),
fetchDatasetDataTypesSummary: (datasetId) => dispatch(fetchDatasetDataTypesSummariesIfPossible(datasetId)),
fetchDatasetSummary: (datasetId) => dispatch(fetchDatasetSummariesIfNeeded(datasetId)),
fetchDatasetDataTypesSummary: (datasetId) => dispatch(fetchDatasetDataTypesIfPossible(datasetId)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Dataset);
29 changes: 15 additions & 14 deletions src/components/datasets/DatasetDataTypes.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { memo, useCallback, useMemo, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types";

import { Button, Col, Dropdown, Row, Table, Typography } from "antd";
import { DeleteOutlined, DownOutlined, ImportOutlined } from "@ant-design/icons";

import { datasetPropTypesShape, projectPropTypesShape } from "@/propTypes";
import { fetchDatasetDataTypesSummariesIfPossible } from "@/modules/datasets/actions";
import { fetchDatasetDataTypesIfPossible, invalidateDatasetSummaries } from "@/modules/datasets/actions";
import { useDatasetDataTypesByID, useDatasetSummariesByID } from "@/modules/datasets/hooks";
import { clearDatasetDataType } from "@/modules/metadata/actions";
import { useWorkflows } from "@/modules/services/hooks";
import { useAppDispatch } from "@/store";
import { useStartIngestionFlow } from "../manager/workflowCommon";

import genericConfirm from "../ConfirmationModal";
Expand All @@ -17,20 +18,19 @@ import DataTypeSummaryModal from "./datatype/DataTypeSummaryModal";
const NA_TEXT = <span style={{ color: "#999", fontStyle: "italic" }}>N/A</span>;

const DatasetDataTypes = memo(({ isPrivate, project, dataset }) => {
const dispatch = useDispatch();
const datasetDataTypes = useSelector((state) => state.datasetDataTypes.itemsByID[dataset.identifier]);
const datasetDataTypeValues = useMemo(() => Object.values(datasetDataTypes?.itemsByID ?? {}), [datasetDataTypes]);
const datasetSummaries = useSelector((state) => state.datasetSummaries.itemsByID[dataset.identifier]);
const dispatch = useAppDispatch();
const datasetDataTypes = useDatasetDataTypesByID(dataset.identifier);
const datasetDataTypeValues = useMemo(() => Object.values(datasetDataTypes.dataTypesByID), [datasetDataTypes]);
const datasetSummaries = useDatasetSummariesByID(dataset.identifier);

const isFetchingDataset = datasetDataTypes?.isFetching ?? false;
const isFetchingDataset = datasetDataTypes.isFetching ?? false;

const { workflowsByType } = useWorkflows();
const ingestionWorkflows = workflowsByType.ingestion.items;

const [datatypeSummaryVisible, setDatatypeSummaryVisible] = useState(false);
const [selectedDataType, setSelectedDataType] = useState(null);

const selectedSummary = datasetSummaries?.data?.[selectedDataType?.id] ?? {};
const selectedDataTypeSummary = datasetSummaries?.data?.[selectedDataType?.id] ?? {};

const handleClearDataType = useCallback(
(dataType) => {
Expand All @@ -41,7 +41,8 @@ const DatasetDataTypes = memo(({ isPrivate, project, dataset }) => {
"will be deleted permanently, and will no longer be available for exploration.",
onOk: async () => {
await dispatch(clearDatasetDataType(dataset.identifier, dataType.id));
await dispatch(fetchDatasetDataTypesSummariesIfPossible(dataset.identifier));
await dispatch(fetchDatasetDataTypesIfPossible(dataset.identifier));
dispatch(invalidateDatasetSummaries(dataset.identifier));
},
});
},
Expand All @@ -50,7 +51,6 @@ const DatasetDataTypes = memo(({ isPrivate, project, dataset }) => {

const showDataTypeSummary = useCallback((dataType) => {
setSelectedDataType(dataType);
setDatatypeSummaryVisible(true);
}, []);

const startIngestionFlow = useStartIngestionFlow();
Expand Down Expand Up @@ -124,15 +124,16 @@ const DatasetDataTypes = memo(({ isPrivate, project, dataset }) => {
[isPrivate, project, dataset, handleClearDataType, ingestionWorkflows, startIngestionFlow, showDataTypeSummary],
);

const onDataTypeSummaryModalCancel = useCallback(() => setDatatypeSummaryVisible(false), []);
const onDataTypeSummaryModalCancel = useCallback(() => setSelectedDataType(null), []);

return (
<>
<DataTypeSummaryModal
dataType={selectedDataType}
summary={selectedSummary}
open={datatypeSummaryVisible}
summary={selectedDataTypeSummary}
open={selectedDataType !== null}
onCancel={onDataTypeSummaryModalCancel}
isFetching={datasetSummaries?.isFetching}
/>

<Typography.Title level={4} style={{ marginTop: 0 }}>
Expand Down
12 changes: 6 additions & 6 deletions src/components/datasets/datatype/DataTypeSummaryModal.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { useSelector } from "react-redux";
import PropTypes from "prop-types";

import { Modal, Skeleton } from "antd";
import { Alert, Modal, Skeleton } from "antd";

import { summaryPropTypesShape } from "@/propTypes";

import GenericSummary from "./GenericSummary";
import PhenopacketSummary from "./PhenopacketSummary";
import VariantSummary from "./VariantSummary";

const DataTypeSummaryModal = ({ dataType, summary, onCancel, open }) => {
const isFetchingSummaries = useSelector((state) => state.datasetDataTypes.isFetchingAll);

const DataTypeSummaryModal = ({ dataType, summary, onCancel, open, isFetching }) => {
if (!dataType) {
return <></>;
}
Expand All @@ -38,7 +35,9 @@ const DataTypeSummaryModal = ({ dataType, summary, onCancel, open }) => {
width={960}
footer={null}
>
{!summaryData || isFetchingSummaries ? <Skeleton /> : <Summary summary={summaryData} />}
<Alert.ErrorBoundary>
{!summaryData || isFetching ? <Skeleton /> : <Summary summary={summaryData} />}
</Alert.ErrorBoundary>
</Modal>
);
};
Expand All @@ -48,6 +47,7 @@ DataTypeSummaryModal.propTypes = {
summary: summaryPropTypesShape,
onCancel: PropTypes.func,
open: PropTypes.bool,
isFetching: PropTypes.bool,
};

export default DataTypeSummaryModal;
13 changes: 4 additions & 9 deletions src/components/datasets/datatype/VariantSummary.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import { Col, Row, Statistic } from "antd";
import { FileOutlined } from "@ant-design/icons";

import { EM_DASH } from "@/constants";
import { summaryPropTypesShape } from "@/propTypes";

const VariantSummary = ({ summary }) => (
<Row gutter={16}>
<Col span={8}>
<Col span={12}>
<Statistic title="Variants" value={summary.count} />
</Col>
<Col span={8}>
<Statistic title="Samples" value={summary.data_type_specific.samples} />
<Col span={12}>
<Statistic title="Samples" value={summary.data_type_specific?.samples ?? EM_DASH} />
</Col>
{summary.data_type_specific?.vcf_files !== undefined ? (
<Col span={8}>
<Statistic title="VCF Files" prefix={<FileOutlined />} value={summary.data_type_specific.vcf_files} />
</Col>
) : null}
</Row>
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCallback } from "react";
import { useDispatch } from "react-redux";
import PropTypes from "prop-types";

import { Form, Modal } from "antd";
Expand All @@ -8,13 +7,14 @@ import LinkedFieldSetForm from "./LinkedFieldSetForm";

import { FORM_MODE_ADD } from "@/constants";
import { addDatasetLinkedFieldSetIfPossible, saveDatasetLinkedFieldSetIfPossible } from "@/modules/metadata/actions";
import { useProjects } from "@/modules/metadata/hooks";
import { useDataTypes } from "@/modules/services/hooks";
import { datasetPropTypesShape, linkedFieldSetPropTypesShape, propTypesFormMode } from "@/propTypes";
import { useAppDispatch } from "@/store";
import { nop } from "@/utils/misc";
import { useProjects } from "@/modules/metadata/hooks";

const LinkedFieldSetModal = ({ dataset, linkedFieldSetIndex, linkedFieldSet, mode, open, onCancel, onSubmit }) => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();

const [form] = Form.useForm();

Expand Down
13 changes: 5 additions & 8 deletions src/components/discovery/DiscoveryQueryBuilder.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux";
import PropTypes from "prop-types";

import { Button, Card, Dropdown, Empty, Tabs, Typography } from "antd";
Expand All @@ -15,7 +14,7 @@ import {
removeDataTypeQueryForm,
} from "@/modules/explorer/actions";
import { useDataTypes, useServices } from "@/modules/services/hooks";
import { useAppDispatch } from "@/store";
import { useAppDispatch, useAppSelector } from "@/store";
import { nop } from "@/utils/misc";
import { OP_EQUALS } from "@/utils/search";
import { getFieldSchema } from "@/utils/schema";
Expand All @@ -28,18 +27,16 @@ const DiscoveryQueryBuilder = ({ activeDataset, dataTypeForms, requiredDataTypes

const { isFetching: isFetchingServiceDataTypes, itemsByID: dataTypesByID } = useDataTypes();
const dataTypesByDataset = useDatasetDataTypes();
const { isFetching: isFetchingServices } = useServices();

const { autoQuery, fetchingTextSearch } = useAppSelector((state) => state.explorer);

const autoQuery = useSelector((state) => state.explorer.autoQuery);
// Mini state machine: when auto query is set:
// 1. clear form(s) and set this to true;
// 2. re-create forms and wait to receive ref;
// 3. if this is true, and we have refs, execute part two of auto-query.
const [shouldExecAutoQueryPt2, setShouldExecAutoQueryPt2] = useState(false);

const isFetchingTextSearch = useSelector((state) => state.explorer.fetchingTextSearch);

const { isFetching: isFetchingServices } = useServices();

const dataTypesLoading = isFetchingServices || isFetchingServiceDataTypes || dataTypesByDataset.isFetchingAll;

const [schemasModalShown, setSchemasModalShown] = useState(false);
Expand Down Expand Up @@ -274,7 +271,7 @@ const DiscoveryQueryBuilder = ({ activeDataset, dataTypeForms, requiredDataTypes
type="primary"
icon={<SearchOutlined />}
loading={searchLoading}
disabled={dataTypeForms.length === 0 || isFetchingTextSearch}
disabled={dataTypeForms.length === 0 || fetchingTextSearch}
onClick={handleSubmit}
>
Search
Expand Down
4 changes: 2 additions & 2 deletions src/components/discovery/VariantSearchHeader.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState, useEffect, useCallback, useMemo } from "react";
import { useSelector } from "react-redux";
import PropTypes from "prop-types";

import { Form, Input, Select } from "antd";
Expand All @@ -8,6 +7,7 @@ import LocusSearch from "./LocusSearch";

import { notAlleleCharactersRegex } from "@/utils/misc";
import { useGohanVariantsOverview } from "@/modules/explorer/hooks";
import { useAppSelector } from "@/store";

const isValidLocus = (locus) => locus.chrom !== null && locus.start !== null && locus.end !== null;
const normalizeAlleleText = (text) => text.toUpperCase().replaceAll(notAlleleCharactersRegex, "");
Expand Down Expand Up @@ -44,7 +44,7 @@ const VariantSearchHeader = ({ dataType, addVariantSearchValues }) => {
const [activeAltValue, setActiveAltValue] = useState(null);
const [assemblyId, setAssemblyId] = useState(overviewAssemblyIds.length === 1 ? overviewAssemblyIds[0] : null);
const [locus, setLocus] = useState({ chrom: null, start: null, end: null });
const isSubmitting = useSelector((state) => state.explorer.isSubmittingSearch);
const { isSubmittingSearch: isSubmitting } = useAppSelector((state) => state.explorer);

// begin with required fields considered valid, so user isn't assaulted with error messages
const [fieldsValidity, setFieldsValidity] = useState(INITIAL_FIELDS_VALIDITY);
Expand Down
Loading

0 comments on commit 42acd14

Please sign in to comment.