diff --git a/src/main/webapp/app/components/LevelWithDescription.tsx b/src/main/webapp/app/components/LevelWithDescription.tsx
index cb73ddae8..8f5de178c 100644
--- a/src/main/webapp/app/components/LevelWithDescription.tsx
+++ b/src/main/webapp/app/components/LevelWithDescription.tsx
@@ -8,7 +8,7 @@ import ReactHtmlParser from 'react-html-parser';
import { LEVELS } from 'app/config/constants';
export const LevelWithDescription: React.FunctionComponent<{
- level: LEVELS;
+ level: LEVELS | undefined;
appStore?: AppStore;
description?: string;
}> = inject('appStore')(props => {
diff --git a/src/main/webapp/app/components/PageContainer.tsx b/src/main/webapp/app/components/PageContainer.tsx
index 6b52726b4..319c9ad5a 100644
--- a/src/main/webapp/app/components/PageContainer.tsx
+++ b/src/main/webapp/app/components/PageContainer.tsx
@@ -4,7 +4,10 @@ import WindowStore from 'app/store/WindowStore';
import { RouterStore } from 'mobx-react-router';
import { PAGE_ROUTE } from 'app/config/constants';
import { GENETIC_TYPE } from 'app/components/geneticTypeTabs/GeneticTypeTabs';
-import { parseGenePagePath } from 'app/shared/utils/UrlUtils';
+import {
+ parseGenePagePath,
+ parseAlterationPagePath,
+} from 'app/shared/utils/UrlUtils';
const Container: FunctionComponent<{
inGenePage: boolean;
@@ -25,18 +28,23 @@ const PageContainer: React.FunctionComponent<{
}> = props => {
const genePagePath = parseGenePagePath(props.routing.location.pathname);
const inGenePage = genePagePath.geneticType !== undefined;
+ const inAlterationPage =
+ parseAlterationPagePath(props.routing.location.pathname).geneticType !==
+ undefined;
return (
- {props.children}
+
+ {props.children}
+
);
diff --git a/src/main/webapp/app/config/constants.tsx b/src/main/webapp/app/config/constants.tsx
index 0db2981ab..f686a8fc0 100644
--- a/src/main/webapp/app/config/constants.tsx
+++ b/src/main/webapp/app/config/constants.tsx
@@ -604,6 +604,8 @@ export enum PAGE_ROUTE {
SOMATIC_GENE = '/gene/:hugoSymbol/somatic',
GERMLINE_GENE = '/gene/:hugoSymbol/germline',
ALTERATION = '/gene/:hugoSymbol/:alteration',
+ SOMATIC_ALTERATION = '/gene/:hugoSymbol/somatic/:alteration',
+ GERMLINE_ALTERATION = '/gene/:hugoSymbol/germline/:alteration',
HGVSG = '/hgvsg',
HGVSG_WITH_QUERY = '/hgvsg/:query',
GENOMIC_CHANGE = '/genomic-change',
diff --git a/src/main/webapp/app/pages/genePage/GeneInfo.tsx b/src/main/webapp/app/pages/genePage/GeneInfo.tsx
index 5792aa3c7..34b463662 100644
--- a/src/main/webapp/app/pages/genePage/GeneInfo.tsx
+++ b/src/main/webapp/app/pages/genePage/GeneInfo.tsx
@@ -16,7 +16,7 @@ enum GENE_TYPE_DESC {
TUMOR_SUPPRESSOR = 'Tumor Suppressor',
}
-const HighestLevelItem: React.FunctionComponent<{
+export const HighestLevelItem: React.FunctionComponent<{
level: LEVELS;
key?: string;
}> = props => {
diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss
new file mode 100644
index 000000000..94d9ce84d
--- /dev/null
+++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.module.scss
@@ -0,0 +1,27 @@
+.itemLink a {
+ justify-content: initial;
+}
+
+.itemHeader {
+ font-size: 1rem;
+ color: #878d96;
+}
+
+.alterationTile {
+ padding: 16px;
+ padding-bottom: 24px;
+ min-width: 347px;
+ max-width: 392px;
+ gap: 8px;
+ box-shadow: $default-box-shadow;
+ border-radius: 8px;
+}
+
+.alterationTileGrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+}
+
+.alterationTileItems {
+ gap: 16px;
+}
diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx
new file mode 100644
index 000000000..117558eb4
--- /dev/null
+++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/AlterationTile.tsx
@@ -0,0 +1,80 @@
+import React from 'react';
+import styles from './AlterationTile.module.scss';
+import classNames from 'classnames';
+import ExternalLinkIcon from 'app/shared/icons/ExternalLinkIcon';
+
+type AlterationItemProps =
+ | {
+ title: string;
+ value: JSX.Element | string;
+ link?: undefined;
+ }
+ | {
+ title: string;
+ value: string;
+ link: string;
+ };
+
+function AlterationItem({
+ title: itemTitle,
+ value,
+ link,
+}: AlterationItemProps) {
+ return (
+
+
{itemTitle}
+
+ {typeof value === 'string' ? (
+
+ {link ? (
+
+ {value}
+
+ ) : (
+ value
+ )}
+
+ ) : (
+ value
+ )}
+
+
+ );
+}
+
+type AlterationTileProps = {
+ title: string;
+ items: (AlterationItemProps | [AlterationItemProps, AlterationItemProps])[];
+};
+
+export default function AlterationTile({
+ title,
+ items,
+}: AlterationTileProps): JSX.Element {
+ return (
+
+
{title}
+
+ {items.map((parent, i) => {
+ if (Array.isArray(parent)) {
+ return (
+
+ {parent.map((child, j) => {
+ return
;
+ })}
+
+ );
+ } else {
+ return
;
+ }
+ })}
+
+
+ );
+}
diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx
new file mode 100644
index 000000000..6f7c0b609
--- /dev/null
+++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/HighestLevelEvidence.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import {
+ levelOfEvidence2Level,
+ OncoKBLevelIcon,
+ FdaLevelIcon,
+} from 'app/shared/utils/Utils';
+
+type HighestLevelEvidenceProp = {
+ type:
+ | 'Sensitive'
+ | 'Resistance'
+ | 'DiagnosticImplication'
+ | 'PrognosticImplication'
+ | 'Fda';
+ level: string | undefined;
+};
+
+export default function HighestLevelEvidence({
+ type,
+ level: rawLevel = '',
+}: HighestLevelEvidenceProp): JSX.Element {
+ if (type === 'Sensitive') {
+ const level = levelOfEvidence2Level(rawLevel, false);
+ return (
+
+ );
+ }
+ if (type === 'Resistance') {
+ const level = levelOfEvidence2Level(rawLevel, false);
+ return (
+
+ );
+ }
+ if (type === 'DiagnosticImplication') {
+ const level = levelOfEvidence2Level(rawLevel, false);
+ return (
+
+ );
+ }
+ if (type === 'PrognosticImplication') {
+ const level = levelOfEvidence2Level(rawLevel, false);
+ return (
+
+ );
+ }
+ if (type === 'Fda') {
+ const level = levelOfEvidence2Level(rawLevel, false);
+ return ;
+ }
+ return <>>;
+}
diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss
new file mode 100644
index 000000000..763372ffc
--- /dev/null
+++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.module.scss
@@ -0,0 +1,37 @@
+.backLink {
+ gap: 0.5rem;
+}
+
+.header {
+ margin-bottom: 0.5rem;
+}
+
+.headerContent {
+ gap: 0.5rem;
+}
+
+.navBarTitle {
+ .pill {
+ font-size: 0.85rem;
+ }
+}
+
+.pill {
+ font-size: 1rem;
+ border: #0968c3;
+ color: #0968c3;
+ border-style: solid;
+ border-width: 2px;
+ border-radius: 56px;
+ padding: 0px 8px;
+ background: #f0f5ff;
+}
+
+.alterationTiles {
+ gap: 8px;
+ padding: 32px 0px;
+}
+
+.descriptionContainer {
+ margin-bottom: 1rem;
+}
diff --git a/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx
new file mode 100644
index 000000000..9d84269f5
--- /dev/null
+++ b/src/main/webapp/app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage.tsx
@@ -0,0 +1,774 @@
+import React from 'react';
+import { Helmet } from 'react-helmet-async';
+import {
+ AnnotationStore,
+ TherapeuticImplication,
+ FdaImplication,
+} from 'app/store/AnnotationStore';
+import { AnnotationType } from '../annotationPage/AnnotationPage';
+import { RouteComponentProps } from 'react-router';
+import * as QueryString from 'query-string';
+import {
+ decodeSlash,
+ getPageTitle,
+ levelOfEvidence2Level,
+ getCancerTypeNameFromOncoTreeType,
+ getCancerTypesName,
+ getTreatmentNameByPriority,
+ articles2Citations,
+ isCategoricalAlteration,
+} from 'app/shared/utils/Utils';
+import {
+ getAlterationPageLink,
+ parseAlterationPagePath,
+ getGenePageLink,
+ AlterationPageLink,
+} from 'app/shared/utils/UrlUtils';
+import { computed, reaction, action, observable } from 'mobx';
+import { GENETIC_TYPE } from 'app/components/geneticTypeTabs/GeneticTypeTabs';
+import { observer, inject } from 'mobx-react';
+import { Link } from 'react-router-dom';
+import styles from './SomaticGermlineAlterationPage.module.scss';
+import classNames from 'classnames';
+import StickyMiniNavBar from 'app/shared/nav/StickyMiniNavBar';
+import AlterationView from '../annotationPage/AlterationView';
+import AppStore from 'app/store/AppStore';
+import ShowHideText from 'app/shared/texts/ShowHideText';
+import { Col, Alert } from 'reactstrap';
+import { Row, Container } from 'react-bootstrap';
+import { CancerTypeView } from '../annotationPage/CancerTypeView';
+import WindowStore from 'app/store/WindowStore';
+import AuthenticationStore from 'app/store/AuthenticationStore';
+import {
+ EVIDENCE_TYPES,
+ REFERENCE_GENOME,
+ ANNOTATION_PAGE_TAB_KEYS,
+ TREATMENT_EVIDENCE_TYPES,
+ ONCOKB_TM,
+} from 'app/config/constants';
+import { Alteration } from 'app/shared/api/generated/OncoKbAPI';
+import {
+ Evidence,
+ VariantAnnotationTumorType,
+} from 'app/shared/api/generated/OncoKbPrivateAPI';
+import WithSeparator from 'react-with-separator';
+import { uniqBy, upperFirst } from 'app/shared/utils/LodashUtils';
+import {
+ SummaryKey,
+ getSummaries,
+ getUniqueFdaImplications,
+} from '../annotationPage/Utils';
+import autobind from 'autobind-decorator';
+import { AlterationPageHashQueries } from 'app/shared/route/types';
+import MutationEffectDescription from '../annotationPage/MutationEffectDescription';
+import MiniNavBarHeader from 'app/shared/nav/MiniNavBarHeader';
+import AlterationTile from './AlterationTile';
+import HighestLevelEvidence from './HighestLevelEvidence';
+import { GenomicIndicatorTable } from '../genePage/GenomicIndicatorTable';
+import { Else, If, Then } from 'react-if';
+import LoadingIndicator, {
+ LoaderSize,
+} from 'app/components/loadingIndicator/LoadingIndicator';
+
+type MatchParams = {
+ hugoSymbol: string;
+ alteration: string;
+};
+
+type SomaticGermlineAlterationPageProps = {
+ appStore: AppStore;
+ windowStore: WindowStore;
+ authenticationStore: AuthenticationStore;
+} & RouteComponentProps;
+
+type SomaticGermlineAlterationPageState = {};
+
+@inject('appStore', 'windowStore', 'authenticationStore')
+@observer
+export class SomaticGermlineAlterationPage extends React.Component<
+ SomaticGermlineAlterationPageProps,
+ SomaticGermlineAlterationPageState
+> {
+ private store: AnnotationStore;
+ private selectedTab: ANNOTATION_PAGE_TAB_KEYS;
+
+ @observable showMutationEffect = true;
+
+ constructor(props: SomaticGermlineAlterationPageProps) {
+ super(props);
+ const alterationQuery = decodeSlash(props.match.params.alteration);
+ reaction(
+ () => [this.geneticType],
+ ([geneticType]) => {
+ if (props.match.params) {
+ this.store = new AnnotationStore({
+ type: alterationQuery
+ ? AnnotationType.PROTEIN_CHANGE
+ : AnnotationType.GENE,
+ hugoSymbolQuery: props.match.params.hugoSymbol,
+ alterationQuery,
+ germline: geneticType === GENETIC_TYPE.GERMLINE,
+ });
+ if (this.store.cancerTypeName) {
+ this.showMutationEffect = false;
+ }
+ }
+ },
+ true
+ );
+ reaction(
+ () => [props.location.hash],
+ ([hash]) => {
+ const queryStrings = QueryString.parse(
+ hash
+ ) as AlterationPageHashQueries;
+ if (queryStrings.tab) {
+ this.selectedTab = queryStrings.tab;
+ if (queryStrings.tab === ANNOTATION_PAGE_TAB_KEYS.FDA) {
+ this.props.appStore.inFdaRecognizedContent = true;
+ }
+ }
+ },
+ true
+ );
+ }
+
+ @action.bound
+ toggleMutationEffect(value: boolean) {
+ this.showMutationEffect = value;
+ }
+
+ @computed
+ get pageShouldBeRendered() {
+ return (
+ this.store.gene.isComplete &&
+ this.store.geneNumber.isComplete &&
+ this.store.ensemblGenes.isComplete &&
+ this.store.clinicalAlterations.isComplete &&
+ this.store.biologicalAlterations.isComplete &&
+ this.store.annotationData.isComplete
+ );
+ }
+
+ @computed
+ get errorOccurred() {
+ return (
+ this.store.gene.isError ||
+ this.store.geneNumber.isError ||
+ this.store.ensemblGenes.isError ||
+ this.store.clinicalAlterations.isError ||
+ this.store.biologicalAlterations.isError ||
+ this.store.annotationData.isError
+ );
+ }
+
+ @computed
+ get documentTitle() {
+ const content = [];
+ if (this.store.hugoSymbol) {
+ content.push(this.store.hugoSymbol);
+ }
+ if (this.store.alterationQuery) {
+ content.push(this.store.alterationQuery);
+ }
+ if (this.store.tumorTypeQuery) {
+ content.push(`in ${this.store.cancerTypeName}`);
+ }
+ return getPageTitle(content.join(' '));
+ }
+
+ @computed
+ get geneticType() {
+ const { geneticType } = parseAlterationPagePath(
+ this.props.location.pathname
+ );
+ return geneticType ?? GENETIC_TYPE.SOMATIC;
+ }
+
+ onChangeTumorType(newTumorType: string) {
+ this.store.tumorTypeQuery = newTumorType;
+ }
+
+ getImplications(evidences: Evidence[]) {
+ return evidences.reduce((acc, evidence) => {
+ const level = levelOfEvidence2Level(evidence.levelOfEvidence);
+ const fdaLevel = levelOfEvidence2Level(evidence.fdaLevel);
+ const alterations = evidence.alterations.filter(alteration =>
+ alteration.referenceGenomes.includes(this.store.referenceGenomeQuery)
+ );
+ const alterationsName = alterations
+ .map(alteration => alteration.name)
+ .join(', ');
+ const cancerTypes = evidence.cancerTypes.map(cancerType =>
+ getCancerTypeNameFromOncoTreeType(cancerType)
+ );
+ const excludedCancerTypes = evidence.excludedCancerTypes.map(ct =>
+ getCancerTypeNameFromOncoTreeType(ct)
+ );
+ const cancerTypesName = getCancerTypesName(
+ cancerTypes,
+ excludedCancerTypes
+ );
+ if (evidence.treatments.length > 0) {
+ evidence.treatments.forEach(treatment => {
+ acc.push({
+ level,
+ fdaLevel,
+ drugDescription: evidence.description,
+ alterations: alterationsName,
+ alterationsView: (
+
+ {alterations.map(alteration =>
+ alteration.consequence ? (
+
+ ) : (
+ {alteration.name}
+ )
+ )}
+
+ ),
+ drugs: getTreatmentNameByPriority(treatment),
+ cancerTypes: cancerTypesName,
+ cancerTypesView: (
+ <>
+
+ {cancerTypes.map(cancerType => (
+
+ {cancerType}
+
+ ))}
+
+ {excludedCancerTypes.length > 0 ? (
+ (excluding {excludedCancerTypes.join(', ')})
+ ) : (
+ <>>
+ )}
+ >
+ ),
+ citations: articles2Citations(evidence.articles),
+ } as TherapeuticImplication);
+ });
+ } else {
+ acc.push({
+ level,
+ fdaLevel,
+ drugDescription: evidence.description,
+ alterations: alterationsName,
+ alterationsView: (
+
+ {alterations.map(alteration =>
+ alteration.consequence ? (
+
+ ) : (
+ {alteration.name}
+ )
+ )}
+
+ ),
+ drugs: '',
+ cancerTypes: cancerTypesName,
+ cancerTypesView: (
+ <>
+
+ {cancerTypes.map(cancerType => (
+
+ {cancerType}
+
+ ))}
+
+ {excludedCancerTypes.length > 0 ? (
+ (excluding {excludedCancerTypes.join(', ')})
+ ) : (
+ <>>
+ )}
+ >
+ ),
+ citations: articles2Citations(evidence.articles),
+ } as TherapeuticImplication);
+ }
+ return acc;
+ }, [] as TherapeuticImplication[]);
+ }
+
+ getEvidenceByEvidenceTypes(
+ cancerTypes: VariantAnnotationTumorType[],
+ evidenceTypes: EVIDENCE_TYPES[]
+ ): Evidence[] {
+ let uniqueEvidences: Evidence[] = [];
+ cancerTypes.forEach(cancerType => {
+ uniqueEvidences = uniqueEvidences.concat(
+ cancerType.evidences.filter(evidence =>
+ evidenceTypes.includes(evidence.evidenceType as EVIDENCE_TYPES)
+ )
+ );
+ });
+
+ return uniqBy(uniqueEvidences, evidence => evidence.id);
+ }
+
+ @computed
+ get therapeuticImplications(): TherapeuticImplication[] {
+ return this.getImplications(
+ this.getEvidenceByEvidenceTypes(
+ this.store.annotationData.result.tumorTypes,
+ TREATMENT_EVIDENCE_TYPES
+ )
+ );
+ }
+
+ @computed
+ get fdaImplication(): FdaImplication[] {
+ const evidences = this.getEvidenceByEvidenceTypes(
+ this.store.annotationData.result.tumorTypes,
+ TREATMENT_EVIDENCE_TYPES
+ );
+ const fdaImplications: FdaImplication[] = [];
+ evidences.forEach(evidence => {
+ const level = levelOfEvidence2Level(evidence.levelOfEvidence);
+ const fdaLevel = levelOfEvidence2Level(evidence.fdaLevel);
+ const alterations = evidence.alterations.filter(alteration =>
+ alteration.referenceGenomes.includes(this.store.referenceGenomeQuery)
+ );
+ alterations.forEach(alt => {
+ // convert all alterations to matchedAlteration/alteration query if not positional variant
+ let mappedAlteration = {} as Alteration;
+ if (
+ this.store.relevantAlterations.result
+ .map(relevantAlt => relevantAlt.alteration)
+ .includes(alt.alteration)
+ ) {
+ mappedAlteration = alt;
+ } else {
+ if (this.store.alteration.result) {
+ mappedAlteration = this.store.alteration.result;
+ } else {
+ mappedAlteration.name = mappedAlteration.alteration = this.store.alterationName;
+ }
+ }
+ const ctNames = evidence.cancerTypes.map(ct =>
+ getCancerTypeNameFromOncoTreeType(ct)
+ );
+ const excludedCtNames = evidence.excludedCancerTypes.map(ct =>
+ getCancerTypeNameFromOncoTreeType(ct)
+ );
+ fdaImplications.push({
+ level: fdaLevel,
+ alteration: mappedAlteration,
+ alterationView: (
+
+ ),
+ cancerType: getCancerTypesName(ctNames, excludedCtNames),
+ cancerTypeView: (
+ <>
+
+ {ctNames.map(cancerType => (
+
+ {cancerType}
+
+ ))}
+
+ {excludedCtNames.length > 0 ? (
+ (excluding {excludedCtNames.join(', ')})
+ ) : (
+ <>>
+ )}
+ >
+ ),
+ });
+ });
+ });
+ return getUniqueFdaImplications(fdaImplications);
+ }
+
+ @computed
+ get diagnosticImplications(): TherapeuticImplication[] {
+ return this.getImplications(
+ this.getEvidenceByEvidenceTypes(
+ this.store.annotationData.result.tumorTypes,
+ [EVIDENCE_TYPES.DIAGNOSTIC_IMPLICATION]
+ )
+ );
+ }
+
+ @computed
+ get prognosticImplications(): TherapeuticImplication[] {
+ return this.getImplications(
+ this.getEvidenceByEvidenceTypes(
+ this.store.annotationData.result.tumorTypes,
+ [EVIDENCE_TYPES.PROGNOSTIC_IMPLICATION]
+ )
+ );
+ }
+
+ @computed
+ get alterationSummaries() {
+ const orderedSummaries = [SummaryKey.GENE_SUMMARY];
+ if (!this.isCategoricalAlteration) {
+ orderedSummaries.push(SummaryKey.ALTERATION_SUMMARY);
+ }
+ return getSummaries(this.store.annotationData.result, orderedSummaries);
+ }
+
+ @computed get isCategoricalAlteration() {
+ return isCategoricalAlteration(this.store.alterationName);
+ }
+
+ @autobind
+ onChangeTab(
+ selectedTabKey: ANNOTATION_PAGE_TAB_KEYS,
+ newTabKey: ANNOTATION_PAGE_TAB_KEYS
+ ) {
+ if (newTabKey === ANNOTATION_PAGE_TAB_KEYS.FDA) {
+ this.props.appStore.inFdaRecognizedContent = true;
+ }
+ if (
+ selectedTabKey === ANNOTATION_PAGE_TAB_KEYS.FDA &&
+ newTabKey !== ANNOTATION_PAGE_TAB_KEYS.FDA
+ ) {
+ this.props.appStore.showFdaModal = true;
+ } else {
+ const newHash: AlterationPageHashQueries = { tab: newTabKey };
+ window.location.hash = QueryString.stringify(newHash);
+ }
+ this.selectedTab = newTabKey;
+ }
+
+ render() {
+ const headerText = (
+
+ {this.store.hugoSymbol} {this.store.alterationNameWithDiff}{' '}
+
+ {upperFirst(this.geneticType)}
+
+
+ );
+ return (
+ <>
+
+ {this.documentTitle}
+
+
+ {this.pageShouldBeRendered ? (
+ <>
+
+
+
+
+
+
+ Back to {this.store.hugoSymbol}
+
+
+ {headerText}
+
+
+ {this.store.annotationData.result.mutationEffect
+ .description && (
+
+
+
+ }
+ onClick={() =>
+ this.toggleMutationEffect(!this.showMutationEffect)
+ }
+ />
+
+
+ )}
+
+
+
+
+ {headerText}
+
+ }
+ />
+
+
+
+
+ {this.store.germline && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+ ),
+ },
+ {
+ title: 'Diagnostic',
+ value: (
+
+ ),
+ },
+ ],
+ [
+ {
+ title: 'Prognostic',
+ value: (
+
+ ),
+ },
+ {
+ title: 'FDA',
+ value: (
+
+ ),
+ },
+ ],
+ ]}
+ />
+
+
+
+
+
+
+ Genomic Indicators
+
+
+
+ Clinical Implications
+
+ {this.store.cancerTypeName ? (
+
+ ) : (
+
+ )}
+
+
+
+ >
+ ) : (
+
+
+
+ An error occurred while annotating your variant.
+
+
+
+
+
+
+ )}
+ >
+ );
+ }
+}
diff --git a/src/main/webapp/app/routes/routes.tsx b/src/main/webapp/app/routes/routes.tsx
index b95a37211..a62238256 100644
--- a/src/main/webapp/app/routes/routes.tsx
+++ b/src/main/webapp/app/routes/routes.tsx
@@ -37,6 +37,7 @@ import { NewsPageNavTab } from 'app/pages/newsPage/NewsPageNavTab';
import CompanionDiagnosticDevicePage from 'app/pages/companionDiagnosticDevicesPage/companionDiagnosticDevicePage';
import OncokbRoute from 'app/shared/route/OncokbRoute';
import GenePage from 'app/pages/genePage/GenePage';
+import { SomaticGermlineAlterationPage } from 'app/pages/somaticGermlineAlterationPage/SomaticGermlineAlterationPage';
const getOldLevelsRedirectRoute = (hash: string) => {
const queryStrings = QueryString.parse(hash) as {
@@ -192,6 +193,20 @@ const AppRoutes = (props: {
path={PAGE_ROUTE.ALTERATION}
component={AlterationPage}
/>
+
+
{
const linkoutAltName = getCategoricalAlteration(
typeof props.alteration === 'string'
@@ -96,7 +97,12 @@ export const getAlterationPageLink = (props: {
: props.alteration.name
);
- let pageLink = `${PAGE_ROUTE.GENE_HEADER}/${props.hugoSymbol}/${linkoutAltName}`;
+ const geneTypePath =
+ props.germline !== undefined
+ ? `/${props.germline ? 'germline' : 'somatic'}`
+ : '';
+
+ let pageLink = `${PAGE_ROUTE.GENE_HEADER}/${props.hugoSymbol}${geneTypePath}/${linkoutAltName}`;
if (props.cancerType) {
pageLink = `${pageLink}/${encodeSlash(props.cancerType)}`;
}
@@ -202,6 +208,28 @@ export const parseGenePagePath = (pathname: string) => {
}
};
+export type AlterationPagePath = {
+ hugoSymbol?: string;
+ geneticType?: GENETIC_TYPE;
+};
+
+export const parseAlterationPagePath = (pathname: string) => {
+ const startsWithGene = pathname.startsWith(PAGE_ROUTE.GENE_HEADER);
+ const inExtendedGenePage = (pathname.match(/\//g) || []).length === 4;
+ if (startsWithGene && inExtendedGenePage) {
+ const segments = pathname.split('/') || [];
+ const result: GenePagePath = {
+ hugoSymbol: segments[1],
+ };
+ if (segments.length > 3) {
+ result.geneticType = segments[3] as GENETIC_TYPE;
+ }
+ return result;
+ } else {
+ return {};
+ }
+};
+
export const getGenomicPageLocation = (props: {
rootRoute: PAGE_ROUTE.GENOMIC_CHANGE | PAGE_ROUTE.HGVSG;
query: string;
diff --git a/src/main/webapp/app/shared/utils/Utils.tsx b/src/main/webapp/app/shared/utils/Utils.tsx
index eea79422d..f7dd578ac 100644
--- a/src/main/webapp/app/shared/utils/Utils.tsx
+++ b/src/main/webapp/app/shared/utils/Utils.tsx
@@ -142,7 +142,7 @@ export function toggleStrList(element: string, list: string[]) {
return list;
}
-export function level2LevelOfEvidence(level: LEVELS) {
+export function level2LevelOfEvidence(level: LEVELS | undefined) {
switch (level) {
case LEVELS.Tx3:
case LEVELS.Tx3A:
@@ -314,7 +314,7 @@ export const OncoKBLevelIcon: React.FunctionComponent<{
};
export const FdaLevelIcon: React.FunctionComponent<{
- level: LEVELS;
+ level: LEVELS | undefined;
withDescription?: boolean;
size?: 's1' | 's2' | 's3';
}> = ({ level, withDescription = true, size = 's1' }) => {
@@ -324,11 +324,14 @@ export const FdaLevelIcon: React.FunctionComponent<{
lineHeight: `${16 * scale}px`,
margin: '0 3px',
};
+ if (level === undefined) {
+ return <>>;
+ }
const fdaIcon = (
- {level.toString().replace('Fda', '')}
+ {level?.toString().replace('Fda', '')}
);