Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: configurability + precomputed peaks #9

Merged
merged 106 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
c5d5936
lint: controls
davidlougheed Jun 12, 2023
adea46a
fix: update ui if peak changes in url in PeakResults
davidlougheed Jun 12, 2023
072dbb9
chore(config): add note on switching to hg38
davidlougheed Jun 12, 2023
2982525
chore: rm unused front end box plot component
davidlougheed Jun 16, 2023
b16886b
chore: remove unused front end svg pattern
davidlougheed Jun 16, 2023
8e72434
refact: rewrite App as functional component
davidlougheed Jun 16, 2023
48db23e
lint
davidlougheed Jun 16, 2023
ea3cbd2
lint: remove dead code & reformat
davidlougheed Jun 16, 2023
b55ed4a
feat: configurable assembly/conditions/ethnicities
davidlougheed Jun 16, 2023
72f98af
feat: configurable gemini sample name extractor fn
davidlougheed Jun 16, 2023
52123ec
chore: dynamic conditions for p-values in db
davidlougheed Jun 16, 2023
40e2a87
refact: rewrite App component with more memoization
davidlougheed Jun 16, 2023
4432c8d
chore: add input-files subdirectory gitkeeps
davidlougheed Jun 19, 2023
ea89b1d
feat: configurable static tracks file location
davidlougheed Jun 19, 2023
400659a
feat: if ucsc static tracks not provided, show nothing instead of err…
davidlougheed Jun 19, 2023
022e64d
fix: missed handling of dynamic conditions for plot generation
davidlougheed Jun 19, 2023
dacf74c
refact: configurable qtls input file path
davidlougheed Jun 19, 2023
477cc5a
refact: common constants for genotype states (ref/het/hom)
davidlougheed Jun 19, 2023
4509c27
lint
davidlougheed Jun 19, 2023
d6efb57
chore: consistent rna-seq default spelling
davidlougheed Jun 19, 2023
06366d2
chore: prep work in tracks model for precomputed values
davidlougheed Jun 19, 2023
8a88682
feat: accept precomputed query arg for values/plot track endpoints
davidlougheed Jun 19, 2023
2613fe5
fix: use different cache key for precomputed track values
davidlougheed Jun 19, 2023
2a06590
refact: use plot size constants everywhere; add constant for track va…
davidlougheed Jun 19, 2023
2d2fcf0
chore: add config path for point matrix file path template
davidlougheed Jun 19, 2023
569f2bd
chore: add points 2d array to schema for precomputed vals (opt)
davidlougheed Jun 19, 2023
3badc5e
chore!: change default config to use env vars for some directories/paths
davidlougheed Jun 20, 2023
6385c59
docs: specify when notes are aracena-et-al specific
davidlougheed Jun 20, 2023
cb90c01
feat: precomputed points import in import-peaks script
davidlougheed Jun 20, 2023
3477a36
chore: optionally load qtls location template from env var
davidlougheed Jun 20, 2023
58c5028
chore: add inputFilesDirname to config & use for gene files
davidlougheed Jun 20, 2023
5e37a24
fix: filter out undefined rows in feature matrix for precomputed points
davidlougheed Jun 20, 2023
3595924
fix: loading precomputed points error in script
davidlougheed Jun 20, 2023
826acfe
fix: sql error in import-peaks
davidlougheed Jun 20, 2023
f935865
fix: sql error again?
davidlougheed Jun 20, 2023
c0e1b65
chore: one-off script for fixing symbols in rna matrix
davidlougheed Jun 20, 2023
90e0b83
fix: issue with one-off
davidlougheed Jun 20, 2023
9e8caf3
oops
davidlougheed Jun 20, 2023
2c58935
fix: preserve old symbols in rnaseq one-off if no new one found
davidlougheed Jun 20, 2023
59ee988
fix: one-off
davidlougheed Jun 20, 2023
8306e70
fix: importing precomputed points
davidlougheed Jun 20, 2023
583ed38
fix(scripts): import-peaks strip quote
davidlougheed Jun 20, 2023
f935aed
fix(scripts): import-peaks points
davidlougheed Jun 20, 2023
555e4f4
refact!: move points to features
davidlougheed Jun 20, 2023
55e7e94
fix: import in scripts common
davidlougheed Jun 20, 2023
6d7155f
oops
davidlougheed Jun 20, 2023
ac98c02
fix: consistent atac-seq assay name
davidlougheed Jun 20, 2023
c555189
feat(client): usePrecomputed ui/plot state + checkbox
davidlougheed Jun 21, 2023
2f2da96
fix: server box plot module export/import/es6 module
davidlougheed Jun 21, 2023
646eee5
fix: boxplot config import
davidlougheed Jun 21, 2023
ebeae29
fix: conditions loading in track model
davidlougheed Jun 21, 2023
ebd8bfe
chore: bump version to 0.13.0
davidlougheed Jun 21, 2023
f0c294c
chore: update dependencies
davidlougheed Jun 21, 2023
8c82612
docs: notes on building metadata file from scratch + more info on tracks
davidlougheed Jun 21, 2023
a2b00ed
docs: wording tweaks + notes on peaks
davidlougheed Jun 21, 2023
58f9596
docs: note peaks are csvs
davidlougheed Jun 21, 2023
9bc7d5d
docs: conditions and ethnicities config
davidlougheed Jun 21, 2023
66ebb8b
fix(client): bad deconstruct of ui state
davidlougheed Jun 23, 2023
f21a8e0
fix(client): value access in peak table
davidlougheed Jun 23, 2023
b231b2a
fix(client): hook typo
davidlougheed Jun 23, 2023
52416d0
fix: geminiSampleNameConverter access
davidlougheed Jun 23, 2023
5dbd884
fix: precomputed values fetch
davidlougheed Jun 23, 2023
99ea629
fix: plot positioning / transform / width handling
davidlougheed Jun 23, 2023
cadce64
debug
davidlougheed Jun 23, 2023
0ca1ac2
contd
davidlougheed Jun 23, 2023
9c9c036
debug: temp disable cache
davidlougheed Jun 23, 2023
05a6702
chore(client): temp-disable precomputed checkbox
davidlougheed Jun 23, 2023
5a0820e
restore caching
davidlougheed Jun 23, 2023
eb7e29f
fix: precomputed points as flat array with donor_condition keys
davidlougheed Jun 23, 2023
7fce57e
chore: temp hide precomputed checkbox
davidlougheed Jun 23, 2023
18e1954
^^
davidlougheed Jun 23, 2023
d6f58da
chore: explicit sort fn for donor lookup
davidlougheed Jun 23, 2023
36e8096
fix: consistent feature names for precomputed point lookup
davidlougheed Jun 23, 2023
5569899
chore: parse metadata tracks as json in models.source.metadata
davidlougheed Jun 23, 2023
0547720
fix(scripts): ref to valueFlu/valueNI in calculate-top-peaks
davidlougheed Jun 23, 2023
f9520f8
fix(scripts): calculate-top-peaks nested query
davidlougheed Jun 23, 2023
f7b51d6
^^
davidlougheed Jun 23, 2023
fc2ea30
^^
davidlougheed Jun 23, 2023
84d290f
chore(scripts): split calculate-peak-groups into separate script
davidlougheed Jun 23, 2023
10ad6d3
fix(scripts): missing dotenv import
davidlougheed Jun 23, 2023
5cdd071
chore: add runWithTransaction db method
davidlougheed Jun 23, 2023
92776f3
fix: gene pre-processing to find top peaks
davidlougheed Jun 23, 2023
52dc740
fix(scripts): clear peak groups table on re run
davidlougheed Jun 23, 2023
2e3de7e
fix: pull chromosome sizes from assembly not manhattan config
davidlougheed Jun 23, 2023
795ff75
fix(client): overview page crash
davidlougheed Jun 23, 2023
96fc034
docs: notes on genes and gene-features files
davidlougheed Jun 26, 2023
2997869
fix(scripts): import points with features from gene-peaks file
davidlougheed Jun 26, 2023
f59e8e4
Merge remote-tracking branch 'origin/master' into feat/precomputed-peaks
davidlougheed Jul 3, 2023
2e2165e
fix: loading precomputed points for features in gene-peaks file
davidlougheed Jul 4, 2023
5a2bddc
fix: consistent donor lookup array between plot + ingest
davidlougheed Jul 5, 2023
55c9405
docs: require metadata ingest before points ingest
davidlougheed Jul 5, 2023
f2e0e1a
fix: script imports
davidlougheed Jul 5, 2023
b86bc13
fix: null handling for precomputed points
davidlougheed Jul 5, 2023
2adcf0e
debug logging
davidlougheed Jul 6, 2023
34f66a5
chore(client): re-enable checkbox for precomputed
davidlougheed Jul 6, 2023
64b784b
lint: clean up debug logging
davidlougheed Jul 6, 2023
3cdbfc6
chore(client): re-enable precomputed checkbox
davidlougheed Jul 6, 2023
ad40023
chore(client): use precomputed points by default
davidlougheed Jul 6, 2023
d5cd0f9
fix(client): properly include precomputed setting in value cache posts
davidlougheed Jul 6, 2023
e7678f5
docs: update wording around link to current instance
davidlougheed Jul 6, 2023
d3c9d94
fix(client): missing usePrecomputed pass to cacheValues
davidlougheed Jul 6, 2023
725fc01
chore(client): try debugging pre-cache
davidlougheed Jul 6, 2023
172322d
perf: disable pre-caching requests
davidlougheed Jul 6, 2023
9991e91
chore: update server dependencies
davidlougheed Jul 6, 2023
5a688be
chore(client): update client dependencies
davidlougheed Jul 6, 2023
2569c2b
chore(client): run npm audit
davidlougheed Jul 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/data/*
!/data/.gitkeep
!/data/otherData
!/data/ucsc.other-tracks.txt
/input-files/*.csv
!/input-files/flu-infection-gene-peaks.csv
/config.js
Expand Down
3 changes: 3 additions & 0 deletions app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ if (!process.env.VARWIG_DISABLE_AUTH) {
app.use('/api/assays', (await import('./routes/assays.mjs')).default);
app.use('/api/autocomplete', (await import('./routes/autocomplete.mjs')).default);
app.use('/api/messages', (await import('./routes/messages.mjs')).default);
app.use('/api/assembly', (await import('./routes/assembly.mjs')).default);
app.use('/api/overview', (await import('./routes/overview.mjs')).default);
app.use('/api/conditions', (await import('./routes/conditions.mjs')).default);
app.use('/api/ethnicities', (await import('./routes/ethnicities.mjs')).default);
app.use('/api/peaks', (await import('./routes/peaks.mjs')).default);
app.use('/api/sessions', (await import('./routes/sessions.mjs')).default);
app.use('/api/samples', (await import('./routes/samples.mjs')).default);
Expand Down
15,441 changes: 1,619 additions & 13,822 deletions client/package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flu-infection-client",
"version": "0.12.2",
"version": "0.13.0",
"private": true,
"dependencies": {
"@redux-devtools/extension": "^3.2.5",
Expand All @@ -13,11 +13,11 @@
"d3-scale": "^4.0.2",
"font-awesome": "^4.7.0",
"memoize-one": "^6.0.0",
"rambda": "^7.5.0",
"rambda": "^8.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^8.0.5",
"react-router-dom": "^6.11.2",
"react-redux": "^8.1.1",
"react-router-dom": "^6.14.1",
"react-table": "^7.8.0",
"reactstrap": "^8.10.1",
"redux": "^4.2.1",
Expand Down
73 changes: 45 additions & 28 deletions client/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,38 @@ import axios from 'axios'

import * as api from './api'
import * as k from './constants/ActionTypes.js'
import {ASSEMBLY, BASE_URL} from "./constants/app";
import {BASE_URL} from "./constants/app";
import {constructUCSCUrl} from "./helpers/ucsc";

export const setChrom = createAction(k.SET_CHROM);
export const setPosition = createAction(k.SET_POSITION);
export const setOverviewChrom = createAction(k.SET_OVERVIEW_CHROM);
export const setOverviewAssay = createAction(k.SET_OVERVIEW_ASSAY);
export const handleError = createAction(k.HANDLE_ERROR);

export const assays = createFetchActions(k.ASSAYS);
export const samples = createFetchActions(k.SAMPLES);
export const chroms = createFetchActions(k.CHROMS);
export const positions = createFetchActions(k.POSITIONS);
export const values = createFetchActions(k.VALUES);
export const peaks = createFetchActions(k.PEAKS);
export const overviewConfig = createFetchActions(k.OVERVIEW_CONFIG);
export const manhattanData = createFetchActions(k.MANHATTAN_DATA);
export const user = createFetchActions(k.USER);
export const messages = createFetchActions(k.MESSAGES);
export const setChrom = createAction(k.SET_CHROM);
export const setPosition = createAction(k.SET_POSITION);
export const setOverviewChrom = createAction(k.SET_OVERVIEW_CHROM);
export const setOverviewAssay = createAction(k.SET_OVERVIEW_ASSAY);
export const setUsePrecomputed = createAction(k.SET_USE_PRECOMPUTED);
export const handleError = createAction(k.HANDLE_ERROR);

export const assays = createFetchActions(k.ASSAYS);
export const samples = createFetchActions(k.SAMPLES);
export const chroms = createFetchActions(k.CHROMS);
export const positions = createFetchActions(k.POSITIONS);
export const values = createFetchActions(k.VALUES);
export const peaks = createFetchActions(k.PEAKS);
export const assembly = createFetchActions(k.ASSEMBLY);
export const conditions = createFetchActions(k.CONDITIONS);
export const ethnicities = createFetchActions(k.ETHNICITIES);
export const overviewConfig = createFetchActions(k.OVERVIEW_CONFIG);
export const manhattanData = createFetchActions(k.MANHATTAN_DATA);
export const user = createFetchActions(k.USER);
export const messages = createFetchActions(k.MESSAGES);

export const fetchAssays = createFetchFunction(api.fetchAssays, assays);
export const fetchChroms = createFetchFunction(api.fetchChroms, chroms);
// export const fetchChroms = createFetchFunction(api.fetchChroms, chroms);
export const fetchPositions = createFetchFunction(api.fetchPositions, positions);
export const cacheValues = createFetchFunction(api.cacheValues, values);
// export const cacheValues = createFetchFunction(api.cacheValues, values);
export const fetchPeaks = createFetchFunction(api.fetchPeaks, peaks);
export const fetchAssembly = createFetchFunction(api.fetchAssembly, assembly);
export const fetchConditions = createFetchFunction(api.fetchConditions, conditions);
export const fetchEthnicities = createFetchFunction(api.fetchEthnicities, ethnicities);
export const fetchOverviewConfig = createFetchFunction(api.fetchOverviewConfig, overviewConfig);
export const fetchManhattanData = createFetchFunction(api.fetchManhattanData, manhattanData);
export const fetchUser = createFetchFunction(api.fetchUser, user);
Expand All @@ -44,32 +51,42 @@ export const doSearch = () => (dispatch, getState) => {
}
};

export const mergeTracks = peak => dispatch => {
const buildUCSCHighlight = (asm, chr, start, end, color) => `${asm}.${chr}:${start}-${end}${color}`;

export const mergeTracks = peak => (dispatch, getState) => {
const assembly = getState().assembly.data?.id;

if (!assembly) {
console.error(`Could not retrieve assembly ID - got ${assembly}`);
return;
}

const session = {...peak};
const {feature, snp} = session;

const padding = 500;

const featureChrom = `chr${session.feature.chrom}`;
const snpChrom = `chr${session.snp.chrom}`;
const featureChrom = `chr${feature.chrom}`;
const snpChrom = `chr${snp.chrom}`;

api.createSession(session)
.then(sessionID => {
const snpPosition = session.snp.position;
const snpPosition = snp.position;
const displayWindow = featureChrom === snpChrom
? [Math.min(session.feature.start, snpPosition), Math.max(session.feature.end, snpPosition)]
: [session.feature.start, session.feature.end];
? [Math.min(feature.start, snpPosition), Math.max(feature.end, snpPosition)]
: [feature.start, feature.end];
const position = `${featureChrom}:${displayWindow[0]-padding}-${displayWindow[1]+padding}`;
const hubURL = `${BASE_URL}/api/ucsc/hub/${sessionID}`;
const ucscURL = constructUCSCUrl([
["db", ASSEMBLY],
["db", assembly],
["hubClear", hubURL],
// ["hubClear", permaHubURL],
["position", position],

// Highlight the SNP in red, and the feature in light yellow
["highlight", [
`${ASSEMBLY}.${featureChrom}:${session.feature.start}-${session.feature.end}#FFEECC`,
`${ASSEMBLY}.${snpChrom}:${session.snp.position}-${session.snp.position+1}#FF9F9F`,
buildUCSCHighlight(assembly, featureChrom, feature.start, feature.end, "#FFEECC"),
buildUCSCHighlight(assembly, snpChrom, snp.position, snp.position + 1, "#FF9F9F"),
].join("|")],
]);

Expand Down
28 changes: 16 additions & 12 deletions client/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@ export function fetchPeaks(params) {
return get('/peaks/query', params)
}

export function fetchChroms() {
return get('/autocomplete/chroms')
.then(chroms => {
const parse = string => +string.slice(3)
chroms.sort((a, b) => parse(a) - parse(b))
return chroms
})
}
// export function fetchChroms() {
// return get('/autocomplete/chroms')
// .then(chroms => {
// const parse = string => +string.slice(3)
// chroms.sort((a, b) => parse(a) - parse(b))
// return chroms
// })
// }

export function fetchPositions(params, cancelToken) {
return get('/autocomplete/positions', params, {cancelToken})
}

export const fetchOverviewConfig = () => get('/overview/config');
export const fetchAssembly = () => get('/assembly');
export const fetchConditions = () => get('/conditions');
export const fetchEthnicities = () => get('/ethnicities');

export const fetchOverviewConfig = () => get('/overview/config');
export const fetchManhattanData = ({chrom, assay}) => get(`/overview/assays/${assay}/topBinned/${chrom}`);


Expand All @@ -46,14 +49,15 @@ export const fetchManhattanData = ({chrom, assay}) => get(`/overview/assays/${as
* @property {string} feature.chrom
* @property {number} feature.start
* @property {number} feature.end
* @property {boolean} usePrecomputed
*/

