diff --git a/.github/workflows/deploy-to-staging.yml b/.github/workflows/deploy-to-staging.yml
new file mode 100644
index 00000000..1071622f
--- /dev/null
+++ b/.github/workflows/deploy-to-staging.yml
@@ -0,0 +1,99 @@
+# .github/workflows/deploy-to-staging.yml
+name: Deploy to Staging
+
+on:
+ push:
+ branches:
+ - staging
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout the repository
+ uses: actions/checkout@v3
+
+ - name: Set up SSH agent
+ uses: webfactory/ssh-agent@v0.5.4
+ with:
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
+
+ - name: Add remote SSH server to known hosts
+ run: |
+ ssh-keyscan -H deepdream.psy.utexas.edu >> ~/.ssh/known_hosts
+
+ - name: Force sync staging branch to remote server
+ run: |
+ ssh git@deepdream.psy.utexas.edu '
+ cd /var/www/neurostore &&
+ git fetch origin && # Fetch full history
+ git fetch --all && # Fetch all branches and their histories
+ git reset --hard origin/staging &&
+ git clean -fd
+ '
+
+ - name: Detect changes in specific directories from the previous commit
+ id: changes
+ run: |
+ # Capture the output of the SSH session and process it locally
+ ssh_output=$(ssh git@deepdream.psy.utexas.edu '
+ cd /var/www/neurostore &&
+ git fetch origin &&
+ git log -n 2 &&
+ changes_in_store=$(git diff --name-only HEAD~1 -- | grep "^store/" || echo "") &&
+ changes_in_compose=$(git diff --name-only HEAD~1 -- | grep "^compose/" || echo "") &&
+ changes_in_frontend=$(git diff --name-only HEAD~1 -- | grep "^compose/neurosynth-frontend/" || echo "") &&
+
+ # Print results for local parsing
+ echo "store:$changes_in_store" &&
+ echo "compose:$changes_in_compose" &&
+ echo "frontend:$changes_in_frontend"
+ ')
+
+ # Parse the results locally and set outputs
+ echo "$ssh_output" | while IFS=':' read -r key value; do
+ if [ -n "$value" ]; then
+ echo "$key=$value" >> $GITHUB_OUTPUT
+ fi
+ done
+
+ - name: Run Docker commands in store directory if changes detected
+ if: ${{ steps.changes.outputs.store }}
+ run: |
+ ssh git@deepdream.psy.utexas.edu '
+ cd /var/www/neurostore/store &&
+ docker compose down &&
+ docker compose build &&
+ docker compose up -d
+
+ # Wait for PostgreSQL to be ready
+ until docker compose exec store_pgsql pg_isready; do sleep 5; done
+
+ docker compose exec store flask db migrate
+ docker compose exec store flask db upgrade
+ '
+
+ - name: Run Docker commands in compose directory if changes detected
+ if: ${{ steps.changes.outputs.compose }}
+ run: |
+ ssh git@deepdream.psy.utexas.edu '
+ cd /var/www/neurostore/compose &&
+ docker compose down &&
+ docker compose build &&
+ docker compose up -d
+
+ # Wait for PostgreSQL to be ready
+ until docker compose exec compose_pgsql pg_isready; do sleep 5; done
+
+ docker compose exec compose flask db migrate
+ docker compose exec compose flask db upgrade
+ '
+
+ - name: Skip Docker commands and run frontend build if only frontend changes detected
+ if: ${{ steps.changes.outputs.frontend }}
+ run: |
+ ssh git@deepdream.psy.utexas.edu '
+ cd /var/www/neurostore/compose &&
+ docker compose exec -T compose bash -c "cd /compose/neurosynth-frontend/ && npm install && npm run build:staging"
+ '
diff --git a/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx b/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx
index 9a75dd51..24b8054f 100644
--- a/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx
+++ b/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx
@@ -95,26 +95,26 @@ describe('ExtractionTable', () => {
cy.get('tbody > tr').should('have.length', 1);
});
- it('should filter the table by doi', () => {
- cy.wait('@studysetFixture').then((studysetFixture) => {
- const studyset = studysetFixture?.response?.body as StudysetReturn;
- const studysetStudies = studyset.studies as StudyReturn[];
- cy.get('input').eq(4).click();
- cy.get(`input`)
- .eq(4)
- .type(studysetStudies[0].doi || '');
- });
-
- cy.get('tbody > tr').should('have.length', 1);
- });
+ // it('should filter the table by doi', () => {
+ // cy.wait('@studysetFixture').then((studysetFixture) => {
+ // const studyset = studysetFixture?.response?.body as StudysetReturn;
+ // const studysetStudies = studyset.studies as StudyReturn[];
+ // cy.get('input').eq(4).click();
+ // cy.get(`input`)
+ // .eq(4)
+ // .type(studysetStudies[0].doi || '');
+ // });
+
+ // cy.get('tbody > tr').should('have.length', 1);
+ // });
it('should filter the table by pmid', () => {
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture?.response?.body as StudysetReturn;
const studysetStudies = studyset.studies as StudyReturn[];
- cy.get('input').eq(5).click();
+ cy.get('input').eq(4).click();
cy.get(`input`)
- .eq(5)
+ .eq(4)
.type(studysetStudies[0].pmid || '');
});
@@ -181,7 +181,7 @@ describe('ExtractionTable', () => {
it('should change the study status', () => {
// ARRANGE
- cy.get('tbody > tr').eq(0).get('td').eq(6).as('getFirstRowStudyStatusCol');
+ cy.get('tbody > tr').eq(0).get('td').eq(5).as('getFirstRowStudyStatusCol');
cy.get('@getFirstRowStudyStatusCol').within(() => {
cy.get('button').eq(0).should('have.class', 'MuiButton-contained');
});
@@ -205,8 +205,7 @@ describe('ExtractionTable', () => {
});
it('should sort by year desc', () => {
- cy.contains('Year').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(0).click();
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture.response?.body as StudysetReturn;
@@ -227,8 +226,8 @@ describe('ExtractionTable', () => {
});
it('should sort by year asc', () => {
- cy.contains('Year').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(0).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(0).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@studysetFixture').then((studysetFixture) => {
@@ -250,8 +249,7 @@ describe('ExtractionTable', () => {
});
it('should sort by name asc', () => {
- cy.contains('Name').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(1).click();
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture.response?.body as StudysetReturn;
@@ -272,8 +270,8 @@ describe('ExtractionTable', () => {
});
it('should sort by name desc', () => {
- cy.contains('Name').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(1).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(1).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@studysetFixture').then((studysetFixture) => {
@@ -293,8 +291,7 @@ describe('ExtractionTable', () => {
});
it('should sort by authors desc', () => {
- cy.contains('Authors').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(2).click();
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture.response?.body as StudysetReturn;
@@ -313,8 +310,8 @@ describe('ExtractionTable', () => {
});
it('should sort by authors asc', () => {
- cy.contains('Authors').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(2).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(2).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@studysetFixture').then((studysetFixture) => {
@@ -334,8 +331,7 @@ describe('ExtractionTable', () => {
});
it('should sort by journal desc', () => {
- cy.contains('Journal').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(3).click();
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture.response?.body as StudysetReturn;
@@ -354,8 +350,8 @@ describe('ExtractionTable', () => {
});
it('should sort by journal desc', () => {
- cy.contains('Journal').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(3).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(3).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@studysetFixture').then((studysetFixture) => {
@@ -374,51 +370,49 @@ describe('ExtractionTable', () => {
});
});
- it('should sort by doi desc', () => {
- cy.contains('DOI').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ // it('should sort by doi desc', () => {
+ // cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
- cy.wait('@studysetFixture').then((studysetFixture) => {
- const studyset = studysetFixture.response?.body as StudysetReturn;
- const studies = [...(studyset.studies || [])] as StudyReturn[];
+ // cy.wait('@studysetFixture').then((studysetFixture) => {
+ // const studyset = studysetFixture.response?.body as StudysetReturn;
+ // const studies = [...(studyset.studies || [])] as StudyReturn[];
- const sortedStudies = studies.sort((a, b) =>
- (b.doi as string).localeCompare(a.doi as string)
- );
+ // const sortedStudies = studies.sort((a, b) =>
+ // (b.doi as string).localeCompare(a.doi as string)
+ // );
- console.log(sortedStudies);
+ // console.log(sortedStudies);
- cy.get('tbody > tr').each((tr, index) => {
- cy.wrap(tr).within(() => {
- cy.get('td').eq(4).should('have.text', sortedStudies[index].doi);
- });
- });
- });
- });
- it('should sort by doi asc', () => {
- cy.contains('DOI').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
- cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
+ // cy.get('tbody > tr').each((tr, index) => {
+ // cy.wrap(tr).within(() => {
+ // cy.get('td').eq(4).should('have.text', sortedStudies[index].doi);
+ // });
+ // });
+ // });
+ // });
+ // it('should sort by doi asc', () => {
+ // cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
+ // cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
+ // cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
- cy.wait('@studysetFixture').then((studysetFixture) => {
- const studyset = studysetFixture.response?.body as StudysetReturn;
- const studies = [...(studyset.studies || [])] as StudyReturn[];
+ // cy.wait('@studysetFixture').then((studysetFixture) => {
+ // const studyset = studysetFixture.response?.body as StudysetReturn;
+ // const studies = [...(studyset.studies || [])] as StudyReturn[];
- const sortedStudies = studies.sort((a, b) =>
- (a.doi as string).localeCompare(b.doi as string)
- );
+ // const sortedStudies = studies.sort((a, b) =>
+ // (a.doi as string).localeCompare(b.doi as string)
+ // );
- cy.get('tbody > tr').each((tr, index) => {
- cy.wrap(tr).within(() => {
- cy.get('td').eq(4).should('have.text', sortedStudies[index].doi);
- });
- });
- });
- });
+ // cy.get('tbody > tr').each((tr, index) => {
+ // cy.wrap(tr).within(() => {
+ // cy.get('td').eq(4).should('have.text', sortedStudies[index].doi);
+ // });
+ // });
+ // });
+ // });
it('should sort by pmid desc', () => {
- cy.contains('PMID').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
cy.wait('@studysetFixture').then((studysetFixture) => {
const studyset = studysetFixture.response?.body as StudysetReturn;
@@ -430,12 +424,10 @@ describe('ExtractionTable', () => {
})
);
- console.log(sortedStudies);
-
cy.get('tbody > tr').each((tr, index) => {
cy.wrap(tr).within(() => {
cy.get('td')
- .eq(5)
+ .eq(4)
.should('have.text', sortedStudies[index].pmid ?? '');
});
});
@@ -443,8 +435,8 @@ describe('ExtractionTable', () => {
});
it('should sort by pmid asc', () => {
- cy.contains('PMID').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@studysetFixture').then((studysetFixture) => {
@@ -460,7 +452,7 @@ describe('ExtractionTable', () => {
cy.get('tbody > tr').each((tr, index) => {
cy.wrap(tr).within(() => {
cy.get('td')
- .eq(5)
+ .eq(4)
.should('have.text', sortedStudies[index].pmid ?? '');
});
});
@@ -468,8 +460,7 @@ describe('ExtractionTable', () => {
});
it('should sort by status desc', () => {
- cy.contains('Status').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').should('exist');
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click();
cy.wait('@projectFixture').then((projectFixture) => {
const project = projectFixture?.response?.body as INeurosynthProjectReturn;
@@ -494,7 +485,7 @@ describe('ExtractionTable', () => {
cy.get('tbody > tr').each((tr, index) => {
cy.wrap(tr).within(() => {
cy.get('td')
- .eq(6)
+ .eq(5)
.within(() => {
const studyStatus = sortedStudies[index].status;
const buttonIndex =
@@ -525,8 +516,8 @@ describe('ExtractionTable', () => {
});
it('should sort by status asc', () => {
- cy.contains('Status').click();
- cy.get('[data-testid="ArrowDownwardIcon"]').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click();
cy.get('[data-testid="ArrowUpwardIcon"]').should('exist');
cy.wait('@projectFixture').then((projectFixture) => {
@@ -552,7 +543,7 @@ describe('ExtractionTable', () => {
cy.get('tbody > tr').each((tr, index) => {
cy.wrap(tr).within(() => {
cy.get('td')
- .eq(6)
+ .eq(5)
.within(() => {
const studyStatus = sortedStudies[index].status;
const buttonIndex =
@@ -583,7 +574,7 @@ describe('ExtractionTable', () => {
});
});
- describe.only('pagination', () => {
+ describe('pagination', () => {
beforeEach(() => {
cy.fixture('studyset').then((studyset) => {
// as we are artificially creating new studies below, the out of sync popup wil appear. That's expected and
@@ -668,7 +659,7 @@ describe('ExtractionTable', () => {
});
it('should save the filter and sorting to the table state', () => {
- cy.contains('Year').click();
+ cy.get('[data-testid="ArrowDownwardIcon"]').eq(0).click(); // click on year sort
cy.get('input').eq(1).click();
cy.get(`input`).eq(1).type('Activation');
@@ -681,7 +672,7 @@ describe('ExtractionTable', () => {
cy.window().then((window) => {
const state = window.sessionStorage.getItem(`abc123-extraction-table`);
const parsedState = JSON.parse(state || '{}');
- console.log(parsedState);
+
cy.wrap(parsedState).should('deep.equal', {
columnFilters: [{ id: 'name', value: 'Activation' }],
sorting: [{ id: 'year', desc: true }],
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.module.css b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.module.css
index 7a6e3849..2490da7c 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.module.css
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.module.css
@@ -1,12 +1,11 @@
.completed {
- background-color: #e5ffe5;
+ background-color: #f8fff8;
}
.savedforlater {
- background-color: #effbff;
+ background-color: #f1fbff;
}
.uncategorized {
- /* background-color: #ffffeb; */
background-color: white;
}
\ No newline at end of file
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx
index f61f96cd..1024be82 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx
@@ -37,7 +37,6 @@ import { useNavigate } from 'react-router-dom';
import { EExtractionStatus } from '../ExtractionPage';
import styles from './ExtractionTable.module.css';
import { ExtractionTableAuthorCell, ExtractionTableAuthorHeader } from './ExtractionTableAuthor';
-import { ExtractionTableDOICell, ExtractionTableDOIHeader } from './ExtractionTableDOI';
import ExtractionTableFilterInput from './ExtractionTableFilterInput';
import { ExtractionTableJournalCell, ExtractionTableJournalHeader } from './ExtractionTableJournal';
import { ExtractionTableNameCell, ExtractionTableNameHeader } from './ExtractionTableName';
@@ -106,9 +105,9 @@ const ExtractionTable: React.FC = () => {
return [
columnHelper.accessor(({ year }) => (year ? String(year) : ''), {
id: 'year',
- size: 5,
- minSize: 5,
- maxSize: 5,
+ size: 70,
+ minSize: 70,
+ maxSize: 70,
cell: ExtractionTableYearCell,
header: ExtractionTableYearHeader,
enableSorting: true,
@@ -121,9 +120,9 @@ const ExtractionTable: React.FC = () => {
columnHelper.accessor('name', {
id: 'name',
cell: ExtractionTableNameCell,
- size: 25,
- minSize: 25,
- maxSize: 25,
+ size: 500,
+ minSize: 500,
+ maxSize: 500,
header: ExtractionTableNameHeader,
enableSorting: true,
sortingFn: 'text',
@@ -134,9 +133,9 @@ const ExtractionTable: React.FC = () => {
}),
columnHelper.accessor('authors', {
id: 'authors',
- size: 20,
- minSize: 20,
- maxSize: 20,
+ size: 300,
+ minSize: 300,
+ maxSize: 300,
enableSorting: true,
enableColumnFilter: true,
sortingFn: 'text',
@@ -149,9 +148,9 @@ const ExtractionTable: React.FC = () => {
}),
columnHelper.accessor('publication', {
id: 'journal',
- size: 15,
- minSize: 15,
- maxSize: 15,
+ size: 100,
+ minSize: 100,
+ maxSize: 100,
enableSorting: true,
enableColumnFilter: true,
cell: ExtractionTableJournalCell,
@@ -160,26 +159,26 @@ const ExtractionTable: React.FC = () => {
filterVariant: 'journal-autocomplete',
},
}),
- columnHelper.accessor('doi', {
- id: 'doi',
- size: 15,
- minSize: 15,
- maxSize: 15,
- sortingFn: 'alphanumeric',
- enableSorting: true,
- enableColumnFilter: true,
- filterFn: 'includesString',
- cell: ExtractionTableDOICell,
- header: ExtractionTableDOIHeader,
- meta: {
- filterVariant: 'text',
- },
- }),
+ // columnHelper.accessor('doi', {
+ // id: 'doi',
+ // size: 10,
+ // minSize: 10,
+ // maxSize: 10,
+ // sortingFn: 'alphanumeric',
+ // enableSorting: true,
+ // enableColumnFilter: true,
+ // filterFn: 'includesString',
+ // cell: ExtractionTableDOICell,
+ // header: ExtractionTableDOIHeader,
+ // meta: {
+ // filterVariant: 'text',
+ // },
+ // }),
columnHelper.accessor('pmid', {
id: 'pmid',
- size: 10,
- minSize: 10,
- maxSize: 10,
+ size: 100,
+ minSize: 100,
+ maxSize: 100,
enableColumnFilter: true,
filterFn: 'includesString',
cell: ExtractionTablePMIDCell,
@@ -192,9 +191,9 @@ const ExtractionTable: React.FC = () => {
}),
columnHelper.accessor('status', {
id: 'status',
- size: 10,
- minSize: 10,
- maxSize: 10,
+ size: 120,
+ minSize: 120,
+ maxSize: 120,
enableSorting: true,
cell: ExtractionTableStatusCell,
filterFn: (row, columnId, filterValue: EExtractionStatus | null) => {
@@ -227,14 +226,12 @@ const ExtractionTable: React.FC = () => {
getPaginationRowModel: getPaginationRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnFiltersChange: setColumnFilters,
+ autoResetPageIndex: false,
state: {
pagination: pagination,
columnFilters: columnFilters,
sorting: sorting,
},
- meta: {
- studyStatusMap,
- },
});
const handleRowsPerPageChange = useCallback(
@@ -272,12 +269,11 @@ const ExtractionTable: React.FC = () => {
onChange={handlePaginationChangeMuiPaginator}
page={pagination.pageIndex + 1}
/>
- Total: {data.length} studies
{table.getHeaderGroups().map((headerGroup) => (
@@ -285,7 +281,13 @@ const ExtractionTable: React.FC = () => {
{headerGroup.headers.map((header) => (
{flexRender(
@@ -294,6 +296,7 @@ const ExtractionTable: React.FC = () => {
)}
{header.column.getCanFilter() ? (
) : (
@@ -417,7 +420,14 @@ const ExtractionTable: React.FC = () => {
- Viewing {table.getFilteredRowModel().rows.length} / {data.length}
+ {columnFilters.length > 0 ? (
+
+ Viewing {table.getFilteredRowModel().rows.length} /{' '}
+ {data.length}
+
+ ) : (
+ Total: {data.length} studies
+ )}
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx
index 117ab4ed..0636a61c 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx
@@ -1,8 +1,8 @@
-import { Box, IconButton, Link, Tooltip, Typography } from '@mui/material';
-import { CellContext, HeaderContext } from '@tanstack/react-table';
-import { IExtractionTableStudy } from './ExtractionTable';
import { ArrowDownward } from '@mui/icons-material';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
+import { CellContext, HeaderContext } from '@tanstack/react-table';
+import { IExtractionTableStudy } from './ExtractionTable';
export const ExtractionTableAuthorCell: React.FC> = (
props
@@ -18,41 +18,41 @@ export const ExtractionTableAuthorHeader: React.FC<
return (
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'authors', desc: true }]);
- }
- }}
+
+ Authors
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'authors', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'authors', desc: false }])}
>
- Authors
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'authors', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'authors', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx
index f4a21dd4..5e4844cf 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx
@@ -1,8 +1,8 @@
-import { Box, IconButton, Link, Tooltip, Typography } from '@mui/material';
-import { CellContext, HeaderContext } from '@tanstack/react-table';
-import { IExtractionTableStudy } from './ExtractionTable';
import { ArrowDownward } from '@mui/icons-material';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
+import { CellContext, HeaderContext } from '@tanstack/react-table';
+import { IExtractionTableStudy } from './ExtractionTable';
export const ExtractionTableDOICell: React.FC> = (
props
@@ -22,41 +22,41 @@ export const ExtractionTableDOIHeader: React.FC
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'doi', desc: true }]);
- }
- }}
+
+ DOI
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'doi', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'doi', desc: false }])}
>
- DOI
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'doi', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'doi', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx
index c9ac8c29..86a4ec00 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx
@@ -1,5 +1,5 @@
import { Box } from '@mui/material';
-import { Column } from '@tanstack/react-table';
+import { Column, Table } from '@tanstack/react-table';
import DebouncedTextField from 'components/DebouncedTextField';
import { useCallback } from 'react';
import { EExtractionStatus } from '../ExtractionPage';
@@ -7,17 +7,19 @@ import { IExtractionTableStudy } from './ExtractionTable';
import ExtractionTableJournalAutocomplete from './ExtractionTableJournalAutocomplete';
import ExtractionTableStatusFilter from './ExtractionTableStatusFilter';
-const ExtractionTableFilterInput: React.FC<{ column: Column }> = ({
- column,
-}) => {
+const ExtractionTableFilterInput: React.FC<{
+ table: Table;
+ column: Column;
+}> = ({ table, column }) => {
const columnFilterValue = column.getFilterValue();
const { filterVariant } = column.columnDef.meta ?? {};
const handleChangeAutocomplete = useCallback(
(event: string | null | undefined) => {
+ table.resetPageIndex();
column.setFilterValue(event ?? null);
},
- [column]
+ [column, table]
);
if (filterVariant === 'status-select') {
@@ -39,7 +41,7 @@ const ExtractionTableFilterInput: React.FC<{ column: Column
> = (
props
@@ -17,41 +17,41 @@ export const ExtractionTableJournalHeader: React.FC<
const isSorted = column.getIsSorted();
return (
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'journal', desc: true }]);
- }
- }}
+
+ Journal
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'journal', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'journal', desc: false }])}
>
- Journal
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'journal', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'journal', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournalAutocomplete.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournalAutocomplete.tsx
index 07225d68..e4fa3cbe 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournalAutocomplete.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournalAutocomplete.tsx
@@ -31,7 +31,7 @@ const ExtractionTableJournalAutocomplete: React.FC<{
}
+ renderInput={(params) => }
onChange={handleChange}
value={value || null}
options={options}
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx
index 66f9820e..f25011e3 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx
@@ -1,8 +1,8 @@
-import { Box, IconButton, Link, Tooltip, Typography } from '@mui/material';
-import { CellContext, HeaderContext } from '@tanstack/react-table';
-import { IExtractionTableStudy } from './ExtractionTable';
import { ArrowDownward } from '@mui/icons-material';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
+import { CellContext, HeaderContext } from '@tanstack/react-table';
+import { IExtractionTableStudy } from './ExtractionTable';
export const ExtractionTableNameCell: React.FC> = (
props
@@ -22,41 +22,41 @@ export const ExtractionTableNameHeader: React.FC
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'name', desc: true }]);
- }
- }}
+
+ Name
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'name', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'name', desc: false }])}
>
- Name
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'name', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'name', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx
index 8cdcb882..6ded5224 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx
@@ -1,8 +1,8 @@
-import { Box, IconButton, Link, Tooltip, Typography } from '@mui/material';
-import { CellContext, HeaderContext } from '@tanstack/react-table';
-import { IExtractionTableStudy } from './ExtractionTable';
import { ArrowDownward } from '@mui/icons-material';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
+import { CellContext, HeaderContext } from '@tanstack/react-table';
+import { IExtractionTableStudy } from './ExtractionTable';
export const ExtractionTablePMIDCell: React.FC> = (
props
@@ -18,41 +18,41 @@ export const ExtractionTablePMIDHeader: React.FC
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'pmid', desc: true }]);
- }
- }}
+
+ PMID
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'pmid', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'pmid', desc: false }])}
>
- PMID
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'pmid', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'pmid', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx
index c8696575..7a23808a 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx
@@ -1,11 +1,11 @@
import { ArrowDownward, CheckCircle, QuestionMark } from '@mui/icons-material';
+import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
import BookmarkIcon from '@mui/icons-material/Bookmark';
-import { Box, Button, ButtonGroup, IconButton, Link, Tooltip, Typography } from '@mui/material';
+import { Box, Button, ButtonGroup, IconButton, Tooltip, Typography } from '@mui/material';
import { CellContext, HeaderContext } from '@tanstack/react-table';
import { useProjectExtractionAddOrUpdateStudyListStatus } from 'pages/Project/store/ProjectStore';
import { EExtractionStatus } from '../ExtractionPage';
import { IExtractionTableStudy } from './ExtractionTable';
-import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
export const ExtractionTableStatusCell: React.FC<
CellContext
@@ -28,6 +28,7 @@ export const ExtractionTableStatusCell: React.FC<
disableElevation
sx={{ paddingX: '0' }}
color="warning"
+ size="small"
variant={
status === undefined || status === EExtractionStatus.UNCATEGORIZED
? 'contained'
@@ -47,6 +48,7 @@ export const ExtractionTableStatusCell: React.FC<
disableElevation
sx={{ paddingX: '0' }}
color="info"
+ size="small"
variant={status === EExtractionStatus.SAVEDFORLATER ? 'contained' : 'outlined'}
>
@@ -62,6 +64,7 @@ export const ExtractionTableStatusCell: React.FC<
disableElevation
sx={{ paddingX: '0' }}
color="success"
+ size="small"
variant={status === EExtractionStatus.COMPLETED ? 'contained' : 'outlined'}
>
@@ -77,41 +80,41 @@ export const ExtractionTableStatusHeader: React.FC<
const isSorted = column.getIsSorted();
return (
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'status', desc: true }]);
- }
- }}
+
+ Status
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'status', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'status', desc: false }])}
>
- Status
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'status', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'status', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx
index 5d48a275..ce9d7d4f 100644
--- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx
+++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx
@@ -1,6 +1,6 @@
import { ArrowDownward } from '@mui/icons-material';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
-import { Box, IconButton, Link, Tooltip, Typography } from '@mui/material';
+import { Box, IconButton, Tooltip, Typography } from '@mui/material';
import { CellContext, HeaderContext } from '@tanstack/react-table';
import { IExtractionTableStudy } from './ExtractionTable';
@@ -19,41 +19,41 @@ export const ExtractionTableYearHeader: React.FC
-
- {
- if (!!isSorted) {
- table.resetSorting();
- } else {
- table.setSorting([{ id: 'year', desc: true }]);
- }
- }}
+
+ Year
+
+ {!isSorted ? (
+
+ {
+ if (!!isSorted) {
+ table.resetSorting();
+ } else {
+ table.setSorting([{ id: 'year', desc: true }]);
+ }
+ }}
+ >
+
+
+
+ ) : isSorted === 'asc' ? (
+ table.resetSorting()}>
+
+
+ ) : (
+ table.setSorting([{ id: 'year', desc: false }])}
>
- Year
-
-
- {!!isSorted && (
- <>
- {isSorted === 'asc' ? (
- table.setSorting([{ id: 'year', desc: true }])}
- >
-
-
- ) : (
- table.setSorting([{ id: 'year', desc: false }])}
- >
-
-
- )}
- >
+
+
)}
);
diff --git a/compose/neurosynth-frontend/src/pages/Study/EditStudyPage.tsx b/compose/neurosynth-frontend/src/pages/Study/EditStudyPage.tsx
index 88d8ef44..12712db2 100644
--- a/compose/neurosynth-frontend/src/pages/Study/EditStudyPage.tsx
+++ b/compose/neurosynth-frontend/src/pages/Study/EditStudyPage.tsx
@@ -22,6 +22,7 @@ import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useClearAnnotationStore, useInitAnnotationStore } from 'stores/AnnotationStore.actions';
import { useAnnotationId, useGetAnnotationIsLoading } from 'stores/AnnotationStore.getters';
+import DisplayExtractionTableState from './components/DisplayExtractionTableState';
const EditStudyPage: React.FC = (props) => {
const { studyId } = useParams<{ studyId: string }>();
@@ -72,6 +73,7 @@ const EditStudyPage: React.FC = (props) => {
+
diff --git a/compose/neurosynth-frontend/src/pages/Study/components/DisplayExtractionTableState.tsx b/compose/neurosynth-frontend/src/pages/Study/components/DisplayExtractionTableState.tsx
new file mode 100644
index 00000000..fb4f1cc7
--- /dev/null
+++ b/compose/neurosynth-frontend/src/pages/Study/components/DisplayExtractionTableState.tsx
@@ -0,0 +1,72 @@
+import { Box, Chip, Typography } from '@mui/material';
+import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
+import { useGetStudysetById } from 'hooks';
+import { useProjectExtractionStudysetId, useProjectId } from 'pages/Project/store/ProjectStore';
+import { useMemo } from 'react';
+
+const DisplayExtractionTableState: React.FC = (props) => {
+ const projectId = useProjectId();
+ const studysetId = useProjectExtractionStudysetId();
+ const { data } = useGetStudysetById(studysetId);
+ const { columnFilters, sorting, studies } = useMemo(() => {
+ try {
+ const state = window.sessionStorage.getItem(`${projectId}-extraction-table`);
+ const parsedState = JSON.parse(state || '{}') as {
+ columnFilters: ColumnFiltersState;
+ sorting: SortingState;
+ studies: string[];
+ };
+ if (!state) {
+ return {
+ columnFilters: [],
+ sorting: [],
+ studies: [],
+ };
+ }
+
+ return {
+ columnFilters: parsedState.columnFilters,
+ sorting: parsedState.sorting,
+ studies: parsedState.studies,
+ };
+ } catch (e) {
+ return {
+ columnFilters: [],
+ sorting: [],
+ studies: [],
+ };
+ }
+ }, [projectId]);
+
+ return (
+
+ {columnFilters
+ .filter((filter) => !!filter.value)
+ .map((filter) => (
+
+ ))}
+ {sorting.map((sort) => (
+
+ ))}
+
+ ({studies.length} / {data?.studies?.length || 0} studies)
+
+
+ );
+};
+
+export default DisplayExtractionTableState;
diff --git a/compose/neurosynth-frontend/src/pages/Study/components/EditStudyToolbar.tsx b/compose/neurosynth-frontend/src/pages/Study/components/EditStudyToolbar.tsx
index 9ea015a5..773ef1c9 100644
--- a/compose/neurosynth-frontend/src/pages/Study/components/EditStudyToolbar.tsx
+++ b/compose/neurosynth-frontend/src/pages/Study/components/EditStudyToolbar.tsx
@@ -41,7 +41,7 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
const { data, isLoading, isError } = useGetStudysetById(studysetId || '', true);
// derived from the extraction table
- const [studiesState, setStudiesState] = useState<{
+ const [extractionTableState, setExtractionTableState] = useState<{
columnFilters: ColumnFiltersState;
sorting: SortingState;
studies: string[];
@@ -54,7 +54,7 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
useEffect(() => {
const stateFromSessionStorage = sessionStorage.getItem(`${projectId}-extraction-table`);
if (!stateFromSessionStorage) {
- setStudiesState((prev) => ({
+ setExtractionTableState((prev) => ({
...prev,
studies: (data?.studies || []).map((study) => (study as StudyReturn).id as string),
}));
@@ -65,7 +65,7 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
sorting: SortingState;
studies: string[];
};
- setStudiesState(parsedState);
+ setExtractionTableState(parsedState);
} catch (e) {
throw new Error('couldnt parse table state from session storage');
}
@@ -81,8 +81,8 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
};
const handleMoveToPreviousStudy = () => {
- const index = studiesState.studies.indexOf(studyId || '');
- const prevId = studiesState.studies[index - 1];
+ const index = extractionTableState.studies.indexOf(studyId || '');
+ const prevId = extractionTableState.studies[index - 1];
if (!prevId) throw new Error('no previous study');
canEdit
? navigate(`/projects/${projectId}/extraction/studies/${prevId}/edit`)
@@ -90,8 +90,8 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
};
const handleMoveToNextStudy = () => {
- const index = studiesState.studies.indexOf(studyId || '');
- const nextId = studiesState.studies[index + 1];
+ const index = extractionTableState.studies.indexOf(studyId || '');
+ const nextId = extractionTableState.studies[index + 1];
if (!nextId) throw new Error('no next study');
canEdit
? navigate(`/projects/${projectId}/extraction/studies/${nextId}/edit`)
@@ -130,16 +130,16 @@ const EditStudyToolbar: React.FC<{ isViewOnly?: boolean }> = ({ isViewOnly = fal
}, [extractionSummary.completed, extractionSummary.total]);
const hasPrevStudies = useMemo(() => {
- const studies = studiesState.studies;
+ const studies = extractionTableState.studies;
const index = studies.indexOf(studyId || '');
return index - 1 >= 0;
- }, [studiesState.studies, studyId]);
+ }, [extractionTableState.studies, studyId]);
const hasNextStudies = useMemo(() => {
- const studies = studiesState.studies;
+ const studies = extractionTableState.studies;
const index = studies.indexOf(studyId || '');
return index + 1 < studies.length;
- }, [studiesState.studies, studyId]);
+ }, [extractionTableState.studies, studyId]);
return (
diff --git a/compose/neurosynth_compose/openapi b/compose/neurosynth_compose/openapi
index ca2494f7..3e4cba60 160000
--- a/compose/neurosynth_compose/openapi
+++ b/compose/neurosynth_compose/openapi
@@ -1 +1 @@
-Subproject commit ca2494f73d0c88c013c7aab0b0de8f8a89b4bc11
+Subproject commit 3e4cba60f52a6c6bdd1ac5b55cb70d0ae3399aab