From 9883e7b7b2b3db6845b8b959ea29608446c4e53d Mon Sep 17 00:00:00 2001 From: James Kent Date: Thu, 19 Sep 2024 12:25:00 -0500 Subject: [PATCH 1/4] add deployment script (#822) --- .github/workflows/deploy-to-staging.yml | 77 +++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/deploy-to-staging.yml diff --git a/.github/workflows/deploy-to-staging.yml b/.github/workflows/deploy-to-staging.yml new file mode 100644 index 00000000..9aa7f391 --- /dev/null +++ b/.github/workflows/deploy-to-staging.yml @@ -0,0 +1,77 @@ +# .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: Force sync staging branch to remote server + run: | + ssh jdkent@deepdream.psy.utexas.edu 'cd /var/www/neurostore && git fetch origin && git reset --hard origin/staging && git clean -fd' + + - name: Detect changes in specific directories + id: changes + run: | + git fetch origin staging + changes_in_store=$(git diff --name-only origin/staging HEAD | grep '^store/' || echo "") + changes_in_compose=$(git diff --name-only origin/staging HEAD | grep '^compose/' || echo "") + changes_in_frontend=$(git diff --name-only origin/staging HEAD | grep '^compose/neurosynth-frontend/' || echo "") + + echo "::set-output name=store::$changes_in_store" + echo "::set-output name=compose::$changes_in_compose" + echo "::set-output name=frontend::$changes_in_frontend" + + - name: Run Docker commands in store directory if changes detected + if: ${{ steps.changes.outputs.store }} + run: | + ssh jdkent@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 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 && !steps.changes.outputs.frontend }} + run: | + ssh jdkent@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 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 jdkent@deepdream.psy.utexas.edu ' + cd /var/www/neurostore/compose/neurosynth-frontend && + npm install && + npm run build:staging + ' From ac12969a44cc7a0b62d6fb8e64f2a9b37b1b3066 Mon Sep 17 00:00:00 2001 From: James Kent Date: Thu, 19 Sep 2024 14:33:13 -0500 Subject: [PATCH 2/4] [FIX] deploy script (#823) * modify staging * update workflow script * check previous commit * check for changes differently * remove unshallow * handle passing variable outside of ssh * remove dashes * use correct container for checking database * call container --- .github/workflows/deploy-to-staging.yml | 66 ++++++++++++++++--------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/.github/workflows/deploy-to-staging.yml b/.github/workflows/deploy-to-staging.yml index 9aa7f391..1071622f 100644 --- a/.github/workflows/deploy-to-staging.yml +++ b/.github/workflows/deploy-to-staging.yml @@ -19,59 +19,81 @@ jobs: 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 jdkent@deepdream.psy.utexas.edu 'cd /var/www/neurostore && git fetch origin && git reset --hard origin/staging && git clean -fd' + 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 + - name: Detect changes in specific directories from the previous commit id: changes run: | - git fetch origin staging - changes_in_store=$(git diff --name-only origin/staging HEAD | grep '^store/' || echo "") - changes_in_compose=$(git diff --name-only origin/staging HEAD | grep '^compose/' || echo "") - changes_in_frontend=$(git diff --name-only origin/staging HEAD | grep '^compose/neurosynth-frontend/' || echo "") + # 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" + ') - echo "::set-output name=store::$changes_in_store" - echo "::set-output name=compose::$changes_in_compose" - echo "::set-output name=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 jdkent@deepdream.psy.utexas.edu ' + 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 pg_isready; do sleep 5; done + 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 + 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 && !steps.changes.outputs.frontend }} + if: ${{ steps.changes.outputs.compose }} run: | - ssh jdkent@deepdream.psy.utexas.edu ' + 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 pg_isready; do sleep 5; done + 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 + 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 jdkent@deepdream.psy.utexas.edu ' - cd /var/www/neurostore/compose/neurosynth-frontend && - npm install && - npm run build:staging + 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" ' From 83b42d91eb33f260b93e8ef4fa64bf46d0c0b243 Mon Sep 17 00:00:00 2001 From: Nicholas Lee Date: Mon, 23 Sep 2024 10:39:29 -0400 Subject: [PATCH 3/4] 518 extraction phase can we make the list of studies more spreadsheet like (#824) * feat: added wireframes for extraction * feat: added table * feat: implemented table and table ordering during study edit * feat: preserve table state in session storage and fix unit tests * feat: finished integration tests * fix: table pagination resets on data update * feat: updated table with feedback * fix: integration test --- .../Extraction/ExtractionTable.cy.tsx | 55 +++++++------- .../components/ExtractionTable.module.css | 5 +- .../Extraction/components/ExtractionTable.tsx | 5 +- .../components/ExtractionTableAuthor.tsx | 68 ++++++++---------- .../components/ExtractionTableDOI.tsx | 68 ++++++++---------- .../components/ExtractionTableFilterInput.tsx | 12 ++-- .../components/ExtractionTableJournal.tsx | 68 ++++++++---------- .../components/ExtractionTableName.tsx | 68 ++++++++---------- .../components/ExtractionTablePMID.tsx | 68 ++++++++---------- .../components/ExtractionTableStatus.tsx | 66 ++++++++--------- .../components/ExtractionTableYear.tsx | 64 ++++++++--------- .../src/pages/Study/EditStudyPage.tsx | 2 + .../DisplayExtractionTableState.tsx | 72 +++++++++++++++++++ .../Study/components/EditStudyToolbar.tsx | 22 +++--- 14 files changed, 334 insertions(+), 309 deletions(-) create mode 100644 compose/neurosynth-frontend/src/pages/Study/components/DisplayExtractionTableState.tsx 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..d2b039dd 100644 --- a/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx +++ b/compose/neurosynth-frontend/cypress/e2e/workflows/Extraction/ExtractionTable.cy.tsx @@ -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) => { @@ -375,8 +371,7 @@ describe('ExtractionTable', () => { }); it('should sort by doi desc', () => { - cy.contains('DOI').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; @@ -396,8 +391,8 @@ describe('ExtractionTable', () => { }); }); it('should sort by doi asc', () => { - cy.contains('DOI').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) => { @@ -417,8 +412,7 @@ describe('ExtractionTable', () => { }); it('should sort by pmid desc', () => { - cy.contains('PMID').click(); - cy.get('[data-testid="ArrowDownwardIcon"]').should('exist'); + cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click(); cy.wait('@studysetFixture').then((studysetFixture) => { const studyset = studysetFixture.response?.body as StudysetReturn; @@ -443,8 +437,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(5).click(); + cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click(); cy.get('[data-testid="ArrowUpwardIcon"]').should('exist'); cy.wait('@studysetFixture').then((studysetFixture) => { @@ -468,8 +462,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(6).click(); cy.wait('@projectFixture').then((projectFixture) => { const project = projectFixture?.response?.body as INeurosynthProjectReturn; @@ -525,8 +518,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(6).click(); + cy.get('[data-testid="ArrowDownwardIcon"]').eq(6).click(); cy.get('[data-testid="ArrowUpwardIcon"]').should('exist'); cy.wait('@projectFixture').then((projectFixture) => { @@ -583,7 +576,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 +661,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 +674,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..f8937694 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx @@ -227,14 +227,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( @@ -294,6 +292,7 @@ const ExtractionTable: React.FC = () => { )} {header.column.getCanFilter() ? ( ) : ( diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx index 117ab4ed..8cfac918 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,35 @@ 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..bf36cf6e 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,35 @@ 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..728bdcbe 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') { diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournal.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournal.tsx index d76bc065..3a33d951 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournal.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableJournal.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 ExtractionTableJournalCell: React.FC> = ( props @@ -17,41 +17,35 @@ 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/ExtractionTableName.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx index 66f9820e..21198759 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,35 @@ 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..aefc1208 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,35 @@ 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..0be5f302 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 @@ -77,41 +77,35 @@ 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..84cd28a4 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,35 @@ 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 ( From 3ded6310f5a4e78a295629f9030ea698a3adcac5 Mon Sep 17 00:00:00 2001 From: Nicholas Lee Date: Mon, 23 Sep 2024 17:23:08 -0400 Subject: [PATCH 4/4] 518 extraction phase can we make the list of studies more spreadsheet like (#825) * feat: added wireframes for extraction * feat: added table * feat: implemented table and table ordering during study edit * feat: preserve table state in session storage and fix unit tests * feat: finished integration tests * fix: table pagination resets on data update * feat: updated table with feedback * fix: integration test * fix: address feedback * fix: extraction tests * fix: tests --- .../Extraction/ExtractionTable.cy.tsx | 118 +++++++++--------- .../Extraction/components/ExtractionTable.tsx | 87 +++++++------ .../components/ExtractionTableAuthor.tsx | 12 +- .../components/ExtractionTableDOI.tsx | 12 +- .../components/ExtractionTableFilterInput.tsx | 2 +- .../components/ExtractionTableJournal.tsx | 12 +- .../ExtractionTableJournalAutocomplete.tsx | 2 +- .../components/ExtractionTableName.tsx | 12 +- .../components/ExtractionTablePMID.tsx | 12 +- .../components/ExtractionTableStatus.tsx | 15 ++- .../components/ExtractionTableYear.tsx | 12 +- compose/neurosynth_compose/openapi | 2 +- 12 files changed, 176 insertions(+), 122 deletions(-) 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 d2b039dd..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'); }); @@ -370,49 +370,49 @@ describe('ExtractionTable', () => { }); }); - it('should sort by doi desc', () => { - cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click(); + // 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.get('[data-testid="ArrowDownwardIcon"]').eq(4).click(); - cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).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.get('[data-testid="ArrowDownwardIcon"]').eq(5).click(); + cy.get('[data-testid="ArrowDownwardIcon"]').eq(4).click(); cy.wait('@studysetFixture').then((studysetFixture) => { const studyset = studysetFixture.response?.body as StudysetReturn; @@ -424,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 ?? ''); }); }); @@ -437,8 +435,8 @@ describe('ExtractionTable', () => { }); it('should sort by pmid asc', () => { - cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click(); - cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).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) => { @@ -454,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 ?? ''); }); }); @@ -462,7 +460,7 @@ describe('ExtractionTable', () => { }); it('should sort by status desc', () => { - cy.get('[data-testid="ArrowDownwardIcon"]').eq(6).click(); + cy.get('[data-testid="ArrowDownwardIcon"]').eq(5).click(); cy.wait('@projectFixture').then((projectFixture) => { const project = projectFixture?.response?.body as INeurosynthProjectReturn; @@ -487,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 = @@ -518,8 +516,8 @@ describe('ExtractionTable', () => { }); it('should sort by status asc', () => { - cy.get('[data-testid="ArrowDownwardIcon"]').eq(6).click(); - cy.get('[data-testid="ArrowDownwardIcon"]').eq(6).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) => { @@ -545,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 = diff --git a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTable.tsx index f8937694..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) => { @@ -270,12 +269,11 @@ const ExtractionTable: React.FC = () => { onChange={handlePaginationChangeMuiPaginator} page={pagination.pageIndex + 1} /> - Total: {data.length} studies {table.getHeaderGroups().map((headerGroup) => ( @@ -283,7 +281,13 @@ const ExtractionTable: React.FC = () => { {headerGroup.headers.map((header) => ( {flexRender( @@ -416,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 8cfac918..0636a61c 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableAuthor.tsx @@ -33,19 +33,25 @@ export const ExtractionTableAuthorHeader: React.FC< } }} > - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 bf36cf6e..5e4844cf 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableDOI.tsx @@ -37,19 +37,25 @@ export const ExtractionTableDOIHeader: React.FC - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 728bdcbe..86a4ec00 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableFilterInput.tsx @@ -41,7 +41,7 @@ const ExtractionTableFilterInput: React.FC<{ - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 21198759..f25011e3 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableName.tsx @@ -37,19 +37,25 @@ export const ExtractionTableNameHeader: React.FC - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 aefc1208..6ded5224 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTablePMID.tsx @@ -33,19 +33,25 @@ export const ExtractionTablePMIDHeader: React.FC - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 0be5f302..7a23808a 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableStatus.tsx @@ -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'} > @@ -92,19 +95,25 @@ export const ExtractionTableStatusHeader: React.FC< } }} > - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( 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 84cd28a4..ce9d7d4f 100644 --- a/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx +++ b/compose/neurosynth-frontend/src/pages/Extraction/components/ExtractionTableYear.tsx @@ -34,19 +34,25 @@ export const ExtractionTableYearHeader: React.FC - + ) : isSorted === 'asc' ? ( table.resetSorting()}> - + ) : ( table.setSorting([{ id: 'year', desc: false }])} > - + )} 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