/**
* @param {ValuesOptions} params
*/
export function cacheValues(params) {
return post('/tracks/values', params)
}
// export function cacheValues(params) {
// return post(`/tracks/values?precomputed=${params.usePrecomputed ? '1' : '0'}`, params)
// }

export function createSession(params) {
return post('/sessions/create', params)
Expand Down
101 changes: 43 additions & 58 deletions client/src/components/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {Component, useEffect, useState} from 'react';
import React, {useCallback, useEffect, useState} from 'react';
import {Navigate, Outlet, Route, Routes, useLocation, useNavigate} from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";

Expand All @@ -8,8 +8,6 @@ import PeakResults from './PeakResults'
import HelpModal from "./HelpModal";
import TermsModal from "./TermsModal";

import {ETHNICITY_BOX_COLOR} from "../constants/app";
import {saveUser} from "../actions";
import ContactModal from "./ContactModal";
import AboutPage from "./pages/AboutPage";
import ProtectedPageContainer from "./pages/ProtectedPageContainer";
Expand All @@ -18,6 +16,8 @@ import ExplorePage from "./pages/ExplorePage";
import DatasetsPage from "./pages/DatasetsPage";
import FAQPage from "./pages/FAQPage";

import {saveUser} from "../actions";


const RoutedApp = () => {
const location = useLocation();
Expand All @@ -33,21 +33,22 @@ const RoutedApp = () => {
const chrom = useSelector(state => state.ui.chrom);
const position = useSelector(state => state.ui.position);

const toggleHelp = () => setHelpModal(!helpModal);
const toggleContact = () => setContactModal(!contactModal);
const toggleTerms = () => setTermsModal(!termsModal);
const toggleHelp = useCallback(() => setHelpModal(!helpModal), [helpModal]);
const toggleContact = useCallback(() => setContactModal(!contactModal), [contactModal]);
const toggleTerms = useCallback(() => setTermsModal(!termsModal), [termsModal]);

const navigateAbout = () => navigate("/about");
const navigateDatasets = () => navigate("/datasets");
const navigateOverview = () => navigate("/overview"); // TODO: remember chrom and assay
const navigateExplore = () => {
const navigateAbout = useCallback(() => navigate("/about"), [navigate]);
const navigateDatasets = useCallback(() => navigate("/datasets"), [navigate]);
// TODO: remember chrom and assay:
const navigateOverview = useCallback(() => navigate("/overview"), [navigate]);
const navigateExplore = useCallback(() => {
if (location.pathname.startsWith("/explore")) return;
if (chrom && position) {
navigate(`/explore/locus/${chrom}/${position}`);
} else {
navigate("/explore");
}
}
}, [location.pathname, chrom, position, navigate]);
const navigateFAQ = () => navigate("/faq");

useEffect(() => {
Expand All @@ -61,17 +62,19 @@ const RoutedApp = () => {
}
}, [userData]);

const termsOnAgree = useCallback(institution => {
if (userData.isLoaded) {
dispatch(saveUser({consentedToTerms: true, institution}));
}
}, [userData, dispatch]);

return (
<div className="RoutedApp">
<TermsModal
isOpen={termsModal}
toggle={toggleTerms}
showAgree={userData.data && !userData.data.consentedToTerms}
onAgree={institution => {
if (userData.isLoaded) {
dispatch(saveUser({consentedToTerms: true, institution}));
}
}}
onAgree={termsOnAgree}
/>

<ContactModal isOpen={contactModal} toggle={toggleContact} />
Expand All @@ -93,48 +96,30 @@ const RoutedApp = () => {
};


class App extends Component {
render() {
return (
<div className='App'>
<svg height={0} style={{position: "absolute"}}>
<pattern id="diagonal" patternUnits="userSpaceOnUse" width={9} height={9} patternTransform="rotate(45 0 0)">
<rect x={0} y={0} width={9} height={9} fill="#FFFFFF" />
<line x1={3} y1={0} x2={3} y2={9} style={{
stroke: ETHNICITY_BOX_COLOR.AF,
strokeWidth: 6,
}} />
<line x1={7.5} y1={0} x2={7.5} y2={9} style={{
stroke: ETHNICITY_BOX_COLOR.EU,
strokeWidth: 3,
}} />
</pattern>
</svg>

<Routes>
<Route path="/" element={<RoutedApp />}>
<Route index={true} element={<Navigate to="/about" replace={true} />} />
<Route path="about" element={<AboutPage />} />
<Route path="datasets" element={<DatasetsPage />} />
<Route path="overview" element={<ProtectedPageContainer>
<OverviewPage />
</ProtectedPageContainer>} />
<Route path="explore" element={<ProtectedPageContainer>
<ExplorePage />
</ProtectedPageContainer>}>
<Route index={true} element={<PeakResults />} />
<Route path="locus/:chrom/:position/:assay" element={<PeakResults />} />
<Route path="locus/:chrom/:position" element={<PeakResults />} />
</Route>
<Route path="faq" element={<FAQPage />} />
<Route path="auth-failure" element={<div />} />
</Route>
<Route path="*" element={<Navigate to="/" />}/>
</Routes>
</div>
)
}
}
const App = () => (
<div className='App'>
<Routes>
<Route path="/" element={<RoutedApp />}>
<Route index={true} element={<Navigate to="/about" replace={true} />} />
<Route path="about" element={<AboutPage />} />
<Route path="datasets" element={<DatasetsPage />} />
<Route path="overview" element={<ProtectedPageContainer>
<OverviewPage />
</ProtectedPageContainer>} />
<Route path="explore" element={<ProtectedPageContainer>
<ExplorePage />
</ProtectedPageContainer>}>
<Route index={true} element={<PeakResults />} />
<Route path="locus/:chrom/:position/:assay" element={<PeakResults />} />
<Route path="locus/:chrom/:position" element={<PeakResults />} />
</Route>
<Route path="faq" element={<FAQPage />} />
<Route path="auth-failure" element={<div />} />
</Route>
<Route path="*" element={<Navigate to="/" />}/>
</Routes>
</div>
);


export default App;
Loading