handleArrowClick('right')}
disabled={!active || disableRightArrow}
>
diff --git a/packages/libs/eda/src/lib/workspace/AnalysisPanel.tsx b/packages/libs/eda/src/lib/workspace/AnalysisPanel.tsx
index 612fdf9677..7587793db6 100644
--- a/packages/libs/eda/src/lib/workspace/AnalysisPanel.tsx
+++ b/packages/libs/eda/src/lib/workspace/AnalysisPanel.tsx
@@ -29,6 +29,7 @@ import { useGeoConfig } from '../core/hooks/geoConfig';
// Components
import WorkspaceNavigation from '@veupathdb/wdk-client/lib/Components/Workspace/WorkspaceNavigation';
+import UserDatasetDetailController from '@veupathdb/user-datasets/lib/Controllers/UserDatasetDetailController';
import { AnalysisSummary } from './AnalysisSummary';
import { EntityDiagram } from '../core';
import { ComputationRoute } from './ComputationRoute';
@@ -56,6 +57,7 @@ import FilterChipList from '../core/components/FilterChipList';
import { Public } from '@material-ui/icons';
import { Link } from 'react-router-dom';
import { AnalysisError } from '../core/components/AnalysisError';
+import { wdkRecordIdToDiyUserDatasetId } from '@veupathdb/user-datasets/lib/Utils/diyDatasets';
const AnalysisTabErrorBoundary = ({
children,
@@ -418,10 +420,36 @@ export function AnalysisPanel({
path={`${routeBase}/details`}
render={() => (
- p.value).join('/')}
- />
+ {studyMetadata.isUserStudy ? (
+ // TODO Make both cases below configurable via the root component.
+ // This will need to be done if we want EDA to stand on its own.
+
+ // Note that we are not inluding the custom detail page.
+ // As of this writing, details pages only add a link to
+ // EDA. Since we are in EDA, we don't want to add it here.
+
+
+
+ ) : (
+ p.value).join('/')}
+ />
+ )}
)}
/>
diff --git a/packages/libs/user-datasets-legacy/.env.sample b/packages/libs/user-datasets-legacy/.env.sample
deleted file mode 100644
index 3401e904cf..0000000000
--- a/packages/libs/user-datasets-legacy/.env.sample
+++ /dev/null
@@ -1,11 +0,0 @@
-VEUPATHDB_LOGIN_USER=apidb
-VEUPATHDB_LOGIN_PASS=**********
-
-BASE_URL=https://qa.microbiomedb.org
-WEBAPP=mbio
-
-REACT_APP_WDK_SERVICE_URL=$BASE_URL/$WEBAPP/service
-
-DATASET_IMPORT_SERVICE_URL=$BASE_URL/dataset-import
-
-REACT_APP_AVAILABLE_UPLOAD_TYPES=biom
diff --git a/packages/libs/user-datasets-legacy/.github/workflows/compile.yml b/packages/libs/user-datasets-legacy/.github/workflows/compile.yml
deleted file mode 100644
index 12b5a78048..0000000000
--- a/packages/libs/user-datasets-legacy/.github/workflows/compile.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: Compile Check
-
-on:
- pull_request:
-
-jobs:
- compile:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
- with:
- node-version: 14
- - name: Check TypeScript compilation
- run: |
- yarn install
- yarn tsc --noEmit
diff --git a/packages/libs/user-datasets-legacy/.github/workflows/npm-publish.yml b/packages/libs/user-datasets-legacy/.github/workflows/npm-publish.yml
deleted file mode 100644
index c63b3ead25..0000000000
--- a/packages/libs/user-datasets-legacy/.github/workflows/npm-publish.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Node.js Package
-
-on:
- push:
- branches: [ main ]
-
-jobs:
- publish:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v2
- with:
- node-version: 14
- - name: Build NPM packages
- run: |
- yarn install
- yarn build-npm-modules
- - uses: JS-DevTools/npm-publish@v1
- with:
- token: ${{ secrets.NPM_TOKEN }}
- access: public
diff --git a/packages/libs/user-datasets-legacy/.gitignore b/packages/libs/user-datasets-legacy/.gitignore
deleted file mode 100644
index eb8492103c..0000000000
--- a/packages/libs/user-datasets-legacy/.gitignore
+++ /dev/null
@@ -1,25 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-/lib
-
-# misc
-.DS_Store
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
diff --git a/packages/libs/user-datasets-legacy/.prettierignore b/packages/libs/user-datasets-legacy/.prettierignore
deleted file mode 100644
index e85a63251e..0000000000
--- a/packages/libs/user-datasets-legacy/.prettierignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-/lib
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
diff --git a/packages/libs/user-datasets-legacy/LICENSE b/packages/libs/user-datasets-legacy/LICENSE
deleted file mode 100644
index 261eeb9e9f..0000000000
--- a/packages/libs/user-datasets-legacy/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/packages/libs/user-datasets-legacy/README.adoc b/packages/libs/user-datasets-legacy/README.adoc
deleted file mode 100644
index 01093a4665..0000000000
--- a/packages/libs/user-datasets-legacy/README.adoc
+++ /dev/null
@@ -1,56 +0,0 @@
-# Getting Started with Create React App
-
-This project was bootstrapped with https://github.com/facebook/create-react-app[Create React App], using the https://github.com/VEuPathDB/web-dev/packages/cra-template[`@veupathdb` template].
-
-## Available Scripts
-
-In the project directory, you can run:
-
-### `yarn start`
-
-Runs the app in the development mode.
-Open http://localhost:3000[http://localhost:3000] to view it in the browser.
-
-The page will reload if you make edits.
-You will also see any lint errors in the console.
-
-You will be prompted to enter the VEuPathDB BRC Pre-Release Login credentials.
-
-**Environment Variables**
-
-- **`VEUPATHDB_LOGIN_USER`** VEuPathDB BRC prerelease user name (optional, but recommended)
-- **`VEUPATHDB_LOGIN_PASS`** VEuPathDB BRC prerelease user password (optional, but recommended)
-- **`REACT_APP_WDK_SERVICE_URL`** URL for a VEuPathDB service endpoint (required)
-- **`DATASET_IMPORT_SERVICE_URL`** URL for a dataset import service endpoint (required)
-- **`REACT_APP_AVAILABLE_UPLOAD_TYPES`** Comma-separated list of upload types which the local client will support (required)
-
-### `yarn test`
-
-Launches the test runner in the interactive watch mode.
-See the section about https://facebook.github.io/create-react-app/docs/running-tests[running tests] for more information.
-
-### `yarn build`
-
-Builds the app for production to the `build` folder.
-It correctly bundles React in production mode and optimizes the build for the best performance.
-
-The build is minified and the filenames include the hashes.
-Your app is ready to be deployed!
-
-See the section about https://facebook.github.io/create-react-app/docs/deployment[deployment] for more information.
-
-### `yarn eject`
-
-**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
-
-If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
-
-Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
-
-You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
-
-## Learn More
-
-You can learn more in the https://facebook.github.io/create-react-app/docs/getting-started[Create React App documentation].
-
-To learn React, check out the https://reactjs.org/[React documentation].
diff --git a/packages/libs/user-datasets-legacy/config-overrides.js b/packages/libs/user-datasets-legacy/config-overrides.js
deleted file mode 100644
index b20e562cf9..0000000000
--- a/packages/libs/user-datasets-legacy/config-overrides.js
+++ /dev/null
@@ -1,33 +0,0 @@
-module.exports = function override(config, env) {
- return {
- ...config,
- resolve: {
- ...config.resolve,
- fallback: {
- assert: require.resolve('assert/'),
- buffer: require.resolve('buffer/'),
- path: require.resolve('path-browserify'),
- querystring: require.resolve('querystring-es3'),
- fs: false,
- stream: require.resolve('stream-browserify'),
- },
- },
- externals: [{ jquery: 'jQuery' }],
- module: {
- ...config.module,
- rules: [
- ...config.module?.rules,
- // The ify-loader is needed for some plotly features.
- // This also needs the bubleify packages installed as
- // a dev dependency.
- {
- test: /\.(js|jsx|ts|tsx)$/,
- include: /node_modules/,
- loader: 'ify-loader',
- },
- ],
- },
- stats: 'errors-warnings',
- ignoreWarnings: [/Failed to parse source map/],
- };
-};
diff --git a/packages/libs/user-datasets-legacy/package.json b/packages/libs/user-datasets-legacy/package.json
deleted file mode 100644
index 723f39e15c..0000000000
--- a/packages/libs/user-datasets-legacy/package.json
+++ /dev/null
@@ -1,106 +0,0 @@
-{
- "name": "@veupathdb/user-datasets-legacy",
- "version": "0.1.15",
- "sideEffects": [
- "src/globals.js",
- "*.scss"
- ],
- "files": [
- "lib",
- "src/lib"
- ],
- "dependencies": {
- "@veupathdb/wdk-client": "workspace:^",
- "lodash": "^4.17.21"
- },
- "scripts": {
- "start": "veupathdb-react-scripts start",
- "build": "veupathdb-react-scripts prepare",
- "test": "veupathdb-react-scripts test",
- "eject": "veupathdb-react-scripts eject",
- "compile": "veupathdb-react-scripts compile",
- "copy-assets": "veupathdb-react-scripts copy-assets",
- "build-npm-modules": "veupathdb-react-scripts prepare",
- "clean": "rm -rf lib"
- },
- "eslintConfig": {
- "extends": [
- "@veupathdb"
- ]
- },
- "browserslist": [
- "extends @veupathdb/browserslist-config"
- ],
- "peerDependencies": {
- "react": "^16.14.0 || ^17.0.2",
- "react-dom": "^16.14.0 || ^17.0.2",
- "react-redux": "^7.1.3",
- "react-router-dom": "^5.3.0",
- "redux": "^4.0.0",
- "redux-observable": "^1.2.0",
- "rxjs": "^6.2.2"
- },
- "devDependencies": {
- "@babel/core": "^7.17.9",
- "@babel/plugin-syntax-flow": "^7.14.5",
- "@babel/plugin-transform-react-jsx": "^7.14.9",
- "@emotion/babel-preset-css-prop": "^11.10.0",
- "@emotion/react": "^11.10.0",
- "@emotion/serialize": "^1.0.2",
- "@emotion/styled": "^11.10.0",
- "@emotion/utils": "^1.1.0",
- "@material-ui/core": "^4.12.4",
- "@material-ui/icons": "^4.11.3",
- "@material-ui/lab": "^4.0.0-alpha.61",
- "@testing-library/dom": "8.11.2",
- "@testing-library/jest-dom": "5.16.1",
- "@testing-library/react": "12.1.2",
- "@testing-library/user-event": "13.5.0",
- "@types/history": "^4.7.4",
- "@types/node": "^14.0.0",
- "@types/react": "^17.0.38",
- "@types/react-dom": "^17.0.11",
- "@types/react-redux": "^7.1.20",
- "@types/react-router-dom": "^5.3.3",
- "@veupathdb/browserslist-config": "workspace:^",
- "@veupathdb/components": "workspace:^",
- "@veupathdb/coreui": "workspace:^",
- "@veupathdb/eda": "workspace:^",
- "@veupathdb/eslint-config": "workspace:^",
- "@veupathdb/http-utils": "workspace:^",
- "@veupathdb/react-scripts": "workspace:^",
- "@veupathdb/study-data-access": "workspace:^",
- "@veupathdb/tsconfig": "workspace:^",
- "assert": "^2.0.0",
- "autoprefixer": "^10.0.2",
- "bubleify": "^2.0.0",
- "buffer": "^6.0.3",
- "eslint": "^8.7.0",
- "eslint-config-react-app": "^7.0.0",
- "ify-loader": "^1.1.0",
- "notistack": "^1.0.10",
- "path-browserify": "^1.0.1",
- "postcss": "^8.1.0",
- "querystring-es3": "^0.2.1",
- "react": "^17.0.2",
- "react-app-rewired": "^2.2.1",
- "react-dom": "^17.0.2",
- "react-redux": "^7.1.3",
- "react-router-dom": "^5.3.0",
- "react-scripts": "5.0.0",
- "read": "^1.0.7",
- "redux": "^4.0.0",
- "redux-observable": "^1.2.0",
- "rxjs": "^6.2.2",
- "sass": "^1.49.0",
- "script-loader": "^0.7.2",
- "set-cookie-parser": "^2.4.6",
- "stream-browserify": "^3.0.0",
- "typescript": "^4.5.4",
- "web-vitals": "^0.2.4"
- },
- "volta": {
- "node": "16.14.1",
- "yarn": "1.22.11"
- }
-}
diff --git a/packages/libs/user-datasets-legacy/public/favicon.ico b/packages/libs/user-datasets-legacy/public/favicon.ico
deleted file mode 100644
index a11777cc47..0000000000
Binary files a/packages/libs/user-datasets-legacy/public/favicon.ico and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/public/index.html b/packages/libs/user-datasets-legacy/public/index.html
deleted file mode 100644
index 02c3a100e6..0000000000
--- a/packages/libs/user-datasets-legacy/public/index.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- React App
-
-
-
- You need to enable JavaScript to run this app.
-
-
-
-
diff --git a/packages/libs/user-datasets-legacy/public/logo192.png b/packages/libs/user-datasets-legacy/public/logo192.png
deleted file mode 100644
index fc44b0a379..0000000000
Binary files a/packages/libs/user-datasets-legacy/public/logo192.png and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/public/logo512.png b/packages/libs/user-datasets-legacy/public/logo512.png
deleted file mode 100644
index a4e47a6545..0000000000
Binary files a/packages/libs/user-datasets-legacy/public/logo512.png and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/public/manifest.json b/packages/libs/user-datasets-legacy/public/manifest.json
deleted file mode 100644
index 080d6c77ac..0000000000
--- a/packages/libs/user-datasets-legacy/public/manifest.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "short_name": "React App",
- "name": "Create React App Sample",
- "icons": [
- {
- "src": "favicon.ico",
- "sizes": "64x64 32x32 24x24 16x16",
- "type": "image/x-icon"
- },
- {
- "src": "logo192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "logo512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ],
- "start_url": ".",
- "display": "standalone",
- "theme_color": "#000000",
- "background_color": "#ffffff"
-}
diff --git a/packages/libs/user-datasets-legacy/public/robots.txt b/packages/libs/user-datasets-legacy/public/robots.txt
deleted file mode 100644
index e9e57dc4d4..0000000000
--- a/packages/libs/user-datasets-legacy/public/robots.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-# https://www.robotstxt.org/robotstxt.html
-User-agent: *
-Disallow:
diff --git a/packages/libs/user-datasets-legacy/src/Header.tsx b/packages/libs/user-datasets-legacy/src/Header.tsx
deleted file mode 100644
index 4eba9f0376..0000000000
--- a/packages/libs/user-datasets-legacy/src/Header.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as React from 'react';
-
-export default function Header() {
- return (
-
- {/* eslint-disable-next-line react/jsx-no-comment-textnodes */}
- /// ========================== \\\
-
- ||| VEUPATHDB DEVELOPMENT SITE |||
-
- \\\ ========================== ///
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/Home.tsx b/packages/libs/user-datasets-legacy/src/Home.tsx
deleted file mode 100644
index 880ab9c6d1..0000000000
--- a/packages/libs/user-datasets-legacy/src/Home.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Link } from '@veupathdb/wdk-client/lib/Components';
-
-export default function Home() {
- return (
-
-
Welcome to the VEuPathDB development environment.
-
- To get started, follow these steps:
-
-
- Put your feature code in src/lib
-
-
- Add and modify routes in src/index.tsx
-
-
- Configure external services in src/setupProxy.js
-
-
-
Work under development:
-
-
- User Datasets workspace
-
-
-
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/constants.js b/packages/libs/user-datasets-legacy/src/constants.js
deleted file mode 100644
index bea7665674..0000000000
--- a/packages/libs/user-datasets-legacy/src/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports.rootUrl = '/';
-module.exports.rootElement = '#root';
-module.exports.endpoint = '/service';
diff --git a/packages/libs/user-datasets-legacy/src/globals.js b/packages/libs/user-datasets-legacy/src/globals.js
deleted file mode 100644
index db41632c08..0000000000
--- a/packages/libs/user-datasets-legacy/src/globals.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { rootElement, rootUrl, endpoint } from './constants';
-window.__asset_path_remove_me_please__ = '/';
-window.__OUTPUT_SUBDIR__ = '';
-window.__DEV__ = process.env.NODE_ENV !== 'production';
-window.__SITE_CONFIG__ = {
- rootUrl,
- rootElement,
- endpoint,
- // projectId: process.env.PROJECT_ID
-};
diff --git a/packages/libs/user-datasets-legacy/src/index.css b/packages/libs/user-datasets-legacy/src/index.css
deleted file mode 100644
index ec2585e8c0..0000000000
--- a/packages/libs/user-datasets-legacy/src/index.css
+++ /dev/null
@@ -1,13 +0,0 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
diff --git a/packages/libs/user-datasets-legacy/src/index.tsx b/packages/libs/user-datasets-legacy/src/index.tsx
deleted file mode 100644
index 81329fbd34..0000000000
--- a/packages/libs/user-datasets-legacy/src/index.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import './globals';
-
-import { RouteComponentProps } from 'react-router-dom';
-
-import { partial } from 'lodash';
-
-import { initialize } from '@veupathdb/web-common/lib/bootstrap';
-import { RouteEntry } from '@veupathdb/wdk-client/lib/Core/RouteEntry';
-import Header from './Header';
-import Home from './Home';
-import { endpoint, rootElement, rootUrl } from './constants';
-import reportWebVitals from './reportWebVitals';
-
-import { Loading } from '@veupathdb/wdk-client/lib/Components';
-import { useWdkService } from '@veupathdb/wdk-client/lib/Hooks/WdkServiceHook';
-
-import UserDatasetHelp from './lib/Components/UserDatasetHelp';
-import { quotaSize } from './lib/Components/UserDatasetUtils';
-import { UserDatasetRouter } from './lib/Controllers/UserDatasetRouter';
-import { wrapWdkService } from './lib/Service';
-import { wrapStoreModules } from './lib/StoreModules';
-import {
- makeDatasetUploadPageConfig,
- uploadTypeConfig,
-} from './lib/Utils/upload-config';
-
-import '@veupathdb/wdk-client/lib/Core/Style/index.scss';
-import '@veupathdb/web-common/lib/styles/client.scss';
-
-const availableUploadTypes =
- process.env.REACT_APP_AVAILABLE_UPLOAD_TYPES?.trim().split(/\s*,\s*/g);
-
-const hasDirectUpload = makeDatasetUploadPageConfig(
- availableUploadTypes,
- uploadTypeConfig
-).hasDirectUpload;
-
-const datasetImportUrl = hasDirectUpload ? '/dataset-import' : undefined;
-
-initialize({
- rootUrl,
- rootElement,
- wrapRoutes: (routes: any): RouteEntry[] => [
- {
- path: '/',
- component: (props: RouteComponentProps) => ,
- },
- {
- path: '/user-datasets',
- exact: false,
- component: () => (
-
- ),
- },
- {
- path: '/help',
- exact: true,
- component: function DevHelp() {
- const projectName = useWdkService(
- async (wdkService) => (await wdkService.getConfig()).displayName,
- []
- );
-
- return projectName == null ? (
-
- ) : (
-
- );
- },
- },
- ...routes,
- ],
- componentWrappers: {
- SiteHeader: () => Header,
- },
- endpoint,
- wrapStoreModules,
- wrapWdkService: partial(
- wrapWdkService,
- datasetImportUrl == null || process.env.REACT_APP_WDK_SERVICE_URL == null
- ? undefined
- : {
- datasetImportUrl,
- fullWdkServiceUrl: process.env.REACT_APP_WDK_SERVICE_URL,
- }
- ),
-} as any);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
diff --git a/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetUploadActions.ts b/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetUploadActions.ts
deleted file mode 100644
index 5df3ef87dc..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetUploadActions.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import {
- makeActionCreator,
- InferAction,
-} from '@veupathdb/wdk-client/lib/Utils/ActionCreatorUtils';
-
-import { FormSubmission } from '../Components/UploadForm';
-import { UserDatasetUpload } from '../Utils/types';
-
-export const submitUploadForm = makeActionCreator(
- 'user-dataset-upload/submit-form',
- (formSubmission: FormSubmission, redirectTo?: string) => ({
- formSubmission,
- redirectTo,
- })
-);
-
-export const receiveBadUpload = makeActionCreator(
- 'user-dataset-upload/receive-bad-upload',
- (message: string) => ({ message, timestamp: Date.now() })
-);
-
-export const clearBadUpload = makeActionCreator(
- 'user-dataset-upload/clear-bad-upload',
- () => ({})
-);
-
-export const cancelCurrentUpload = makeActionCreator(
- 'user-dataset-upload/cancel-upload',
- (id: string) => ({ id })
-);
-
-export const clearMessages = makeActionCreator(
- 'user-dataset-upload/clear-messages',
- (ids: string[]) => ({ ids })
-);
-
-export const requestUploadMessages = makeActionCreator(
- 'user-dataset-upload/load-upload-messages',
- () => {}
-);
-
-export const receiveUploadMessages = makeActionCreator(
- 'user-dataset-upload/receive-upload-messages',
- (uploads: Array) => ({ uploads })
-);
-
-export const receiveBadUploadHistoryAction = makeActionCreator(
- 'user-dataset-upload/receive-bad-upload-history-action',
- (message: string) => ({ message, timestamp: Date.now() })
-);
-
-export type Action =
- | InferAction
- | InferAction
- | InferAction
- | InferAction
- | InferAction
- | InferAction
- | InferAction
- | InferAction;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetsActions.ts b/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetsActions.ts
deleted file mode 100644
index 12bcc808f7..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Actions/UserDatasetsActions.ts
+++ /dev/null
@@ -1,495 +0,0 @@
-import { get } from 'lodash';
-
-import {
- transitionToInternalPage,
- Action as RouteAction,
-} from '@veupathdb/wdk-client/lib/Actions/RouterActions';
-import {
- updateUserPreference,
- PreferenceUpdateAction,
-} from '@veupathdb/wdk-client/lib/Actions/UserActions';
-import {
- EmptyAction,
- emptyAction,
-} from '@veupathdb/wdk-client/lib/Core/WdkMiddleware';
-import { ServiceError } from '@veupathdb/wdk-client/lib/Service/ServiceError';
-
-import {
- UserDatasetShareResponse,
- validateUserDatasetCompatibleThunk,
-} from '../Service/UserDatasetWrappers';
-
-import { FILTER_BY_PROJECT_PREF } from '../Utils/project-filter';
-import { UserDataset, UserDatasetMeta } from '../Utils/types';
-
-export type Action =
- | DetailErrorAction
- | DetailLoadingAction
- | DetailReceivedAction
- | DetailRemoveErrorAction
- | DetailRemoveSuccessAction
- | DetailRemovingAction
- | DetailUpdateErrorAction
- | DetailUpdateSuccessAction
- | DetailUpdatingAction
- | ListLoadingAction
- | ListErrorReceivedAction
- | ListReceivedAction
- | ProjectFilterAction
- | SharingDatasetAction
- | SharingSuccessAction;
-
-//==============================================================================
-
-export const LIST_LOADING = 'user-datasets/list-loading';
-
-export type ListLoadingAction = {
- type: typeof LIST_LOADING;
-};
-
-export function listLoading(): ListLoadingAction {
- return {
- type: LIST_LOADING,
- };
-}
-
-//==============================================================================
-
-export const LIST_RECEIVED = 'user-dataset/list-received';
-
-export type ListReceivedAction = {
- type: typeof LIST_RECEIVED;
- payload: {
- userDatasets: UserDataset[];
- filterByProject: boolean;
- };
-};
-
-export function listReceived(
- userDatasets: UserDataset[],
- filterByProject: boolean
-): ListReceivedAction {
- return {
- type: LIST_RECEIVED,
- payload: {
- userDatasets,
- filterByProject,
- },
- };
-}
-
-//==============================================================================
-
-export const LIST_ERROR_RECEIVED = 'user-dataset/list-error';
-
-export type ListErrorReceivedAction = {
- type: typeof LIST_ERROR_RECEIVED;
- payload: {
- error: ServiceError;
- };
-};
-
-export function listErrorReceived(
- error: ServiceError
-): ListErrorReceivedAction {
- return {
- type: LIST_ERROR_RECEIVED,
- payload: {
- error,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_LOADING = 'user-datasets/detail-loading';
-
-export type DetailLoadingAction = {
- type: typeof DETAIL_LOADING;
- payload: {
- id: number;
- };
-};
-
-export function detailLoading(id: number): DetailLoadingAction {
- return {
- type: DETAIL_LOADING,
- payload: {
- id,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_RECEIVED = 'user-datasets/detail-received';
-
-export type DetailReceivedAction = {
- type: typeof DETAIL_RECEIVED;
- payload: {
- id: number;
- userDataset?: UserDataset;
- };
-};
-
-export function detailReceived(
- id: number,
- userDataset?: UserDataset
-): DetailReceivedAction {
- return {
- type: DETAIL_RECEIVED,
- payload: {
- id,
- userDataset,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_ERROR = 'user-datasets/detail-error';
-
-export type DetailErrorAction = {
- type: typeof DETAIL_ERROR;
- payload: {
- error: ServiceError;
- };
-};
-
-export function detailError(error: ServiceError): DetailErrorAction {
- return {
- type: DETAIL_ERROR,
- payload: {
- error,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_UPDATING = 'user-dataests/detail-updating';
-
-export type DetailUpdatingAction = {
- type: typeof DETAIL_UPDATING;
-};
-
-export function detailUpdating(): DetailUpdatingAction {
- return {
- type: DETAIL_UPDATING,
- };
-}
-
-//==============================================================================
-
-export const DETAIL_UPDATE_SUCCESS = 'user-datasets/detail-update-success';
-
-export type DetailUpdateSuccessAction = {
- type: typeof DETAIL_UPDATE_SUCCESS;
- payload: {
- userDataset: UserDataset;
- };
-};
-
-export function detailUpdateSuccess(
- userDataset: UserDataset
-): DetailUpdateSuccessAction {
- return {
- type: DETAIL_UPDATE_SUCCESS,
- payload: {
- userDataset,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_UPDATE_ERROR = 'user-datasets/detail-update-error';
-
-export type DetailUpdateErrorAction = {
- type: typeof DETAIL_UPDATE_ERROR;
- payload: {
- error: ServiceError;
- };
-};
-
-export function detailUpdateError(
- error: ServiceError
-): DetailUpdateErrorAction {
- return {
- type: DETAIL_UPDATE_ERROR,
- payload: {
- error,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_REMOVING = 'user-datasets/detail-removing';
-
-export type DetailRemovingAction = {
- type: typeof DETAIL_REMOVING;
-};
-
-export function detailRemoving(): DetailRemovingAction {
- return {
- type: DETAIL_REMOVING,
- };
-}
-
-//==============================================================================
-
-export const DETAIL_REMOVE_SUCCESS = 'user-datasets/detail-remove-success';
-
-export type DetailRemoveSuccessAction = {
- type: typeof DETAIL_REMOVE_SUCCESS;
- payload: {
- userDataset: UserDataset;
- };
-};
-
-export function detailRemoveSuccess(
- userDataset: UserDataset
-): DetailRemoveSuccessAction {
- return {
- type: DETAIL_REMOVE_SUCCESS,
- payload: {
- userDataset,
- },
- };
-}
-
-//==============================================================================
-
-export const DETAIL_REMOVE_ERROR = 'user-datasets/detail-remove-error';
-
-export type DetailRemoveErrorAction = {
- type: typeof DETAIL_REMOVE_ERROR;
- payload: {
- error: ServiceError;
- };
-};
-
-export function detailRemoveError(
- error: ServiceError
-): DetailRemoveErrorAction {
- return {
- type: DETAIL_REMOVE_ERROR,
- payload: {
- error,
- },
- };
-}
-
-//==============================================================================
-
-export const SHARING_DATASET = 'user-datasets/sharing-dataset';
-
-export type SharingDatasetAction = {
- type: typeof SHARING_DATASET;
- payload: {
- userDataset: UserDataset;
- recipients: string[];
- };
-};
-
-export function sharingDataset(
- userDataset: UserDataset,
- recipients: string[]
-): SharingDatasetAction {
- return {
- type: SHARING_DATASET,
- payload: {
- userDataset,
- recipients,
- },
- };
-}
-
-//==============================================================================
-
-export const SHARING_SUCCESS = 'user-datasets/sharing-success';
-
-export type SharingSuccessAction = {
- type: typeof SHARING_SUCCESS;
- payload: {
- response: UserDatasetShareResponse;
- };
-};
-
-export function sharingSuccess(
- response: UserDatasetShareResponse
-): SharingSuccessAction {
- return {
- type: SHARING_SUCCESS,
- payload: {
- response,
- },
- };
-}
-
-//==============================================================================
-
-export const SHARING_ERROR = 'user-datasets/sharing-error';
-
-export type SharingErrorAction = {
- type: typeof SHARING_ERROR;
- payload: {
- error: Error;
- };
-};
-
-export function sharingError(error: Error): SharingErrorAction {
- return {
- type: SHARING_ERROR,
- payload: {
- error,
- },
- };
-}
-
-//==============================================================================
-
-export const PROJECT_FILTER =
- 'user-datasets/project-filter-preference-received';
-
-export type ProjectFilterAction = {
- type: typeof PROJECT_FILTER;
- payload: {
- filterByProject: boolean;
- };
-};
-
-export function projectFilter(filterByProject: boolean): ProjectFilterAction {
- return {
- type: PROJECT_FILTER,
- payload: {
- filterByProject,
- },
- };
-}
-
-//==============================================================================
-
-type ListAction =
- | ListLoadingAction
- | ListReceivedAction
- | ListErrorReceivedAction;
-type DetailAction =
- | DetailLoadingAction
- | DetailReceivedAction
- | DetailErrorAction;
-type UpdateAction =
- | DetailUpdatingAction
- | DetailUpdateSuccessAction
- | DetailUpdateErrorAction;
-type RemovalAction =
- | DetailRemovingAction
- | DetailRemoveSuccessAction
- | DetailRemoveErrorAction;
-type SharingAction =
- | SharingDatasetAction
- | SharingSuccessAction
- | SharingErrorAction;
-
-export function loadUserDatasetList() {
- return validateUserDatasetCompatibleThunk(({ wdkService }) => [
- listLoading(),
- Promise.all([
- wdkService.getCurrentUserPreferences().then(
- (preferences) =>
- get(preferences.global, FILTER_BY_PROJECT_PREF, 'false') !== 'false',
- // ignore error and default to false
- () => false
- ),
- wdkService.getCurrentUserDatasets(),
- ]).then(
- ([filterByProject, userDatasets]) =>
- listReceived(userDatasets, filterByProject),
- listErrorReceived
- ),
- ]);
-}
-
-export function loadUserDatasetDetail(id: number) {
- return validateUserDatasetCompatibleThunk(({ wdkService }) => [
- detailLoading(id),
- wdkService.getUserDataset(id).then(
- (userDataset) => detailReceived(id, userDataset),
- (error: ServiceError) =>
- error.status === 404 ? detailReceived(id) : detailError(error)
- ),
- ]);
-}
-
-export function shareUserDatasets(
- userDatasetIds: number[],
- recipientUserIds: number[]
-) {
- return validateUserDatasetCompatibleThunk(({ wdkService }) => {
- return wdkService
- .editUserDatasetSharing('add', userDatasetIds, recipientUserIds)
- .then(sharingSuccess, sharingError);
- });
-}
-
-export function unshareUserDatasets(
- userDatasetIds: number[],
- recipientUserIds: number[]
-) {
- return validateUserDatasetCompatibleThunk(({ wdkService }) => {
- return wdkService
- .editUserDatasetSharing('delete', userDatasetIds, recipientUserIds)
- .then(sharingSuccess, sharingError);
- });
-}
-
-export function updateUserDatasetDetail(
- userDataset: UserDataset,
- meta: UserDatasetMeta
-) {
- return validateUserDatasetCompatibleThunk(({ wdkService }) => [
- detailUpdating(),
- wdkService
- .updateUserDataset(userDataset.id, meta)
- .then(
- () => detailUpdateSuccess({ ...userDataset, meta }),
- detailUpdateError
- ),
- ]);
-}
-
-export function removeUserDataset(
- userDataset: UserDataset,
- redirectTo?: string
-) {
- return validateUserDatasetCompatibleThunk<
- RemovalAction | EmptyAction | RouteAction
- >(({ wdkService }) => [
- detailRemoving(),
- wdkService
- .removeUserDataset(userDataset.id)
- .then(
- () => [
- detailRemoveSuccess(userDataset),
- typeof redirectTo === 'string'
- ? transitionToInternalPage(redirectTo)
- : emptyAction,
- ],
- detailRemoveError
- ),
- ]);
-}
-
-export function updateProjectFilter(filterByProject: boolean) {
- return validateUserDatasetCompatibleThunk<
- PreferenceUpdateAction | ProjectFilterAction
- >(() => [
- updateUserPreference(
- 'global',
- FILTER_BY_PROJECT_PREF,
- JSON.stringify(filterByProject)
- ),
- projectFilter(filterByProject),
- ]);
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/AllUploads.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/AllUploads.tsx
deleted file mode 100644
index 499029ef9c..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/AllUploads.tsx
+++ /dev/null
@@ -1,279 +0,0 @@
-import { ReactNode, useCallback, useMemo } from 'react';
-
-import { Checkbox, Link } from '@veupathdb/wdk-client/lib/Components';
-import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-import { useWdkService } from '@veupathdb/wdk-client/lib/Hooks/WdkServiceHook';
-
-import UserDatasetEmptyState from './EmptyState';
-import { useProjectFilter } from '../Hooks/project-filter';
-import { UserDatasetUpload } from '../Utils/types';
-
-interface Props {
- baseUrl: string;
- uploadList?: Array;
- errorMessage?: string;
- actions: {
- clearMessages: (ids: string[]) => void;
- cancelCurrentUpload: (id: string) => void;
- };
-}
-
-const ClearAllMessagesButton = (
- onClickCallback: () => void,
- buttonContent: ReactNode
-) => (
-
- {buttonContent}
-
-);
-
-type HeaderProps = {
- color: string;
- iconType: string;
- date: string;
-};
-const UploadHeader = ({ color, iconType, date }: HeaderProps) => (
-
-
-
- {new Date(date).toLocaleString()}
-
-
-);
-
-const OngoingUpload = (
- upload: UserDatasetUpload,
- onClickCancel: () => void
-) => (
-
-
-
- Currently uploading: {upload.datasetName}
-
-
- Status:{' '}
- {upload.status +
- (upload.stepPercent ? ' ... ' + upload.stepPercent + '%' : '')}
-
- {upload.isCancellable && (
-
onClickCancel()}>
- Cancel upload
-
- )}
-
-);
-
-const SuccessfulUpload = (upload: UserDatasetUpload, baseUrl: string) => (
-
-
- Successfully uploaded:
-
- {upload.datasetName}
-
-
-);
-
-const InvalidatedUpload = (upload: UserDatasetUpload) => {
- return (
-
-
-
-
{upload.datasetName}
was rejected as it is invalid
- {upload.errors ? (
-
- :
-
- {upload.errors.map((line, ix) => (
- {line}
- ))}
-
-
- ) : (
-
.
- )}
-
-
- );
-};
-
-const FailedUpload = (upload: UserDatasetUpload) => (
-
-
-
- {upload.datasetName}
could not be uploaded.
-
-
- Please try again. If the problem persists, please let us know through our
-
-
- support form
-
- .
-
-
-);
-
-const UploadsTable = (props: {
- baseUrl: string;
- uploads: Array;
- cancelCurrentUpload: (id: string) => void;
-}) => {
- const { baseUrl, uploads, cancelCurrentUpload } = props;
- return (
-
-
- {uploads.map((upload, ix) => (
-
-
- {upload.isOngoing
- ? OngoingUpload(upload, () => cancelCurrentUpload(upload.id))
- : upload.isSuccessful
- ? SuccessfulUpload(upload, baseUrl)
- : upload.isUserError
- ? InvalidatedUpload(upload)
- : FailedUpload(upload)}
-
-
- ))}
-
-
- );
-};
-const RefreshButton = () => (
- {
- window.location.reload();
- }}
- >
- Refresh page
-
-);
-const ErrorMessage = (message: string) => (
-
- {message.split('\n').map((line, ix) => (
-
- {ix === 0 && }
- {line}
-
- ))}
-
-);
-const AllUploads = (props: Props) => {
- const uploads = useMemo(() => props.uploadList ?? [], [props.uploadList]);
- const ongoingUploads = useMemo(
- () => uploads.filter((u) => u.isOngoing),
- [uploads]
- );
- const finishedUploads = useMemo(
- () => uploads.filter((u) => !u.isOngoing),
- [uploads]
- );
-
- const projectInfo = useWdkService(async (wdkService) => {
- const config = await wdkService.getConfig();
-
- return {
- id: config.projectId,
- name: config.displayName,
- };
- }, []);
-
- const [projectFilter, setProjectFilter] = useProjectFilter();
-
- const hasUploadFromAnotherProject = useMemo(
- () =>
- uploads.some((upload) =>
- upload.projects.some((project) => project !== projectInfo?.id)
- ),
- [projectInfo, uploads]
- );
-
- const projectFilterApplied = projectFilter !== false;
-
- const uploadFilterPredicate = useCallback(
- (upload: UserDatasetUpload) =>
- projectInfo == null ||
- !projectFilterApplied ||
- upload.projects.includes(projectInfo.id),
- [projectInfo, projectFilterApplied]
- );
-
- const filteredUploads = useMemo(
- () => uploads.filter(uploadFilterPredicate),
- [uploads, uploadFilterPredicate]
- );
- const filteredFinishedUploads = useMemo(
- () => finishedUploads.filter(uploadFilterPredicate),
- [finishedUploads, uploadFilterPredicate]
- );
-
- return (
-
- {props.errorMessage != null && ErrorMessage(props.errorMessage)}
- {ongoingUploads.length > 0 && RefreshButton()}
- {projectInfo != null && hasUploadFromAnotherProject && (
-
- {
- setProjectFilter((projectFilter) => !projectFilter);
- }}
- />
-
- Only show uploads to {projectInfo.name}
-
-
- )}
- {filteredUploads.length > 0 && (
-
- )}
- {filteredFinishedUploads.length > 0 &&
- ClearAllMessagesButton(
- () =>
- props.actions.clearMessages(
- filteredFinishedUploads.map((u) => u.id)
- ),
- projectFilterApplied && hasUploadFromAnotherProject
- ? 'Clear These Messages'
- : 'Clear All Messages'
- )}
- {props.errorMessage == null &&
- projectInfo != null &&
- filteredUploads.length === 0 && (
-
- )}
-
- );
-};
-export default AllUploads;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/DateTime.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/DateTime.tsx
deleted file mode 100644
index 535a5994a7..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/DateTime.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-
-type Value = string | number | Date;
-
-interface Props {
- datetime: Value;
-}
-
-export function DateTime(props: Props) {
- try {
- const dateObj = new Date(props.datetime);
- const isoString = dateObj.toISOString();
- const [_, date = 'Unknown', time = ''] =
- dateObj.toISOString().match(/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}).*/) ?? [];
- return (
-
- {date} {time}
-
- );
- } catch {
- return Unknown
;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigDatasetDetail.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigDatasetDetail.jsx
deleted file mode 100644
index 8e987b2467..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigDatasetDetail.jsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import React from 'react';
-
-import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-import { Mesa, MesaState } from '@veupathdb/coreui/lib/components/Mesa';
-
-import { makeClassifier } from '../UserDatasetUtils';
-import UserDatasetDetail from './UserDatasetDetail';
-import BigwigGBrowseUploader from './BigwigGBrowseUploader';
-
-const classify = makeClassifier('UserDatasetDetail', 'BigwigDatasetDetail');
-
-class BigwigDatasetDetail extends UserDatasetDetail {
- constructor(props) {
- super(props);
- this.renderTracksSection = this.renderTracksSection.bind(this);
- this.getTracksTableColumns = this.getTracksTableColumns.bind(this);
- }
-
- getTracksTableColumns() {
- const { userDataset, appUrl, config } = this.props;
- const { id, type, meta, dependencies } = userDataset;
- const name = meta.name;
- const { projectId } = config;
- const { seqId } = type && type.data ? type.data : { seqId: null };
-
- var genome;
- dependencies.forEach(function (dependency) {
- if (dependency.resourceIdentifier.endsWith('_Genome')) {
- var regex = new RegExp(
- '-' + dependency.resourceVersion + '_(.*)_Genome'
- );
- var genomeList = dependency.resourceIdentifier.match(regex);
- genome = genomeList[1];
- }
- });
-
- return [
- {
- key: 'datafileName',
- name: 'Filename',
- renderCell: ({ row }) => {row.datafileName}
,
- },
- {
- key: 'main',
- name: 'Genome Browser Link',
- renderCell: ({ row }) => (
-
- ),
- },
- ];
- }
-
- renderTracksSection() {
- const { userDataset, appUrl, projectName } = this.props;
-
- const { type } = userDataset;
- const { data } = type;
-
- const rows = data && Array.isArray(data.tracks) ? data.tracks : [];
- const columns = this.getTracksTableColumns({ userDataset, appUrl });
- const tracksTableState = MesaState.create({ rows, columns });
-
- return !rows.length ? null : userDataset.isInstalled ? (
-
-
-
- Genome Browser Tracks
-
-
-
-
-
- ) : (
-
- This data set isn't installed to {projectName} or contains no files.
-
- );
- }
-
- getPageSections() {
- const [headerSection, compatSection, fileSection] = super.getPageSections();
- return [
- headerSection,
- compatSection,
- this.renderTracksSection,
- fileSection,
- ];
- }
-}
-
-export default BigwigDatasetDetail;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.jsx
deleted file mode 100644
index f788a86645..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react';
-import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-
-import './BigwigGBrowseUploader.scss';
-
-class BigwigGBrowseUploader extends React.Component {
- constructor(props) {
- super(props);
- this.getGBrowseUrl = this.getGBrowseUrl.bind(this);
- }
-
- getButtons() {
- const GBrowseUrl = this.getGBrowseUrl();
- return (
-
-
-
- View in Genome Browser
-
-
-
- );
- }
-
- getGBrowseUrl() {
- const { sequenceId, genome, datasetName, datafileName } = this.props;
- var jbrowseTrackName = datasetName + ' ' + datafileName;
- return `/a/jbrowse/index.html?data=/a/service/jbrowse/tracks/${genome}&tracks=gene,${
- jbrowseTrackName || ''
- }&highlight=&loc=${sequenceId || ''}`;
- }
-
- render() {
- const buttons = this.getButtons();
- return (
-
- );
- }
-}
-
-export default BigwigGBrowseUploader;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.scss b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.scss
deleted file mode 100644
index 447b00e2e1..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BigwigGBrowseUploader.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-@import '@veupathdb/wdk-client/lib/Core/Style/palette';
-
-.BigwigGBrowseUploader {
- display: flex;
- width: 100%;
- align-items: center;
- flex-flow: row nowrap;
-
- .BigwigGBrowseUploader-Message {
- flex: 1;
- }
-
- .BigwigGBrowseUploader-Buttons {
- flex: 0 0 auto;
- margin-left: 20px;
- button[disabled] {
- opacity: 0.5;
- cursor: not-allowed;
- }
- }
-
- .BigwigGBrowseUploader-Icon {
- flex: 0 0 auto;
- margin-right: 10px;
- .wdk-Icon {
- font-size: 20px;
- transition: all 0.3s;
- color: #bbb;
- &.fa-check-circle-o {
- color: $green;
- }
- &.fa-spin {
- color: $blue;
- }
- }
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BiomDatasetDetail.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BiomDatasetDetail.jsx
deleted file mode 100644
index 091426f1d4..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/BiomDatasetDetail.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Link } from 'react-router-dom';
-
-import UserDatasetDetail from './UserDatasetDetail';
-
-class BiomDatasetDetail extends UserDatasetDetail {
- constructor(props) {
- super(props);
- this.renderEdaLinkout = this.renderEdaLinkout.bind(this);
- }
-
- renderEdaLinkout() {
- const {
- config: { displayName },
- userDataset: { isInstalled },
- edaWorkspaceUrl,
- } = this.props;
-
- return !isInstalled || !edaWorkspaceUrl ? null : (
-
-
-
- Explore in {displayName}
-
-
-
- );
- }
-
- getPageSections() {
- const [headerSection, , fileSection] = super.getPageSections();
-
- return [headerSection, this.renderEdaLinkout, fileSection];
- }
-}
-
-export default BiomDatasetDetail;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/IsaDatasetDetail.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/IsaDatasetDetail.jsx
deleted file mode 100644
index 6206d36569..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/IsaDatasetDetail.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Link } from 'react-router-dom';
-
-import UserDatasetDetail from './UserDatasetDetail';
-
-class IsaDatasetDetail extends UserDatasetDetail {
- constructor(props) {
- super(props);
- this.renderEdaLinkout = this.renderEdaLinkout.bind(this);
- }
-
- renderEdaLinkout() {
- const {
- config: { displayName },
- userDataset: { isInstalled },
- edaWorkspaceUrl,
- edaMapUrl,
- } = this.props;
-
- if (!isInstalled) return null;
-
- return (
- <>
- {!edaWorkspaceUrl ? null : (
-
-
-
- Explore in {displayName}
-
-
-
- )}
- {!edaMapUrl ? null : (
-
-
-
- Explore in MapVEu
-
-
-
- )}
- >
- );
- }
-
- getPageSections() {
- const [headerSection, , fileSection] = super.getPageSections();
-
- return [headerSection, this.renderEdaLinkout, fileSection];
- }
-}
-
-export default IsaDatasetDetail;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/RnaSeqDatasetDetail.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/RnaSeqDatasetDetail.jsx
deleted file mode 100644
index 4a73c241da..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/RnaSeqDatasetDetail.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import BigwigDatasetDetail from './BigwigDatasetDetail';
-
-class RnaSeqDatasetDetail extends BigwigDatasetDetail {
- constructor(props) {
- super(props);
- this.renderTracksSection = this.renderTracksSection.bind(this);
- this.getTracksTableColumns = this.getTracksTableColumns.bind(this);
- }
-}
-
-export default RnaSeqDatasetDetail;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.jsx
deleted file mode 100644
index b59cffb068..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.jsx
+++ /dev/null
@@ -1,575 +0,0 @@
-import React from 'react';
-
-import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-import SaveableTextEditor from '@veupathdb/wdk-client/lib/Components/InputControls/SaveableTextEditor';
-import Link from '@veupathdb/wdk-client/lib/Components/Link';
-import {
- AnchoredTooltip,
- Mesa,
- MesaState,
-} from '@veupathdb/coreui/lib/components/Mesa';
-import { WdkDependenciesContext } from '@veupathdb/wdk-client/lib/Hooks/WdkDependenciesEffect';
-import { bytesToHuman } from '@veupathdb/wdk-client/lib/Utils/Converters';
-
-import NotFound from '@veupathdb/wdk-client/lib/Views/NotFound/NotFound';
-
-import { isUserDatasetsCompatibleWdkService } from '../../Service/UserDatasetWrappers';
-
-import SharingModal from '../Sharing/UserDatasetSharingModal';
-import UserDatasetStatus from '../UserDatasetStatus';
-import { makeClassifier, normalizePercentage } from '../UserDatasetUtils';
-import { ThemedGrantAccessButton } from '../ThemedGrantAccessButton';
-import { ThemedDeleteButton } from '../ThemedDeleteButton';
-
-import { DateTime } from '../DateTime';
-
-import './UserDatasetDetail.scss';
-
-const classify = makeClassifier('UserDatasetDetail');
-
-class UserDatasetDetail extends React.Component {
- constructor(props) {
- super(props);
- this.state = { sharingModalOpen: false };
-
- this.onMetaSave = this.onMetaSave.bind(this);
- this.isMyDataset = this.isMyDataset.bind(this);
- this.validateKey = this.validateKey.bind(this);
- this.handleDelete = this.handleDelete.bind(this);
-
- this.getAttributes = this.getAttributes.bind(this);
- this.renderAttributeList = this.renderAttributeList.bind(this);
- this.renderHeaderSection = this.renderHeaderSection.bind(this);
- this.renderDatasetActions = this.renderDatasetActions.bind(this);
-
- this.renderCompatibilitySection =
- this.renderCompatibilitySection.bind(this);
- this.getCompatibilityTableColumns =
- this.getCompatibilityTableColumns.bind(this);
-
- this.openSharingModal = this.openSharingModal.bind(this);
- this.renderFileSection = this.renderFileSection.bind(this);
- this.closeSharingModal = this.closeSharingModal.bind(this);
- this.getFileTableColumns = this.getFileTableColumns.bind(this);
- this.renderDetailsSection = this.renderDetailsSection.bind(this);
- this.renderAllDatasetsLink = this.renderAllDatasetsLink.bind(this);
- }
-
- isMyDataset() {
- const { user, userDataset } = this.props;
- return (
- user && userDataset && user.id && user.id === userDataset.ownerUserId
- );
- }
-
- openSharingModal() {
- this.setState({ sharingModalOpen: true });
- }
-
- closeSharingModal() {
- this.setState({ sharingModalOpen: false });
- }
-
- validateKey(key) {
- const META_KEYS = ['name', 'summary', 'description'];
- if (typeof key !== 'string' || !META_KEYS.includes(key))
- throw new TypeError(
- `Can't edit meta for invalid key: ${JSON.stringify(key)}`
- );
- }
-
- onMetaSave(key) {
- this.validateKey(key);
- return (value) => {
- if (typeof value !== 'string')
- throw new TypeError(
- `onMetaSave: expected input value to be string; got ${typeof value}`
- );
- const { userDataset, updateUserDatasetDetail } = this.props;
- const meta = { ...userDataset.meta, [key]: value };
- return updateUserDatasetDetail(userDataset, meta);
- };
- }
-
- handleDelete() {
- const { baseUrl, isOwner, userDataset, removeUserDataset, dataNoun } =
- this.props;
- const { sharedWith } = userDataset;
- const shareCount = !Array.isArray(sharedWith) ? null : sharedWith.length;
- const message =
- `Are you sure you want to ${
- isOwner ? 'delete' : 'remove'
- } this ${dataNoun.singular.toLowerCase()}? ` +
- (!isOwner || !shareCount
- ? ''
- : `${shareCount} collaborator${
- shareCount === 1 ? '' : 's'
- } you've shared with will lose access.`);
-
- if (window.confirm(message)) {
- removeUserDataset(userDataset, baseUrl);
- }
- }
-
- renderAllDatasetsLink() {
- return (
-
-
- All {this.props.workspaceTitle}
-
- );
- }
-
- getAttributes() {
- const { userDataset, quotaSize, questionMap } = this.props;
- const { onMetaSave } = this;
- const {
- id,
- type,
- meta,
- size,
- percentQuotaUsed,
- owner,
- created,
- sharedWith,
- questions,
- isInstalled,
- } = userDataset;
- const { display, name, version } = type;
- const isOwner = this.isMyDataset();
-
- return [
- {
- className: classify('Name'),
- attribute: this.props.detailsPageTitle,
- value: (
-
- ),
- },
- {
- attribute: 'Status',
- value: (
-
- ),
- },
- {
- attribute: 'Owner',
- value: isOwner ? 'Me' : owner,
- },
- {
- attribute: 'Description',
- value: (
-
- ),
- },
- { attribute: 'ID', value: id },
- {
- attribute: 'Data type',
- value: (
-
- {display} ({name} {version})
-
- ),
- },
- {
- attribute: 'Summary',
- value: (
-
- ),
- },
- {
- attribute: 'Created',
- value: ,
- },
- { attribute: 'Data set size', value: bytesToHuman(size) },
- !isOwner
- ? null
- : {
- attribute: 'Quota usage',
- value: `${normalizePercentage(percentQuotaUsed)}% of ${bytesToHuman(
- quotaSize
- )}`,
- },
- !isOwner || !sharedWith || !sharedWith.length
- ? null
- : {
- attribute: 'Shared with',
- value: (
-
- {sharedWith.map((share) => (
-
- {share.userDisplayName} <{share.email}>{' '}
-
-
- ))}
-
- ),
- },
- !questions || !questions.length || !isInstalled
- ? null
- : {
- attribute: 'Available searches',
- value: (
-
- {questions.map((questionName) => {
- const q = questionMap[questionName];
- // User dataset searches typically offer changing the dataset through a dropdown
- // Ths dropdown is a param, "biom_dataset" on MicrobiomeDB and "rna_seq_dataset" on genomic sites
- // Hence the regex: /dataset/
- const ps = q.paramNames.filter((paramName) =>
- paramName.match(/dataset/)
- );
- const urlPath = [
- '',
- 'search',
- q.outputRecordClassName,
- q.urlSegment,
- ].join('/');
- const url =
- urlPath +
- (ps.length === 1 ? '?param.' + ps[0] + '=' + id : '');
- return (
-
- {q.displayName}
-
- );
- })}
-
- ),
- },
- ].filter((attr) => attr);
- }
-
- renderHeaderSection() {
- const AllLink = this.renderAllDatasetsLink;
- const AttributeList = this.renderAttributeList;
- const DatasetActions = this.renderDatasetActions;
-
- return (
-
- );
- }
-
- renderAttributeList() {
- const attributes = this.getAttributes();
- return (
-
- {attributes.map(({ attribute, value, className }, index) => (
-
-
- {typeof attribute === 'string' ? (
- {attribute}:
- ) : (
- attribute
- )}
-
-
{value}
-
- ))}
-
- );
- }
-
- renderDatasetActions() {
- const isOwner = this.isMyDataset();
- return (
-
- {!isOwner ? null : (
-
- )}
-
-
- );
- }
-
- renderDetailsSection() {
- const { userDataset } = this.props;
- return (
-
-
-
- {JSON.stringify(userDataset, null, ' ')}
-
-
-
- );
- }
-
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-
- Files Table
-
- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
-
- renderFileSection() {
- const { userDataset, appUrl, dataNoun } = this.props;
- const fileTableState = MesaState.create({
- columns: this.getFileTableColumns({ userDataset, appUrl }),
- rows: userDataset.datafiles,
- });
-
- return (
-
- Data Files
-
-
- Files in {dataNoun.singular}
-
-
-
- );
- }
-
- getFileTableColumns() {
- const { userDataset } = this.props;
- const { id } = userDataset;
- const { wdkService } = this.context;
-
- return [
- {
- key: 'name',
- name: 'File Name',
- renderCell({ row }) {
- const { name } = row;
- return {name}
;
- },
- },
- {
- key: 'size',
- name: 'File Size',
- renderCell({ row }) {
- const { size } = row;
- return bytesToHuman(size);
- },
- },
- {
- key: 'download',
- name: 'Download',
- width: '130px',
- headingStyle: { textAlign: 'center' },
- renderCell({ row }) {
- const { name } = row;
-
- const downloadUrl = !isUserDatasetsCompatibleWdkService(wdkService)
- ? undefined
- : wdkService.getUserDatasetDownloadUrl(id, name);
-
- const downloadAvailable = downloadUrl != null;
-
- return (
-
-
- Download
-
-
- );
- },
- },
- ].filter((column) => column);
- }
-
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-
- Compatible Table
-
- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
-
- renderCompatibilitySection() {
- const { userDataset, config, dataNoun } = this.props;
- const { projectId, displayName } = config;
-
- const compatibilityTableState = MesaState.create({
- columns: this.getCompatibilityTableColumns(userDataset),
- rows: userDataset.dependencies,
- });
-
- const { buildNumber } = config;
- const { isCompatible } = userDataset;
- const isCompatibleProject = userDataset.projects.includes(projectId);
-
- return (
-
-
- Use This {dataNoun.singular} in {displayName}
-
-
-
- Compatibility Information
-
-
-
-
-
-
-
-
-
- {isCompatibleProject && isCompatible ? (
-
- This {dataNoun.singular.toLowerCase()} is compatible with the
- current release, build {buildNumber}, of {projectId} . It is
- installed for use.
-
- ) : (
-
- This {dataNoun.singular.toLowerCase()} is not compatible with the
- current release, build {buildNumber}, of {projectId} . It is
- not installed for use.
-
- )}
-
- );
- }
-
- getCompatibilityTableColumns() {
- const { userDataset } = this.props;
- const { projects } = userDataset;
- return [
- {
- key: 'project',
- name: 'VEuPathDB Website',
- renderCell() {
- return projects.join(', ');
- },
- },
- {
- key: 'resourceDisplayName',
- name: 'Required Resource',
- renderCell({ row }) {
- const { resourceDisplayName } = row;
- return resourceDisplayName;
- },
- },
- {
- key: 'resourceVersion',
- name: 'Required Resource Release',
- },
- {
- key: 'installedVersion',
- name: 'Installed Resource Release',
- renderCell({ row }) {
- const { compatibilityInfo } = row;
- const { currentBuild } = compatibilityInfo ? compatibilityInfo : {};
- return compatibilityInfo === null || currentBuild === null
- ? 'N/A'
- : currentBuild;
- },
- },
- ];
- }
-
- /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-
- General Rendering
-
- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
-
- // This is needed to resolve downstream typescript errors.
- // TypeScript infers that this method returns JSX.Element[].
- // Some classes extending this will return (JSX.Element | null)[].
- // The ReactNode type is better suited, here, since it allows for null values.
- /** @return {import("react").ReactNode[]} */
- getPageSections() {
- return [
- this.renderHeaderSection,
- this.renderCompatibilitySection,
- this.renderFileSection,
- ];
- }
-
- render() {
- const {
- user,
- userDataset,
- shareUserDatasets,
- unshareUserDatasets,
- dataNoun,
- } = this.props;
- const AllDatasetsLink = this.renderAllDatasetsLink;
- if (!userDataset)
- return (
-
-
-
- );
- const isOwner = this.isMyDataset();
- const { sharingModalOpen } = this.state;
-
- return (
-
- {this.getPageSections().map((Section, key) => (
-
- ))}
- {!isOwner || !sharingModalOpen ? null : (
-
- )}
-
- );
- }
-}
-
-UserDatasetDetail.contextType = WdkDependenciesContext;
-
-export default UserDatasetDetail;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.scss b/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.scss
deleted file mode 100644
index e144ccefb0..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Detail/UserDatasetDetail.scss
+++ /dev/null
@@ -1,106 +0,0 @@
-@import '@veupathdb/wdk-client/lib/Core/Style/palette';
-
-.UserDatasetDetail {
- details {
- margin: 30px 0;
- }
- hr {
- height: 0;
- width: 100%;
- border: none;
- margin: 20px 0 0;
- border-bottom: 1px solid rgba(0, 0, 0, 0.15);
- }
- .MesaComponent {
- .DataTable {
- table {
- width: auto;
- }
- }
- }
-
- .AllDatasetsLink {
- display: block;
- padding: 20px 0;
- }
-
- .UserDatasetDetail-SectionTitle {
- margin-bottom: 10px;
- .wdk-Icon {
- color: $blue;
- margin-right: 10px;
- }
- }
-
- .UserDatasetDetail-Actions {
- flex: 0 0 auto;
- display: flex;
- flex-flow: row nowrap;
- justify-content: flex-end;
- gap: 1em;
- }
-
- .UserDatasetDetail-Header {
- display: flex;
- flex-flow: row nowrap;
- align-items: flex-start;
-
- .UserDatasetDetail-Header-Attributes {
- flex: 1 1 auto;
- }
- .UserDatasetDetail-Header-Actions {
- flex: 0 0 auto;
- }
- }
-
- .wdk-SaveableTextEditor {
- fieldset {
- padding: 0 3px;
- }
- fieldset.wdk-SaveableTextEditor-Field {
- flex: 0 1 auto;
- transition: all 0.5s;
- &.wdk-SaveableTextEditor-Field--Editing {
- flex: 1 1 auto;
- }
- }
- fieldset.wdk-SaveableTextEditor-Buttons {
- min-height: 0;
- }
- }
-
- .UserDatasetDetail-AttributeList {
- display: flex;
- flex-direction: column;
- .UserDatasetDetail-AttributeRow {
- flex: 0 0 auto;
- display: flex;
- flex-direction: row;
- align-items: baseline;
- margin: 0.2em 0;
- font-size: 1.4em;
- font-weight: 300;
-
- &.UserDatasetDetail-Name {
- font-weight: 400;
- font-size: 2.8em;
- strong,
- b {
- font-weight: 400;
- }
-
- .wdk-SaveableTextEditor {
- font-style: italic;
- }
- }
-
- .UserDatasetDetail-AttributeName {
- flex: 0 0 auto;
- padding-right: 10px;
- }
- .UserDatasetDetail-AttributeValue {
- flex: 1 1 auto;
- }
- }
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/EmptyState.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/EmptyState.jsx
deleted file mode 100644
index 8e2aae8e49..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/EmptyState.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react';
-import { IconAlt as Icon } from '@veupathdb/wdk-client/lib/Components';
-
-class UserDatasetEmptyState extends React.Component {
- render() {
- const { message } = this.props;
- return (
-
-
- {typeof message === 'string' ?
{message}
: message}
-
- );
- }
-}
-
-export default UserDatasetEmptyState;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.scss b/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.scss
deleted file mode 100644
index 4602fec090..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-.UserDatasetList {
- margin-bottom: 30px;
-}
-.UserDatasetList-EmptyState {
- width: 100%;
- display: flex;
- align-items: center;
- flex-direction: column;
- justify-content: center;
- p {
- font-size: 1.3em;
- font-weight: 300;
- }
- .wdk-Icon.EmptyState-Icon {
- opacity: 0.1;
- font-size: 60px;
- margin: 5px auto 20px;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.tsx
deleted file mode 100644
index 68de183e59..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/List/UserDatasetList.tsx
+++ /dev/null
@@ -1,650 +0,0 @@
-import React from 'react';
-
-import { add } from 'lodash';
-
-import {
- Checkbox,
- IconAlt as Icon,
- Link,
- RealTimeSearchBox as SearchBox,
- SaveableTextEditor,
-} from '@veupathdb/wdk-client/lib/Components';
-import {
- Mesa,
- MesaState,
- Utils as MesaUtils,
-} from '@veupathdb/coreui/lib/components/Mesa';
-import {
- MesaColumn,
- MesaSortObject,
-} from '@veupathdb/coreui/lib/components/Mesa/types';
-import { bytesToHuman } from '@veupathdb/wdk-client/lib/Utils/Converters';
-
-import { User } from '@veupathdb/wdk-client/lib/Utils/WdkUser';
-
-import {
- DataNoun,
- UserDataset,
- UserDatasetMeta,
- UserDatasetShare,
-} from '../../Utils/types';
-
-import UserDatasetEmptyState from '../EmptyState';
-import SharingModal from '../Sharing/UserDatasetSharingModal';
-import UserDatasetStatus from '../UserDatasetStatus';
-import { normalizePercentage, textCell } from '../UserDatasetUtils';
-
-import './UserDatasetList.scss';
-import { DateTime } from '../DateTime';
-
-import { ThemedGrantAccessButton } from '../ThemedGrantAccessButton';
-import { ThemedDeleteButton } from '../ThemedDeleteButton';
-
-interface Props {
- baseUrl: string;
- user: User;
- location: any;
- projectId: string;
- projectName: string;
- userDatasets: UserDataset[];
- filterByProject: boolean;
- shareUserDatasets: (
- userDatasetIds: number[],
- recipientUserIds: number[]
- ) => any;
- unshareUserDatasets: (
- userDatasetIds: number[],
- recipientUserIds: number[]
- ) => any;
- removeUserDataset: (dataset: UserDataset) => any;
- updateUserDatasetDetail: (
- userDataset: UserDataset,
- meta: UserDatasetMeta
- ) => any;
- updateProjectFilter: (filterByProject: boolean) => any;
- quotaSize: number;
- dataNoun: DataNoun;
-}
-
-interface State {
- selectedRows: number[];
- uiState: { sort: MesaSortObject };
- searchTerm: string;
- sharingModalOpen: boolean;
- editingCache: any;
-}
-
-interface MesaDataCellProps {
- row: UserDataset;
- column: MesaColumn;
- rowIndex: number;
- columnIndex: number;
- inline?: boolean;
-}
-
-class UserDatasetList extends React.Component {
- constructor(props: Props) {
- super(props);
- this.state = {
- selectedRows: [],
- uiState: {
- sort: {
- columnKey: 'created',
- direction: 'asc',
- },
- },
- editingCache: {},
- sharingModalOpen: false,
- searchTerm: '',
- };
-
- this.onRowSelect = this.onRowSelect.bind(this);
- this.onRowDeselect = this.onRowDeselect.bind(this);
- this.isRowSelected = this.isRowSelected.bind(this);
- this.onMultipleRowSelect = this.onMultipleRowSelect.bind(this);
- this.onMultipleRowDeselect = this.onMultipleRowDeselect.bind(this);
-
- this.onSort = this.onSort.bind(this);
- this.getColumns = this.getColumns.bind(this);
- this.isMyDataset = this.isMyDataset.bind(this);
- this.getEventHandlers = this.getEventHandlers.bind(this);
- this.filterAndSortRows = this.filterAndSortRows.bind(this);
- this.onSearchTermChange = this.onSearchTermChange.bind(this);
- this.onMetaAttributeSaveFactory =
- this.onMetaAttributeSaveFactory.bind(this);
-
- this.renderOwnerCell = this.renderOwnerCell.bind(this);
- this.renderStatusCell = this.renderStatusCell.bind(this);
-
- this.openSharingModal = this.openSharingModal.bind(this);
- this.closeSharingModal = this.closeSharingModal.bind(this);
- this.toggleProjectScope = this.toggleProjectScope.bind(this);
- }
-
- isRowSelected(row: UserDataset): boolean {
- const id: number = row.id;
- const { selectedRows } = this.state;
- return selectedRows.includes(id);
- }
-
- isMyDataset(dataset: UserDataset): boolean {
- const { user } = this.props;
- return user.id === dataset.ownerUserId;
- }
-
- onSearchTermChange(searchTerm: string) {
- this.setState({ searchTerm });
- }
-
- onMetaAttributeSaveFactory(dataset: UserDataset, attrKey: string) {
- const { meta } = dataset;
- const { updateUserDatasetDetail } = this.props;
- return (value: string) =>
- updateUserDatasetDetail(dataset, { ...meta, [attrKey]: value });
- }
-
- renderSharedWithCell(cellProps: MesaDataCellProps) {
- const dataset: UserDataset = cellProps.row;
- return !dataset.sharedWith || !dataset.sharedWith.length
- ? null
- : dataset.sharedWith.map((share) => share.userDisplayName).join(', ');
- }
-
- renderStatusCell(cellProps: MesaDataCellProps) {
- const userDataset: UserDataset = cellProps.row;
- const { baseUrl, projectId, projectName } = this.props;
- return (
-
- );
- }
-
- renderOwnerCell(cellProps: MesaDataCellProps) {
- const row: UserDataset = cellProps.row;
- const { owner } = row;
- return this.isMyDataset(row) ? (
- Me
- ) : (
- {owner}
- );
- }
-
- getColumns(): any[] {
- const { baseUrl, user } = this.props;
- function isOwner(ownerId: number): boolean {
- return user.id === ownerId;
- }
- return [
- {
- key: 'meta.name',
- sortable: true,
- name: 'Name / ID',
- helpText: '',
- renderCell: (cellProps: MesaDataCellProps) => {
- const dataset: UserDataset = cellProps.row;
- const saveName = this.onMetaAttributeSaveFactory(dataset, 'name');
- return (
- (
-
- {value}
-
- ({dataset.id})
-
- )}
- />
- );
- },
- },
- {
- key: 'summary',
- name: 'Summary',
- style: { maxWidth: '300px' },
- renderCell: (cellProps: MesaDataCellProps) => {
- const dataset: UserDataset = cellProps.row;
- const saveSummary = this.onMetaAttributeSaveFactory(
- dataset,
- 'summary'
- );
- return (
-
-
-
- );
- },
- },
- {
- key: 'type',
- name: 'Type',
- sortable: true,
- renderCell: textCell('type', (datasetType: any) => {
- const display: string = datasetType.display;
- const version: string = datasetType.version;
- return (
-
- {display} ({version})
-
- );
- }),
- },
- {
- key: 'projects',
- sortable: true,
- name: 'VEuPathDB Websites',
- renderCell(cellProps: MesaDataCellProps) {
- const userDataset: UserDataset = cellProps.row;
- const { projects } = userDataset;
- return projects.join(', ');
- },
- },
- {
- key: 'status',
- className: 'StatusColumn',
- name: 'Status',
- style: { textAlign: 'center' },
- renderCell: this.renderStatusCell,
- },
- {
- key: 'owner',
- name: 'Owner',
- sortable: true,
- renderCell: this.renderOwnerCell,
- },
- {
- key: 'sharedWith',
- name: 'Shared With',
- sortable: true,
- renderCell: this.renderSharedWithCell,
- },
- {
- key: 'created',
- name: 'Created',
- sortable: true,
- renderCell: textCell('created', (created: number) => (
-
- )),
- },
- {
- key: 'datafiles',
- name: 'File Count',
- renderCell: textCell('datafiles', (files: any[]) => files.length),
- },
- {
- key: 'size',
- name: 'Size',
- sortable: true,
- renderCell: textCell('size', (size: number) => bytesToHuman(size)),
- },
- {
- key: 'percentQuotaUsed',
- name: 'Quota Usage',
- sortable: true,
- renderCell: textCell('percentQuotaUsed', (percent: number) =>
- percent ? `${normalizePercentage(percent)}%` : null
- ),
- },
- ].filter((column) => column);
- }
-
- onRowSelect(row: UserDataset): void {
- const id: number = row.id;
- const { selectedRows } = this.state;
- if (selectedRows.includes(id)) return;
- const newSelection: number[] = [...selectedRows, id];
- this.setState({ selectedRows: newSelection });
- }
-
- onRowDeselect(row: UserDataset): void {
- const id: number = row.id;
- const { selectedRows } = this.state;
- if (!selectedRows.includes(id)) return;
- const newSelection: number[] = selectedRows.filter(
- (selectedId) => selectedId !== id
- );
- this.setState({ selectedRows: newSelection });
- }
-
- onMultipleRowSelect(rows: UserDataset[]): void {
- if (!rows.length) return;
- const { selectedRows } = this.state;
- const unselectedRows = rows
- .filter((dataset: UserDataset) => !selectedRows.includes(dataset.id))
- .map((dataset: UserDataset) => dataset.id);
- if (!unselectedRows.length) return;
- const newSelection: number[] = [...selectedRows, ...unselectedRows];
- this.setState({ selectedRows: newSelection });
- }
-
- onMultipleRowDeselect(rows: UserDataset[]): void {
- if (!rows.length) return;
- const { selectedRows } = this.state;
- const deselectedIds: number[] = rows.map((row: UserDataset) => row.id);
- const newSelection = selectedRows.filter(
- (id) => !deselectedIds.includes(id)
- );
- this.setState({ selectedRows: newSelection });
- }
-
- onSort(column: MesaColumn, direction: string): void {
- const key: string = column.key;
- const { state } = this;
- const { setSortColumnKey, setSortDirection } = MesaState;
- const updatedState = setSortDirection(
- setSortColumnKey(state, key),
- direction
- );
- this.setState(updatedState);
- }
-
- getEventHandlers() {
- return {
- onSort: this.onSort,
- onRowSelect: this.onRowSelect,
- onRowDeselect: this.onRowDeselect,
- onMultipleRowSelect: this.onMultipleRowSelect,
- onMultipleRowDeselect: this.onMultipleRowDeselect,
- };
- }
-
- getTableActions() {
- const { isMyDataset } = this;
- const { removeUserDataset, dataNoun } = this.props;
- return [
- {
- callback: (rows: UserDataset[]) => {
- this.openSharingModal();
- },
- element: (
- null}
- />
- ),
- selectionRequired: true,
- },
- {
- callback: (userDatasets: UserDataset[]) => {
- const [noun, pronoun] =
- userDatasets.length === 1
- ? [`this ${this.props.dataNoun.singular.toLowerCase()}`, 'it']
- : [`these ${this.props.dataNoun.plural.toLowerCase()}`, 'them'];
-
- const affectedUsers: UserDatasetShare[] = userDatasets.reduce(
- (
- affectedUserList: UserDatasetShare[],
- userDataset: UserDataset
- ) => {
- if (!isMyDataset(userDataset)) return affectedUserList;
- if (!userDataset.sharedWith || userDataset.sharedWith.length)
- return affectedUserList;
- const newlyAffectedUsers = userDataset.sharedWith.filter(
- (sharedWithUser: UserDatasetShare) => {
- return (
- affectedUserList.find(
- (affectedUser) =>
- affectedUser.user === sharedWithUser.user
- ) != null
- );
- }
- );
- return [...affectedUserList, ...newlyAffectedUsers];
- },
- []
- );
-
- if (
- !window.confirm(
- `Are you sure you want to delete ${noun}? ` +
- (affectedUsers.length
- ? `You have shared ${pronoun} with ${affectedUsers} users.`
- : '')
- )
- )
- return;
- userDatasets.forEach((userDataset) => removeUserDataset(userDataset));
- },
- element: (
- null} />
- ),
- selectionRequired: true,
- },
- ];
- }
-
- getTableOptions() {
- const { isRowSelected, toggleProjectScope } = this;
- const { userDatasets, projectName, filterByProject, dataNoun } = this.props;
- const emptyMessage = !userDatasets.length ? (
-
- This page is empty because you do not have any{' '}
- {dataNoun.plural.toLowerCase()}.
-
- ) : filterByProject ? (
-
-
- You have no {projectName} data sets.
-
-
- toggleProjectScope(false)}
- >
- Show All User {dataNoun.plural}
-
-
- ) : (
-
- Your search returned no results.
-
- this.setState({ searchTerm: '' })}
- >
- Clear Search Query
-
-
- );
- return {
- isRowSelected,
- showToolbar: true,
- renderEmptyState() {
- return (
-
-
-
- );
- },
- };
- }
-
- filterAndSortRows(rows: UserDataset[]): UserDataset[] {
- const { searchTerm, uiState } = this.state;
- const { projectName, filterByProject } = this.props;
- const sort: MesaSortObject = uiState.sort;
- if (filterByProject)
- rows = rows.filter((dataset) => dataset.projects.includes(projectName));
- if (searchTerm && searchTerm.length)
- rows = this.filterRowsBySearchTerm([...rows], searchTerm);
- if (sort.columnKey.length) rows = this.sortRowsByColumnKey([...rows], sort);
- return [...rows];
- }
-
- filterRowsBySearchTerm(
- rows: UserDataset[],
- searchTerm?: string
- ): UserDataset[] {
- if (!searchTerm || !searchTerm.length) return rows;
- return rows.filter((dataset: UserDataset) => {
- const searchableRow: string = JSON.stringify(dataset).toLowerCase();
- return searchableRow.indexOf(searchTerm.toLowerCase()) !== -1;
- });
- }
-
- getColumnSortValueMapper(columnKey: string | null) {
- if (columnKey === null) return (data: any) => data;
- switch (columnKey) {
- case 'type':
- return (data: UserDataset, index: number): string =>
- data.type.display.toLowerCase();
- case 'meta.name':
- return (data: UserDataset) => data.meta.name.toLowerCase();
- default:
- return (data: any, index: number) => {
- return typeof data[columnKey] !== 'undefined'
- ? data[columnKey]
- : null;
- };
- }
- }
-
- sortRowsByColumnKey(
- rows: UserDataset[],
- sort: MesaSortObject
- ): UserDataset[] {
- const direction: string = sort.direction;
- const columnKey: string = sort.columnKey;
- const mappedValue = this.getColumnSortValueMapper(columnKey);
- const sorted = [...rows].sort(MesaUtils.sortFactory(mappedValue));
- return direction === 'asc' ? sorted : sorted.reverse();
- }
-
- closeSharingModal() {
- const sharingModalOpen = false;
- this.setState({ sharingModalOpen });
- }
-
- openSharingModal() {
- const sharingModalOpen = true;
- this.setState({ sharingModalOpen });
- }
-
- toggleProjectScope(newValue: boolean) {
- this.props.updateProjectFilter(newValue);
- }
-
- render() {
- const { isRowSelected, toggleProjectScope } = this;
- const {
- userDatasets,
- user,
- projectName,
- shareUserDatasets,
- unshareUserDatasets,
- filterByProject,
- quotaSize,
- dataNoun,
- } = this.props;
- const { uiState, selectedRows, searchTerm, sharingModalOpen } = this.state;
-
- const rows = userDatasets;
- const selectedDatasets = rows.filter(isRowSelected);
- const columns = this.getColumns();
- const actions = this.getTableActions();
- const options = this.getTableOptions();
- const eventHandlers = this.getEventHandlers();
- const filteredRows = this.filterAndSortRows(rows);
-
- const tableState = {
- rows,
- columns,
- options,
- actions,
- filteredRows,
- selectedRows,
- eventHandlers,
- uiState: {
- ...uiState,
- emptinessCulprit: userDatasets.length ? 'search' : null,
- },
- };
-
- const totalSize = userDatasets
- .filter((ud) => ud.ownerUserId === user.id)
- .map((ud) => ud.size)
- .reduce(add, 0);
-
- const totalPercent = totalSize / quotaSize;
-
- const offerProjectToggle = userDatasets.some(({ projects }) =>
- projects.some((project) => project !== projectName)
- );
-
- return (
-
-
-
- {userDatasets.length > 0 && (
-
- {sharingModalOpen && selectedDatasets.length ? (
-
- ) : null}
-
-
- Showing {filteredRows.length} of {rows.length}{' '}
- {rows.length === 1
- ? dataNoun.singular.toLowerCase()
- : dataNoun.plural.toLowerCase()}
-
- {offerProjectToggle && (
-
-
{' '}
-
toggleProjectScope(!filterByProject)}
- style={{ display: 'inline-block' }}
- >
- Only show {dataNoun.plural.toLowerCase()} related to{' '}
- {projectName}
-
-
- )}
-
- {bytesToHuman(totalSize)} (
- {normalizePercentage(totalPercent)}%) of{' '}
- {bytesToHuman(quotaSize)} used
-
-
- )}
-
-
-
- );
- }
-}
-
-export default UserDatasetList;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/NoDatasetsMessage.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/NoDatasetsMessage.tsx
deleted file mode 100644
index 58f08028ce..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/NoDatasetsMessage.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Link } from '@veupathdb/wdk-client/lib/Components';
-
-interface Props {
- baseUrl: string;
- hasDirectUpload: boolean;
- helpRoute: string;
-}
-
-function NoDatasetsMessage({ baseUrl, hasDirectUpload, helpRoute }: Props) {
- return (
-
-
- You do not have any data sets.
-
-
- {hasDirectUpload ? (
-
- Try adding a data set using the{' '}
- New upload section above.
-
- ) : (
-
- To add a data set, go to{' '}
- VEuPathDB Galaxy .
-
- )}
-
- For an overview of the functionality, see the{' '}
- Help page.
-
-
-
- );
-}
-
-export default NoDatasetsMessage;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.jsx
deleted file mode 100644
index ecf3fad558..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.jsx
+++ /dev/null
@@ -1,526 +0,0 @@
-import React from 'react';
-
-import {
- IconAlt as Icon,
- Loading,
- Modal,
- TextBox,
-} from '@veupathdb/wdk-client/lib/Components';
-import { WdkDependenciesContext } from '@veupathdb/wdk-client/lib/Hooks/WdkDependenciesEffect';
-
-import { isUserDatasetsCompatibleWdkService } from '../../Service/UserDatasetWrappers';
-import { DateTime } from '../DateTime';
-
-import './UserDatasetSharingModal.scss';
-
-const isValidEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
-
-class UserDatasetSharingModal extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- recipients: [],
- recipientInput: null,
- processing: false,
- succeeded: null,
- };
- this.renderShareItem = this.renderShareItem.bind(this);
- this.renderShareList = this.renderShareList.bind(this);
- this.renderDatasetList = this.renderDatasetList.bind(this);
- this.renderDatasetItem = this.renderDatasetItem.bind(this);
- this.renderRecipientItem = this.renderRecipientItem.bind(this);
- this.renderRecipientList = this.renderRecipientList.bind(this);
- this.renderRecipientForm = this.renderRecipientForm.bind(this);
-
- this.handleTextChange = this.handleTextChange.bind(this);
- this.handleRecipientAdd = this.handleRecipientAdd.bind(this);
- this.isMyDataset = this.isMyDataset.bind(this);
- this.verifyRecipient = this.verifyRecipient.bind(this);
- this.removeRecipient = this.removeRecipient.bind(this);
- this.getDatasetNoun = this.getDatasetNoun.bind(this);
- this.disqualifyRecipient = this.disqualifyRecipient.bind(this);
-
- this.submitShare = this.submitShare.bind(this);
- this.renderEmptyState = this.renderEmptyState.bind(this);
- this.unshareWithUser = this.unshareWithUser.bind(this);
- this.isRecipientValid = this.isRecipientValid.bind(this);
- this.renderViewContent = this.renderViewContent.bind(this);
- this.isDatasetShareable = this.isDatasetShareable.bind(this);
- this.getValidRecipients = this.getValidRecipients.bind(this);
- this.getShareableDatasets = this.getShareableDatasets.bind(this);
- this.renderSharingButtons = this.renderSharingButtons.bind(this);
- }
-
- isMyDataset(dataset) {
- const { user } = this.props;
- return dataset && dataset.ownerUserId && dataset.ownerUserId === user.id;
- }
-
- handleTextChange(recipientInput = null) {
- this.setState({ recipientInput });
- }
-
- getDatasetNoun() {
- return this.props.datasets.length === 1
- ? `this ${this.props.dataNoun.singular.toLowerCase()}`
- : `these ${this.props.dataNoun.plural.toLowerCase()}`;
- }
-
- verifyRecipient(recipientEmail) {
- if (typeof recipientEmail !== 'string' || !recipientEmail.length)
- throw new TypeError(
- `verifyRecipient: bad email received (${recipientEmail})`
- );
-
- const { wdkService } = this.context;
-
- if (!isUserDatasetsCompatibleWdkService(wdkService)) {
- throw new Error(
- `verifyRecipient: must have a properly configured UserDatasetsCompatibleWdkService`
- );
- }
-
- return wdkService
- .getUserIdsByEmail([recipientEmail])
- .then(({ results }) => {
- const foundUsers = results.find((result) =>
- Object.keys(result).includes(recipientEmail)
- );
-
- if (!results.length || !foundUsers) {
- return this.disqualifyRecipient(
- recipientEmail,
-
- This email is not associated with a VEuPathDB account. {' '}
- {recipientEmail} will not receive {this.getDatasetNoun()}.
-
- );
- }
-
- const uid = foundUsers[recipientEmail];
-
- if (uid === this.props.user.id) {
- return this.disqualifyRecipient(
- recipientEmail,
-
- Sorry, you cannot share a{' '}
- {this.props.dataNoun.singular.toLowerCase()} with yourself.
-
- );
- } else {
- return this.acceptRecipient(recipientEmail, uid);
- }
- })
- .catch((err) => {
- console.error(
- `verifyRecipient: error checking if '${recipientEmail}' exists.`,
- err
- );
- return this.disqualifyRecipient(
- recipientEmail,
- An unknown error occurred.
- );
- });
- }
-
- removeRecipient(recipient) {
- const { email } = recipient;
- const { onClose } = this.props;
- const recipients = [
- ...this.state.recipients.filter((user) => user.email !== email),
- ];
- return recipients.length ? this.setState({ recipients }) : onClose();
- }
-
- acceptRecipient(recipientEmail, id) {
- const { recipients } = this.state;
- const acceptedRecipient = {
- id,
- verified: true,
- email: recipientEmail,
- error: null,
- };
- this.setState({
- recipients: recipients.map((recipient) => {
- return recipient.email === recipientEmail
- ? acceptedRecipient
- : recipient;
- }),
- });
- }
-
- disqualifyRecipient(recipientEmail, reason) {
- const { recipients } = this.state;
- const disqualifiedRecipient = {
- email: recipientEmail,
- verified: false,
- error: reason,
- };
- this.setState({
- recipients: recipients.map((recipient) => {
- return recipient.email === recipientEmail
- ? disqualifiedRecipient
- : recipient;
- }),
- });
- }
-
- handleRecipientAdd() {
- const { recipientInput, recipients } = this.state;
- if (!isValidEmail(recipientInput))
- return alert('Please enter a valid email to share with.');
- if (
- recipients.find(
- (recipient) =>
- recipient.email.toLowerCase() === recipientInput.toLowerCase()
- )
- )
- return alert('This email has already been entered.');
- this.setState(
- {
- recipientInput: null,
- recipients: [
- ...recipients,
- {
- email: recipientInput,
- verified: null,
- id: null,
- },
- ],
- },
- () => this.verifyRecipient(recipientInput)
- );
- }
-
- renderEmptyState() {
- return (
-
- This {this.props.dataNoun.singular.toLowerCase()} hasn't been shared
- yet.
-
- );
- }
-
- unshareWithUser(datasetId, userId) {
- if (
- !window.confirm(
- `Are you sure you want to stop sharing ${this.getDatasetNoun()} with this user?`
- )
- )
- return;
- const { unshareUserDatasets } = this.props;
- if (typeof unshareUserDatasets !== 'function')
- throw new TypeError(
- 'UserDatasetSharingModal:unshareWithUser: expected unshareUserDatasets to be function. Got: ' +
- typeof unshareUserDatasets
- );
- unshareUserDatasets([datasetId], [userId]);
- }
-
- renderShareItem(share, index, userDataset) {
- const { user, time, userDisplayName } = share;
- return (
-
- Shared with {userDisplayName} {' '}
-
- this.unshareWithUser(userDataset.id, user)}
- className="link"
- >
-
-
-
- );
- }
-
- unselectDataset(dataset) {
- const { deselectDataset } = this.props;
- if (typeof deselectDataset !== 'function') return;
- deselectDataset(dataset);
- }
-
- renderDatasetItem(userDataset) {
- const { sharedWith, id, meta } = userDataset;
- const { name } = meta;
- const isOwner = this.isMyDataset(userDataset);
- const { deselectDataset, dataNoun } = this.props;
-
- const EmptyState = this.renderEmptyState;
- const ShareList = this.renderShareList;
-
- return (
-
-
-
-
-
-
-
{name}
- {!isOwner ? (
-
- This {dataNoun.singular.toLowerCase()} has been shared with you.
- Only the owner can share it.
-
- ) : Array.isArray(sharedWith) && sharedWith.length ? (
-
- ) : (
-
- )}
-
-
-
- {typeof deselectDataset !== 'function' ? null : (
- this.unselectDataset(userDataset)}
- className="link removalLink"
- >
-
-
- )}
-
-
- );
- }
-
- renderRecipientItem(recipient, index) {
- const { email, verified, error } = recipient;
- const invalid = verified === false;
- const userIcon =
- verified === null
- ? 'circle-o-notch fa-spin'
- : verified
- ? 'user-circle'
- : 'user-times danger';
-
- return (
-
-
-
-
-
-
{email}
- {invalid ? (
- {error}
- ) : (
- `will receive ${this.getDatasetNoun()}`
- )}
-
-
- this.removeRecipient(recipient)}
- title="Remove this recipient."
- className="link removalLink"
- >
-
-
-
-
- );
- }
-
- renderRecipientList({ recipients }) {
- return !Array.isArray(recipients) || !recipients.length ? (
-
- No recipients.
-
- ) : (
- recipients.map(this.renderRecipientItem)
- );
- }
-
- renderShareList({ userDataset }) {
- const { sharedWith } = userDataset;
- return !Array.isArray(sharedWith) || !sharedWith.length
- ? null
- : sharedWith.map((share, index) =>
- this.renderShareItem(share, index, userDataset)
- );
- }
-
- renderDatasetList({ datasets }) {
- return !Array.isArray(datasets) || !datasets.length
- ? null
- : datasets.map(this.renderDatasetItem);
- }
-
- isRecipientValid(recipient = {}) {
- return recipient.verified && recipient.id !== this.props.user.id;
- }
-
- isDatasetShareable(dataset = {}) {
- return dataset.ownerUserId === this.props.user.id;
- }
-
- submitShare() {
- const recipients = this.getValidRecipients();
- const datasets = this.getShareableDatasets();
- if (!datasets.length) return;
- const { shareUserDatasets } = this.props;
-
- this.setState({ processing: true }, () => {
- shareUserDatasets(
- datasets.map(({ id }) => id),
- recipients.map(({ id }) => id)
- )
- .then((response) => {
- if (response.type !== 'user-datasets/sharing-success') throw response;
- this.setState({ processing: false, succeeded: true });
- })
- .catch((err) => {
- console.error('submitShare: rejected', err);
- this.setState({ processing: false, succeeded: false });
- });
- });
- }
-
- renderRecipientForm() {
- const { recipientInput } = this.state;
- const { handleTextChange, handleRecipientAdd } = this;
-
- return (
-
- );
- }
-
- getValidRecipients() {
- const { recipients } = this.state;
- return recipients.filter(this.isRecipientValid);
- }
-
- getShareableDatasets() {
- const { datasets } = this.props;
- return datasets.filter(this.isDatasetShareable);
- }
-
- renderSharingButtons() {
- const datasets = this.getShareableDatasets();
- const recipients = this.getValidRecipients();
- const { dataNoun } = this.props;
-
- return (
-
-
-
- Grant {recipients.length} Recipient
- {recipients.length === 1 ? '' : 's'} Access to{' '}
- {datasets.length === 1 ? dataNoun.singular : dataNoun.plural}
-
-
- );
- }
-
- renderViewContent() {
- const { recipients, succeeded } = this.state;
- const { datasets, onClose, dataNoun } = this.props;
- const datasetNoun = this.getDatasetNoun();
-
- const DatasetList = this.renderDatasetList;
- const RecipientList = this.renderRecipientList;
- const RecipientForm = this.renderRecipientForm;
- const SharingButtons = this.renderSharingButtons;
- const CloseButton = () => (
- onClose()}>
- Close this window.
-
- );
-
- switch (succeeded) {
- case true:
- return (
-
-
-
Shared successfully.
-
-
- );
- case false:
- return (
-
-
-
Error Sharing {dataNoun.plural}.
-
- An error occurred while sharing your{' '}
- {dataNoun.plural.toLowerCase()}. Please try again.
-
-
-
- );
- default:
- return (
-
-
-
- Share {datasetNoun}:
-
-
-
-
-
- With the following recipients:
-
-
-
-
-
-
- );
- }
- }
-
- render() {
- const { onClose } = this.props;
- const { processing } = this.state;
- const ViewContent = this.renderViewContent;
-
- return (
-
-
- (typeof onClose === 'function' ? onClose() : null)}
- />
-
- {processing ? : }
-
- );
- }
-}
-
-UserDatasetSharingModal.contextType = WdkDependenciesContext;
-
-export default UserDatasetSharingModal;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.scss b/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.scss
deleted file mode 100644
index ace63c4364..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingModal.scss
+++ /dev/null
@@ -1,192 +0,0 @@
-@import '@veupathdb/wdk-client/lib/Core/Style/palette';
-
-.UserDataset-SharingModal {
- background-color: white;
- color: black;
- padding: 30px 20px;
- display: flex;
- flex-flow: column nowrap;
- border-radius: 10px;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
- max-height: 95vh;
- overflow-y: auto;
- min-width: 650px;
- max-width: 100%;
-
- .UserDataset-SharingModal-CloseBar {
- text-align: right;
- }
- .UserDataset-SharingModal-FormView {
- margin-top: -50px;
- display: flex;
- flex-direction: column;
- min-height: 250px;
- }
-
- .UserDataset-SharingModal-RecipientSection,
- .UserDataset-SharingModal-DatasetSection {
- flex: 1;
- }
-
- .NoRecipients {
- font-size: 1.1em;
- text-align: center;
- font-style: italic;
- opacity: 0.5;
- }
- .fa,
- button .fa {
- margin-right: 0;
- }
-
- .UserDatasetSharing-SectionName {
- margin: 10px 0 -5px;
- font-weight: 200;
- }
-
- fieldset {
- margin: 0;
- padding: 0;
- border: 0;
- display: flex;
- align-items: center;
- flex-flow: row nowrap;
- input {
- flex: 1;
- border-radius: 4px;
- padding: 0 5px !important;
- height: 30px;
- border-color: rgba(0, 0, 0, 0.2) !important;
- }
- }
- hr {
- margin: 20px 0 0;
- border: none;
- border-bottom: 1px solid rgba(0, 0, 0, 0.3);
- }
-
- .UserDatasetSharing-RecipientForm {
- display: flex;
- margin: 0;
- align-items: center;
- input {
- flex: 1;
- box-sizing: border-box;
- height: 35px;
- border-color: rgba(0, 0, 0, 0.3) !important;
- border-radius: 5px;
- background: none;
- padding: 10px !important;
- background: none;
- }
- }
-
- .UserDatasetSharing-Dataset,
- .UserDatasetSharing-Recipient {
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 5px;
- display: flex;
- margin-top: 5px;
- align-items: center;
- flex-flow: row nowrap;
-
- .removalLink {
- cursor: pointer;
- position: relative;
- &:hover {
- color: $red;
- }
- z-index: 20;
- .wdk-Icon {
- padding-right: 5px;
- }
- }
-
- &.invalid {
- opacity: 0.4;
- transition: opacity 0.3s;
- &:hover {
- opacity: 1;
- }
- }
-
- .wdk-Icon {
- font-size: 1.5em;
- margin-left: 10px;
- &.fa-times-circle:hover {
- color: $red;
- }
- &.unshareRecipient {
- font-size: 15px;
- margin-left: 5px;
- }
- }
- .UserDatasetSharing-Dataset-Details,
- .UserDatasetSharing-Recipient-Details {
- flex: 1 1 auto;
- h3 {
- line-height: 1em;
- margin-top: -5px;
- }
- .fa-times {
- font-size: 0.8em;
- }
- }
- .UserDatasetSharing-Dataset-Actions,
- .UserDatasetSharing-Recipient-Actions {
- margin-right: 10px;
- }
- .UserDatasetSharing-Dataset-Icon,
- .UserDatasetSharing-Recipient-Icon {
- flex: 0 0 auto;
- }
- .UserDatasetSharing-Dataset-Details,
- .UserDatasetSharing-Dataset-Icon,
- .UserDatasetSharing-Recipient-Details,
- .UserDatasetSharing-Recipient-Icon {
- padding: 10px;
- }
- }
-
- h3 {
- padding: 5px 0;
- }
-
- .UserDatasetSharing-Buttons {
- display: flex;
- margin: 30px 0 0;
- flex-direction: row-reverse;
- button {
- flex: 0 0 auto;
- }
- }
-}
-
-.UserDataset-SharingModal-StatusView {
- width: 100%;
- text-align: center;
- padding: 20px 5px;
- h2 {
- font-weight: 200;
- text-align: center;
- }
- button {
- margin-top: 15px;
- }
- .wdk-Icon {
- font-size: 50px;
- margin-bottom: 20px;
- }
-}
-
-.SharingModal-Close {
- font-size: 1.2em;
- position: relative;
- top: -10px;
- z-index: 100;
- margin-bottom: -50px;
- cursor: pointer;
- &:hover {
- color: $red;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingReducer.ts b/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingReducer.ts
deleted file mode 100644
index 22ae03422d..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/Sharing/UserDatasetSharingReducer.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { differenceWith, unionWith } from 'lodash';
-
-import {
- Action,
- SharingSuccessAction,
- SHARING_SUCCESS,
-} from '../../Actions/UserDatasetsActions';
-
-import { UserDataset, UserDatasetShare } from '../../Utils/types';
-
-type Response = SharingSuccessAction['payload']['response'];
-type ShareOperation = keyof Response;
-
-type State = Record<
- string,
- {
- isLoading: boolean;
- resource?: UserDataset;
- }
->;
-
-const initialState: State = {};
-
-const handleAdd = handleOperation('add');
-const handleDelete = handleOperation('delete');
-
-export default function reduce(
- state: State = initialState,
- action: Action
-): State {
- switch (action.type) {
- case SHARING_SUCCESS:
- return handleAdd(handleDelete(state, action.payload), action.payload);
- default:
- return state;
- }
-}
-
-function handleOperation(operation: ShareOperation) {
- return function (
- state: State,
- payload: SharingSuccessAction['payload']
- ): State {
- const sharesByTargetId = payload.response[operation];
-
- if (sharesByTargetId == null) return state;
-
- return Object.entries(sharesByTargetId).reduce(
- (state, [userDatasetId, shares]) => {
- const entry = state[userDatasetId];
- // entry can be undefined
- if (entry == null || entry.resource == null || shares == null) {
- return state;
- }
- const sharedWith =
- operation === 'add'
- ? unionWith(entry.resource.sharedWith, shares, shareComparator)
- : differenceWith(
- entry.resource.sharedWith,
- shares,
- shareComparator
- );
-
- return {
- ...state,
- [userDatasetId]: {
- ...entry,
- resource: {
- ...entry.resource,
- sharedWith,
- },
- },
- };
- },
- state
- );
- };
-}
-
-function shareComparator(share1: UserDatasetShare, share2: UserDatasetShare) {
- return share1.user === share2.user;
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/ThemedDeleteButton.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/ThemedDeleteButton.tsx
deleted file mode 100644
index 7e52e21a6a..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/ThemedDeleteButton.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useUITheme } from '@veupathdb/coreui/lib/components/theming';
-import { MesaButton, Trash } from '@veupathdb/coreui';
-import { gray, mutedRed } from '@veupathdb/coreui/lib/definitions/colors';
-import { ThemedButtonProps } from './ThemedGrantAccessButton';
-
-export function ThemedDeleteButton({ buttonText, onPress }: ThemedButtonProps) {
- const theme = useUITheme();
- return (
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/ThemedGrantAccessButton.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/ThemedGrantAccessButton.tsx
deleted file mode 100644
index f2a5058413..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/ThemedGrantAccessButton.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useUITheme } from '@veupathdb/coreui/lib/components/theming';
-import { MesaButton, Share } from '@veupathdb/coreui';
-
-export type ThemedButtonProps = {
- buttonText: string;
- onPress: () => null;
-};
-
-export function ThemedGrantAccessButton({
- buttonText,
- onPress,
-}: ThemedButtonProps) {
- const theme = useUITheme();
- return (
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.scss b/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.scss
deleted file mode 100644
index cfdd8f1b2c..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.scss
+++ /dev/null
@@ -1,68 +0,0 @@
-.UploadForm {
- h2 {
- color: #222;
- font-size: 1.5em;
- font-weight: 500;
- margin: 0;
- padding: 22px 0 8px 0;
- }
- .formSection > label {
- font-size: medium;
- }
- #data-set-name {
- min-width: 300px;
- }
- #data-set-summary {
- width: 100%;
- }
- #data-set-description {
- width: 100%;
- height: 8em;
- }
- #data-set-url {
- width: 100%;
- max-width: 51em;
- }
- .formInfo {
- width: 80%;
- text-align: justify;
- }
- .formSection {
- margin: 1em 0;
- }
- select {
- max-width: 450px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- & &--UploadMethodSelector {
- margin-top: 2em;
- margin-bottom: 3.5em;
-
- li > label {
- display: grid;
- grid-template-columns: auto 10em 1fr;
-
- label {
- font-size: medium;
- margin-left: 0.25em;
- }
- }
- }
- & &--UploadMethodSelector &--FixedUploadItem {
- display: grid;
- grid-template-columns: 10em 1fr;
-
- label {
- font-size: medium;
- margin-left: 0.25em;
- }
- }
- & &--UploadMethodField {
- &__disabled {
- opacity: 0.3;
- pointer-events: none;
- }
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.tsx
deleted file mode 100644
index bca1113975..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UploadForm.tsx
+++ /dev/null
@@ -1,550 +0,0 @@
-import React, {
- FormEvent,
- ReactNode,
- useCallback,
- useEffect,
- useMemo,
- useState,
-} from 'react';
-
-import { keyBy } from 'lodash';
-
-import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-import {
- TextBox,
- TextArea,
- FileInput,
- RadioList,
- SingleSelect,
-} from '@veupathdb/wdk-client/lib/Components';
-
-import { makeClassNameHelper } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils';
-import { StrategySummary } from '@veupathdb/wdk-client/lib/Utils/WdkUser';
-
-import { State } from '../StoreModules/UserDatasetUploadStoreModule';
-import {
- CompatibleRecordTypes,
- DatasetUploadTypeConfigEntry,
- NewUserDataset,
- ResultUploadConfig,
-} from '../Utils/types';
-
-import './UploadForm.scss';
-
-const cx = makeClassNameHelper('UploadForm');
-
-interface Props {
- baseUrl: string;
- datasetUploadType: DatasetUploadTypeConfigEntry;
- projectId: string;
- badUploadMessage: State['badUploadMessage'];
- urlParams: Record;
- strategyOptions: StrategySummary[];
- resultUploadConfig?: ResultUploadConfig;
- clearBadUpload: () => void;
- submitForm: (newUserDataset: FormSubmission, redirectTo?: string) => void;
- supportedFileUploadTypes: string[];
- maxSizeBytes?: number;
-}
-
-type DataUploadMode = 'file' | 'url' | 'strategy' | 'step';
-
-type DataUploadSelection =
- | { type: 'file'; file?: File }
- | { type: 'url'; url?: string }
- | {
- type: 'result';
- stepId?: number;
- compatibleRecordTypes?: CompatibleRecordTypes;
- };
-
-type CompleteDataUploadSelection = Required;
-
-interface FormContent {
- name: string;
- summary: string;
- description: string;
- dataUploadSelection: DataUploadSelection;
-}
-
-export type FormValidation = InvalidForm | ValidForm;
-
-export interface InvalidForm {
- valid: false;
- errors: string[];
-}
-
-export interface ValidForm {
- valid: true;
- submission: FormSubmission;
-}
-
-export interface FormSubmission extends Omit {
- dataUploadSelection: CompleteDataUploadSelection;
-}
-
-function UploadForm({
- badUploadMessage,
- baseUrl,
- datasetUploadType,
- projectId,
- urlParams,
- strategyOptions,
- resultUploadConfig,
- clearBadUpload,
- submitForm,
- supportedFileUploadTypes,
- maxSizeBytes,
-}: Props) {
- const strategyOptionsByStrategyId = useMemo(
- () => keyBy(strategyOptions, (option) => option.strategyId),
- [strategyOptions]
- );
-
- const { useFixedUploadMethod: useFixedUploadMethodStr } = urlParams;
-
- const useFixedUploadMethod = useMemo(
- () => useFixedUploadMethodStr === 'true',
- [useFixedUploadMethodStr]
- );
-
- const displayUrlUploadMethod =
- datasetUploadType.formConfig.uploadMethodConfig.url?.offer !== false;
-
- const displayStrategyUploadMethod =
- datasetUploadType.formConfig.uploadMethodConfig.result?.offerStrategyUpload;
-
- const enableStrategyUploadMethod =
- Boolean(displayStrategyUploadMethod) && strategyOptions.length > 0;
-
- const [name, setName] = useState(urlParams.datasetName ?? '');
- const [summary, setSummary] = useState(urlParams.datasetSummary ?? '');
- const [description, setDescription] = useState(
- urlParams.datasetDescription ?? ''
- );
-
- const [dataUploadMode, setDataUploadMode] = useState(
- urlParams.datasetStepId
- ? 'step'
- : urlParams.datasetStrategyRootStepId && enableStrategyUploadMethod
- ? 'strategy'
- : urlParams.datasetUrl && displayUrlUploadMethod
- ? 'url'
- : 'file'
- );
- const [file, setFile] = useState();
- const [url, setUrl] = useState(urlParams.datasetUrl ?? '');
- const initialStepId = useMemo(() => {
- const parsedStepIdParam = Number(urlParams.datasetStepId);
-
- if (isFinite(parsedStepIdParam)) {
- return parsedStepIdParam;
- }
-
- const parsedStrategyIdParam = Number(urlParams.datasetStrategyId);
-
- return !enableStrategyUploadMethod || !isFinite(parsedStrategyIdParam)
- ? strategyOptions[0]?.rootStepId
- : strategyOptionsByStrategyId[parsedStrategyIdParam]?.rootStepId;
- }, [
- urlParams.datasetStepId,
- urlParams.datasetStrategyId,
- strategyOptions,
- strategyOptionsByStrategyId,
- enableStrategyUploadMethod,
- ]);
- const [stepId, setStepId] = useState(initialStepId);
-
- useEffect(() => {
- setStepId(initialStepId);
- }, [initialStepId]);
-
- const [errorMessages, setErrorMessages] = useState([]);
- const [submitting, setSubmitting] = useState(false);
-
- const dataUploadSelection = useMemo((): DataUploadSelection => {
- if (dataUploadMode === 'file') {
- return { type: 'file', file };
- }
-
- if (dataUploadMode === 'url') {
- return { type: 'url', url };
- }
-
- if (resultUploadConfig == null) {
- throw new Error('This data set type does not support result uploads.');
- }
-
- if (stepId == null) {
- return { type: 'result' };
- }
-
- return {
- type: 'result',
- stepId,
- compatibleRecordTypes: resultUploadConfig.compatibleRecordTypes,
- };
- }, [dataUploadMode, file, url, resultUploadConfig, stepId]);
-
- const onSubmit = useCallback(
- (event: FormEvent) => {
- event.preventDefault();
-
- const formValidation = validateForm(
- projectId,
- datasetUploadType,
- enableStrategyUploadMethod,
- {
- name,
- summary,
- description,
- dataUploadSelection,
- }
- );
-
- if (!formValidation.valid) {
- setErrorMessages(formValidation.errors);
- } else {
- setSubmitting(true);
- submitForm(formValidation.submission, `${baseUrl}/recent`);
- }
- },
- [
- baseUrl,
- projectId,
- datasetUploadType,
- enableStrategyUploadMethod,
- name,
- summary,
- description,
- dataUploadSelection,
- submitForm,
- ]
- );
-
- useEffect(() => {
- if (badUploadMessage != null) {
- setErrorMessages([badUploadMessage.message]);
- setSubmitting(false);
- }
- }, [badUploadMessage]);
-
- useEffect(() => {
- return () => {
- clearBadUpload();
- };
- }, [clearBadUpload]);
-
- const nameInputProps = datasetUploadType.formConfig.name?.inputProps;
- const summaryInputProps = datasetUploadType.formConfig.summary?.inputProps;
- const descriptionInputProps =
- datasetUploadType.formConfig.description?.inputProps;
-
- const summaryRequired = summaryInputProps?.required ?? true;
- const descriptionRequired = descriptionInputProps?.required ?? true;
-
- const defaultFileInputField = (
- `.${fileUploadType}`)
- .join(',')}
- required={dataUploadMode === 'file'}
- disabled={dataUploadMode !== 'file' || useFixedUploadMethod}
- maxSizeBytes={maxSizeBytes}
- onChange={(file) => {
- const fileWithSpacedRemovedFromName =
- file && new File([file], file?.name.replace(/\s+/g, '_'), file);
- setFile(fileWithSpacedRemovedFromName ?? undefined);
- }}
- />
- );
- const renderFileInput =
- datasetUploadType.formConfig.uploadMethodConfig.file?.render;
- const fileInputField =
- renderFileInput == null
- ? defaultFileInputField
- : renderFileInput({ fieldNode: defaultFileInputField });
-
- const uploadMethodItems = [
- {
- value: 'file',
- disabled: useFixedUploadMethod,
- display: (
-
-
- Upload File
-
-
- {fileInputField}
-
-
- ),
- },
- ]
- .concat(
- !displayUrlUploadMethod
- ? []
- : [
- {
- value: 'url',
- disabled: useFixedUploadMethod,
- display: (
-
-
- Upload URL
-
-
-
- ),
- },
- ]
- )
- .concat(
- !displayStrategyUploadMethod
- ? []
- : [
- {
- value: 'strategy',
- disabled: !enableStrategyUploadMethod || useFixedUploadMethod,
- display: (
-
-
- Upload Strategy
-
-
- ({
- value: `${option.rootStepId}`,
- display: `${option.name}${!option.isSaved ? '*' : ''}`,
- }))}
- required={dataUploadMode === 'strategy'}
- onChange={(value) => {
- setStepId(Number(value));
- }}
- />
-
-
- ),
- },
- ]
- );
-
- return (
-
- );
-}
-
-interface FieldLabelProps
- extends React.DetailedHTMLProps<
- React.LabelHTMLAttributes,
- HTMLLabelElement
- > {
- children: ReactNode;
- required: boolean;
-}
-
-function FieldLabel({ children, required, ...labelProps }: FieldLabelProps) {
- return (
-
- {children}
- {required ? '*' : null}
-
- );
-}
-
-function ErrorMessage({ errors }: { errors: string[] }) {
- return (
-
-
-
- Could not upload data set
-
- {errors.map((error, ix) => (
-
- {error}
-
- ))}
-
- );
-}
-
-function validateForm(
- projectId: string,
- datasetUploadType: DatasetUploadTypeConfigEntry,
- enableResultUploadMethod: boolean,
- formContent: FormContent
-): FormValidation {
- const { name, summary, description, dataUploadSelection } = formContent;
-
- if (!isCompleteDataUploadSelection(dataUploadSelection)) {
- return {
- valid: false,
- errors: !enableResultUploadMethod
- ? ['Required: data file or URL']
- : ['Required: data file, URL, or strategy'],
- };
- }
-
- if (
- dataUploadSelection.type === 'url' &&
- !isValidUrl(dataUploadSelection.url)
- ) {
- return {
- valid: false,
- errors: ['The provided data URL does not seem valid'],
- };
- }
-
- return {
- valid: true,
- submission: {
- name,
- summary,
- description,
- datasetType: datasetUploadType.type,
- projects: [projectId],
- dataUploadSelection,
- },
- };
-}
-
-function isCompleteDataUploadSelection(
- dataUploadSelection: DataUploadSelection
-): dataUploadSelection is CompleteDataUploadSelection {
- return Object.values(dataUploadSelection).every((value) => value != null);
-}
-
-// https://stackoverflow.com/a/43467144
-function isValidUrl(string: string) {
- try {
- new URL(string);
- } catch (_) {
- return false;
- }
- return true;
-}
-
-export default UploadForm;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetHelp.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetHelp.tsx
deleted file mode 100644
index f327a059aa..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetHelp.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-import { bytesToHuman } from '@veupathdb/wdk-client/lib/Utils/Converters';
-import { Link } from '@veupathdb/wdk-client/lib/Components';
-
-import tutStep2 from './images/tut-step-2.jpg';
-import tutStep3 from './images/tut-step-3.jpg';
-
-interface Props {
- hasDirectUpload: boolean;
- projectName: string;
- quotaSize: number;
- workspaceTitle: string;
-}
-
-function UserDatasetHelp({
- hasDirectUpload,
- projectName,
- quotaSize,
- workspaceTitle,
-}: Props) {
- // FIXME: Perhaps this should be provided via static content?
- return hasDirectUpload ? (
-
-
- Preparing data for upload
- Processing amplicon sequencing reads
- Taxon counts can be obtained from metagenome sequences through commonly
- available tools. Three of the most common ones (
- DADA2 ,{' '}
- QIIME , and{' '}
- Mothur ) have been integrated
- into workflows, and are available as a free online service, through
- NIAID's Nephele project.
- Formatting the input
- You can upload any file that contains processed taxonomic reads in a
- valid BIOM format. See
- this page
- {' '}
- for examples. If your data is in a different format - for example TSV -
- you can use{' '}
-
- conversion tools
- {' '}
- from the Python package biom-format
.Sample Details
- Annotations for samples are not required, but they can be useful for
- subsetting and grouping samples.
-
-
- If possible, try to include rich sample details in your uploaded file,
- to obtain full benefits from our suite of visualisation and analysis
- tools.
-
-
- This{' '}
-
- documentation page
- {' '}
- shows how to add sample details to a BIOM file using a tool{' '}
- biom-add-metadata
.
-
-
- Tips for analysis
- Queries for user data sets work like they do for MicrobiomeDB data sets,
- allowing you to either proceed with the whole data set for analysis, or
- selecting a subset based on sample details or by taxon abundance. You
- can access them from each data set page.
-
- You can extend and modify those searches using the strategies panel on
- the results page. This allows comparing uploaded data sets with each
- other, or with public MicrobiomeDB data sets.
-
-
- All analyses and visualisations available for MicrobiomeDB data sets can
- also be used on the uploaded data set, for example:
-
-
-
- Box and Whisker plot showing most abundant taxa, split by sample
- groups{' '}
-
-
- Alpha diversity trends for samples annotated by continuous variables
- like patient height or age{' '}
-
- Beta diversity plots annotated by sample groups
-
- {' '}
- Report of differentially abundant samples between groups of samples{' '}
-
-
-
-
- ) : (
-
-
-
-
Introduction
-
-
-
-
VEuPathDB Galaxy
-
-
-
- Use the VEuPathDB Export Tools on the left-side navigation,
- at VEuPathDB Galaxy.
-
-
- Prepare your export data set by selecting the files (galaxy data
- sets) in your history.{' '}
-
-
- The data set name, summary and description can be edited later in
- the {workspaceTitle} page.
-
-
- When you're ready, Execute
the export. The process of
- exporting to VEuPathDB may take some time. Progress can be
- monitored from the right-side history panel in Galaxy.
-
-
-
-
-
{workspaceTitle} page
-
-
-
- You can now view, manage, share, and utilize your data set in{' '}
- {projectName} .
-
-
- {workspaceTitle} you've created contribute to a per-user upload
- limit/quota of {bytesToHuman(quotaSize)} .
-
-
- {' '}
- Bigwig files can be sent to JBrowse in the data set's detail page.
- Click the data set name or status icon to see this page.
-
-
-
-
-
- );
-}
-
-export default UserDatasetHelp;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetStatus.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetStatus.tsx
deleted file mode 100644
index 279f5ac174..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetStatus.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import * as React from 'react';
-import { IconAlt as Icon, Link } from '@veupathdb/wdk-client/lib/Components';
-import { Tooltip } from '@veupathdb/coreui';
-
-import { UserDataset } from '../Utils/types';
-
-interface Props {
- baseUrl: string;
- userDataset: UserDataset;
- projectId: string;
- displayName: string;
- linkToDataset: boolean;
- useTooltip: boolean;
-}
-
-const FOUR_HOURS = 4 * (1000 * 60 * 60);
-
-export default function UserDatasetStatus(props: Props) {
- const { baseUrl, userDataset, projectId, displayName } = props;
- const { isInstalled, isCompatible, projects, age } = userDataset;
- const isInstallable = projects.includes(projectId);
- const isPending = isCompatible && age < FOUR_HOURS;
- const isError = isCompatible && !isPending;
- const link = `${baseUrl}/${userDataset.id}`;
- const content = !isInstallable ? (
- This data set is not compatible with {displayName}.
- ) : isInstalled ? (
- This data set is installed and ready for use in {displayName}.
- ) : isPending ? (
-
- This data set is currently being installed in {displayName}. Please check
- again soon.
-
- ) : isError ? (
-
- This data set could not be installed in {displayName} due to a server
- error.
-
- ) : (
-
- This data set was uploaded but could not be installed, as it is not
- compatible with resources in this release of {displayName}.
-
- );
- const faIcon = !isInstallable
- ? 'minus-circle'
- : isInstalled
- ? 'check-circle'
- : isPending
- ? 'clock-o'
- : isError
- ? 'minus-circle'
- : 'exclamation-circle';
- const children = ;
- const visibleContent = props.useTooltip ? (
- {children}
- ) : (
-
- {children} {content}
-
- );
- return props.linkToDataset ? (
- {visibleContent}
- ) : (
- visibleContent
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetUtils.jsx b/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetUtils.jsx
deleted file mode 100644
index a0c6de670c..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetUtils.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-export function makeClassifier(...classNames) {
- return (substyle = null) =>
- classNames
- .map((className) => `${className}${substyle ? '-' + substyle : ''}`)
- .join(' ');
-}
-
-export const quotaSize = 10737418240; // 10 G
-
-export function normalizePercentage(value) {
- return Math.floor(value * 100) / 100;
-}
-
-export function textCell(prop, transform) {
- const getValue =
- typeof transform === 'function' ? transform : (value) => value;
- return ({ row }) => (prop in row ? {getValue(row[prop])} : null);
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasets.scss b/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasets.scss
deleted file mode 100644
index 34d8f0f42f..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasets.scss
+++ /dev/null
@@ -1,101 +0,0 @@
-@import '@veupathdb/wdk-client/lib/Core/Style/palette';
-
-.UserDatasetList,
-.UserDatasetDetail,
-.UserDataset-SharingModal {
- .success {
- color: $green;
- }
- .danger {
- color: $red;
- }
- .faded {
- opacity: 0.5;
- }
-
- input,
- input[type='text'],
- textarea {
- background-color: none;
- }
-
- section {
- width: 100%;
- margin-bottom: 30px;
- }
-
- .MesaComponent {
- .DataTable {
- button {
- .wdk-Icon {
- font-size: 1em;
- }
- }
- }
- td,
- th {
- vertical-align: middle;
- }
- }
-
- .ActionToolbar-Children {
- display: flex;
- flex: 1 0 auto;
- align-items: center;
- }
- .StatusIcon {
- font-size: 20px;
- &.fa-clock-o {
- color: $blue;
- }
- &.fa-minus-circle {
- color: #bbb;
- }
- &.fa-times-circle {
- color: $red;
- }
- &.fa-check-circle {
- color: $green;
- }
- &.fa-exclamation-circle {
- color: $gold;
- }
- }
-}
-
-.UserDataset-NoDatasets {
- font-size: 1.4em;
- text-align: center;
- line-height: 1.5;
- margin: 2em;
-
- &__lead {
- font-size: 1.2em;
- font-weight: 500;
- }
- ul {
- list-style: none;
- }
-}
-
-.UserDataset-Help {
- font-size: 1.2em;
- /* text-align: center;*/
-
- ul {
- display: block;
- max-width: 90%;
- text-align: left;
- }
- .box {
- padding: 10px;
- }
- iframe {
- margin-bottom: 20px;
- }
- img {
- display: block;
- width: 100%;
- margin-bottom: 20px;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetsWorkspace.tsx b/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetsWorkspace.tsx
deleted file mode 100644
index 4be4841845..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Components/UserDatasetsWorkspace.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-import { ReactNode } from 'react';
-
-import { Switch, Redirect } from 'react-router-dom';
-
-import WorkspaceNavigation from '@veupathdb/wdk-client/lib/Components/Workspace/WorkspaceNavigation';
-import WdkRoute from '@veupathdb/wdk-client/lib/Core/WdkRoute';
-
-import UserDatasetAllUploadsController from '../Controllers/UserDatasetAllUploadsController';
-import UserDatasetListController from '../Controllers/UserDatasetListController';
-import UserDatasetNewUploadController from '../Controllers/UserDatasetNewUploadController';
-
-import { DatasetUploadPageConfig, DataNoun } from '../Utils/types';
-
-interface Props {
- baseUrl: string;
- helpRoute: string;
- uploadPageConfig: DatasetUploadPageConfig;
- urlParams: Record;
- workspaceTitle: string;
- helpTabContents?: ReactNode;
- dataNoun: DataNoun;
-}
-
-function UserDatasetsWorkspace(props: Props) {
- const {
- baseUrl,
- helpRoute,
- uploadPageConfig,
- workspaceTitle,
- helpTabContents,
- dataNoun,
- } = props;
-
- return (
-
-
-
- (
-
- )}
- disclaimerProps={{ toDoWhatMessage: 'To view your datasets' }}
- />
- {uploadPageConfig.hasDirectUpload && (
- (
-
- )}
- disclaimerProps={{
- toDoWhatMessage: `To upload your dataset`,
- extraParagraphContent:
- Object.entries(props.urlParams).length === 0 ? undefined : (
-
- Afterwards, you will be taken back to an upload page with
- these details:
-
- {Object.entries(props.urlParams).map((e) => (
-
- {e[0].charAt(0).toUpperCase() +
- e[0].slice(1).replace('_', ' ') +
- ': '}
- {e[1]}
-
- ))}
-
-
- ),
- }}
- />
- )}
- {uploadPageConfig.hasDirectUpload && (
- (
-
- )}
- disclaimerProps={{ toDoWhatMessage: 'To view your recent uploads' }}
- />
- )}
- {helpTabContents != null && (
- <>{helpTabContents}>}
- />
- )}
-
-
-
- );
-}
-
-export default UserDatasetsWorkspace;
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-1.jpg b/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-1.jpg
deleted file mode 100644
index 2e6a3df905..0000000000
Binary files a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-1.jpg and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-2.jpg b/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-2.jpg
deleted file mode 100644
index 80949cd45b..0000000000
Binary files a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-2.jpg and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-3.jpg b/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-3.jpg
deleted file mode 100644
index 3791fdab15..0000000000
Binary files a/packages/libs/user-datasets-legacy/src/lib/Components/images/tut-step-3.jpg and /dev/null differ
diff --git a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetAllUploadsController.tsx b/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetAllUploadsController.tsx
deleted file mode 100644
index f8616d3d17..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetAllUploadsController.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { connect } from 'react-redux';
-
-import PageController from '@veupathdb/wdk-client/lib/Core/Controllers/PageController';
-import { showLoginForm } from '@veupathdb/wdk-client/lib/Actions/UserSessionActions';
-
-import {
- requestUploadMessages,
- cancelCurrentUpload,
- clearMessages,
-} from '../Actions/UserDatasetUploadActions';
-
-import AllUploads from '../Components/AllUploads';
-
-import { StateSlice } from '../StoreModules/types';
-
-const actionCreators = {
- showLoginForm,
- requestUploadMessages,
- cancelCurrentUpload,
- clearMessages,
-};
-
-type StateProps = StateSlice['userDatasetUpload'] &
- Pick;
-
-type DispatchProps = typeof actionCreators;
-type OwnProps = { baseUrl: string };
-type Props = StateProps & { actions: DispatchProps } & OwnProps;
-
-class UserDatasetAllUploadsController extends PageController {
- loadData(prevProps?: Props) {
- if (prevProps != null) {
- return;
- }
- this.props.actions.requestUploadMessages();
- }
-
- getActionCreators() {
- return actionCreators;
- }
-
- isRenderDataLoaded() {
- return (
- this.props.user != null &&
- (this.props.uploads != null ||
- this.props.badAllUploadsActionMessage != null)
- );
- }
-
- getTitle() {
- return 'Recent Uploads';
- }
-
- renderView() {
- return (
-
- );
- }
-}
-
-const enhance = connect(
- (state) => ({
- badAllUploadsActionMessage:
- state.userDatasetUpload.badAllUploadsActionMessage,
- uploads: state.userDatasetUpload.uploads,
- user: state.globalData.user,
- }),
- actionCreators,
- (stateProps, dispatchProps, ownProps) => ({
- ...stateProps,
- actions: dispatchProps,
- ...ownProps,
- })
-);
-
-export default enhance(UserDatasetAllUploadsController);
diff --git a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetDetailController.tsx b/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetDetailController.tsx
deleted file mode 100644
index 7de6c2625d..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetDetailController.tsx
+++ /dev/null
@@ -1,225 +0,0 @@
-import { ComponentType } from 'react';
-
-import { connect } from 'react-redux';
-
-import { keyBy } from 'lodash';
-
-import { showLoginForm } from '@veupathdb/wdk-client/lib/Actions/UserSessionActions';
-import PageController from '@veupathdb/wdk-client/lib/Core/Controllers/PageController';
-import { Question } from '@veupathdb/wdk-client/lib/Utils/WdkModel';
-
-import {
- loadUserDatasetDetail,
- removeUserDataset,
- shareUserDatasets,
- unshareUserDatasets,
- updateUserDatasetDetail,
-} from '../Actions/UserDatasetsActions';
-
-import BigwigDatasetDetail from '../Components/Detail/BigwigDatasetDetail';
-import BiomDatasetDetail from '../Components/Detail/BiomDatasetDetail';
-import RnaSeqDatasetDetail from '../Components/Detail/RnaSeqDatasetDetail';
-import UserDatasetDetail from '../Components/Detail/UserDatasetDetail';
-import EmptyState from '../Components/EmptyState';
-import { quotaSize } from '../Components/UserDatasetUtils';
-
-import { StateSlice } from '../StoreModules/types';
-import { DataNoun } from '../Utils/types';
-
-const ActionCreators = {
- showLoginForm,
- loadUserDatasetDetail,
- updateUserDatasetDetail,
- removeUserDataset,
- shareUserDatasets,
- unshareUserDatasets,
-};
-
-export type UserDatasetDetailProps = any;
-
-type StateProps = StateSlice['userDatasetDetail'] & StateSlice['globalData'];
-type DispatchProps = typeof ActionCreators;
-type OwnProps = {
- baseUrl: string;
- detailsPageTitle: string;
- workspaceTitle: string;
- id: string;
- detailComponentsByTypeName?: Record<
- string,
- ComponentType
- >;
- dataNoun: DataNoun;
-};
-type MergedProps = {
- ownProps: OwnProps;
- dispatchProps: DispatchProps;
- stateProps: StateProps;
-};
-
-/**
- * View Controller for a userDataset record.
- *
- * Note that we are accessing the userDataset from an object keyed by the
- * userDataset's id. This avoids race conditions that arise when ajax requests
- * complete in a different order than they were invoked.
- */
-class UserDatasetDetailController extends PageController {
- getQuestionUrl = (question: Question): string => {
- return `#${question.urlSegment}`;
- };
-
- getTitle() {
- const entry =
- this.props.stateProps.userDatasetsById[this.props.ownProps.id];
- if (entry && entry.resource) {
- return `${this.props.ownProps.detailsPageTitle} ${entry.resource.meta.name}`;
- }
- if (entry && !entry.resource) {
- return `${this.props.ownProps.detailsPageTitle} not found`;
- }
- return `${this.props.ownProps.detailsPageTitle} ...`;
- }
-
- getActionCreators() {
- return ActionCreators;
- }
-
- loadData(prevProps?: this['props']) {
- const idChanged =
- prevProps == null || prevProps.ownProps.id !== this.props.ownProps.id;
- if (idChanged) {
- this.props.dispatchProps.loadUserDatasetDetail(
- Number(this.props.ownProps.id)
- );
- }
- }
-
- isRenderDataLoadError() {
- return (
- this.props.stateProps.loadError != null &&
- this.props.stateProps.loadError.status >= 500
- );
- }
-
- isRenderDataLoaded() {
- const { id } = this.props.ownProps;
- const { userDatasetsById, user, questions, config } = this.props.stateProps;
- const entry = userDatasetsById[id];
- if (user && user.isGuest) return true;
- return entry && !entry.isLoading && user && questions && config
- ? true
- : false;
- }
-
- getDetailView(type: any) {
- const name: string = type && typeof type === 'object' ? type.name : null;
-
- if (this.props.ownProps.detailComponentsByTypeName?.[name] != null) {
- return this.props.ownProps.detailComponentsByTypeName[name];
- }
-
- switch (name) {
- case 'Bigwigs':
- case 'BigwigFiles':
- return BigwigDatasetDetail;
- case 'RnaSeq':
- return RnaSeqDatasetDetail;
- case 'BIOM':
- return BiomDatasetDetail;
- default:
- return UserDatasetDetail;
- }
- }
-
- renderGuestView() {
- return (
- this.props.dispatchProps.showLoginForm()}
- >
- Please log in to access {this.props.ownProps.workspaceTitle}.
-
- }
- />
- );
- }
-
- renderView() {
- const { baseUrl, detailsPageTitle, id, workspaceTitle, dataNoun } =
- this.props.ownProps;
- const {
- updateUserDatasetDetail,
- shareUserDatasets,
- removeUserDataset,
- unshareUserDatasets,
- } = this.props.dispatchProps;
- const {
- userDatasetsById,
- user,
- updateError,
- questions,
- config,
- userDatasetUpdating,
- } = this.props.stateProps;
- const entry = userDatasetsById[id];
- const isOwner = !!(
- user &&
- entry.resource &&
- entry.resource.ownerUserId === user.id
- );
-
- const props = {
- baseUrl,
- user,
- config,
- isOwner,
- location: window.location,
- updateError,
- removeUserDataset,
- quotaSize,
- userDatasetUpdating,
- shareUserDatasets,
- unshareUserDatasets,
- updateUserDatasetDetail,
- userDataset: entry.resource,
- getQuestionUrl: this.getQuestionUrl,
- questionMap: keyBy(questions, 'fullName'),
- workspaceTitle,
- detailsPageTitle,
- dataNoun,
- };
-
- const DetailView = this.getDetailView(
- typeof entry.resource === 'object' ? entry.resource.type : null
- );
- return user && user.isGuest ? (
- this.renderGuestView()
- ) : (
-
- );
- }
-}
-
-const enhance = connect<
- StateProps,
- DispatchProps,
- OwnProps,
- MergedProps,
- StateSlice
->(
- (state) => ({
- ...state.globalData,
- ...state.userDatasetDetail,
- }),
- ActionCreators,
- (stateProps, dispatchProps, ownProps) => ({
- stateProps,
- dispatchProps,
- ownProps,
- })
-);
-
-export default enhance(UserDatasetDetailController);
diff --git a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetListController.tsx b/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetListController.tsx
deleted file mode 100644
index 0183625d99..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetListController.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import { connect } from 'react-redux';
-import { RouteComponentProps, withRouter } from 'react-router-dom';
-
-import { showLoginForm } from '@veupathdb/wdk-client/lib/Actions/UserSessionActions';
-import PageController from '@veupathdb/wdk-client/lib/Core/Controllers/PageController';
-
-import {
- loadUserDatasetList,
- removeUserDataset,
- shareUserDatasets,
- unshareUserDatasets,
- updateProjectFilter,
- updateUserDatasetDetail,
-} from '../Actions/UserDatasetsActions';
-import { requestUploadMessages } from '../Actions/UserDatasetUploadActions';
-
-import UserDatasetList from '../Components/List/UserDatasetList';
-import NoDatasetsMessage from '../Components/NoDatasetsMessage';
-import { quotaSize } from '../Components/UserDatasetUtils';
-
-import { StateSlice } from '../StoreModules/types';
-
-import { DataNoun, UserDataset } from '../Utils/types';
-
-import '../Components/UserDatasets.scss';
-
-const ActionCreators = {
- showLoginForm,
- loadUserDatasetList,
- updateUserDatasetDetail,
- removeUserDataset,
- shareUserDatasets,
- unshareUserDatasets,
- updateProjectFilter,
- requestUploadMessages,
-};
-
-type StateProps = Pick<
- StateSlice,
- 'userDatasetList' | 'userDatasetUpload' | 'globalData'
->;
-type DispatchProps = typeof ActionCreators;
-interface OwnProps extends RouteComponentProps<{}> {
- baseUrl: string;
- hasDirectUpload: boolean;
- helpRoute: string;
- workspaceTitle: string;
- dataNoun: DataNoun;
-}
-type Props = {
- ownProps: OwnProps;
- dispatchProps: DispatchProps;
- stateProps: StateProps;
-};
-
-class UserDatasetListController extends PageController
{
- constructor(props: Props) {
- super(props);
- this.needsUploadMessages = this.needsUploadMessages.bind(this);
- }
- getTitle() {
- return this.props.ownProps.workspaceTitle;
- }
-
- getActionCreators() {
- return ActionCreators;
- }
- needsUploadMessages() {
- const { config } = this.props.stateProps.globalData;
- const { hasDirectUpload } = this.props.ownProps;
- if (config == null) {
- return true;
- }
- const { uploads, badAllUploadsActionMessage } =
- this.props.stateProps.userDatasetUpload;
- return (
- hasDirectUpload && uploads == null && badAllUploadsActionMessage == null
- );
- }
-
- loadData(prevProps?: Props) {
- if (prevProps == null) {
- this.props.dispatchProps.loadUserDatasetList();
- return;
- }
-
- const { config } = this.props.stateProps.globalData;
- if (
- config != null &&
- prevProps.stateProps.userDatasetList.status !==
- this.props.stateProps.userDatasetList.status &&
- this.needsUploadMessages()
- ) {
- this.props.dispatchProps.requestUploadMessages();
- }
- }
-
- isRenderDataLoaded() {
- return (
- this.props.stateProps.userDatasetList.status !== 'not-requested' &&
- this.props.stateProps.userDatasetList.status !== 'loading' &&
- this.props.stateProps.globalData.config != null &&
- this.props.stateProps.globalData.user != null &&
- !this.needsUploadMessages()
- );
- }
-
- isRenderDataLoadError() {
- return this.props.stateProps.userDatasetList.status === 'error';
- }
-
- renderView() {
- const { config, user } = this.props.stateProps.globalData;
-
- if (user == null || config == null) return this.renderDataLoading();
-
- if (this.props.stateProps.userDatasetList.status !== 'complete')
- return null;
-
- const { projectId, displayName: projectName } = config;
-
- const { baseUrl, hasDirectUpload, helpRoute, location, dataNoun } =
- this.props.ownProps;
-
- const {
- userDatasetList: { userDatasets, userDatasetsById, filterByProject },
- userDatasetUpload: { uploads },
- } = this.props.stateProps;
-
- const numOngoingUploads =
- uploads != null ? uploads.filter((upload) => upload.isOngoing).length : 0;
-
- const {
- shareUserDatasets,
- unshareUserDatasets,
- removeUserDataset,
- updateUserDatasetDetail,
- updateProjectFilter,
- } = this.props.dispatchProps;
-
- const listProps = {
- baseUrl,
- user,
- location,
- dataNoun,
- projectId,
- projectName,
- numOngoingUploads,
- quotaSize,
- userDatasets: userDatasets.map(
- (id) => userDatasetsById[id].resource
- ) as UserDataset[],
- filterByProject,
- shareUserDatasets,
- unshareUserDatasets,
- removeUserDataset,
- updateUserDatasetDetail,
- updateProjectFilter,
- };
- const noDatasetsForThisProject =
- userDatasets
- .map((id) => userDatasetsById[id].resource.projects)
- .flat()
- .indexOf(projectId) === -1;
-
- return (
-
-
- {noDatasetsForThisProject ? (
-
- ) : (
-
- )}
-
-
- );
- }
-}
-
-const enhance = connect(
- (state) => ({
- globalData: state.globalData,
- userDatasetList: state.userDatasetList,
- userDatasetUpload: state.userDatasetUpload,
- }),
- ActionCreators,
- (stateProps, dispatchProps, ownProps) => ({
- stateProps,
- dispatchProps,
- ownProps,
- })
-);
-
-export default withRouter(enhance(UserDatasetListController));
diff --git a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetNewUploadController.tsx b/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetNewUploadController.tsx
deleted file mode 100644
index e730d3ce9c..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetNewUploadController.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import { useCallback } from 'react';
-
-import { useDispatch, useSelector } from 'react-redux';
-
-import { Loading } from '@veupathdb/wdk-client/lib/Components';
-import { useWdkService } from '@veupathdb/wdk-client/lib/Hooks/WdkServiceHook';
-import { useSetDocumentTitle } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils';
-import { StrategySummary } from '@veupathdb/wdk-client/lib/Utils/WdkUser';
-
-import {
- clearBadUpload,
- submitUploadForm,
-} from '../Actions/UserDatasetUploadActions';
-
-import UploadForm, { FormSubmission } from '../Components/UploadForm';
-
-import { assertIsUserDatasetUploadCompatibleWdkService } from '../Service/UserDatasetUploadWrappers';
-
-import { StateSlice } from '../StoreModules/types';
-
-import { DatasetUploadTypeConfigEntry } from '../Utils/types';
-
-interface Props {
- baseUrl: string;
- datasetUploadType: DatasetUploadTypeConfigEntry;
- urlParams: Record;
-}
-
-export default function UserDatasetUploadController({
- baseUrl,
- datasetUploadType,
- urlParams,
-}: Props) {
- useSetDocumentTitle(datasetUploadType.uploadTitle);
-
- const projectId = useWdkService(
- (wdkService) => wdkService.getConfig().then((config) => config.projectId),
- []
- );
-
- const supportedFileUploadTypes = useWdkService(
- async (wdkService) => {
- assertIsUserDatasetUploadCompatibleWdkService(wdkService);
-
- if (projectId == null) {
- return undefined;
- }
-
- return wdkService.getSupportedFileUploadTypes(
- projectId,
- datasetUploadType.type
- );
- },
- [projectId, datasetUploadType.type]
- );
-
- const strategyOptions = useWdkService(
- async (wdkService): Promise => {
- if (
- !datasetUploadType.formConfig.uploadMethodConfig.result
- ?.offerStrategyUpload
- ) {
- return [];
- }
-
- const strategies = await wdkService.getStrategies();
- const compatibleRecordTypeNames = new Set(
- Object.keys(
- datasetUploadType.formConfig.uploadMethodConfig.result
- .compatibleRecordTypes
- )
- );
-
- return strategies.filter(
- (strategy) =>
- strategy.recordClassName != null &&
- compatibleRecordTypeNames.has(strategy.recordClassName)
- );
- },
- [
- datasetUploadType.formConfig.uploadMethodConfig.result
- ?.offerStrategyUpload,
- ]
- );
-
- const badUploadMessage = useSelector(
- (stateSlice: StateSlice) => stateSlice.userDatasetUpload.badUploadMessage
- );
-
- const dispatch = useDispatch();
-
- const clearBadUploadMessage = useCallback(() => {
- dispatch(clearBadUpload);
- }, [dispatch]);
-
- const submitForm = useCallback(
- (formSubmission: FormSubmission, redirectTo?: string) => {
- dispatch(submitUploadForm(formSubmission, redirectTo));
- },
- [dispatch]
- );
-
- return projectId == null ||
- supportedFileUploadTypes == null ||
- strategyOptions == null ? (
-
- ) : (
-
-
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetRouter.tsx b/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetRouter.tsx
deleted file mode 100644
index 404ef0be74..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Controllers/UserDatasetRouter.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { ComponentType, ReactNode, useMemo } from 'react';
-
-import { RouteComponentProps, Switch, useRouteMatch } from 'react-router-dom';
-
-import WdkRoute from '@veupathdb/wdk-client/lib/Core/WdkRoute';
-
-import UserDatasetsWorkspace from '../Components/UserDatasetsWorkspace';
-
-import { makeDatasetUploadPageConfig } from '../Utils/upload-config';
-import { DatasetUploadTypeConfig, DataNoun } from '../Utils/types';
-
-import UserDatasetDetailController, {
- UserDatasetDetailProps,
-} from './UserDatasetDetailController';
-
-interface Props {
- availableUploadTypes?: T1[];
- detailsPageTitle: string;
- helpRoute: string;
- uploadTypeConfig: DatasetUploadTypeConfig;
- workspaceTitle: string;
- helpTabContents?: ReactNode;
- detailComponentsByTypeName?: Record<
- string,
- ComponentType
- >;
- dataNoun: DataNoun;
-}
-
-export function UserDatasetRouter({
- availableUploadTypes,
- detailsPageTitle,
- helpRoute,
- uploadTypeConfig,
- workspaceTitle,
- helpTabContents,
- detailComponentsByTypeName,
- dataNoun,
-}: Props) {
- const { path, url } = useRouteMatch();
-
- const uploadPageConfig = useMemo(
- () => makeDatasetUploadPageConfig(availableUploadTypes, uploadTypeConfig),
- [availableUploadTypes, uploadTypeConfig]
- );
-
- return (
-
- ) => {
- return (
-
- );
- }}
- />
-
- ) {
- const urlParams = useMemo(() => {
- const searchParamEntries = new URLSearchParams(
- props.location.search
- ).entries();
-
- return Object.fromEntries(searchParamEntries);
- }, [props.location.search]);
-
- return (
-
- );
- }}
- />
-
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Hooks/project-filter.ts b/packages/libs/user-datasets-legacy/src/lib/Hooks/project-filter.ts
deleted file mode 100644
index 2d9efb42a3..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Hooks/project-filter.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useState } from 'react';
-
-import { useWdkEffect } from '@veupathdb/wdk-client/lib/Service/WdkService';
-import { Task } from '@veupathdb/wdk-client/lib/Utils/Task';
-
-import { FILTER_BY_PROJECT_PREF } from '../Utils/project-filter';
-
-export function useProjectFilter() {
- const [projectFilter, setProjectFilter] =
- useState(undefined);
-
- useWdkEffect(
- (wdkService) =>
- Task.fromPromise(async () => {
- try {
- const currentUserPreferences =
- await wdkService.getCurrentUserPreferences();
-
- return (
- currentUserPreferences.global[FILTER_BY_PROJECT_PREF] !== 'false'
- );
- } catch {
- return false;
- }
- }).run(setProjectFilter),
- []
- );
-
- useWdkEffect(
- (wdkService) => {
- if (projectFilter != null) {
- wdkService.patchSingleUserPreference(
- 'global',
- FILTER_BY_PROJECT_PREF,
- String(projectFilter)
- );
- }
- },
- [projectFilter]
- );
-
- return [projectFilter, setProjectFilter] as const;
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/README.adoc b/packages/libs/user-datasets-legacy/src/lib/README.adoc
deleted file mode 100644
index 6c0ed6ad62..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/README.adoc
+++ /dev/null
@@ -1 +0,0 @@
-Your package-specific code goes here. By default, code in this directory will be accessible in the top-level `lib` directory when you publish to npm.
diff --git a/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetUploadWrappers.ts b/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetUploadWrappers.ts
deleted file mode 100644
index a3790a1651..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetUploadWrappers.ts
+++ /dev/null
@@ -1,339 +0,0 @@
-import { partial } from 'lodash';
-
-import { WdkService } from '@veupathdb/wdk-client/lib/Core';
-import * as Decode from '@veupathdb/wdk-client/lib/Utils/Json';
-import { appendUrlAndRethrow } from '@veupathdb/wdk-client/lib/Service/ServiceUtils';
-
-import { NewUserDataset, UserDatasetUpload } from '../Utils/types';
-
-export interface ServiceConfig {
- datasetImportUrl: string;
- fullWdkServiceUrl: string;
-}
-
-export type UserDatasetUploadCompatibleWdkService = WdkService &
- {
- [Key in keyof UserDatasetUploadServiceWrappers]: ReturnType<
- UserDatasetUploadServiceWrappers[Key]
- >;
- };
-
-type UserDatasetUploadServiceWrappers = ReturnType<
- typeof makeUserDatasetUploadServiceWrappers
->;
-
-/*
- * The authentication method uses a header, not a cookie like in WDK
- */
-async function fetchWithCredentials(
- serviceUrl: string,
- path: string,
- method: string,
- body: any,
- contentType?: string
-) {
- const cookies = Object.fromEntries(
- document.cookie.split('; ').map((entry) => entry.split(/=(.*)/).slice(0, 2))
- );
-
- let authO: Record;
-
- if ('Authorization' in cookies) {
- authO = {
- Authorization: 'Bearer ' + cookies.Authorization,
- };
- } else {
- const authKeyValue = cookies.wdk_check_auth;
- if (authKeyValue == null) {
- throw new Error(
- `Tried to retrieve a non-existent WDK auth key for user.`
- );
- }
- authO = {
- 'Auth-Key': authKeyValue,
- };
- }
-
- const contentTypeO =
- contentType != null ? { 'Content-Type': contentType } : {};
-
- return fetch(serviceUrl + path, {
- method: method.toUpperCase(),
- body: body,
- credentials: 'include',
- headers: new Headers(Object.assign({}, authO, contentTypeO)),
- }).catch(appendUrlAndRethrow(serviceUrl + path));
-}
-
-/*
- * The successful payload is decoded as a , failure is a text that is acceptable to display
- *
- * The service communicates failures in JSON,
- * with 'message' and 'status' as keys
- * See doc: /api#type:err.ErrorResponse
- */
-function fetchDecodedJsonOrThrowMessage(
- serviceUrl: string,
- decoder: Decode.Decoder,
- options: { path: string; method: string; body?: any }
-): Promise {
- let { method, path, body } = options;
- return fetchWithCredentials(
- serviceUrl,
- path,
- method,
- body,
- 'application/json; charset=utf-8'
- )
- .then(async (response) => {
- const responseBody = await response.json();
- if (response.ok) {
- return responseBody;
- }
-
- const message = responseBody.message;
- if (!message) {
- throw new Error('Unexpected error: ' + response.status);
- }
- if (response.status !== 422) {
- throw new Error(
- 'Error type ' + responseBody.status + ': ' + responseBody.message
- );
- }
- let errorLines = [];
- errorLines.push('Validation failed:');
- if (responseBody.errors.general && responseBody.errors.general.length) {
- errorLines.push(...responseBody.errors.general);
- }
- errorLines.push(
- Object.entries(responseBody.errors.byKey).map((p) => p[0] + ': ' + p[1])
- );
-
- throw errorLines.join('\n \n');
- })
- .then((responseBody) => {
- const result = decoder(responseBody);
- if (result.status === 'ok') return result.value;
-
- let errorMessage = `Could not decode resource from ${options.path}:`;
- if (result.context) {
- errorMessage += `\n\n Problem at _${result.context}:`;
- }
- errorMessage += `\n\n Expected ${
- result.expected
- }, but got ${JSON.stringify(result.value)}.`;
- throw errorMessage;
- });
-}
-const statusDetailDecoder = Decode.combine(
- Decode.field('id', Decode.string),
- Decode.field('datasetId', Decode.optional(Decode.number)),
- Decode.field('datasetName', Decode.string),
- Decode.field('summary', Decode.string),
- Decode.field('projects', Decode.arrayOf(Decode.string)),
- Decode.field('status', Decode.string),
- Decode.field(
- 'statusDetails',
- Decode.optional(
- Decode.combine(
- Decode.field(
- 'errors',
- Decode.optional(
- Decode.combine(
- Decode.field('general', Decode.arrayOf(Decode.string)),
- Decode.field(
- 'byKey',
- Decode.objectOf(Decode.arrayOf(Decode.string))
- )
- )
- )
- )
- )
- )
- ),
- Decode.field('stepPercent', Decode.optional(Decode.number)),
- Decode.field('started', Decode.string),
- Decode.field('finished', Decode.optional(Decode.string))
-);
-type UserDatasetUploadWithStatusDetails = Decode.Unpack<
- typeof statusDetailDecoder
->;
-type StatusDetails = UserDatasetUploadWithStatusDetails['statusDetails'];
-
-function getErrorsFromStatusDetails(statusDetails: StatusDetails): string[] {
- let errorLines = [];
-
- if (statusDetails && statusDetails.errors && statusDetails.errors.general) {
- for (let line of statusDetails.errors.general) {
- errorLines.push(line);
- }
- }
- if (statusDetails && statusDetails.errors && statusDetails.errors.byKey) {
- for (let p of Object.entries(statusDetails.errors.byKey)) {
- errorLines.push(p[0] + ': ' + p[1].join('; '));
- }
- }
- return errorLines;
-}
-
-function userDatasetUploadFromStatusDetail(
- upload: UserDatasetUploadWithStatusDetails
-): UserDatasetUpload {
- const { statusDetails, ...restUpload } = upload;
- return {
- ...restUpload,
- errors: getErrorsFromStatusDetails(statusDetails),
- // Could instead use utility functions and an enum for status values?
- isOngoing: !upload.status.match(/success|rejected|errored/),
- isCancellable: !!upload.status.match(/awaiting-upload/),
- isSuccessful: !!upload.status.match(/success/),
- isUserError: !!upload.status.match(/rejected/),
- };
-}
-
-function issueDeleteCommand(
- datasetImportUrl: string,
- jobId: string
-): Promise {
- return fetchWithCredentials(
- datasetImportUrl,
- '/user-datasets/' + jobId,
- 'DELETE',
- undefined,
- 'text/plain;'
- ).then((x) => {});
-}
-
-const DATASET_IMPORT_URL_KEY = 'datasetImportUrl';
-
-export const makeUserDatasetUploadServiceWrappers = ({
- datasetImportUrl,
- fullWdkServiceUrl,
-}: ServiceConfig) => ({
- [DATASET_IMPORT_URL_KEY]: (wdkService: WdkService) => datasetImportUrl,
- addDataset:
- (wdkService: WdkService) =>
- async (newUserDataset: NewUserDataset): Promise => {
- const metaBody = JSON.stringify({
- datasetName: newUserDataset.name,
- datasetType: newUserDataset.datasetType,
- description: newUserDataset.description,
- summary: newUserDataset.summary,
- projects: newUserDataset.projects,
- origin: 'direct-upload',
- });
-
- const fileBody = new FormData();
-
- const { uploadMethod } = newUserDataset;
-
- if (uploadMethod.type === 'file') {
- fileBody.append('uploadMethod', 'file');
- fileBody.append('file', uploadMethod.file);
- } else if (uploadMethod.type === 'url') {
- fileBody.append('uploadMethod', 'url');
- fileBody.append('url', uploadMethod.url);
- } else if (newUserDataset.uploadMethod.type === 'result') {
- const temporaryResultPath = await wdkService.getTemporaryResultPath(
- uploadMethod.stepId,
- uploadMethod.reportName,
- uploadMethod.reportConfig
- );
-
- const temporaryResultUrl = `${fullWdkServiceUrl}${temporaryResultPath}`;
-
- fileBody.append('uploadMethod', 'url');
- fileBody.append('url', temporaryResultUrl);
- } else {
- throw new Error(
- `Tried to upload a dataset via an unrecognized upload method '${uploadMethod.type}'`
- );
- }
-
- return fetchDecodedJsonOrThrowMessage(
- datasetImportUrl,
- Decode.field('jobId', Decode.string),
- {
- path: '/user-datasets',
- method: 'POST',
- body: metaBody,
- }
- ).then(({ jobId }) =>
- fetchWithCredentials(
- datasetImportUrl,
- '/user-datasets/' + jobId,
- 'POST',
- fileBody
- ).then((response) => {
- if (!response.ok) {
- return response.text().then((error) => {
- throw error;
- });
- }
-
- return;
- })
- );
- },
- listStatusDetails: () => (): Promise => {
- return fetchDecodedJsonOrThrowMessage(
- datasetImportUrl,
- Decode.arrayOf(statusDetailDecoder),
- {
- path: '/user-datasets',
- method: 'GET',
- }
- ).then((uploads) => uploads.map(userDatasetUploadFromStatusDetail));
- },
- // Currently only works for jobs whose status is awaiting-upload
- cancelOngoingUpload:
- () =>
- (jobId: string): Promise => {
- return issueDeleteCommand(datasetImportUrl, jobId);
- },
- clearMessages:
- () =>
- (jobIds: string[]): Promise => {
- return Promise.all(
- jobIds.map(partial(issueDeleteCommand, datasetImportUrl))
- ).then((x) => {});
- },
- getSupportedDatasetTypes: () => (projectId: string) => {
- return fetchDecodedJsonOrThrowMessage(
- datasetImportUrl,
- Decode.arrayOf(Decode.string),
- {
- path: `/projects/${projectId}/datasetTypes`,
- method: 'GET',
- }
- );
- },
- getSupportedFileUploadTypes:
- () => (projectId: string, datasetType: string) => {
- return fetchDecodedJsonOrThrowMessage(
- datasetImportUrl,
- Decode.arrayOf(Decode.string),
- {
- path: `/projects/${projectId}/datasetTypes/${datasetType}/fileTypes`,
- method: 'GET',
- }
- );
- },
-});
-
-export function isUserDatasetUploadCompatibleWdkService(
- wdkService: WdkService
-): wdkService is UserDatasetUploadCompatibleWdkService {
- return DATASET_IMPORT_URL_KEY in wdkService;
-}
-
-export function assertIsUserDatasetUploadCompatibleWdkService(
- wdkService: WdkService
-): asserts wdkService is UserDatasetUploadCompatibleWdkService {
- if (!isUserDatasetUploadCompatibleWdkService(wdkService)) {
- throw new Error(MISCONFIGURED_USER_DATASET_UPLOAD_SERVICE_ERROR_MESSAGE);
- }
-}
-
-export const MISCONFIGURED_USER_DATASET_UPLOAD_SERVICE_ERROR_MESSAGE =
- 'In order to use this feature, a UserDatasetUploadCompatibleWdkService must be configured.';
diff --git a/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetWrappers.ts b/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetWrappers.ts
deleted file mode 100644
index a6dac89924..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Service/UserDatasetWrappers.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-import { WdkService } from '@veupathdb/wdk-client/lib/Core';
-import {
- arrayOf,
- number,
- objectOf,
- record,
-} from '@veupathdb/wdk-client/lib/Utils/Json';
-import { EpicDependencies } from '@veupathdb/wdk-client/lib/Core/Store';
-import { ActionThunk } from '@veupathdb/wdk-client/lib/Core/WdkMiddleware';
-
-import { UserDataset, UserDatasetMeta } from '../Utils/types';
-
-export type UserDatasetsCompatibleWdkService = WdkService &
- {
- [Key in keyof UserDatasetsServiceWrappers]: ReturnType<
- UserDatasetsServiceWrappers[Key]
- >;
- };
-
-export type UserDatasetShareResponse = {
- [Key in 'add' | 'delete']: {
- [Key in string]: UserDataset['sharedWith'];
- };
-};
-
-type UserDatasetsServiceWrappers = typeof userDatasetsServiceWrappers;
-
-const userIdsByEmailDecoder = record({
- results: arrayOf(objectOf(number)),
-});
-
-export const userDatasetsServiceWrappers = {
- getCurrentUserDatasets: (wdkService: WdkService) => () =>
- wdkService._fetchJson(
- 'get',
- '/users/current/user-datasets?expandDetails=true'
- ),
- getUserDataset: (wdkService: WdkService) => (id: number) =>
- wdkService._fetchJson(
- 'get',
- `/users/current/user-datasets/${id}`
- ),
- updateUserDataset:
- (wdkService: WdkService) => (id: number, meta: UserDatasetMeta) =>
- wdkService._fetchJson(
- 'put',
- `/users/current/user-datasets/${id}/meta`,
- JSON.stringify(meta)
- ),
- removeUserDataset: (wdkService: WdkService) => (id: number) =>
- wdkService._fetchJson('delete', `/users/current/user-datasets/${id}`),
- editUserDatasetSharing:
- (wdkService: WdkService) =>
- (
- actionName: string,
- userDatasetIds: number[],
- recipientUserIds: number[]
- ) => {
- const acceptableActions = ['add', 'delete'];
- if (!actionName || !acceptableActions.includes(actionName))
- throw new TypeError(
- `editUserDatasetSharing: invalid action name given: "${actionName}"`
- );
- const delta = JSON.stringify({
- [actionName]: userDatasetIds
- .map((id) => `${id}`)
- .reduce((output: object, datasetId: string) => {
- Object.defineProperty(output, datasetId, {
- value: recipientUserIds.map((id) => `${id}`),
- enumerable: true,
- });
- return output;
- }, {}),
- });
- return wdkService._fetchJson(
- 'patch',
- '/users/current/user-dataset-sharing',
- delta
- );
- },
- getUserDatasetDownloadUrl:
- (wdkService: WdkService) => (datasetId: number, filename: string) => {
- if (typeof datasetId !== 'number')
- throw new TypeError(
- `Can't build downloadUrl; invalid datasetId given (${datasetId}) [${typeof datasetId}]`
- );
- if (typeof filename !== 'string')
- throw new TypeError(
- `Can't build downloadUrl; invalid filename given (${filename}) [${typeof filename}]`
- );
-
- return `${wdkService.serviceUrl}/users/current/user-datasets/${datasetId}/user-datafiles/${filename}`;
- },
- getUserIdsByEmail: (wdkService: WdkService) => (emails: string[]) => {
- return wdkService.sendRequest(userIdsByEmailDecoder, {
- path: '/user-id-query',
- method: 'POST',
- body: JSON.stringify({
- emails,
- }),
- });
- },
-};
-
-export function isUserDatasetsCompatibleWdkService(
- wdkService: WdkService
-): wdkService is UserDatasetsCompatibleWdkService {
- return Object.keys(userDatasetsServiceWrappers).every(
- (userDatasetsServiceWrapperKey) =>
- userDatasetsServiceWrapperKey in wdkService
- );
-}
-
-export function assertIsUserDatasetCompatibleWdkService(
- wdkService: WdkService
-): asserts wdkService is UserDatasetsCompatibleWdkService {
- if (!isUserDatasetsCompatibleWdkService(wdkService)) {
- throw new Error(MISCONFIGURED_USER_DATASET_SERVICE_ERROR_MESSAGE);
- }
-}
-
-export const MISCONFIGURED_USER_DATASET_SERVICE_ERROR_MESSAGE =
- 'In order to use this feature, a UserDatasetsCompatibleWdkService must be configured.';
-
-export interface UserDatasetCompatibleEpicDependencies
- extends EpicDependencies {
- wdkService: UserDatasetsCompatibleWdkService;
-}
-
-export function validateUserDatasetCompatibleThunk(
- thunk: ActionThunk
-): ActionThunk {
- return (wdkDependencies) => {
- assertIsUserDatasetCompatibleWdkService(wdkDependencies.wdkService);
-
- return thunk(wdkDependencies);
- };
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Service/index.ts b/packages/libs/user-datasets-legacy/src/lib/Service/index.ts
deleted file mode 100644
index bad80d5edc..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Service/index.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { mapValues } from 'lodash';
-
-import { WdkService } from '@veupathdb/wdk-client/lib/Core';
-
-import { userDatasetsServiceWrappers } from './UserDatasetWrappers';
-import {
- ServiceConfig as UserDatasetUploadServiceConfig,
- makeUserDatasetUploadServiceWrappers,
-} from './UserDatasetUploadWrappers';
-
-export function wrapWdkService(
- serviceConfig: UserDatasetUploadServiceConfig | undefined,
- wdkService: WdkService
-) {
- const wrappersToInclude =
- serviceConfig == null
- ? userDatasetsServiceWrappers
- : {
- ...userDatasetsServiceWrappers,
- ...makeUserDatasetUploadServiceWrappers(serviceConfig),
- };
-
- return {
- ...wdkService,
- ...mapValues(wrappersToInclude, (wdkServiceWrapper) =>
- wdkServiceWrapper(wdkService)
- ),
- };
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetDetailStoreModule.ts b/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetDetailStoreModule.ts
deleted file mode 100644
index b964ca7c3b..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetDetailStoreModule.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import { ServiceError } from '@veupathdb/wdk-client/lib/Service/ServiceError';
-import {
- Action,
- DETAIL_LOADING,
- DETAIL_RECEIVED,
- DETAIL_ERROR,
- DETAIL_UPDATING,
- DETAIL_UPDATE_SUCCESS,
- DETAIL_UPDATE_ERROR,
- DETAIL_REMOVING,
- DETAIL_REMOVE_SUCCESS,
- DETAIL_REMOVE_ERROR,
- SHARING_SUCCESS,
-} from '../Actions/UserDatasetsActions';
-
-import sharingReducer from '../Components/Sharing/UserDatasetSharingReducer';
-
-import { UserDataset } from '../Utils/types';
-
-export const key = 'userDatasetDetail';
-
-/**
- * If isLoading is false, and resource is undefined,
- * then assume the user dataset does not exist
- */
-export type UserDatasetEntry = {
- isLoading: boolean;
- resource?: UserDataset;
-};
-
-export interface State {
- userDatasetsById: { [key: string]: UserDatasetEntry };
- userDatasetUpdating: boolean;
- userDatasetLoading: boolean;
- userDatasetRemoving: boolean;
- loadError?: ServiceError;
- updateError?: ServiceError;
- removalError?: ServiceError;
-}
-
-const initialState: State = {
- userDatasetsById: {},
- userDatasetLoading: false,
- userDatasetUpdating: false,
- userDatasetRemoving: false,
-};
-
-/**
- * Stores a map of userDatasets by id. By not storing the current userDataset,
- * we avoid race conditions where the DATASET_DETAIL_RECEIVED actions are
- * dispatched in a different order than the corresponding action creators are
- * invoked.
- */
-export function reduce(state: State = initialState, action: Action): State {
- switch (action.type) {
- case DETAIL_LOADING:
- return {
- ...state,
- userDatasetsById: {
- ...state.userDatasetsById,
- [action.payload.id]: {
- isLoading: true,
- },
- },
- };
-
- case DETAIL_RECEIVED:
- return {
- ...state,
- userDatasetLoading: false,
- userDatasetsById: {
- ...state.userDatasetsById,
- [action.payload.id]: {
- isLoading: false,
- resource: action.payload.userDataset,
- },
- },
- };
-
- case DETAIL_ERROR:
- return {
- ...state,
- userDatasetLoading: false,
- loadError: action.payload.error,
- };
-
- case DETAIL_UPDATING:
- return {
- ...state,
- userDatasetUpdating: true,
- updateError: undefined,
- };
-
- case DETAIL_UPDATE_SUCCESS:
- return {
- ...state,
- userDatasetUpdating: false,
- userDatasetsById: {
- ...state.userDatasetsById,
- [action.payload.userDataset.id]: {
- isLoading: false,
- resource: action.payload.userDataset,
- },
- },
- };
-
- case DETAIL_UPDATE_ERROR:
- return {
- ...state,
- userDatasetUpdating: false,
- updateError: action.payload.error,
- };
-
- case DETAIL_REMOVING:
- return {
- ...state,
- userDatasetRemoving: true,
- };
-
- case DETAIL_REMOVE_SUCCESS:
- return {
- ...state,
- userDatasetRemoving: false,
- removalError: undefined,
- };
-
- case DETAIL_REMOVE_ERROR:
- return {
- ...state,
- userDatasetRemoving: false,
- removalError: action.payload.error,
- };
-
- case SHARING_SUCCESS:
- return {
- ...state,
- userDatasetsById: sharingReducer(state.userDatasetsById, action),
- };
-
- default:
- return state;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetListStoreModule.ts b/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetListStoreModule.ts
deleted file mode 100644
index 850b746194..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetListStoreModule.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { difference } from 'lodash';
-
-import {
- Action,
- LIST_LOADING,
- LIST_RECEIVED,
- LIST_ERROR_RECEIVED,
- DETAIL_UPDATE_SUCCESS,
- DETAIL_REMOVE_SUCCESS,
- SHARING_SUCCESS,
- PROJECT_FILTER,
-} from '../Actions/UserDatasetsActions';
-
-import sharingReducer from '../Components/Sharing/UserDatasetSharingReducer';
-
-import { UserDataset } from '../Utils/types';
-
-export const key = 'userDatasetList';
-
-type InitialState = {
- status: 'not-requested';
-};
-
-type LoadingState = {
- status: 'loading';
-};
-
-type ErrorState = {
- status: 'error';
- loadError: Error;
-};
-
-type ForbiddenState = {
- status: 'forbidden';
- loadError: Error;
-};
-
-type CompleteState = {
- status: 'complete';
- userDatasets: number[];
- userDatasetsById: Record;
- filterByProject: boolean;
-};
-
-export type State =
- | InitialState
- | LoadingState
- | ErrorState
- | ForbiddenState
- | CompleteState;
-
-const initialState: State = {
- status: 'not-requested',
-};
-
-export function reduce(state: State = initialState, action: Action): State {
- switch (action.type) {
- case LIST_LOADING:
- return {
- status: 'loading',
- };
-
- case LIST_RECEIVED:
- return {
- status: 'complete',
- filterByProject: action.payload.filterByProject,
- userDatasets: action.payload.userDatasets.map((ud) => ud.id),
- userDatasetsById: action.payload.userDatasets.reduce(
- (uds, ud) =>
- Object.assign(uds, { [ud.id]: { loading: false, resource: ud } }),
- {} as CompleteState['userDatasetsById']
- ),
- };
-
- case LIST_ERROR_RECEIVED:
- return action.payload.error.status === 403
- ? {
- status: 'forbidden',
- loadError: action.payload.error,
- }
- : {
- status: 'error',
- loadError: action.payload.error,
- };
-
- case DETAIL_UPDATE_SUCCESS:
- return state.status === 'complete'
- ? {
- ...state,
- userDatasetsById: {
- ...state.userDatasetsById,
- [action.payload.userDataset.id]: {
- isLoading: false,
- resource: action.payload.userDataset,
- },
- },
- }
- : state;
-
- case DETAIL_REMOVE_SUCCESS:
- return state.status === 'complete'
- ? {
- ...state,
- userDatasets: difference(state.userDatasets, [
- action.payload.userDataset.id,
- ]),
- userDatasetsById: {
- ...state.userDatasetsById,
- [action.payload.userDataset.id]: undefined,
- },
- }
- : state;
-
- case SHARING_SUCCESS: {
- if (state.status === 'complete') {
- const userDatasetsById = sharingReducer(
- state.userDatasetsById,
- action
- ) as CompleteState['userDatasetsById'];
- return {
- ...state,
- userDatasetsById,
- };
- }
- return state;
- }
-
- case PROJECT_FILTER: {
- if (state.status === 'complete') {
- return {
- ...state,
- filterByProject: action.payload.filterByProject,
- } as CompleteState;
- }
- return state;
- }
-
- default:
- return state;
- }
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetUploadStoreModule.ts b/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetUploadStoreModule.ts
deleted file mode 100644
index e2db4dee22..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/StoreModules/UserDatasetUploadStoreModule.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import {
- ActionsObservable,
- combineEpics,
- StateObservable,
-} from 'redux-observable';
-import { Observable } from 'rxjs';
-import { filter, mergeMap } from 'rxjs/operators';
-
-import { EpicDependencies } from '@veupathdb/wdk-client/lib/Core/Store';
-
-import {
- Action,
- submitUploadForm,
- receiveBadUpload,
- requestUploadMessages,
- receiveUploadMessages,
- cancelCurrentUpload,
- clearMessages,
- receiveBadUploadHistoryAction,
- clearBadUpload,
-} from '../Actions/UserDatasetUploadActions';
-
-import { assertIsUserDatasetUploadCompatibleWdkService } from '../Service/UserDatasetUploadWrappers';
-
-import { StateSlice } from './types';
-
-import { UserDatasetUpload } from '../Utils/types';
-import { uploadUserDataset } from '../Utils/upload-user-dataset';
-
-export const key = 'userDatasetUpload';
-
-export type State = {
- uploads?: Array;
- badUploadMessage?: { message: string; timestamp: number };
- badAllUploadsActionMessage?: { message: string; timestamp: number };
-};
-export function reduce(state: State = {}, action: Action): State {
- switch (action.type) {
- case receiveBadUpload.type:
- return { ...state, badUploadMessage: action.payload };
- case clearBadUpload.type:
- return { ...state, badUploadMessage: undefined };
- case receiveUploadMessages.type:
- return { ...state, uploads: action.payload.uploads };
- case receiveBadUploadHistoryAction.type:
- return { ...state, badAllUploadsActionMessage: action.payload };
- default:
- return state;
- }
-}
-
-export const observe = combineEpics(
- observeSubmitUploadForm,
- observeRequestUploadMessages,
- observeCancelCurrentUpload,
- observeClearMessages
-);
-
-function observeSubmitUploadForm(
- action$: ActionsObservable,
- state$: StateObservable,
- dependencies: EpicDependencies
-): Observable {
- return action$.pipe(
- filter(submitUploadForm.isOfType),
- mergeMap(async (action) => {
- try {
- await uploadUserDataset(
- dependencies.wdkService,
- action.payload.formSubmission
- );
-
- if (action.payload.redirectTo != null) {
- dependencies.transitioner.transitionToInternalPage(
- action.payload.redirectTo
- );
- }
- return requestUploadMessages();
- } catch (err) {
- return receiveBadUpload(String(err) ?? 'Failed to upload dataset');
- }
- })
- );
-}
-
-function observeRequestUploadMessages(
- action$: ActionsObservable,
- state$: StateObservable,
- dependencies: EpicDependencies
-): Observable {
- return action$.pipe(
- filter(requestUploadMessages.isOfType),
- mergeMap(async (action) => {
- assertIsUserDatasetUploadCompatibleWdkService(dependencies.wdkService);
-
- try {
- const uploads = await dependencies.wdkService.listStatusDetails();
- return receiveUploadMessages(uploads);
- } catch (err) {
- return receiveBadUploadHistoryAction(
- 'Could not retrieve upload history\n' + err
- );
- }
- })
- );
-}
-
-function observeCancelCurrentUpload(
- action$: ActionsObservable,
- state$: StateObservable,
- dependencies: EpicDependencies
-): Observable {
- return action$.pipe(
- filter(cancelCurrentUpload.isOfType),
- mergeMap(async (action) => {
- assertIsUserDatasetUploadCompatibleWdkService(dependencies.wdkService);
-
- try {
- await dependencies.wdkService.cancelOngoingUpload(action.payload.id);
- return requestUploadMessages();
- } catch (err) {
- return receiveBadUploadHistoryAction(
- 'Could not cancel current upload\n' + err
- );
- }
- })
- );
-}
-
-function observeClearMessages(
- action$: ActionsObservable,
- state$: StateObservable,
- dependencies: EpicDependencies
-): Observable {
- return action$.pipe(
- filter(clearMessages.isOfType),
- mergeMap(async (action) => {
- assertIsUserDatasetUploadCompatibleWdkService(dependencies.wdkService);
-
- try {
- await dependencies.wdkService.clearMessages(action.payload.ids);
- return requestUploadMessages();
- } catch (err) {
- return receiveBadUploadHistoryAction(
- 'Could not clear messages\n' + err
- );
- }
- })
- );
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/StoreModules/index.ts b/packages/libs/user-datasets-legacy/src/lib/StoreModules/index.ts
deleted file mode 100644
index 85b11fb94d..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/StoreModules/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as userDatasetDetail from './UserDatasetDetailStoreModule';
-import * as userDatasetList from './UserDatasetListStoreModule';
-import * as userDatasetUpload from './UserDatasetUploadStoreModule';
-
-type WdkStoreModules =
- typeof import('@veupathdb/wdk-client/lib/StoreModules').default;
-
-export function wrapStoreModules(storeModules: WdkStoreModules) {
- return {
- ...storeModules,
- userDatasetDetail,
- userDatasetList,
- userDatasetUpload,
- };
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/StoreModules/types.ts b/packages/libs/user-datasets-legacy/src/lib/StoreModules/types.ts
deleted file mode 100644
index 168fd7b4a2..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/StoreModules/types.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { RootState } from '@veupathdb/wdk-client/lib/Core/State/Types';
-
-import { State as UserDatasetDetailState } from './UserDatasetDetailStoreModule';
-import { State as UserDatasetListState } from './UserDatasetListStoreModule';
-import { State as UserDatasetUploadState } from './UserDatasetUploadStoreModule';
-
-export interface StateSlice extends Pick {
- userDatasetDetail: UserDatasetDetailState;
- userDatasetList: UserDatasetListState;
- userDatasetUpload: UserDatasetUploadState;
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Utils/project-filter.ts b/packages/libs/user-datasets-legacy/src/lib/Utils/project-filter.ts
deleted file mode 100644
index 14793f50c2..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Utils/project-filter.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const FILTER_BY_PROJECT_PREF = 'userDatasets.filterByProject';
diff --git a/packages/libs/user-datasets-legacy/src/lib/Utils/types.ts b/packages/libs/user-datasets-legacy/src/lib/Utils/types.ts
deleted file mode 100644
index be54be1b07..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Utils/types.ts
+++ /dev/null
@@ -1,149 +0,0 @@
-import React, { ReactNode } from 'react';
-
-export interface UserDatasetMeta {
- description: string;
- name: string;
- summary: string;
-}
-
-export interface UserDatasetShare {
- time: number;
- user: number;
- email: string;
- userDisplayName: string;
-}
-
-export interface UserDataset {
- created: number;
- age: number;
- isInstalled: boolean;
- isCompatible: boolean;
- dependencies: Array<{
- resourceDisplayName: string;
- resourceIdentifier: string;
- resourceVersion: string;
- }>;
- datafiles: Array<{
- name: string;
- size: number;
- }>;
- projects: string[];
- id: number;
- meta: UserDatasetMeta;
- modified: number;
- owner: string;
- ownerUserId: number;
- percentQuotaUsed: number;
- sharedWith: UserDatasetShare[] | undefined;
- questions: string[];
- size: number;
- type: {
- name: string;
- display: string;
- version: string;
- };
- uploaded: number;
-}
-
-export interface UserDatasetUpload {
- id: string;
- datasetId?: number;
- datasetName: string;
- summary?: string;
- description?: string;
- projects: string[];
- status: string;
- errors: string[];
- stepPercent?: number;
- started: string;
- finished?: string;
- isOngoing: boolean;
- isCancellable: boolean;
- isSuccessful: boolean;
- isUserError: boolean;
-}
-
-export type DatasetUploadTypeConfig = {
- [K in T]: DatasetUploadTypeConfigEntry;
-};
-
-export interface DatasetUploadTypeConfigEntry {
- type: T;
- uploadTitle: string;
- formConfig: {
- name?: {
- inputProps: Partial>;
- };
- summary?: {
- inputProps: Partial>;
- };
- description?: {
- inputProps: Partial>;
- };
- uploadMethodConfig: {
- file?: FileUploadConfig;
- url?: UrlUploadConfig;
- result?: ResultUploadConfig;
- };
- renderInfo?: () => ReactNode;
- };
-}
-
-export interface FileUploadConfig {
- render?: (props: { fieldNode: ReactNode }) => ReactNode;
- maxSizeBytes?: number;
-}
-
-export interface UrlUploadConfig {
- offer: boolean;
-}
-
-export interface ResultUploadConfig {
- offerStrategyUpload: boolean;
- compatibleRecordTypes: CompatibleRecordTypes;
-}
-
-export type CompatibleRecordTypes = Record<
- string,
- { reportName: string; reportConfig: unknown }
->;
-
-export type DatasetUploadPageConfig<
- T1 extends string = string,
- T2 extends string = string
-> =
- | { hasDirectUpload: false }
- | {
- hasDirectUpload: true;
- availableUploadTypes: T1[];
- uploadTypeConfig: DatasetUploadTypeConfig;
- };
-
-export interface NewUserDataset extends UserDatasetMeta {
- datasetType: string; // In prototype, the only value is "biom" - will eventually be an enum
- projects: string[];
- uploadMethod:
- | {
- type: 'file';
- file: File;
- }
- | {
- type: 'url';
- url: string;
- }
- | {
- type: 'result';
- stepId: number;
- reportName: string;
- reportConfig: unknown;
- };
-}
-
-/**
- * In EDA, data is referred to as "Study" or "Studies"
- * In genomics, data is referred to as "Data Set" or "Data Sets"
- */
-export type DataNoun = {
- singular: string;
- plural: string;
-};
diff --git a/packages/libs/user-datasets-legacy/src/lib/Utils/upload-config.tsx b/packages/libs/user-datasets-legacy/src/lib/Utils/upload-config.tsx
deleted file mode 100644
index b505822f7d..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Utils/upload-config.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import { intersection } from 'lodash';
-
-import { DatasetUploadPageConfig, DatasetUploadTypeConfig } from './types';
-
-type ImplementedUploadTypes = 'biom' | 'gene-list' | 'isasimple';
-
-export const uploadTypeConfig: DatasetUploadTypeConfig =
- {
- biom: {
- type: 'biom',
- uploadTitle: 'Upload My Data Set',
- formConfig: {
- renderInfo: () => (
-
- We accept any file in the{' '}
- BIOM format , either JSON-based
- (BIOM 1.0) or HDF5 (BIOM 2.0+). The maximum allowed file size is
- 1GB.
-
-
- If possible, try including taxonomic information and rich sample
- details in your file. This will allow you to select groups of
- samples and create meaningful comparisons at a desired aggregation
- level, using our filtering and visualisation tools.
-
- ),
- uploadMethodConfig: {
- file: {
- maxSizeBytes: 1e7, // 10 megabytes
- render: ({ fieldNode }) => (
- <>
- {fieldNode}
-
- File must be 10 MB or smaller.
-
- >
- ),
- },
- },
- },
- },
- 'gene-list': {
- type: 'gene-list',
- uploadTitle: 'Upload My Gene List',
- formConfig: {
- uploadMethodConfig: {
- result: {
- offerStrategyUpload: false,
- compatibleRecordTypes: {
- transcript: {
- reportName: 'attributesTabular',
- reportConfig: {
- attributes: ['primary_key'],
- includeHeader: false,
- attachmentType: 'plain',
- applyFilter: true,
- },
- },
- },
- },
- },
- },
- },
- isasimple: {
- type: 'isasimple',
- uploadTitle: 'Upload My Study',
- formConfig: {
- summary: {
- inputProps: {
- placeholder: 'brief summary of the data set in 1-2 sentences',
- },
- },
- description: {
- inputProps: {
- required: false,
- placeholder:
- 'optional longer description of the data set including background, study objectives, methodology, etc.',
- },
- },
- uploadMethodConfig: {
- file: {
- render: ({ fieldNode }) => (
- <>
- {fieldNode}
-
- File must be a .csv, .tsv, or tab-delimited .txt file
-
- >
- ),
- },
- url: {
- offer: false,
- },
- },
- },
- },
- };
-
-export function makeDatasetUploadPageConfig<
- T1 extends string,
- T2 extends string
->(
- availableUploadTypes: T1[] = [],
- uploadTypeConfig: DatasetUploadTypeConfig
-): DatasetUploadPageConfig {
- const restrictedUploadTypes = intersection(
- availableUploadTypes,
- Object.keys(uploadTypeConfig)
- ) as (T1 & T2)[];
-
- return restrictedUploadTypes.length === 0
- ? { hasDirectUpload: false }
- : {
- hasDirectUpload: true,
- availableUploadTypes: restrictedUploadTypes,
- uploadTypeConfig,
- };
-}
diff --git a/packages/libs/user-datasets-legacy/src/lib/Utils/upload-user-dataset.tsx b/packages/libs/user-datasets-legacy/src/lib/Utils/upload-user-dataset.tsx
deleted file mode 100644
index bcec5eacb3..0000000000
--- a/packages/libs/user-datasets-legacy/src/lib/Utils/upload-user-dataset.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { WdkService } from '@veupathdb/wdk-client/lib/Core';
-
-import { FormSubmission } from '../Components/UploadForm';
-import { assertIsUserDatasetUploadCompatibleWdkService } from '../Service/UserDatasetUploadWrappers';
-
-import { NewUserDataset } from './types';
-
-export async function uploadUserDataset(
- wdkService: WdkService,
- formSubmission: FormSubmission
-) {
- assertIsUserDatasetUploadCompatibleWdkService(wdkService);
-
- const newUserDatasetConfig = await makeNewUserDatasetConfig(
- wdkService,
- formSubmission
- );
-
- return await wdkService.addDataset(newUserDatasetConfig);
-}
-
-export async function makeNewUserDatasetConfig(
- wdkService: WdkService,
- formSubmission: FormSubmission
-): Promise {
- if (formSubmission.dataUploadSelection.type !== 'result') {
- return {
- ...formSubmission,
- uploadMethod: formSubmission.dataUploadSelection,
- };
- }
-
- const { compatibleRecordTypes, stepId } = formSubmission.dataUploadSelection;
-
- const { recordClassName } = await wdkService.findStep(stepId);
-
- const resultReportSettings = compatibleRecordTypes[recordClassName];
-
- if (resultReportSettings == null) {
- throw new Error(
- `Tried to upload a result (step id ${stepId}) with an incompatible record type ${recordClassName}.`
- );
- }
-
- return {
- ...formSubmission,
- uploadMethod: {
- type: 'result',
- stepId,
- ...resultReportSettings,
- },
- };
-}
diff --git a/packages/libs/user-datasets-legacy/src/logo.svg b/packages/libs/user-datasets-legacy/src/logo.svg
deleted file mode 100644
index 9dfc1c058c..0000000000
--- a/packages/libs/user-datasets-legacy/src/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/libs/user-datasets-legacy/src/react-app-env.d.ts b/packages/libs/user-datasets-legacy/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5fc6..0000000000
--- a/packages/libs/user-datasets-legacy/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/packages/libs/user-datasets-legacy/src/reportWebVitals.ts b/packages/libs/user-datasets-legacy/src/reportWebVitals.ts
deleted file mode 100644
index 49a2a16e0f..0000000000
--- a/packages/libs/user-datasets-legacy/src/reportWebVitals.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { ReportHandler } from 'web-vitals';
-
-const reportWebVitals = (onPerfEntry?: ReportHandler) => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
diff --git a/packages/libs/user-datasets-legacy/src/setupProxy.js b/packages/libs/user-datasets-legacy/src/setupProxy.js
deleted file mode 100644
index 3955372be7..0000000000
--- a/packages/libs/user-datasets-legacy/src/setupProxy.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const { createProxyMiddleware } = require('http-proxy-middleware');
-const { endpoint } = require('./constants');
-
-module.exports = function (app) {
- app.use(
- endpoint,
- createProxyMiddleware({
- target: process.env.REACT_APP_WDK_SERVICE_URL,
- pathRewrite: { [`^${endpoint}`]: '' },
- secure: false,
- changeOrigin: true,
- followRedirects: true,
- logLevel: 'debug',
- onProxyReq: addPrereleaseAuthCookieToProxyReq,
- })
- );
- app.use(
- '/dataset-import',
- createProxyMiddleware({
- target: process.env.DATASET_IMPORT_SERVICE_URL,
- pathRewrite: { '^/dataset-import': '' },
- secure: false,
- changeOrigin: true,
- followRedirects: true,
- logLevel: 'debug',
- onProxyReq: addPrereleaseAuthCookieToProxyReq,
- })
- );
-};
-
-function addPrereleaseAuthCookieToProxyReq(proxyReq) {
- if (proxyReq._isRedirect) return;
- const authCookie = `auth_tkt=${process.env.VEUPATHDB_AUTH_TKT}`;
- const cookieRaw = proxyReq.getHeader('cookie');
- const cookies =
- cookieRaw == null
- ? authCookie
- : Array.isArray(cookieRaw)
- ? [...cookieRaw, authCookie]
- : [cookieRaw, authCookie];
- proxyReq.setHeader('cookie', cookies);
-}
diff --git a/packages/libs/user-datasets-legacy/src/setupTests.ts b/packages/libs/user-datasets-legacy/src/setupTests.ts
deleted file mode 100644
index 8f2609b7b3..0000000000
--- a/packages/libs/user-datasets-legacy/src/setupTests.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom';
diff --git a/packages/libs/user-datasets-legacy/tsconfig.build.json b/packages/libs/user-datasets-legacy/tsconfig.build.json
deleted file mode 100644
index caf23fdbf4..0000000000
--- a/packages/libs/user-datasets-legacy/tsconfig.build.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "include": [
- "src/lib",
- "src/react-app-env.d.ts"
- ],
- "compilerOptions": {
- "noEmit": false,
- "sourceMap": true,
- "declaration": true,
- "declarationMap": true,
- "outDir": "lib",
- "target": "ES2015"
- }
-}
diff --git a/packages/libs/user-datasets-legacy/tsconfig.json b/packages/libs/user-datasets-legacy/tsconfig.json
deleted file mode 100644
index 65d3451ba1..0000000000
--- a/packages/libs/user-datasets-legacy/tsconfig.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "extends": "@veupathdb/tsconfig/tsconfig.json",
- "include": ["src"]
-}
diff --git a/packages/libs/user-datasets/src/lib/Actions/UserDatasetsActions.ts b/packages/libs/user-datasets/src/lib/Actions/UserDatasetsActions.ts
index 48a00e258c..206ce076ae 100644
--- a/packages/libs/user-datasets/src/lib/Actions/UserDatasetsActions.ts
+++ b/packages/libs/user-datasets/src/lib/Actions/UserDatasetsActions.ts
@@ -21,7 +21,6 @@ import {
UserDatasetDetails,
UserDatasetMeta,
UserDatasetVDI,
- UserQuotaMetadata,
UserDatasetFileListing,
} from '../Utils/types';
import { FetchClientError } from '@veupathdb/http-utils';
@@ -504,13 +503,12 @@ export function loadUserDatasetListWithoutLoadingIndicator() {
() => false
),
wdkService.getCurrentUserDatasets(),
- wdkService.getUserQuotaMetadata(),
- ]).then(([filterByProject, userDatasets, userQuotaMetadata]) => {
+ ]).then(([filterByProject, userDatasets]) => {
const vdiToExistingUds = userDatasets.map(
(ud: UserDatasetVDI): UserDataset => {
const { fileCount, shares, fileSizeTotal } = ud;
const partiallyTransformedResponse =
- transformVdiResponseToLegacyResponseHelper(ud, userQuotaMetadata);
+ transformVdiResponseToLegacyResponseHelper(ud);
return {
...partiallyTransformedResponse,
fileCount,
@@ -535,16 +533,12 @@ export function loadUserDatasetDetailWithoutLoadingIndicator(id: string) {
return validateVdiCompatibleThunk(({ wdkService }) =>
Promise.all([
wdkService.getUserDataset(id),
- wdkService.getUserQuotaMetadata(),
wdkService.getUserDatasetFileListing(id),
]).then(
- ([userDataset, userQuotaMetadata, fileListing]) => {
+ ([userDataset, fileListing]) => {
const { shares, dependencies } = userDataset as UserDatasetDetails;
const partiallyTransformedResponse =
- transformVdiResponseToLegacyResponseHelper(
- userDataset,
- userQuotaMetadata
- );
+ transformVdiResponseToLegacyResponseHelper(userDataset);
const transformedResponse = {
...partiallyTransformedResponse,
fileListing,
@@ -693,8 +687,7 @@ type PartialLegacyUserDataset = Omit<
>;
function transformVdiResponseToLegacyResponseHelper(
- ud: UserDatasetDetails | UserDatasetVDI,
- userQuotaMetadata: UserQuotaMetadata
+ ud: UserDatasetDetails | UserDatasetVDI
): PartialLegacyUserDataset {
const {
name,
@@ -709,7 +702,6 @@ function transformVdiResponseToLegacyResponseHelper(
importMessages,
visibility,
} = ud;
- const { quota } = userQuotaMetadata;
return {
owner: owner.firstName + ' ' + owner.lastName,
projects: projectIds ?? [],
@@ -728,7 +720,6 @@ function transformVdiResponseToLegacyResponseHelper(
ownerUserId: owner.userId,
age: Date.now() - Date.parse(created),
id: datasetId,
- percentQuotaUsed: quota.usage / quota.limit,
status,
importMessages: importMessages ?? [],
};
diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/BigwigDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/BigwigDatasetDetail.jsx
index 2ffc3e962d..dbccc3ec6c 100644
--- a/packages/libs/user-datasets/src/lib/Components/Detail/BigwigDatasetDetail.jsx
+++ b/packages/libs/user-datasets/src/lib/Components/Detail/BigwigDatasetDetail.jsx
@@ -1,7 +1,11 @@
import React from 'react';
import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
-import { Mesa, MesaState } from '@veupathdb/coreui/lib/components/Mesa';
+import {
+ AnchoredTooltip,
+ Mesa,
+ MesaState,
+} from '@veupathdb/coreui/lib/components/Mesa';
import { makeClassifier } from '../UserDatasetUtils';
import UserDatasetDetail from './UserDatasetDetail';
@@ -14,6 +18,11 @@ class BigwigDatasetDetail extends UserDatasetDetail {
super(props);
this.renderTracksSection = this.renderTracksSection.bind(this);
this.getTracksTableColumns = this.getTracksTableColumns.bind(this);
+ this.renderCompatibilitySection =
+ this.renderCompatibilitySection.bind(this);
+ this.getCompatibilityStatus = this.getCompatibilityStatus.bind(this);
+ this.getCompatibilityTableColumns =
+ this.getCompatibilityTableColumns.bind(this);
this.state = {
...this.state,
sequenceId: null,
@@ -133,11 +142,135 @@ class BigwigDatasetDetail extends UserDatasetDetail {
);
}
+ /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+ Compatible Table
+
+ -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
+
+ renderCompatibilitySection() {
+ const { userDataset, config, dataNoun } = this.props;
+ const { displayName } = config;
+
+ const compatibilityTableState = MesaState.create({
+ columns: this.getCompatibilityTableColumns(userDataset),
+ rows: userDataset.dependencies,
+ });
+
+ const compatibilityStatus = this.getCompatibilityStatus();
+
+ return (
+
+
+ Use This {dataNoun.singular} in {displayName}
+
+
+
+ Compatibility Information
+
+
+
+
+
+
+
+
+
+ {compatibilityStatus}
+
+ );
+ }
+
+ getCompatibilityStatus() {
+ const { userDataset, config, dataNoun } = this.props;
+ const { projectId } = config;
+
+ const { status, projects } = userDataset;
+
+ /**
+ * In VDI, we know a dataset is compatible when the site-specific's install status
+ * indicates a successful install.
+ *
+ * We know a dataset is incompatible when the site-specific's install status
+ * indicates `missing-dependency`
+ */
+ const installStatusForCurrentProject = status.install?.find(
+ (d) => d.projectId === projectId
+ );
+
+ const isTargetingCurrentSite = projects.includes(projectId);
+ const isInstalled = [
+ userDataset.status.import,
+ installStatusForCurrentProject?.metaStatus,
+ installStatusForCurrentProject?.dataStatus,
+ ].every((status) => status === 'complete');
+
+ const isIncompatible =
+ installStatusForCurrentProject?.dataStatus === 'missing-dependency';
+
+ if (!isTargetingCurrentSite || (isTargetingCurrentSite && isIncompatible)) {
+ return (
+ // if projectIds don't match, then we're not installable and thus incompatible
+ // if we're installable but failed due to a missing dependency, we're incompatible
+
+ This {dataNoun.singular.toLowerCase()} is not compatible with{' '}
+ {projectId} .
+
+ );
+ } else if (isInstalled) {
+ return (
+ // if we've installed successfully and we're installable, we're compatible
+
+ This {dataNoun.singular.toLowerCase()} is compatible with{' '}
+ {projectId} . It is installed for use.
+
+ );
+ } else {
+ // instead of attempting to provide very granular messaging for when things are neither
+ // compatible nor incompatible, let's let the dataset page's Status messaging handle this
+ return null;
+ }
+ }
+
+ getCompatibilityTableColumns() {
+ const { userDataset } = this.props;
+ const { projects } = userDataset;
+ return [
+ {
+ key: 'project',
+ name: 'VEuPathDB Website',
+ renderCell() {
+ return projects.join(', ');
+ },
+ },
+ {
+ key: 'resourceDisplayName',
+ name: 'Required Resource',
+ renderCell({ row }) {
+ const { resourceDisplayName } = row;
+ return resourceDisplayName;
+ },
+ },
+ {
+ key: 'resourceVersion',
+ name: 'Required Resource Release',
+ renderCell({ row }) {
+ const { resourceVersion } = row;
+ return resourceVersion;
+ },
+ },
+ ];
+ }
+
+ // See note in the base class, UserDatasetDetail
+ /** @return {import("react").ReactNode[]} */
getPageSections() {
- const [headerSection, compatSection, fileSection] = super.getPageSections();
+ const [headerSection, fileSection] = super.getPageSections();
return [
headerSection,
- compatSection,
+ this.renderCompatibilitySection,
this.renderTracksSection,
fileSection,
];
diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/BiomDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/BiomDatasetDetail.jsx
deleted file mode 100644
index f26ed689b1..0000000000
--- a/packages/libs/user-datasets/src/lib/Components/Detail/BiomDatasetDetail.jsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { Link } from 'react-router-dom';
-
-import UserDatasetDetail from './UserDatasetDetail';
-
-class BiomDatasetDetail extends UserDatasetDetail {
- constructor(props) {
- super(props);
- this.renderEdaLinkout = this.renderEdaLinkout.bind(this);
- }
-
- renderEdaLinkout() {
- const {
- config: { displayName, projectId },
- userDataset: { status },
- edaWorkspaceUrl,
- } = this.props;
-
- const isInstalled =
- status?.import === 'complete' &&
- status?.install?.find((d) => d.projectId === projectId)?.dataStatus ===
- 'complete';
-
- return !isInstalled || !edaWorkspaceUrl ? null : (
-
-
-
- Explore in {displayName}
-
-
-
- );
- }
-
- getPageSections() {
- const [headerSection, , fileSection] = super.getPageSections();
-
- return [headerSection, this.renderEdaLinkout, fileSection];
- }
-}
-
-export default BiomDatasetDetail;
diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/EdaDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/EdaDatasetDetail.jsx
new file mode 100644
index 0000000000..79fc457cc7
--- /dev/null
+++ b/packages/libs/user-datasets/src/lib/Components/Detail/EdaDatasetDetail.jsx
@@ -0,0 +1,66 @@
+import { Link } from 'react-router-dom';
+
+import UserDatasetDetail from './UserDatasetDetail';
+
+class EdaDatasetDetail extends UserDatasetDetail {
+ constructor(props) {
+ super(props);
+ this.renderEdaLinkout = this.renderEdaLinkout.bind(this);
+ }
+
+ renderEdaLinkout() {
+ const {
+ config: { displayName, projectId },
+ userDataset: { status },
+ edaWorkspaceUrl,
+ edaMapUrl,
+ } = this.props;
+
+ const isInstalled =
+ status?.import === 'complete' &&
+ status?.install?.find((d) => d.projectId === projectId)?.dataStatus ===
+ 'complete';
+
+ if (!isInstalled) return null;
+
+ if (edaWorkspaceUrl == null && edaMapUrl == null) return null;
+
+ return (
+
+ {!edaWorkspaceUrl ? null : (
+
+
+ Explore in {displayName}
+
+
+ )}
+ {!edaMapUrl ? null : (
+
+
+ Explore in MapVEu
+
+
+ )}
+
+ );
+ }
+
+ getAttributes() {
+ const attributes = super.getAttributes();
+
+ if (!this.isInstalled()) return attributes;
+
+ const edaLinks = {
+ attribute: 'Explore',
+ value: this.renderEdaLinkout(),
+ };
+ const spliceIndex = this.props.includeNameHeader ? 2 : 1;
+ return [
+ ...attributes.slice(0, spliceIndex),
+ edaLinks,
+ ...attributes.slice(spliceIndex),
+ ];
+ }
+}
+
+export default EdaDatasetDetail;
diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/IsaDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/IsaDatasetDetail.jsx
deleted file mode 100644
index 2c52557f47..0000000000
--- a/packages/libs/user-datasets/src/lib/Components/Detail/IsaDatasetDetail.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Link } from 'react-router-dom';
-
-import UserDatasetDetail from './UserDatasetDetail';
-
-class IsaDatasetDetail extends UserDatasetDetail {
- constructor(props) {
- super(props);
- this.renderEdaLinkout = this.renderEdaLinkout.bind(this);
- }
-
- renderEdaLinkout() {
- const {
- config: { displayName, projectId },
- userDataset: { status },
- edaWorkspaceUrl,
- edaMapUrl,
- } = this.props;
-
- const isInstalled =
- status?.import === 'complete' &&
- status?.install?.find((d) => d.projectId === projectId)?.dataStatus ===
- 'complete';
-
- if (!isInstalled) return null;
-
- return (
- <>
- {!edaWorkspaceUrl ? null : (
-
-
-
- Explore in {displayName}
-
-
-
- )}
- {!edaMapUrl ? null : (
-
-
-
- Explore in MapVEu
-
-
-
- )}
- >
- );
- }
-
- getPageSections() {
- const [headerSection, , fileSection] = super.getPageSections();
-
- return [headerSection, this.renderEdaLinkout, fileSection];
- }
-}
-
-export default IsaDatasetDetail;
diff --git a/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx b/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx
index 882e416762..9cd01b6b95 100644
--- a/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx
+++ b/packages/libs/user-datasets/src/lib/Components/Detail/UserDatasetDetail.jsx
@@ -4,11 +4,7 @@ import { Public } from '@material-ui/icons';
import Icon from '@veupathdb/wdk-client/lib/Components/Icon/IconAlt';
import SaveableTextEditor from '@veupathdb/wdk-client/lib/Components/InputControls/SaveableTextEditor';
import Link from '@veupathdb/wdk-client/lib/Components/Link';
-import {
- AnchoredTooltip,
- Mesa,
- MesaState,
-} from '@veupathdb/coreui/lib/components/Mesa';
+import { Mesa, MesaState } from '@veupathdb/coreui/lib/components/Mesa';
import { WdkDependenciesContext } from '@veupathdb/wdk-client/lib/Hooks/WdkDependenciesEffect';
import { bytesToHuman } from '@veupathdb/wdk-client/lib/Utils/Converters';
@@ -17,12 +13,13 @@ import NotFound from '@veupathdb/wdk-client/lib/Views/NotFound/NotFound';
import SharingModal from '../Sharing/UserDatasetSharingModal';
import CommunityModal from '../Sharing/UserDatasetCommunityModal';
import UserDatasetStatus from '../UserDatasetStatus';
-import { makeClassifier, normalizePercentage } from '../UserDatasetUtils';
+import { makeClassifier } from '../UserDatasetUtils';
import { ThemedGrantAccessButton } from '../ThemedGrantAccessButton';
import { ThemedDeleteButton } from '../ThemedDeleteButton';
import { DateTime } from '../DateTime';
+import '../UserDatasets.scss';
import './UserDatasetDetail.scss';
const classify = makeClassifier('UserDatasetDetail');
@@ -41,12 +38,6 @@ class UserDatasetDetail extends React.Component {
this.renderHeaderSection = this.renderHeaderSection.bind(this);
this.renderDatasetActions = this.renderDatasetActions.bind(this);
- this.renderCompatibilitySection =
- this.renderCompatibilitySection.bind(this);
- this.getCompatibilityStatus = this.getCompatibilityStatus.bind(this);
- this.getCompatibilityTableColumns =
- this.getCompatibilityTableColumns.bind(this);
-
this.openSharingModal = this.openSharingModal.bind(this);
this.renderFileSection = this.renderFileSection.bind(this);
this.closeSharingModal = this.closeSharingModal.bind(this);
@@ -127,6 +118,7 @@ class UserDatasetDetail extends React.Component {
}
renderAllDatasetsLink() {
+ if (!this.props.includeAllLink) return null;
return (
@@ -135,27 +127,24 @@ class UserDatasetDetail extends React.Component {
);
}
+ isInstalled() {
+ const { config } = this.props;
+ const { status } = this.props.userDataset;
+ return (
+ status?.import === 'complete' &&
+ status?.install?.find((d) => d.projectId === config.projectId)
+ ?.dataStatus === 'complete'
+ );
+ }
+
getAttributes() {
- const { userDataset, quotaSize, questionMap, dataNoun, config } =
- this.props;
+ const { userDataset, questionMap, dataNoun } = this.props;
const { onMetaSave } = this;
- const {
- id,
- type,
- meta,
- size,
- percentQuotaUsed,
- owner,
- created,
- sharedWith,
- status,
- } = userDataset;
+ const { id, type, meta, size, owner, created, sharedWith, status } =
+ userDataset;
const { display, name, version } = type;
const isOwner = this.isMyDataset();
- const isInstalled =
- status?.import === 'complete' &&
- status?.install?.find((d) => d.projectId === config.projectId)
- ?.dataStatus === 'complete';
+ const isInstalled = this.isInstalled();
const questions = Object.values(questionMap).filter(
(q) =>
'userDatasetType' in q.properties &&
@@ -163,17 +152,19 @@ class UserDatasetDetail extends React.Component {
);
return [
- {
- attribute: this.props.detailsPageTitle,
- className: classify('Name'),
- value: (
-
- ),
- },
+ this.props.includeNameHeader
+ ? {
+ attribute: this.props.detailsPageTitle,
+ className: classify('Name'),
+ value: (
+
+ ),
+ }
+ : null,
{
attribute: 'Status',
value: (
@@ -187,14 +178,50 @@ class UserDatasetDetail extends React.Component {
/>
),
},
+ !questions || !questions.length || !isInstalled
+ ? null
+ : {
+ attribute: 'Available searches',
+ value: (
+
+ {questions.map((q) => {
+ // User dataset searches typically offer changing the dataset through a dropdown
+ // Ths dropdown is a param, "biom_dataset" on MicrobiomeDB and "rna_seq_dataset" on genomic sites
+ // Hence the regex: /dataset/
+ const ps = q.paramNames.filter((paramName) =>
+ paramName.match(/dataset/)
+ );
+ const urlPath = [
+ '',
+ 'search',
+ q.outputRecordClassName,
+ q.urlSegment,
+ ].join('/');
+ const url =
+ urlPath +
+ (ps.length === 1 ? '?param.' + ps[0] + '=' + id : '');
+ return (
+
+ {q.displayName}
+
+ );
+ })}
+
+ ),
+ },
+ {
+ attribute: 'Owner',
+ value: isOwner ? 'Me' : owner,
+ },
{
attribute: 'Visibility',
value:
meta.visibility === 'public' ? (
<>
{' '}
- This{' '}
- {dataNoun.singular.toLowerCase()} is visible to the community.
+ This is a "Community{' '}
+ {dataNoun.singular}" made accessible to the public by user {owner}
+ .
>
) : (
<>
@@ -207,6 +234,7 @@ class UserDatasetDetail extends React.Component {
? null
: {
attribute: 'Shared with',
+ className: classify('SharedWith'),
value: (